solid-tag-runtime 0.0.7 → 0.0.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/ARCHITECTURE.md CHANGED
@@ -101,6 +101,7 @@ const runtime = createRuntime(options);
101
101
 
102
102
  ```ts
103
103
  runtime.define(id, source, options?);
104
+ runtime.defineMany(definitions);
104
105
  runtime.update(id, source, options?);
105
106
  ```
106
107
 
@@ -138,9 +139,18 @@ runtime.getModuleInfo(id);
138
139
 
139
140
  ```ts
140
141
  runtime.invalidate(id);
142
+ runtime.remove(id, options?);
143
+ runtime.clear(options?);
144
+
145
+ runtime.subscribe(listener);
146
+ runtime.subscribe(type, listener);
147
+ runtime.subscribe(types, listener);
148
+
141
149
  runtime.dispose();
142
150
  ```
143
151
 
152
+ Lifecycle subscriptions are synchronous observational notifications. They do not intercept or modify runtime behavior. Resolution behavior remains customizable through `resolve()`, compilation through the compiler adapter, and executable-module creation through `ModuleUrlBackend`.
153
+
144
154
  ## 6. Module kinds
145
155
 
146
156
  The runtime currently has three module record kinds.
@@ -232,6 +242,10 @@ await html.observe({ registerExisting: false });
232
242
  The controller exposes:
233
243
 
234
244
  ```ts
245
+ html.subscribe(listener);
246
+ html.subscribe(type, listener);
247
+ html.subscribe(types, listener);
248
+
235
249
  html.register(options?);
236
250
  html.observe(options?);
237
251
  html.flush(options?);
@@ -1031,3 +1045,102 @@ Invariants:
1031
1045
  8. `removeElement()` is the canonical operation for releasing shared WeakMap ownership.
1032
1046
 
1033
1047
  Regression coverage: package tests cover batch definition and duplicate rejection, dependency removal/invalidation, clear-with-host-preservation, lifecycle event delivery/unsubscribe, owned element updates, identity-change rejection, ownership release, DOM removal, and preserving a runtime module while releasing its HTML element.
1048
+
1049
+ ### 0.0.8 — selective lifecycle subscriptions and HTML observability
1050
+
1051
+ Decision: expand lifecycle observation into typed, independently removable subscriptions on both the core runtime and the HTML adapter. Keep lifecycle subscriptions observational and separate from behavioral hooks/interceptors.
1052
+
1053
+ Core subscription forms:
1054
+
1055
+ ```ts
1056
+ runtime.subscribe(listener);
1057
+ runtime.subscribe("module-error", listener);
1058
+ runtime.subscribe(["module-evaluating", "module-evaluated"], listener);
1059
+ ```
1060
+
1061
+ HTML controllers expose the same shape:
1062
+
1063
+ ```ts
1064
+ html.subscribe(listener);
1065
+ html.subscribe("element-registered", listener);
1066
+ html.subscribe(["observer-batch", "html-error"], listener);
1067
+ ```
1068
+
1069
+ Each call creates an independent subscription and returns its own disposer. Disposing one subscription never affects another registration of the same callback or subscriptions to other event groups. Subscriber exceptions are reported but cannot alter runtime/HTML operations.
1070
+
1071
+ Core event additions:
1072
+
1073
+ ```text
1074
+ module-updated
1075
+ modules-defined
1076
+ module-resolving
1077
+ module-resolved
1078
+ module-linking
1079
+ module-linked
1080
+ ```
1081
+
1082
+ The evaluation pipeline is now observed in semantic order:
1083
+
1084
+ ```text
1085
+ module-linking
1086
+ ↓
1087
+ module-resolving / module-resolved
1088
+ ↓
1089
+ module-linked
1090
+ ↓
1091
+ module-compiled
1092
+ ↓
1093
+ module-evaluating
1094
+ ↓
1095
+ module-evaluated
1096
+ ```
1097
+
1098
+ `module-resolved` exposes the logical resolution result and resolution kind, while `module-linked` exposes source specifier → resolved module-ID dependencies without exposing Blob/data URL implementation details. `modules-defined` marks the completion of a `defineMany()` batch after buffered per-module events become observable. `module-updated` supplements the backward-compatible `module-defined { operation: "update" }` event with an explicit semantic update notification.
1099
+
1100
+ `module-error` remains one event with a structured pipeline phase instead of creating separate error event types. Supported phases are `resolve`, `analyze`, `compile`, `link`, `url-create`, `evaluate`, and `host-bridge`.
1101
+
1102
+ HTML controller event additions:
1103
+
1104
+ ```text
1105
+ element-discovered
1106
+ element-claimed
1107
+ element-loading
1108
+ element-loaded
1109
+ element-registered
1110
+ element-updating
1111
+ element-updated
1112
+ element-removing
1113
+ element-released
1114
+ element-removed
1115
+ observer-connected
1116
+ observer-disconnected
1117
+ observer-batch
1118
+ root-changing
1119
+ root-changed
1120
+ append-target-changed
1121
+ entry-executing
1122
+ entry-executed
1123
+ html-error
1124
+ ```
1125
+
1126
+ HTML ownership events include an origin describing the ingestion path (`initial-scan`, `observer`, `register-element`, `append`, `add-module`, etc.). The observer emits summarized `observer-batch` events instead of exposing raw `MutationRecord` values. Root/append-target events belong only to the HTML controller and are deliberately not mixed into core runtime events.
1127
+
1128
+ Architectural boundary:
1129
+
1130
+ ```text
1131
+ Observation
1132
+ runtime.subscribe()
1133
+ html.subscribe()
1134
+
1135
+ Behavior customization
1136
+ resolve()
1137
+ compiler adapter
1138
+ ModuleUrlBackend
1139
+ ```
1140
+
1141
+ Lifecycle listeners are not interceptors: they cannot cancel evaluation, rewrite source, change resolution, or alter cache behavior. If source/evaluation interception is needed later, it must be designed as a separate hook contract with explicit ordering, async, cancellation, and caching semantics.
1142
+
1143
+ Implementation note: core and HTML event streams share a small internal dispatcher so all/selective subscription behavior, independent disposers, and listener-error isolation have identical semantics while keeping separate event unions.
1144
+
1145
+ Regression coverage: package tests cover catch-all, single-type, and multi-type subscriptions; independent unsubscribe; listener exception isolation; update/batch events; resolution/link/evaluation ordering and payloads; structured resolution errors; HTML ownership origins; observer connect/disconnect/batch events; root and append-target events; external source loading; entry execution; update/removal events; HTML error reporting; and HTML subscriber exception isolation.
1146
+
package/README.md CHANGED
@@ -107,8 +107,8 @@ Browser import maps must map the subpath explicitly as well as the package root:
107
107
  ```json
108
108
  {
109
109
  "imports": {
110
- "solid-tag-runtime": "https://esm.sh/solid-tag-runtime@0.0.7",
111
- "solid-tag-runtime/html": "https://esm.sh/solid-tag-runtime@0.0.7/html"
110
+ "solid-tag-runtime": "https://esm.sh/solid-tag-runtime@0.0.8",
111
+ "solid-tag-runtime/html": "https://esm.sh/solid-tag-runtime@0.0.8/html"
112
112
  }
113
113
  }
114
114
  ```
@@ -572,28 +572,131 @@ removes everything. The returned array contains the IDs that were removed.
572
572
 
573
573
  ### Lifecycle events
574
574
 
575
+ `0.0.8` expands lifecycle subscriptions into a typed event stream with independent join/leave semantics.
576
+
577
+ Subscribe to everything:
578
+
575
579
  ```ts
576
580
  const unsubscribe = runtime.subscribe(event => {
577
581
  console.log(event.type, event);
578
582
  });
583
+ ```
584
+
585
+ Subscribe to one event type:
586
+
587
+ ```ts
588
+ const leaveErrors = runtime.subscribe(
589
+ "module-error",
590
+ event => {
591
+ console.error(event.phase, event.error);
592
+ },
593
+ );
594
+ ```
595
+
596
+ Subscribe to several event types:
597
+
598
+ ```ts
599
+ const leaveEvaluation = runtime.subscribe(
600
+ ["module-evaluating", "module-evaluated"],
601
+ event => {
602
+ console.log(event.type, event.id);
603
+ },
604
+ );
605
+ ```
606
+
607
+ Each call owns an independent subscription:
608
+
609
+ ```ts
610
+ leaveEvaluation();
611
+ // the error subscription remains active
612
+ ```
613
+
614
+ Core events include:
615
+
616
+ ```text
617
+ module-defined
618
+ module-updated
619
+ modules-defined
620
+
621
+ module-resolving
622
+ module-resolved
623
+
624
+ module-linking
625
+ module-linked
626
+
627
+ module-invalidated
628
+ module-compiled
629
+ module-evaluating
630
+ module-evaluated
631
+
632
+ module-removed
633
+ module-error
634
+
635
+ runtime-cleared
636
+ runtime-disposed
637
+ ```
638
+
639
+ `module-resolved` reports how a specifier was resolved (`relative`, `registered`, `custom`, `native`, and so on). `module-linked` exposes the logical dependency mapping used to link a source module. `modules-defined` marks the completion of a `defineMany()` batch after all definitions have been installed.
640
+
641
+ `module-error.phase` is one of the runtime pipeline phases such as `resolve`, `analyze`, `compile`, `link`, `url-create`, `evaluate`, or `host-bridge`.
642
+
643
+ Subscribers are observational only: they cannot cancel or modify runtime operations, and subscriber exceptions are isolated from runtime execution. Behavioral extension remains separate through the resolver, compiler adapter, and module URL backend.
644
+
645
+ ### HTML lifecycle events
646
+
647
+ The HTML controller has its own event stream because DOM ownership/observation is intentionally separate from the core module engine:
648
+
649
+ ```ts
650
+ const leaveHTML = html.subscribe(event => {
651
+ console.log(event.type, event);
652
+ });
653
+ ```
654
+
655
+ The same selective forms are supported:
656
+
657
+ ```ts
658
+ const leaveOwnership = html.subscribe(
659
+ ["element-claimed", "element-registered", "element-removed"],
660
+ event => {
661
+ console.log(event.type, event.moduleId);
662
+ },
663
+ );
664
+ ```
665
+
666
+ HTML events include:
667
+
668
+ ```text
669
+ element-discovered
670
+ element-claimed
671
+ element-loading
672
+ element-loaded
673
+ element-registered
674
+
675
+ element-updating
676
+ element-updated
677
+ element-removing
678
+ element-released
679
+ element-removed
680
+
681
+ observer-connected
682
+ observer-disconnected
683
+ observer-batch
684
+
685
+ root-changing
686
+ root-changed
687
+ append-target-changed
688
+
689
+ entry-executing
690
+ entry-executed
579
691
 
580
- // later
581
- unsubscribe();
692
+ html-error
582
693
  ```
583
694
 
584
- Events currently include:
695
+ Ownership-related events include an `origin` describing how the element entered the controller (`observer`, `append`, `register-element`, `add-module`, and related internal scan origins). This is useful for tracing observer/manual-registration races and proving that an explicit `append()` was not processed a second time by the observer.
585
696
 
586
- - `module-defined`
587
- - `module-invalidated`
588
- - `module-compiled`
589
- - `module-evaluating`
590
- - `module-evaluated`
591
- - `module-removed`
592
- - `module-error`
593
- - `runtime-cleared`
594
- - `runtime-disposed`
697
+ `observer-batch` summarizes a DOM discovery pass instead of exposing noisy raw `MutationRecord` objects. `element-loading` / `element-loaded` are emitted for `src`-backed modules, and entry events distinguish evaluation caused by an HTML `entry` declaration from an ordinary `runtime.import()`.
595
698
 
596
- Subscriber exceptions are isolated from runtime execution. This makes `subscribe()` suitable for tracing, developer tools, and editor diagnostics.
699
+ Like core runtime subscribers, HTML subscribers are observational and exception-isolated.
597
700
 
598
701
  ## HTML-owned module updates and removal
599
702
 
package/html.d.ts CHANGED
@@ -107,6 +107,45 @@ export interface RegisterHTMLResult {
107
107
  }>;
108
108
  }
109
109
 
110
+ export type HTMLRegistrationOrigin =
111
+ | "initial-scan"
112
+ | "observer"
113
+ | "observer-initial"
114
+ | "observer-root-change"
115
+ | "root-change-scan"
116
+ | "define-element"
117
+ | "register-element"
118
+ | "append"
119
+ | "add-module"
120
+ | "update-element"
121
+ | "remove-element"
122
+ | string;
123
+
124
+ export type HTMLRuntimeEvent =
125
+ | { htmlRuntimeId: string; scope?: string; timestamp: number; type: "element-discovered"; element: HTMLModuleScriptElement; moduleId?: string; origin: HTMLRegistrationOrigin; accepted: boolean }
126
+ | { htmlRuntimeId: string; scope?: string; timestamp: number; type: "element-claimed"; element: HTMLModuleScriptElement; moduleId: string; format: ModuleFormat; entry: boolean; sourceUrl?: string; origin: HTMLRegistrationOrigin }
127
+ | { htmlRuntimeId: string; scope?: string; timestamp: number; type: "element-loading"; element: HTMLModuleScriptElement; moduleId: string; sourceUrl: string; origin: HTMLRegistrationOrigin }
128
+ | { htmlRuntimeId: string; scope?: string; timestamp: number; type: "element-loaded"; element: HTMLModuleScriptElement; moduleId: string; sourceUrl: string; origin: HTMLRegistrationOrigin; bytes: number; durationMs: number }
129
+ | { htmlRuntimeId: string; scope?: string; timestamp: number; type: "element-registered"; element: HTMLModuleScriptElement; moduleId: string; definition: HTMLModuleDefinition; origin: HTMLRegistrationOrigin }
130
+ | { htmlRuntimeId: string; scope?: string; timestamp: number; type: "element-updating"; element: HTMLModuleScriptElement; moduleId: string; origin: HTMLRegistrationOrigin }
131
+ | { htmlRuntimeId: string; scope?: string; timestamp: number; type: "element-updated"; element: HTMLModuleScriptElement; moduleId: string; definition: HTMLModuleDefinition; origin: HTMLRegistrationOrigin }
132
+ | { htmlRuntimeId: string; scope?: string; timestamp: number; type: "element-removing"; element: HTMLModuleScriptElement; moduleId: string; origin: HTMLRegistrationOrigin; removeModule: boolean; removeFromDOM: boolean }
133
+ | { htmlRuntimeId: string; scope?: string; timestamp: number; type: "element-released"; element: HTMLModuleScriptElement; moduleId: string; origin: HTMLRegistrationOrigin }
134
+ | { htmlRuntimeId: string; scope?: string; timestamp: number; type: "element-removed"; element: HTMLModuleScriptElement; moduleId: string; origin: HTMLRegistrationOrigin; moduleRemoved: boolean; elementRemoved: boolean }
135
+ | { htmlRuntimeId: string; scope?: string; timestamp: number; type: "observer-connected"; root: HTMLModuleRoot; subtree: boolean; registerExisting: boolean; reason: string }
136
+ | { htmlRuntimeId: string; scope?: string; timestamp: number; type: "observer-disconnected"; root?: HTMLModuleRoot; reason: string }
137
+ | { htmlRuntimeId: string; scope?: string; timestamp: number; type: "observer-batch"; root: HTMLModuleRoot; origin: HTMLRegistrationOrigin; discovered: number; matched: number; claimed: number; ignored: number; modules: string[] }
138
+ | { htmlRuntimeId: string; scope?: string; timestamp: number; type: "root-changing"; previousRoot?: HTMLModuleRoot; root: HTMLModuleRoot; connected: boolean; registerExisting: boolean }
139
+ | { htmlRuntimeId: string; scope?: string; timestamp: number; type: "root-changed"; previousRoot?: HTMLModuleRoot; root: HTMLModuleRoot; connected: boolean; registerExisting: boolean }
140
+ | { htmlRuntimeId: string; scope?: string; timestamp: number; type: "append-target-changed"; previousTarget?: HTMLAppendTarget; target: HTMLAppendTarget }
141
+ | { htmlRuntimeId: string; scope?: string; timestamp: number; type: "entry-executing"; element: HTMLModuleScriptElement; moduleId: string; origin: HTMLRegistrationOrigin }
142
+ | { htmlRuntimeId: string; scope?: string; timestamp: number; type: "entry-executed"; element: HTMLModuleScriptElement; moduleId: string; namespace: ModuleNamespaceLike; origin: HTMLRegistrationOrigin }
143
+ | { htmlRuntimeId: string; scope?: string; timestamp: number; type: "html-error"; operation: "discover" | "claim" | "fetch" | "register" | "update" | "remove" | "entry" | "root-change" | "observer" | string; error: unknown; element?: HTMLModuleScriptElement; moduleId?: string; origin?: HTMLRegistrationOrigin };
144
+
145
+ export type HTMLRuntimeEventType = HTMLRuntimeEvent["type"];
146
+ export type HTMLRuntimeEventOfType<T extends HTMLRuntimeEventType> =
147
+ Extract<HTMLRuntimeEvent, { type: T }>;
148
+
110
149
  export interface ObserveHTMLOptions extends RegisterHTMLOptions {
111
150
  /** Register scripts already present when observation starts. Default: true. */
112
151
  registerExisting?: boolean;
@@ -158,6 +197,16 @@ export interface HTMLRuntimeController {
158
197
  /** Result of the observer's initial scan. */
159
198
  readonly initial: RegisterHTMLResult;
160
199
 
200
+ subscribe(listener: (event: HTMLRuntimeEvent) => void): () => boolean;
201
+ subscribe<T extends HTMLRuntimeEventType>(
202
+ type: T,
203
+ listener: (event: HTMLRuntimeEventOfType<T>) => void,
204
+ ): () => boolean;
205
+ subscribe<const T extends readonly HTMLRuntimeEventType[]>(
206
+ types: T,
207
+ listener: (event: HTMLRuntimeEventOfType<T[number]>) => void,
208
+ ): () => boolean;
209
+
161
210
  /** One-shot scan of matching scripts under the configured/root override. */
162
211
  register(options?: RegisterHTMLOptions): Promise<RegisterHTMLResult>;
163
212
 
package/index.d.ts CHANGED
@@ -91,17 +91,50 @@ export interface ClearRuntimeOptions {
91
91
  preserveHostModules?: boolean;
92
92
  }
93
93
 
94
+ export type ModuleResolutionKind =
95
+ | "custom"
96
+ | "registered"
97
+ | "url"
98
+ | "absolute"
99
+ | "relative"
100
+ | "native";
101
+
102
+ export type ModuleErrorPhase =
103
+ | "resolve"
104
+ | "analyze"
105
+ | "compile"
106
+ | "link"
107
+ | "url-create"
108
+ | "evaluate"
109
+ | "host-bridge";
110
+
111
+ export interface LinkedDependency {
112
+ specifier: string;
113
+ resolvedId: string;
114
+ }
115
+
94
116
  export type RuntimeLifecycleEvent =
95
117
  | { runtimeId: string; timestamp: number; type: "module-defined"; id: string; kind: ModuleInfo["kind"]; version: number; operation: "define" | "defineMany" | "update" | "defineModule" | "defineUrl"; replaced: boolean; module: ModuleInfo }
118
+ | { runtimeId: string; timestamp: number; type: "module-updated"; id: string; kind: ModuleInfo["kind"]; previousVersion: number; version: number; format?: ModuleFormat; module: ModuleInfo }
119
+ | { runtimeId: string; timestamp: number; type: "modules-defined"; ids: string[]; modules: ModuleInfo[] }
120
+ | { runtimeId: string; timestamp: number; type: "module-resolving"; specifier: string; importer: string | null }
121
+ | { runtimeId: string; timestamp: number; type: "module-resolved"; specifier: string; importer: string | null; resolvedId: string; resolutionKind: ModuleResolutionKind }
122
+ | { runtimeId: string; timestamp: number; type: "module-linking"; id: string; kind: ModuleInfo["kind"]; version: number }
123
+ | { runtimeId: string; timestamp: number; type: "module-linked"; id: string; kind: ModuleInfo["kind"]; version: number; dependencies: LinkedDependency[]; module: ModuleInfo }
96
124
  | { runtimeId: string; timestamp: number; type: "module-invalidated"; id: string; kind: ModuleInfo["kind"]; version: number; reason: string }
97
125
  | { runtimeId: string; timestamp: number; type: "module-compiled"; id: string; kind: ModuleInfo["kind"]; version: number; module: ModuleInfo }
98
126
  | { runtimeId: string; timestamp: number; type: "module-evaluating"; id: string; kind: ModuleInfo["kind"]; version: number }
99
127
  | { runtimeId: string; timestamp: number; type: "module-evaluated"; id: string; kind: ModuleInfo["kind"]; version: number; module: ModuleInfo }
100
128
  | { runtimeId: string; timestamp: number; type: "module-removed"; id: string; kind: ModuleInfo["kind"]; version: number; invalidateDependents: boolean }
101
- | { runtimeId: string; timestamp: number; type: "module-error"; id?: string; kind?: ModuleInfo["kind"]; version?: number; phase: string; error: unknown }
129
+ | { runtimeId: string; timestamp: number; type: "module-error"; id?: string; kind?: ModuleInfo["kind"]; version?: number; phase: ModuleErrorPhase; error: unknown }
102
130
  | { runtimeId: string; timestamp: number; type: "runtime-cleared"; ids: string[]; preserveHostModules: boolean }
103
131
  | { runtimeId: string; timestamp: number; type: "runtime-disposed" };
104
132
 
133
+ export type RuntimeLifecycleEventType = RuntimeLifecycleEvent["type"];
134
+ export type RuntimeLifecycleEventOfType<T extends RuntimeLifecycleEventType> =
135
+ Extract<RuntimeLifecycleEvent, { type: T }>;
136
+
137
+
105
138
  export interface SolidTagRuntime {
106
139
  define(id: string, source: string, options?: DefineOptions): string;
107
140
  defineMany(definitions: SourceModuleDefinition[]): string[];
@@ -117,6 +150,14 @@ export interface SolidTagRuntime {
117
150
  remove(id: string, options?: RemoveModuleOptions): boolean;
118
151
  clear(options?: ClearRuntimeOptions): string[];
119
152
  subscribe(listener: (event: RuntimeLifecycleEvent) => void): () => boolean;
153
+ subscribe<T extends RuntimeLifecycleEventType>(
154
+ type: T,
155
+ listener: (event: RuntimeLifecycleEventOfType<T>) => void,
156
+ ): () => boolean;
157
+ subscribe<const T extends readonly RuntimeLifecycleEventType[]>(
158
+ types: T,
159
+ listener: (event: RuntimeLifecycleEventOfType<T[number]>) => void,
160
+ ): () => boolean;
120
161
  modules(): ModuleInfo[];
121
162
  dependencies(id: string): string[];
122
163
  dependents(id: string): string[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "solid-tag-runtime",
3
- "version": "0.0.7",
3
+ "version": "0.0.8",
4
4
  "description": "Runtime module system for JSX modules compiled with solid-tag and executed through @solidjs/html",
5
5
  "type": "module",
6
6
  "exports": {
package/src/events.js ADDED
@@ -0,0 +1,63 @@
1
+ export function createEventDispatcher(options = {}) {
2
+ const subscriptions = new Set();
3
+ const onListenerError = options.onListenerError;
4
+
5
+ return {
6
+ subscribe,
7
+ emit,
8
+ clear,
9
+ get size() {
10
+ return subscriptions.size;
11
+ },
12
+ };
13
+
14
+ function subscribe(typesOrListener, maybeListener) {
15
+ let types;
16
+ let listener;
17
+
18
+ if (typeof typesOrListener === "function") {
19
+ listener = typesOrListener;
20
+ types = undefined;
21
+ } else {
22
+ listener = maybeListener;
23
+ if (typeof typesOrListener === "string") {
24
+ types = new Set([typesOrListener]);
25
+ } else if (Array.isArray(typesOrListener)) {
26
+ if (typesOrListener.length === 0 || typesOrListener.some(type => typeof type !== "string" || !type)) {
27
+ throw new TypeError("subscribe(types, listener) expects a non-empty event type or event type array.");
28
+ }
29
+ types = new Set(typesOrListener);
30
+ } else {
31
+ throw new TypeError("subscribe() expects a listener, event type + listener, or event type array + listener.");
32
+ }
33
+ }
34
+
35
+ if (typeof listener !== "function") {
36
+ throw new TypeError("subscribe() expects a listener function.");
37
+ }
38
+
39
+ const subscription = { types, listener };
40
+ subscriptions.add(subscription);
41
+
42
+ return () => subscriptions.delete(subscription);
43
+ }
44
+
45
+ function emit(event) {
46
+ for (const subscription of [...subscriptions]) {
47
+ if (subscription.types && !subscription.types.has(event.type)) continue;
48
+ try {
49
+ subscription.listener(event);
50
+ } catch (error) {
51
+ try {
52
+ onListenerError?.(error, event);
53
+ } catch {
54
+ // Listener error reporting must never break the emitter.
55
+ }
56
+ }
57
+ }
58
+ }
59
+
60
+ function clear() {
61
+ subscriptions.clear();
62
+ }
63
+ }