aieventjs 0.1.1 → 0.3.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 +14 -6
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +49 -12
- package/dist/index.d.ts +49 -12
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/llms-full.txt +29 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
Part of the [ai\*js micro-runtime ecosystem](https://github.com/yshengliao) — see also [aifsmjs](https://github.com/yshengliao/aifsmjs) (FSM), [aiecsjs](https://github.com/yshengliao/aiecsjs) (ECS), [aibridgejs](https://github.com/yshengliao/aibridgejs) (cross-context RPC), [aipooljs](https://github.com/yshengliao/aipooljs) (object pool), [aiquadtreejs](https://github.com/yshengliao/aiquadtreejs) (spatial partitioning), and [aiaudiojs](https://github.com/yshengliao/aiaudiojs) (Web Audio shell).
|
|
12
12
|
|
|
13
|
-
> **Status: 0.
|
|
13
|
+
> **Status: 0.3.0.** Full implementation shipped; all methods are live. Coverage ≥ 95/90/100/100; ~1050 B gzip (budget 1100 B).
|
|
14
14
|
|
|
15
15
|
---
|
|
16
16
|
|
|
@@ -87,6 +87,9 @@ bus.dispose(); // idempotent; post-dispose calls throw EmitterDisposedError
|
|
|
87
87
|
| `dispose()` idempotent; post-dispose calls throw | Error-event special casing (Node EventEmitter style) |
|
|
88
88
|
| Handler-array snapshot on `emit` (safe re-entrancy) | Persistent storage / replay (not its job) |
|
|
89
89
|
| Destructurable methods (`const { on, emit } = bus`) | Zero-allocation `emit` (one snapshot per dispatch is required for re-entrancy) |
|
|
90
|
+
| `on('*', fn, { sampleRate })` — probabilistic delivery for debug subscribers (wildcard only) | |
|
|
91
|
+
| `on('*', fn, { throttleMs })` — leading-edge throttle for debug subscribers (wildcard only) | |
|
|
92
|
+
| `createEmitter({ captureHandlerErrors })` — opt-in error policy; per-handler override via `OnOptions.captureErrors` | |
|
|
90
93
|
|
|
91
94
|
---
|
|
92
95
|
|
|
@@ -101,12 +104,17 @@ type WildcardHandler<Events extends Record<string, unknown>> =
|
|
|
101
104
|
interface OnOptions {
|
|
102
105
|
signal?: AbortSignal;
|
|
103
106
|
once?: boolean;
|
|
107
|
+
captureErrors?: boolean | ((err: unknown, type: string, payload: unknown) => void); // typed only
|
|
108
|
+
sampleRate?: number; // wildcard "*" only — probability in (0, 1]
|
|
109
|
+
throttleMs?: number; // wildcard "*" only — leading-edge throttle, uses Date.now()
|
|
104
110
|
}
|
|
105
111
|
|
|
106
112
|
interface EmitterOptions {
|
|
107
|
-
//
|
|
108
|
-
//
|
|
109
|
-
|
|
113
|
+
// Default error policy. undefined/false (default): first throw aborts dispatch.
|
|
114
|
+
// true: swallow errors and continue dispatch over all handlers.
|
|
115
|
+
// (err, type, payload) => void: callback invoked per throwing handler.
|
|
116
|
+
// Per-handler OnOptions.captureErrors overrides this for individual subscriptions.
|
|
117
|
+
captureHandlerErrors?: boolean | ((err: unknown, type: string, payload: unknown) => void);
|
|
110
118
|
}
|
|
111
119
|
|
|
112
120
|
interface Emitter<Events extends Record<string, unknown>> {
|
|
@@ -139,8 +147,8 @@ Full JSDoc lives in [`src/index.ts`](src/index.ts).
|
|
|
139
147
|
| ---------- | ----------------------------------------------------------------------------------------------------------------------------------- |
|
|
140
148
|
| **0.0.1** | Scaffold landed — frozen API surface as a `throw` stub; full config + CI walk clean. |
|
|
141
149
|
| **0.1.0** | First npm release. `on` / `once` / `off` / `emit` / `clear` / `dispose` implemented; coverage ≥ 95/90/100/100; ≤ 800 B gzip (strict-TS overhead lands at ~747 B). |
|
|
142
|
-
| **0.
|
|
143
|
-
| **0.
|
|
150
|
+
| **0.3.0** | `captureHandlerErrors` + wildcard sampling/throttling. The v0.2 number was skipped to align with the four-package v0.3 release cohort. |
|
|
151
|
+
| **0.6+** | Async handler tracking (draft) — see [STABILITY.md](STABILITY.md). |
|
|
144
152
|
|
|
145
153
|
---
|
|
146
154
|
|
package/dist/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
'use strict';var
|
|
1
|
+
'use strict';var v=class extends Error{name="EmitterError"},h=class extends Error{name="EmitterDisposedError"};function b(s,d){let i=s.findIndex(o=>o.u===d);if(i>=0){let o=s[i];o!==void 0&&(o.c?.(),o.c=void 0),s.splice(i,1);}}function y(s){for(let d of s)d.c?.(),d.c=void 0;}function m(s,d,i){s.push(d);let o=()=>{let a=s.indexOf(d);a>=0&&s.splice(a,1),d.c?.(),d.c=void 0;};if(i!==void 0){let a=()=>o();i.addEventListener("abort",a,{once:true}),d.c=()=>i.removeEventListener("abort",a);}return o}function j(s){let d=s?.captureHandlerErrors,i=new Map,o=[],a=false;function p(){if(a)throw new h("aieventjs: emitter has been disposed")}function g(e){let n=i.get(e);return n===void 0&&(n=[],i.set(e,n)),n}function k(e,n,t){p();let c=t?.sampleRate,u=t?.throttleMs;if(e==="*"){if(t?.captureErrors!==void 0)throw new v("aieventjs: captureErrors invalid on *")}else {if(c!==void 0)throw new v("aieventjs: sampleRate wildcard-only");if(u!==void 0)throw new v("aieventjs: throttleMs wildcard-only")}if(c!==void 0&&(c<=0||c>1))throw new v("aieventjs: sampleRate must be in (0,1]");if(u!==void 0&&u<0)throw new v("aieventjs: throttleMs must be >= 0");let l=t?.signal;if(l?.aborted)return ()=>{};if(e==="*"){let E=n;if(t?.once){let H=m(o,{h:(R,M)=>{H(),E(R,M);},u:E,c:void 0,r:c,tm:u},l);return H}return m(o,{h:E,u:E,c:void 0,r:c,tm:u},l)}let r=n,f=t?.captureErrors;if(t?.once){let E={h:H=>{w(),r(H);},u:r,c:void 0,ce:f},w=m(g(e),E,l);return w}return m(g(e),{h:r,u:r,c:void 0,ce:f},l)}function A(e,n){return k(e,n,{once:true})}function O(e,n){if(p(),e==="*"){n===void 0?(y(o),o.length=0):b(o,n);return}let t=i.get(e);t!==void 0&&(n===void 0?(y(t),i.delete(e)):b(t,n));}function x(e,n,t,c){if(e===void 0||e===false)throw n;if(typeof e=="function")try{e(n,t,c);}catch{}}function W(e,n){p();let t=e,c=n,u=(i.get(t)??[]).slice(),l=o.slice();for(let r of u)try{r.h(c);}catch(f){x(r.ce!==void 0?r.ce:d,f,t,c);}for(let r of l)if(!(r.r!==void 0&&Math.random()>=r.r)){if(r.tm){let f=Date.now();if(r.ts!==void 0&&f-r.ts<r.tm)continue;r.ts=f;}try{r.h(t,c);}catch(f){x(d,f,t,c);}}}function K(){for(let e of i.values())y(e);y(o),i.clear(),o.length=0;}return {on:k,once:A,off:O,emit:W,clear(){p(),K();},dispose(){a||(K(),a=true);},get disposed(){return a}}}exports.EmitterDisposedError=h;exports.EmitterError=v;exports.createEmitter=j;//# 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","t","w","d","ck","ga","k","a","on","type","handler","o","tp","p","once","off","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// 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}. Reserved for future options\n * (e.g. handler error policy); the scaffold accepts an empty object.\n *\n * @public\n */\nexport interface EmitterOptions {\n /**\n * If true, throwing handlers do not abort the remaining dispatch — the\n * error is collected and re-thrown as a single `AggregateError` after\n * all handlers ran. Default `false` (mitt-compatible: first throw wins).\n *\n * Reserved for 0.2.0; ignored in 0.1.0.\n */\n captureHandlerErrors?: boolean;\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/**\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 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 list is snapshotted before\n * iteration, so removing a handler inside its own callback does not\n * skip subsequent handlers. The first throwing handler aborts the\n * dispatch; remaining handlers (typed and wildcard) do not fire.\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. Reserved for future precondition violations.\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.\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}\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 * @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 void opts;\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 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 };\n const rm = sub(w, e, sig);\n return rm;\n }\n return sub(w, { h: fn, u: fn, c: undefined }, sig);\n }\n\n const fn = handler as AH;\n if (o?.once) {\n const e: E<AH> = {\n h: (p) => {\n rm();\n fn(p);\n },\n u: fn,\n c: undefined,\n };\n const rm = sub(ga(type), e, sig);\n return rm;\n }\n return sub(ga(type), { h: fn, u: fn, c: undefined }, sig);\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 t.delete(type);\n } else {\n rmByUser(arr, handler as AH);\n }\n }\n\n function emit<K extends keyof Events>(type: K, payload: Events[K]): void {\n ck();\n // Snapshot BOTH arrays before either dispatch loop, so a typed handler\n // that adds/removes a wildcard handler during dispatch does not affect\n // the wildcards that fire in this same emit (spec §2 / §5).\n const k = type as string;\n const ts = (t.get(k) ?? []).slice();\n const ws = w.slice();\n for (const e of ts) e.h(payload as unknown);\n for (const e of ws) e.h(type as string, payload);\n }\n\n function purge(): void {\n for (const a of t.values()) flush(a);\n flush(w);\n t.clear();\n w.length = 0;\n }\n\n return {\n on: on as Emitter<Events>[\"on\"],\n 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 };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["EmitterError","EmitterDisposedError","rmByUser","arr","user","e","flush","sub","sig","rm","i","fn","createEmitter","opts","cap","t","w","d","ck","ga","k","a","on","type","handler","o","sr","tm2","tp","p","ce","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 * Wildcard \"*\" only. Minimum milliseconds between successive calls.\n * Leading-edge: the first dispatch after subscription always fires;\n * subsequent dispatches within `throttleMs` are dropped (not queued).\n * Uses Date.now(). 0 = no throttle. Negative values are rejected.\n *\n * Throws EmitterError if set on a typed handler.\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 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` / `throttleMs` set on a typed subscription; `sampleRate`\n * outside `(0, 1]`; or `throttleMs` 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 wildcard 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 (wildcard only)\n ts?: number | undefined; // last call timestamp — mutated during dispatch (wildcard only)\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 * @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 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 if (tm2 !== undefined) throw new EmitterError(\"aieventjs: throttleMs wildcard-only\");\n }\n if (sr !== undefined && (sr <= 0 || sr > 1))\n throw new EmitterError(\"aieventjs: sampleRate must be in (0,1]\");\n if (tm2 !== undefined && tm2 < 0) 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 if (o?.once) {\n const e: E<AH> = {\n h: (p) => {\n rm();\n fn(p);\n },\n u: fn,\n c: undefined,\n ce: ce,\n };\n const rm = sub(ga(type), e, sig);\n return rm;\n }\n return sub(ga(type), { h: fn, u: fn, c: undefined, ce: ce }, sig);\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 t.delete(type);\n } else {\n rmByUser(arr, handler as AH);\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 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 = Date.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()) flush(a);\n flush(w);\n t.clear();\n w.length = 0;\n }\n\n return {\n on: on as Emitter<Events>[\"on\"],\n 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 };\n}\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Configuration for {@link createEmitter}.
|
|
3
|
-
*
|
|
2
|
+
* Configuration for {@link createEmitter}. Controls the default error
|
|
3
|
+
* policy for handlers thrown during `emit()`; per-handler
|
|
4
|
+
* {@link OnOptions.captureErrors} overrides this default.
|
|
4
5
|
*
|
|
5
6
|
* @public
|
|
6
7
|
*/
|
|
7
8
|
interface EmitterOptions {
|
|
8
9
|
/**
|
|
9
|
-
*
|
|
10
|
-
* error is collected and re-thrown as a single `AggregateError` after
|
|
11
|
-
* all handlers ran. Default `false` (mitt-compatible: first throw wins).
|
|
10
|
+
* Default error policy for all handlers when they throw during emit().
|
|
12
11
|
*
|
|
13
|
-
*
|
|
12
|
+
* - undefined / false (default) — first throw aborts dispatch (mitt-compatible).
|
|
13
|
+
* - true — swallow; dispatch continues over all handlers in the snapshot.
|
|
14
|
+
* - (err, type, payload) => void — invoked with the unknown error, the
|
|
15
|
+
* event name as string, and the payload as unknown. If this callback
|
|
16
|
+
* itself throws, the error is silently ignored.
|
|
17
|
+
*
|
|
18
|
+
* Per-subscription OnOptions.captureErrors overrides this for that handler.
|
|
14
19
|
*/
|
|
15
|
-
captureHandlerErrors?: boolean;
|
|
20
|
+
captureHandlerErrors?: boolean | ((err: unknown, type: string, payload: unknown) => void);
|
|
16
21
|
}
|
|
17
22
|
/**
|
|
18
23
|
* Handler invoked for a single typed event.
|
|
@@ -40,6 +45,34 @@ interface OnOptions {
|
|
|
40
45
|
signal?: AbortSignal;
|
|
41
46
|
/** Auto-remove the handler after the first dispatch. Equivalent to `once()`. */
|
|
42
47
|
once?: boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Override emitter-level captureHandlerErrors for this handler.
|
|
50
|
+
* - undefined — fall through to emitter-level.
|
|
51
|
+
* - false — force re-throw, even when emitter-level is true / callback.
|
|
52
|
+
* - true — swallow.
|
|
53
|
+
* - (err, type, payload) => void — same semantics as the emitter-level callback.
|
|
54
|
+
*
|
|
55
|
+
* Throws EmitterError if set on a wildcard "*" subscription.
|
|
56
|
+
* @invariant does not break snapshot-before-iterate semantics.
|
|
57
|
+
*/
|
|
58
|
+
captureErrors?: boolean | ((err: unknown, type: string, payload: unknown) => void);
|
|
59
|
+
/**
|
|
60
|
+
* Wildcard "*" only. Probability in (0, 1] that a dispatch reaches this
|
|
61
|
+
* handler. Math.random() is sampled per dispatch. Values <= 0 or > 1 are
|
|
62
|
+
* rejected at on() time.
|
|
63
|
+
*
|
|
64
|
+
* Throws EmitterError if set on a typed handler.
|
|
65
|
+
*/
|
|
66
|
+
sampleRate?: number;
|
|
67
|
+
/**
|
|
68
|
+
* Wildcard "*" only. Minimum milliseconds between successive calls.
|
|
69
|
+
* Leading-edge: the first dispatch after subscription always fires;
|
|
70
|
+
* subsequent dispatches within `throttleMs` are dropped (not queued).
|
|
71
|
+
* Uses Date.now(). 0 = no throttle. Negative values are rejected.
|
|
72
|
+
*
|
|
73
|
+
* Throws EmitterError if set on a typed handler.
|
|
74
|
+
*/
|
|
75
|
+
throttleMs?: number;
|
|
43
76
|
}
|
|
44
77
|
/**
|
|
45
78
|
* Strongly-typed event emitter. Subscribe with {@link Emitter.on} (returns
|
|
@@ -78,10 +111,11 @@ interface Emitter<Events extends Record<string, unknown>> {
|
|
|
78
111
|
off(type: "*", handler?: WildcardHandler<Events>): void;
|
|
79
112
|
/**
|
|
80
113
|
* Dispatch synchronously. Handlers receive `payload`; wildcard handlers
|
|
81
|
-
* receive `(type, payload)`. Handler
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
114
|
+
* receive `(type, payload)`. Handler lists are snapshotted before iteration,
|
|
115
|
+
* so removing a handler inside its own callback does not skip subsequent
|
|
116
|
+
* handlers. By default, the first throwing handler aborts the dispatch;
|
|
117
|
+
* set EmitterOptions.captureHandlerErrors (or per-handler OnOptions.captureErrors)
|
|
118
|
+
* to swallow or report errors and continue.
|
|
85
119
|
*/
|
|
86
120
|
emit<K extends keyof Events>(type: K, payload: Events[K]): void;
|
|
87
121
|
/**
|
|
@@ -98,7 +132,10 @@ interface Emitter<Events extends Record<string, unknown>> {
|
|
|
98
132
|
readonly disposed: boolean;
|
|
99
133
|
}
|
|
100
134
|
/**
|
|
101
|
-
* Recoverable emitter error.
|
|
135
|
+
* Recoverable emitter error. Thrown by `on()` when `OnOptions` violates a
|
|
136
|
+
* precondition: `captureErrors` set on a wildcard `"*"` subscription;
|
|
137
|
+
* `sampleRate` / `throttleMs` set on a typed subscription; `sampleRate`
|
|
138
|
+
* outside `(0, 1]`; or `throttleMs` negative.
|
|
102
139
|
*
|
|
103
140
|
* @public
|
|
104
141
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Configuration for {@link createEmitter}.
|
|
3
|
-
*
|
|
2
|
+
* Configuration for {@link createEmitter}. Controls the default error
|
|
3
|
+
* policy for handlers thrown during `emit()`; per-handler
|
|
4
|
+
* {@link OnOptions.captureErrors} overrides this default.
|
|
4
5
|
*
|
|
5
6
|
* @public
|
|
6
7
|
*/
|
|
7
8
|
interface EmitterOptions {
|
|
8
9
|
/**
|
|
9
|
-
*
|
|
10
|
-
* error is collected and re-thrown as a single `AggregateError` after
|
|
11
|
-
* all handlers ran. Default `false` (mitt-compatible: first throw wins).
|
|
10
|
+
* Default error policy for all handlers when they throw during emit().
|
|
12
11
|
*
|
|
13
|
-
*
|
|
12
|
+
* - undefined / false (default) — first throw aborts dispatch (mitt-compatible).
|
|
13
|
+
* - true — swallow; dispatch continues over all handlers in the snapshot.
|
|
14
|
+
* - (err, type, payload) => void — invoked with the unknown error, the
|
|
15
|
+
* event name as string, and the payload as unknown. If this callback
|
|
16
|
+
* itself throws, the error is silently ignored.
|
|
17
|
+
*
|
|
18
|
+
* Per-subscription OnOptions.captureErrors overrides this for that handler.
|
|
14
19
|
*/
|
|
15
|
-
captureHandlerErrors?: boolean;
|
|
20
|
+
captureHandlerErrors?: boolean | ((err: unknown, type: string, payload: unknown) => void);
|
|
16
21
|
}
|
|
17
22
|
/**
|
|
18
23
|
* Handler invoked for a single typed event.
|
|
@@ -40,6 +45,34 @@ interface OnOptions {
|
|
|
40
45
|
signal?: AbortSignal;
|
|
41
46
|
/** Auto-remove the handler after the first dispatch. Equivalent to `once()`. */
|
|
42
47
|
once?: boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Override emitter-level captureHandlerErrors for this handler.
|
|
50
|
+
* - undefined — fall through to emitter-level.
|
|
51
|
+
* - false — force re-throw, even when emitter-level is true / callback.
|
|
52
|
+
* - true — swallow.
|
|
53
|
+
* - (err, type, payload) => void — same semantics as the emitter-level callback.
|
|
54
|
+
*
|
|
55
|
+
* Throws EmitterError if set on a wildcard "*" subscription.
|
|
56
|
+
* @invariant does not break snapshot-before-iterate semantics.
|
|
57
|
+
*/
|
|
58
|
+
captureErrors?: boolean | ((err: unknown, type: string, payload: unknown) => void);
|
|
59
|
+
/**
|
|
60
|
+
* Wildcard "*" only. Probability in (0, 1] that a dispatch reaches this
|
|
61
|
+
* handler. Math.random() is sampled per dispatch. Values <= 0 or > 1 are
|
|
62
|
+
* rejected at on() time.
|
|
63
|
+
*
|
|
64
|
+
* Throws EmitterError if set on a typed handler.
|
|
65
|
+
*/
|
|
66
|
+
sampleRate?: number;
|
|
67
|
+
/**
|
|
68
|
+
* Wildcard "*" only. Minimum milliseconds between successive calls.
|
|
69
|
+
* Leading-edge: the first dispatch after subscription always fires;
|
|
70
|
+
* subsequent dispatches within `throttleMs` are dropped (not queued).
|
|
71
|
+
* Uses Date.now(). 0 = no throttle. Negative values are rejected.
|
|
72
|
+
*
|
|
73
|
+
* Throws EmitterError if set on a typed handler.
|
|
74
|
+
*/
|
|
75
|
+
throttleMs?: number;
|
|
43
76
|
}
|
|
44
77
|
/**
|
|
45
78
|
* Strongly-typed event emitter. Subscribe with {@link Emitter.on} (returns
|
|
@@ -78,10 +111,11 @@ interface Emitter<Events extends Record<string, unknown>> {
|
|
|
78
111
|
off(type: "*", handler?: WildcardHandler<Events>): void;
|
|
79
112
|
/**
|
|
80
113
|
* Dispatch synchronously. Handlers receive `payload`; wildcard handlers
|
|
81
|
-
* receive `(type, payload)`. Handler
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
114
|
+
* receive `(type, payload)`. Handler lists are snapshotted before iteration,
|
|
115
|
+
* so removing a handler inside its own callback does not skip subsequent
|
|
116
|
+
* handlers. By default, the first throwing handler aborts the dispatch;
|
|
117
|
+
* set EmitterOptions.captureHandlerErrors (or per-handler OnOptions.captureErrors)
|
|
118
|
+
* to swallow or report errors and continue.
|
|
85
119
|
*/
|
|
86
120
|
emit<K extends keyof Events>(type: K, payload: Events[K]): void;
|
|
87
121
|
/**
|
|
@@ -98,7 +132,10 @@ interface Emitter<Events extends Record<string, unknown>> {
|
|
|
98
132
|
readonly disposed: boolean;
|
|
99
133
|
}
|
|
100
134
|
/**
|
|
101
|
-
* Recoverable emitter error.
|
|
135
|
+
* Recoverable emitter error. Thrown by `on()` when `OnOptions` violates a
|
|
136
|
+
* precondition: `captureErrors` set on a wildcard `"*"` subscription;
|
|
137
|
+
* `sampleRate` / `throttleMs` set on a typed subscription; `sampleRate`
|
|
138
|
+
* outside `(0, 1]`; or `throttleMs` negative.
|
|
102
139
|
*
|
|
103
140
|
* @public
|
|
104
141
|
*/
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var
|
|
1
|
+
var v=class extends Error{name="EmitterError"},h=class extends Error{name="EmitterDisposedError"};function b(s,d){let i=s.findIndex(o=>o.u===d);if(i>=0){let o=s[i];o!==void 0&&(o.c?.(),o.c=void 0),s.splice(i,1);}}function y(s){for(let d of s)d.c?.(),d.c=void 0;}function m(s,d,i){s.push(d);let o=()=>{let a=s.indexOf(d);a>=0&&s.splice(a,1),d.c?.(),d.c=void 0;};if(i!==void 0){let a=()=>o();i.addEventListener("abort",a,{once:true}),d.c=()=>i.removeEventListener("abort",a);}return o}function j(s){let d=s?.captureHandlerErrors,i=new Map,o=[],a=false;function p(){if(a)throw new h("aieventjs: emitter has been disposed")}function g(e){let n=i.get(e);return n===void 0&&(n=[],i.set(e,n)),n}function k(e,n,t){p();let c=t?.sampleRate,u=t?.throttleMs;if(e==="*"){if(t?.captureErrors!==void 0)throw new v("aieventjs: captureErrors invalid on *")}else {if(c!==void 0)throw new v("aieventjs: sampleRate wildcard-only");if(u!==void 0)throw new v("aieventjs: throttleMs wildcard-only")}if(c!==void 0&&(c<=0||c>1))throw new v("aieventjs: sampleRate must be in (0,1]");if(u!==void 0&&u<0)throw new v("aieventjs: throttleMs must be >= 0");let l=t?.signal;if(l?.aborted)return ()=>{};if(e==="*"){let E=n;if(t?.once){let H=m(o,{h:(R,M)=>{H(),E(R,M);},u:E,c:void 0,r:c,tm:u},l);return H}return m(o,{h:E,u:E,c:void 0,r:c,tm:u},l)}let r=n,f=t?.captureErrors;if(t?.once){let E={h:H=>{w(),r(H);},u:r,c:void 0,ce:f},w=m(g(e),E,l);return w}return m(g(e),{h:r,u:r,c:void 0,ce:f},l)}function A(e,n){return k(e,n,{once:true})}function O(e,n){if(p(),e==="*"){n===void 0?(y(o),o.length=0):b(o,n);return}let t=i.get(e);t!==void 0&&(n===void 0?(y(t),i.delete(e)):b(t,n));}function x(e,n,t,c){if(e===void 0||e===false)throw n;if(typeof e=="function")try{e(n,t,c);}catch{}}function W(e,n){p();let t=e,c=n,u=(i.get(t)??[]).slice(),l=o.slice();for(let r of u)try{r.h(c);}catch(f){x(r.ce!==void 0?r.ce:d,f,t,c);}for(let r of l)if(!(r.r!==void 0&&Math.random()>=r.r)){if(r.tm){let f=Date.now();if(r.ts!==void 0&&f-r.ts<r.tm)continue;r.ts=f;}try{r.h(t,c);}catch(f){x(d,f,t,c);}}}function K(){for(let e of i.values())y(e);y(o),i.clear(),o.length=0;}return {on:k,once:A,off:O,emit:W,clear(){p(),K();},dispose(){a||(K(),a=true);},get disposed(){return a}}}export{h as EmitterDisposedError,v as EmitterError,j 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","t","w","d","ck","ga","k","a","on","type","handler","o","tp","p","once","off","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// 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}. Reserved for future options\n * (e.g. handler error policy); the scaffold accepts an empty object.\n *\n * @public\n */\nexport interface EmitterOptions {\n /**\n * If true, throwing handlers do not abort the remaining dispatch — the\n * error is collected and re-thrown as a single `AggregateError` after\n * all handlers ran. Default `false` (mitt-compatible: first throw wins).\n *\n * Reserved for 0.2.0; ignored in 0.1.0.\n */\n captureHandlerErrors?: boolean;\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/**\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 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 list is snapshotted before\n * iteration, so removing a handler inside its own callback does not\n * skip subsequent handlers. The first throwing handler aborts the\n * dispatch; remaining handlers (typed and wildcard) do not fire.\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. Reserved for future precondition violations.\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.\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}\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 * @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 void opts;\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 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 };\n const rm = sub(w, e, sig);\n return rm;\n }\n return sub(w, { h: fn, u: fn, c: undefined }, sig);\n }\n\n const fn = handler as AH;\n if (o?.once) {\n const e: E<AH> = {\n h: (p) => {\n rm();\n fn(p);\n },\n u: fn,\n c: undefined,\n };\n const rm = sub(ga(type), e, sig);\n return rm;\n }\n return sub(ga(type), { h: fn, u: fn, c: undefined }, sig);\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 t.delete(type);\n } else {\n rmByUser(arr, handler as AH);\n }\n }\n\n function emit<K extends keyof Events>(type: K, payload: Events[K]): void {\n ck();\n // Snapshot BOTH arrays before either dispatch loop, so a typed handler\n // that adds/removes a wildcard handler during dispatch does not affect\n // the wildcards that fire in this same emit (spec §2 / §5).\n const k = type as string;\n const ts = (t.get(k) ?? []).slice();\n const ws = w.slice();\n for (const e of ts) e.h(payload as unknown);\n for (const e of ws) e.h(type as string, payload);\n }\n\n function purge(): void {\n for (const a of t.values()) flush(a);\n flush(w);\n t.clear();\n w.length = 0;\n }\n\n return {\n on: on as Emitter<Events>[\"on\"],\n 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 };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["EmitterError","EmitterDisposedError","rmByUser","arr","user","e","flush","sub","sig","rm","i","fn","createEmitter","opts","cap","t","w","d","ck","ga","k","a","on","type","handler","o","sr","tm2","tp","p","ce","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 * Wildcard \"*\" only. Minimum milliseconds between successive calls.\n * Leading-edge: the first dispatch after subscription always fires;\n * subsequent dispatches within `throttleMs` are dropped (not queued).\n * Uses Date.now(). 0 = no throttle. Negative values are rejected.\n *\n * Throws EmitterError if set on a typed handler.\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 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` / `throttleMs` set on a typed subscription; `sampleRate`\n * outside `(0, 1]`; or `throttleMs` 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 wildcard 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 (wildcard only)\n ts?: number | undefined; // last call timestamp — mutated during dispatch (wildcard only)\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 * @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 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 if (tm2 !== undefined) throw new EmitterError(\"aieventjs: throttleMs wildcard-only\");\n }\n if (sr !== undefined && (sr <= 0 || sr > 1))\n throw new EmitterError(\"aieventjs: sampleRate must be in (0,1]\");\n if (tm2 !== undefined && tm2 < 0) 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 if (o?.once) {\n const e: E<AH> = {\n h: (p) => {\n rm();\n fn(p);\n },\n u: fn,\n c: undefined,\n ce: ce,\n };\n const rm = sub(ga(type), e, sig);\n return rm;\n }\n return sub(ga(type), { h: fn, u: fn, c: undefined, ce: ce }, sig);\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 t.delete(type);\n } else {\n rmByUser(arr, handler as AH);\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 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 = Date.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()) flush(a);\n flush(w);\n t.clear();\n w.length = 0;\n }\n\n return {\n on: on as Emitter<Events>[\"on\"],\n 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 };\n}\n"]}
|
package/llms-full.txt
CHANGED
|
@@ -23,7 +23,7 @@ The short index lives at `llms.txt` (see https://llmstxt.org/).
|
|
|
23
23
|
|
|
24
24
|
Part of the [ai\*js micro-runtime ecosystem](https://github.com/yshengliao) — see also [aifsmjs](https://github.com/yshengliao/aifsmjs) (FSM), [aiecsjs](https://github.com/yshengliao/aiecsjs) (ECS), [aibridgejs](https://github.com/yshengliao/aibridgejs) (cross-context RPC), [aipooljs](https://github.com/yshengliao/aipooljs) (object pool), [aiquadtreejs](https://github.com/yshengliao/aiquadtreejs) (spatial partitioning), and [aiaudiojs](https://github.com/yshengliao/aiaudiojs) (Web Audio shell).
|
|
25
25
|
|
|
26
|
-
> **Status: 0.
|
|
26
|
+
> **Status: 0.3.0.** Full implementation shipped; all methods are live. Coverage ≥ 95/90/100/100; ~1050 B gzip (budget 1100 B).
|
|
27
27
|
|
|
28
28
|
---
|
|
29
29
|
|
|
@@ -100,6 +100,9 @@ bus.dispose(); // idempotent; post-dispose calls throw EmitterDisposedError
|
|
|
100
100
|
| `dispose()` idempotent; post-dispose calls throw | Error-event special casing (Node EventEmitter style) |
|
|
101
101
|
| Handler-array snapshot on `emit` (safe re-entrancy) | Persistent storage / replay (not its job) |
|
|
102
102
|
| Destructurable methods (`const { on, emit } = bus`) | Zero-allocation `emit` (one snapshot per dispatch is required for re-entrancy) |
|
|
103
|
+
| `on('*', fn, { sampleRate })` — probabilistic delivery for debug subscribers (wildcard only) | |
|
|
104
|
+
| `on('*', fn, { throttleMs })` — leading-edge throttle for debug subscribers (wildcard only) | |
|
|
105
|
+
| `createEmitter({ captureHandlerErrors })` — opt-in error policy; per-handler override via `OnOptions.captureErrors` | |
|
|
103
106
|
|
|
104
107
|
---
|
|
105
108
|
|
|
@@ -114,12 +117,17 @@ type WildcardHandler<Events extends Record<string, unknown>> =
|
|
|
114
117
|
interface OnOptions {
|
|
115
118
|
signal?: AbortSignal;
|
|
116
119
|
once?: boolean;
|
|
120
|
+
captureErrors?: boolean | ((err: unknown, type: string, payload: unknown) => void); // typed only
|
|
121
|
+
sampleRate?: number; // wildcard "*" only — probability in (0, 1]
|
|
122
|
+
throttleMs?: number; // wildcard "*" only — leading-edge throttle, uses Date.now()
|
|
117
123
|
}
|
|
118
124
|
|
|
119
125
|
interface EmitterOptions {
|
|
120
|
-
//
|
|
121
|
-
//
|
|
122
|
-
|
|
126
|
+
// Default error policy. undefined/false (default): first throw aborts dispatch.
|
|
127
|
+
// true: swallow errors and continue dispatch over all handlers.
|
|
128
|
+
// (err, type, payload) => void: callback invoked per throwing handler.
|
|
129
|
+
// Per-handler OnOptions.captureErrors overrides this for individual subscriptions.
|
|
130
|
+
captureHandlerErrors?: boolean | ((err: unknown, type: string, payload: unknown) => void);
|
|
123
131
|
}
|
|
124
132
|
|
|
125
133
|
interface Emitter<Events extends Record<string, unknown>> {
|
|
@@ -152,8 +160,8 @@ Full JSDoc lives in [`src/index.ts`](src/index.ts).
|
|
|
152
160
|
| ---------- | ----------------------------------------------------------------------------------------------------------------------------------- |
|
|
153
161
|
| **0.0.1** | Scaffold landed — frozen API surface as a `throw` stub; full config + CI walk clean. |
|
|
154
162
|
| **0.1.0** | First npm release. `on` / `once` / `off` / `emit` / `clear` / `dispose` implemented; coverage ≥ 95/90/100/100; ≤ 800 B gzip (strict-TS overhead lands at ~747 B). |
|
|
155
|
-
| **0.
|
|
156
|
-
| **0.
|
|
163
|
+
| **0.3.0** | `captureHandlerErrors` + wildcard sampling/throttling. The v0.2 number was skipped to align with the four-package v0.3 release cohort. |
|
|
164
|
+
| **0.6+** | Async handler tracking (draft) — see [STABILITY.md](STABILITY.md). |
|
|
157
165
|
|
|
158
166
|
---
|
|
159
167
|
|
|
@@ -173,6 +181,21 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
173
181
|
|
|
174
182
|
## [Unreleased]
|
|
175
183
|
|
|
184
|
+
## [0.3.0] - 2026-05-29
|
|
185
|
+
### Added
|
|
186
|
+
- `EmitterOptions.captureHandlerErrors`: opt-in emitter-level error policy. Accepts `true` (swallow) or `(err, type, payload) => void` callback. Default behaviour (first throw aborts dispatch) is unchanged.
|
|
187
|
+
- `OnOptions.captureErrors`: per-handler override of the emitter-level policy. `false` forces re-throw even when emitter-level swallows. Setting it on a wildcard `"*"` subscription throws `EmitterError`.
|
|
188
|
+
- `OnOptions.sampleRate` (wildcard only): probability in `(0, 1]` that a dispatch reaches the handler; uses `Math.random()`. Out-of-range values throw `EmitterError` at `on()` time.
|
|
189
|
+
- `OnOptions.throttleMs` (wildcard only): minimum ms between successive calls, leading-edge; uses `Date.now()`. Negative values throw `EmitterError` at `on()` time.
|
|
190
|
+
- `STABILITY.md`: stability index for all public API surface, plus `[experimental]` placeholder for async handler tracking (targeted v0.6+).
|
|
191
|
+
|
|
192
|
+
### Changed
|
|
193
|
+
- Internal entry shape gains optional `ce` / `r` / `tm` / `ts` fields to carry per-handler error policy and wildcard throttle/sample state. Snapshot-before-iterate semantics in `emit()` are unchanged.
|
|
194
|
+
- `scripts/check-size.mjs` budget raised from 800 B to 1100 B. Actual v0.3.0 gzip lands at ~1050 B; the spec estimate of 900 B was optimistic (~100 B per feature accounting for guard message strings and try/catch frames).
|
|
195
|
+
|
|
196
|
+
### Notes
|
|
197
|
+
- v0.2 was skipped; v0.3.0 directly supersedes the v0.2 roadmap entry for `captureHandlerErrors`. Existing v0.1 callers that did not pass the option see no behavioural change.
|
|
198
|
+
|
|
176
199
|
## [0.1.1] - 2026-05-28
|
|
177
200
|
|
|
178
201
|
### Changed (CI)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aieventjs",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.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",
|