aieventjs 0.5.3 → 0.5.6
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 +9 -3
- package/README_ZHTW.md +9 -3
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +25 -1
- package/dist/index.d.ts +25 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/llms-full.txt +79 -3
- package/package.json +7 -6
package/README.md
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
# aieventjs
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/aieventjs)
|
|
4
|
-
[](https://github.com/islumina/aieventjs/actions/workflows/ci.yml)
|
|
5
5
|
[](LICENSE)
|
|
6
6
|
[](https://www.anthropic.com/claude-code)
|
|
7
7
|
[](README_ZHTW.md)
|
|
8
8
|
|
|
9
9
|
> A small, strict, typed event emitter — `on()` returns an unsubscribe function, `once` is built-in, `AbortSignal` is first-class, `dispose()` is idempotent, wildcard `*` handlers are preserved. Mitt-shaped API where it counts; ai\*js conventions everywhere else.
|
|
10
10
|
|
|
11
|
-
Part of the [ai\*js micro-runtime ecosystem](https://github.com/
|
|
11
|
+
Part of the [ai\*js micro-runtime ecosystem](https://github.com/islumina) — see also [aifsmjs](https://github.com/islumina/aifsmjs) (FSM), [aiecsjs](https://github.com/islumina/aiecsjs) (ECS), [aibridgejs](https://github.com/islumina/aibridgejs) (cross-context RPC), [aipooljs](https://github.com/islumina/aipooljs) (object pool), [aiquadtreejs](https://github.com/islumina/aiquadtreejs) (spatial partitioning), and [aiaudiojs](https://github.com/islumina/aiaudiojs) (Web Audio shell).
|
|
12
12
|
|
|
13
|
-
> **Status: 0.5.
|
|
13
|
+
> **Status: 0.5.6.** Full implementation shipped; all methods are live. Coverage ≥ 95/90/100/100; ~1050 B gzip (budget 1100 B).
|
|
14
14
|
|
|
15
15
|
---
|
|
16
16
|
|
|
@@ -119,6 +119,9 @@ interface OnOptions {
|
|
|
119
119
|
captureErrors?: boolean | ((err: unknown, type: string, payload: unknown) => void); // typed only
|
|
120
120
|
sampleRate?: number; // wildcard "*" only — probability in (0, 1]
|
|
121
121
|
throttleMs?: number; // typed or wildcard — per-handler leading-edge throttle, uses Date.now()
|
|
122
|
+
// Note: Date.now() is not monotonic; a system-clock regression silently
|
|
123
|
+
// mutes the handler until wall time re-passes the stored timestamp.
|
|
124
|
+
// Switching to performance.now() is deferred to the next minor. (EVT-R-02)
|
|
122
125
|
}
|
|
123
126
|
|
|
124
127
|
interface EmitterOptions {
|
|
@@ -134,6 +137,9 @@ interface Emitter<Events extends Record<string, unknown>> {
|
|
|
134
137
|
on<K extends keyof Events>(type: K, handler: EventHandler<Events[K]>, opts?: OnOptions): () => void;
|
|
135
138
|
on(type: "*", handler: WildcardHandler<Events>, opts?: OnOptions): () => void;
|
|
136
139
|
once<K extends keyof Events>(type: K, handler: EventHandler<Events[K]>): () => void;
|
|
140
|
+
// Note: once() only accepts typed event keys. For wildcard-once semantics use
|
|
141
|
+
// on("*", handler, { once: true }) — the handler receives (type, payload) as
|
|
142
|
+
// WildcardHandler, not (payload) as EventHandler. (EVT-B-02)
|
|
137
143
|
off<K extends keyof Events>(type: K, handler?: EventHandler<Events[K]>): void;
|
|
138
144
|
off(type: "*", handler?: WildcardHandler<Events>): void;
|
|
139
145
|
emit<K extends keyof Events>(type: K, payload: Events[K]): void;
|
package/README_ZHTW.md
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
# aieventjs
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/aieventjs)
|
|
4
|
-
[](https://github.com/islumina/aieventjs/actions/workflows/ci.yml)
|
|
5
5
|
[](LICENSE)
|
|
6
6
|
[](https://www.anthropic.com/claude-code)
|
|
7
7
|
[](README.md)
|
|
8
8
|
|
|
9
9
|
> 一個小而嚴格的 typed event emitter ── `on()` 回傳 unsubscribe function、內建 `once`、`AbortSignal` 一級公民、`dispose()` 冪等、保留 wildcard `*` handler。形似 mitt 的 API、但裡裡外外都是 ai\*js convention。
|
|
10
10
|
|
|
11
|
-
隸屬 [ai\*js micro-runtime 生態系](https://github.com/
|
|
11
|
+
隸屬 [ai\*js micro-runtime 生態系](https://github.com/islumina) ─ 另見 [aifsmjs](https://github.com/islumina/aifsmjs)(FSM)、[aiecsjs](https://github.com/islumina/aiecsjs)(ECS)、[aibridgejs](https://github.com/islumina/aibridgejs)(cross-context RPC)、[aipooljs](https://github.com/islumina/aipooljs)(物件池)、[aiquadtreejs](https://github.com/islumina/aiquadtreejs)(空間分割)、[aiaudiojs](https://github.com/islumina/aiaudiojs)(Web Audio 薄殼)。
|
|
12
12
|
|
|
13
|
-
> **狀態:0.5.
|
|
13
|
+
> **狀態:0.5.6。** 完整實作已上線、所有 method 皆可用。Coverage ≥ 95/90/100/100;~1050 B gzip(budget 1100 B)。
|
|
14
14
|
|
|
15
15
|
---
|
|
16
16
|
|
|
@@ -119,6 +119,9 @@ interface OnOptions {
|
|
|
119
119
|
captureErrors?: boolean | ((err: unknown, type: string, payload: unknown) => void); // 僅限 typed handler
|
|
120
120
|
sampleRate?: number; // wildcard "*" only — 機率 (0, 1]
|
|
121
121
|
throttleMs?: number; // typed 或 wildcard — per-handler leading-edge throttle,使用 Date.now()
|
|
122
|
+
// 注意:Date.now() 非單調時鐘;系統時鐘往回跳時,handler 會被靜默
|
|
123
|
+
// mute 直到 wall time 再次超過紀錄的時間戳。切換 performance.now()
|
|
124
|
+
// 延至下個 minor。(EVT-R-02)
|
|
122
125
|
}
|
|
123
126
|
|
|
124
127
|
interface EmitterOptions {
|
|
@@ -133,6 +136,9 @@ interface Emitter<Events extends Record<string, unknown>> {
|
|
|
133
136
|
on<K extends keyof Events>(type: K, handler: EventHandler<Events[K]>, opts?: OnOptions): () => void;
|
|
134
137
|
on(type: "*", handler: WildcardHandler<Events>, opts?: OnOptions): () => void;
|
|
135
138
|
once<K extends keyof Events>(type: K, handler: EventHandler<Events[K]>): () => void;
|
|
139
|
+
// 注意:once() 只接受 typed 事件 key。wildcard-once 請改用
|
|
140
|
+
// on("*", handler, { once: true })——handler 收 (type, payload)
|
|
141
|
+
// 而非 (payload)。型別層修正延至下個 minor。(EVT-B-02)
|
|
136
142
|
off<K extends keyof Events>(type: K, handler?: EventHandler<Events[K]>): void;
|
|
137
143
|
off(type: "*", handler?: WildcardHandler<Events>): void;
|
|
138
144
|
emit<K extends keyof Events>(type: K, payload: Events[K]): void;
|
package/dist/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
'use strict';var E=class extends Error{name="EmitterError"},g=class extends Error{name="EmitterDisposedError"};function K(d,c){let i=d.findIndex(r=>r.u===c);if(i>=0){let r=d[i];r!==void 0&&(r.c?.(),r.c=void 0),d.splice(i,1);}}function H(d){for(let c of d)c.c?.(),c.c=void 0;}function y(d,c,i){d.push(c);let r=()=>{let f=d.indexOf(c);f>=0&&d.splice(f,1),c.c?.(),c.c=void 0;};if(i!==void 0){let f=()=>r();i.addEventListener("abort",f,{once:true}),c.c=()=>i.removeEventListener("abort",f);}return r}function j(d){let c=d?.captureHandlerErrors,i=new Map,r=[],f=false;function p(){if(f)throw new g("aieventjs: emitter has been disposed")}function h(n){let t=i.get(n);return t===void 0&&(t=[],i.set(n,t)),t}function k(n,t,o){p();let s=o?.sampleRate,u=o?.throttleMs;if(n==="*"){if(o?.captureErrors!==void 0)throw new E("aieventjs: captureErrors invalid on *")}else if(s!==void 0)throw new E("aieventjs: sampleRate wildcard-only");if(s!==void 0&&(!Number.isFinite(s)||s<=0||s>1))throw new E("aieventjs: sampleRate must be in (0,1]");if(u!==void 0&&(!Number.isFinite(u)||u<0))throw new E("aieventjs: throttleMs must be >= 0");let l=o?.signal;if(l?.aborted)return ()=>{};if(n==="*"){let v=t;if(o?.once){let m=y(r,{h:(R,M)=>{m(),v(R,M);},u:v,c:void 0,r:s,tm:u},l);return m}return y(r,{h:v,u:v,c:void 0,r:s,tm:u},l)}let e=t,a=o?.captureErrors;if(o?.once){let v={h:m=>{w(),e(m);},u:e,c:void 0,ce:a,tm:u},w=y(h(n),v,l);return w}return y(h(n),{h:e,u:e,c:void 0,ce:a,tm:u},l)}function A(n,t){return k(n,t,{once:true})}function O(n,t){if(p(),n==="*"){t===void 0?(H(r),r.length=0):K(r,t);return}let o=i.get(n);o!==void 0&&(t===void 0?(H(o),i.delete(n)):K(o,t));}function x(n,t,o,s){if(n===void 0||n===false)throw t;if(typeof n=="function")try{n(t,o,s);}catch{}}function W(n,t){p();let o=n,s=t,u=(i.get(o)??[]).slice(),l=r.slice();for(let e of u){if(e.tm){let a=Date.now();if(e.ts!==void 0&&a-e.ts<e.tm)continue;e.ts=a;}try{e.h(s);}catch(a){x(e.ce!==void 0?e.ce:c,a,o,s);}}for(let e of l)if(!(e.r!==void 0&&Math.random()>=e.r)){if(e.tm){let a=Date.now();if(e.ts!==void 0&&a-e.ts<e.tm)continue;e.ts=a;}try{e.h(o,s);}catch(a){x(c,a,o,s);}}}function b(){for(let n of i.values())H(n);H(r),i.clear(),r.length=0;}return {on:k,once:A,off:O,emit:W,clear(){p(),b();},dispose(){f||(b(),f=true);},get disposed(){return f}}}exports.EmitterDisposedError=g;exports.EmitterError=E;exports.createEmitter=j;//# sourceMappingURL=index.cjs.map
|
|
1
|
+
'use strict';var E=class extends Error{name="EmitterError"},g=class extends Error{name="EmitterDisposedError"};function K(d,c){let i=d.findIndex(r=>r.u===c);if(i>=0){let r=d[i];r!==void 0&&(r.c?.(),r.c=void 0),d.splice(i,1);}}function H(d){for(let c of d)c.c?.(),c.c=void 0;}function y(d,c,i){d.push(c);let r=()=>{let f=d.indexOf(c);f>=0&&d.splice(f,1),c.c?.(),c.c=void 0;};if(i!==void 0){let f=()=>r();i.addEventListener("abort",f,{once:true}),c.c=()=>i.removeEventListener("abort",f);}return r}function j(d){let c=d?.captureHandlerErrors,i=new Map,r=[],f=false;function p(){if(f)throw new g("aieventjs: emitter has been disposed")}function h(n){let t=i.get(n);return t===void 0&&(t=[],i.set(n,t)),t}function k(n,t,o){p();let s=o?.sampleRate,u=o?.throttleMs;if(n==="*"){if(o?.captureErrors!==void 0)throw new E("aieventjs: captureErrors invalid on *")}else if(s!==void 0)throw new E("aieventjs: sampleRate wildcard-only");if(s!==void 0&&(!Number.isFinite(s)||s<=0||s>1))throw new E("aieventjs: sampleRate must be in (0,1]");if(u!==void 0&&(!Number.isFinite(u)||u<0))throw new E("aieventjs: throttleMs must be >= 0");let l=o?.signal;if(l?.aborted)return ()=>{};if(n==="*"){let v=t;if(o?.once){let m=y(r,{h:(R,M)=>{m(),v(R,M);},u:v,c:void 0,r:s,tm:u},l);return m}return y(r,{h:v,u:v,c:void 0,r:s,tm:u},l)}let e=t,a=o?.captureErrors;if(o?.once){let v={h:m=>{w(),e(m);},u:e,c:void 0,ce:a,tm:u},w=y(h(n),v,l);return w}return y(h(n),{h:e,u:e,c:void 0,ce:a,tm:u},l)}function A(n,t){return k(n,t,{once:true})}function O(n,t){if(p(),n==="*"){t===void 0?(H(r),r.length=0):K(r,t);return}let o=i.get(n);o!==void 0&&(t===void 0?(H(o),o.length=0,i.delete(n)):K(o,t));}function x(n,t,o,s){if(n===void 0||n===false)throw t;if(typeof n=="function")try{n(t,o,s);}catch{}}function W(n,t){p();let o=n,s=t,u=(i.get(o)??[]).slice(),l=r.slice();for(let e of u){if(e.tm){let a=Date.now();if(e.ts!==void 0&&a-e.ts<e.tm)continue;e.ts=a;}try{e.h(s);}catch(a){x(e.ce!==void 0?e.ce:c,a,o,s);}}for(let e of l)if(!(e.r!==void 0&&Math.random()>=e.r)){if(e.tm){let a=Date.now();if(e.ts!==void 0&&a-e.ts<e.tm)continue;e.ts=a;}try{e.h(o,s);}catch(a){x(c,a,o,s);}}}function b(){for(let n of i.values())H(n),n.length=0;H(r),i.clear(),r.length=0;}return {on:k,once:A,off:O,emit:W,clear(){p(),b();},dispose(){f||(b(),f=true);},get disposed(){return f}}}exports.EmitterDisposedError=g;exports.EmitterError=E;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","e","flush","sub","sig","rm","i","fn","createEmitter","opts","cap","t","w","d","ck","ga","k","a","on","type","handler","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 * 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 Date.now(). 0 = no throttle. Non-finite or negative values are rejected.\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 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` 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 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 tm: tm2,\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, tm: tm2 }, 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 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(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"]}
|
|
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","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 * 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 `Date.now()`. 0 = no throttle. Non-finite or negative values are\n * 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 * **Wall-clock limitation (EVT-R-02):** the throttle clock uses\n * `Date.now()`, which is not monotonic. If the system clock regresses (NTP\n * correction, manual change) by Δ ms after a dispatch, `now - e.ts` will be\n * negative and every subsequent dispatch will be dropped until wall time\n * re-passes the stored timestamp — up to Δ ms of silence with no error.\n * For most web/game use cases this is acceptable; switching to\n * `performance.now()` (monotonic) would be a surface-level behaviour change\n * and is deferred to the next minor release window. Document this trade-off\n * at integration if clock stability is a concern.\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 public\n * overload only accepts `K extends keyof Events`; the wildcard `\"*\"` is\n * handled by the `on(\"*\", handler, { once: true })` overload instead.\n * Passing `\"*\"` to `once()` would route through `on()` with the wildcard\n * branch and invoke the handler as `(type, payload)` — the first positional\n * argument would be the event *name*, not the payload — which diverges from\n * the `EventHandler<payload>` type implied by the `once` signature. Use\n * `on(\"*\", handler, { once: true })` explicitly for wildcard-once semantics.\n * This behaviour is intentional and deferred for a type-level fix to the\n * next minor that can introduce a breaking overload change. (EVT-B-02)\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` 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 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 tm: tm2,\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, tm: tm2 }, 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 arr.length = 0;\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 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(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()) {\n flush(a);\n a.length = 0;\n }\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
|
@@ -68,11 +68,23 @@ interface OnOptions {
|
|
|
68
68
|
* Per-handler leading-edge throttle. Minimum milliseconds between successive
|
|
69
69
|
* calls to this handler. The first dispatch after subscription always fires;
|
|
70
70
|
* subsequent dispatches within `throttleMs` are dropped (not queued).
|
|
71
|
-
* Uses Date.now()
|
|
71
|
+
* Uses `Date.now()`. 0 = no throttle. Non-finite or negative values are
|
|
72
|
+
* rejected at `on()` time.
|
|
72
73
|
*
|
|
73
74
|
* Valid on both typed and wildcard `"*"` subscriptions (since v0.5.3); each
|
|
74
75
|
* handler keeps its own throttle clock. Useful for per-event HUD throttling,
|
|
75
76
|
* e.g. a `credits/change` event that fires every frame.
|
|
77
|
+
*
|
|
78
|
+
* @remarks
|
|
79
|
+
* **Wall-clock limitation (EVT-R-02):** the throttle clock uses
|
|
80
|
+
* `Date.now()`, which is not monotonic. If the system clock regresses (NTP
|
|
81
|
+
* correction, manual change) by Δ ms after a dispatch, `now - e.ts` will be
|
|
82
|
+
* negative and every subsequent dispatch will be dropped until wall time
|
|
83
|
+
* re-passes the stored timestamp — up to Δ ms of silence with no error.
|
|
84
|
+
* For most web/game use cases this is acceptable; switching to
|
|
85
|
+
* `performance.now()` (monotonic) would be a surface-level behaviour change
|
|
86
|
+
* and is deferred to the next minor release window. Document this trade-off
|
|
87
|
+
* at integration if clock stability is a concern.
|
|
76
88
|
*/
|
|
77
89
|
throttleMs?: number;
|
|
78
90
|
}
|
|
@@ -99,6 +111,18 @@ interface Emitter<Events extends Record<string, unknown>> {
|
|
|
99
111
|
/**
|
|
100
112
|
* Subscribe and auto-remove after the first dispatch. Equivalent to
|
|
101
113
|
* `on(type, handler, { once: true })`.
|
|
114
|
+
*
|
|
115
|
+
* @remarks
|
|
116
|
+
* **`"*"` is not a valid `type` argument for `once()`.** The public
|
|
117
|
+
* overload only accepts `K extends keyof Events`; the wildcard `"*"` is
|
|
118
|
+
* handled by the `on("*", handler, { once: true })` overload instead.
|
|
119
|
+
* Passing `"*"` to `once()` would route through `on()` with the wildcard
|
|
120
|
+
* branch and invoke the handler as `(type, payload)` — the first positional
|
|
121
|
+
* argument would be the event *name*, not the payload — which diverges from
|
|
122
|
+
* the `EventHandler<payload>` type implied by the `once` signature. Use
|
|
123
|
+
* `on("*", handler, { once: true })` explicitly for wildcard-once semantics.
|
|
124
|
+
* This behaviour is intentional and deferred for a type-level fix to the
|
|
125
|
+
* next minor that can introduce a breaking overload change. (EVT-B-02)
|
|
102
126
|
*/
|
|
103
127
|
once<K extends keyof Events>(type: K, handler: EventHandler<Events[K]>): () => void;
|
|
104
128
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -68,11 +68,23 @@ interface OnOptions {
|
|
|
68
68
|
* Per-handler leading-edge throttle. Minimum milliseconds between successive
|
|
69
69
|
* calls to this handler. The first dispatch after subscription always fires;
|
|
70
70
|
* subsequent dispatches within `throttleMs` are dropped (not queued).
|
|
71
|
-
* Uses Date.now()
|
|
71
|
+
* Uses `Date.now()`. 0 = no throttle. Non-finite or negative values are
|
|
72
|
+
* rejected at `on()` time.
|
|
72
73
|
*
|
|
73
74
|
* Valid on both typed and wildcard `"*"` subscriptions (since v0.5.3); each
|
|
74
75
|
* handler keeps its own throttle clock. Useful for per-event HUD throttling,
|
|
75
76
|
* e.g. a `credits/change` event that fires every frame.
|
|
77
|
+
*
|
|
78
|
+
* @remarks
|
|
79
|
+
* **Wall-clock limitation (EVT-R-02):** the throttle clock uses
|
|
80
|
+
* `Date.now()`, which is not monotonic. If the system clock regresses (NTP
|
|
81
|
+
* correction, manual change) by Δ ms after a dispatch, `now - e.ts` will be
|
|
82
|
+
* negative and every subsequent dispatch will be dropped until wall time
|
|
83
|
+
* re-passes the stored timestamp — up to Δ ms of silence with no error.
|
|
84
|
+
* For most web/game use cases this is acceptable; switching to
|
|
85
|
+
* `performance.now()` (monotonic) would be a surface-level behaviour change
|
|
86
|
+
* and is deferred to the next minor release window. Document this trade-off
|
|
87
|
+
* at integration if clock stability is a concern.
|
|
76
88
|
*/
|
|
77
89
|
throttleMs?: number;
|
|
78
90
|
}
|
|
@@ -99,6 +111,18 @@ interface Emitter<Events extends Record<string, unknown>> {
|
|
|
99
111
|
/**
|
|
100
112
|
* Subscribe and auto-remove after the first dispatch. Equivalent to
|
|
101
113
|
* `on(type, handler, { once: true })`.
|
|
114
|
+
*
|
|
115
|
+
* @remarks
|
|
116
|
+
* **`"*"` is not a valid `type` argument for `once()`.** The public
|
|
117
|
+
* overload only accepts `K extends keyof Events`; the wildcard `"*"` is
|
|
118
|
+
* handled by the `on("*", handler, { once: true })` overload instead.
|
|
119
|
+
* Passing `"*"` to `once()` would route through `on()` with the wildcard
|
|
120
|
+
* branch and invoke the handler as `(type, payload)` — the first positional
|
|
121
|
+
* argument would be the event *name*, not the payload — which diverges from
|
|
122
|
+
* the `EventHandler<payload>` type implied by the `once` signature. Use
|
|
123
|
+
* `on("*", handler, { once: true })` explicitly for wildcard-once semantics.
|
|
124
|
+
* This behaviour is intentional and deferred for a type-level fix to the
|
|
125
|
+
* next minor that can introduce a breaking overload change. (EVT-B-02)
|
|
102
126
|
*/
|
|
103
127
|
once<K extends keyof Events>(type: K, handler: EventHandler<Events[K]>): () => void;
|
|
104
128
|
/**
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var E=class extends Error{name="EmitterError"},g=class extends Error{name="EmitterDisposedError"};function K(d,c){let i=d.findIndex(r=>r.u===c);if(i>=0){let r=d[i];r!==void 0&&(r.c?.(),r.c=void 0),d.splice(i,1);}}function H(d){for(let c of d)c.c?.(),c.c=void 0;}function y(d,c,i){d.push(c);let r=()=>{let f=d.indexOf(c);f>=0&&d.splice(f,1),c.c?.(),c.c=void 0;};if(i!==void 0){let f=()=>r();i.addEventListener("abort",f,{once:true}),c.c=()=>i.removeEventListener("abort",f);}return r}function j(d){let c=d?.captureHandlerErrors,i=new Map,r=[],f=false;function p(){if(f)throw new g("aieventjs: emitter has been disposed")}function h(n){let t=i.get(n);return t===void 0&&(t=[],i.set(n,t)),t}function k(n,t,o){p();let s=o?.sampleRate,u=o?.throttleMs;if(n==="*"){if(o?.captureErrors!==void 0)throw new E("aieventjs: captureErrors invalid on *")}else if(s!==void 0)throw new E("aieventjs: sampleRate wildcard-only");if(s!==void 0&&(!Number.isFinite(s)||s<=0||s>1))throw new E("aieventjs: sampleRate must be in (0,1]");if(u!==void 0&&(!Number.isFinite(u)||u<0))throw new E("aieventjs: throttleMs must be >= 0");let l=o?.signal;if(l?.aborted)return ()=>{};if(n==="*"){let v=t;if(o?.once){let m=y(r,{h:(R,M)=>{m(),v(R,M);},u:v,c:void 0,r:s,tm:u},l);return m}return y(r,{h:v,u:v,c:void 0,r:s,tm:u},l)}let e=t,a=o?.captureErrors;if(o?.once){let v={h:m=>{w(),e(m);},u:e,c:void 0,ce:a,tm:u},w=y(h(n),v,l);return w}return y(h(n),{h:e,u:e,c:void 0,ce:a,tm:u},l)}function A(n,t){return k(n,t,{once:true})}function O(n,t){if(p(),n==="*"){t===void 0?(H(r),r.length=0):K(r,t);return}let o=i.get(n);o!==void 0&&(t===void 0?(H(o),i.delete(n)):K(o,t));}function x(n,t,o,s){if(n===void 0||n===false)throw t;if(typeof n=="function")try{n(t,o,s);}catch{}}function W(n,t){p();let o=n,s=t,u=(i.get(o)??[]).slice(),l=r.slice();for(let e of u){if(e.tm){let a=Date.now();if(e.ts!==void 0&&a-e.ts<e.tm)continue;e.ts=a;}try{e.h(s);}catch(a){x(e.ce!==void 0?e.ce:c,a,o,s);}}for(let e of l)if(!(e.r!==void 0&&Math.random()>=e.r)){if(e.tm){let a=Date.now();if(e.ts!==void 0&&a-e.ts<e.tm)continue;e.ts=a;}try{e.h(o,s);}catch(a){x(c,a,o,s);}}}function b(){for(let n of i.values())H(n);H(r),i.clear(),r.length=0;}return {on:k,once:A,off:O,emit:W,clear(){p(),b();},dispose(){f||(b(),f=true);},get disposed(){return f}}}export{g as EmitterDisposedError,E as EmitterError,j as createEmitter};//# sourceMappingURL=index.js.map
|
|
1
|
+
var E=class extends Error{name="EmitterError"},g=class extends Error{name="EmitterDisposedError"};function K(d,c){let i=d.findIndex(r=>r.u===c);if(i>=0){let r=d[i];r!==void 0&&(r.c?.(),r.c=void 0),d.splice(i,1);}}function H(d){for(let c of d)c.c?.(),c.c=void 0;}function y(d,c,i){d.push(c);let r=()=>{let f=d.indexOf(c);f>=0&&d.splice(f,1),c.c?.(),c.c=void 0;};if(i!==void 0){let f=()=>r();i.addEventListener("abort",f,{once:true}),c.c=()=>i.removeEventListener("abort",f);}return r}function j(d){let c=d?.captureHandlerErrors,i=new Map,r=[],f=false;function p(){if(f)throw new g("aieventjs: emitter has been disposed")}function h(n){let t=i.get(n);return t===void 0&&(t=[],i.set(n,t)),t}function k(n,t,o){p();let s=o?.sampleRate,u=o?.throttleMs;if(n==="*"){if(o?.captureErrors!==void 0)throw new E("aieventjs: captureErrors invalid on *")}else if(s!==void 0)throw new E("aieventjs: sampleRate wildcard-only");if(s!==void 0&&(!Number.isFinite(s)||s<=0||s>1))throw new E("aieventjs: sampleRate must be in (0,1]");if(u!==void 0&&(!Number.isFinite(u)||u<0))throw new E("aieventjs: throttleMs must be >= 0");let l=o?.signal;if(l?.aborted)return ()=>{};if(n==="*"){let v=t;if(o?.once){let m=y(r,{h:(R,M)=>{m(),v(R,M);},u:v,c:void 0,r:s,tm:u},l);return m}return y(r,{h:v,u:v,c:void 0,r:s,tm:u},l)}let e=t,a=o?.captureErrors;if(o?.once){let v={h:m=>{w(),e(m);},u:e,c:void 0,ce:a,tm:u},w=y(h(n),v,l);return w}return y(h(n),{h:e,u:e,c:void 0,ce:a,tm:u},l)}function A(n,t){return k(n,t,{once:true})}function O(n,t){if(p(),n==="*"){t===void 0?(H(r),r.length=0):K(r,t);return}let o=i.get(n);o!==void 0&&(t===void 0?(H(o),o.length=0,i.delete(n)):K(o,t));}function x(n,t,o,s){if(n===void 0||n===false)throw t;if(typeof n=="function")try{n(t,o,s);}catch{}}function W(n,t){p();let o=n,s=t,u=(i.get(o)??[]).slice(),l=r.slice();for(let e of u){if(e.tm){let a=Date.now();if(e.ts!==void 0&&a-e.ts<e.tm)continue;e.ts=a;}try{e.h(s);}catch(a){x(e.ce!==void 0?e.ce:c,a,o,s);}}for(let e of l)if(!(e.r!==void 0&&Math.random()>=e.r)){if(e.tm){let a=Date.now();if(e.ts!==void 0&&a-e.ts<e.tm)continue;e.ts=a;}try{e.h(o,s);}catch(a){x(c,a,o,s);}}}function b(){for(let n of i.values())H(n),n.length=0;H(r),i.clear(),r.length=0;}return {on:k,once:A,off:O,emit:W,clear(){p(),b();},dispose(){f||(b(),f=true);},get disposed(){return f}}}export{g as EmitterDisposedError,E 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","e","flush","sub","sig","rm","i","fn","createEmitter","opts","cap","t","w","d","ck","ga","k","a","on","type","handler","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 * 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 Date.now(). 0 = no throttle. Non-finite or negative values are rejected.\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 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` 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 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 tm: tm2,\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, tm: tm2 }, 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 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(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"]}
|
|
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","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 * 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 `Date.now()`. 0 = no throttle. Non-finite or negative values are\n * 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 * **Wall-clock limitation (EVT-R-02):** the throttle clock uses\n * `Date.now()`, which is not monotonic. If the system clock regresses (NTP\n * correction, manual change) by Δ ms after a dispatch, `now - e.ts` will be\n * negative and every subsequent dispatch will be dropped until wall time\n * re-passes the stored timestamp — up to Δ ms of silence with no error.\n * For most web/game use cases this is acceptable; switching to\n * `performance.now()` (monotonic) would be a surface-level behaviour change\n * and is deferred to the next minor release window. Document this trade-off\n * at integration if clock stability is a concern.\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 public\n * overload only accepts `K extends keyof Events`; the wildcard `\"*\"` is\n * handled by the `on(\"*\", handler, { once: true })` overload instead.\n * Passing `\"*\"` to `once()` would route through `on()` with the wildcard\n * branch and invoke the handler as `(type, payload)` — the first positional\n * argument would be the event *name*, not the payload — which diverges from\n * the `EventHandler<payload>` type implied by the `once` signature. Use\n * `on(\"*\", handler, { once: true })` explicitly for wildcard-once semantics.\n * This behaviour is intentional and deferred for a type-level fix to the\n * next minor that can introduce a breaking overload change. (EVT-B-02)\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` 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 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 tm: tm2,\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, tm: tm2 }, 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 arr.length = 0;\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 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(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()) {\n flush(a);\n a.length = 0;\n }\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
|
@@ -14,16 +14,16 @@ The short index lives at `llms.txt` (see https://llmstxt.org/).
|
|
|
14
14
|
# aieventjs
|
|
15
15
|
|
|
16
16
|
[](https://www.npmjs.com/package/aieventjs)
|
|
17
|
-
[](https://github.com/islumina/aieventjs/actions/workflows/ci.yml)
|
|
18
18
|
[](LICENSE)
|
|
19
19
|
[](https://www.anthropic.com/claude-code)
|
|
20
20
|
[](README_ZHTW.md)
|
|
21
21
|
|
|
22
22
|
> A small, strict, typed event emitter — `on()` returns an unsubscribe function, `once` is built-in, `AbortSignal` is first-class, `dispose()` is idempotent, wildcard `*` handlers are preserved. Mitt-shaped API where it counts; ai\*js conventions everywhere else.
|
|
23
23
|
|
|
24
|
-
Part of the [ai\*js micro-runtime ecosystem](https://github.com/
|
|
24
|
+
Part of the [ai\*js micro-runtime ecosystem](https://github.com/islumina) — see also [aifsmjs](https://github.com/islumina/aifsmjs) (FSM), [aiecsjs](https://github.com/islumina/aiecsjs) (ECS), [aibridgejs](https://github.com/islumina/aibridgejs) (cross-context RPC), [aipooljs](https://github.com/islumina/aipooljs) (object pool), [aiquadtreejs](https://github.com/islumina/aiquadtreejs) (spatial partitioning), and [aiaudiojs](https://github.com/islumina/aiaudiojs) (Web Audio shell).
|
|
25
25
|
|
|
26
|
-
> **Status: 0.5.
|
|
26
|
+
> **Status: 0.5.6.** Full implementation shipped; all methods are live. Coverage ≥ 95/90/100/100; ~1050 B gzip (budget 1100 B).
|
|
27
27
|
|
|
28
28
|
---
|
|
29
29
|
|
|
@@ -132,6 +132,9 @@ interface OnOptions {
|
|
|
132
132
|
captureErrors?: boolean | ((err: unknown, type: string, payload: unknown) => void); // typed only
|
|
133
133
|
sampleRate?: number; // wildcard "*" only — probability in (0, 1]
|
|
134
134
|
throttleMs?: number; // typed or wildcard — per-handler leading-edge throttle, uses Date.now()
|
|
135
|
+
// Note: Date.now() is not monotonic; a system-clock regression silently
|
|
136
|
+
// mutes the handler until wall time re-passes the stored timestamp.
|
|
137
|
+
// Switching to performance.now() is deferred to the next minor. (EVT-R-02)
|
|
135
138
|
}
|
|
136
139
|
|
|
137
140
|
interface EmitterOptions {
|
|
@@ -147,6 +150,9 @@ interface Emitter<Events extends Record<string, unknown>> {
|
|
|
147
150
|
on<K extends keyof Events>(type: K, handler: EventHandler<Events[K]>, opts?: OnOptions): () => void;
|
|
148
151
|
on(type: "*", handler: WildcardHandler<Events>, opts?: OnOptions): () => void;
|
|
149
152
|
once<K extends keyof Events>(type: K, handler: EventHandler<Events[K]>): () => void;
|
|
153
|
+
// Note: once() only accepts typed event keys. For wildcard-once semantics use
|
|
154
|
+
// on("*", handler, { once: true }) — the handler receives (type, payload) as
|
|
155
|
+
// WildcardHandler, not (payload) as EventHandler. (EVT-B-02)
|
|
150
156
|
off<K extends keyof Events>(type: K, handler?: EventHandler<Events[K]>): void;
|
|
151
157
|
off(type: "*", handler?: WildcardHandler<Events>): void;
|
|
152
158
|
emit<K extends keyof Events>(type: K, payload: Events[K]): void;
|
|
@@ -195,6 +201,27 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
195
201
|
|
|
196
202
|
## [Unreleased]
|
|
197
203
|
|
|
204
|
+
## [0.5.6] - 2026-06-10
|
|
205
|
+
|
|
206
|
+
### Fixed
|
|
207
|
+
|
|
208
|
+
- `purge()` and `off(type)` now truncate the typed handler arrays they flush (mirroring the wildcard array's existing handling), so a retained unsubscribe closure no longer pins every sibling handler entry against GC. (Review wave 2026-06-10, EVT-R-01.)
|
|
209
|
+
|
|
210
|
+
### Changed
|
|
211
|
+
|
|
212
|
+
- Supply-chain and release hardening: CI/publish actions SHA-pinned, npm CLI pinned (`11.16.0`) in the OIDC publish job, `permissions: contents: read` on CI, job timeouts, tag↔package.json version guard, `npm publish --ignore-scripts`, and **manual publish dispatch now defaults to dry-run** (it previously performed a real publish). New `verify:docs` banner gate; `typecheck` now also type-checks the test suite; `llms-full.txt` embeds `STABILITY.md`.
|
|
213
|
+
|
|
214
|
+
### Docs
|
|
215
|
+
|
|
216
|
+
- `STABILITY.md` freeze table now records `OnOptions.throttleMs` as **typed or wildcard since 0.5.3** (the table previously only listed the 0.3.0 wildcard-only surface). (EVT-B-01.)
|
|
217
|
+
- `once("*")` is documented (and pinned by test) as routing through the wildcard `on()` overload with `(type, payload)` handler arguments; the wall-clock (`Date.now()`) limitation of `throttleMs` under backwards clock steps is documented. New pins for abort-listener detach on `off()` paths and `once` × `sampleRate` interaction.
|
|
218
|
+
|
|
219
|
+
## [0.5.5] - 2026-06-08
|
|
220
|
+
|
|
221
|
+
### Changed
|
|
222
|
+
|
|
223
|
+
- Project home migrated to the [`islumina`](https://github.com/islumina) GitHub org; the package is now published from there via npm trusted publisher (OIDC + SLSA provenance). Family-wide version alignment at `0.5.5` — no runtime or API changes.
|
|
224
|
+
|
|
198
225
|
## [0.5.3] - 2026-06-05
|
|
199
226
|
|
|
200
227
|
### Added
|
|
@@ -338,6 +365,55 @@ No runtime / source / API changes. This is a CI-only patch to validate the GitHu
|
|
|
338
365
|
|
|
339
366
|
---
|
|
340
367
|
|
|
368
|
+
<!-- ===== STABILITY.md ===== -->
|
|
369
|
+
|
|
370
|
+
# aieventjs Stability Index
|
|
371
|
+
|
|
372
|
+
Tags follow Node's stability index conventions:
|
|
373
|
+
|
|
374
|
+
- `[stable]` — frozen API, semver-protected.
|
|
375
|
+
- `[experimental]` — usable but may change without major bump in 0.x; will stabilise pre-1.0.
|
|
376
|
+
- `[draft]` — placeholder; API not yet shipped.
|
|
377
|
+
|
|
378
|
+
## Public API
|
|
379
|
+
|
|
380
|
+
| Surface | Stability | Since |
|
|
381
|
+
|---|---|---|
|
|
382
|
+
| `createEmitter<Events>(opts?)` | [stable] | 0.1.0 |
|
|
383
|
+
| `on / once / off / emit / clear / dispose / disposed` | [stable] | 0.1.0 |
|
|
384
|
+
| `on(type, handler, { signal })` (AbortSignal) | [stable] | 0.1.0 |
|
|
385
|
+
| Wildcard `"*"` handler | [stable] | 0.1.0 |
|
|
386
|
+
| `EmitterOptions.captureHandlerErrors` (boolean) | [stable] | 0.3.0 |
|
|
387
|
+
| `EmitterOptions.captureHandlerErrors` (callback) | [stable] | 0.3.0 |
|
|
388
|
+
| `OnOptions.captureErrors` | [stable] | 0.3.0 |
|
|
389
|
+
| `OnOptions.sampleRate` (wildcard only) | [stable] | 0.3.0 |
|
|
390
|
+
| `OnOptions.throttleMs` (wildcard only) | [stable] | 0.3.0 |
|
|
391
|
+
| `OnOptions.throttleMs` (typed or wildcard) | [stable] | 0.5.3 |
|
|
392
|
+
| `EmitterError` / `EmitterDisposedError` | [stable] | 0.1.0 |
|
|
393
|
+
|
|
394
|
+
> **Stability freeze (0.4.0).** Every `[stable]` row above is frozen for the 1.x line: once 1.0 ships, these signatures, error names, and default behaviours will not change without a major version bump. v0.4.0 adds no runtime API — it formalises the 0.3.x surface as 1.0-track.
|
|
395
|
+
|
|
396
|
+
## Drafts (not yet implemented)
|
|
397
|
+
|
|
398
|
+
### Async handler tracking — [experimental] placeholder
|
|
399
|
+
|
|
400
|
+
Targeted for v0.6+. Concept sketch (subject to change):
|
|
401
|
+
|
|
402
|
+
```ts
|
|
403
|
+
// possibly via a new emitter option
|
|
404
|
+
createEmitter<Events>({
|
|
405
|
+
awaitAsyncHandlers: true,
|
|
406
|
+
});
|
|
407
|
+
// emit() may return a Promise that resolves after Promise.allSettled over
|
|
408
|
+
// any handler that returned a Promise. Sync handlers run synchronously
|
|
409
|
+
// before any awaiting begins. Snapshot semantics preserved.
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
Not part of v0.4.0; do not depend on this API. The current `emit()` remains
|
|
413
|
+
fully synchronous and ignores handler return values.
|
|
414
|
+
|
|
415
|
+
---
|
|
416
|
+
|
|
341
417
|
<!-- ===== CONTRIBUTING.md ===== -->
|
|
342
418
|
|
|
343
419
|
# Contributing to aieventjs
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aieventjs",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.6",
|
|
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",
|
|
@@ -15,13 +15,13 @@
|
|
|
15
15
|
],
|
|
16
16
|
"author": "yshengliao",
|
|
17
17
|
"license": "MIT",
|
|
18
|
-
"homepage": "https://github.com/
|
|
18
|
+
"homepage": "https://github.com/islumina/aieventjs#readme",
|
|
19
19
|
"repository": {
|
|
20
20
|
"type": "git",
|
|
21
|
-
"url": "git+https://github.com/
|
|
21
|
+
"url": "git+https://github.com/islumina/aieventjs.git"
|
|
22
22
|
},
|
|
23
23
|
"bugs": {
|
|
24
|
-
"url": "https://github.com/
|
|
24
|
+
"url": "https://github.com/islumina/aieventjs/issues"
|
|
25
25
|
},
|
|
26
26
|
"type": "module",
|
|
27
27
|
"sideEffects": false,
|
|
@@ -49,13 +49,14 @@
|
|
|
49
49
|
"test:watch": "vitest",
|
|
50
50
|
"lint": "biome check src test",
|
|
51
51
|
"format": "biome format --write src test",
|
|
52
|
-
"typecheck": "tsc --noEmit",
|
|
52
|
+
"typecheck": "tsc --noEmit && tsc -p tsconfig.test.json --noEmit",
|
|
53
|
+
"verify:docs": "node scripts/verify-docs.mjs",
|
|
53
54
|
"verify:exports": "node scripts/verify-exports.mjs",
|
|
54
55
|
"check:size": "node scripts/check-size.mjs",
|
|
55
56
|
"build:llms": "node scripts/build-llms-full.mjs",
|
|
56
57
|
"verify:llms": "node scripts/build-llms-full.mjs --check",
|
|
57
58
|
"coverage": "vitest run --coverage",
|
|
58
|
-
"prepublishOnly": "pnpm typecheck && pnpm lint && pnpm coverage && pnpm build && pnpm verify:exports && pnpm verify:llms && pnpm check:size"
|
|
59
|
+
"prepublishOnly": "pnpm typecheck && pnpm lint && pnpm verify:docs && pnpm coverage && pnpm build && pnpm verify:exports && pnpm verify:llms && pnpm check:size"
|
|
59
60
|
},
|
|
60
61
|
"devDependencies": {
|
|
61
62
|
"@biomejs/biome": "^1.9.0",
|