Browser-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.
- Full
diagnostics_channelAPI compatibility - Channel publish/subscribe mechanism
- TracingChannel for structured tracing
- Integration with AsyncLocalStorage via
bindStore - Zero runtime dependencies
- TypeScript support with full type definitions
- ESM and CommonJS builds
- Comprehensive test coverage
npm install dc-browser
# or
pnpm add dc-browser
# or
yarn add dc-browserFor AsyncLocalStorage integration:
npm install als-browserimport { channel } from 'dc-browser';
const requestChannel = channel('http.request');
// Subscribe to messages
requestChannel.subscribe((message, name) => {
console.log(`Received on ${name}:`, message);
});
// Publish a message
requestChannel.publish({ url: '/api/data', method: 'GET' });TracingChannel provides structured tracing with start, end, asyncStart, asyncEnd, and error events:
import { tracingChannel } from 'dc-browser';
const httpChannel = tracingChannel('http.request');
// Subscribe to events
httpChannel.subscribe({
start: (context) => {
console.log('Request started:', context);
},
end: (context) => {
console.log('Request ended:', context);
},
error: (context) => {
console.error('Request error:', context.error);
}
});
// Trace a synchronous operation
const result = httpChannel.traceSync(() => {
// Your sync code here
return fetchData();
}, { requestId: 'req-123' });
// Trace a promise-based operation
const data = await httpChannel.tracePromise(async () => {
// Your async code here
return await fetch('/api/data');
}, { requestId: 'req-456' });Channels can be bound to AsyncLocalStorage instances to transform events into stored context:
import { channel } from 'dc-browser';
import { AsyncLocalStorage } from 'als-browser';
const requestChannel = channel('http.request');
const requestContext = new AsyncLocalStorage();
// Bind the store to the channel
requestChannel.bindStore(requestContext);
// Now runStores will propagate context to the store
requestChannel.runStores({ requestId: 'req-789' }, () => {
console.log(requestContext.getStore()); // { requestId: 'req-789' }
// Context is available in the callback
doWork();
});
function doWork() {
const context = requestContext.getStore();
console.log('Current request:', context.requestId);
}You can provide a transform function when binding a store to extract/transform the message:
import { channel } from 'dc-browser';
import { AsyncLocalStorage } from 'als-browser';
const requestChannel = channel('http.request');
const userIdStore = new AsyncLocalStorage<string>();
// Extract just the userId from messages
requestChannel.bindStore(
userIdStore,
(message) => message.userId
);
requestChannel.runStores({ userId: 'user-123', url: '/api/data' }, () => {
console.log(userIdStore.getStore()); // 'user-123'
});You can bind multiple AsyncLocalStorage instances to a single channel:
const requestChannel = channel('http.request');
const requestIdStore = new AsyncLocalStorage<string>();
const userIdStore = new AsyncLocalStorage<string>();
requestChannel.bindStore(requestIdStore, (msg) => msg.requestId);
requestChannel.bindStore(userIdStore, (msg) => msg.userId);
requestChannel.runStores({ requestId: 'req-123', userId: 'user-456' }, () => {
console.log(requestIdStore.getStore()); // 'req-123'
console.log(userIdStore.getStore()); // 'user-456'
});const store = new AsyncLocalStorage();
const ch = channel('test');
ch.bindStore(store);
// ... later
ch.unbindStore(store); // Returns true if successfully unboundGet or create a channel by name.
Check if a channel has any subscribers.
Subscribe to a channel.
Unsubscribe from a channel.
Create a TracingChannel for structured tracing.
Add a subscriber to this channel.
Remove a subscriber from this channel.
Publish a message to all subscribers.
Bind an AsyncLocalStorage instance to this channel. When runStores is called, the message (optionally transformed) will be set as the store value.
Unbind an AsyncLocalStorage instance from this channel.
Publish the context and run the function within all bound AsyncLocalStorage contexts.
A TracingChannel manages 5 individual channels:
start: Published before operation beginsend: Published after operation completesasyncStart: Published when async operation starts resolvingasyncEnd: Published when async operation finishes resolvingerror: Published when operation throws/rejects
Subscribe to tracing events.
Unsubscribe from tracing events.
Trace a synchronous operation.
Trace a promise-based operation.
traceCallback<T>(fn: Function, position?: number, context?: any, thisArg?: any, ...args: any[]): any
Trace a callback-based operation.
When using bindStore with als-browser, the context will be preserved through:
- Synchronous code: Full context propagation within the
runStorescallback - Patched async APIs: setTimeout, setInterval, requestAnimationFrame (via als-browser auto-patches)
- Manual propagation: Use
AsyncLocalStorage.bind()orsnapshot()for other async operations
Note: Native Promise await boundaries will lose context unless you:
- Use the patched timer APIs (setTimeout, etc.)
- Manually bind callbacks with
AsyncLocalStorage.bind() - Use the capture/restore functions from
als-browser
import { tracingChannel } from 'dc-browser';
import { AsyncLocalStorage } from 'als-browser';
const httpChannel = tracingChannel('http.request');
const requestStore = new AsyncLocalStorage();
// Bind store to start channel for context propagation
httpChannel.start.bindStore(requestStore);
// Subscribe to events
httpChannel.subscribe({
start: (ctx) => console.log('Started:', ctx.requestId),
end: (ctx) => console.log('Ended:', ctx.requestId, 'result:', ctx.result),
error: (ctx) => console.error('Error:', ctx.requestId, ctx.error)
});
// Make a traced request
const response = httpChannel.traceSync(() => {
// Context is available throughout the operation
console.log('Current request:', requestStore.getStore()?.requestId);
return fetch('/api/data');
}, { requestId: 'req-123', url: '/api/data' });# Run tests
pnpm test
# Build
pnpm buildMIT
This implementation is based on Node.js's diagnostics_channel API from Node.js core.