{"_id":"bidc","_rev":"6-5a070b9c42dd544e05aa633891366d85","name":"bidc","dist-tags":{"latest":"0.0.4"},"versions":{"0.0.1":{"name":"bidc","version":"0.0.1","keywords":[],"author":{"name":"Shu Ding","email":"g@shud.in"},"license":"MIT","_id":"bidc@0.0.1","maintainers":[{"name":"quietshu","email":"g@shud.in"}],"dist":{"shasum":"3f94bba79e45c15ce583c9d794fc1c5d457e729a","tarball":"https://registry.npmjs.org/bidc/-/bidc-0.0.1.tgz","fileCount":6,"integrity":"sha512-F/lO/tyCZ3KZ1paLmIsZffk5GxUEVQ+GsavS1MTi99pbAAMIZ62db2qQs4TkhPVhz/ndBXZHyTQe6JH6+2LXaA==","signatures":[{"sig":"MEYCIQC0kmOtZjkTjHRNtP+TGZKHjLYjswaWzV86iVCVb7+PaAIhAPimgzmw5YoZV466BOxp9fyeje0URIN5ost2oCJ6P4A6","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":117165},"main":"./dist/index.js","pnpm":{"patchedDependencies":{"devalue@5.1.1":"patches/devalue@5.1.1.patch"}},"types":"./dist/index.d.ts","module":"./dist/index.mjs","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"}},"gitHead":"d6a9dcf675df26f2a28a4c195a02cf6ebd8b8bbb","scripts":{"dev":"bunchee --watch","test":"vitest","build":"bunchee","clean":"rm -rf dist","watch":"bunchee --watch","test:ui":"vitest --ui","typecheck":"tsc --noEmit","test:coverage":"vitest --coverage","prepublishOnly":"pnpm run clean && pnpm run build"},"_npmUser":{"name":"quietshu","email":"g@shud.in"},"_npmVersion":"10.9.2","description":"Bidirectional Channel for JavaScript","directories":{},"_nodeVersion":"22.13.1","_hasShrinkwrap":false,"packageManager":"pnpm@8.7.0+sha512.1d61f8c10e8a5a5dbfbddea0f1f5efb7dbd5177223073aec953cf230aa46416884b9427a6421dba78f28ad0f93fbede38ba57083237b350243de6b1e3a8ce71d","devDependencies":{"vitest":"^3.2.4","bunchee":"^6.6.0","devalue":"^5.1.1","happy-dom":"^18.0.1","@vitest/ui":"^3.2.4","typescript":"^5.9.2"},"_npmOperationalInternal":{"tmp":"tmp/bidc_0.0.1_1754476484035_0.2762939892662417","host":"s3://npm-registry-packages-npm-production"}},"0.0.2":{"name":"bidc","version":"0.0.2","keywords":["bidirectional","channel","communication","javascript","rpc","async","cross-context","postmessage","iframe","worker"],"author":{"name":"Shu Ding","email":"g@shud.in"},"license":"MIT","_id":"bidc@0.0.2","maintainers":[{"name":"quietshu","email":"g@shud.in"}],"dist":{"shasum":"b5b92b4ebcd77e1c0d9bd741b4753350299b001f","tarball":"https://registry.npmjs.org/bidc/-/bidc-0.0.2.tgz","fileCount":6,"integrity":"sha512-+t2xK8oQEpxYEM+350mMHSx7eZNoOX6SpjqD+p+lDpVwpYVXjkTCfy0N60xyjelZ2AOa8NAg/12uhAX3MuAR0g==","signatures":[{"sig":"MEYCIQDNCV4rLG1kgT9Tmr2OMELW2NaeRAC58dkyvtsftqxUvAIhAM14LSAQvi8lo9d6re/OWbguYuIE3JH7A6yqUkxsq34I","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":117374},"main":"./dist/index.js","pnpm":{"patchedDependencies":{"devalue@5.1.1":"patches/devalue@5.1.1.patch"}},"types":"./dist/index.d.ts","module":"./dist/index.mjs","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"}},"gitHead":"859bbf461b98abe3bbb95fd3708dcdaaf6533eb3","scripts":{"dev":"bunchee --watch","test":"vitest","build":"bunchee","clean":"rm -rf dist","watch":"bunchee --watch","test:ui":"vitest --ui","typecheck":"tsc --noEmit","test:coverage":"vitest --coverage","prepublishOnly":"pnpm run clean && pnpm run build"},"_npmUser":{"name":"quietshu","email":"g@shud.in"},"_npmVersion":"10.9.2","description":"Bidirectional Channel for JavaScript","directories":{},"_nodeVersion":"22.13.1","_hasShrinkwrap":false,"packageManager":"pnpm@8.7.0+sha512.1d61f8c10e8a5a5dbfbddea0f1f5efb7dbd5177223073aec953cf230aa46416884b9427a6421dba78f28ad0f93fbede38ba57083237b350243de6b1e3a8ce71d","devDependencies":{"vitest":"^3.2.4","bunchee":"^6.6.0","devalue":"^5.1.1","happy-dom":"^18.0.1","@vitest/ui":"^3.2.4","typescript":"^5.9.2","@vitest/coverage-v8":"3.2.4"},"_npmOperationalInternal":{"tmp":"tmp/bidc_0.0.2_1754476717947_0.8821446371556705","host":"s3://npm-registry-packages-npm-production"}},"0.0.3":{"name":"bidc","version":"0.0.3","keywords":["bidirectional","channel","communication","javascript","rpc","async","cross-context","postmessage","iframe","worker"],"author":{"name":"Shu Ding","email":"g@shud.in"},"license":"MIT","_id":"bidc@0.0.3","maintainers":[{"name":"quietshu","email":"g@shud.in"}],"dist":{"shasum":"97a90272532f7bc386b53501c65d420ce2a794f2","tarball":"https://registry.npmjs.org/bidc/-/bidc-0.0.3.tgz","fileCount":6,"integrity":"sha512-stoXSIDBnqJhquTf0fNNoEfz2JfFCVXADoIim9c5QjWB7CoK3353ZtnJSydPNfzTLoeYuV5/0QNuSz34nSRSag==","signatures":[{"sig":"MEUCIQCEz5lBSars1sHkL0aWfmew4Zermy1jkX6CydKuM9+0twIgNU3eP02VB+r7a+jJZHKB4MjlLYljdqw/Hk43UfWSFnY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":122364},"main":"./dist/index.js","pnpm":{"patchedDependencies":{"devalue@5.3.2":"patches/devalue@5.3.2.patch"}},"types":"./dist/index.d.ts","module":"./dist/index.mjs","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"}},"gitHead":"f6f99a5480f1e67ed53967745e8091f9abf4a81c","scripts":{"dev":"bunchee --watch","test":"vitest","build":"bunchee","clean":"rm -rf dist","watch":"bunchee --watch","test:ui":"vitest --ui","typecheck":"tsc --noEmit","test:coverage":"vitest --coverage","prepublishOnly":"pnpm run clean && pnpm run build"},"_npmUser":{"name":"quietshu","email":"g@shud.in"},"_npmVersion":"10.9.2","description":"Bidirectional Channel for JavaScript","directories":{},"_nodeVersion":"22.13.1","_hasShrinkwrap":false,"packageManager":"pnpm@8.7.0+sha512.1d61f8c10e8a5a5dbfbddea0f1f5efb7dbd5177223073aec953cf230aa46416884b9427a6421dba78f28ad0f93fbede38ba57083237b350243de6b1e3a8ce71d","devDependencies":{"vitest":"^3.2.4","bunchee":"^6.6.0","devalue":"^5.3.2","happy-dom":"^18.0.1","@vitest/ui":"^3.2.4","typescript":"^5.9.2","@vitest/coverage-v8":"3.2.4"},"_npmOperationalInternal":{"tmp":"tmp/bidc_0.0.3_1756398752405_0.779933196668078","host":"s3://npm-registry-packages-npm-production"}},"0.0.4":{"name":"bidc","version":"0.0.4","description":"Bidirectional Channel for JavaScript","main":"./dist/index.js","module":"./dist/index.mjs","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"}},"scripts":{"build":"bunchee","watch":"bunchee --watch","dev":"bunchee --watch","typecheck":"tsc --noEmit","clean":"rm -rf dist","prepublishOnly":"pnpm run clean && pnpm run build","test":"vitest","test:ui":"vitest --ui","test:coverage":"vitest --coverage"},"keywords":["bidirectional","channel","communication","javascript","rpc","async","cross-context","postmessage","iframe","worker"],"author":{"name":"Shu Ding","email":"g@shud.in"},"license":"MIT","devDependencies":{"@vitest/coverage-v8":"3.2.4","@vitest/ui":"^3.2.4","bunchee":"^6.6.0","devalue":"^5.6.1","happy-dom":"^18.0.1","typescript":"^5.9.2","vitest":"^3.2.4"},"gitHead":"87c4b6378a818c29fdf0f8293450653473d0bc41","_id":"bidc@0.0.4","_nodeVersion":"24.12.0","_npmVersion":"11.6.2","dist":{"integrity":"sha512-WgHZ4TFk9/Lb6QhW/ntEquB6RSieCcMToRSG2xIOHeALDeiUTOXdMx9ixBHUe3wuXphtjqGEOXBvkmkGEZznSw==","shasum":"50fb18d726f79c162c2ce29173451f6e888bbe6a","tarball":"https://registry.npmjs.org/bidc/-/bidc-0.0.4.tgz","fileCount":6,"unpackedSize":124253,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCcGiiPF535UP6yfepR6C770uR+4GtP9ivT1yFVyxeCmwIgLc0PwIK/Zi35e7fhQ2IBFhQ9+PHQK7ILSrQgh9eOhWQ="}]},"_npmUser":{"name":"quietshu","email":"g@shud.in"},"directories":{},"maintainers":[{"name":"quietshu","email":"g@shud.in"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/bidc_0.0.4_1765882177422_0.486572163070601"},"_hasShrinkwrap":false}},"time":{"created":"2025-08-06T10:34:44.035Z","modified":"2025-12-16T10:49:37.782Z","1.0.0":"2025-08-04T20:49:50.509Z","0.0.1":"2025-08-06T10:34:44.205Z","0.0.2":"2025-08-06T10:38:38.129Z","0.0.3":"2025-08-28T16:32:32.596Z","0.0.4":"2025-12-16T10:49:37.602Z"},"author":{"name":"Shu Ding","email":"g@shud.in"},"license":"MIT","keywords":["bidirectional","channel","communication","javascript","rpc","async","cross-context","postmessage","iframe","worker"],"description":"Bidirectional Channel for JavaScript","maintainers":[{"name":"quietshu","email":"g@shud.in"}],"readme":"# BIDC - Bidirectional Channels for JavaScript\n\nA bidirectional channel library for __asynchronous communication__ support across JavaScript contexts.\n\n## Overview\n\nUnlike traditional `postMessage` APIs, BIDC enables seamless communication between different JavaScript execution contexts (workers, iframes, service workers) with full support for complex data types, with __Promises__ and __Async Functions__, via a custom streaming protocol.\n\nBIDC automatically establishes a channel between the current context and the target context, regardless of which side initiates first. It also handles re-establishing connections if one side is reloaded (e.g., an iframe) and buffers messages until the other side is ready to receive them.\n\n<p align=\"center\">\n  <img src=\"demo.svg\" />\n</p>\n\n## Features\n\n- **Automatic Handshake**: Robust connection establishment with collision resolution\n- **Complex Data Types**: Seamlessly transfer Dates, RegExp, Maps, Sets, ArrayBuffers, TypedArrays, and more\n- **Asynchronous**: Promises and Async Functions remain as first-class citizens during transfer\n- **Concurrent Operations**: Handle multiple simultaneous messages without blocking\n- **TypeScript**: Easy to infer types for the RPC-style method calls\n\nExamples\n\n- [Basic Data Transfer](#basic-data-transfer)\n- [Two-Way Communication](#two-way-communication)\n- [Complex Data Types](#complex-data-types)\n- [Advanced Use Cases: Nested Promises](#advanced-use-cases-nested-promises)\n- [Advanced Use Cases: Remote Callbacks](#advanced-use-cases-remote-callbacks)\n- [Advanced Use Cases: Worker Communication](#advanced-use-cases-worker-communication)\n- [Advanced Use Cases: Namespaced Channels](#advanced-use-cases-namespaced-channels)\n\n## Installation\n\n```bash\npnpm i bidc\n```\n\n## Examples\n\n### Basic Data Transfer\n\nBIDC automatically establishes a channel between the current context and the target context (e.g., an iframe or worker),\n__no matter which side initiates first__. It will also re-establish the connection if one side (such as an iframe) is reloaded.\n\nParent window:\n\n```js\nimport { createChannel } from 'bidc'\n\n// Connect to an iframe\nconst { send } = createChannel(iframe.contentWindow)\n\n// Send a simple message to the iframe\nconst result = await send({ value: 'Hello, iframe!' })\n\nconsole.assert(result === 'HELLO, IFRAME!')\n```\n\nInside the iframe:\n\n```js\nimport { createChannel } from 'bidc'\n\n// Omitting the target will create a channel to the parent window by default\n// This is equivalent to `createChannel(window.parent)`\nconst { receive } = createChannel()\n\n// Handle incoming messages and return a response\nreceive(payload => {\n  return payload.value.toUpperCase()\n})\n```\n\n`await send(...)` will return the response handled by the `receive` function in the iframe.\nBefore the connection is established, `send` will wait until the other side is ready to receive and handle the message.\n\n### Two-Way Communication\n\nYou can use `send` and `receive` methods on both sides to establish a two-way communication channel.\n\n```js\nimport { createChannel } from 'bidc'\n\nconst { send, receive } = createChannel(iframe.contentWindow)\n\n// Handle incoming messages from the iframe\nreceive((payload) => {\n  console.log('Received from iframe:', payload)\n  return { response: 'Message received!' }\n})\n\n// Send a message to the iframe and wait for its response\nconst responseFromIframe = await send({ value: 'Hello, iframe!' })\n```\n\nInside the iframe:\n\n```js\nimport { createChannel } from 'bidc'\n\nconst { send, receive } = createChannel()\n\n// Handle incoming messages from the parent window\nreceive((payload) => {\n  console.log('Received from parent:', payload)\n  return { response: 'Hello, parent!' }\n})\n\n// Send a message to the parent window and wait for its response\nconst responseFromParent = await send({ value: 'Hello, parent!' })\n```\n\n### Complex Data Types\n\nComplex data types are supported, including:\n- string, number, boolean, bigint, null, undefined\n- Date, RegExp, ArrayBuffer, TypedArray\n- Map, Set, Object, Array\n- __Promise__ and __Async Function__\n\nPromises must resolve/reject to values that are supported by BIDC (can be another Promise). Likewise, async functions can accept and return any supported value, including new Promises and async functions.\n\n```js\n// Send complex objects with various JavaScript types\nconst response = await send({\n  date: new Date(),\n  map: new Map([['key', 'value']]),\n  set: new Set([1, 2, 3]),\n  arrayBuffer: new Uint8Array([1, 2, 3]).buffer,\n  promise: Promise.resolve('resolved value'),\n  function: async (x) => {\n    await sleep(1000)\n    return x * 2\n  }\n})\n\nconsole.assert(response.status === 'success')\n```\n\nReceiving side:\n\n```js\nreceive(async (payload) => {\n  // Handle the promise\n  const resolvedValue = await payload.promise\n  console.assert(resolvedValue === 'resolved value')\n  \n  // Call the function\n  const result = await payload.function(5)\n  console.assert(result === 10)\n\n  return { status: 'success' }\n})\n```\n\n### Advanced Use Cases: Nested Promises\n\nThis example demonstrates how to handle nested Promises and async functions, allowing for complex asynchronous workflows.\n\nParent window:\n\n```js\nimport { createChannel } from 'bidc'\n\nconst { send } = createChannel(iframe.contentWindow)\n\nawait send({\n  foo: new Promise(resolve => {\n    setTimeout(() => {\n      resolve({\n        bar: new Promise(resolve => {\n          setTimeout(() => {\n            resolve('Hello from the parent!')\n          }, 1000)\n        }),\n      })\n    }, 1000)\n  }),\n})\n```\n\nOn the receiving side, you can handle the data payload like this:\n\n```js\n// ...\nreceive(async (data) => {\n  const result = await data.foo\n  const finalResult = await result.bar\n\n  console.assert(finalResult === 'Hello from the parent!')\n})\n```\n\n### Advanced Use Cases: Remote Callbacks\n\nThis example demonstrates how to create nested remote callback mechanisms where two targets (parent and iframe) can both\nsend and receive async functions (which can also take or return async functions) to each other.\n\nParent window:\n\n```js\nimport { createChannel } from 'bidc'\n\nconst { send } = createChannel(iframe.contentWindow)\n\n// Give an async function to the iframe\n// It will always be executed in the parent window's context\nconst calc = await send(\n  async x => {\n    return x * 3\n  }\n)\n\nconsole.log(await calc(1))\n```\n\nIframe:\n\n```js\nimport { createChannel } from 'bidc'\n\nconst { receive } = createChannel()\n\nreceive(parentCalc => {\n  return async y => {\n    const result = await parentCalc(y * 2)\n    return result * 4\n  }\n})\n```\n\nCalling `send(...)` will give that function to the iframe receiver as `parentCalc`. Which then\nwraps it in another async function and returns it back to the parent window as `calc`.\n\nSo the final result of `calc(1)` will be `1 * 2 * 3 * 4 = 24`. `1` will be passed as `y`, which is then multiplied by `2` in the iframe, then passed to the parent window's `parentCalc` function as `x`, which multiplies it by `3`, and finally the `result` is multiplied by `4` in the iframe.\n\n### Advanced Use Cases: Worker Communication\n\nYou can also use BIDC to communicate with Web Workers too. Simply pass the worker instance to `createChannel` as the target context:\n\n```js\nimport { createChannel } from 'bidc'\n\nconst worker = new Worker('./worker.js')\n\nconst { send, receive } = createChannel(worker)\n\n// ...\n```\n\nInside the worker, it will automatically create a channel to the parent context:\n\n```js\nimport { createChannel } from 'bidc'\n\nconst { send, receive } = createChannel()\n```\n\n### Advanced Use Cases: Namespaced Channels\n\nYou can create namespaced channels to avoid conflicts between different parts of your application. This is useful when you have multiple components or modules that need to communicate without interfering with each other.\n\nFor example, you can create 2 channels between the parent and the iframe by specifying different channel IDs (namespaces):\n\n```js\nimport { createChannel } from 'bidc'\n\nconst { send: sendA, receive: receiveA } = createChannel(iframe.contentWindow, 'namespaceA')\nconst { send: sendB, receive: receiveB } = createChannel(iframe.contentWindow, 'namespaceB')\n```\n\nInside the iframe, you can handle messages from both namespaces:\n\n```js\nimport { createChannel } from 'bidc'\n\nconst { send: sendA, receive: receiveA } = createChannel('namespaceA')\nconst { send: sendB, receive: receiveB } = createChannel('namespaceB')\n```\n\nJust ensure that the namespace IDs match on both sides.\n\n## Acknowledgements\n\nThis library is a recreation of RSC (React Server Components)'s serialization mechanism and React Server Actions reference passing idea.\n\nThe underlying serialization and deserialization logic is implemented using the [devalue](https://github.com/Rich-Harris/devalue) library created by Rich Harris.\n\nThis library is authored by [Shu Ding](https://x.com/shuding_).\n\n## License\n\nMIT\n","readmeFilename":"README.md"}