aieventjs 0.5.5 → 0.5.8

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/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"},h=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 h("aieventjs: emitter has been disposed")}function g(e){let t=i.get(e);return t===void 0&&(t=[],i.set(e,t)),t}function k(e,t,o){p();let s=o?.sampleRate,u=o?.throttleMs;if(e==="*"){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(e==="*"){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 n=t,a=o?.captureErrors;if(o?.once){let v={h:m=>{w(),n(m);},u:n,c:void 0,ce:a,tm:u},w=y(g(e),v,l);return w}return y(g(e),{h:n,u:n,c:void 0,ce:a,tm:u},l)}function A(e,t){return k(e,t,{once:true})}function O(e,t){if(p(),e==="*"){t===void 0?(H(r),r.length=0):K(r,t);return}let o=i.get(e);o!==void 0&&(t===void 0?(H(o),o.length=0,i.delete(e)):K(o,t));}function x(e,t,o,s){if(e===void 0||e===false)throw t;if(typeof e=="function")try{e(t,o,s);}catch{}}function W(e,t){p();let o=e,s=t,u=(i.get(o)??[]).slice(),l=r.slice();for(let n of u){if(n.tm){let a=performance.now();if(n.ts!==void 0&&a-n.ts<n.tm)continue;n.ts=a;}try{n.h(s);}catch(a){x(n.ce!==void 0?n.ce:c,a,o,s);}}for(let n of l)if(!(n.r!==void 0&&Math.random()>=n.r)){if(n.tm){let a=performance.now();if(n.ts!==void 0&&a-n.ts<n.tm)continue;n.ts=a;}try{n.h(o,s);}catch(a){x(c,a,o,s);}}}function b(){for(let e of i.values())H(e),e.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{h 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 `performance.now()` (monotonic). 0 = no throttle. Non-finite or\n * negative values are rejected at `on()` time.\n *\n * Valid on both typed and wildcard `\"*\"` subscriptions (since v0.5.3); each\n * handler keeps its own throttle clock. Useful for per-event HUD throttling,\n * e.g. a `credits/change` event that fires every frame.\n *\n * @remarks\n * The throttle clock uses `performance.now()`, which is monotonic and\n * unaffected by system-clock corrections (NTP step-backs, manual adjustments).\n * This ensures handlers are never silently muted by a wall-clock regression.\n */\n throttleMs?: number;\n}\n\n/**\n * Strongly-typed event emitter. Subscribe with {@link Emitter.on} (returns\n * an unsubscribe function), dispatch with {@link Emitter.emit}, dispose\n * with {@link Emitter.dispose} when finished.\n *\n * @typeParam Events — a string-keyed map from event name to payload type.\n * @public\n */\nexport interface Emitter<Events extends Record<string, unknown>> {\n /**\n * Subscribe to a single event type. Returns an unsubscribe function;\n * calling it (or aborting `opts.signal`) removes the handler.\n */\n on<K extends keyof Events>(\n type: K,\n handler: EventHandler<Events[K]>,\n opts?: OnOptions,\n ): () => void;\n\n /**\n * Subscribe to every event with a single handler that receives\n * `(type, payload)`. Wildcard handlers fire AFTER type-matched\n * handlers — same ordering as `mitt`.\n */\n on(type: \"*\", handler: WildcardHandler<Events>, opts?: OnOptions): () => void;\n\n /**\n * Subscribe and auto-remove after the first dispatch. Equivalent to\n * `on(type, handler, { once: true })`.\n *\n * @remarks\n * **`\"*\"` is not a valid `type` argument for `once()`.** The wildcard is\n * handled by the `on(\"*\", handler, { once: true })` overload instead.\n * The explicit rejection overload below ensures `once(\"*\", ...)` is a\n * compile-time error (handler typed as `never`). (EVT-B-02)\n */\n /** @internal — compile-time rejection: `once(\"*\", handler)` is a type error. */\n once(type: \"*\", handler: never): never;\n once<K extends keyof Events>(type: K, handler: EventHandler<Events[K]>): () => void;\n\n /**\n * Imperative unsubscribe. Prefer the unsubscribe function returned by\n * `on()` — it's faster (no reference lookup) and survives renames.\n * If `handler` is omitted, removes every handler for `type`.\n */\n off<K extends keyof Events>(type: K, handler?: EventHandler<Events[K]>): void;\n\n /**\n * Imperative wildcard unsubscribe.\n */\n off(type: \"*\", handler?: WildcardHandler<Events>): void;\n\n /**\n * Dispatch synchronously. Handlers receive `payload`; wildcard handlers\n * receive `(type, payload)`. Handler lists are snapshotted before iteration,\n * so removing a handler inside its own callback does not skip subsequent\n * handlers. By default, the first throwing handler aborts the dispatch;\n * set EmitterOptions.captureHandlerErrors (or per-handler OnOptions.captureErrors)\n * to swallow or report errors and continue.\n */\n emit<K extends keyof Events>(type: K, payload: Events[K]): void;\n\n /**\n * Remove every handler for every event (including wildcards). The\n * emitter remains usable. Use {@link dispose} for permanent teardown.\n */\n clear(): void;\n\n /**\n * Idempotent teardown. Drops every handler; subsequent `on` / `once` /\n * `emit` / `off` / `clear` throw {@link EmitterDisposedError}.\n */\n dispose(): void;\n\n /** `true` once {@link dispose} has been called. */\n readonly disposed: boolean;\n}\n\n/**\n * Recoverable emitter error. Thrown by `on()` when `OnOptions` violates a\n * precondition: `captureErrors` set on a wildcard `\"*\"` subscription;\n * `sampleRate` set on a typed subscription; `sampleRate` outside `(0, 1]`; or\n * `throttleMs` non-finite or negative.\n *\n * @public\n */\nexport class EmitterError extends Error {\n override readonly name = \"EmitterError\";\n}\n\n/**\n * Thrown by any emitter method called after {@link Emitter.dispose}.\n *\n * @public\n */\nexport class EmitterDisposedError extends Error {\n override readonly name = \"EmitterDisposedError\";\n}\n\n// ---------------------------------------------------------------------------\n// Internal types\n// ---------------------------------------------------------------------------\n\n// Mutable `c` field (not optional `?:`) avoids exactOptionalPropertyTypes TS2412\n// when assigning undefined. Short field names reduce minified output size.\ntype ErrorPolicy = boolean | ((err: unknown, type: string, payload: unknown) => void);\n\ninterface E<H> {\n h: H; // handler (may be a once-wrapper)\n c: (() => void) | undefined; // abortCleanup\n u: H; // user-provided handler (off matching)\n // v0.3.0: per-handler error policy and throttle/sample state.\n // Fields typed as `T | undefined` (not just `T`) so that exactOptionalPropertyTypes\n // permits assigning `undefined` in object literals (avoids TS2375).\n ce?: ErrorPolicy | undefined; // captureErrors override (typed only)\n r?: number | undefined; // sampleRate (wildcard only)\n tm?: number | undefined; // throttleMs (typed or wildcard; v0.5.3)\n ts?: number | undefined; // last call timestamp — mutated during dispatch (throttle clock)\n}\n\ntype AH = EventHandler<unknown>;\ntype WH = WildcardHandler<Record<string, unknown>>;\n\n// Remove one entry by user-identity from an array; run its abort cleanup.\nfunction rmByUser<H>(arr: E<H>[], user: H): void {\n const i = arr.findIndex((e) => e.u === user);\n if (i >= 0) {\n const e = arr[i];\n if (e !== undefined) {\n e.c?.();\n e.c = undefined;\n }\n arr.splice(i, 1);\n }\n}\n\n// Flush all abort cleanups from an array (for clear / dispose).\nfunction flush<H>(arr: E<H>[]): void {\n for (const e of arr) {\n e.c?.();\n e.c = undefined;\n }\n}\n\n// Push entry onto arr, wire AbortSignal, return unsubscribe.\nfunction sub<H>(arr: E<H>[], e: E<H>, sig: AbortSignal | undefined): () => void {\n arr.push(e);\n const rm = () => {\n const i = arr.indexOf(e);\n if (i >= 0) arr.splice(i, 1);\n e.c?.();\n e.c = undefined;\n };\n if (sig !== undefined) {\n const fn = () => rm();\n sig.addEventListener(\"abort\", fn, { once: true });\n e.c = () => sig.removeEventListener(\"abort\", fn);\n }\n return rm;\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Construct a strongly-typed event emitter.\n *\n * @remarks\n * Declare the event map with a `type` alias, not an `interface`. The `Events`\n * generic is constrained to `Record<string, unknown>`, and a *plain* TypeScript\n * `interface` has no implicit index signature, so it fails the constraint with\n * *\"Index signature for type 'string' is missing in type ...\"*. A `type` object\n * literal satisfies the constraint structurally. (An `interface` with an explicit\n * index signature or `extends Record<string, unknown>` also compiles, but widens\n * `keyof Events` to `string`, losing strict event-name checking.)\n *\n * ```ts\n * // ❌ interface — fails the Record<string, unknown> constraint\n * interface Events { \"user:login\": { id: string } }\n * const bus = createEmitter<Events>(); // TS2344\n *\n * // ✅ type — satisfies the constraint\n * type Events = { \"user:login\": { id: string } };\n * const bus = createEmitter<Events>();\n * ```\n *\n * @example\n * ```ts\n * import { createEmitter } from \"aieventjs\";\n *\n * type Events = {\n * \"user:login\": { id: string };\n * \"user:logout\": void;\n * };\n *\n * const bus = createEmitter<Events>();\n *\n * const off = bus.on(\"user:login\", (u) => console.log(\"hi\", u.id));\n * bus.emit(\"user:login\", { id: \"alice\" });\n * off();\n *\n * bus.on(\"*\", (type, payload) => console.log(\"event\", type, payload));\n * ```\n *\n * @public\n */\nexport function createEmitter<Events extends Record<string, unknown> = Record<string, unknown>>(\n opts?: EmitterOptions,\n): Emitter<Events> {\n const cap = opts?.captureHandlerErrors;\n\n const t: Map<string, E<AH>[]> = new Map();\n const w: E<WH>[] = [];\n let d = false;\n\n function ck(): void {\n if (d) throw new EmitterDisposedError(\"aieventjs: emitter has been disposed\");\n }\n\n // Get or create typed handler array for a key.\n function ga(k: string): E<AH>[] {\n let a = t.get(k);\n if (a === undefined) {\n a = [];\n t.set(k, a);\n }\n return a;\n }\n\n function on(type: string | \"*\", handler: AH | WH, o?: OnOptions): () => void {\n ck();\n // v0.3.0 guards: cross-domain options + range checks.\n // v0.5.3: throttleMs is now valid on typed handlers too (per-handler clock);\n // sampleRate remains wildcard-only.\n const sr = o?.sampleRate;\n const tm2 = o?.throttleMs;\n if (type === \"*\") {\n if (o?.captureErrors !== undefined)\n throw new EmitterError(\"aieventjs: captureErrors invalid on *\");\n } else {\n if (sr !== undefined) throw new EmitterError(\"aieventjs: sampleRate wildcard-only\");\n }\n if (sr !== undefined && (!Number.isFinite(sr) || sr <= 0 || sr > 1))\n throw new EmitterError(\"aieventjs: sampleRate must be in (0,1]\");\n if (tm2 !== undefined && (!Number.isFinite(tm2) || tm2 < 0))\n throw new EmitterError(\"aieventjs: throttleMs must be >= 0\");\n const sig = o?.signal;\n if (sig?.aborted) return () => {};\n\n if (type === \"*\") {\n const fn = handler as WH;\n if (o?.once) {\n const e: E<WH> = {\n h: (tp, p) => {\n rm();\n fn(tp, p);\n },\n u: fn,\n c: undefined,\n r: sr,\n tm: tm2,\n };\n const rm = sub(w, e, sig);\n return rm;\n }\n return sub(w, { h: fn, u: fn, c: undefined, r: sr, tm: tm2 }, sig);\n }\n\n const fn = handler as AH;\n const ce = o?.captureErrors;\n 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 = performance.now();\n if (e.ts !== undefined && now - e.ts < e.tm) continue;\n e.ts = now;\n }\n try {\n e.h(p);\n } catch (err) {\n ap(e.ce !== undefined ? e.ce : cap, err, k, p);\n }\n }\n for (const e of ws) {\n if (e.r !== undefined && Math.random() >= e.r) continue;\n if (e.tm) {\n const now = performance.now();\n if (e.ts !== undefined && now - e.ts < e.tm) continue;\n e.ts = now;\n }\n try {\n e.h(k, p as never);\n } catch (err) {\n ap(cap, err, k, p);\n }\n }\n }\n\n function purge(): void {\n for (const a of t.values()) {\n flush(a);\n a.length = 0;\n }\n flush(w);\n t.clear();\n w.length = 0;\n }\n\n return {\n on: on as Emitter<Events>[\"on\"],\n once: once as Emitter<Events>[\"once\"],\n off: off as Emitter<Events>[\"off\"],\n emit,\n clear() {\n ck();\n purge();\n },\n dispose() {\n if (!d) {\n purge();\n d = true;\n }\n },\n get disposed() {\n return d;\n },\n };\n}\n"]}
package/llms-full.txt CHANGED
@@ -13,175 +13,70 @@ The short index lives at `llms.txt` (see https://llmstxt.org/).
13
13
 
14
14
  # aieventjs
15
15
 
16
- [![npm version](https://img.shields.io/npm/v/aieventjs.svg)](https://www.npmjs.com/package/aieventjs)
17
- [![CI](https://github.com/yshengliao/aieventjs/actions/workflows/ci.yml/badge.svg)](https://github.com/yshengliao/aieventjs/actions/workflows/ci.yml)
18
- [![License](https://img.shields.io/badge/license-MIT-brightgreen.svg)](LICENSE)
19
- [![AI Generated](https://img.shields.io/badge/AI_Generated-Claude_Code_Opus_4.7_Max-blueviolet.svg)](https://www.anthropic.com/claude-code)
20
- [![繁體中文](https://img.shields.io/badge/lang-繁體中文-red.svg)](README_ZHTW.md)
16
+ Small, strict, typed event emitter with ai*js lifecycle conventions: `on()` returns unsubscribe, `once` is built in, `AbortSignal` is first-class, wildcard handlers are supported, and `dispose()` is idempotent.
21
17
 
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.
18
+ > **Status: 0.5.8 - stable 1.0-track surface.** The root entry is the public API.
23
19
 
24
- Part of the [ai\*js micro-runtime ecosystem](https://github.com/yshengliao) — see also [aifsmjs](https://github.com/yshengliao/aifsmjs) (FSM), [aiecsjs](https://github.com/yshengliao/aiecsjs) (ECS), [aibridgejs](https://github.com/yshengliao/aibridgejs) (cross-context RPC), [aipooljs](https://github.com/yshengliao/aipooljs) (object pool), [aiquadtreejs](https://github.com/yshengliao/aiquadtreejs) (spatial partitioning), and [aiaudiojs](https://github.com/yshengliao/aiaudiojs) (Web Audio shell).
25
-
26
- > **Status: 0.5.3.** Full implementation shipped; all methods are live. Coverage ≥ 95/90/100/100; ~1050 B gzip (budget 1100 B).
27
-
28
- ---
29
-
30
- ## Why aieventjs
31
-
32
- Why not just use `mitt`? Honest answer: `mitt` is the right choice for many projects — it's MIT, ~282 B gzipped, and the API is genuinely well-shaped. We evaluated it and chose to write from scratch instead. Three reasons:
33
-
34
- - **mitt has been unmaintained since 2023-07-04.** The PRs the community most wants — `unsubscribe`-returning `on()`, `AbortSignal`, `sideEffects: false`, nodenext compatibility — are all open and untouched. Forking would mean shipping a copy with our name on it; the upstream couldn't accept improvements back even if we wanted.
35
- - **The implementation is ~35 lines of pure logic.** "Fork and improve" doesn't really exist at that size class — any non-trivial change is a rewrite, and the cost of carrying the upstream copyright notice exceeds the benefit.
36
- - **ai\*js conventions are pervasive enough that fitting them onto mitt's API surface would change every method signature.** `on()` returning `void` vs. returning an unsubscribe is the visible difference; the strict TypeScript posture (`noUncheckedIndexedAccess`, `exactOptionalPropertyTypes`, no `!` non-null assertions) is the invisible one that touches every line.
37
-
38
- So `aieventjs` is the ai\*js-shaped event emitter:
39
-
40
- - **`on()` returns an unsubscribe function.** Cleanup via the standard "call this to undo" idiom — closes over nothing, survives handler renames, drops in to `$effect()` / `onScopeDispose()` / `useEffect` cleanup without ceremony.
41
- - **`AbortSignal` everywhere it makes sense.** `on(type, handler, { signal })` removes the handler when the signal aborts. Pre-aborted signals never register. The whole library uses the same cancellation primitive as `fetch` and the rest of the platform.
42
- - **`dispose()` is idempotent.** Post-dispose `on` / `emit` / `once` throw `EmitterDisposedError`. This is the family-wide convention; an emitter that "looks alive but does nothing" after teardown is the canonical leak vector and we refuse to ship it.
43
- - **Wildcard `*` is preserved.** `bus.on("*", (type, payload) => ...)` works exactly like in `mitt`; wildcard handlers fire AFTER type-matched handlers. ~80 B gzip cost; kept to make migration mechanical.
44
- - **Handler-array snapshot on `emit`.** Removing a handler inside its own callback does not skip subsequent handlers (mitt has this since 2.x; preserved).
45
- - **Functional, destructurable.** `const { on, emit } = bus` works — no `this` capture anywhere.
46
-
47
- What this is **not**: not an async event bus (handlers are synchronous), not a namespaced bus (`user.*` style wildcards are out), not a priority queue, not a transport. It is the in-process synchronous fan-out primitive — nothing more.
48
-
49
- ---
50
-
51
- ## Quick Start
20
+ ## Install
52
21
 
53
22
  ```bash
54
23
  pnpm add aieventjs
55
24
  ```
56
25
 
57
- ```typescript
26
+ ```ts
58
27
  import { createEmitter } from "aieventjs";
28
+ ```
59
29
 
30
+ ## Quick Start
31
+
32
+ ```ts
60
33
  type Events = {
61
- "user:login": { id: string };
62
- "user:logout": void;
63
- "score:tick": { delta: number };
34
+ "score/change": { value: number };
35
+ "scene/end": void;
64
36
  };
65
37
 
66
- const bus = createEmitter<Events>();
67
-
68
- // 1. Subscribe; capture the unsubscribe handle.
69
- const off = bus.on("user:login", (u) => console.log("hi", u.id));
38
+ const events = createEmitter<Events>();
70
39
 
71
- // 2. Or wire to an AbortSignal for framework-native cleanup.
72
- const ctrl = new AbortController();
73
- bus.on("score:tick", (e) => render(e.delta), { signal: ctrl.signal });
40
+ const off = events.on("score/change", ({ value }) => {
41
+ console.log(value);
42
+ });
74
43
 
75
- // 3. Wildcard receives (type, payload) — fires AFTER type-matched handlers.
76
- bus.on("*", (type, payload) => trace(type, payload));
77
-
78
- // 4. Dispatch.
79
- bus.emit("user:login", { id: "alice" });
80
-
81
- // 5. Tear down.
44
+ events.on("*", (type, payload) => console.log(type, payload), { sampleRate: 0.1 });
45
+ events.emit("score/change", { value: 10 });
82
46
  off();
83
- ctrl.abort();
84
- bus.dispose(); // idempotent; post-dispose calls throw EmitterDisposedError
47
+ events.dispose();
85
48
  ```
86
49
 
87
- `createEmitter()` returns a plain object whose methods do not depend on `this` — `const { on, emit } = bus` works fine.
88
-
89
- > **Declare the event map with `type`, not `interface`.** The `Events` generic is constrained to `Record<string, unknown>`. A *plain* TypeScript `interface` has no implicit index signature, so passing one fails the constraint with *"Index signature for type 'string' is missing in type ..."*. A `type` object literal satisfies it structurally. (An `interface` with an explicit index signature — or one that `extends Record<string, unknown>` — also compiles, but widens `keyof Events` to `string` and loses strict event-name checking, so prefer `type`.)
90
- >
91
- > ```typescript
92
- > // ❌ interface — fails the Record<string, unknown> constraint (TS2344)
93
- > interface Events { "user:login": { id: string } }
94
- > const bus = createEmitter<Events>();
95
- >
96
- > // ✅ type — satisfies the constraint
97
- > type Events = { "user:login": { id: string } };
98
- > const bus = createEmitter<Events>();
99
- > ```
100
-
101
- ---
102
-
103
- ## Capabilities / Limitations
104
-
105
- | Will do (v1) | Won't do |
106
- | --------------------------------------------------------- | ----------------------------------------------------- |
107
- | Typed `createEmitter<Events>()` | Untyped string-key bus (the type is the point) |
108
- | `on()` returns unsubscribe function | Async / promise-returning handlers (sync only) |
109
- | `once(type, handler)` + `on(..., { once: true })` | Namespaced wildcards (`"user.*"`) — out of scope |
110
- | `on(..., { signal })` — `AbortSignal` cleanup | Priority / weight / ordering hints |
111
- | Wildcard `"*"` handler — `(type, payload)` | Cross-context transport (use `aibridgejs` for that) |
112
- | `dispose()` idempotent; post-dispose calls throw | Error-event special casing (Node EventEmitter style) |
113
- | Handler-array snapshot on `emit` (safe re-entrancy) | Persistent storage / replay (not its job) |
114
- | Destructurable methods (`const { on, emit } = bus`) | Zero-allocation `emit` (one snapshot per dispatch is required for re-entrancy) |
115
- | `on('*', fn, { sampleRate })` — probabilistic delivery for debug subscribers (wildcard only) | |
116
- | `on(type, fn, { throttleMs })` — per-handler leading-edge throttle, on typed **and** wildcard subscriptions (e.g. a per-frame `credits/change` HUD event) | |
117
- | `createEmitter({ captureHandlerErrors })` — opt-in error policy; per-handler override via `OnOptions.captureErrors` | |
118
-
119
- ---
50
+ ## Core API
120
51
 
121
- ## API sketch
122
-
123
- ```typescript
124
- type EventHandler<P> = (payload: P) => void;
125
-
126
- type WildcardHandler<Events extends Record<string, unknown>> =
127
- <K extends keyof Events>(type: K, payload: Events[K]) => void;
128
-
129
- interface OnOptions {
130
- signal?: AbortSignal;
131
- once?: boolean;
132
- captureErrors?: boolean | ((err: unknown, type: string, payload: unknown) => void); // typed only
133
- sampleRate?: number; // wildcard "*" only — probability in (0, 1]
134
- throttleMs?: number; // typed or wildcard — per-handler leading-edge throttle, uses Date.now()
135
- }
136
-
137
- interface EmitterOptions {
138
- // Default error policy. undefined/false (default): first throw aborts dispatch.
139
- // true: swallow errors and continue dispatch over all handlers.
140
- // (err, type, payload) => void: callback invoked per throwing handler
141
- // (if the callback itself throws, that error is silently ignored and dispatch continues).
142
- // Per-handler OnOptions.captureErrors overrides this for individual subscriptions.
143
- captureHandlerErrors?: boolean | ((err: unknown, type: string, payload: unknown) => void);
144
- }
145
-
146
- interface Emitter<Events extends Record<string, unknown>> {
147
- on<K extends keyof Events>(type: K, handler: EventHandler<Events[K]>, opts?: OnOptions): () => void;
148
- on(type: "*", handler: WildcardHandler<Events>, opts?: OnOptions): () => void;
149
- once<K extends keyof Events>(type: K, handler: EventHandler<Events[K]>): () => void;
150
- off<K extends keyof Events>(type: K, handler?: EventHandler<Events[K]>): void;
151
- off(type: "*", handler?: WildcardHandler<Events>): void;
152
- emit<K extends keyof Events>(type: K, payload: Events[K]): void;
153
- clear(): void;
154
- dispose(): void;
155
- readonly disposed: boolean;
156
- }
157
-
158
- class EmitterError extends Error {}
159
- class EmitterDisposedError extends Error {}
160
-
161
- function createEmitter<Events extends Record<string, unknown> = Record<string, unknown>>(
162
- opts?: EmitterOptions,
163
- ): Emitter<Events>;
164
- ```
52
+ - `createEmitter<Events>(options?)` creates a typed emitter.
53
+ - `on(type, handler, options?)` subscribes and returns an unsubscribe function.
54
+ - `on("*", wildcard, options?)` subscribes to every event after type-matched handlers.
55
+ - `once(type, handler)` is shorthand for a one-shot typed handler.
56
+ - `off(type, handler?)`, `clear()`, and `dispose()` remove handlers at different scopes.
57
+ - `emit(type, payload)` dispatches synchronously over a snapshot of handlers.
58
+ - Options: `signal`, `once`, `captureErrors`, `sampleRate` for wildcard, `throttleMs` for typed and wildcard.
165
59
 
166
- Full JSDoc lives in [`src/index.ts`](src/index.ts).
60
+ ## Sharp Edges
167
61
 
168
- ---
62
+ - Default error policy is mitt-like: the first throwing handler aborts dispatch. Use `captureHandlerErrors` or per-handler `captureErrors` to swallow/report and continue.
63
+ - Wildcard handlers receive `(type, payload)`, not just payload.
64
+ - Use `on("*", handler, { once: true })` for wildcard-once. `once("*")` is intentionally not part of the typed public overload.
65
+ - `throttleMs` uses `Date.now()`. If the system clock moves backward, a throttled handler can be muted until wall time catches up.
66
+ - `sampleRate` is wildcard-only and uses `Math.random()` per dispatch.
67
+ - `dispose()` is permanent; post-dispose APIs throw `EmitterDisposedError` except cleanup calls that are no-ops by design.
169
68
 
170
- ## Roadmap
69
+ ## AI Context
171
70
 
172
- | Version | Adds |
173
- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------- |
174
- | **0.0.1** | Scaffold landed — frozen API surface as a `throw` stub; full config + CI walk clean. |
175
- | **0.1.0** | First npm release. `on` / `once` / `off` / `emit` / `clear` / `dispose` implemented; coverage ≥ 95/90/100/100; ≤ 800 B gzip (strict-TS overhead lands at ~747 B). |
176
- | **0.3.0** | `captureHandlerErrors` + wildcard sampling/throttling. The v0.2 number was skipped to align with the four-package v0.3 release cohort. |
177
- | **0.4.0** | Dependency hygiene + stability freeze: removed the unused `tsx` devDependency, aligned `fast-check` to `^4.8.0`, and froze the 0.3.x public surface for the 1.x line. No runtime API change; bundle byte-identical to 0.3.1. |
178
- | **0.6+** | Async handler tracking (draft) — see [STABILITY.md](STABILITY.md). |
179
-
180
- ---
71
+ - Short index: [`llms.txt`](llms.txt)
72
+ - Full generated context: [`llms-full.txt`](llms-full.txt)
73
+ - Stability contract: [`STABILITY.md`](STABILITY.md)
74
+ - Current review backlog: [`REVIEW.md`](REVIEW.md)
75
+ - Release history: [`CHANGELOG.md`](CHANGELOG.md)
181
76
 
182
77
  ## License
183
78
 
184
- [MIT](LICENSE).
79
+ MIT
185
80
 
186
81
  ---
187
82
 
@@ -189,158 +84,58 @@ Full JSDoc lives in [`src/index.ts`](src/index.ts).
189
84
 
190
85
  # Changelog
191
86
 
192
- All notable changes to this project are documented here. The format follows
193
- [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project
194
- adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
87
+ All notable changes to aieventjs are summarized here.
195
88
 
196
89
  ## [Unreleased]
197
90
 
198
- ## [0.5.5] - 2026-06-08
91
+ ## [0.5.8] - 2026-06-14
92
+
93
+ - Fixed: per-handler `throttleMs` now uses the monotonic `performance.now()` clock instead of `Date.now()`, so a wall-clock regression can no longer silently mute throttled handlers.
94
+ - Changed: `once("*")` is now a compile-time type error; use `on("*", handler, { once: true })` for wildcard-once semantics.
95
+ - Documentation-only slimming pass across README, stability notes, review backlog, and LLM context.
96
+
97
+ ## [0.5.6] - 2026-06-10
98
+
99
+ - Hardened wildcard/once documentation and wall-clock throttle caveats.
100
+ - Kept typed dispatch, wildcard dispatch, and AbortSignal behavior stable.
101
+ - Regenerated generated LLM context from canonical docs.
102
+
103
+ ## Older releases
199
104
 
200
- ### Changed
105
+ - `0.5.5` through `0.5.1` focused on release hygiene, docs accuracy, and regression tests for wildcard/once/error behavior.
106
+ - `0.4.0` declared the stable ai*js surface.
107
+ - `0.3.x` added sampling, throttle, capture-error options, and public stability docs.
108
+ - `0.1.x` introduced `createEmitter`, typed `on/once/off/emit`, wildcard handlers, abort cleanup, and dispose semantics.
201
109
 
202
- - 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.
110
+ ---
203
111
 
204
- ## [0.5.3] - 2026-06-05
112
+ <!-- ===== STABILITY.md ===== -->
205
113
 
206
- ### Added
207
- - `throttleMs` is now accepted on typed `on()` (non-breaking; per-handler leading-edge throttle, e.g. a per-frame `credits/change` HUD event). Previously it threw `EmitterError` (wildcard-only); `sampleRate` stays wildcard-only.
114
+ # aieventjs Stability Index
208
115
 
209
- ### Docs
210
- - Document that event maps must be declared with `type`, not `interface` (an `interface` lacks an index signature and fails the `Record<string, unknown>` constraint with TS2344); `createEmitter` JSDoc + README + `README_ZHTW.md` + regenerated `llms-full.txt`.
116
+ ## Stable API
211
117
 
212
- ## [0.5.2] - 2026-06-05
118
+ | Surface | Status | Notes |
119
+ | --- | --- | --- |
120
+ | `createEmitter(options?)` | Stable | Generic typed event map. |
121
+ | `Emitter.on` | Stable | Typed and wildcard overloads; returns unsubscribe. |
122
+ | `Emitter.once` | Stable | Typed events only; wildcard once uses `on("*", ..., { once: true })`. |
123
+ | `Emitter.off`, `clear`, `dispose` | Stable | Cleanup methods; dispose is permanent and idempotent. |
124
+ | `Emitter.emit` | Stable | Synchronous snapshot dispatch. |
125
+ | Error classes | Stable | `EmitterError`, `EmitterDisposedError`. |
213
126
 
214
- ### Docs
127
+ ## Behavior
215
128
 
216
- - Review-driven documentation fixes (`README.md`, `README_ZHTW.md`, `llms-full.txt`; plus repo-only `CONTRIBUTING.md`): clarity and accuracy from a cross-package code review. No runtime or API change; `dist` byte-identical to 0.5.1.
129
+ - Type-matched handlers run before wildcard handlers.
130
+ - Handler lists are snapshotted before dispatch.
131
+ - Default handler errors propagate; capture options can swallow/report.
132
+ - `AbortSignal` removes subscriptions and pre-aborted signals do not register.
133
+ - `throttleMs` uses `Date.now()` and is therefore wall-clock based.
217
134
 
218
- ## [0.5.1] - 2026-06-02
135
+ ## Drafts
219
136
 
220
- ### Fixed
221
- - `NaN` sampleRate and `NaN` throttleMs now throw `EmitterError` at `on()` time.
222
- Previously, `NaN <= 0` and `NaN > 1` both evaluate to `false`, so a `NaN`
223
- sampleRate silently passed validation and degraded to always-fire semantics;
224
- similarly `NaN < 0` is `false`, so a `NaN` throttleMs passed and degraded to
225
- no-throttle. Both guards now include `Number.isFinite()` as the first check,
226
- consistent with the documented contract that invalid values throw `EmitterError`.
227
-
228
- ### Tests
229
- - Added regression tests locking in `NaN` rejection for both `sampleRate` and
230
- `throttleMs` (wildcard-throttle.test.ts §A6, §B4).
231
- - Added regression tests confirming that a `once` handler that throws is still
232
- removed and does not re-fire on the next emit — for both typed and wildcard
233
- once subscriptions (emitter.test.ts §C2a, §C2b).
234
- - Added regression tests for post-dispose guard coverage: `off()` after
235
- `dispose()` throws `EmitterDisposedError`; `clear()` after `dispose()` throws
236
- `EmitterDisposedError`; the unsubscribe function returned by `on()` is a safe
237
- no-op (does not throw) after `dispose()` (emitter.test.ts §H2a, §H2b, §H2c).
238
-
239
- ## [0.4.0] - 2026-05-29
240
-
241
- Dependency hygiene + stability freeze. No runtime API addition; `dist/` is byte-identical to 0.3.1 (no `src/` change). Consumer-facing behaviour is unchanged.
242
-
243
- ### Changed
244
- - Aligned the `fast-check` devDependency to `^4.8.0` (was `^3.23.0`), matching the newer ai*js family cohort. Test-only; never bundled; zero consumer impact.
245
- - Declared the entire 0.3.x public API surface frozen for the 1.x line — see STABILITY.md. No signatures, error names, or default behaviours changed.
246
-
247
- ### Removed
248
- - `tsx` devDependency — unused (no script, config, test, or CI step referenced it). Trims the lockfile subtree; no functional impact.
249
-
250
- ## [0.3.1] - 2026-05-29
251
- ### Added
252
- - `test/emitter.prop.test.ts`: three fast-check property invariants — dispatch
253
- order (typed before wildcard, registration order preserved), snapshot stability
254
- under mid-dispatch `off()`, live-set accuracy after upfront unsubscribe.
255
- `numRuns: 100`; inline `fc.assert` style matching family convention.
256
- - `test/wildcard-throttle.test.ts` §F: five edge-case tests — pre-aborted signal
257
- + valid `sampleRate` never registers; `on()` guard-before-signal ordering;
258
- `captureErrors` + signal leak check (`removeEventListener` called on abort);
259
- throttled wildcard + mid-dispatch abort (snapshot-in-flight completes);
260
- `sampleRate` exact boundary (`Math.random() === sampleRate` is a miss).
261
- - `test/capture-errors.test.ts` §F: dispose-during-capturing-dispatch —
262
- snapshot completes; `EmitterDisposedError` from re-entrant `emit()` is routed
263
- through the capture callback; sibling handler still runs.
264
- - `fast-check ^3.23.0` devDependency (family convention: aifsmjs `^3.20.0`,
265
- aiquadtreejs `^3.23.0`).
266
-
267
- ### Changed
268
- - No `src/index.ts` behaviour change. `dist/` is byte-identical to v0.3.0.
269
- gzip 1050 B / 1100 B unchanged.
270
-
271
- ## [0.3.0] - 2026-05-29
272
- ### Added
273
- - `EmitterOptions.captureHandlerErrors`: opt-in emitter-level error policy. Accepts `true` (swallow) or `(err, type, payload) => void` callback. Default behaviour (first throw aborts dispatch) is unchanged.
274
- - `OnOptions.captureErrors`: per-handler override of the emitter-level policy. `false` forces re-throw even when emitter-level swallows. Setting it on a wildcard `"*"` subscription throws `EmitterError`.
275
- - `OnOptions.sampleRate` (wildcard only): probability in `(0, 1]` that a dispatch reaches the handler; uses `Math.random()`. Out-of-range values throw `EmitterError` at `on()` time.
276
- - `OnOptions.throttleMs` (wildcard only): minimum ms between successive calls, leading-edge; uses `Date.now()`. Negative values throw `EmitterError` at `on()` time.
277
- - `STABILITY.md`: stability index for all public API surface, plus `[experimental]` placeholder for async handler tracking (targeted v0.6+).
278
-
279
- ### Changed
280
- - Internal entry shape gains optional `ce` / `r` / `tm` / `ts` fields to carry per-handler error policy and wildcard throttle/sample state. Snapshot-before-iterate semantics in `emit()` are unchanged.
281
- - `scripts/check-size.mjs` budget raised from 800 B to 1100 B. Actual v0.3.0 gzip lands at ~1050 B; the spec estimate of 900 B was optimistic (~100 B per feature accounting for guard message strings and try/catch frames).
282
-
283
- ### Notes
284
- - v0.2 was skipped; v0.3.0 directly supersedes the v0.2 roadmap entry for `captureHandlerErrors`. Existing v0.1 callers that did not pass the option see no behavioural change.
285
-
286
- ## [0.1.1] - 2026-05-28
287
-
288
- ### Changed (CI)
289
-
290
- - **`publish.yml` now triggers on `push: tags: ["v*"]`** (was `workflow_dispatch` only). Aligns with the trigger used by `aifsmjs` / `aiecsjs` / `aibridgejs`. Tag push now automatically runs the OIDC trusted publish.
291
- - **`npm publish --provenance --access public`** — the workflow now emits a [sigstore provenance attestation](https://docs.npmjs.com/generating-provenance-statements) so consumers can verify the tarball was built by this workflow on this commit.
292
-
293
- No runtime / source / API changes. This is a CI-only patch to validate the GitHub Actions OIDC trusted-publisher pipeline now that the npm trusted publisher entry is configured. Production bundles are byte-identical to 0.1.0.
294
-
295
- ## [0.1.0] - 2026-05-28
296
-
297
- ### Added
298
-
299
- - Strict typed `createEmitter<Events>()` with `on` returning an unsubscribe
300
- function, `once`, wildcard `*` handler, `off`, `emit`, `clear`, `dispose`.
301
- - `on(type, handler, { signal?, once? })` — `AbortSignal` integration so
302
- framework code (Svelte 5 `$effect`, Vue `onScopeDispose`) cleans up listeners
303
- via the standard cancellation primitive.
304
- - `dispose()` idempotent; post-dispose `on` / `emit` / `once` throw
305
- `EmitterDisposedError`.
306
- - Handler-array snapshot on `emit` so removing a handler during dispatch does
307
- not skip its successor (mitt has had this property since 2.x — preserved).
308
- - Functional — methods are destructurable (`const { on, emit } = bus`).
309
- - Test coverage ≥95% statements / lines / functions / ≥90% branches.
310
- - Size budget: ≤ 550 B gzip.
311
- - Dual ESM + CJS via `tsup`; `sideEffects: false`; zero runtime dependencies.
312
-
313
- ## [0.0.1] - 2026-05-28
314
-
315
- ### Added (scaffold)
316
-
317
- - Full package scaffold landed (`package.json`, `tsconfig.json`,
318
- `tsconfig.test.json`, `tsup.config.ts`, `vitest.config.ts`, `biome.json`,
319
- `scripts/{verify-exports,check-size,build-llms-full}.mjs`,
320
- `test/scaffold.test.ts`, `examples/.gitkeep`, `.github/workflows/{ci,publish}.yml`,
321
- `README.md`, `README_ZHTW.md`, `CHANGELOG.md`, `CONTRIBUTING.md`,
322
- `LICENSE`, `llms.txt`, `llms-full.txt`).
323
- - `src/index.ts` is a `throw` stub exposing the frozen 0.1.0 API surface
324
- (`createEmitter`, `Emitter<Events>`, wildcard `*` handler signature, `once`,
325
- `AbortSignal`-aware `on`, `dispose`, `EmitterError`, `EmitterDisposedError`).
326
- - `pnpm typecheck && pnpm lint && pnpm coverage && pnpm build &&
327
- pnpm verify:exports && pnpm verify:llms && pnpm check:size` walks clean
328
- against a single placeholder test.
329
- - Coverage thresholds temporarily set to `0/0/0/0`; tightened to
330
- `95/90/100/100` in 0.1.0.
331
- - Size budget temporarily set to 3 KB gzip; tightened to the 550 B README
332
- target in 0.1.0.
333
- - Publish workflow exists but trigger is `workflow_dispatch` only — no
334
- accidental npm release until 0.1.0.
335
-
336
- ### Decision log (carried over from LEARNINGS.md v0.3.0 cycle 預備區)
337
-
338
- - **Not a `mitt` fork.** `mitt@3.0.1` is MIT-fork-friendly but is ~35 lines of
339
- pure logic and has been unmaintained since 2023-07. Forking is equivalent
340
- to rewriting, and the upstream copyright notice would carry no benefit.
341
- Cleaner to write from scratch with the ai\*js conventions baked in.
342
- - **Wildcard `*` is kept.** It is `mitt`'s signature feature; keeping it
343
- preserves migration ergonomics for existing `mitt` users at ~80 B gzip cost.
137
+ - Async handler tracking is not implemented.
138
+ - A future minor may switch throttle timing to monotonic time or add a runtime guard around wildcard `once()`.
344
139
 
345
140
  ---
346
141
 
@@ -348,75 +143,31 @@ No runtime / source / API changes. This is a CI-only patch to validate the GitHu
348
143
 
349
144
  # Contributing to aieventjs
350
145
 
351
- Thanks for taking the time to look. aieventjs is a deliberately small library
352
- (target ≤ 1100 B gzip); contributions that keep the surface narrow are easier
353
- to accept than ones that expand it.
146
+ Keep the emitter small, synchronous, and predictable.
354
147
 
355
- ## Quick start
148
+ ## Local workflow
356
149
 
357
150
  ```bash
358
151
  pnpm install
359
- pnpm test # vitest
360
- pnpm coverage # vitest with v0.1.0 thresholds (95/90/100/100)
361
- pnpm typecheck # tsc --noEmit on strict mode
362
- pnpm lint # biome check
363
- pnpm build # tsup; dual ESM/CJS + .d.ts
364
- pnpm verify:exports # ensures package.json#exports matches dist/
365
- pnpm verify:llms # ensures llms-full.txt is in sync with README + CHANGELOG
366
- pnpm check:size # gzip per subpath against the size budget
152
+ pnpm typecheck
153
+ pnpm test
154
+ pnpm verify:docs
155
+ pnpm build:llms
156
+ pnpm verify:llms
157
+ pnpm check:size
367
158
  ```
368
159
 
369
- ## What gets in easily
370
-
371
- - Bug fixes with a failing test added first
372
- - README / typing corrections
373
- - Tests that lock down existing behaviour (especially the re-entrancy
374
- invariant on `emit`)
375
- - Performance work that keeps `on()` / `emit()` O(1) on the dispatch path
376
-
377
- ## What needs discussion first
378
-
379
- - Anything that changes the public surface (`createEmitter`, `Emitter<Events>`,
380
- `OnOptions`, error classes)
381
- - Namespaced wildcards (`user.*`) — explicit non-goal; bring `eventemitter2`
382
- if you need that
383
- - Async / promise-returning handlers — explicit non-goal (handlers are
384
- synchronous; resolve promises in user-land)
385
- - Anything that pushes the core gzip past 1100 B
386
-
387
- ## Design principles
388
-
389
- aieventjs follows the ai*js library-core priority order:
390
-
391
- > Security > Correctness > Simplicity > YAGNI > Performance
392
-
393
- Key invariants:
394
-
395
- - `on()` returns a callable unsubscribe; calling it (or aborting
396
- `opts.signal`) removes the handler in O(1).
397
- - `emit()` snapshots the handler array before iterating — handlers added
398
- during dispatch do NOT fire this round; handlers removed during dispatch
399
- do NOT skip their successor.
400
- - Wildcard `*` handlers fire AFTER type-matched handlers.
401
- - `dispose()` is idempotent.
402
- - All methods are destructurable: `const { on, emit } = bus` works.
403
-
404
- ## Commit & PR style
405
-
406
- - Commit messages: imperative subject under 70 chars; body explains *why*.
407
- - PRs: keep scope to one topic. Link the issue if any.
408
- - Tests required for any behaviour change.
160
+ Run `pnpm lint` before PRs. If docs change, regenerate `llms-full.txt`.
409
161
 
410
- ## Reporting issues
162
+ ## Rules
411
163
 
412
- - Minimal reproduction welcome (paste the smallest `createEmitter` + on /
413
- emit sequence that shows the bug).
414
- - For security issues, please email the maintainer rather than filing
415
- publicly.
164
+ - Preserve snapshot-before-iterate dispatch semantics.
165
+ - Keep wildcard ordering after typed handlers.
166
+ - Add tests for `AbortSignal`, `once`, wildcard, throttle, sample, and error policy changes.
167
+ - Do not add async queueing to the stable emitter without a separate design note.
416
168
 
417
169
  ## License
418
170
 
419
- By contributing, you agree your changes will be licensed under the MIT
420
- license that covers this project.
171
+ MIT
421
172
 
422
173
  ---