aieventjs 0.5.9 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -6
- package/README_ZHTW.md +9 -7
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +33 -19
- package/dist/index.d.ts +33 -19
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/llms-full.txt +37 -11
- package/package.json +9 -4
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Small, strict, typed event emitter with ai*js lifecycle conventions: `on()` returns unsubscribe, `once` is built in, `AbortSignal` is first-class, wildcard handlers are supported, and `dispose()` is idempotent.
|
|
4
4
|
|
|
5
|
-
> **Status: 0.
|
|
5
|
+
> **Status: 0.6.0 - stable 1.0-track surface.** The root entry is the public API.
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
@@ -39,19 +39,21 @@ events.dispose();
|
|
|
39
39
|
- `createEmitter<Events>(options?)` creates a typed emitter.
|
|
40
40
|
- `on(type, handler, options?)` subscribes and returns an unsubscribe function.
|
|
41
41
|
- `on("*", wildcard, options?)` subscribes to every event after type-matched handlers.
|
|
42
|
-
- `once(type, handler)` is shorthand for
|
|
42
|
+
- `once(type, handler)` is shorthand for `on(type, handler, { once: true })`, for typed events and `"*"` alike.
|
|
43
43
|
- `off(type, handler?)`, `clear()`, and `dispose()` remove handlers at different scopes.
|
|
44
44
|
- `emit(type, payload)` dispatches synchronously over a snapshot of handlers.
|
|
45
|
-
- Options: `signal`, `once`, `captureErrors
|
|
45
|
+
- Options: `signal`, `once`, `captureErrors` for typed, `sampleRate` for wildcard, `throttleMs` for typed and wildcard.
|
|
46
46
|
|
|
47
47
|
## Sharp Edges
|
|
48
48
|
|
|
49
|
-
- Default error policy is mitt-like: the first throwing handler aborts dispatch. Use `captureHandlerErrors` or per-handler `captureErrors` to swallow/report and continue.
|
|
49
|
+
- Default error policy is mitt-like: the first throwing handler aborts dispatch. Use `captureHandlerErrors` or per-handler `captureErrors` to swallow/report and continue; report callbacks always get the event name as a string.
|
|
50
50
|
- Wildcard handlers receive `(type, payload)`, not just payload.
|
|
51
|
-
-
|
|
51
|
+
- `once("*", handler)` is equivalent to `on("*", handler, { once: true })`.
|
|
52
|
+
- A nested `emit()` from a handler runs to completion before the outer dispatch resumes; the outer dispatch skips handlers removed meanwhile (unsubscribe, `off`, `clear`, `dispose`, abort), and `once` handlers go inert before their first call.
|
|
52
53
|
- `throttleMs` uses `performance.now()` (monotonic); system-clock corrections do not affect throttle windows.
|
|
53
54
|
- `sampleRate` is wildcard-only and uses `Math.random()` per dispatch.
|
|
54
|
-
- `
|
|
55
|
+
- Misuse throws `EmitterError` from `on()`/`once()` before anything is registered: a handler that is not a function, a `signal` that is not an `AbortSignal`, or an invalid option.
|
|
56
|
+
- `dispose()` is permanent; after it, `on`/`once`/`emit`/`off`/`clear` all throw `EmitterDisposedError`. Only `dispose()` itself and previously returned unsubscribe functions are safe no-ops post-dispose.
|
|
55
57
|
|
|
56
58
|
## AI Context
|
|
57
59
|
|
package/README_ZHTW.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
小而嚴格的 typed event emitter,具備 ai*js lifecycle 慣例:`on()` 回傳 unsubscribe、內建 `once`、支援 `AbortSignal`、wildcard handlers,以及可重複呼叫的 `dispose()`。
|
|
4
4
|
|
|
5
|
-
> **狀態:0.
|
|
5
|
+
> **狀態:0.6.0 - 穩定 1.0 軌道 API。** root entry 是公開 API。
|
|
6
6
|
|
|
7
7
|
## 安裝
|
|
8
8
|
|
|
@@ -39,19 +39,21 @@ events.dispose();
|
|
|
39
39
|
- `createEmitter<Events>(options?)` 建立 typed emitter。
|
|
40
40
|
- `on(type, handler, options?)` 訂閱並回傳 unsubscribe。
|
|
41
41
|
- `on("*", wildcard, options?)` 訂閱所有事件,且在 typed handlers 之後呼叫。
|
|
42
|
-
- `once(type, handler)` 是
|
|
42
|
+
- `once(type, handler)` 是 `on(type, handler, { once: true })` 的 shorthand,typed event 與 `"*"` 都適用。
|
|
43
43
|
- `off(type, handler?)`、`clear()`、`dispose()` 用不同 scope 移除 handlers。
|
|
44
44
|
- `emit(type, payload)` 同步 dispatch,且會先 snapshot handler list。
|
|
45
|
-
- Options:`signal`、`once
|
|
45
|
+
- Options:`signal`、`once`、typed-only `captureErrors`、wildcard-only `sampleRate`、typed/wildcard `throttleMs`。
|
|
46
46
|
|
|
47
47
|
## 注意事項
|
|
48
48
|
|
|
49
|
-
- 預設錯誤策略與 mitt 類似:第一個 throw 的 handler 會中止 dispatch。可用 `captureHandlerErrors` 或單一 handler 的 `captureErrors`
|
|
49
|
+
- 預設錯誤策略與 mitt 類似:第一個 throw 的 handler 會中止 dispatch。可用 `captureHandlerErrors` 或單一 handler 的 `captureErrors` 改成吞掉/回報後繼續;回報 callback 收到的事件名稱一律是字串。
|
|
50
50
|
- Wildcard handler 收到 `(type, payload)`,不是只有 payload。
|
|
51
|
-
-
|
|
52
|
-
-
|
|
51
|
+
- `once("*", handler)` 等同於 `on("*", handler, { once: true })`。
|
|
52
|
+
- 在 handler 內巢狀呼叫的 `emit()` 會先同步執行完畢,外層 dispatch 才繼續;外層 dispatch 會略過期間被移除的 handler(unsubscribe、`off`、`clear`、`dispose`、abort),`once` handler 則在第一次呼叫前就失效。
|
|
53
|
+
- `throttleMs` 使用 `performance.now()`(單調時鐘),不受系統時間校正影響。
|
|
53
54
|
- `sampleRate` 只支援 wildcard,且每次 dispatch 以 `Math.random()` 取樣。
|
|
54
|
-
- `
|
|
55
|
+
- 誤用時 `on()`/`once()` 會在註冊任何東西之前丟出 `EmitterError`:handler 不是函式、`signal` 不是 `AbortSignal`,或 option 不合法。
|
|
56
|
+
- `dispose()` 是永久 teardown;之後 `on`/`once`/`emit`/`off`/`clear` 全部會丟 `EmitterDisposedError`。只有 `dispose()` 本身,以及先前 `on()` 回傳的 unsubscribe 函式,在 dispose 後仍安全地維持 no-op。
|
|
55
57
|
|
|
56
58
|
## AI Context
|
|
57
59
|
|
package/dist/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
'use strict';var
|
|
1
|
+
'use strict';var d=class extends Error{name="EmitterError"},y=class extends Error{name="EmitterDisposedError"},j=()=>{};function O(n){n.c?.(),n.c=void 0,n.h=j;}function h(n){for(let a of n)O(a);}function K(n){if(n.r!==void 0&&Math.random()>=n.r)return false;if(n.tm){let a=performance.now();if(n.ts!==void 0&&a-n.ts<n.tm)return false;n.ts=a;}return true}function M(n){let a=n?.captureHandlerErrors,s=new Map,v=[],E=false;function p(){if(E)throw new y("aieventjs: emitter has been disposed")}function b(e,t,o){p();let i=o?.sampleRate,r=o?.throttleMs,c=o?.captureErrors,g=e==="*";if(typeof t!="function")throw new d("aieventjs: handler must be a function");if(g){if(c!==void 0)throw new d("aieventjs: captureErrors must be unset on *")}else if(i!==void 0)throw new d("aieventjs: sampleRate must be unset on typed events");if(i!==void 0&&(!Number.isFinite(i)||i<=0||i>1))throw new d("aieventjs: sampleRate must be in (0,1]");if(r!==void 0&&(!Number.isFinite(r)||r<0))throw new d("aieventjs: throttleMs must be a finite number >= 0");let f=o?.signal;if(f&&(typeof f.addEventListener!="function"||typeof f.removeEventListener!="function"))throw new d("aieventjs: signal must be an AbortSignal");if(f?.aborted)return ()=>{};let u=g?v:s.get(e)??[],l={h:t,u:t,c:void 0,ce:c,r:i,tm:r},m=()=>{let w=u.indexOf(l);w>=0&&u.splice(w,1),O(l),!u.length&&s.get(e)===u&&s.delete(e);};return o?.once&&(l.h=(...w)=>{m(),t(...w);}),f&&(f.addEventListener("abort",m,{once:true}),l.c=()=>f.removeEventListener("abort",m)),u.push(l),g||s.set(e,u),m}function H(e,t){return b(e,t,{once:true})}function F(e,t){p();let o=e==="*"?v:s.get(e);if(o!==void 0){if(t===void 0)h(o),o.length=0;else {let i=o.findIndex(r=>r.u===t);i>=0&&h(o.splice(i,1));}o.length||s.delete(e);}}function k(e,t,o,i){if(e===void 0||e===false)throw t;if(typeof e=="function")try{e(t,String(o),i);}catch{}}function R(e,t){p();let o=(s.get(e)??[]).slice(),i=v.slice();for(let r of o)if(K(r))try{r.h(t);}catch(c){k(r.ce!==void 0?r.ce:a,c,e,t);}for(let r of i)if(K(r))try{r.h(e,t);}catch(c){k(a,c,e,t);}}function x(){for(let e of [...s.values(),v])h(e),e.length=0;s.clear();}return {on:b,once:H,off:F,emit:R,clear(){p(),x();},dispose(){E||(x(),E=true);},get disposed(){return E},get _mapSize(){return s.size}}}exports.EmitterDisposedError=y;exports.EmitterError=d;exports.createEmitter=M;//# sourceMappingURL=index.cjs.map
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":["EmitterError","EmitterDisposedError","rmByUser","arr","user","i","e","flush","sub","sig","rm","fn","createEmitter","opts","cap","t","w","d","ck","ga","k","a","on","type","handler","o","sr","tm2","tp","p","ce","prune","unsub","once","off","ap","pol","err","emit","payload","ts","ws","now","purge"],"mappings":"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","file":"index.cjs","sourcesContent":["// aieventjs — small, strict, typed event emitter for the ai*js family.\n//\n// v0.1.0: full implementation of the frozen API surface. Mitt-compatible\n// snapshot semantics, wildcard \"*\" handler, AbortSignal integration, once,\n// idempotent dispose, destructurable methods (no `this`).\n\n/**\n * Configuration for {@link createEmitter}. Controls the default error\n * policy for handlers thrown during `emit()`; per-handler\n * {@link OnOptions.captureErrors} overrides this default.\n *\n * @public\n */\nexport interface EmitterOptions {\n /**\n * Default error policy for all handlers when they throw during emit().\n *\n * - undefined / false (default) — first throw aborts dispatch (mitt-compatible).\n * - true — swallow; dispatch continues over all handlers in the snapshot.\n * - (err, type, payload) => void — invoked with the unknown error, the\n * event name as string, and the payload as unknown. If this callback\n * itself throws, the error is silently ignored.\n *\n * Per-subscription OnOptions.captureErrors overrides this for that handler.\n */\n captureHandlerErrors?: boolean | ((err: unknown, type: string, payload: unknown) => void);\n}\n\n/**\n * Handler invoked for a single typed event.\n *\n * @public\n */\nexport type EventHandler<Payload> = (payload: Payload) => void;\n\n/**\n * Handler invoked for the wildcard `\"*\"` subscription. Receives the actual\n * event type alongside the payload.\n *\n * @public\n */\nexport type WildcardHandler<Events extends Record<string, unknown>> = <K extends keyof Events>(\n type: K,\n payload: Events[K],\n) => void;\n\n/**\n * Subscription options accepted by {@link Emitter.on}.\n *\n * @public\n */\nexport interface OnOptions {\n /**\n * Aborting this signal removes the handler. The same effect as calling\n * the returned unsubscribe function. Pre-aborted signals never register.\n */\n signal?: AbortSignal;\n\n /** Auto-remove the handler after the first dispatch. Equivalent to `once()`. */\n once?: boolean;\n\n /**\n * Override emitter-level captureHandlerErrors for this handler.\n * - undefined — fall through to emitter-level.\n * - false — force re-throw, even when emitter-level is true / callback.\n * - true — swallow.\n * - (err, type, payload) => void — same semantics as the emitter-level callback.\n *\n * Throws EmitterError if set on a wildcard \"*\" subscription.\n * @invariant does not break snapshot-before-iterate semantics.\n */\n captureErrors?: boolean | ((err: unknown, type: string, payload: unknown) => void);\n\n /**\n * Wildcard \"*\" only. Probability in (0, 1] that a dispatch reaches this\n * handler. Math.random() is sampled per dispatch. Values <= 0 or > 1 are\n * rejected at on() time.\n *\n * Throws EmitterError if set on a typed handler.\n */\n sampleRate?: number;\n\n /**\n * Per-handler leading-edge throttle. Minimum milliseconds between successive\n * calls to this handler. The first dispatch after subscription always fires;\n * subsequent dispatches within `throttleMs` are dropped (not queued).\n * Uses `performance.now()` (monotonic). 0 = no throttle. Non-finite or\n * negative values are rejected at `on()` time.\n *\n * Valid on both typed and wildcard `\"*\"` subscriptions (since v0.5.3); each\n * handler keeps its own throttle clock. Useful for per-event HUD throttling,\n * e.g. a `credits/change` event that fires every frame.\n *\n * @remarks\n * The throttle clock uses `performance.now()`, which is monotonic and\n * unaffected by system-clock corrections (NTP step-backs, manual adjustments).\n * This ensures handlers are never silently muted by a wall-clock regression.\n */\n throttleMs?: number;\n}\n\n/**\n * Strongly-typed event emitter. Subscribe with {@link Emitter.on} (returns\n * an unsubscribe function), dispatch with {@link Emitter.emit}, dispose\n * with {@link Emitter.dispose} when finished.\n *\n * @typeParam Events — a string-keyed map from event name to payload type.\n * @public\n */\nexport interface Emitter<Events extends Record<string, unknown>> {\n /**\n * Subscribe to a single event type. Returns an unsubscribe function;\n * calling it (or aborting `opts.signal`) removes the handler.\n */\n on<K extends keyof Events>(\n type: K,\n handler: EventHandler<Events[K]>,\n opts?: OnOptions,\n ): () => void;\n\n /**\n * Subscribe to every event with a single handler that receives\n * `(type, payload)`. Wildcard handlers fire AFTER type-matched\n * handlers — same ordering as `mitt`.\n */\n on(type: \"*\", handler: WildcardHandler<Events>, opts?: OnOptions): () => void;\n\n /**\n * Subscribe and auto-remove after the first dispatch. Equivalent to\n * `on(type, handler, { once: true })`.\n *\n * @remarks\n * **`\"*\"` is not a valid `type` argument for `once()`.** The wildcard is\n * handled by the `on(\"*\", handler, { once: true })` overload instead.\n * The explicit rejection overload below ensures `once(\"*\", ...)` is a\n * compile-time error (handler typed as `never`). (EVT-B-02)\n */\n /** @internal — compile-time rejection: `once(\"*\", handler)` is a type error. */\n once(type: \"*\", handler: never): never;\n once<K extends keyof Events>(type: K, handler: EventHandler<Events[K]>): () => void;\n\n /**\n * Imperative unsubscribe. Prefer the unsubscribe function returned by\n * `on()` — it's faster (no reference lookup) and survives renames.\n * If `handler` is omitted, removes every handler for `type`.\n */\n off<K extends keyof Events>(type: K, handler?: EventHandler<Events[K]>): void;\n\n /**\n * Imperative wildcard unsubscribe.\n */\n off(type: \"*\", handler?: WildcardHandler<Events>): void;\n\n /**\n * Dispatch synchronously. Handlers receive `payload`; wildcard handlers\n * receive `(type, payload)`. Handler lists are snapshotted before iteration,\n * so removing a handler inside its own callback does not skip subsequent\n * handlers. By default, the first throwing handler aborts the dispatch;\n * set EmitterOptions.captureHandlerErrors (or per-handler OnOptions.captureErrors)\n * to swallow or report errors and continue.\n */\n emit<K extends keyof Events>(type: K, payload: Events[K]): void;\n\n /**\n * Remove every handler for every event (including wildcards). The\n * emitter remains usable. Use {@link dispose} for permanent teardown.\n */\n clear(): void;\n\n /**\n * Idempotent teardown. Drops every handler; subsequent `on` / `once` /\n * `emit` / `off` / `clear` throw {@link EmitterDisposedError}.\n */\n dispose(): void;\n\n /** `true` once {@link dispose} has been called. */\n readonly disposed: boolean;\n}\n\n/**\n * Recoverable emitter error. Thrown by `on()` when `OnOptions` violates a\n * precondition: `captureErrors` set on a wildcard `\"*\"` subscription;\n * `sampleRate` set on a typed subscription; `sampleRate` outside `(0, 1]`; or\n * `throttleMs` non-finite or negative.\n *\n * @public\n */\nexport class EmitterError extends Error {\n override readonly name = \"EmitterError\";\n}\n\n/**\n * Thrown by any emitter method called after {@link Emitter.dispose}.\n *\n * @public\n */\nexport class EmitterDisposedError extends Error {\n override readonly name = \"EmitterDisposedError\";\n}\n\n// ---------------------------------------------------------------------------\n// Internal types\n// ---------------------------------------------------------------------------\n\n// Mutable `c` field (not optional `?:`) avoids exactOptionalPropertyTypes TS2412\n// when assigning undefined. Short field names reduce minified output size.\ntype ErrorPolicy = boolean | ((err: unknown, type: string, payload: unknown) => void);\n\ninterface E<H> {\n h: H; // handler (may be a once-wrapper)\n c: (() => void) | undefined; // abortCleanup\n u: H; // user-provided handler (off matching)\n // v0.3.0: per-handler error policy and throttle/sample state.\n // Fields typed as `T | undefined` (not just `T`) so that exactOptionalPropertyTypes\n // permits assigning `undefined` in object literals (avoids TS2375).\n ce?: ErrorPolicy | undefined; // captureErrors override (typed only)\n r?: number | undefined; // sampleRate (wildcard only)\n tm?: number | undefined; // throttleMs (typed or wildcard; v0.5.3)\n ts?: number | undefined; // last call timestamp — mutated during dispatch (throttle clock)\n}\n\ntype AH = EventHandler<unknown>;\ntype WH = WildcardHandler<Record<string, unknown>>;\n\n// Remove one entry by user-identity from an array; run its abort cleanup.\nfunction rmByUser<H>(arr: E<H>[], user: H): void {\n const i = arr.findIndex((e) => e.u === user);\n if (i >= 0) {\n const e = arr[i];\n if (e !== undefined) {\n e.c?.();\n e.c = undefined;\n }\n arr.splice(i, 1);\n }\n}\n\n// Flush all abort cleanups from an array (for clear / dispose).\nfunction flush<H>(arr: E<H>[]): void {\n for (const e of arr) {\n e.c?.();\n e.c = undefined;\n }\n}\n\n// Push entry onto arr, wire AbortSignal, return unsubscribe.\nfunction sub<H>(arr: E<H>[], e: E<H>, sig: AbortSignal | undefined): () => void {\n arr.push(e);\n const rm = () => {\n const i = arr.indexOf(e);\n if (i >= 0) arr.splice(i, 1);\n e.c?.();\n e.c = undefined;\n };\n if (sig !== undefined) {\n const fn = () => rm();\n sig.addEventListener(\"abort\", fn, { once: true });\n e.c = () => sig.removeEventListener(\"abort\", fn);\n }\n return rm;\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Construct a strongly-typed event emitter.\n *\n * @remarks\n * Declare the event map with a `type` alias, not an `interface`. The `Events`\n * generic is constrained to `Record<string, unknown>`, and a *plain* TypeScript\n * `interface` has no implicit index signature, so it fails the constraint with\n * *\"Index signature for type 'string' is missing in type ...\"*. A `type` object\n * literal satisfies the constraint structurally. (An `interface` with an explicit\n * index signature or `extends Record<string, unknown>` also compiles, but widens\n * `keyof Events` to `string`, losing strict event-name checking.)\n *\n * ```ts\n * // ❌ interface — fails the Record<string, unknown> constraint\n * interface Events { \"user:login\": { id: string } }\n * const bus = createEmitter<Events>(); // TS2344\n *\n * // ✅ type — satisfies the constraint\n * type Events = { \"user:login\": { id: string } };\n * const bus = createEmitter<Events>();\n * ```\n *\n * @example\n * ```ts\n * import { createEmitter } from \"aieventjs\";\n *\n * type Events = {\n * \"user:login\": { id: string };\n * \"user:logout\": void;\n * };\n *\n * const bus = createEmitter<Events>();\n *\n * const off = bus.on(\"user:login\", (u) => console.log(\"hi\", u.id));\n * bus.emit(\"user:login\", { id: \"alice\" });\n * off();\n *\n * bus.on(\"*\", (type, payload) => console.log(\"event\", type, payload));\n * ```\n *\n * @public\n */\nexport function createEmitter<Events extends Record<string, unknown> = Record<string, unknown>>(\n opts?: EmitterOptions,\n): Emitter<Events> {\n const cap = opts?.captureHandlerErrors;\n\n const t: Map<string, E<AH>[]> = new Map();\n const w: E<WH>[] = [];\n let d = false;\n\n function ck(): void {\n if (d) throw new EmitterDisposedError(\"aieventjs: emitter has been disposed\");\n }\n\n // Get or create typed handler array for a key.\n function ga(k: string): E<AH>[] {\n let a = t.get(k);\n if (a === undefined) {\n a = [];\n t.set(k, a);\n }\n return a;\n }\n\n function on(type: string | \"*\", handler: AH | WH, o?: OnOptions): () => void {\n ck();\n // v0.3.0 guards: cross-domain options + range checks.\n // v0.5.3: throttleMs is now valid on typed handlers too (per-handler clock);\n // sampleRate remains wildcard-only.\n const sr = o?.sampleRate;\n const tm2 = o?.throttleMs;\n if (type === \"*\") {\n if (o?.captureErrors !== undefined)\n throw new EmitterError(\"aieventjs: captureErrors invalid on *\");\n } else {\n if (sr !== undefined) throw new EmitterError(\"aieventjs: sampleRate wildcard-only\");\n }\n if (sr !== undefined && (!Number.isFinite(sr) || sr <= 0 || sr > 1))\n throw new EmitterError(\"aieventjs: sampleRate must be in (0,1]\");\n if (tm2 !== undefined && (!Number.isFinite(tm2) || tm2 < 0))\n throw new EmitterError(\"aieventjs: throttleMs must be >= 0\");\n const sig = o?.signal;\n if (sig?.aborted) return () => {};\n\n if (type === \"*\") {\n const fn = handler as WH;\n if (o?.once) {\n const e: E<WH> = {\n h: (tp, p) => {\n rm();\n fn(tp, p);\n },\n u: fn,\n c: undefined,\n r: sr,\n tm: tm2,\n };\n const rm = sub(w, e, sig);\n return rm;\n }\n return sub(w, { h: fn, u: fn, c: undefined, r: sr, tm: tm2 }, sig);\n }\n\n const fn = handler as AH;\n const ce = o?.captureErrors;\n const arr = ga(type);\n const prune = () => {\n // Identity guard: only delete the key when `arr` is STILL the array\n // currently mapped. ga() mints a NEW array when a deleted key is\n // re-subscribed, so a stale/double unsub of the original handler must\n // not prune the live re-subscribed key (idempotency).\n if (!arr.length && t.get(type) === arr) t.delete(type);\n };\n if (o?.once) {\n let unsub!: () => void;\n const rm = () => {\n unsub();\n prune();\n };\n const e: E<AH> = {\n h: (p) => {\n rm();\n fn(p);\n },\n u: fn,\n c: undefined,\n ce: ce,\n tm: tm2,\n };\n unsub = sub(arr, e, sig);\n return rm;\n }\n const unsub = sub(arr, { h: fn, u: fn, c: undefined, ce: ce, tm: tm2 }, sig);\n return () => {\n unsub();\n prune();\n };\n }\n\n function once<K extends keyof Events>(type: K, handler: EventHandler<Events[K]>): () => void {\n return on(type as string, handler as AH, { once: true });\n }\n\n function off(type: string | \"*\", handler?: AH | WH): void {\n ck();\n if (type === \"*\") {\n if (handler === undefined) {\n flush(w);\n w.length = 0;\n } else {\n rmByUser(w, handler as WH);\n }\n return;\n }\n const arr = t.get(type);\n if (arr === undefined) return;\n if (handler === undefined) {\n flush(arr);\n arr.length = 0;\n t.delete(type);\n } else {\n rmByUser(arr, handler as AH);\n if (!arr.length) t.delete(type);\n }\n }\n\n // Inline error policy handler — policy undefined/false → re-throw; true → swallow;\n // function → invoke and swallow; if callback throws, ignore silently.\n function ap(pol: ErrorPolicy | undefined, err: unknown, k: string, p: unknown): void {\n if (pol === undefined || pol === false) throw err;\n if (typeof pol === \"function\")\n try {\n pol(err, k, p);\n } catch {\n /* silent */\n }\n }\n\n function emit<K extends keyof Events>(type: K, payload: Events[K]): void {\n ck();\n // Both slices happen BEFORE any handler call (snapshot-before-iterate).\n const k = type as string;\n const p = payload as unknown;\n const ts = (t.get(k) ?? []).slice();\n const ws = w.slice();\n for (const e of ts) {\n if (e.tm) {\n const now = performance.now();\n if (e.ts !== undefined && now - e.ts < e.tm) continue;\n e.ts = now;\n }\n try {\n e.h(p);\n } catch (err) {\n ap(e.ce !== undefined ? e.ce : cap, err, k, p);\n }\n }\n for (const e of ws) {\n if (e.r !== undefined && Math.random() >= e.r) continue;\n if (e.tm) {\n const now = performance.now();\n if (e.ts !== undefined && now - e.ts < e.tm) continue;\n e.ts = now;\n }\n try {\n e.h(k, p as never);\n } catch (err) {\n ap(cap, err, k, p);\n }\n }\n }\n\n function purge(): void {\n for (const a of t.values()) {\n flush(a);\n a.length = 0;\n }\n flush(w);\n t.clear();\n w.length = 0;\n }\n\n // _mapSize: test-only observation seam (not on the public Emitter interface).\n // Casted away at the return type; exposes t.size for Map-pruning regression tests.\n return {\n on: on as Emitter<Events>[\"on\"],\n once: once as Emitter<Events>[\"once\"],\n off: off as Emitter<Events>[\"off\"],\n emit,\n clear() {\n ck();\n purge();\n },\n dispose() {\n if (!d) {\n purge();\n d = true;\n }\n },\n get disposed() {\n return d;\n },\n get _mapSize() {\n return t.size;\n },\n } as unknown as Emitter<Events>;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["EmitterError","EmitterDisposedError","N","kill","e","flush","arr","gate","now","createEmitter","opts","cap","t","w","d","ck","on","type","handler","sr","tm","ce","wild","sig","rm","i","a","once","off","ap","pol","err","k","p","emit","payload","ts","ws","purge"],"mappings":"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","file":"index.cjs","sourcesContent":["// aieventjs — small, strict, typed event emitter for the ai*js family.\n//\n// Mitt-shaped API: snapshot dispatch (handlers removed mid-dispatch are\n// skipped, per the ai*js fan-out rule), wildcard \"*\" handler, AbortSignal\n// integration, once, idempotent dispose, destructurable methods (no `this`).\n\n/**\n * Configuration for {@link createEmitter}. Controls the default error\n * policy for handlers thrown during `emit()`; per-handler\n * {@link OnOptions.captureErrors} overrides this default.\n *\n * @public\n */\nexport interface EmitterOptions {\n /**\n * Default error policy for all handlers when they throw during emit().\n *\n * - undefined / false (default) — first throw aborts dispatch (mitt-compatible).\n * - true — swallow; dispatch continues over all handlers in the snapshot.\n * - (err, type, payload) => void — invoked with the unknown error, the\n * event name as a string (numeric and symbol keys are converted with\n * `String()`), and the payload as unknown. If this callback itself\n * throws, the error is silently ignored.\n *\n * Per-subscription OnOptions.captureErrors overrides this for that handler.\n */\n captureHandlerErrors?: boolean | ((err: unknown, type: string, payload: unknown) => void);\n}\n\n/**\n * Handler invoked for a single typed event.\n *\n * @public\n */\nexport type EventHandler<Payload> = (payload: Payload) => void;\n\n/**\n * Handler invoked for the wildcard `\"*\"` subscription. Receives the actual\n * event type alongside the payload.\n *\n * @public\n */\nexport type WildcardHandler<Events extends Record<string, unknown>> = <K extends keyof Events>(\n type: K,\n payload: Events[K],\n) => void;\n\n/**\n * Subscription options accepted by {@link Emitter.on}.\n *\n * @public\n */\nexport interface OnOptions {\n /**\n * Aborting this signal removes the handler. The same effect as calling\n * the returned unsubscribe function. Pre-aborted signals never register.\n * A value without `addEventListener` / `removeEventListener` throws\n * EmitterError; `null` is treated like `undefined`.\n */\n signal?: AbortSignal;\n\n /** Auto-remove the handler after the first dispatch. Equivalent to `once()`. */\n once?: boolean;\n\n /**\n * Override emitter-level captureHandlerErrors for this handler.\n * - undefined — fall through to emitter-level.\n * - false — force re-throw, even when emitter-level is true / callback.\n * - true — swallow.\n * - (err, type, payload) => void — same semantics as the emitter-level callback.\n *\n * Throws EmitterError if set on a wildcard \"*\" subscription.\n * @invariant does not break snapshot-before-iterate semantics.\n */\n captureErrors?: boolean | ((err: unknown, type: string, payload: unknown) => void);\n\n /**\n * Wildcard \"*\" only. Probability in (0, 1] that a dispatch reaches this\n * handler. Math.random() is sampled per dispatch. Values <= 0 or > 1 are\n * rejected at on() time.\n *\n * Throws EmitterError if set on a typed handler.\n */\n sampleRate?: number;\n\n /**\n * Per-handler leading-edge throttle. Minimum milliseconds between successive\n * calls to this handler. The first dispatch after subscription always fires;\n * subsequent dispatches within `throttleMs` are dropped (not queued).\n * Uses `performance.now()` (monotonic). 0 = no throttle. Non-finite or\n * negative values are rejected at `on()` time.\n *\n * Valid on both typed and wildcard `\"*\"` subscriptions (since v0.5.3); each\n * handler keeps its own throttle clock. Useful for per-event HUD throttling,\n * e.g. a `credits/change` event that fires every frame.\n *\n * @remarks\n * The throttle clock uses `performance.now()`, which is monotonic and\n * unaffected by system-clock corrections (NTP step-backs, manual adjustments).\n * This ensures handlers are never silently muted by a wall-clock regression.\n */\n throttleMs?: number;\n}\n\n/**\n * Strongly-typed event emitter. Subscribe with {@link Emitter.on} (returns\n * an unsubscribe function), dispatch with {@link Emitter.emit}, dispose\n * with {@link Emitter.dispose} when finished.\n *\n * @typeParam Events — a string-keyed map from event name to payload type.\n * @public\n */\nexport interface Emitter<Events extends Record<string, unknown>> {\n /**\n * Subscribe to a single event type. Returns an unsubscribe function;\n * calling it (or aborting `opts.signal`) removes the handler.\n */\n on<K extends keyof Events>(\n type: K,\n handler: EventHandler<Events[K]>,\n opts?: OnOptions,\n ): () => void;\n\n /**\n * Subscribe to every event with a single handler that receives\n * `(type, payload)`. Wildcard handlers fire AFTER type-matched\n * handlers — same ordering as `mitt`.\n */\n on(type: \"*\", handler: WildcardHandler<Events>, opts?: OnOptions): () => void;\n\n /**\n * Wildcard-once: subscribe to every event and auto-remove after the first\n * dispatch. Equivalent to `on(\"*\", handler, { once: true })`: the handler\n * receives `(type, payload)`, fires after type-matched handlers, and goes\n * inert before it is called.\n *\n * @remarks\n * Declared before the typed overload so `\"*\"` always resolves here, even\n * when `Events` has a string index signature (EVT-B-02).\n */\n once(type: \"*\", handler: WildcardHandler<Events>): () => void;\n\n /**\n * Subscribe and auto-remove after the first dispatch. Equivalent to\n * `on(type, handler, { once: true })`.\n */\n once<K extends keyof Events>(type: K, handler: EventHandler<Events[K]>): () => void;\n\n /**\n * Imperative unsubscribe. Prefer the unsubscribe function returned by\n * `on()` — it's faster (no reference lookup) and survives renames.\n * If `handler` is omitted, removes every handler for `type`.\n */\n off<K extends keyof Events>(type: K, handler?: EventHandler<Events[K]>): void;\n\n /**\n * Imperative wildcard unsubscribe.\n */\n off(type: \"*\", handler?: WildcardHandler<Events>): void;\n\n /**\n * Dispatch synchronously. Handlers receive `payload`; wildcard handlers\n * receive `(type, payload)`. Handler lists are snapshotted before iteration:\n * a handler added during the dispatch waits for the next `emit()`, and a\n * handler removed during it (unsubscribe, `off`, `clear`, `dispose` or an\n * aborted signal) is skipped for the rest of it. A nested `emit()` from a\n * handler runs to completion before the outer dispatch resumes. By default,\n * the first throwing handler aborts the dispatch; set\n * EmitterOptions.captureHandlerErrors (or per-handler OnOptions.captureErrors)\n * to swallow or report errors and continue.\n */\n emit<K extends keyof Events>(type: K, payload: Events[K]): void;\n\n /**\n * Remove every handler for every event (including wildcards). The\n * emitter remains usable. Use {@link dispose} for permanent teardown.\n */\n clear(): void;\n\n /**\n * Idempotent teardown. Drops every handler; subsequent `on` / `once` /\n * `emit` / `off` / `clear` throw {@link EmitterDisposedError}.\n */\n dispose(): void;\n\n /** `true` once {@link dispose} has been called. */\n readonly disposed: boolean;\n}\n\n/**\n * Recoverable emitter error. Thrown by `on()` / `once()` before anything is\n * registered when `handler` is not a function, or when `OnOptions` violates a\n * precondition: `signal` is not an `AbortSignal`; `captureErrors` set on a\n * wildcard `\"*\"` subscription; `sampleRate` set on a typed subscription;\n * `sampleRate` outside `(0, 1]`; or `throttleMs` non-finite or negative.\n * Messages read `aieventjs: <subject> must be <constraint>`.\n *\n * @public\n */\nexport class EmitterError extends Error {\n override readonly name = \"EmitterError\";\n}\n\n/**\n * Thrown by `on`/`once`/`emit`/`off`/`clear` when called after\n * {@link Emitter.dispose}. `dispose()` itself never throws — it is\n * idempotent — and unsubscribe functions returned before dispose remain\n * safe no-ops afterward.\n *\n * @public\n */\nexport class EmitterDisposedError extends Error {\n override readonly name = \"EmitterDisposedError\";\n}\n\n// ---------------------------------------------------------------------------\n// Internal types\n// ---------------------------------------------------------------------------\n\ntype ErrorPolicy = boolean | ((err: unknown, type: string, payload: unknown) => void);\n\n// Stored handler shape: typed entries are called as h(payload), wildcard\n// entries as h(type, payload).\ntype F = (...a: unknown[]) => void;\n\n// One subscription (typed or wildcard). Short field names reduce minified\n// output size. Fields are typed `T | undefined` so exactOptionalPropertyTypes\n// permits assigning `undefined` (avoids TS2375 / TS2412).\ninterface E {\n h: F; // handler: the user's, a once-wrapper, or N once removed\n c: (() => void) | undefined; // abortCleanup\n u: F; // user-provided handler (off matching)\n ce: ErrorPolicy | undefined; // captureErrors override (typed only)\n r: number | undefined; // sampleRate (wildcard only)\n tm: number | undefined; // throttleMs (typed or wildcard; v0.5.3)\n ts?: number | undefined; // last call timestamp — mutated during dispatch (throttle clock)\n}\n\n// Inert handler swapped into every removed entry (see kill()).\nconst N: F = () => {};\n\n// Detach an entry on every removal path (unsubscribe, abort, off, clear,\n// dispose): run its abort cleanup and make it inert, so an emit() whose\n// snapshot still holds the entry skips it for the rest of that dispatch\n// (ai*js fan-out re-entrancy rule). Idempotent.\nfunction kill(e: E): void {\n e.c?.();\n e.c = undefined;\n e.h = N;\n}\n\n// Detach every entry of an array (clear / dispose / off without handler).\nfunction flush(arr: E[]): void {\n for (const e of arr) kill(e);\n}\n\n// Per-dispatch gate shared by the typed and wildcard loops of emit(): the\n// wildcard-only sampleRate draw, then the leading-edge throttle. The throttle\n// timestamp is written before the handler runs, so a throwing handler still\n// consumes its window, and a sample miss never touches it.\nfunction gate(e: E): boolean {\n if (e.r !== undefined && Math.random() >= e.r) return false;\n if (e.tm) {\n const now = performance.now();\n if (e.ts !== undefined && now - e.ts < e.tm) return false;\n e.ts = now;\n }\n return true;\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Construct a strongly-typed event emitter.\n *\n * @remarks\n * Declare the event map with a `type` alias, not an `interface`. The `Events`\n * generic is constrained to `Record<string, unknown>`, and a *plain* TypeScript\n * `interface` has no implicit index signature, so it fails the constraint with\n * *\"Index signature for type 'string' is missing in type ...\"*. A `type` object\n * literal satisfies the constraint structurally. (An `interface` with an explicit\n * index signature or `extends Record<string, unknown>` also compiles, but widens\n * `keyof Events` to `string`, losing strict event-name checking.)\n *\n * ```ts\n * // ❌ interface — fails the Record<string, unknown> constraint\n * interface Events { \"user:login\": { id: string } }\n * const bus = createEmitter<Events>(); // TS2344\n *\n * // ✅ type — satisfies the constraint\n * type Events = { \"user:login\": { id: string } };\n * const bus = createEmitter<Events>();\n * ```\n *\n * @example\n * ```ts\n * import { createEmitter } from \"aieventjs\";\n *\n * type Events = {\n * \"user:login\": { id: string };\n * \"user:logout\": void;\n * };\n *\n * const bus = createEmitter<Events>();\n *\n * const off = bus.on(\"user:login\", (u) => console.log(\"hi\", u.id));\n * bus.emit(\"user:login\", { id: \"alice\" });\n * off();\n *\n * bus.on(\"*\", (type, payload) => console.log(\"event\", type, payload));\n * ```\n *\n * @public\n */\nexport function createEmitter<Events extends Record<string, unknown> = Record<string, unknown>>(\n opts?: EmitterOptions,\n): Emitter<Events> {\n const cap = opts?.captureHandlerErrors;\n\n const t: Map<string, E[]> = new Map();\n const w: E[] = [];\n let d = false;\n\n function ck(): void {\n if (d) throw new EmitterDisposedError(\"aieventjs: emitter has been disposed\");\n }\n\n function on(type: string, handler: F, o?: OnOptions): () => void {\n ck();\n // Validate everything before any side effect; misuse is EmitterError with\n // the ai*js `aieventjs: <subject> must be <constraint>` message shape.\n // Cross-domain options: captureErrors is typed-only, sampleRate\n // wildcard-only; throttleMs is valid on both (v0.5.3, per-handler clock).\n const sr = o?.sampleRate;\n const tm = o?.throttleMs;\n const ce = o?.captureErrors;\n const wild = type === \"*\";\n if (typeof handler !== \"function\")\n throw new EmitterError(\"aieventjs: handler must be a function\");\n if (wild) {\n if (ce !== undefined) throw new EmitterError(\"aieventjs: captureErrors must be unset on *\");\n } else if (sr !== undefined)\n throw new EmitterError(\"aieventjs: sampleRate must be unset on typed events\");\n if (sr !== undefined && (!Number.isFinite(sr) || sr <= 0 || sr > 1))\n throw new EmitterError(\"aieventjs: sampleRate must be in (0,1]\");\n if (tm !== undefined && (!Number.isFinite(tm) || tm < 0))\n throw new EmitterError(\"aieventjs: throttleMs must be a finite number >= 0\");\n // Duck-typed (not instanceof) so polyfilled and cross-realm signals pass;\n // `null` is treated like `undefined` here and below.\n const sig = o?.signal;\n if (\n sig &&\n (typeof sig.addEventListener !== \"function\" || typeof sig.removeEventListener !== \"function\")\n )\n throw new EmitterError(\"aieventjs: signal must be an AbortSignal\");\n if (sig?.aborted) return () => {};\n\n // The checks above keep `ce` off wildcard entries and `r` off typed ones,\n // so one entry shape serves both lists.\n const arr = wild ? w : (t.get(type) ?? []);\n const e: E = { h: handler, u: handler, c: undefined, ce, r: sr, tm };\n const rm = () => {\n const i = arr.indexOf(e);\n if (i >= 0) arr.splice(i, 1);\n kill(e);\n // Prune an emptied typed key. Identity guard: only while `arr` is STILL\n // the array mapped to `type` — a key re-subscribed after pruning gets a\n // new array, so a stale/double unsubscribe must not delete the live key.\n // Never true for \"*\": wildcards live in `w`, not in `t`.\n if (!arr.length && t.get(type) === arr) t.delete(type);\n };\n // rm() makes the entry inert before the call: an outer emit's snapshot\n // may still hold it after a nested emit consumed it (never fire twice).\n if (o?.once)\n e.h = (...a) => {\n rm();\n handler(...a);\n };\n // Wire the signal before touching any list or Map key, so a throwing\n // addEventListener leaves no partial state.\n if (sig) {\n sig.addEventListener(\"abort\", rm, { once: true });\n e.c = () => sig.removeEventListener(\"abort\", rm);\n }\n arr.push(e);\n if (!wild) t.set(type, arr);\n return rm;\n }\n\n // Both overloads (typed and \"*\") delegate to on(): on() routes \"*\" to the\n // wildcard list, so once(\"*\", h) === on(\"*\", h, { once: true }).\n function once(type: string, handler: F): () => void {\n return on(type, handler, { once: true });\n }\n\n function off(type: string, handler?: F): void {\n ck();\n const arr = type === \"*\" ? w : t.get(type);\n if (arr === undefined) return;\n if (handler === undefined) {\n flush(arr);\n arr.length = 0;\n } else {\n // First entry registered with `handler` (matched by the user-provided\n // handler, so once-wrapped entries match too).\n const i = arr.findIndex((e) => e.u === handler);\n if (i >= 0) flush(arr.splice(i, 1));\n }\n if (!arr.length) t.delete(type);\n }\n\n // Inline error policy handler — policy undefined/false → re-throw; true → swallow;\n // function → invoke and swallow; if callback throws, ignore silently.\n // The callback's `type` is always a string: numeric or symbol Events keys\n // reach emit() raw (handlers and Map keys keep them), so coerce here.\n function ap(pol: ErrorPolicy | undefined, err: unknown, k: string, p: unknown): void {\n if (pol === undefined || pol === false) throw err;\n if (typeof pol === \"function\")\n try {\n pol(err, String(k), p);\n } catch {\n /* silent */\n }\n }\n\n function emit(type: string, payload: unknown): void {\n ck();\n // Both slices happen BEFORE any handler call (snapshot-before-iterate);\n // entries removed meanwhile are inert (kill()), so they are skipped.\n const ts = (t.get(type) ?? []).slice();\n const ws = w.slice();\n for (const e of ts) {\n if (gate(e))\n try {\n e.h(payload);\n } catch (err) {\n ap(e.ce !== undefined ? e.ce : cap, err, type, payload);\n }\n }\n for (const e of ws) {\n if (gate(e))\n try {\n e.h(type, payload);\n } catch (err) {\n ap(cap, err, type, payload);\n }\n }\n }\n\n function purge(): void {\n for (const a of [...t.values(), w]) {\n flush(a);\n a.length = 0;\n }\n t.clear();\n }\n\n // _mapSize: test-only observation seam (not on the public Emitter interface).\n // Casted away at the return type; exposes t.size for Map-pruning regression tests.\n return {\n on: on as Emitter<Events>[\"on\"],\n once: once as Emitter<Events>[\"once\"],\n off: off as Emitter<Events>[\"off\"],\n emit: emit as Emitter<Events>[\"emit\"],\n clear() {\n ck();\n purge();\n },\n dispose() {\n if (!d) {\n purge();\n d = true;\n }\n },\n get disposed() {\n return d;\n },\n get _mapSize() {\n return t.size;\n },\n } as unknown as Emitter<Events>;\n}\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -12,8 +12,9 @@ interface EmitterOptions {
|
|
|
12
12
|
* - undefined / false (default) — first throw aborts dispatch (mitt-compatible).
|
|
13
13
|
* - true — swallow; dispatch continues over all handlers in the snapshot.
|
|
14
14
|
* - (err, type, payload) => void — invoked with the unknown error, the
|
|
15
|
-
* event name as string
|
|
16
|
-
*
|
|
15
|
+
* event name as a string (numeric and symbol keys are converted with
|
|
16
|
+
* `String()`), and the payload as unknown. If this callback itself
|
|
17
|
+
* throws, the error is silently ignored.
|
|
17
18
|
*
|
|
18
19
|
* Per-subscription OnOptions.captureErrors overrides this for that handler.
|
|
19
20
|
*/
|
|
@@ -41,6 +42,8 @@ interface OnOptions {
|
|
|
41
42
|
/**
|
|
42
43
|
* Aborting this signal removes the handler. The same effect as calling
|
|
43
44
|
* the returned unsubscribe function. Pre-aborted signals never register.
|
|
45
|
+
* A value without `addEventListener` / `removeEventListener` throws
|
|
46
|
+
* EmitterError; `null` is treated like `undefined`.
|
|
44
47
|
*/
|
|
45
48
|
signal?: AbortSignal;
|
|
46
49
|
/** Auto-remove the handler after the first dispatch. Equivalent to `once()`. */
|
|
@@ -103,17 +106,20 @@ interface Emitter<Events extends Record<string, unknown>> {
|
|
|
103
106
|
*/
|
|
104
107
|
on(type: "*", handler: WildcardHandler<Events>, opts?: OnOptions): () => void;
|
|
105
108
|
/**
|
|
106
|
-
*
|
|
107
|
-
* `on(
|
|
109
|
+
* Wildcard-once: subscribe to every event and auto-remove after the first
|
|
110
|
+
* dispatch. Equivalent to `on("*", handler, { once: true })`: the handler
|
|
111
|
+
* receives `(type, payload)`, fires after type-matched handlers, and goes
|
|
112
|
+
* inert before it is called.
|
|
108
113
|
*
|
|
109
114
|
* @remarks
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
|
|
113
|
-
|
|
115
|
+
* Declared before the typed overload so `"*"` always resolves here, even
|
|
116
|
+
* when `Events` has a string index signature (EVT-B-02).
|
|
117
|
+
*/
|
|
118
|
+
once(type: "*", handler: WildcardHandler<Events>): () => void;
|
|
119
|
+
/**
|
|
120
|
+
* Subscribe and auto-remove after the first dispatch. Equivalent to
|
|
121
|
+
* `on(type, handler, { once: true })`.
|
|
114
122
|
*/
|
|
115
|
-
/** @internal — compile-time rejection: `once("*", handler)` is a type error. */
|
|
116
|
-
once(type: "*", handler: never): never;
|
|
117
123
|
once<K extends keyof Events>(type: K, handler: EventHandler<Events[K]>): () => void;
|
|
118
124
|
/**
|
|
119
125
|
* Imperative unsubscribe. Prefer the unsubscribe function returned by
|
|
@@ -127,10 +133,13 @@ interface Emitter<Events extends Record<string, unknown>> {
|
|
|
127
133
|
off(type: "*", handler?: WildcardHandler<Events>): void;
|
|
128
134
|
/**
|
|
129
135
|
* Dispatch synchronously. Handlers receive `payload`; wildcard handlers
|
|
130
|
-
* receive `(type, payload)`. Handler lists are snapshotted before iteration
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
*
|
|
136
|
+
* receive `(type, payload)`. Handler lists are snapshotted before iteration:
|
|
137
|
+
* a handler added during the dispatch waits for the next `emit()`, and a
|
|
138
|
+
* handler removed during it (unsubscribe, `off`, `clear`, `dispose` or an
|
|
139
|
+
* aborted signal) is skipped for the rest of it. A nested `emit()` from a
|
|
140
|
+
* handler runs to completion before the outer dispatch resumes. By default,
|
|
141
|
+
* the first throwing handler aborts the dispatch; set
|
|
142
|
+
* EmitterOptions.captureHandlerErrors (or per-handler OnOptions.captureErrors)
|
|
134
143
|
* to swallow or report errors and continue.
|
|
135
144
|
*/
|
|
136
145
|
emit<K extends keyof Events>(type: K, payload: Events[K]): void;
|
|
@@ -148,10 +157,12 @@ interface Emitter<Events extends Record<string, unknown>> {
|
|
|
148
157
|
readonly disposed: boolean;
|
|
149
158
|
}
|
|
150
159
|
/**
|
|
151
|
-
* Recoverable emitter error. Thrown by `on()`
|
|
152
|
-
*
|
|
153
|
-
* `
|
|
154
|
-
* `
|
|
160
|
+
* Recoverable emitter error. Thrown by `on()` / `once()` before anything is
|
|
161
|
+
* registered when `handler` is not a function, or when `OnOptions` violates a
|
|
162
|
+
* precondition: `signal` is not an `AbortSignal`; `captureErrors` set on a
|
|
163
|
+
* wildcard `"*"` subscription; `sampleRate` set on a typed subscription;
|
|
164
|
+
* `sampleRate` outside `(0, 1]`; or `throttleMs` non-finite or negative.
|
|
165
|
+
* Messages read `aieventjs: <subject> must be <constraint>`.
|
|
155
166
|
*
|
|
156
167
|
* @public
|
|
157
168
|
*/
|
|
@@ -159,7 +170,10 @@ declare class EmitterError extends Error {
|
|
|
159
170
|
readonly name = "EmitterError";
|
|
160
171
|
}
|
|
161
172
|
/**
|
|
162
|
-
* Thrown by
|
|
173
|
+
* Thrown by `on`/`once`/`emit`/`off`/`clear` when called after
|
|
174
|
+
* {@link Emitter.dispose}. `dispose()` itself never throws — it is
|
|
175
|
+
* idempotent — and unsubscribe functions returned before dispose remain
|
|
176
|
+
* safe no-ops afterward.
|
|
163
177
|
*
|
|
164
178
|
* @public
|
|
165
179
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -12,8 +12,9 @@ interface EmitterOptions {
|
|
|
12
12
|
* - undefined / false (default) — first throw aborts dispatch (mitt-compatible).
|
|
13
13
|
* - true — swallow; dispatch continues over all handlers in the snapshot.
|
|
14
14
|
* - (err, type, payload) => void — invoked with the unknown error, the
|
|
15
|
-
* event name as string
|
|
16
|
-
*
|
|
15
|
+
* event name as a string (numeric and symbol keys are converted with
|
|
16
|
+
* `String()`), and the payload as unknown. If this callback itself
|
|
17
|
+
* throws, the error is silently ignored.
|
|
17
18
|
*
|
|
18
19
|
* Per-subscription OnOptions.captureErrors overrides this for that handler.
|
|
19
20
|
*/
|
|
@@ -41,6 +42,8 @@ interface OnOptions {
|
|
|
41
42
|
/**
|
|
42
43
|
* Aborting this signal removes the handler. The same effect as calling
|
|
43
44
|
* the returned unsubscribe function. Pre-aborted signals never register.
|
|
45
|
+
* A value without `addEventListener` / `removeEventListener` throws
|
|
46
|
+
* EmitterError; `null` is treated like `undefined`.
|
|
44
47
|
*/
|
|
45
48
|
signal?: AbortSignal;
|
|
46
49
|
/** Auto-remove the handler after the first dispatch. Equivalent to `once()`. */
|
|
@@ -103,17 +106,20 @@ interface Emitter<Events extends Record<string, unknown>> {
|
|
|
103
106
|
*/
|
|
104
107
|
on(type: "*", handler: WildcardHandler<Events>, opts?: OnOptions): () => void;
|
|
105
108
|
/**
|
|
106
|
-
*
|
|
107
|
-
* `on(
|
|
109
|
+
* Wildcard-once: subscribe to every event and auto-remove after the first
|
|
110
|
+
* dispatch. Equivalent to `on("*", handler, { once: true })`: the handler
|
|
111
|
+
* receives `(type, payload)`, fires after type-matched handlers, and goes
|
|
112
|
+
* inert before it is called.
|
|
108
113
|
*
|
|
109
114
|
* @remarks
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
|
|
113
|
-
|
|
115
|
+
* Declared before the typed overload so `"*"` always resolves here, even
|
|
116
|
+
* when `Events` has a string index signature (EVT-B-02).
|
|
117
|
+
*/
|
|
118
|
+
once(type: "*", handler: WildcardHandler<Events>): () => void;
|
|
119
|
+
/**
|
|
120
|
+
* Subscribe and auto-remove after the first dispatch. Equivalent to
|
|
121
|
+
* `on(type, handler, { once: true })`.
|
|
114
122
|
*/
|
|
115
|
-
/** @internal — compile-time rejection: `once("*", handler)` is a type error. */
|
|
116
|
-
once(type: "*", handler: never): never;
|
|
117
123
|
once<K extends keyof Events>(type: K, handler: EventHandler<Events[K]>): () => void;
|
|
118
124
|
/**
|
|
119
125
|
* Imperative unsubscribe. Prefer the unsubscribe function returned by
|
|
@@ -127,10 +133,13 @@ interface Emitter<Events extends Record<string, unknown>> {
|
|
|
127
133
|
off(type: "*", handler?: WildcardHandler<Events>): void;
|
|
128
134
|
/**
|
|
129
135
|
* Dispatch synchronously. Handlers receive `payload`; wildcard handlers
|
|
130
|
-
* receive `(type, payload)`. Handler lists are snapshotted before iteration
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
*
|
|
136
|
+
* receive `(type, payload)`. Handler lists are snapshotted before iteration:
|
|
137
|
+
* a handler added during the dispatch waits for the next `emit()`, and a
|
|
138
|
+
* handler removed during it (unsubscribe, `off`, `clear`, `dispose` or an
|
|
139
|
+
* aborted signal) is skipped for the rest of it. A nested `emit()` from a
|
|
140
|
+
* handler runs to completion before the outer dispatch resumes. By default,
|
|
141
|
+
* the first throwing handler aborts the dispatch; set
|
|
142
|
+
* EmitterOptions.captureHandlerErrors (or per-handler OnOptions.captureErrors)
|
|
134
143
|
* to swallow or report errors and continue.
|
|
135
144
|
*/
|
|
136
145
|
emit<K extends keyof Events>(type: K, payload: Events[K]): void;
|
|
@@ -148,10 +157,12 @@ interface Emitter<Events extends Record<string, unknown>> {
|
|
|
148
157
|
readonly disposed: boolean;
|
|
149
158
|
}
|
|
150
159
|
/**
|
|
151
|
-
* Recoverable emitter error. Thrown by `on()`
|
|
152
|
-
*
|
|
153
|
-
* `
|
|
154
|
-
* `
|
|
160
|
+
* Recoverable emitter error. Thrown by `on()` / `once()` before anything is
|
|
161
|
+
* registered when `handler` is not a function, or when `OnOptions` violates a
|
|
162
|
+
* precondition: `signal` is not an `AbortSignal`; `captureErrors` set on a
|
|
163
|
+
* wildcard `"*"` subscription; `sampleRate` set on a typed subscription;
|
|
164
|
+
* `sampleRate` outside `(0, 1]`; or `throttleMs` non-finite or negative.
|
|
165
|
+
* Messages read `aieventjs: <subject> must be <constraint>`.
|
|
155
166
|
*
|
|
156
167
|
* @public
|
|
157
168
|
*/
|
|
@@ -159,7 +170,10 @@ declare class EmitterError extends Error {
|
|
|
159
170
|
readonly name = "EmitterError";
|
|
160
171
|
}
|
|
161
172
|
/**
|
|
162
|
-
* Thrown by
|
|
173
|
+
* Thrown by `on`/`once`/`emit`/`off`/`clear` when called after
|
|
174
|
+
* {@link Emitter.dispose}. `dispose()` itself never throws — it is
|
|
175
|
+
* idempotent — and unsubscribe functions returned before dispose remain
|
|
176
|
+
* safe no-ops afterward.
|
|
163
177
|
*
|
|
164
178
|
* @public
|
|
165
179
|
*/
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var
|
|
1
|
+
var d=class extends Error{name="EmitterError"},y=class extends Error{name="EmitterDisposedError"},j=()=>{};function O(n){n.c?.(),n.c=void 0,n.h=j;}function h(n){for(let a of n)O(a);}function K(n){if(n.r!==void 0&&Math.random()>=n.r)return false;if(n.tm){let a=performance.now();if(n.ts!==void 0&&a-n.ts<n.tm)return false;n.ts=a;}return true}function M(n){let a=n?.captureHandlerErrors,s=new Map,v=[],E=false;function p(){if(E)throw new y("aieventjs: emitter has been disposed")}function b(e,t,o){p();let i=o?.sampleRate,r=o?.throttleMs,c=o?.captureErrors,g=e==="*";if(typeof t!="function")throw new d("aieventjs: handler must be a function");if(g){if(c!==void 0)throw new d("aieventjs: captureErrors must be unset on *")}else if(i!==void 0)throw new d("aieventjs: sampleRate must be unset on typed events");if(i!==void 0&&(!Number.isFinite(i)||i<=0||i>1))throw new d("aieventjs: sampleRate must be in (0,1]");if(r!==void 0&&(!Number.isFinite(r)||r<0))throw new d("aieventjs: throttleMs must be a finite number >= 0");let f=o?.signal;if(f&&(typeof f.addEventListener!="function"||typeof f.removeEventListener!="function"))throw new d("aieventjs: signal must be an AbortSignal");if(f?.aborted)return ()=>{};let u=g?v:s.get(e)??[],l={h:t,u:t,c:void 0,ce:c,r:i,tm:r},m=()=>{let w=u.indexOf(l);w>=0&&u.splice(w,1),O(l),!u.length&&s.get(e)===u&&s.delete(e);};return o?.once&&(l.h=(...w)=>{m(),t(...w);}),f&&(f.addEventListener("abort",m,{once:true}),l.c=()=>f.removeEventListener("abort",m)),u.push(l),g||s.set(e,u),m}function H(e,t){return b(e,t,{once:true})}function F(e,t){p();let o=e==="*"?v:s.get(e);if(o!==void 0){if(t===void 0)h(o),o.length=0;else {let i=o.findIndex(r=>r.u===t);i>=0&&h(o.splice(i,1));}o.length||s.delete(e);}}function k(e,t,o,i){if(e===void 0||e===false)throw t;if(typeof e=="function")try{e(t,String(o),i);}catch{}}function R(e,t){p();let o=(s.get(e)??[]).slice(),i=v.slice();for(let r of o)if(K(r))try{r.h(t);}catch(c){k(r.ce!==void 0?r.ce:a,c,e,t);}for(let r of i)if(K(r))try{r.h(e,t);}catch(c){k(a,c,e,t);}}function x(){for(let e of [...s.values(),v])h(e),e.length=0;s.clear();}return {on:b,once:H,off:F,emit:R,clear(){p(),x();},dispose(){E||(x(),E=true);},get disposed(){return E},get _mapSize(){return s.size}}}export{y as EmitterDisposedError,d as EmitterError,M as createEmitter};//# sourceMappingURL=index.js.map
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":["EmitterError","EmitterDisposedError","rmByUser","arr","user","i","e","flush","sub","sig","rm","fn","createEmitter","opts","cap","t","w","d","ck","ga","k","a","on","type","handler","o","sr","tm2","tp","p","ce","prune","unsub","once","off","ap","pol","err","emit","payload","ts","ws","now","purge"],"mappings":"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","file":"index.js","sourcesContent":["// aieventjs — small, strict, typed event emitter for the ai*js family.\n//\n// v0.1.0: full implementation of the frozen API surface. Mitt-compatible\n// snapshot semantics, wildcard \"*\" handler, AbortSignal integration, once,\n// idempotent dispose, destructurable methods (no `this`).\n\n/**\n * Configuration for {@link createEmitter}. Controls the default error\n * policy for handlers thrown during `emit()`; per-handler\n * {@link OnOptions.captureErrors} overrides this default.\n *\n * @public\n */\nexport interface EmitterOptions {\n /**\n * Default error policy for all handlers when they throw during emit().\n *\n * - undefined / false (default) — first throw aborts dispatch (mitt-compatible).\n * - true — swallow; dispatch continues over all handlers in the snapshot.\n * - (err, type, payload) => void — invoked with the unknown error, the\n * event name as string, and the payload as unknown. If this callback\n * itself throws, the error is silently ignored.\n *\n * Per-subscription OnOptions.captureErrors overrides this for that handler.\n */\n captureHandlerErrors?: boolean | ((err: unknown, type: string, payload: unknown) => void);\n}\n\n/**\n * Handler invoked for a single typed event.\n *\n * @public\n */\nexport type EventHandler<Payload> = (payload: Payload) => void;\n\n/**\n * Handler invoked for the wildcard `\"*\"` subscription. Receives the actual\n * event type alongside the payload.\n *\n * @public\n */\nexport type WildcardHandler<Events extends Record<string, unknown>> = <K extends keyof Events>(\n type: K,\n payload: Events[K],\n) => void;\n\n/**\n * Subscription options accepted by {@link Emitter.on}.\n *\n * @public\n */\nexport interface OnOptions {\n /**\n * Aborting this signal removes the handler. The same effect as calling\n * the returned unsubscribe function. Pre-aborted signals never register.\n */\n signal?: AbortSignal;\n\n /** Auto-remove the handler after the first dispatch. Equivalent to `once()`. */\n once?: boolean;\n\n /**\n * Override emitter-level captureHandlerErrors for this handler.\n * - undefined — fall through to emitter-level.\n * - false — force re-throw, even when emitter-level is true / callback.\n * - true — swallow.\n * - (err, type, payload) => void — same semantics as the emitter-level callback.\n *\n * Throws EmitterError if set on a wildcard \"*\" subscription.\n * @invariant does not break snapshot-before-iterate semantics.\n */\n captureErrors?: boolean | ((err: unknown, type: string, payload: unknown) => void);\n\n /**\n * Wildcard \"*\" only. Probability in (0, 1] that a dispatch reaches this\n * handler. Math.random() is sampled per dispatch. Values <= 0 or > 1 are\n * rejected at on() time.\n *\n * Throws EmitterError if set on a typed handler.\n */\n sampleRate?: number;\n\n /**\n * Per-handler leading-edge throttle. Minimum milliseconds between successive\n * calls to this handler. The first dispatch after subscription always fires;\n * subsequent dispatches within `throttleMs` are dropped (not queued).\n * Uses `performance.now()` (monotonic). 0 = no throttle. Non-finite or\n * negative values are rejected at `on()` time.\n *\n * Valid on both typed and wildcard `\"*\"` subscriptions (since v0.5.3); each\n * handler keeps its own throttle clock. Useful for per-event HUD throttling,\n * e.g. a `credits/change` event that fires every frame.\n *\n * @remarks\n * The throttle clock uses `performance.now()`, which is monotonic and\n * unaffected by system-clock corrections (NTP step-backs, manual adjustments).\n * This ensures handlers are never silently muted by a wall-clock regression.\n */\n throttleMs?: number;\n}\n\n/**\n * Strongly-typed event emitter. Subscribe with {@link Emitter.on} (returns\n * an unsubscribe function), dispatch with {@link Emitter.emit}, dispose\n * with {@link Emitter.dispose} when finished.\n *\n * @typeParam Events — a string-keyed map from event name to payload type.\n * @public\n */\nexport interface Emitter<Events extends Record<string, unknown>> {\n /**\n * Subscribe to a single event type. Returns an unsubscribe function;\n * calling it (or aborting `opts.signal`) removes the handler.\n */\n on<K extends keyof Events>(\n type: K,\n handler: EventHandler<Events[K]>,\n opts?: OnOptions,\n ): () => void;\n\n /**\n * Subscribe to every event with a single handler that receives\n * `(type, payload)`. Wildcard handlers fire AFTER type-matched\n * handlers — same ordering as `mitt`.\n */\n on(type: \"*\", handler: WildcardHandler<Events>, opts?: OnOptions): () => void;\n\n /**\n * Subscribe and auto-remove after the first dispatch. Equivalent to\n * `on(type, handler, { once: true })`.\n *\n * @remarks\n * **`\"*\"` is not a valid `type` argument for `once()`.** The wildcard is\n * handled by the `on(\"*\", handler, { once: true })` overload instead.\n * The explicit rejection overload below ensures `once(\"*\", ...)` is a\n * compile-time error (handler typed as `never`). (EVT-B-02)\n */\n /** @internal — compile-time rejection: `once(\"*\", handler)` is a type error. */\n once(type: \"*\", handler: never): never;\n once<K extends keyof Events>(type: K, handler: EventHandler<Events[K]>): () => void;\n\n /**\n * Imperative unsubscribe. Prefer the unsubscribe function returned by\n * `on()` — it's faster (no reference lookup) and survives renames.\n * If `handler` is omitted, removes every handler for `type`.\n */\n off<K extends keyof Events>(type: K, handler?: EventHandler<Events[K]>): void;\n\n /**\n * Imperative wildcard unsubscribe.\n */\n off(type: \"*\", handler?: WildcardHandler<Events>): void;\n\n /**\n * Dispatch synchronously. Handlers receive `payload`; wildcard handlers\n * receive `(type, payload)`. Handler lists are snapshotted before iteration,\n * so removing a handler inside its own callback does not skip subsequent\n * handlers. By default, the first throwing handler aborts the dispatch;\n * set EmitterOptions.captureHandlerErrors (or per-handler OnOptions.captureErrors)\n * to swallow or report errors and continue.\n */\n emit<K extends keyof Events>(type: K, payload: Events[K]): void;\n\n /**\n * Remove every handler for every event (including wildcards). The\n * emitter remains usable. Use {@link dispose} for permanent teardown.\n */\n clear(): void;\n\n /**\n * Idempotent teardown. Drops every handler; subsequent `on` / `once` /\n * `emit` / `off` / `clear` throw {@link EmitterDisposedError}.\n */\n dispose(): void;\n\n /** `true` once {@link dispose} has been called. */\n readonly disposed: boolean;\n}\n\n/**\n * Recoverable emitter error. Thrown by `on()` when `OnOptions` violates a\n * precondition: `captureErrors` set on a wildcard `\"*\"` subscription;\n * `sampleRate` set on a typed subscription; `sampleRate` outside `(0, 1]`; or\n * `throttleMs` non-finite or negative.\n *\n * @public\n */\nexport class EmitterError extends Error {\n override readonly name = \"EmitterError\";\n}\n\n/**\n * Thrown by any emitter method called after {@link Emitter.dispose}.\n *\n * @public\n */\nexport class EmitterDisposedError extends Error {\n override readonly name = \"EmitterDisposedError\";\n}\n\n// ---------------------------------------------------------------------------\n// Internal types\n// ---------------------------------------------------------------------------\n\n// Mutable `c` field (not optional `?:`) avoids exactOptionalPropertyTypes TS2412\n// when assigning undefined. Short field names reduce minified output size.\ntype ErrorPolicy = boolean | ((err: unknown, type: string, payload: unknown) => void);\n\ninterface E<H> {\n h: H; // handler (may be a once-wrapper)\n c: (() => void) | undefined; // abortCleanup\n u: H; // user-provided handler (off matching)\n // v0.3.0: per-handler error policy and throttle/sample state.\n // Fields typed as `T | undefined` (not just `T`) so that exactOptionalPropertyTypes\n // permits assigning `undefined` in object literals (avoids TS2375).\n ce?: ErrorPolicy | undefined; // captureErrors override (typed only)\n r?: number | undefined; // sampleRate (wildcard only)\n tm?: number | undefined; // throttleMs (typed or wildcard; v0.5.3)\n ts?: number | undefined; // last call timestamp — mutated during dispatch (throttle clock)\n}\n\ntype AH = EventHandler<unknown>;\ntype WH = WildcardHandler<Record<string, unknown>>;\n\n// Remove one entry by user-identity from an array; run its abort cleanup.\nfunction rmByUser<H>(arr: E<H>[], user: H): void {\n const i = arr.findIndex((e) => e.u === user);\n if (i >= 0) {\n const e = arr[i];\n if (e !== undefined) {\n e.c?.();\n e.c = undefined;\n }\n arr.splice(i, 1);\n }\n}\n\n// Flush all abort cleanups from an array (for clear / dispose).\nfunction flush<H>(arr: E<H>[]): void {\n for (const e of arr) {\n e.c?.();\n e.c = undefined;\n }\n}\n\n// Push entry onto arr, wire AbortSignal, return unsubscribe.\nfunction sub<H>(arr: E<H>[], e: E<H>, sig: AbortSignal | undefined): () => void {\n arr.push(e);\n const rm = () => {\n const i = arr.indexOf(e);\n if (i >= 0) arr.splice(i, 1);\n e.c?.();\n e.c = undefined;\n };\n if (sig !== undefined) {\n const fn = () => rm();\n sig.addEventListener(\"abort\", fn, { once: true });\n e.c = () => sig.removeEventListener(\"abort\", fn);\n }\n return rm;\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Construct a strongly-typed event emitter.\n *\n * @remarks\n * Declare the event map with a `type` alias, not an `interface`. The `Events`\n * generic is constrained to `Record<string, unknown>`, and a *plain* TypeScript\n * `interface` has no implicit index signature, so it fails the constraint with\n * *\"Index signature for type 'string' is missing in type ...\"*. A `type` object\n * literal satisfies the constraint structurally. (An `interface` with an explicit\n * index signature or `extends Record<string, unknown>` also compiles, but widens\n * `keyof Events` to `string`, losing strict event-name checking.)\n *\n * ```ts\n * // ❌ interface — fails the Record<string, unknown> constraint\n * interface Events { \"user:login\": { id: string } }\n * const bus = createEmitter<Events>(); // TS2344\n *\n * // ✅ type — satisfies the constraint\n * type Events = { \"user:login\": { id: string } };\n * const bus = createEmitter<Events>();\n * ```\n *\n * @example\n * ```ts\n * import { createEmitter } from \"aieventjs\";\n *\n * type Events = {\n * \"user:login\": { id: string };\n * \"user:logout\": void;\n * };\n *\n * const bus = createEmitter<Events>();\n *\n * const off = bus.on(\"user:login\", (u) => console.log(\"hi\", u.id));\n * bus.emit(\"user:login\", { id: \"alice\" });\n * off();\n *\n * bus.on(\"*\", (type, payload) => console.log(\"event\", type, payload));\n * ```\n *\n * @public\n */\nexport function createEmitter<Events extends Record<string, unknown> = Record<string, unknown>>(\n opts?: EmitterOptions,\n): Emitter<Events> {\n const cap = opts?.captureHandlerErrors;\n\n const t: Map<string, E<AH>[]> = new Map();\n const w: E<WH>[] = [];\n let d = false;\n\n function ck(): void {\n if (d) throw new EmitterDisposedError(\"aieventjs: emitter has been disposed\");\n }\n\n // Get or create typed handler array for a key.\n function ga(k: string): E<AH>[] {\n let a = t.get(k);\n if (a === undefined) {\n a = [];\n t.set(k, a);\n }\n return a;\n }\n\n function on(type: string | \"*\", handler: AH | WH, o?: OnOptions): () => void {\n ck();\n // v0.3.0 guards: cross-domain options + range checks.\n // v0.5.3: throttleMs is now valid on typed handlers too (per-handler clock);\n // sampleRate remains wildcard-only.\n const sr = o?.sampleRate;\n const tm2 = o?.throttleMs;\n if (type === \"*\") {\n if (o?.captureErrors !== undefined)\n throw new EmitterError(\"aieventjs: captureErrors invalid on *\");\n } else {\n if (sr !== undefined) throw new EmitterError(\"aieventjs: sampleRate wildcard-only\");\n }\n if (sr !== undefined && (!Number.isFinite(sr) || sr <= 0 || sr > 1))\n throw new EmitterError(\"aieventjs: sampleRate must be in (0,1]\");\n if (tm2 !== undefined && (!Number.isFinite(tm2) || tm2 < 0))\n throw new EmitterError(\"aieventjs: throttleMs must be >= 0\");\n const sig = o?.signal;\n if (sig?.aborted) return () => {};\n\n if (type === \"*\") {\n const fn = handler as WH;\n if (o?.once) {\n const e: E<WH> = {\n h: (tp, p) => {\n rm();\n fn(tp, p);\n },\n u: fn,\n c: undefined,\n r: sr,\n tm: tm2,\n };\n const rm = sub(w, e, sig);\n return rm;\n }\n return sub(w, { h: fn, u: fn, c: undefined, r: sr, tm: tm2 }, sig);\n }\n\n const fn = handler as AH;\n const ce = o?.captureErrors;\n const arr = ga(type);\n const prune = () => {\n // Identity guard: only delete the key when `arr` is STILL the array\n // currently mapped. ga() mints a NEW array when a deleted key is\n // re-subscribed, so a stale/double unsub of the original handler must\n // not prune the live re-subscribed key (idempotency).\n if (!arr.length && t.get(type) === arr) t.delete(type);\n };\n if (o?.once) {\n let unsub!: () => void;\n const rm = () => {\n unsub();\n prune();\n };\n const e: E<AH> = {\n h: (p) => {\n rm();\n fn(p);\n },\n u: fn,\n c: undefined,\n ce: ce,\n tm: tm2,\n };\n unsub = sub(arr, e, sig);\n return rm;\n }\n const unsub = sub(arr, { h: fn, u: fn, c: undefined, ce: ce, tm: tm2 }, sig);\n return () => {\n unsub();\n prune();\n };\n }\n\n function once<K extends keyof Events>(type: K, handler: EventHandler<Events[K]>): () => void {\n return on(type as string, handler as AH, { once: true });\n }\n\n function off(type: string | \"*\", handler?: AH | WH): void {\n ck();\n if (type === \"*\") {\n if (handler === undefined) {\n flush(w);\n w.length = 0;\n } else {\n rmByUser(w, handler as WH);\n }\n return;\n }\n const arr = t.get(type);\n if (arr === undefined) return;\n if (handler === undefined) {\n flush(arr);\n arr.length = 0;\n t.delete(type);\n } else {\n rmByUser(arr, handler as AH);\n if (!arr.length) t.delete(type);\n }\n }\n\n // Inline error policy handler — policy undefined/false → re-throw; true → swallow;\n // function → invoke and swallow; if callback throws, ignore silently.\n function ap(pol: ErrorPolicy | undefined, err: unknown, k: string, p: unknown): void {\n if (pol === undefined || pol === false) throw err;\n if (typeof pol === \"function\")\n try {\n pol(err, k, p);\n } catch {\n /* silent */\n }\n }\n\n function emit<K extends keyof Events>(type: K, payload: Events[K]): void {\n ck();\n // Both slices happen BEFORE any handler call (snapshot-before-iterate).\n const k = type as string;\n const p = payload as unknown;\n const ts = (t.get(k) ?? []).slice();\n const ws = w.slice();\n for (const e of ts) {\n if (e.tm) {\n const now = performance.now();\n if (e.ts !== undefined && now - e.ts < e.tm) continue;\n e.ts = now;\n }\n try {\n e.h(p);\n } catch (err) {\n ap(e.ce !== undefined ? e.ce : cap, err, k, p);\n }\n }\n for (const e of ws) {\n if (e.r !== undefined && Math.random() >= e.r) continue;\n if (e.tm) {\n const now = performance.now();\n if (e.ts !== undefined && now - e.ts < e.tm) continue;\n e.ts = now;\n }\n try {\n e.h(k, p as never);\n } catch (err) {\n ap(cap, err, k, p);\n }\n }\n }\n\n function purge(): void {\n for (const a of t.values()) {\n flush(a);\n a.length = 0;\n }\n flush(w);\n t.clear();\n w.length = 0;\n }\n\n // _mapSize: test-only observation seam (not on the public Emitter interface).\n // Casted away at the return type; exposes t.size for Map-pruning regression tests.\n return {\n on: on as Emitter<Events>[\"on\"],\n once: once as Emitter<Events>[\"once\"],\n off: off as Emitter<Events>[\"off\"],\n emit,\n clear() {\n ck();\n purge();\n },\n dispose() {\n if (!d) {\n purge();\n d = true;\n }\n },\n get disposed() {\n return d;\n },\n get _mapSize() {\n return t.size;\n },\n } as unknown as Emitter<Events>;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["EmitterError","EmitterDisposedError","N","kill","e","flush","arr","gate","now","createEmitter","opts","cap","t","w","d","ck","on","type","handler","sr","tm","ce","wild","sig","rm","i","a","once","off","ap","pol","err","k","p","emit","payload","ts","ws","purge"],"mappings":"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","file":"index.js","sourcesContent":["// aieventjs — small, strict, typed event emitter for the ai*js family.\n//\n// Mitt-shaped API: snapshot dispatch (handlers removed mid-dispatch are\n// skipped, per the ai*js fan-out rule), wildcard \"*\" handler, AbortSignal\n// integration, once, idempotent dispose, destructurable methods (no `this`).\n\n/**\n * Configuration for {@link createEmitter}. Controls the default error\n * policy for handlers thrown during `emit()`; per-handler\n * {@link OnOptions.captureErrors} overrides this default.\n *\n * @public\n */\nexport interface EmitterOptions {\n /**\n * Default error policy for all handlers when they throw during emit().\n *\n * - undefined / false (default) — first throw aborts dispatch (mitt-compatible).\n * - true — swallow; dispatch continues over all handlers in the snapshot.\n * - (err, type, payload) => void — invoked with the unknown error, the\n * event name as a string (numeric and symbol keys are converted with\n * `String()`), and the payload as unknown. If this callback itself\n * throws, the error is silently ignored.\n *\n * Per-subscription OnOptions.captureErrors overrides this for that handler.\n */\n captureHandlerErrors?: boolean | ((err: unknown, type: string, payload: unknown) => void);\n}\n\n/**\n * Handler invoked for a single typed event.\n *\n * @public\n */\nexport type EventHandler<Payload> = (payload: Payload) => void;\n\n/**\n * Handler invoked for the wildcard `\"*\"` subscription. Receives the actual\n * event type alongside the payload.\n *\n * @public\n */\nexport type WildcardHandler<Events extends Record<string, unknown>> = <K extends keyof Events>(\n type: K,\n payload: Events[K],\n) => void;\n\n/**\n * Subscription options accepted by {@link Emitter.on}.\n *\n * @public\n */\nexport interface OnOptions {\n /**\n * Aborting this signal removes the handler. The same effect as calling\n * the returned unsubscribe function. Pre-aborted signals never register.\n * A value without `addEventListener` / `removeEventListener` throws\n * EmitterError; `null` is treated like `undefined`.\n */\n signal?: AbortSignal;\n\n /** Auto-remove the handler after the first dispatch. Equivalent to `once()`. */\n once?: boolean;\n\n /**\n * Override emitter-level captureHandlerErrors for this handler.\n * - undefined — fall through to emitter-level.\n * - false — force re-throw, even when emitter-level is true / callback.\n * - true — swallow.\n * - (err, type, payload) => void — same semantics as the emitter-level callback.\n *\n * Throws EmitterError if set on a wildcard \"*\" subscription.\n * @invariant does not break snapshot-before-iterate semantics.\n */\n captureErrors?: boolean | ((err: unknown, type: string, payload: unknown) => void);\n\n /**\n * Wildcard \"*\" only. Probability in (0, 1] that a dispatch reaches this\n * handler. Math.random() is sampled per dispatch. Values <= 0 or > 1 are\n * rejected at on() time.\n *\n * Throws EmitterError if set on a typed handler.\n */\n sampleRate?: number;\n\n /**\n * Per-handler leading-edge throttle. Minimum milliseconds between successive\n * calls to this handler. The first dispatch after subscription always fires;\n * subsequent dispatches within `throttleMs` are dropped (not queued).\n * Uses `performance.now()` (monotonic). 0 = no throttle. Non-finite or\n * negative values are rejected at `on()` time.\n *\n * Valid on both typed and wildcard `\"*\"` subscriptions (since v0.5.3); each\n * handler keeps its own throttle clock. Useful for per-event HUD throttling,\n * e.g. a `credits/change` event that fires every frame.\n *\n * @remarks\n * The throttle clock uses `performance.now()`, which is monotonic and\n * unaffected by system-clock corrections (NTP step-backs, manual adjustments).\n * This ensures handlers are never silently muted by a wall-clock regression.\n */\n throttleMs?: number;\n}\n\n/**\n * Strongly-typed event emitter. Subscribe with {@link Emitter.on} (returns\n * an unsubscribe function), dispatch with {@link Emitter.emit}, dispose\n * with {@link Emitter.dispose} when finished.\n *\n * @typeParam Events — a string-keyed map from event name to payload type.\n * @public\n */\nexport interface Emitter<Events extends Record<string, unknown>> {\n /**\n * Subscribe to a single event type. Returns an unsubscribe function;\n * calling it (or aborting `opts.signal`) removes the handler.\n */\n on<K extends keyof Events>(\n type: K,\n handler: EventHandler<Events[K]>,\n opts?: OnOptions,\n ): () => void;\n\n /**\n * Subscribe to every event with a single handler that receives\n * `(type, payload)`. Wildcard handlers fire AFTER type-matched\n * handlers — same ordering as `mitt`.\n */\n on(type: \"*\", handler: WildcardHandler<Events>, opts?: OnOptions): () => void;\n\n /**\n * Wildcard-once: subscribe to every event and auto-remove after the first\n * dispatch. Equivalent to `on(\"*\", handler, { once: true })`: the handler\n * receives `(type, payload)`, fires after type-matched handlers, and goes\n * inert before it is called.\n *\n * @remarks\n * Declared before the typed overload so `\"*\"` always resolves here, even\n * when `Events` has a string index signature (EVT-B-02).\n */\n once(type: \"*\", handler: WildcardHandler<Events>): () => void;\n\n /**\n * Subscribe and auto-remove after the first dispatch. Equivalent to\n * `on(type, handler, { once: true })`.\n */\n once<K extends keyof Events>(type: K, handler: EventHandler<Events[K]>): () => void;\n\n /**\n * Imperative unsubscribe. Prefer the unsubscribe function returned by\n * `on()` — it's faster (no reference lookup) and survives renames.\n * If `handler` is omitted, removes every handler for `type`.\n */\n off<K extends keyof Events>(type: K, handler?: EventHandler<Events[K]>): void;\n\n /**\n * Imperative wildcard unsubscribe.\n */\n off(type: \"*\", handler?: WildcardHandler<Events>): void;\n\n /**\n * Dispatch synchronously. Handlers receive `payload`; wildcard handlers\n * receive `(type, payload)`. Handler lists are snapshotted before iteration:\n * a handler added during the dispatch waits for the next `emit()`, and a\n * handler removed during it (unsubscribe, `off`, `clear`, `dispose` or an\n * aborted signal) is skipped for the rest of it. A nested `emit()` from a\n * handler runs to completion before the outer dispatch resumes. By default,\n * the first throwing handler aborts the dispatch; set\n * EmitterOptions.captureHandlerErrors (or per-handler OnOptions.captureErrors)\n * to swallow or report errors and continue.\n */\n emit<K extends keyof Events>(type: K, payload: Events[K]): void;\n\n /**\n * Remove every handler for every event (including wildcards). The\n * emitter remains usable. Use {@link dispose} for permanent teardown.\n */\n clear(): void;\n\n /**\n * Idempotent teardown. Drops every handler; subsequent `on` / `once` /\n * `emit` / `off` / `clear` throw {@link EmitterDisposedError}.\n */\n dispose(): void;\n\n /** `true` once {@link dispose} has been called. */\n readonly disposed: boolean;\n}\n\n/**\n * Recoverable emitter error. Thrown by `on()` / `once()` before anything is\n * registered when `handler` is not a function, or when `OnOptions` violates a\n * precondition: `signal` is not an `AbortSignal`; `captureErrors` set on a\n * wildcard `\"*\"` subscription; `sampleRate` set on a typed subscription;\n * `sampleRate` outside `(0, 1]`; or `throttleMs` non-finite or negative.\n * Messages read `aieventjs: <subject> must be <constraint>`.\n *\n * @public\n */\nexport class EmitterError extends Error {\n override readonly name = \"EmitterError\";\n}\n\n/**\n * Thrown by `on`/`once`/`emit`/`off`/`clear` when called after\n * {@link Emitter.dispose}. `dispose()` itself never throws — it is\n * idempotent — and unsubscribe functions returned before dispose remain\n * safe no-ops afterward.\n *\n * @public\n */\nexport class EmitterDisposedError extends Error {\n override readonly name = \"EmitterDisposedError\";\n}\n\n// ---------------------------------------------------------------------------\n// Internal types\n// ---------------------------------------------------------------------------\n\ntype ErrorPolicy = boolean | ((err: unknown, type: string, payload: unknown) => void);\n\n// Stored handler shape: typed entries are called as h(payload), wildcard\n// entries as h(type, payload).\ntype F = (...a: unknown[]) => void;\n\n// One subscription (typed or wildcard). Short field names reduce minified\n// output size. Fields are typed `T | undefined` so exactOptionalPropertyTypes\n// permits assigning `undefined` (avoids TS2375 / TS2412).\ninterface E {\n h: F; // handler: the user's, a once-wrapper, or N once removed\n c: (() => void) | undefined; // abortCleanup\n u: F; // user-provided handler (off matching)\n ce: ErrorPolicy | undefined; // captureErrors override (typed only)\n r: number | undefined; // sampleRate (wildcard only)\n tm: number | undefined; // throttleMs (typed or wildcard; v0.5.3)\n ts?: number | undefined; // last call timestamp — mutated during dispatch (throttle clock)\n}\n\n// Inert handler swapped into every removed entry (see kill()).\nconst N: F = () => {};\n\n// Detach an entry on every removal path (unsubscribe, abort, off, clear,\n// dispose): run its abort cleanup and make it inert, so an emit() whose\n// snapshot still holds the entry skips it for the rest of that dispatch\n// (ai*js fan-out re-entrancy rule). Idempotent.\nfunction kill(e: E): void {\n e.c?.();\n e.c = undefined;\n e.h = N;\n}\n\n// Detach every entry of an array (clear / dispose / off without handler).\nfunction flush(arr: E[]): void {\n for (const e of arr) kill(e);\n}\n\n// Per-dispatch gate shared by the typed and wildcard loops of emit(): the\n// wildcard-only sampleRate draw, then the leading-edge throttle. The throttle\n// timestamp is written before the handler runs, so a throwing handler still\n// consumes its window, and a sample miss never touches it.\nfunction gate(e: E): boolean {\n if (e.r !== undefined && Math.random() >= e.r) return false;\n if (e.tm) {\n const now = performance.now();\n if (e.ts !== undefined && now - e.ts < e.tm) return false;\n e.ts = now;\n }\n return true;\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Construct a strongly-typed event emitter.\n *\n * @remarks\n * Declare the event map with a `type` alias, not an `interface`. The `Events`\n * generic is constrained to `Record<string, unknown>`, and a *plain* TypeScript\n * `interface` has no implicit index signature, so it fails the constraint with\n * *\"Index signature for type 'string' is missing in type ...\"*. A `type` object\n * literal satisfies the constraint structurally. (An `interface` with an explicit\n * index signature or `extends Record<string, unknown>` also compiles, but widens\n * `keyof Events` to `string`, losing strict event-name checking.)\n *\n * ```ts\n * // ❌ interface — fails the Record<string, unknown> constraint\n * interface Events { \"user:login\": { id: string } }\n * const bus = createEmitter<Events>(); // TS2344\n *\n * // ✅ type — satisfies the constraint\n * type Events = { \"user:login\": { id: string } };\n * const bus = createEmitter<Events>();\n * ```\n *\n * @example\n * ```ts\n * import { createEmitter } from \"aieventjs\";\n *\n * type Events = {\n * \"user:login\": { id: string };\n * \"user:logout\": void;\n * };\n *\n * const bus = createEmitter<Events>();\n *\n * const off = bus.on(\"user:login\", (u) => console.log(\"hi\", u.id));\n * bus.emit(\"user:login\", { id: \"alice\" });\n * off();\n *\n * bus.on(\"*\", (type, payload) => console.log(\"event\", type, payload));\n * ```\n *\n * @public\n */\nexport function createEmitter<Events extends Record<string, unknown> = Record<string, unknown>>(\n opts?: EmitterOptions,\n): Emitter<Events> {\n const cap = opts?.captureHandlerErrors;\n\n const t: Map<string, E[]> = new Map();\n const w: E[] = [];\n let d = false;\n\n function ck(): void {\n if (d) throw new EmitterDisposedError(\"aieventjs: emitter has been disposed\");\n }\n\n function on(type: string, handler: F, o?: OnOptions): () => void {\n ck();\n // Validate everything before any side effect; misuse is EmitterError with\n // the ai*js `aieventjs: <subject> must be <constraint>` message shape.\n // Cross-domain options: captureErrors is typed-only, sampleRate\n // wildcard-only; throttleMs is valid on both (v0.5.3, per-handler clock).\n const sr = o?.sampleRate;\n const tm = o?.throttleMs;\n const ce = o?.captureErrors;\n const wild = type === \"*\";\n if (typeof handler !== \"function\")\n throw new EmitterError(\"aieventjs: handler must be a function\");\n if (wild) {\n if (ce !== undefined) throw new EmitterError(\"aieventjs: captureErrors must be unset on *\");\n } else if (sr !== undefined)\n throw new EmitterError(\"aieventjs: sampleRate must be unset on typed events\");\n if (sr !== undefined && (!Number.isFinite(sr) || sr <= 0 || sr > 1))\n throw new EmitterError(\"aieventjs: sampleRate must be in (0,1]\");\n if (tm !== undefined && (!Number.isFinite(tm) || tm < 0))\n throw new EmitterError(\"aieventjs: throttleMs must be a finite number >= 0\");\n // Duck-typed (not instanceof) so polyfilled and cross-realm signals pass;\n // `null` is treated like `undefined` here and below.\n const sig = o?.signal;\n if (\n sig &&\n (typeof sig.addEventListener !== \"function\" || typeof sig.removeEventListener !== \"function\")\n )\n throw new EmitterError(\"aieventjs: signal must be an AbortSignal\");\n if (sig?.aborted) return () => {};\n\n // The checks above keep `ce` off wildcard entries and `r` off typed ones,\n // so one entry shape serves both lists.\n const arr = wild ? w : (t.get(type) ?? []);\n const e: E = { h: handler, u: handler, c: undefined, ce, r: sr, tm };\n const rm = () => {\n const i = arr.indexOf(e);\n if (i >= 0) arr.splice(i, 1);\n kill(e);\n // Prune an emptied typed key. Identity guard: only while `arr` is STILL\n // the array mapped to `type` — a key re-subscribed after pruning gets a\n // new array, so a stale/double unsubscribe must not delete the live key.\n // Never true for \"*\": wildcards live in `w`, not in `t`.\n if (!arr.length && t.get(type) === arr) t.delete(type);\n };\n // rm() makes the entry inert before the call: an outer emit's snapshot\n // may still hold it after a nested emit consumed it (never fire twice).\n if (o?.once)\n e.h = (...a) => {\n rm();\n handler(...a);\n };\n // Wire the signal before touching any list or Map key, so a throwing\n // addEventListener leaves no partial state.\n if (sig) {\n sig.addEventListener(\"abort\", rm, { once: true });\n e.c = () => sig.removeEventListener(\"abort\", rm);\n }\n arr.push(e);\n if (!wild) t.set(type, arr);\n return rm;\n }\n\n // Both overloads (typed and \"*\") delegate to on(): on() routes \"*\" to the\n // wildcard list, so once(\"*\", h) === on(\"*\", h, { once: true }).\n function once(type: string, handler: F): () => void {\n return on(type, handler, { once: true });\n }\n\n function off(type: string, handler?: F): void {\n ck();\n const arr = type === \"*\" ? w : t.get(type);\n if (arr === undefined) return;\n if (handler === undefined) {\n flush(arr);\n arr.length = 0;\n } else {\n // First entry registered with `handler` (matched by the user-provided\n // handler, so once-wrapped entries match too).\n const i = arr.findIndex((e) => e.u === handler);\n if (i >= 0) flush(arr.splice(i, 1));\n }\n if (!arr.length) t.delete(type);\n }\n\n // Inline error policy handler — policy undefined/false → re-throw; true → swallow;\n // function → invoke and swallow; if callback throws, ignore silently.\n // The callback's `type` is always a string: numeric or symbol Events keys\n // reach emit() raw (handlers and Map keys keep them), so coerce here.\n function ap(pol: ErrorPolicy | undefined, err: unknown, k: string, p: unknown): void {\n if (pol === undefined || pol === false) throw err;\n if (typeof pol === \"function\")\n try {\n pol(err, String(k), p);\n } catch {\n /* silent */\n }\n }\n\n function emit(type: string, payload: unknown): void {\n ck();\n // Both slices happen BEFORE any handler call (snapshot-before-iterate);\n // entries removed meanwhile are inert (kill()), so they are skipped.\n const ts = (t.get(type) ?? []).slice();\n const ws = w.slice();\n for (const e of ts) {\n if (gate(e))\n try {\n e.h(payload);\n } catch (err) {\n ap(e.ce !== undefined ? e.ce : cap, err, type, payload);\n }\n }\n for (const e of ws) {\n if (gate(e))\n try {\n e.h(type, payload);\n } catch (err) {\n ap(cap, err, type, payload);\n }\n }\n }\n\n function purge(): void {\n for (const a of [...t.values(), w]) {\n flush(a);\n a.length = 0;\n }\n t.clear();\n }\n\n // _mapSize: test-only observation seam (not on the public Emitter interface).\n // Casted away at the return type; exposes t.size for Map-pruning regression tests.\n return {\n on: on as Emitter<Events>[\"on\"],\n once: once as Emitter<Events>[\"once\"],\n off: off as Emitter<Events>[\"off\"],\n emit: emit as Emitter<Events>[\"emit\"],\n clear() {\n ck();\n purge();\n },\n dispose() {\n if (!d) {\n purge();\n d = true;\n }\n },\n get disposed() {\n return d;\n },\n get _mapSize() {\n return t.size;\n },\n } as unknown as Emitter<Events>;\n}\n"]}
|
package/llms-full.txt
CHANGED
|
@@ -15,7 +15,7 @@ The short index lives at `llms.txt` (see https://llmstxt.org/).
|
|
|
15
15
|
|
|
16
16
|
Small, strict, typed event emitter with ai*js lifecycle conventions: `on()` returns unsubscribe, `once` is built in, `AbortSignal` is first-class, wildcard handlers are supported, and `dispose()` is idempotent.
|
|
17
17
|
|
|
18
|
-
> **Status: 0.
|
|
18
|
+
> **Status: 0.6.0 - stable 1.0-track surface.** The root entry is the public API.
|
|
19
19
|
|
|
20
20
|
## Install
|
|
21
21
|
|
|
@@ -52,19 +52,21 @@ events.dispose();
|
|
|
52
52
|
- `createEmitter<Events>(options?)` creates a typed emitter.
|
|
53
53
|
- `on(type, handler, options?)` subscribes and returns an unsubscribe function.
|
|
54
54
|
- `on("*", wildcard, options?)` subscribes to every event after type-matched handlers.
|
|
55
|
-
- `once(type, handler)` is shorthand for
|
|
55
|
+
- `once(type, handler)` is shorthand for `on(type, handler, { once: true })`, for typed events and `"*"` alike.
|
|
56
56
|
- `off(type, handler?)`, `clear()`, and `dispose()` remove handlers at different scopes.
|
|
57
57
|
- `emit(type, payload)` dispatches synchronously over a snapshot of handlers.
|
|
58
|
-
- Options: `signal`, `once`, `captureErrors
|
|
58
|
+
- Options: `signal`, `once`, `captureErrors` for typed, `sampleRate` for wildcard, `throttleMs` for typed and wildcard.
|
|
59
59
|
|
|
60
60
|
## Sharp Edges
|
|
61
61
|
|
|
62
|
-
- Default error policy is mitt-like: the first throwing handler aborts dispatch. Use `captureHandlerErrors` or per-handler `captureErrors` to swallow/report and continue.
|
|
62
|
+
- Default error policy is mitt-like: the first throwing handler aborts dispatch. Use `captureHandlerErrors` or per-handler `captureErrors` to swallow/report and continue; report callbacks always get the event name as a string.
|
|
63
63
|
- Wildcard handlers receive `(type, payload)`, not just payload.
|
|
64
|
-
-
|
|
64
|
+
- `once("*", handler)` is equivalent to `on("*", handler, { once: true })`.
|
|
65
|
+
- A nested `emit()` from a handler runs to completion before the outer dispatch resumes; the outer dispatch skips handlers removed meanwhile (unsubscribe, `off`, `clear`, `dispose`, abort), and `once` handlers go inert before their first call.
|
|
65
66
|
- `throttleMs` uses `performance.now()` (monotonic); system-clock corrections do not affect throttle windows.
|
|
66
67
|
- `sampleRate` is wildcard-only and uses `Math.random()` per dispatch.
|
|
67
|
-
- `
|
|
68
|
+
- Misuse throws `EmitterError` from `on()`/`once()` before anything is registered: a handler that is not a function, a `signal` that is not an `AbortSignal`, or an invalid option.
|
|
69
|
+
- `dispose()` is permanent; after it, `on`/`once`/`emit`/`off`/`clear` all throw `EmitterDisposedError`. Only `dispose()` itself and previously returned unsubscribe functions are safe no-ops post-dispose.
|
|
68
70
|
|
|
69
71
|
## AI Context
|
|
70
72
|
|
|
@@ -86,7 +88,28 @@ MIT
|
|
|
86
88
|
|
|
87
89
|
All notable changes to aieventjs are summarized here.
|
|
88
90
|
|
|
89
|
-
## [
|
|
91
|
+
## [0.6.0] - 2026-09-29
|
|
92
|
+
|
|
93
|
+
### Breaking
|
|
94
|
+
|
|
95
|
+
- `emit()`: a handler removed during a dispatch (by a sibling's unsubscribe function, `off()`, `clear()`, `dispose()` or an aborted `signal`) is now skipped for the rest of that dispatch instead of still receiving the in-flight event, per the ai*js fan-out re-entrancy rule. Migration: if a removed handler must still see the current event, remove it after `emit()` returns (for example `queueMicrotask(off)`), and do not rely on the remaining handlers running after a mid-dispatch `dispose()`.
|
|
96
|
+
- `on()` / `once()`: a `handler` that is not a function, or a `signal` without `addEventListener` / `removeEventListener`, now throws `EmitterError` before anything is registered, instead of registering a handler that threw a bare `TypeError` at every `emit()` (or was silently swallowed under `captureHandlerErrors`) or throwing a bare `TypeError` from `on()`. Migration: pass a function and a real `AbortSignal` (or omit `signal`), and catch `EmitterError` where you caught `TypeError`.
|
|
97
|
+
|
|
98
|
+
### Changes
|
|
99
|
+
|
|
100
|
+
- Changed: `once("*", handler)` is now a typed overload, declared before the typed one and equivalent to `on("*", handler, { once: true })` (the handler receives `(type, payload)`, fires after typed handlers and goes inert before it is called); this reverses the 0.5.8 compile-time rejection, which never rejected `"*"` on index-signature maps such as the default `createEmitter()` map and there typed the event name as the payload.
|
|
101
|
+
- Changed: `EmitterError` messages follow the ai*js shape `aieventjs: <subject> must be <constraint>` (`captureErrors must be unset on *`, `sampleRate must be unset on typed events`, `throttleMs must be a finite number >= 0`); match on the class plus a regex rather than the exact text.
|
|
102
|
+
- Changed: `dist/index.js` shrinks from 1,149 B to 1,062 B gzip (budget unchanged at 1,150 B): typed and wildcard subscriptions share one registration path, one removal helper and one per-dispatch gate.
|
|
103
|
+
- Fixed: aborting a typed subscription's `AbortSignal` now prunes the empty handler array from the internal Map, the same as the returned unsubscribe function; previously only unsubscribe pruned, leaving one Map entry behind per aborted, unique event name.
|
|
104
|
+
- Fixed: a `once` handler consumed by a re-entrant `emit()` (called from within another handler during the same outer dispatch) no longer fires a second time when the outer dispatch's snapshot still holds it.
|
|
105
|
+
- Fixed: `on(type, handler, { signal })` no longer leaves a registered-but-unusable subscription when `signal` is `null` (or another invalid/throwing `AbortSignal`); `null` is now treated the same as `undefined`.
|
|
106
|
+
- Fixed: `captureHandlerErrors` / `captureErrors` callbacks now always receive the event name as a string, as typed and documented; a numeric `Events` key (e.g. `{ 404: string }`) used to arrive as a `number`, and a symbol key now arrives as `String(symbol)`. Handlers, wildcard handlers and the internal map keep the raw key.
|
|
107
|
+
- Fixed: `on()` with a `signal` whose `addEventListener` throws no longer leaves an empty handler list for that event name in the internal map.
|
|
108
|
+
- Fixed: `package.json` `exports` nests `types` under `import` and `require` (`require.types` points at `dist/index.d.cts`), so `node16` / `nodenext` CommonJS consumers no longer hit TS1479; `verify-exports` walks nested conditions.
|
|
109
|
+
- Fixed: `pnpm typecheck` now type-checks `test/` (the test tsconfig inherited `exclude: ["test"]`), so the suite's compile-time assertions are enforced.
|
|
110
|
+
- Docs: README.md and README_ZHTW.md's Sharp Edges note corrected — only `dispose()` itself and previously returned unsubscribe functions are no-ops after dispose; `off()`/`clear()` throw `EmitterDisposedError` like every other post-dispose call.
|
|
111
|
+
- Docs: README_ZHTW.md's `throttleMs` note updated to match the monotonic `performance.now()` behavior already documented elsewhere; it previously still described the stale `Date.now()` wall-clock caveat.
|
|
112
|
+
- Docs: STABILITY.md's "Behavior" section becomes the family "Behavioral Contract" and states the re-entrancy clause, the removal paths and the misuse errors; README and README_ZHTW Sharp Edges mirror it, and the Options line now says `captureErrors` is typed-only.
|
|
90
113
|
|
|
91
114
|
## [0.5.9] - 2026-06-29
|
|
92
115
|
|
|
@@ -124,16 +147,19 @@ All notable changes to aieventjs are summarized here.
|
|
|
124
147
|
| --- | --- | --- |
|
|
125
148
|
| `createEmitter(options?)` | Stable | Generic typed event map. |
|
|
126
149
|
| `Emitter.on` | Stable | Typed and wildcard overloads; returns unsubscribe. |
|
|
127
|
-
| `Emitter.once` | Stable | Typed
|
|
150
|
+
| `Emitter.once` | Stable | Typed and wildcard overloads; `once(type, handler)` equals `on(type, handler, { once: true })`, including `"*"`. |
|
|
128
151
|
| `Emitter.off`, `clear`, `dispose` | Stable | Cleanup methods; dispose is permanent and idempotent. |
|
|
129
152
|
| `Emitter.emit` | Stable | Synchronous snapshot dispatch. |
|
|
130
153
|
| Error classes | Stable | `EmitterError`, `EmitterDisposedError`. |
|
|
131
154
|
|
|
132
|
-
##
|
|
155
|
+
## Behavioral Contract
|
|
133
156
|
|
|
134
157
|
- Type-matched handlers run before wildcard handlers.
|
|
135
158
|
- Handler lists are snapshotted before dispatch.
|
|
136
|
-
-
|
|
159
|
+
- A nested `emit()` from inside a handler runs synchronously to completion before the outer dispatch resumes; the outer dispatch continues over its pre-taken snapshot, skipping handlers removed meanwhile; `once` handlers go inert before their first call.
|
|
160
|
+
- Every removal path counts for that skip: the returned unsubscribe function, `off`, `clear`, `dispose` and an aborted `signal`. Nested dispatch is never rejected or queued; there is no mailbox.
|
|
161
|
+
- Default handler errors propagate; capture options can swallow/report. Capture callbacks always receive the event name as a string (`String(type)`); handlers keep numeric or symbol keys as they are.
|
|
162
|
+
- Misuse is reported by `on()` / `once()` with `EmitterError` and a message of the form `aieventjs: <subject> must be <constraint>`, checked before anything is registered: a handler that is not a function, a `signal` that is not an `AbortSignal`, `captureErrors` on `"*"`, `sampleRate` on a typed event or outside `(0, 1]`, and a `throttleMs` that is not a finite number `>= 0`.
|
|
137
163
|
- `AbortSignal` removes subscriptions and pre-aborted signals do not register.
|
|
138
164
|
- `throttleMs` uses `performance.now()` (monotonic) and is unaffected by system-clock corrections.
|
|
139
165
|
|
|
@@ -165,7 +191,7 @@ Run `pnpm lint` before PRs. If docs change, regenerate `llms-full.txt`.
|
|
|
165
191
|
|
|
166
192
|
## Rules
|
|
167
193
|
|
|
168
|
-
- Preserve snapshot-before-iterate dispatch semantics.
|
|
194
|
+
- Preserve snapshot-before-iterate dispatch semantics: handlers removed mid-dispatch are skipped, and a nested `emit()` runs to completion first.
|
|
169
195
|
- Keep wildcard ordering after typed handlers.
|
|
170
196
|
- Add tests for `AbortSignal`, `once`, wildcard, throttle, sample, and error policy changes.
|
|
171
197
|
- Do not add async queueing to the stable emitter without a separate design note.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aieventjs",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Small, strict, typed event emitter — on() returns an unsubscribe function, once is built-in, AbortSignal is first-class, dispose() is idempotent, wildcard '*' handlers preserved. Mitt-shaped API; ai*js conventions everywhere else.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"event-emitter",
|
|
@@ -30,9 +30,14 @@
|
|
|
30
30
|
"types": "./dist/index.d.ts",
|
|
31
31
|
"exports": {
|
|
32
32
|
".": {
|
|
33
|
-
"
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
"import": {
|
|
34
|
+
"types": "./dist/index.d.ts",
|
|
35
|
+
"default": "./dist/index.js"
|
|
36
|
+
},
|
|
37
|
+
"require": {
|
|
38
|
+
"types": "./dist/index.d.cts",
|
|
39
|
+
"default": "./dist/index.cjs"
|
|
40
|
+
}
|
|
36
41
|
}
|
|
37
42
|
},
|
|
38
43
|
"files": [
|