solid-tag-runtime 0.0.6 → 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
@@ -38,9 +38,11 @@ The runtime should allow applications to:
38
38
  5. expose host components, signals, callbacks, services, and data as modules
39
39
  6. reuse compiled/evaluated modules through caching
40
40
  7. invalidate and rebuild a module graph when source changes
41
- 8. inspect dependencies and compiled source
42
- 9. support normal JavaScript modules in the same graph
43
- 10. keep the JSX compiler replaceable behind a narrow adapter
41
+ 8. explicitly remove and clear module definitions
42
+ 9. observe runtime lifecycle transitions for tooling/tracing
43
+ 10. inspect dependencies and compiled source
44
+ 11. support normal JavaScript modules in the same graph
45
+ 12. keep the JSX compiler replaceable behind a narrow adapter
44
46
 
45
47
  ## 3. Current non-goals
46
48
 
@@ -99,6 +101,7 @@ const runtime = createRuntime(options);
99
101
 
100
102
  ```ts
101
103
  runtime.define(id, source, options?);
104
+ runtime.defineMany(definitions);
102
105
  runtime.update(id, source, options?);
103
106
  ```
104
107
 
@@ -136,9 +139,18 @@ runtime.getModuleInfo(id);
136
139
 
137
140
  ```ts
138
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
+
139
149
  runtime.dispose();
140
150
  ```
141
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
+
142
154
  ## 6. Module kinds
143
155
 
144
156
  The runtime currently has three module record kinds.
@@ -230,6 +242,10 @@ await html.observe({ registerExisting: false });
230
242
  The controller exposes:
231
243
 
232
244
  ```ts
245
+ html.subscribe(listener);
246
+ html.subscribe(type, listener);
247
+ html.subscribe(types, listener);
248
+
233
249
  html.register(options?);
234
250
  html.observe(options?);
235
251
  html.flush(options?);
@@ -980,3 +996,151 @@ Reason: applications can reparent an existing observed root without intervention
980
996
  Invariant: a root change must not create a second observer, lose ownership records, or race pending scans from the old root. Pending observer work is drained before rebinding. A live root change registers existing scripts in the new root by default; a disconnected root change does not perform discovery unless requested.
981
997
 
982
998
  Regression coverage: the suite verifies same-node reparenting, live root rebinding, skipping existing scripts, disconnected root changes, `moveTo()` synchronization of root/append target, and independent append-target changes.
999
+
1000
+
1001
+ ### 0.0.7 — explicit module lifecycle, batching, and HTML-owned replacement
1002
+
1003
+ Decision: add `defineMany()`, `remove()`, `clear()`, and `subscribe()` to the core runtime, and `updateElement()` / `removeElement()` to `solid-tag-runtime/html`.
1004
+
1005
+ Reason: long-lived runtime environments need an explicit lifecycle beyond define/update/invalidate. Editors, previews, CMSs, and plugin hosts must be able to install related modules together, remove stale definitions, reset runtime state while preserving host modules, trace module transitions, and keep HTML ownership synchronized with explicit module replacement/removal.
1006
+
1007
+ Core lifecycle semantics:
1008
+
1009
+ - `defineMany()` validates the definition list before applying it and rejects duplicate IDs inside one batch. No module evaluation happens during definition, so the full batch is available before an import starts linking. Lifecycle events produced while applying the batch are buffered and published only after every definition has been installed, preventing a subscriber from observing or importing a partially installed batch.
1010
+ - `remove(id)` deletes one module record, revokes generated URLs, removes host-registry state when applicable, and invalidates transitive dependents by default.
1011
+ - `clear()` removes source/URL modules while preserving host modules by default; `preserveHostModules:false` removes every record.
1012
+ - `subscribe(listener)` emits synchronous lifecycle events but isolates subscriber exceptions from runtime execution.
1013
+ - public `resolve()` remains the canonical tooling hook for asking how a specifier resolves without evaluating it.
1014
+
1015
+ Lifecycle event contract:
1016
+
1017
+ ```text
1018
+ module-defined
1019
+ module-invalidated
1020
+ module-compiled
1021
+ module-evaluating
1022
+ module-evaluated
1023
+ module-removed
1024
+ module-error
1025
+ runtime-cleared
1026
+ runtime-disposed
1027
+ ```
1028
+
1029
+ HTML lifecycle semantics:
1030
+
1031
+ - `updateElement(element)` only operates on an element already owned by that controller. It re-reads/fetches source and calls `runtime.update()` while preserving logical module identity. Changing `module` identity requires explicit remove + register.
1032
+ - `removeElement(element)` releases the shared WeakMap ownership and per-controller binding. By default it also removes the runtime module and DOM node. `removeModule` and `removeFromDOM` can be controlled independently.
1033
+ - DOM removal by itself still does not imply module removal. Module lifecycle remains explicit.
1034
+ - If ownership is released while the element remains under an active observed root, the controller marks that element ignored so the observer does not immediately reclaim it. An explicit `registerElement()` may claim it again.
1035
+
1036
+ Invariants:
1037
+
1038
+ 1. Removing a module must revoke its executable URL/namespace cache and clean outbound graph edges.
1039
+ 2. Dependents are invalidated before dependency removal unless explicitly disabled.
1040
+ 3. `defineMany()` must not publish lifecycle events until the whole batch has been installed.
1041
+ 4. `clear()` must not silently discard host environment modules under its default configuration.
1042
+ 5. Lifecycle listeners are observational; listener failures cannot alter runtime execution.
1043
+ 6. HTML element ownership and runtime module existence are related but separate lifecycles.
1044
+ 7. `updateElement()` never changes the module identity associated with an owned element.
1045
+ 8. `removeElement()` is the canonical operation for releasing shared WeakMap ownership.
1046
+
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
@@ -102,6 +102,17 @@ The runtime compiles and links the dependency graph automatically.
102
102
 
103
103
  `solid-tag-runtime/html` is the browser/DOM adapter. The core runtime remains DOM-independent.
104
104
 
105
+ Browser import maps must map the subpath explicitly as well as the package root:
106
+
107
+ ```json
108
+ {
109
+ "imports": {
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
+ }
113
+ }
114
+ ```
115
+
105
116
  For simple single-runtime use, the original helpers remain available:
106
117
 
107
118
  ```ts
@@ -334,9 +345,9 @@ The `root` may be a `Document`, normal `Element`, `DocumentFragment`, or `Shadow
334
345
 
335
346
  ### Addition-only observation
336
347
 
337
- Observation remains addition-only in `0.0.6`.
348
+ Observation remains addition-oriented.
338
349
 
339
- Changing the source/attributes of an already owned script or removing it from the DOM does not implicitly update/delete the corresponding runtime module. Use `runtime.update()` / `runtime.invalidate()` for explicit module lifecycle changes.
350
+ Changing the source/attributes of an already owned script or removing it from the DOM does not implicitly update/delete the corresponding runtime module. In `0.0.7`, use `html.updateElement()` and `html.removeElement()` when the lifecycle should follow an owned script explicitly, or use the core `runtime.update()` / `runtime.remove()` APIs directly.
340
351
 
341
352
  The HTML adapter does **not** reinterpret arbitrary document HTML as JSX and does not currently assign component semantics to `<template>`.
342
353
 
@@ -510,6 +521,223 @@ Updating a module invalidates its compiled URL and its dependent runtime modules
510
521
 
511
522
  Existing references to an older module namespace/component are not mutated. Applications that implement live editing should import the updated entry module again.
512
523
 
524
+ ### Define several source modules together
525
+
526
+ ```ts
527
+ runtime.defineMany([
528
+ {
529
+ id: "/ui/Button.jsx",
530
+ source: buttonSource,
531
+ },
532
+ {
533
+ id: "/App.jsx",
534
+ source: appSource,
535
+ },
536
+ ]);
537
+ ```
538
+
539
+ `defineMany()` validates the whole definition list before applying it and rejects duplicate module IDs inside the batch. It is useful when a group of modules should become available before anything is imported. Lifecycle notifications from the batch are published only after every definition has been installed.
540
+
541
+ ### Remove modules
542
+
543
+ ```ts
544
+ runtime.remove("/ui/Button.jsx");
545
+ ```
546
+
547
+ Removal invalidates transitive dependents by default. A later import of a dependent will fail resolution until the missing module is defined again.
548
+
549
+ If you intentionally want to leave currently linked dependents untouched:
550
+
551
+ ```ts
552
+ runtime.remove("/ui/Button.jsx", {
553
+ invalidateDependents: false,
554
+ });
555
+ ```
556
+
557
+ ### Clear a runtime
558
+
559
+ ```ts
560
+ runtime.clear();
561
+ ```
562
+
563
+ By default `clear()` removes source and URL modules while preserving host modules registered with `defineModule()`. This is convenient for editor/preview resets where the host environment should remain installed.
564
+
565
+ ```ts
566
+ runtime.clear({
567
+ preserveHostModules: false,
568
+ });
569
+ ```
570
+
571
+ removes everything. The returned array contains the IDs that were removed.
572
+
573
+ ### Lifecycle events
574
+
575
+ `0.0.8` expands lifecycle subscriptions into a typed event stream with independent join/leave semantics.
576
+
577
+ Subscribe to everything:
578
+
579
+ ```ts
580
+ const unsubscribe = runtime.subscribe(event => {
581
+ console.log(event.type, event);
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
691
+
692
+ html-error
693
+ ```
694
+
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.
696
+
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()`.
698
+
699
+ Like core runtime subscribers, HTML subscribers are observational and exception-isolated.
700
+
701
+ ## HTML-owned module updates and removal
702
+
703
+ When a script element is already owned by an HTML runtime controller, update the runtime module from the current element contents with:
704
+
705
+ ```ts
706
+ script.textContent = `
707
+ export function Card() {
708
+ return <div>updated</div>;
709
+ }
710
+ `;
711
+
712
+ await html.updateElement(script);
713
+ ```
714
+
715
+ `updateElement()` keeps the existing logical module ID and calls `runtime.update()` underneath. Changing the element's logical `module` identity is intentionally rejected; remove and register it again instead.
716
+
717
+ Release an owned element explicitly with:
718
+
719
+ ```ts
720
+ await html.removeElement(script);
721
+ ```
722
+
723
+ By default this:
724
+
725
+ 1. removes the runtime module,
726
+ 2. invalidates its dependents,
727
+ 3. releases HTML ownership, and
728
+ 4. removes the script element from the DOM.
729
+
730
+ The two lifecycles can be controlled independently:
731
+
732
+ ```ts
733
+ await html.removeElement(script, {
734
+ removeModule: false,
735
+ removeFromDOM: true,
736
+ });
737
+ ```
738
+
739
+ This is intentionally explicit: ordinary DOM removal does not silently delete a runtime module.
740
+
513
741
  ## Custom resolution
514
742
 
515
743
  ```ts
@@ -548,10 +776,12 @@ Set it to `false` when all module dependencies must be explicitly registered wit
548
776
 
549
777
  ```ts
550
778
  runtime.invalidate("/App.jsx");
779
+ runtime.remove("/Unused.jsx");
780
+ runtime.clear();
551
781
  runtime.dispose();
552
782
  ```
553
783
 
554
- `dispose()` revokes generated module URLs and clears the host-module registry for that runtime instance.
784
+ `invalidate()` forces fresh compilation/evaluation without deleting the module definition. `remove()` deletes one definition, `clear()` resets a group of definitions, and `dispose()` permanently tears down the runtime instance.
555
785
 
556
786
  ## Current limitations
557
787
 
package/html.d.ts CHANGED
@@ -5,7 +5,8 @@ export interface HTMLModuleScriptElement {
5
5
  textContent?: string | null;
6
6
  baseURI?: string;
7
7
  ownerDocument?: HTMLDocumentLike | null;
8
- parentNode?: unknown;
8
+ parentNode?: { removeChild?(element: any): unknown } | null;
9
+ remove?(): void;
9
10
  getAttribute(name: string): string | null;
10
11
  hasAttribute?(name: string): boolean;
11
12
  setAttribute?(name: string, value: string): void;
@@ -76,6 +77,22 @@ export interface HTMLModuleDefinition {
76
77
  element: HTMLModuleScriptElement;
77
78
  }
78
79
 
80
+ export interface RemoveHTMLElementOptions {
81
+ /** Remove the runtime module as well as releasing HTML ownership. Default: true. */
82
+ removeModule?: boolean;
83
+ /** Remove the script element from the DOM. Default: true. */
84
+ removeFromDOM?: boolean;
85
+ /** Invalidate runtime dependents when removing the module. Default: true. */
86
+ invalidateDependents?: boolean;
87
+ }
88
+
89
+ export interface RemoveHTMLElementResult {
90
+ id: string;
91
+ element: HTMLModuleScriptElement;
92
+ moduleRemoved: boolean;
93
+ elementRemoved: boolean;
94
+ }
95
+
79
96
  export interface RegisterHTMLOptions extends DefineScriptOptions {
80
97
  selector?: string;
81
98
  executeEntries?: boolean;
@@ -90,6 +107,45 @@ export interface RegisterHTMLResult {
90
107
  }>;
91
108
  }
92
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
+
93
149
  export interface ObserveHTMLOptions extends RegisterHTMLOptions {
94
150
  /** Register scripts already present when observation starts. Default: true. */
95
151
  registerExisting?: boolean;
@@ -141,6 +197,16 @@ export interface HTMLRuntimeController {
141
197
  /** Result of the observer's initial scan. */
142
198
  readonly initial: RegisterHTMLResult;
143
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
+
144
210
  /** One-shot scan of matching scripts under the configured/root override. */
145
211
  register(options?: RegisterHTMLOptions): Promise<RegisterHTMLResult>;
146
212
 
@@ -181,6 +247,18 @@ export interface HTMLRuntimeController {
181
247
  options?: RegisterHTMLOptions,
182
248
  ): Promise<HTMLModuleDefinition>;
183
249
 
250
+ /** Re-read and replace source for an element already owned by this controller. */
251
+ updateElement(
252
+ element: HTMLModuleScriptElement,
253
+ options?: RegisterHTMLOptions,
254
+ ): Promise<HTMLModuleDefinition>;
255
+
256
+ /** Release an owned element and optionally remove its module and DOM node. */
257
+ removeElement(
258
+ element: HTMLModuleScriptElement,
259
+ options?: RemoveHTMLElementOptions,
260
+ ): Promise<RemoveHTMLElementResult>;
261
+
184
262
  /** Claim first, append a user-created element, then register it directly. */
185
263
  append(
186
264
  element: HTMLModuleScriptElement,
package/index.d.ts CHANGED
@@ -76,8 +76,68 @@ export interface CompileResult {
76
76
  diagnostics: unknown[];
77
77
  }
78
78
 
79
+ export interface SourceModuleDefinition extends DefineOptions {
80
+ id: string;
81
+ source: string;
82
+ }
83
+
84
+ export interface RemoveModuleOptions {
85
+ /** Invalidate transitive dependents before removal. Default: true. */
86
+ invalidateDependents?: boolean;
87
+ }
88
+
89
+ export interface ClearRuntimeOptions {
90
+ /** Keep host namespace modules registered with defineModule(). Default: true. */
91
+ preserveHostModules?: boolean;
92
+ }
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
+
116
+ export type RuntimeLifecycleEvent =
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 }
124
+ | { runtimeId: string; timestamp: number; type: "module-invalidated"; id: string; kind: ModuleInfo["kind"]; version: number; reason: string }
125
+ | { runtimeId: string; timestamp: number; type: "module-compiled"; id: string; kind: ModuleInfo["kind"]; version: number; module: ModuleInfo }
126
+ | { runtimeId: string; timestamp: number; type: "module-evaluating"; id: string; kind: ModuleInfo["kind"]; version: number }
127
+ | { runtimeId: string; timestamp: number; type: "module-evaluated"; id: string; kind: ModuleInfo["kind"]; version: number; module: ModuleInfo }
128
+ | { runtimeId: string; timestamp: number; type: "module-removed"; id: string; kind: ModuleInfo["kind"]; version: number; invalidateDependents: boolean }
129
+ | { runtimeId: string; timestamp: number; type: "module-error"; id?: string; kind?: ModuleInfo["kind"]; version?: number; phase: ModuleErrorPhase; error: unknown }
130
+ | { runtimeId: string; timestamp: number; type: "runtime-cleared"; ids: string[]; preserveHostModules: boolean }
131
+ | { runtimeId: string; timestamp: number; type: "runtime-disposed" };
132
+
133
+ export type RuntimeLifecycleEventType = RuntimeLifecycleEvent["type"];
134
+ export type RuntimeLifecycleEventOfType<T extends RuntimeLifecycleEventType> =
135
+ Extract<RuntimeLifecycleEvent, { type: T }>;
136
+
137
+
79
138
  export interface SolidTagRuntime {
80
139
  define(id: string, source: string, options?: DefineOptions): string;
140
+ defineMany(definitions: SourceModuleDefinition[]): string[];
81
141
  update(id: string, source: string, options?: DefineOptions): string;
82
142
  defineModule(id: string, namespace: ModuleNamespaceLike | Function): string;
83
143
  defineUrl(id: string, url: string): string;
@@ -87,6 +147,17 @@ export interface SolidTagRuntime {
87
147
  toComponent<T extends Function = Function>(source: string, options?: ToComponentOptions): Promise<T>;
88
148
  has(id: string): boolean;
89
149
  invalidate(id: string, options?: { dependents?: boolean }): string[];
150
+ remove(id: string, options?: RemoveModuleOptions): boolean;
151
+ clear(options?: ClearRuntimeOptions): string[];
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;
90
161
  modules(): ModuleInfo[];
91
162
  dependencies(id: string): string[];
92
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.6",
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": {