solid-tag-runtime 0.0.12 → 0.0.13

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/docs/README.md ADDED
@@ -0,0 +1,23 @@
1
+ # solid-tag-runtime documentation
2
+
3
+ `solid-tag-runtime` is a runtime module system for dynamically defined Solid JSX/JavaScript modules. The core package remains module-first and DOM-independent; browser/DOM behavior lives in `solid-tag-runtime/html`.
4
+
5
+ ## Start here
6
+
7
+ - [Getting started](./getting-started.md)
8
+ - [Runtime modules and resolution](./modules.md)
9
+ - [HTML runtime and ownership](./html-runtime.md)
10
+ - [Declarative rendering](./rendering.md)
11
+ - [`<solid-render>`](./solid-render.md)
12
+ - [Lifecycle events](./lifecycle-events.md)
13
+ - [Persistent compile cache](./compile-cache.md)
14
+
15
+ ## API reference
16
+
17
+ - [Core runtime API](./api/runtime.md)
18
+ - [HTML runtime API](./api/html.md)
19
+ - [Compile-cache API](./api/compile-cache.md)
20
+
21
+ ## Engineering design
22
+
23
+ The root [`ARCHITECTURE.md`](../ARCHITECTURE.md) is the engineering handoff and decision log. It documents internal invariants, compiler/linker boundaries, ownership rules, and compatibility decisions.
@@ -0,0 +1,103 @@
1
+ # Compile-cache API
2
+
3
+ ## Providers
4
+
5
+ ```ts
6
+ import {
7
+ createMemoryCompileCache,
8
+ createLocalStorageCompileCache,
9
+ createIndexedDBCompileCache,
10
+ } from "solid-tag-runtime";
11
+ ```
12
+
13
+ ### Memory
14
+
15
+ ```ts
16
+ const store = createMemoryCompileCache();
17
+ ```
18
+
19
+ ### localStorage
20
+
21
+ ```ts
22
+ const store = createLocalStorageCompileCache({
23
+ prefix: "solid-tag-runtime:compile-cache:",
24
+ });
25
+ ```
26
+
27
+ ### IndexedDB
28
+
29
+ ```ts
30
+ const store = createIndexedDBCompileCache({
31
+ database: "my-app-runtime",
32
+ storeName: "artifacts",
33
+ databaseVersion: 1,
34
+ });
35
+ ```
36
+
37
+ ## Runtime configuration
38
+
39
+ ```ts
40
+ const runtime = createRuntime({
41
+ compileCache: {
42
+ store,
43
+ namespace: "main",
44
+ version: "1",
45
+ maxEntries: 500,
46
+ maxBytes: 50 * 1024 * 1024,
47
+ maxAge: 30 * 24 * 60 * 60 * 1000,
48
+ },
49
+ });
50
+ ```
51
+
52
+ ## Store contract
53
+
54
+ ```ts
55
+ interface CompileCacheStore {
56
+ kind?: string;
57
+
58
+ get(key): Promise<CachedCompileRecord | undefined>;
59
+ set(key, record): Promise<void>;
60
+ delete(key): Promise<boolean | void>;
61
+
62
+ clear?({ prefix? }): Promise<number | void>;
63
+ entries?({ prefix? }): Promise<Array<[string, CachedCompileRecord]>>;
64
+ }
65
+ ```
66
+
67
+ `clear()` and `entries()` are used by namespace/version management and built-in pruning. Custom stores that omit them can still serve normal lookup/write/delete operations, but cache-wide management is limited.
68
+
69
+ ## Cache modes
70
+
71
+ ```ts
72
+ await runtime.compile(id, { cache: "use" });
73
+ await runtime.compile(id, { cache: "refresh" });
74
+ await runtime.compile(id, { cache: "bypass" });
75
+ ```
76
+
77
+ ## Management
78
+
79
+ ```ts
80
+ await runtime.compileCache.invalidate(id);
81
+ await runtime.compileCache.clear();
82
+ await runtime.compileCache.clear({ allVersions: true });
83
+ ```
84
+
85
+ ## Events
86
+
87
+ ```text
88
+ compile-cache-hit
89
+ compile-cache-miss
90
+ compile-cache-write
91
+ compile-cache-refresh
92
+ compile-cache-bypass
93
+ compile-cache-error
94
+ compile-cache-evict
95
+ ```
96
+
97
+ All events are observational.
98
+
99
+ ## Failure and quota behavior
100
+
101
+ Normal cache lookup/write failures are optimization failures rather than module failures. A quota-style write failure triggers pruning and one write retry before `compile-cache-error` is emitted.
102
+
103
+ Explicit management operations such as `runtime.compileCache.clear()` may reject because the caller explicitly requested the storage mutation.
@@ -0,0 +1,83 @@
1
+ # HTML runtime API
2
+
3
+ ```ts
4
+ import {
5
+ createHTMLRuntime,
6
+ registerHTML,
7
+ observeHTML,
8
+ defineScript,
9
+ registerSolidRenderElement,
10
+ } from "solid-tag-runtime/html";
11
+ ```
12
+
13
+ ## Controller
14
+
15
+ ```ts
16
+ const html = createHTMLRuntime(runtime, options);
17
+ ```
18
+
19
+ Common options:
20
+
21
+ - `scope`
22
+ - `root`
23
+ - `appendTo`
24
+ - `acceptUnscoped`
25
+ - `executeEntries`
26
+ - `executeRenders`
27
+ - `onError`
28
+
29
+ ## Discovery and observation
30
+
31
+ ```ts
32
+ await html.register(options?);
33
+ await html.observe(options?);
34
+ await html.flush(options?);
35
+ html.disconnect();
36
+ ```
37
+
38
+ ## Explicit ownership
39
+
40
+ ```ts
41
+ await html.registerElement(element, options?);
42
+ await html.append(element, options?);
43
+ await html.addModule(options);
44
+ await html.updateElement(element, options?);
45
+ await html.removeElement(element, options?);
46
+ ```
47
+
48
+ ## Root/append lifecycle
49
+
50
+ ```ts
51
+ await html.setRoot(root, options?);
52
+ await html.moveTo(root, options?);
53
+ html.setAppendTarget(target);
54
+
55
+ html.root;
56
+ html.appendTarget;
57
+ ```
58
+
59
+ ## Introspection
60
+
61
+ ```ts
62
+ html.owns(element);
63
+ html.getModuleId(element);
64
+ html.getElement(moduleId);
65
+ ```
66
+
67
+ ## Events
68
+
69
+ ```ts
70
+ html.subscribe(listener);
71
+ html.subscribe(type, listener);
72
+ html.subscribe(types, listener);
73
+ ```
74
+
75
+ ## Convenience functions
76
+
77
+ ```ts
78
+ await registerHTML(runtime, options?);
79
+ await defineScript(runtime, element, options?);
80
+ const observer = await observeHTML(runtime, options?);
81
+ ```
82
+
83
+ For rendering semantics see [Declarative rendering](../rendering.md) and [`<solid-render>`](../solid-render.md).
@@ -0,0 +1,87 @@
1
+ # Core runtime API
2
+
3
+ ```ts
4
+ import { createRuntime } from "solid-tag-runtime";
5
+ ```
6
+
7
+ ## Creation
8
+
9
+ ```ts
10
+ const runtime = createRuntime(options);
11
+ ```
12
+
13
+ Important options:
14
+
15
+ - `modules` — initial host namespace modules
16
+ - `urls` — initial URL modules
17
+ - `compiler` / `compilerOptions`
18
+ - `resolve`
19
+ - `allowNativeImports`
20
+ - `moduleUrlBackend`
21
+ - `compileCache` — optional persistent pre-link compiler cache
22
+
23
+ ## Definition
24
+
25
+ ```ts
26
+ runtime.define(id, source, options?);
27
+ runtime.defineMany(definitions);
28
+ runtime.defineModule(id, namespace);
29
+ runtime.defineUrl(id, url);
30
+ runtime.update(id, source, options?);
31
+ ```
32
+
33
+ ## Loading/compilation
34
+
35
+ ```ts
36
+ await runtime.import(id);
37
+ await runtime.compile(id, { cache?: "use" | "refresh" | "bypass" });
38
+ await runtime.toModule(source, options?);
39
+ await runtime.toComponent(source, options?);
40
+ ```
41
+
42
+ `runtime.compile()` preserves its public meaning: it returns the current linked/executable code and URL. The persistent cache stores an internal pre-link artifact instead.
43
+
44
+ ## Lifecycle
45
+
46
+ ```ts
47
+ runtime.invalidate(id, {
48
+ dependents?: boolean,
49
+ compile?: boolean,
50
+ });
51
+
52
+ runtime.remove(id, options?);
53
+ runtime.clear(options?);
54
+ runtime.dispose();
55
+ ```
56
+
57
+ ## Introspection
58
+
59
+ ```ts
60
+ runtime.has(id);
61
+ runtime.resolve(specifier, importer?);
62
+ runtime.modules();
63
+ runtime.dependencies(id);
64
+ runtime.dependents(id);
65
+ runtime.getModuleInfo(id);
66
+ ```
67
+
68
+ ## Events
69
+
70
+ ```ts
71
+ runtime.subscribe(listener);
72
+ runtime.subscribe(type, listener);
73
+ runtime.subscribe(types, listener);
74
+ ```
75
+
76
+ See [Lifecycle events](../lifecycle-events.md).
77
+
78
+ ## Compile-cache management
79
+
80
+ ```ts
81
+ runtime.compileCache.enabled;
82
+ await runtime.compileCache.invalidate(id);
83
+ await runtime.compileCache.clear();
84
+ await runtime.compileCache.clear({ allVersions: true });
85
+ ```
86
+
87
+ See [Compile-cache API](./compile-cache.md).
@@ -0,0 +1,271 @@
1
+ # Persistent compile cache
2
+
3
+ Persistent compile caching is opt-in. It stores **pre-link compiled artifacts**, not linked runtime modules.
4
+
5
+ ```text
6
+ source
7
+ ↓
8
+ compile
9
+ ↓
10
+ compiled artifact ← persistent cache boundary
11
+ ↓
12
+ link
13
+ ↓
14
+ module URL
15
+ ↓
16
+ native import/evaluation
17
+ ```
18
+
19
+ The cache accelerates parsing/transformation/module-reference analysis while all runtime-dependent linking stays fresh.
20
+
21
+ ## What is persisted
22
+
23
+ A compiled artifact contains linker-ready JavaScript and import-reference metadata:
24
+
25
+ ```ts
26
+ {
27
+ code,
28
+ imports,
29
+ diagnostics,
30
+ sourceMap?
31
+ }
32
+ ```
33
+
34
+ Import metadata refers to the **compiled code**, not the original JSX source.
35
+
36
+ ## What is never persisted
37
+
38
+ - linked source containing current Blob/data/dependency URLs
39
+ - Blob/data URLs
40
+ - module URL backend results
41
+ - host bridge identities
42
+ - native module namespaces
43
+ - import promises
44
+ - runtime resolver output
45
+
46
+ ## Enable IndexedDB caching
47
+
48
+ IndexedDB is the recommended browser backend:
49
+
50
+ ```ts
51
+ import {
52
+ createRuntime,
53
+ createIndexedDBCompileCache,
54
+ } from "solid-tag-runtime";
55
+
56
+ const runtime = createRuntime({
57
+ compileCache: {
58
+ store: createIndexedDBCompileCache({
59
+ database: "my-app-runtime",
60
+ }),
61
+ namespace: "main",
62
+ version: "1",
63
+ },
64
+ });
65
+ ```
66
+
67
+ Without `compileCache`, persistent lookup/write behavior is disabled.
68
+
69
+ ## localStorage
70
+
71
+ Useful for small demos and debugging:
72
+
73
+ ```ts
74
+ import { createLocalStorageCompileCache } from "solid-tag-runtime";
75
+
76
+ const store = createLocalStorageCompileCache();
77
+ ```
78
+
79
+ `localStorage` is synchronous underneath, smaller, and less suitable for large compiled artifacts. The provider still exposes the same async store interface.
80
+
81
+ ## Memory provider
82
+
83
+ ```ts
84
+ import { createMemoryCompileCache } from "solid-tag-runtime";
85
+
86
+ const store = createMemoryCompileCache();
87
+ ```
88
+
89
+ This is useful for tests, benchmarks, and validating cache identity across runtime instances within one page/process.
90
+
91
+ ## Cache identity
92
+
93
+ The logical artifact key includes:
94
+
95
+ ```text
96
+ artifact ABI
97
+ + compiler fingerprint
98
+ + source format
99
+ + compile-affecting context
100
+ + exact source SHA-256
101
+ ```
102
+
103
+ It deliberately excludes:
104
+
105
+ - dependency versions/source
106
+ - resolver state
107
+ - module URL backend
108
+ - host-module values
109
+ - module ID by default
110
+
111
+ Therefore identical source may share one compiler artifact under different module IDs while linking relative imports independently for each ID.
112
+
113
+ ## Compiler fingerprint
114
+
115
+ The default `solid-tag` compiler supplies a stable fingerprint. A custom compiler must provide one to use persistent storage safely:
116
+
117
+ ```ts
118
+ const compiler = {
119
+ fingerprint: "my-compiler:v3",
120
+ compile(source, context) {
121
+ return artifact;
122
+ },
123
+ };
124
+ ```
125
+
126
+ The fingerprint may be a function when compilation behavior depends on context:
127
+
128
+ ```ts
129
+ fingerprint(context) {
130
+ return `my-compiler:v3:${context.format}`;
131
+ }
132
+ ```
133
+
134
+ A custom compiler without a stable fingerprint still compiles normally, but persistent caching is bypassed and a one-time warning/event is produced.
135
+
136
+ ## `use`, `refresh`, and `bypass`
137
+
138
+ ```ts
139
+ await runtime.compile("/App.jsx", { cache: "use" });
140
+ await runtime.compile("/App.jsx", { cache: "refresh" });
141
+ await runtime.compile("/App.jsx", { cache: "bypass" });
142
+ ```
143
+
144
+ ### `use`
145
+
146
+ Read reusable compiler artifacts; compile/write on miss.
147
+
148
+ ### `refresh`
149
+
150
+ Skip reusable artifact reads for the requested module, compile fresh, and replace the stored record.
151
+
152
+ ### `bypass`
153
+
154
+ Compile the requested module fresh without persistent read/write.
155
+
156
+ Dependencies continue using their normal reusable artifacts.
157
+
158
+ ## Graph invalidation vs compiler invalidation
159
+
160
+ Normal invalidation preserves compiler artifacts:
161
+
162
+ ```ts
163
+ runtime.invalidate("/Button.jsx");
164
+ ```
165
+
166
+ This revokes/rebuilds linked/evaluated graph state but keeps syntax work reusable.
167
+
168
+ Force only the requested module to compile fresh next time:
169
+
170
+ ```ts
171
+ runtime.invalidate("/Button.jsx", {
172
+ compile: true,
173
+ });
174
+ ```
175
+
176
+ Dependents relink but are not forced to recompile.
177
+
178
+ ## Persistent cache management
179
+
180
+ Delete the current module's stored artifact:
181
+
182
+ ```ts
183
+ await runtime.compileCache.invalidate("/App.jsx");
184
+ ```
185
+
186
+ Clear the current namespace/version:
187
+
188
+ ```ts
189
+ await runtime.compileCache.clear();
190
+ ```
191
+
192
+ Clear all versions in the current namespace:
193
+
194
+ ```ts
195
+ await runtime.compileCache.clear({
196
+ allVersions: true,
197
+ });
198
+ ```
199
+
200
+ These are cache-management operations, not graph-removal APIs.
201
+
202
+ ## Application version
203
+
204
+ ```ts
205
+ compileCache: {
206
+ namespace: "editor",
207
+ version: "2026.10.3",
208
+ store,
209
+ }
210
+ ```
211
+
212
+ Changing `version` creates logical misses without requiring an eager database scan/deletion. It is the application-controlled global invalidation switch.
213
+
214
+ ## Bounds
215
+
216
+ ```ts
217
+ compileCache: {
218
+ store,
219
+ namespace: "editor",
220
+ version: "1",
221
+ maxEntries: 500,
222
+ maxBytes: 50 * 1024 * 1024,
223
+ maxAge: 30 * 24 * 60 * 60 * 1000,
224
+ }
225
+ ```
226
+
227
+ Built-in stores support basic age/entry/byte pruning. This is intentionally simple; cross-tab locks and sophisticated global LRU are not part of the first implementation.
228
+
229
+ ## Failure behavior
230
+
231
+ Persistent storage is an optimization. Read/write/corruption/provider failures fall back to normal compilation whenever possible and are observable through `compile-cache-error`.
232
+
233
+ For quota-style write failures, the runtime first prunes eligible/older records and retries the write once. If that retry also fails, it emits `compile-cache-error` and continues with the freshly compiled in-memory artifact. Cache failure never makes normal module compilation unavailable.
234
+
235
+ Explicit management calls such as `compileCache.clear()` may reject when the explicitly requested storage operation cannot be completed.
236
+
237
+ ## Introspection
238
+
239
+ ```ts
240
+ runtime.getModuleInfo("/App.jsx")?.compileCache;
241
+ ```
242
+
243
+ Example fields:
244
+
245
+ ```ts
246
+ {
247
+ enabled: true,
248
+ status: "hit",
249
+ source: "indexeddb",
250
+ key: "...",
251
+ compilerFingerprint: "...",
252
+ sourceHash: "...",
253
+ compileContextHash: "..."
254
+ }
255
+ ```
256
+
257
+ ## Dependency-only changes
258
+
259
+ If `/App.jsx` imports `/Button.jsx` and only `Button` source changes:
260
+
261
+ ```text
262
+ Button
263
+ → compiler miss/new artifact
264
+ → relink
265
+
266
+ App
267
+ → compiler artifact reused
268
+ → relink against new Button URL
269
+ ```
270
+
271
+ This is the primary reason the persistent cache boundary is pre-link rather than linked output.
@@ -0,0 +1,105 @@
1
+ # Getting started
2
+
3
+ ## Install
4
+
5
+ ```bash
6
+ npm install solid-tag-runtime solid-tag
7
+ ```
8
+
9
+ The runtime intentionally does not bundle its own Solid runtime. Register the host application's existing Solid modules so dynamically compiled code shares the same reactive system.
10
+
11
+ ```ts
12
+ import * as Solid from "solid-js";
13
+ import * as SolidWeb from "@solidjs/web";
14
+ import html from "@solidjs/html";
15
+ import { createRuntime } from "solid-tag-runtime";
16
+
17
+ const runtime = createRuntime({
18
+ modules: {
19
+ "solid-js": Solid,
20
+ "@solidjs/web": SolidWeb,
21
+ "@solidjs/html": { default: html },
22
+ },
23
+ });
24
+ ```
25
+
26
+ ## Define and import a module
27
+
28
+ ```ts
29
+ runtime.define("/Counter.jsx", `
30
+ import { createSignal } from "solid-js";
31
+
32
+ export default function Counter() {
33
+ const [count, setCount] = createSignal(0);
34
+ return (
35
+ <button onClick={() => setCount(value => value + 1)}>
36
+ {count()}
37
+ </button>
38
+ );
39
+ }
40
+ `);
41
+
42
+ const module = await runtime.import("/Counter.jsx");
43
+ module.default;
44
+ ```
45
+
46
+ Relative imports between runtime modules work normally:
47
+
48
+ ```ts
49
+ runtime.define("/ui/Button.jsx", `
50
+ export function Button(props) {
51
+ return <button>{props.children}</button>;
52
+ }
53
+ `);
54
+
55
+ runtime.define("/App.jsx", `
56
+ import { Button } from "./ui/Button.jsx";
57
+
58
+ export default function App() {
59
+ return <Button>Hello</Button>;
60
+ }
61
+ `);
62
+ ```
63
+
64
+ ## Browser HTML adapter
65
+
66
+ ```ts
67
+ import { createHTMLRuntime } from "solid-tag-runtime/html";
68
+
69
+ const htmlRuntime = createHTMLRuntime(runtime, {
70
+ scope: "main",
71
+ root: document,
72
+ });
73
+
74
+ await htmlRuntime.register();
75
+ await htmlRuntime.observe({ registerExisting: false });
76
+ ```
77
+
78
+ Declarative source:
79
+
80
+ ```html
81
+ <script
82
+ type="solid-jsx"
83
+ data-solid-runtime="main"
84
+ module="/App.jsx"
85
+ >
86
+ export default function App() {
87
+ return <h1>Hello</h1>;
88
+ }
89
+ </script>
90
+ ```
91
+
92
+ See [HTML runtime](./html-runtime.md), [declarative rendering](./rendering.md), and [`<solid-render>`](./solid-render.md) for browser-specific behavior.
93
+
94
+ ## Browser import maps
95
+
96
+ When loading through an import map, map both the package root and HTML subpath to the same release:
97
+
98
+ ```json
99
+ {
100
+ "imports": {
101
+ "solid-tag-runtime": "https://esm.sh/solid-tag-runtime@0.0.13",
102
+ "solid-tag-runtime/html": "https://esm.sh/solid-tag-runtime@0.0.13/html"
103
+ }
104
+ }
105
+ ```