{"_id":"@atlaschain/subscribable","name":"@atlaschain/subscribable","dist-tags":{"latest":"3.0.0"},"versions":{"3.0.0":{"name":"@atlaschain/subscribable","version":"3.0.0","description":"Helpers for creating subscription-based event emitters","exports":{"edge-light":{"import":"./dist/index.node.mjs","require":"./dist/index.node.cjs"},"workerd":{"import":"./dist/index.node.mjs","require":"./dist/index.node.cjs"},"browser":{"import":"./dist/index.browser.mjs","require":"./dist/index.browser.cjs"},"node":{"import":"./dist/index.node.mjs","require":"./dist/index.node.cjs"},"react-native":"./dist/index.native.mjs","types":"./dist/types/index.d.ts"},"browser":{"./dist/index.node.cjs":"./dist/index.browser.cjs","./dist/index.node.mjs":"./dist/index.browser.mjs"},"main":"./dist/index.node.cjs","module":"./dist/index.node.mjs","react-native":"./dist/index.native.mjs","types":"./dist/types/index.d.ts","type":"commonjs","sideEffects":false,"keywords":["blockchain","atlas","web3"],"scripts":{"compile:docs":"typedoc","compile:js":"tsup --config build-scripts/tsup.config.package.ts","compile:typedefs":"tsc -p ./tsconfig.declarations.json","dev":"jest -c ../../node_modules/@atlas/test-config/jest-dev.config.ts --rootDir . --watch","prepublishOnly":"pnpm pkg delete devDependencies","publish-impl":"npm view $npm_package_name@$npm_package_version > /dev/null 2>&1 || (pnpm publish --tag ${PUBLISH_TAG:-canary} --access public --no-git-checks && (([ -n \"${GITHUB_OUTPUT:-}\" ] && echo 'published=true' >> \"$GITHUB_OUTPUT\") || true) && (([ \"$PUBLISH_TAG\" != \"canary\" ] && ../build-scripts/maybe-tag-latest.ts $npm_package_name@$npm_package_version) || true))","publish-packages":"pnpm prepublishOnly && pnpm publish-impl","style:fix":"pnpm eslint --fix src && pnpm prettier --log-level warn --ignore-unknown --write ./*","test:lint":"TERM_OVERRIDE=\"${TURBO_HASH:+dumb}\" TERM=${TERM_OVERRIDE:-$TERM} jest -c ../../node_modules/@atlas/test-config/jest-lint.config.ts --rootDir . --silent","test:prettier":"TERM_OVERRIDE=\"${TURBO_HASH:+dumb}\" TERM=${TERM_OVERRIDE:-$TERM} jest -c ../../node_modules/@atlas/test-config/jest-prettier.config.ts --rootDir . --silent","test:treeshakability:browser":"agadoo dist/index.browser.mjs","test:treeshakability:native":"agadoo dist/index.native.mjs","test:treeshakability:node":"agadoo dist/index.node.mjs","test:typecheck":"tsc --noEmit","test:unit:browser":"TERM_OVERRIDE=\"${TURBO_HASH:+dumb}\" TERM=${TERM_OVERRIDE:-$TERM} jest -c ../../node_modules/@atlas/test-config/jest-unit.config.browser.ts --rootDir . --silent","test:unit:node":"TERM_OVERRIDE=\"${TURBO_HASH:+dumb}\" TERM=${TERM_OVERRIDE:-$TERM} jest -c ../../node_modules/@atlas/test-config/jest-unit.config.node.ts --rootDir . --silent"},"author":{"name":"Atlas Chain Foundation"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/anza-xyz/kit.git"},"bugs":{"url":"https://github.com/anza-xyz/kit/issues"},"browserslist":["supports bigint and not dead","maintained node versions"],"dependencies":{"@atlaschain/errors":"workspace:*"},"peerDependencies":{"typescript":">=5.3.3"},"engines":{"node":">=20.18.0"},"_id":"@atlaschain/subscribable@3.0.0","homepage":"https://github.com/anza-xyz/kit#readme","_nodeVersion":"22.12.0","_npmVersion":"10.9.0","dist":{"integrity":"sha512-+egM+4lJ5yRLfq0xm2yP53sSlzCLxR3BaJkXhT8VR/92gOot6fwMpH0Ud+bjfK39cNOkR9HYP9KO658Tyq5r8A==","shasum":"ea3a53f5349424cb9acb6a7a976ae82b82a14bbe","tarball":"https://registry.npmjs.org/@atlaschain/subscribable/-/subscribable-3.0.0.tgz","fileCount":5,"unpackedSize":16788,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIB7K4Ze+cNtxFmWnBXl577tY4bdzmRGiLQVaIj05FQGaAiAWHzNiRCPyiAsDU6Caj0Z/yPwzrLtqwcBYqZOmfN96nQ=="}]},"_npmUser":{"name":"atlaschain","email":"jq@atlaschain.org"},"directories":{},"maintainers":[{"name":"atlaschain","email":"jq@atlaschain.org"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/subscribable_3.0.0_1756885195782_0.9938280838400619"},"_hasShrinkwrap":false}},"time":{"created":"2025-09-03T07:39:55.679Z","3.0.0":"2025-09-03T07:39:55.952Z","modified":"2025-09-03T07:39:56.212Z"},"maintainers":[{"name":"atlaschain","email":"jq@atlaschain.org"}],"description":"Helpers for creating subscription-based event emitters","homepage":"https://github.com/anza-xyz/kit#readme","keywords":["blockchain","atlas","web3"],"repository":{"type":"git","url":"git+https://github.com/anza-xyz/kit.git"},"author":{"name":"Atlas Chain Foundation"},"bugs":{"url":"https://github.com/anza-xyz/kit/issues"},"license":"MIT","readme":"[![npm][npm-image]][npm-url]\n[![npm-downloads][npm-downloads-image]][npm-url]\n<br />\n[![code-style-prettier][code-style-prettier-image]][code-style-prettier-url]\n\n[code-style-prettier-image]: https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square\n[code-style-prettier-url]: https://github.com/prettier/prettier\n[npm-downloads-image]: https://img.shields.io/npm/dm/@atlas/subscribable?style=flat\n[npm-image]: https://img.shields.io/npm/v/@atlas/subscribable?style=flat\n[npm-url]: https://www.npmjs.com/package/@atlas/subscribable\n\n# @atlas/subscribable\n\nThis package contains utilities for creating subscription-based event targets. These differ from the `EventTarget` interface in that the method you use to add a listener returns an unsubscribe function. It is primarily intended for internal use &ndash; particularly for those building `RpcSubscriptionChannels` and associated infrastructure.\n\n## Types\n\n### `DataPublisher<TDataByChannelName>`\n\nThis type represents an object with an `on` function that you can call to subscribe to certain data over a named channel.\n\n```ts\nlet dataPublisher: DataPublisher<{ error: AtlasError }>;\ndataPublisher.on('data', handleData); // ERROR. `data` is not a known channel name.\ndataPublisher.on('error', e => {\n    console.error(e);\n}); // OK.\n```\n\n### `TypedEventEmitter<TEventMap>`\n\nThis type allows you to type `addEventListener` and `removeEventListener` so that the call signature of the listener matches the event type given.\n\n```ts\nconst emitter: TypedEventEmitter<{ message: MessageEvent }> = new WebSocket('wss://api.devnet.atlas.com');\nemitter.addEventListener('data', handleData); // ERROR. `data` is not a known event type.\nemitter.addEventListener('message', message => {\n    console.log(message.origin); // OK. `message` is a `MessageEvent` so it has an `origin` property.\n});\n```\n\n### `TypedEventTarget<TEventMap>`\n\nThis type is a superset of `TypedEventEmitter` that allows you to constrain calls to `dispatchEvent`.\n\n```ts\nconst target: TypedEventTarget<{ candyVended: CustomEvent<{ flavour: string }> }> = new EventTarget();\ntarget.dispatchEvent(new CustomEvent('candyVended', { detail: { flavour: 'raspberry' } })); // OK.\ntarget.dispatchEvent(new CustomEvent('candyVended', { detail: { flavor: 'raspberry' } })); // ERROR. Misspelling in detail.\n```\n\n## Functions\n\n### `createAsyncIterableFromDataPublisher({ abortSignal, dataChannelName, dataPublisher, errorChannelName })`\n\nReturns an `AsyncIterable` given a data publisher. The iterable will produce iterators that vend messages published to `dataChannelName` and will throw the first time a message is published to `errorChannelName`. Triggering the abort signal will cause all iterators spawned from this iterator to return once they have published all queued messages.\n\n```ts\nconst iterable = createAsyncIterableFromDataPublisher({\n    abortSignal: AbortSignal.timeout(10_000),\n    dataChannelName: 'message',\n    dataPublisher,\n    errorChannelName: 'error',\n});\ntry {\n    for await (const message of iterable) {\n        console.log('Got message', message);\n    }\n} catch (e) {\n    console.error('An error was published to the error channel', e);\n} finally {\n    console.log(\"It's been 10 seconds; that's enough for now.\");\n}\n```\n\nThings to note:\n\n- If a message is published over a channel before the `AsyncIterator` attached to it has polled for the next result, the message will be queued in memory.\n- Messages only begin to be queued after the first time an iterator begins to poll. Channel messages published before that time will be dropped.\n- If there are messages in the queue and an error occurs, all queued messages will be vended to the iterator before the error is thrown.\n- If there are messages in the queue and the abort signal fires, all queued messages will be vended to the iterator after which it will return.\n- Any new iterators created after the first error is encountered will reject with that error when polled.\n\n### `demultiplexDataPublisher(publisher, sourceChannelName, messageTransformer)`\n\nGiven a channel that carries messages for multiple subscribers on a single channel name, this function returns a new `DataPublisher` that splits them into multiple channel names.\n\nImagine a channel that carries multiple notifications whose destination is contained within the message itself.\n\n```ts\nconst demuxedDataPublisher = demultiplexDataPublisher(channel, 'message', message => {\n    const destinationChannelName = `notification-for:${message.subscriberId}`;\n    return [destinationChannelName, message];\n});\n```\n\nNow you can subscribe to _only_ the messages you are interested in, without having to subscribe to the entire `'message'` channel and filter out the messages that are not for you.\n\n```ts\ndemuxedDataPublisher.on(\n    'notification-for:123',\n    message => {\n        console.log('Got a message for subscriber 123', message);\n    },\n    { signal: AbortSignal.timeout(5_000) },\n);\n```\n\n### `getDataPublisherFromEventEmitter(emitter)`\n\nReturns an object with an `on` function that you can call to subscribe to certain data over a named channel. The `on` function returns an unsubscribe function.\n\n```ts\nconst socketDataPublisher = getDataPublisherFromEventEmitter(new WebSocket('wss://api.devnet.atlas.com'));\nconst unsubscribe = socketDataPublisher.on('message', message => {\n    if (JSON.parse(message.data).id === 42) {\n        console.log('Got response 42');\n        unsubscribe();\n    }\n});\n```\n","readmeFilename":"README.md","_rev":"1-6dd0ae9fb8437c37c98dc7788abb2e84"}