{"id":18581899,"url":"https://github.com/coder/wxnm","last_synced_at":"2025-05-16T04:09:20.028Z","repository":{"id":57103135,"uuid":"235648479","full_name":"coder/wxnm","owner":"coder","description":"A library for providing TypeScript typed communication between your web extension and your native Node application using Native Messaging","archived":false,"fork":false,"pushed_at":"2021-08-23T19:47:49.000Z","size":196,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-02-06T12:11:48.866Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"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/coder.png","metadata":{"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}},"created_at":"2020-01-22T19:23:06.000Z","updated_at":"2024-08-02T23:06:38.000Z","dependencies_parsed_at":"2022-08-20T23:20:43.477Z","dependency_job_id":null,"html_url":"https://github.com/coder/wxnm","commit_stats":null,"previous_names":["cdr/wxnm"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coder%2Fwxnm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coder%2Fwxnm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coder%2Fwxnm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coder%2Fwxnm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/coder","download_url":"https://codeload.github.com/coder/wxnm/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":239000673,"owners_count":19565831,"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":[],"created_at":"2024-11-07T00:08:13.240Z","updated_at":"2025-02-17T16:20:14.885Z","avatar_url":"https://github.com/coder.png","language":"TypeScript","readme":"# (W)eb E(x)tension (N)ative (M)essenger\n\n`wxnm` is a TypeScript library for providing typed communication between your web extension and your native Node application using [Native Messaging](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Native_messaging). It also provides some utilities for installing your native application's app manifest.\n\n`wxnm` is meant for long-running applications and uses the [`runtime.connectNative`](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/connectNative) API under the hood. If your use case only requires infrequent, one-off messages to be sent, it's recommended you use [`runtime.sendNativeMessage`](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime/sendNativeMessage) directly.\n\n## Compatibility\n\n| Browser       | Windows | Linux | Mac |\n| :------------ | :-----: | :---: | :-: |\n| Firefox       |    ✕    |   ✓   |  ✓  |\n| Waterfox      |    ✕    |   ✓   |  ✓  |\n| Chrome        |    ✕    |   ✓   |  ✓  |\n| Chrome Canary |    ✕    |       |  ✓  |\n| Chrome Beta   |    ✕    |   ✓   |     |\n| Chromium      |         |   ✓   |  ✓  |\n| Brave         |    ✕    |   ✓   |  ✓  |\n| Opera         |    ✕    |   ✕   |  ✓  |\n| Vivaldi       |    ✕    |   ✓   |  ✓  |\n\n\u003cdiv\u003e\u003csup\u003e✓ - supported\u003c/sup\u003e\u003c/div\u003e\n\u003cdiv\u003e\u003csup\u003e✕ - not supported\u003c/sup\u003e\u003c/div\u003e\n\u003cdiv\u003e\u003csup\u003eblank - OS does not support this browser\u003c/sup\u003e\u003c/div\u003e\n\nWindows support is a WIP\n\n## Installation\n\n```bash\nnpm install @coder/wxnm\n# or #\nyarn add @coder/wxnm\n```\n\n## Example\n\nSee the [`example` directory](/example) for a full implementation of an extension that talks to a native application to get its process ID.\n\n## Installing your Native Messaging App\n\nBefore your extension and your native app can communicate, the app will need to register an application manifest on the user's machine, and the extension will need to declare the `nativeMessaging` permission in its manifest. This library provides an `installManifest` utility to install a manifest for all of the user's currently installed browsers, which you can put somewhere in your app as either its own executable, or run via a flag on your main executable. For instance:\n\n```ts\n// If passed --install, install the manifest\nconst argv = yargs.argv\nif (argv.install) {\n  installManifest({\n    name: \"com.coder.wxnm_example\",\n    description: \"Example of the wxnm library\",\n    path: path.resolve(\"./node/dist/wxnm-node-example\"),\n    chromeExtensionIds: [process.env.CHROME_EXTENSION_ID],\n    webExtensionIds: [\"wxnmexample@coder.com\"],\n  })\n  process.exit(0)\n}\n```\n\nSee the example for more info.\n\n## Usage\n\n### Types\n\nTo get the full effectiveness of `wxnm`, you'll want to create some types that will be shared between your browser extension and your node application. They should look something like this:\n\n```ts\nimport { NativeMessage } from \"@coder/wxnm\"\n\n/**\n * Messages the extension will send, native app will recieve\n */\ninterface PingMessage extends NativeMessage {\n  type: \"PING\"\n  message: string\n}\n\nexport type ExtensionMessages = PingMessage\n\n/**\n * Messages the native app will send, extension will receive\n */\ninterface PongMessage extends NativeMessage {\n  type: \"PONG\"\n  message: string\n}\n\ninterface ErrorMessage extends NativeMessage {\n  type: \"ERROR\"\n  error: string\n}\n\nexport type NativeMessages = PongMessage | ErrorMessage\n```\n\n### Web Extension\n\nIn your web extension, you'll call `createExtensionMessenger` which will allow you to send messages, listen for messages, and listen for disconnects:\n\n```ts\nimport { createExtensionMessenger } from \"@coder/wxnm/extension\"\nimport { ExtensionMessages, NativeMessages } from \"../shared/types\"\n\n/**\n * Create an instance of ExtensionNativeMessenger\n */\nconst msger = createExtensionMessenger\u003cExtensionMessages, NativeMessages\u003e(\"com.company.name_of_app\")\n\n/**\n * Handle each message type as it comes in from the app\n */\nmsger.onMessage((msg: NativeMessages) =\u003e {\n  switch (msg.type) {\n    case \"PONG\":\n      console.log(\"Got PONG back!\", msg.message)\n    case \"ERROR\":\n      console.error(\"Uh oh!\", msg.error)\n  }\n})\n\n/**\n * Alert to an error if we unexpectedly disconnect\n */\nmsger.onDisconnect((err?: Error) =\u003e {\n  if (err) {\n    console.error(\"Error with disconnect!\", err)\n  }\n})\n\n/**\n * Send a ping message to the native app\n */\nmsger.sendMessage({\n  type: \"PING\",\n  message: \"Hello\",\n})\n```\n\n### Native Node Application\n\nThe node side looks very similar, with the generics flipped for `createNodeMessenger`:\n\n```ts\nimport { createNodeMessenger } from \"@coder/wxnm/node\"\nimport { ExtensionMessages, NativeMessages } from \"../shared/types\"\n\n/**\n * Create an instance of NodeNativeMessenger\n */\nconst msger = createNodeMessenger\u003cNativeMessages, ExtensionMessages\u003e()\n\n/**\n * Handle ping messages and respond with a pong message\n */\nmsger.onMessage((msg: ExtensionMessages) =\u003e {\n  switch (msg.type) {\n    case \"PING\":\n      msger.sendMessage({\n        type: \"PONG\",\n        data: { message: `You said: ${msg.data.message}` },\n      })\n  }\n})\n\n/**\n * Alert to an error if we unexpectedly disconnect\n */\nmsger.onDisconnect((err?: Error) =\u003e {\n  if (err) {\n    console.error(\"Error with disconnect!\", err)\n  }\n  // Maybe run some cleanup code if you need to as well\n})\n```\n\n## API\n\nThe `wxnm` API is **_not isomorphic_**, so you'll have two import paths depending on which environment you're in.\n\n### @coder/wxnm/extension`\n\n```ts\nexport class ExtensionNativeMessenger\u003cExtensionMessage, NodeMessage\u003e {\n  /**\n   * Disconnect from the native app. This will kill the native app process and\n   * trigger all onDisconnect listeners on both sides.\n   */\n  disconnect(): void\n  /**\n   * Add a listener for when you disconnect. Calls to `disconnect` will trigger\n   * this without an error. Returns an unlistener function.\n   */\n  onDisconnect(listener: (err?: Error) =\u003e void): () =\u003e void\n  /**\n   * Add a listener for when you receive messages from the native app. Returns\n   * an unlistener function.\n   */\n  onMessage(listener: (msg: NodeMessage) =\u003e void): () =\u003e void\n  /**\n   * Send a message to the native app. Must be JSON serializable.\n   */\n  sendMessage(msg: ExtensionMessage): void\n}\n\n/**\n * Creates an instance of ExtensionNativeMessenger\n */\nexport function createExtensionMessenger\u003cExtensionMessage, NodeMessage\u003e()\n```\n\n### @coder/wxnm/extension\n\n```ts\nexport class NodeNativeMessenger\u003cNodeMessage, ExtensionMessage\u003e {\n  /**\n   * Add a listener for when the extension disconnects. The app will terminate\n   * after this runs. Returns an unlistener function.\n   */\n  onDisconnect(listener: (err?: Error) =\u003e void): () =\u003e void\n  /**\n   * Add a listener for when you receive messages from the extension. Returns\n   * an unlistener function.\n   */\n  onMessage(listener: (msg: NodeMessage) =\u003e void): () =\u003e void\n  /**\n   * Send a message to the extension. Must be JSON serializable.\n   */\n  sendMessage(msg: ExtensionMessage): void\n}\n\n/**\n * Creates an instance of ExtensionNativeMessenger\n */\nexport function createExtensionMessenger\u003cNodeMessage, ExtensionMessage\u003e()\n```\n\n## Publishing\n\nBecause using this package relies on you importing submodules, you must run `npm publish` from within the `dist/` directory to avoid imports having to add `/dist/` to their paths. If you attempt to publish from the top level, the `prepublishOnly` script will fail.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcoder%2Fwxnm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcoder%2Fwxnm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcoder%2Fwxnm/lists"}