{"_id":"dc-browser","_rev":"5-71278442035bdcc2d771c863f274c1a0","name":"dc-browser","dist-tags":{"latest":"1.0.4"},"versions":{"1.0.0":{"name":"dc-browser","version":"1.0.0","keywords":["diagnostics_channel","TracingChannel","diagnostics","tracing","browser","polyfill"],"author":{"name":"Stephen Belanger","email":"admin@stephenbelanger.com"},"license":"MIT","_id":"dc-browser@1.0.0","maintainers":[{"name":"qard","email":"admin@stephenbelanger.com"}],"dist":{"shasum":"adc44c43a87ccc3229e0f1d904c469be1f18b4cd","tarball":"https://registry.npmjs.org/dc-browser/-/dc-browser-1.0.0.tgz","fileCount":12,"integrity":"sha512-od44HjJ+IDOQ6zYcMPYmgr24RizX2/dddo7QpoGH0SyOiMouS6JQa8BcntN3e3opqNM7iu/6TtDLM1b7eguBjg==","signatures":[{"sig":"MEYCIQDo9z9BEhMwRjhltvU/mVuOu+DjeoKjRLU0E4BNP36t4AIhAIduJdk+uRaAEFnOKPVtfQpEcFLZ8I8hzscnCRSsU2he","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":57947},"main":"./dist/index.js","types":"./dist/index.d.ts","module":"./dist/index.mjs","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","module":"./dist/index.mjs","require":"./dist/index.js"},"./package.json":"./package.json"},"scripts":{"lint":"eslint src --ext .ts","test":"vitest run","build":"tsup","watch":"tsup --watch","typecheck":"tsc --noEmit"},"_npmUser":{"name":"qard","email":"admin@stephenbelanger.com"},"_npmVersion":"11.6.2","description":"Browser polyfill for Node.js diagnostics_channel","directories":{},"_nodeVersion":"24.12.0","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.0","vitest":"^2.1.9","happy-dom":"^12.0.0","typescript":"5.4.4","@types/node":"^20.10.5","als-browser":"../als-browser"},"_npmOperationalInternal":{"tmp":"tmp/dc-browser_1.0.0_1769041649399_0.7327053141101354","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"dc-browser","version":"1.0.1","keywords":["diagnostics_channel","TracingChannel","diagnostics","tracing","browser","polyfill"],"author":{"name":"Stephen Belanger","email":"admin@stephenbelanger.com"},"license":"MIT","_id":"dc-browser@1.0.1","maintainers":[{"name":"qard","email":"admin@stephenbelanger.com"}],"dist":{"shasum":"d9127e21cdeff421b58d2da55020d92f6fea8859","tarball":"https://registry.npmjs.org/dc-browser/-/dc-browser-1.0.1.tgz","fileCount":7,"integrity":"sha512-VGPWCBFjYwMsfCSVSMQkmALsAaKU0EitS4lkO6vMwEt8IkfYIZa2rLQo6NJp/abiWY0UT4tm33P7jmRTXmqCIQ==","signatures":[{"sig":"MEQCIHkD4tT67FbbPsWWD25wtoFVe3jluxQmULMsEEIzzb25AiB5fWn3wPC/c7++zKCcbjfmGC4Z3R3q/jJP/lNUXkatXA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":36175},"main":"./dist/index.js","types":"./dist/index.d.ts","module":"./dist/index.mjs","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","module":"./dist/index.mjs","require":"./dist/index.js"},"./package.json":"./package.json"},"scripts":{"lint":"eslint src --ext .ts","test":"vitest run","build":"tsup","watch":"tsup --watch","typecheck":"tsc --noEmit","prepublishOnly":"npm run build && npm test"},"_npmUser":{"name":"qard","email":"admin@stephenbelanger.com"},"_npmVersion":"11.6.2","description":"Browser polyfill for Node.js diagnostics_channel","directories":{},"_nodeVersion":"24.12.0","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.0","vitest":"^2.1.9","happy-dom":"^12.0.0","typescript":"5.4.4","@types/node":"^20.10.5","als-browser":"../als-browser"},"_npmOperationalInternal":{"tmp":"tmp/dc-browser_1.0.1_1769042644952_0.5882337376777822","host":"s3://npm-registry-packages-npm-production"}},"1.0.2":{"name":"dc-browser","version":"1.0.2","keywords":["diagnostics_channel","TracingChannel","diagnostics","tracing","browser","polyfill"],"author":{"name":"Stephen Belanger","email":"admin@stephenbelanger.com"},"license":"MIT","_id":"dc-browser@1.0.2","maintainers":[{"name":"qard","email":"admin@stephenbelanger.com"}],"dist":{"shasum":"733274a374f68be33907a7473957ecefa3d5da77","tarball":"https://registry.npmjs.org/dc-browser/-/dc-browser-1.0.2.tgz","fileCount":7,"integrity":"sha512-8p/aQiuvRKQuUw8DV3Zlh8eNN6sIRKrxEg4lQ1+R0ao+LYBoy6D22WJPwby67poS9t+wu0gyYbd88LQCsa6rdQ==","signatures":[{"sig":"MEQCIA7vlsNXNFhCtZ+SaMcJiYbOXkA9s4HbDzsWyUjNZpBIAiBHmPdBiG8P7uVU18FyAeykweVXZwr8JgIW/7w6Ly8rPw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":36167},"main":"./dist/index.js","types":"./dist/index.d.ts","module":"./dist/index.mjs","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","module":"./dist/index.mjs","require":"./dist/index.js"},"./package.json":"./package.json"},"scripts":{"lint":"eslint src --ext .ts","test":"vitest run","build":"tsup","watch":"tsup --watch","typecheck":"tsc --noEmit","prepublishOnly":"npm run build && npm test"},"_npmUser":{"name":"qard","email":"admin@stephenbelanger.com"},"_npmVersion":"11.6.2","description":"Browser polyfill for Node.js diagnostics_channel","directories":{},"_nodeVersion":"24.12.0","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.0","vitest":"^2.1.9","happy-dom":"^12.0.0","typescript":"5.4.4","@types/node":"^20.10.5","als-browser":"^1.0.1"},"_npmOperationalInternal":{"tmp":"tmp/dc-browser_1.0.2_1769042761620_0.5623391326401244","host":"s3://npm-registry-packages-npm-production"}},"1.0.3":{"name":"dc-browser","version":"1.0.3","keywords":["diagnostics_channel","TracingChannel","diagnostics","tracing","browser","polyfill"],"author":{"name":"Stephen Belanger","email":"admin@stephenbelanger.com"},"license":"MIT","_id":"dc-browser@1.0.3","maintainers":[{"name":"qard","email":"admin@stephenbelanger.com"}],"dist":{"shasum":"06ce7f2d35ccfdcf74187ea76abf7d9a66b12564","tarball":"https://registry.npmjs.org/dc-browser/-/dc-browser-1.0.3.tgz","fileCount":7,"integrity":"sha512-B2PJFzWouEPzc+jLo+yTgUXaVz0eZjVsfKX9CnUDzezsluxOKOjllBpw0EJJ1KNJGnNzKpZ4dDYtd+jVp0KRNA==","signatures":[{"sig":"MEUCIQCvGtdKBz53Bil97Kf/LrUVtBenOgU+Q6eymzuiNHW/AgIgbp2khqXVPU1SLGUl8+8aY8G6OMMUsBU81QjSYWPy8GM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":36578},"main":"./dist/index.js","types":"./dist/index.d.ts","module":"./dist/index.mjs","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","module":"./dist/index.mjs","require":"./dist/index.js"},"./package.json":"./package.json"},"gitHead":"84249cebbb938d3248b2480734fff308b6042b3b","scripts":{"lint":"eslint src --ext .ts","test":"vitest run","build":"tsup","watch":"tsup --watch","typecheck":"tsc --noEmit","prepublishOnly":"npm run build && npm test"},"_npmUser":{"name":"qard","email":"admin@stephenbelanger.com"},"_npmVersion":"11.6.2","description":"Browser polyfill for Node.js diagnostics_channel","directories":{},"_nodeVersion":"24.12.0","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.0","jsdom":"^28.1.0","vitest":"^2.1.9","happy-dom":"^12.0.0","typescript":"5.4.4","@types/node":"^20.10.5","als-browser":"^1.0.1","@types/jsdom":"^28.0.0"},"_npmOperationalInternal":{"tmp":"tmp/dc-browser_1.0.3_1772656592148_0.06504540345535448","host":"s3://npm-registry-packages-npm-production"}},"1.0.4":{"name":"dc-browser","version":"1.0.4","description":"Browser polyfill for Node.js diagnostics_channel","author":{"name":"Stephen Belanger","email":"admin@stephenbelanger.com"},"license":"MIT","main":"./dist/index.js","module":"./dist/index.mjs","types":"./dist/index.d.ts","exports":{"./package.json":"./package.json",".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","module":"./dist/index.mjs","require":"./dist/index.js"}},"scripts":{"build":"tsup","lint":"eslint src --ext .ts","prepublishOnly":"npm run build && npm test","test":"vitest run","typecheck":"tsc --noEmit","watch":"tsup --watch"},"devDependencies":{"@types/jsdom":"^28.0.0","@types/node":"^20.10.5","als-browser":"^1.0.1","happy-dom":"^12.0.0","jsdom":"^28.1.0","tsup":"^8.5.0","typescript":"5.4.4","vitest":"^2.1.9"},"keywords":["diagnostics_channel","TracingChannel","diagnostics","tracing","browser","polyfill"],"gitHead":"f13eaae71a0aab036f7c179bb3d3c9833b048f8e","_id":"dc-browser@1.0.4","_nodeVersion":"24.12.0","_npmVersion":"11.6.2","dist":{"integrity":"sha512-7oEtnzNlcE+hr4OvO3GR6Gndgw8BhW+wKOEwMqSleyY7N29jbAxzyW5BaJl7qBCw+6OIxfMWtY0T+6dxq8RWLw==","shasum":"9947e4ed441461517ec19bceeb4a2703249589fe","tarball":"https://registry.npmjs.org/dc-browser/-/dc-browser-1.0.4.tgz","fileCount":7,"unpackedSize":37510,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCCIwUR+9btous0qr+DSIEWAKD60satzIdXM7xNyWac9AIhANsiTCD3YkuAXckUeq3Nx15aUScjaRUR6MvmXZmWv2kG"}]},"_npmUser":{"name":"qard","email":"admin@stephenbelanger.com"},"directories":{},"maintainers":[{"name":"qard","email":"admin@stephenbelanger.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/dc-browser_1.0.4_1774912229598_0.319662027485474"},"_hasShrinkwrap":false}},"time":{"created":"2026-01-22T00:27:29.397Z","modified":"2026-03-30T23:10:29.877Z","1.0.0":"2026-01-22T00:27:29.566Z","1.0.1":"2026-01-22T00:44:05.103Z","1.0.2":"2026-01-22T00:46:01.761Z","1.0.3":"2026-03-04T20:36:32.335Z","1.0.4":"2026-03-30T23:10:29.751Z"},"author":{"name":"Stephen Belanger","email":"admin@stephenbelanger.com"},"license":"MIT","keywords":["diagnostics_channel","TracingChannel","diagnostics","tracing","browser","polyfill"],"description":"Browser polyfill for Node.js diagnostics_channel","maintainers":[{"name":"qard","email":"admin@stephenbelanger.com"}],"readme":"# dc-browser\n\nBrowser-compatible polyfill for Node.js's `diagnostics_channel` API. This package provides the core diagnostics channel functionality that works in browser environments, including integration with AsyncLocalStorage via `als-browser`.\n\n## Features\n\n- Full `diagnostics_channel` API compatibility\n- Channel publish/subscribe mechanism\n- TracingChannel for structured tracing\n- Integration with AsyncLocalStorage via `bindStore`\n- Zero runtime dependencies\n- TypeScript support with full type definitions\n- ESM and CommonJS builds\n- Comprehensive test coverage\n\n## Installation\n\n```bash\nnpm install dc-browser\n# or\npnpm add dc-browser\n# or\nyarn add dc-browser\n```\n\nFor AsyncLocalStorage integration:\n```bash\nnpm install als-browser\n```\n\n## Usage\n\n### Basic Channel\n\n```typescript\nimport { channel } from 'dc-browser';\n\nconst requestChannel = channel('http.request');\n\n// Subscribe to messages\nrequestChannel.subscribe((message, name) => {\n  console.log(`Received on ${name}:`, message);\n});\n\n// Publish a message\nrequestChannel.publish({ url: '/api/data', method: 'GET' });\n```\n\n### TracingChannel\n\nTracingChannel provides structured tracing with start, end, asyncStart, asyncEnd, and error events:\n\n```typescript\nimport { tracingChannel } from 'dc-browser';\n\nconst httpChannel = tracingChannel('http.request');\n\n// Subscribe to events\nhttpChannel.subscribe({\n  start: (context) => {\n    console.log('Request started:', context);\n  },\n  end: (context) => {\n    console.log('Request ended:', context);\n  },\n  error: (context) => {\n    console.error('Request error:', context.error);\n  }\n});\n\n// Trace a synchronous operation\nconst result = httpChannel.traceSync(() => {\n  // Your sync code here\n  return fetchData();\n}, { requestId: 'req-123' });\n\n// Trace a promise-based operation\nconst data = await httpChannel.tracePromise(async () => {\n  // Your async code here\n  return await fetch('/api/data');\n}, { requestId: 'req-456' });\n```\n\n### AsyncLocalStorage Integration\n\nChannels can be bound to AsyncLocalStorage instances to transform events into stored context:\n\n```typescript\nimport { channel } from 'dc-browser';\nimport { AsyncLocalStorage } from 'als-browser';\n\nconst requestChannel = channel('http.request');\nconst requestContext = new AsyncLocalStorage();\n\n// Bind the store to the channel\nrequestChannel.bindStore(requestContext);\n\n// Now runStores will propagate context to the store\nrequestChannel.runStores({ requestId: 'req-789' }, () => {\n  console.log(requestContext.getStore()); // { requestId: 'req-789' }\n\n  // Context is available in the callback\n  doWork();\n});\n\nfunction doWork() {\n  const context = requestContext.getStore();\n  console.log('Current request:', context.requestId);\n}\n```\n\n### Transform Function\n\nYou can provide a transform function when binding a store to extract/transform the message:\n\n```typescript\nimport { channel } from 'dc-browser';\nimport { AsyncLocalStorage } from 'als-browser';\n\nconst requestChannel = channel('http.request');\nconst userIdStore = new AsyncLocalStorage<string>();\n\n// Extract just the userId from messages\nrequestChannel.bindStore(\n  userIdStore,\n  (message) => message.userId\n);\n\nrequestChannel.runStores({ userId: 'user-123', url: '/api/data' }, () => {\n  console.log(userIdStore.getStore()); // 'user-123'\n});\n```\n\n### Multiple Stores\n\nYou can bind multiple AsyncLocalStorage instances to a single channel:\n\n```typescript\nconst requestChannel = channel('http.request');\nconst requestIdStore = new AsyncLocalStorage<string>();\nconst userIdStore = new AsyncLocalStorage<string>();\n\nrequestChannel.bindStore(requestIdStore, (msg) => msg.requestId);\nrequestChannel.bindStore(userIdStore, (msg) => msg.userId);\n\nrequestChannel.runStores({ requestId: 'req-123', userId: 'user-456' }, () => {\n  console.log(requestIdStore.getStore()); // 'req-123'\n  console.log(userIdStore.getStore());    // 'user-456'\n});\n```\n\n### Unbinding Stores\n\n```typescript\nconst store = new AsyncLocalStorage();\nconst ch = channel('test');\n\nch.bindStore(store);\n// ... later\nch.unbindStore(store); // Returns true if successfully unbound\n```\n\n## API\n\n### Module Functions\n\n#### `channel(name: string | symbol): Channel`\n\nGet or create a channel by name.\n\n#### `hasSubscribers(name: string | symbol): boolean`\n\nCheck if a channel has any subscribers.\n\n#### `subscribe(name: string | symbol, callback: Function): void`\n\nSubscribe to a channel.\n\n#### `unsubscribe(name: string | symbol, callback: Function): boolean`\n\nUnsubscribe from a channel.\n\n#### `tracingChannel(name: string): TracingChannel`\n\nCreate a TracingChannel for structured tracing.\n\n### Channel Class\n\n#### `subscribe(callback: (message: any, name: string) => void): void`\n\nAdd a subscriber to this channel.\n\n#### `unsubscribe(callback: Function): boolean`\n\nRemove a subscriber from this channel.\n\n#### `publish(message: any): void`\n\nPublish a message to all subscribers.\n\n#### `bindStore(store: AsyncLocalStorage, transform?: (message: any) => any): void`\n\nBind an AsyncLocalStorage instance to this channel. When `runStores` is called, the message (optionally transformed) will be set as the store value.\n\n#### `unbindStore(store: AsyncLocalStorage): boolean`\n\nUnbind an AsyncLocalStorage instance from this channel.\n\n#### `runStores(context: any, fn: () => any): any`\n\nPublish the context and run the function within all bound AsyncLocalStorage contexts.\n\n### TracingChannel Class\n\nA TracingChannel manages 5 individual channels:\n- `start`: Published before operation begins\n- `end`: Published after operation completes\n- `asyncStart`: Published when async operation starts resolving\n- `asyncEnd`: Published when async operation finishes resolving\n- `error`: Published when operation throws/rejects\n\n#### `subscribe(handlers: ChannelHandlers): void`\n\nSubscribe to tracing events.\n\n#### `unsubscribe(handlers: ChannelHandlers): boolean`\n\nUnsubscribe from tracing events.\n\n#### `traceSync<T>(fn: Function, context?: any, thisArg?: any, ...args: any[]): T`\n\nTrace a synchronous operation.\n\n#### `tracePromise<T>(fn: Function, context?: any, thisArg?: any, ...args: any[]): Promise<T>`\n\nTrace a promise-based operation.\n\n#### `traceCallback<T>(fn: Function, position?: number, context?: any, thisArg?: any, ...args: any[]): any`\n\nTrace a callback-based operation.\n\n## Integration with als-browser\n\nWhen using `bindStore` with `als-browser`, the context will be preserved through:\n\n1. **Synchronous code**: Full context propagation within the `runStores` callback\n2. **Patched async APIs**: setTimeout, setInterval, requestAnimationFrame (via als-browser auto-patches)\n3. **Manual propagation**: Use `AsyncLocalStorage.bind()` or `snapshot()` for other async operations\n\n**Note**: Native Promise await boundaries will lose context unless you:\n- Use the patched timer APIs (setTimeout, etc.)\n- Manually bind callbacks with `AsyncLocalStorage.bind()`\n- Use the capture/restore functions from `als-browser`\n\n## Example: Request Tracing with Context\n\n```typescript\nimport { tracingChannel } from 'dc-browser';\nimport { AsyncLocalStorage } from 'als-browser';\n\nconst httpChannel = tracingChannel('http.request');\nconst requestStore = new AsyncLocalStorage();\n\n// Bind store to start channel for context propagation\nhttpChannel.start.bindStore(requestStore);\n\n// Subscribe to events\nhttpChannel.subscribe({\n  start: (ctx) => console.log('Started:', ctx.requestId),\n  end: (ctx) => console.log('Ended:', ctx.requestId, 'result:', ctx.result),\n  error: (ctx) => console.error('Error:', ctx.requestId, ctx.error)\n});\n\n// Make a traced request\nconst response = httpChannel.traceSync(() => {\n  // Context is available throughout the operation\n  console.log('Current request:', requestStore.getStore()?.requestId);\n\n  return fetch('/api/data');\n}, { requestId: 'req-123', url: '/api/data' });\n```\n\n## Testing\n\n```bash\n# Run tests\npnpm test\n\n# Build\npnpm build\n```\n\n## License\n\nMIT\n\n## Credits\n\nThis implementation is based on Node.js's `diagnostics_channel` API from Node.js core.\n","readmeFilename":"README.md"}