evarcher
v0.3.0
Published
A type-safe, zero-dependency event manager.
Readme
evarcher
A type-safe, zero-dependency event manager.
ESM only | full typescript support
Installation
npm install evarcherRequirements: Node.js >= 20 or modern browsers with ESM support.
Quick Start
Create and export an evarcher instance, then import it wherever you need event management.
Create Evarcher Instance
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,
})Import to Use
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()Core Concepts
Namespace
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
Handler States
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)
API
createEvarcher
<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'))ns
(namespace: string) => EvFnReturns the event manager ev under a specified namespace.
namespace:string- The namespace to manage.
EvFn
EvFn<C extends EventCollection> = <K extends keyof C>(event: K) => OperatorThe event manager.
event: The event name to manage.
Operator
Operator<C extends EventCollection, K extends keyof C>
register
(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)once
(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)unregister
(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` eventenable
(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` eventdisable
(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).
emit
(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().
collect
(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().
Parallel & Serial
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 })Types
EventCollection
type EventCollection = Record<string, EventConfig>A collection of EventConfig objects.
EventConfig
interface EventConfig {
payload: any
result: any
}An event type that includes payload and result.
DefineEvents
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[]
}
}>Handler<E extends EventConfig>
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']
EvarcherError
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
