{"id":15710777,"url":"https://github.com/extend-chrome/messages","last_synced_at":"2025-08-20T22:32:31.546Z","repository":{"id":43664764,"uuid":"197461152","full_name":"extend-chrome/messages","owner":"extend-chrome","description":"An API for Chrome Extension messages that makes sense.","archived":false,"fork":false,"pushed_at":"2023-08-03T06:30:35.000Z","size":1370,"stargazers_count":74,"open_issues_count":12,"forks_count":8,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-12-10T00:51:49.223Z","etag":null,"topics":["chrome-api","chrome-extension","chrome-extensions","messaging","typescript"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/@extend-chrome/messages","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/extend-chrome.png","metadata":{"funding":{"ko_fi":"jacksteam"},"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-07-17T20:52:55.000Z","updated_at":"2024-11-17T17:48:26.000Z","dependencies_parsed_at":"2024-06-18T18:14:33.003Z","dependency_job_id":"71ee331b-cdcd-4934-b397-0d6722092a11","html_url":"https://github.com/extend-chrome/messages","commit_stats":{"total_commits":174,"total_committers":4,"mean_commits":43.5,"dds":"0.28735632183908044","last_synced_commit":"4488ea32c81a9f6223ee6f21d9fa93e30b9de443"},"previous_names":[],"tags_count":26,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/extend-chrome%2Fmessages","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/extend-chrome%2Fmessages/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/extend-chrome%2Fmessages/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/extend-chrome%2Fmessages/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/extend-chrome","download_url":"https://codeload.github.com/extend-chrome/messages/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":230462911,"owners_count":18229865,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["chrome-api","chrome-extension","chrome-extensions","messaging","typescript"],"created_at":"2024-10-03T21:10:04.079Z","updated_at":"2024-12-19T16:12:41.670Z","avatar_url":"https://github.com/extend-chrome.png","language":"TypeScript","funding_links":["https://ko-fi.com/jacksteam","https://ko-fi.com/K3K1QNTF"],"categories":[],"sub_categories":[],"readme":"\u003c!--\nTemplate tags:\nextend-chrome\nmessages\n@extend-chrome\nhttps://imgur.com/cKFLQ0o.png\n--\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https: //github.com/extend-chrome/messages\" rel=\"noopener\"\u003e\n  \u003cimg width=200px height=200px src=\"https://imgur.com/XYtiJBw.png\" alt=\"@extend-chrome/messages logo\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003ch3 align=\"center\"\u003e@extend-chrome/messages\u003c/h3\u003e\n\n\u003cdiv align=\"center\"\u003e\n\n[![npm (scoped)](https://img.shields.io/npm/v/@extend-chrome/messages.svg)](https://www.npmjs.com/package/@extend-chrome/messages)\n[![GitHub last commit](https://img.shields.io/github/last-commit/extend-chrome/messages.svg)](https://github.com/extend-chrome/messages)\n[![License](https://img.shields.io/badge/license-MIT-blue.svg)](/LICENSE)\n[![TypeScript Declarations Included](https://img.shields.io/badge/types-TypeScript-informational)](#typescript)\n\n\u003c/div\u003e\n\n\u003cdiv align=\"center\"\u003e\n\n[![Fiverr: We make Chrome extensions](https://img.shields.io/badge/Fiverr%20-We%20make%20Chrome%20extensions-brightgreen.svg)](https://www.fiverr.com/jacksteam)\n[![ko-fi](https://img.shields.io/badge/ko--fi-Buy%20me%20a%20coffee-ff5d5b)](https://ko-fi.com/K3K1QNTF)\n\n\u003c/div\u003e\n\n---\n\nAn API for Chrome extension messaging that makes sense. Uses Promises and\nObservables for convenience.\n\n## Table of Contents\n\n- [Getting Started](#getting_started)\n- [Usage](#usage)\n- [Features](#features)\n- [API](#api)\n\n## Getting started \u003ca name = \"getting_started\"\u003e\u003c/a\u003e\n\nYou will need to use a bundler like [Rollup](https://rollupjs.org/guide/en/),\nParcel, or Webpack to include this library in the build of Chrome extension.\n\nSee\n[`rollup-plugin-chrome-extension`](https://github.com/extend-chrome/rollup-plugin-chrome-extension)\nfor an easy way use Rollup to build your Chrome extension!\n\n### Installation\n\n```sh\n$ npm i @extend-chrome/messages\n```\n\n## Usage \u003ca name = \"usage\"\u003e\u003c/a\u003e\n\nSend and receive messages using isomorphic message wrappers, or with a\ntraditional messages object.\n\n```javascript\n// messages.js, used in both the background and content script\nimport { getMessage } from '@extend-chrome/messages'\n\n// getMessage returns [Function, Observable, Function]\nexport const [sendNumber, numberStream, waitForNumber] = getMessage(\n  // String to be used as a greeting\n  'NUMBER',\n)\n```\n\n```javascript\n// background.js, a background script\nimport { numberStream } from './messages'\n\n// numberStream is an RxJs Observable\nnumberStream.subscribe(([n, sender]) =\u003e {\n  console.log('the data passed to sendNumber', n)\n  // Sender is a Chrome runtime MessageSender\n  console.log('the message sender', sender)\n})\n```\n\n```javascript\n// content.ts, a content script\nimport { sendNumber } from './messages'\n\ndocument.body.onclick = () =\u003e {\n  sendNumber(42) // 42 is logged in the background\n}\n```\n\n### `getMessage` has great TypeScript support!\n\nIf you're into TypeScript, `getMessage` is a generic function. It shines when\nyou define the message data type. No more message data type mistakes!\nIntellisense has you covered.\n\n```typescript\n// messages.ts\nimport { getMessage } from '@extend-chrome/messages'\n\ninterface Stats {\n  hi: number\n  low: number\n  date: string\n}\n\nexport const [sendStats, statsStream, waitForStats] = getMessage\u003cStats\u003e('STATS')\n\n// If you have a message type with no data, use void rather than undefined\n// This way you can call it with zero arguments\nexport const [sendReady, readyStream, waitForReady] = getMessage\u003cvoid\u003e('READY')\n```\n\n```typescript\n// background.ts\nimport { statsStream } from './messages'\n\nstatsStream.subscribe(([{ hi, low, date }, sender]) =\u003e {\n  // Intellisense knows this is an Observable of\n  // [Stats, chrome.runtime.MessageSender]\n})\n\nwaitForReady().then(() =\u003e {\n  console.log('content.ts is ready')\n})\n```\n\n```typescript\n// content.ts\nimport { sendStats } from './messages'\n\nsendStats({ hi: 30, low: 14, date: '11/12/2019' })\n\n// Throws a TS error\nsendStats('not a Stats object')\n\nsendReady()\n```\n\n## Features \u003ca name = \"features\"\u003e\u003c/a\u003e\n\n### TypeScript Definitions \u003ca name = \"typescript\"\u003e\u003c/a\u003e\n\nThis library is written in TypeScript, extensively typed, and definitions are\nincluded, so no need to install an additional `@types` library!\n\n### RxJs Observables\n\nVersion 0.5.0 introduces an\n[RxJs Observable](https://rxjs-dev.firebaseapp.com/guide/overview) as\n[`messages.stream`](#api-messages-stream).\n\n### Scopes\n\nVersion 0.5.0 introduces [`getScope`](#api-use-scope), a way to use a separate\nmessaging space.\n\nThis is useful if you are writing a library for Chrome extensions that uses\nmessages internally, but you don't want to pollute the global messaging space.\n\n## API \u003ca name = \"api\"\u003e\u003c/a\u003e\n\n### `getMessage(greeting)`\n\n```javascript\nimport { getMessage } from '@extend-chrome/messages'\n\nconst [sendMessage, messageStream, waitForMessage] = getMessage('greeting')\n```\n\nUse this function to create an isomorphic message system. Import it into both\nthe message sender and receiver context (ie, the background page and a content\nscript). `getMessage` is a TypeScript generic function. See the [Usage](#usage)\nsection for more information, including TypeScript support!\n\n#### `greeting`\n\n\u003e Type: `string`\n\nA unique string to identify the message.\n\n#### Returns: `[messageSender, messageStream]`\n\n\u003e Type: `[Function, Observable]`\n\nImport the messageSender into the context you wish to send a message. Call the\nsender with the data you want to send.\n\n`messageStream` is an Observable of a `[data, MessageSender]` tuple. Import the\nmessageStream into the context you wish to recieve messages. Subscribe to it\nwith a message handler function.\n\n### The `messages` Namespace\n\nIf you're more comfortable with a traditional messages namespace, import the\n`messages` object.\n\n#### `messages.send(data, [options])` \u003ca name = \"api-messages-send\"\u003e\u003c/a\u003e\n\nSending one-way messages is simple: just call `messages.send` with an object\nthat includes at least a `greeting` property.\n\n```javascript\n// content-script.js\nimport { messages } from '@extend-chrome/messages'\n\n// Simple message with no data\nmessages.send({ greeting: 'hello' }).then(() =\u003e {\n  console.log('The message was sent.')\n})\n\n// Message with data\nmessages\n  .send({\n    greeting: 'with-data',\n    // You can use any prop name or value\n    data: { x: 1 },\n  })\n  .then(() =\u003e {\n    console.log('The message was sent.')\n  })\n```\n\nActually, you can send any data type as a message, but an object with a\n`greeting` prop is a nice, flexible pattern.\n\n##### Get a response with `options.async` \u003ca name = \"api-messages-send-async\"\u003e\u003c/a\u003e\n\nSet the optional `options.async` to `true` to receive a response. Only message\nlisteners with the third `sendResponse` argument will receive async messages.\n\n```javascript\n// content-script.js\nimport { messages } from '@extend-chrome/messages'\n\nmessages\n  .send(\n    // Message\n    { greeting: 'hello' },\n    // Options\n    { async: true },\n  )\n  .then((response) =\u003e {\n    console.log('They said', response.greeting)\n  })\n```\n\n#### `messages.on(handler)` \u003ca name = \"api-messages-on\"\u003e\u003c/a\u003e\n\nTo receive one way messages, use a message handler function with 0 or 1\narguments. This handler will only receive messages sent without the async\noption.\n\nThe return value of the handler is unused.\n\n```javascript\n// background.js\nimport { messages } from '@extend-chrome/messages'\n\n// Listener should have 2, 1, or 0 arguments\nmessages.on((message, sender) =\u003e {\n  if (message.greeting === 'hello') {\n    console.log(sender.id, 'said hello')\n  }\n})\n```\n\n##### Async Messages \u003ca name = \"api-messages-on-async\"\u003e\u003c/a\u003e\n\n\u003e I've found relying on async messages to be a bit of an anti-pattern. Chrome is\n\u003e pretty aggressive about closing the response port, so unless you're doing\n\u003e something synchronous, it's better to use a separate message and use a\n\u003e listener to handle responses.\n\nTo receive async messages, use a message handler with 3 arguments. This handler\nwill only receive messages sent with the async option.\n\nThe third argument is a `sendResponse` function, which must be called very\nquickly, or Chrome will throw an error. Even a single await may make the extension unreliable.\n\n```javascript\n// Async functions are OK!\nmessages.on(async (message, sender, sendResponse) =\u003e {\n  if (message.greeting === 'hello') {\n    console.log(sender.id, 'said hello')\n\n    await somethingAsync()\n\n    // Still need to call sendResponse\n    sendResponse({ greeting: 'goodbye' })\n  }\n})\n```\n\n#### `messages.off(handler)` \u003ca name = \"api-messages-off\"\u003e\u003c/a\u003e\n\nCall this with the message handler function you wish to stop using.\n\n#### `messages.stream` \u003ca name = \"api-messages-stream\"\u003e\u003c/a\u003e\n\n\u003e Type: `Observable`\n\nAn Observable of all messages in its scope.\n\n```typescript\nimport { messages } from '@extend-chrome/messages'\n\n// Receives all messages in the default scope\nmessages.stream.subscribe(([message, sender, sendResponse]) =\u003e {\n  if (typeof sendResponse !== 'undefined') {\n    // If sendResponse is defined, it must be called\n    sendResponse({ greeting: 'message received!' })\n  }\n})\n```\n\n### `getScope` \u003ca name = \"api-use-scope\"\u003e\u003c/a\u003e\n\nThis is useful if you are writing a library for Chrome extensions that uses\nmessages internally, but you don't want to pollute the global messaging space.\n\n```javascript\nimport { messages, getScope } from '@extend-chrome/messages'\n\nconst myScope = getScope('my-library')\n\n// `messages.on` will not receive this message\nmyScope.send({ greeting: 'hey' })\n\n// `myScope.on` will not receive this message\nmessages.send({ greeting: 'hello?' })\n```\n\n\u003e Note: The Chrome API Event `chrome.runtime.onMessage` will still receive all\n\u003e messages, but projects using `@extend-chrome/messages` will not receive messages from\n\u003e other scopes.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fextend-chrome%2Fmessages","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fextend-chrome%2Fmessages","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fextend-chrome%2Fmessages/lists"}