@gottheflag/lifecycle API Reference · 0.1.0

Reference

Lifecycle

Typed lifetime, event, and resource management for JavaScript and TypeScript.

Package@gottheflag/lifecycle
Version0.1.0
ModulesESM + CJS
LicenseApache-2.0

Start

Installation

pnpmpnpm add @gottheflag/lifecycle
npmnpm install @gottheflag/lifecycle
yarnyarn add @gottheflag/lifecycle
bunbun add @gottheflag/lifecycle

Start

Requirements

Node.js18.18.0 or newer.
TypeScript5.2 or newer for TypeScript consumers.
SuppressedErrorRuntimes without a native implementation need a polyfill only when both a using block body and its disposal throw.

Package surface

Exports

LifecycleClass

Synchronous lifetime, ownership, cleanup, child, and event management.

AsyncLifecycleClass

Asynchronous lifetime management with awaited teardown.

LifecycleOptionsType

Constructor options accepted by Lifecycle.

LifecycleSignalType

Environment-agnostic structural contract for an abort-compatible signal.

NoEventsType

Empty event map used by lifecycles with no typed events.

CleanupType

Synchronous cleanup callback.

AsyncCleanupType

Synchronous or asynchronous cleanup callback.

DestroyableType

Resource exposing synchronous destroy().

AsyncDestroyableType

Resource exposing sync or async destroy().

EventArgumentsType

Conditional argument tuple used by typed event emission.

EventListenerType

Typed synchronous event listener.

Class

Lifecycle<TEvents>

sync

Owns everything associated with one synchronous lifetime: deferred cleanup, destroyable resources, child lifecycles, and typed event listeners.

class Lifecycle<TEvents extends object = NoEvents>

constructor

Lifecycle
new Lifecycle<TEvents>(options?: LifecycleOptions)

Creates a synchronous lifecycle. When an abort-compatible signal is supplied, aborting that signal destroys the lifecycle. An already-aborted signal creates an already-destroyed lifecycle.

destroyed

getter
get destroyed(): boolean

Indicates whether destruction has started. Once true, it never becomes false.

defer()

cleanup
defer<TCleanup extends Cleanup>(cleanup: TCleanup): Cleanup

Registers synchronous cleanup work. Returns an exactly-once cleanup function that can execute the registered cleanup early and remove it from lifecycle teardown.

  • Async cleanup callbacks are rejected by the type system.
  • Registration after destruction executes immediately.
  • Repeated execution is ignored.

own()

resource
own<TResource extends Destroyable>(resource: TResource): TResource

Registers a synchronous destroyable resource with the lifecycle and returns the same resource unchanged.

  • The resource must expose synchronous destroy(): void.
  • Owned resources participate in normal reverse-order teardown.
  • Ownership after destruction destroys the resource immediately.

child()

hierarchy
child<TChildEvents extends object = NoEvents>(): Lifecycle<TChildEvents>

Creates an independently typed child lifecycle owned by the parent. Destroying the child early detaches it from the parent. Destroying the parent destroys every still-owned child.

destroy()

termination
destroy(): void

Ends the lifecycle exactly once. Event delivery stops first, then active cleanup entries run in reverse registration order. Every cleanup is attempted even when another cleanup throws.

Symbol.dispose

disposal
[Symbol.dispose](): void

Delegates to destroy() and makes the lifecycle compatible with synchronous explicit resource management.

Class

AsyncLifecycle<TEvents>

async

Asynchronous counterpart to Lifecycle. Cleanup entries may return promises and are awaited sequentially in reverse registration order. Event dispatch remains synchronous.

class AsyncLifecycle<TEvents extends object = NoEvents>

destroyed

getter
get destroyed(): boolean

Indicates whether asynchronous destruction has started. It may be true while cleanup work is still being awaited.

defer()

cleanup
defer(cleanup: AsyncCleanup): AsyncCleanup

Registers synchronous or asynchronous cleanup work and returns an exactly-once cleanup function that may be awaited when executed early.

  • Registration after destruction throws.
  • Cleanup is awaited during destruction.
  • Repeated execution is ignored.

own()

resource
own<TResource extends AsyncDestroyable>(resource: TResource): TResource

Owns a resource whose destroy() method may complete synchronously or return a promise-like value.

child()

hierarchy
child<TChildEvents extends object = NoEvents>(): AsyncLifecycle<TChildEvents>

Creates an independently typed asynchronous child owned by the parent. Early child destruction detaches it from parent teardown.

destroy()

termination
destroy(): Promise<void>

Starts teardown once and returns the destruction promise. Cleanup entries are awaited sequentially in reverse registration order. Concurrent calls receive the same promise.

Symbol.asyncDispose

disposal
[Symbol.asyncDispose](): Promise<void>

Delegates to destroy() and makes the lifecycle compatible with asynchronous explicit resource management.

Shared API

Typed events

sync dispatch

Both lifecycle classes expose the same typed synchronous event API. Event spaces belong to each lifecycle independently and do not inherit or bubble between parent and child lifecycles.

on()

event
on<TKey extends keyof TEvents>(type: TKey, listener: EventListener<TEvents[TKey]>): Cleanup

Registers a synchronous listener for an event key. Returns an exactly-once unsubscribe function.

once()

event
once<TKey extends keyof TEvents>(type: TKey, listener: EventListener<TEvents[TKey]>): Cleanup

Registers a synchronous listener that unsubscribes before its first invocation. Returns an unsubscribe function for cancellation before emission.

emit()

event
emit<TKey extends keyof TEvents>(type: TKey, ...args: EventArguments<TEvents[TKey]>): void

Synchronously dispatches an event to the listeners registered on that lifecycle. Void events require no payload. Emission after destruction is ignored.

clear()

event
clear(): void
clear<TKey extends keyof TEvents>(type: TKey): void

Removes every event listener or every listener for one event key. It does not run deferred cleanup, destroy resources, or destroy child lifecycles.

Contract

Abort signal

interface LifecycleOptions { signal?: LifecycleSignal }

Lifecycle accepts an abort-compatible signal. Aborting it triggers synchronous destruction. Manual destruction removes the abort listener. AsyncLifecycle currently has no signal constructor option.

Contract

Child lifecycles

Children are ownership relationships, not event inheritance. A child may have a different event map. Parent destruction destroys live children; early child destruction detaches that child from its parent.

Contract

Cleanup order

Active cleanup entries are executed in reverse registration order. This applies to deferred callbacks, owned resources, and children because each is represented in the same teardown sequence.

Contract

Errors

Destruction attempts every active cleanup even when earlier cleanup fails. Multiple failures are reported through AggregateError. Event emission also completes listener delivery before collected listener failures are reported.

Contract

Late registration

Lifecycle

defer() after destruction executes immediately. Consequently, late own() destroys immediately and late child() returns an already-destroyed child. New event subscriptions are rejected.

AsyncLifecycle

New cleanup, ownership, child, and event subscriptions are rejected after destruction starts because asynchronous cleanup cannot be completed synchronously at registration time.

Reference

Exported types

Cleanup() => void
AsyncCleanup() => void | PromiseLike<void>
Destroyable{ destroy(): void }
AsyncDestroyable{ destroy(): void | PromiseLike<void> }
LifecycleOptions{ signal?: LifecycleSignal }
LifecycleSignal{ readonly aborted: boolean; addEventListener(...); removeEventListener(...) }
NoEventsRecord<never, never>
EventArguments<TPayload>void payload → []; otherwise [payload]
EventListener<TPayload>typed synchronous listener