topic-dispatch
v3.0.0
Published
a very simplistic amqp-style topic-based dispatcher
Maintainers
Readme
Topic Dispatch
A way to bind function(s) to a topic using AMQP style matching rules. Not optimized for situations with a lot of "topic churn".
It is probably not for you. Check out Jim Cowart's monologue or postal instead.
Version 3.0 - Breaking Changes
- ESM-only: CommonJS is no longer supported. Use
importinstead ofrequire() - Node.js 22+: Minimum required Node.js version is 22
- TypeScript: Full TypeScript support with type definitions included
- Zero dependencies: No external runtime dependencies
Installation
npm install topic-dispatchRequirements:
- Node.js >= 22
- ESM project (set
"type": "module"in your package.json)
API
JavaScript
import dispatcher from 'topic-dispatch'
const topics = dispatcher()
topics.on('*', (event, topic) => {}) // catch all
topics.on('#', (event, topic) => {}) // catch all
topics.on('connected', (event, topic) => {}) // catch 'connected'
topics.on('account.*', (event, topic) => {}) // catch anything starting with 'account'
topics.on('*.created', (event, topic) => {}) // catch anything ending in .created'
topics.on('account.#', (event, topic) => {}) // catch any two segment topic beginning with `account`
topics.on('#.created', (event, topic) => {}) // catch any two segment topic ending with `created`
topics.once('ready', (event, topic) => {}) // removes itself after one event is received
const handler = () => {}
topics.on('added', handler)
topics.removeListener('added', handler) // removes specific handler from a topic
topics.on('added', () => {})
topics.on('added', () => {})
topics.on('added', () => {})
topics.removeAllListeners('added') // removes all handlers from topic
topics.emit('account.created', {}) // will call all matching handlers
topics.removeAllListeners() // erase all bindingsTypeScript
import dispatcher, { Dispatcher, Handler, Subscription } from 'topic-dispatch'
const topics: Dispatcher = dispatcher()
// Typed handler
const handler: Handler<{ id: number; name: string }> = (event, topic) => {
console.log(`Event on ${topic}:`, event.id, event.name)
}
const subscription: Subscription = topics.on('user.created', handler)
// Subscription with promise-like API
subscription
.then(result => console.log('Handler result:', result))
.catch(error => console.error('Handler error:', error))
// Emit with type safety
await topics.emit('user.created', { id: 1, name: 'Alice' })
// Unsubscribe
subscription.off()Pattern Matching
The library supports AMQP-style topic matching:
*- matches any single segment (e.g.,account.*matchesaccount.createdbut notaccount.user.created)#- matches any single segment (same as*)- Exact match -
account.createdonly matchesaccount.created
Promise-based Emit
The emit() method returns a Promise that resolves with an array of all handler results:
const topics = dispatcher()
topics.on('calculate', () => 10)
topics.on('calculate', () => 20)
topics.on('calculate', () => Promise.resolve(30))
const results = await topics.emit('calculate', {})
console.log(results) // [10, 20, 30]Error Handling
Exceptions in handlers are caught and logged, preventing one handler from breaking others:
topics.on('event', () => { throw new Error('oops') })
topics.on('event', () => { console.log('still runs!') })
topics.emit('event', {}) // logs error, but second handler still executesAPI Reference
dispatcher(): Dispatcher
Creates a new dispatcher instance.
Dispatcher
on(pattern: string, handler: Handler): Subscription
Subscribe to events matching the pattern. Returns a subscription object.
once(pattern: string, handler: Handler): void
Subscribe to events matching the pattern, automatically unsubscribing after the first event.
emit(topic: string, event: any): Promise<any[]>
Emit an event to all matching handlers. Returns a promise that resolves with all handler results.
removeListener(pattern: string, handler: Handler): void
Remove a specific handler from a pattern.
removeAllListeners(pattern?: string): void
Remove all handlers from a pattern, or all handlers from all patterns if no pattern is specified.
isQuiet(): boolean
Returns true if there are no active subscriptions.
Subscription
off(): void
Unsubscribe the handler.
remove(): void
Alias for off().
then(handler: (result: any) => void): Subscription
Add a handler for the result of the subscription handler.
catch(handler: (error: any) => void): Subscription
Add an error handler for the subscription handler.
Development
This project includes a Makefile for common development tasks:
# Install dependencies
make install
# Run all checks (lint, typecheck, build, test)
make all
# Individual tasks
make lint # Check code style
make lint-fix # Fix code style issues
make typecheck # Run TypeScript type checking
make build # Compile TypeScript to JavaScript
make test # Run tests
make test-watch # Run tests in watch mode
make test-coverage # Run tests with coverage report
# Release management
make release # Create a new release with standard-version
make release-minor # Create a minor release (1.2.0 -> 1.3.0)
make release-major # Create a major release (1.2.0 -> 2.0.0)
make release-patch # Create a patch release (1.2.0 -> 1.2.1)
# Other
make clean # Remove generated files
make pre-commit # Run checks before committing
make help # Show all available targetsQuick Start for Contributors
# Set up development environment
make dev
# Make changes, then run checks
make all
# Create a release
make releaseLicense
MIT
