0Z4C0
// navigation
// @ozaco/std

Event

A tiny, fully typed event emitter with effect-native subscription helpers.

import from@ozaco/std/event

createEvent returns an EventEmitter typed by a map of event name → argument tuple, so on, emit, once, and friends are all checked against that map. Listeners run synchronously via emit, or are awaited together via emitAsync; on/once return an unsubscribe function.

The emitter is a plain object tagged with the registered std:event symbol on its _t field, so isEventEmitter can narrow any value without instanceof.

For effect code, the module bridges emitters into the effect world: useEvent turns an event into a Stream, onEvent runs an operation per event until halted, useEventOnce awaits a single occurrence, and useBufferedEvent subscribes eagerly into a queue so no events are dropped between reads. Each helper subscribes on start and unsubscribes on teardown.

01

Constructors

Create an emitter.

fn

createEvent

Create a typed `EventEmitter`.

signature
function createEvent<T extends EventEmitter.Map = EmptyType>(): EventEmitter<T>
// returns A fresh `EventEmitter<T>` with no listeners.
example
import { createEvent } from '@ozaco/std/event'

interface Events {
  message: [text: string]
  close: [code: number]
}

const bus = createEvent<Events>()

const off = bus.on('message', text => console.log(text))
bus.emit('message', 'hello') //=> logs 'hello'
off() // unsubscribe
02

Guards

Narrow unknown values.

fn

isEventEmitter

Narrow a value to an `EventEmitter` via its `std:event` tag.

signature
function isEventEmitter<T extends EventEmitter.Map = any>(
  value: unknown,
): value is EventEmitter<T>
// returns Whether `value` is an object tagged with the `std:event` symbol on `_t`.
03

Effect integration

Consume emitter events from effect operations.

fn

useEvent

Bridge an emitter event into an effect `Stream`.

signature
function useEvent<
  T extends EventEmitter<any>,
  K extends keyof EventEmitter.Infer<T>,
>(target: T, name: K): Stream<EventEmitter.InferType<T, K>, never>

Subscribes to name on target when the stream is opened and unsubscribes when the surrounding scope tears down. Values are delivered via a signal, so an event fired while no reader is waiting is dropped — use useBufferedEvent when you need buffering.

example
import { useEvent } from '@ozaco/std/event'
import { each } from 'std:effect'

for (const [text] of yield* each(useEvent(bus, 'message'))) {
  yield* handle(text)
  yield* each.next()
}
fn

onEvent

Run an operation for every occurrence of an event until halted.

signature
function onEvent<
  T extends EventEmitter<any>,
  K extends keyof EventEmitter.Infer<T>,
>(
  target: T,
  name: K,
  handler: (...args: EventEmitter.InferType<T, K>) => Operation<void>,
): Operation<void>

A convenience wrapper over useEvent: iterates the event stream, invoking handler with the emitted arguments for each event. Typically spawned so it runs in the background.

example
import { onEvent } from '@ozaco/std/event'
import { spawn } from 'std:effect'

yield* spawn(() =>
  onEvent(bus, 'close', function* (code) {
    yield* shutdown(code)
  }),
)
fn

useEventOnce

Await the next occurrence of an event and resolve with its arguments.

signature
function useEventOnce<
  T extends EventEmitter<any>,
  K extends keyof EventEmitter.Infer<T>,
>(target: T, name: K): Operation<EventEmitter.InferType<T, K>>
// returns The argument tuple of the first event fired after subscribing.
example
import { useEventOnce } from '@ozaco/std/event'

const [code] = yield* useEventOnce(bus, 'close')
fn

useBufferedEvent

Subscribe eagerly into a queue so no events are missed between reads.

signature
function useBufferedEvent<
  T extends EventEmitter<any>,
  K extends keyof EventEmitter.Infer<T>,
>(
  target: T,
  name: K,
): Operation<Subscription<EventEmitter.InferType<T, K>, never>>

Unlike useEvent, this attaches its listener immediately and buffers every event into a queue, so occurrences that arrive between calls to next() are retained rather than dropped. The listener is removed on teardown.

example
import { useBufferedEvent } from '@ozaco/std/event'

const sub = yield* useBufferedEvent(bus, 'message')
const first = yield* sub.next()
const second = yield* sub.next() // buffered, even if fired earlier
04

Types

iface

EventEmitter

A typed event emitter keyed by an event → argument-tuple map.

signature
interface EventEmitter<T extends EventEmitter.Map = EmptyType> {
  _t: typeof EVENT

  on<K>(name: K, listener: EventEmitter.Listener<T[K]>): () => void
  once<K>(name: K, listener: EventEmitter.Listener<T[K]>): () => void
  off<K>(name: K, listener?: EventEmitter.Listener<T[K]>): void
  emit<K>(name: K, ...args: T[K]): void
  emitAsync<K>(name: K, ...args: T[K]): Promise<void>
  clear(): void
  listenerCount<K>(name: K): number
}

on and once return an unsubscribe function; off removes a specific listener (or all listeners for name when omitted). emit calls listeners synchronously; emitAsync awaits any promises they return via Promise.all. clear drops every listener.

The EventEmitter namespace also exposes:

  • MapRecord<string, unknown[]>, the event → args mapping.
  • Listener<T>(...args: T) => void | Promise<void>.
  • Infer<T> — extract the event map from an EventEmitter type.
  • InferType<T, K> — the argument tuple for event K of emitter T.