A type-safe, zero-dependency event manager.
ESM only | full typescript support
npm install evarcherRequirements: Node.js >= 20 or modern browsers with ESM support.
Create and export an evarcher instance, then import it wherever you need event management.
file event.ts
import type { DefineEvents } from 'evarcher'
import { createEvarcher } from 'evarcher'
// Define your custom events via the DefineEvents helper
export type MyEvents = DefineEvents<{
open: {
payload: string
result: void
}
'send:pos': {
payload: {
x: number
y: number
}
result: number[]
}
'send:message': {
payload: string
result: void
}
'report:active': {
payload: boolean
result: {
target: string
message: string
}
}
}>
// Export ns & ev
export const { ns, ev } = createEvarcher<MyEvents>({
// Enable handlers when registering
defaultEnabled: true,
})file main.ts
import { Handler } from 'evarcher'
import { ev, ns } from './event'
import type { MyEvents } from './event'
const myns = ns('myns')
// Register a handler and get its id
const openId = myns('open').register(() => console.log('opened')).id
const raId = myns('report:active')
.register((p) => ({ target: 'rock', message: 'Activated' }))
.id
// Unregister handlers via id
myns('open').unregister(openId)
myns('report:active').unregister(raId)
// Use the same handler reference to enable after registering
const sendPos: Handler<MyEvents['send:pos']> = (p) => {
console.log(`Current Position: (${p.x}, ${p.y})`)
return [p.x, p.y]
}
const sendPosEv = myns('send:pos')
// Call `enable()/disable()` to enable/disable a handler after registering
sendPosEv.register(sendPos).disable()
// Enable it later when needed
sendPosEv.enable(sendPos)
// Register a run-once handler
myns('send:message').once((p) => console.log(`Message: ${p}`))
// Emit an event
myns('open').emit()
// Emit an event with data
myns('send:message').emit('Success!')
// Collect the results
const result = myns('report:active').collect(true)
// ev is used the same way as ns, but events will be
// managed by the default namespace.
const openEv = ev('open')
openEv.register(() => console.log('opened'))
openEv.emit()Namespaces help organize events in multi-layered projects by creating isolated event scopes. This prevents naming conflicts and improves code organization.
// Different modules can use the same event names
const authNs = ns('auth')
const uiNs = ns('ui')
authNs('login').register(handleAuthLogin)
uiNs('login').register(handleUILogin) // No conflict!When to use:
- Use
evfor simple cases (uses the default namespace) - Use
nswhen you need event isolation or logical grouping
Handlers can be in two states:
- Enabled: Will be executed when the event is emitted
- Disabled: Registered but won't be executed (useful for temporary muting)
<C extends EventCollection>(option?: EvarcherOption) => EvarcherReturnCreate an evarcher instance to start event management.
EvarcherOption
defaultNamespace:string- The namespace to use forev. Default"DEFAULT_NAMESPACE"defaultEnabled:boolean- Iftrue, handlers are enabled immediately upon registration. Default:falsehandleError:(error: EvarcherError) => void- The custom error handler that will be invoked when an error occurs during event emission or collectiontrace:boolean- Enables debug tracing logs
EvarcherReturn
DEFAULT_NAMESPACE:readonly string- The default namespace name used byev.ns: The namespace manager.ev: The event manager in the default namespace. Equal tons(DEFAULT_NAMESPACE).
Example with custom default namespace:
const { ns, ev, DEFAULT_NAMESPACE } = createEvarcher<MyEvents>({
defaultNamespace: 'app',
defaultEnabled: true,
})
console.log(DEFAULT_NAMESPACE) // Output: 'app'
// ev uses 'app' namespace
ev('open').register(() => console.log('opened'))
// Equivalent to:
ns('app')('open').register(() => console.log('opened'))
// or
ns(DEFAULT_NAMESPACE)('open').register(() => console.log('opened'))(namespace: string) => EvFnReturns the event manager ev under a specified namespace.
namespace:string- The namespace to manage.
EvFn<C extends EventCollection> = <K extends keyof C>(event: K) => OperatorThe event manager.
event: The event name to manage.
Operator<C extends EventCollection, K extends keyof C>
(handler: Handler) => RegisterReturnRegister a handler for an event. EvarcherOption.defaultEnabled controls whether the handler is enabled by default.
Returns a RegisterReturn object for immediate state control:
RegisterReturn
id:string- The id of the registered handlerenable:() => void- Enable the handler immediatelydisable:() => void- Disable the handler immediately
Example:
// Register and immediately disable
const saveReg = ev('save').register(handleSave)
saveReg.disable()
// ...
// Later: enable it
saveReg.enable()
// unregister via id
ev('save').unregister(saveReg.id)(handler: Handler) => RegisterReturnRegister a handler that runs only once, then automatically unregisters itself. EvarcherOption.defaultEnabled controls whether the handler is enabled by default. You can also immediately enable/disable via RegisterReturn.
Example:
// Handler runs once then auto-removes
ev('init').once(() => console.log('Initialized!')).enable()
ev('init').emit() // Output: Initialized!
ev('init').emit() // No output (already removed)(handler: Handler) => void
(id: string) => void
() => voidUnregister and remove a handler for the event. Match the handler by function reference or the id obtained from register / once.
const handler = (p) => { ... }
const handlerId = ev('clear').register(handler).id
// unregister via function reference
ev('clear').unregister(handler)
// unregister via id
ev('clear').unregister(handlerId)The handler parameter is optional. When omitted, all handlers of the event will be removed.
ev('clear').unregister() // remove all handlers of the `clear` event(handler: Handler) => void
(id: string) => void
() => voidEnable a handler for the event. Accepts the same parameter types as unregister (handler function, id, or none for all).
const handler = (p) => { ... }
const handlerId = ev('turn:on').register(handler).id
// enable via function reference
ev('turn:on').enable(handler)
// enable via id
ev('turn:on').enable(handlerId)The handler parameter is optional. When omitted, all handlers of the event will be enabled.
ev('turn:on').enable() // enable all handlers of the `turn:on` event(handler: Handler) => void
(id: string) => void
() => voidDisable a handler for the event. Accepts the same parameter types as enable (handler function, id, or none for all).
(payload: C[K]['payload']) => voidEmit an event with optional data. This calls all enabled handlers synchronously in registration order.
ev('run').emit() // Call all enabled handlers of the `run` event
ev('report:pos').emit({ x: 1, y: 2 }) // Pass data to all enabled handlersNote: Handlers are executed synchronously. Async handlers will start execution but won't be awaited by
emit().
(payload: C[K]['payload']) => Array<C[K]['result']>Emit an event with optional data, then collect all results from called handlers into an array. This calls all enabled handlers synchronously in registration order.
const results = ev('run').collect() // Call all enabled handlers of the `run` event, then collect all results to an array
const resultsWithData = ev('report:pos').collect({ x: 1, y: 2 }) // Pass data to all enabled handlers and collect resultsNote: Handlers are executed synchronously. Async handlers will start execution but won't be awaited by
collect().
Both parallel and serial include emit and collect functions. They call all handlers asynchronously.
ev('run').parallel.emit()
ev('run').serial.emit()
ev('run').parallel.collect({ x: 1, y: 2 })
ev('run').serial.collect({ x: 1, y: 2 })type EventCollection = Record<string, EventConfig>A collection of EventConfig objects.
interface EventConfig {
payload: any
result: any
}An event type that includes payload and result.
The helper for defining a type-safe collection of events.
type MyEvents = DefineEvents<{
open: {
payload: string
result: void
}
pos: {
payload: {
x: number
y: number
}
result: number[]
}
}>A function that handles event data of type E['payload'] and returns data of type E['result'].
- For events with data:
(payload: E['payload']) => E['result'] - For events without data:
() => E['result']or(payload?: undefined) => E['result']
An error object containing target information and error message.
EvarcherError:
target:EvErrorTarget- Target where the error occurredmessage:string- Error description message
EvErrorTarget:
namespace:string- Namespace identifierevent:string- Event nameunitId:HandlerUnit['id']- Unique identifier of the HandlerUnit