npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

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 evarcher

Requirements: 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 ev for simple cases (uses the default namespace)
  • Use ns when 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) => EvarcherReturn

Create an evarcher instance to start event management.

EvarcherOption

  • defaultNamespace: string - The namespace to use for ev. Default "DEFAULT_NAMESPACE"
  • defaultEnabled: boolean - If true, handlers are enabled immediately upon registration. Default: false
  • handleError: (error: EvarcherError) => void - The custom error handler that will be invoked when an error occurs during event emission or collection
  • trace: boolean - Enables debug tracing logs

EvarcherReturn

  • DEFAULT_NAMESPACE: readonly string - The default namespace name used by ev.
  • ns: The namespace manager.
  • ev: The event manager in the default namespace. Equal to ns(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) => EvFn

Returns 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) => Operator

The event manager.

  • event: The event name to manage.

Operator

Operator

Operator<C extends EventCollection, K extends keyof C>

register

(handler: Handler) => RegisterReturn

Register 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 handler
  • enable: () => void - Enable the handler immediately
  • disable: () => 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) => RegisterReturn

Register 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
() => void

Unregister 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

enable

(handler: Handler) => void
(id: string) => void
() => void

Enable 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

disable

(handler: Handler) => void
(id: string) => void
() => void

Disable a handler for the event. Accepts the same parameter types as enable (handler function, id, or none for all).

emit

(payload: C[K]['payload']) => void

Emit 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 handlers

Note: 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 results

Note: 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 occurred
  • message: string - Error description message

EvErrorTarget:

  • namespace: string - Namespace identifier
  • event: string - Event name
  • unitId: HandlerUnit['id'] - Unique identifier of the HandlerUnit

License

MIT