@hydraharness/harness-client-modules 0.1.1-rc.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +26 -0
- package/lib/client.js +324 -0
- package/lib/index.js +493 -0
- package/lib/invariant.js +34 -0
- package/lib/types/client/index.d.ts +32 -0
- package/lib/types/client/manifest.d.ts +239 -0
- package/lib/types/client/system.d.ts +46 -0
- package/lib/types/index.d.ts +127 -0
- package/lib/types/invariant.d.ts +16 -0
- package/package.json +65 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 DeepSeek
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# @hydraharness/harness-client-modules
|
|
2
|
+
|
|
3
|
+
Client module system: the browser peer of Node's internal ESM loader, built as a lazy CJS table. The web shell mounts the vendored cordis Loader for entry governance (fiber lifecycle, inject waiting, update/refresh) and injects this package's `ClientModuleLoader` through its `internal` contract — the vendored side's only consumption point is `EntryTree.import`, so replacing `internal` replaces exactly "how plugin code arrives" and nothing else.
|
|
4
|
+
|
|
5
|
+
Lazy CJS model (web2): executing a plugin bundle only REGISTERS its factory (`window.__ModuleLoader__.load({id, factory})`); every module body side effect — CSS injection included — lives in the factory closure and runs at materialization (`factory(require)` → exports, memoized in `loadCache`), not at script execution. A factory that requires another registered-but-unmaterialized module materializes it recursively; graph composition places declared dynamic requests before their consumers, and require cycles throw because factory-form CJS cannot deliver partial exports. `<id>/client` and the bare id resolve to the same exports (a plugin bundle IS its package's client half).
|
|
6
|
+
|
|
7
|
+
The Host installs `window.__ModuleLoader__` before parser preloads run. Its queue-mode `load()` retains early registrations; `create()` materializes this package's factory with an external-rejecting bootstrap require and calls its `createClientModuleSystem` export. Construction caches those same exports as the modules row, switches the same facade to live registration, and drains the remaining queue. The bundle retains the resulting system in a module closure, so its later Cordis `apply()` provides the identical instance as `ctx.modules` without another page global.
|
|
8
|
+
|
|
9
|
+
Resolution branch order (`import(specifier)`): platform seed word → shell instance; memoized record → exports; graph row (`window.__HYDRA_BOOT__`) → register its classic-script factory; registered factory → materialize; anything else throws — the runtime mirror of the build-time bundle purity gate. The synchronous `require` handed to factories walks the same order minus the asynchronous graph-row load and records observed edges into the module record. `prefetch` is the stage-one arrival hook (script load and factory registration only; concurrent calls share one in-flight task); `invalidate` drops a non-bootstrap factory and materialized record so the next prefetch/import reloads the script (the HMR hook).
|
|
10
|
+
|
|
11
|
+
The Node half scans enabled Loader entries for web `hydra.client` packages, resolves each `exports["./client"]`, hashes the built bundle into the boot graph, carries package-specific `hydra.client.external` requests, orders dynamic providers before consumers, and serves each bundle with its source map under `/plugins`. Source launch maps host imports to TypeScript source but still consumes this built client export; missing files share one build instruction followed by a package/path list, while unrelated filesystem errors remain separate failures.
|
|
12
|
+
|
|
13
|
+
`hydra.client.external` is an optional exact-specifier request list beyond the implicit baseline: shell-seeded React, Cordis, and static UI libraries plus parser-preloaded runtime. A request is answered by the dynamic package row it names or an exact static-table key; only a trailing `/client` aliases a package row, and there is no provider-alias declaration. Type-only imports are erased and create no request. Composition rejects malformed requests, missing suppliers, self-requests, and synchronous request cycles; import and prefetch recursively register dynamic suppliers before their consumers materialize. See [shared modules and the module graph](../AGENTS.md#shared-modules-and-the-module-graph).
|
|
14
|
+
|
|
15
|
+
## Model Experience
|
|
16
|
+
|
|
17
|
+
None, as the module loader is browser-side kernel machinery; nothing here reaches a model request.
|
|
18
|
+
|
|
19
|
+
#### KV Cache effect
|
|
20
|
+
|
|
21
|
+
None; this package neither assembles nor sends a provider request.
|
|
22
|
+
|
|
23
|
+
## Known Limitations and Deferred Work
|
|
24
|
+
|
|
25
|
+
- **Flat module graph by design** — every bundle is one module node whose edges point only at table leaves; the interface (`loadCache`/`edges`/`invalidate`) already supports a general module graph, so the externalization granularity can change without an interface change.
|
|
26
|
+
- **No unload bookkeeping of its own** — style removal and fiber teardown ordering live with the HMR driver (`@hydraharness/harness-client-hmr`); the loader only inventories owned style tag ids per record.
|
package/lib/client.js
ADDED
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
window.__ModuleLoader__.load({
|
|
2
|
+
id: "@hydraharness/harness-client-modules",
|
|
3
|
+
factory: (require) => {
|
|
4
|
+
var module = { exports: {} };
|
|
5
|
+
var exports = module.exports;
|
|
6
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
|
+
//#region lib/types/client/manifest.js
|
|
8
|
+
/**
|
|
9
|
+
* Client module system: the browser peer of Node's internal ESM loader, built
|
|
10
|
+
* as a lazy CJS table. The vendored cordis Loader consumes this object
|
|
11
|
+
* through its `internal` contract (the only call site is `EntryTree.import` →
|
|
12
|
+
* `internal.import`), which keeps entry governance (fiber lifecycle, inject
|
|
13
|
+
* waiting, update/refresh) entirely on the vendored side while this package
|
|
14
|
+
* owns code arrival.
|
|
15
|
+
*
|
|
16
|
+
* Lazy CJS model: executing a plugin bundle only REGISTERS its
|
|
17
|
+
* factory (`window.__ModuleLoader__.load({id, factory})`); every module body
|
|
18
|
+
* side effect — including CSS injection — lives inside the factory closure
|
|
19
|
+
* and runs at materialization, not at script execution. Materialization
|
|
20
|
+
* (factory(require) → exports) happens on first import/require and is
|
|
21
|
+
* memoized in {@link ClientModuleLoader.loadCache}; a factory that requires
|
|
22
|
+
* another registered-but-unmaterialized module materializes it recursively,
|
|
23
|
+
* so load order needs no external sequencing.
|
|
24
|
+
*
|
|
25
|
+
* Resolution branch order (import): seed word → shell instance; memoized
|
|
26
|
+
* record → exports; graph row → register its dependency factories and own
|
|
27
|
+
* factory; registered factory → materialize; anything else → throw (loud —
|
|
28
|
+
* the runtime mirror of the build-time bundle purity gate).
|
|
29
|
+
* The synchronous `require` handed to factories walks the same order minus
|
|
30
|
+
* the load branch. Loading is async, so a requested dynamic package must have
|
|
31
|
+
* registered its factory before a consumer materializes.
|
|
32
|
+
*
|
|
33
|
+
* This file is the browser-safe contract face (zero node imports): the
|
|
34
|
+
* `__HYDRA_BOOT__` wire types, the boot-manifest parser, and the boundaries around
|
|
35
|
+
* {@link ClientModuleSystem}. The package root is the host-side service that
|
|
36
|
+
* composes the wire.
|
|
37
|
+
*/
|
|
38
|
+
/**
|
|
39
|
+
* Validate an optional string-array field read from a `hydra.client` declaration
|
|
40
|
+
* or from the boot wire.
|
|
41
|
+
* @param subject - diagnostic prefix naming the package or the wire row.
|
|
42
|
+
* @param field - field name as it appears in the diagnostic.
|
|
43
|
+
* @param value - the raw field value.
|
|
44
|
+
* @returns the validated array, or undefined when the field is absent.
|
|
45
|
+
* @throws {Error} when the value is present but is not an array of strings.
|
|
46
|
+
*/
|
|
47
|
+
function optionalStringArray(subject, field, value) {
|
|
48
|
+
if (value === void 0) return void 0;
|
|
49
|
+
if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) throw new Error(`client-modules: ${subject} ${field} must be a string array`);
|
|
50
|
+
return value;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Normalize a module specifier onto the graph row that owns it: a plugin bundle
|
|
54
|
+
* IS its package's client half, so `<id>/client` (the exports subpath external
|
|
55
|
+
* bundles emit) and the bare package name resolve to the same exports. Both the
|
|
56
|
+
* require path and graph composition normalize here, which is what lets each
|
|
57
|
+
* importing package request the subpath its own code imports.
|
|
58
|
+
* @param spec - module specifier as a bundle requires it or a declaration spells it.
|
|
59
|
+
* @returns the specifier with a trailing `/client` removed.
|
|
60
|
+
*/
|
|
61
|
+
function stripClientSuffix(spec) {
|
|
62
|
+
return spec.endsWith("/client") ? spec.slice(0, -7) : spec;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Parse `window.__HYDRA_BOOT__` into the two consumer views. Wire boundary:
|
|
66
|
+
* a missing or malformed graph throws (the shell shows the loud failure —
|
|
67
|
+
* a page without a valid manifest cannot boot anything).
|
|
68
|
+
* @param wire - the raw `window.__HYDRA_BOOT__` value.
|
|
69
|
+
* @returns the manifest with optional plugin-view fields normalized.
|
|
70
|
+
*/
|
|
71
|
+
function parseBootManifest(wire) {
|
|
72
|
+
if (typeof wire !== "object" || wire === null) throw new Error("client-modules: window.__HYDRA_BOOT__ is missing or not an object");
|
|
73
|
+
const graph = wire;
|
|
74
|
+
if (typeof graph.rev !== "string") throw new Error("client-modules: boot manifest rev must be a string");
|
|
75
|
+
if (!Array.isArray(graph.entries)) throw new Error("client-modules: boot manifest entries must be an array");
|
|
76
|
+
const modules = [];
|
|
77
|
+
const plugins = [];
|
|
78
|
+
for (const value of graph.entries) {
|
|
79
|
+
if (typeof value !== "object" || value === null) throw new Error("client-modules: boot manifest entry is not an object");
|
|
80
|
+
const row = value;
|
|
81
|
+
const where = typeof row.id === "string" ? `"${row.id}"` : JSON.stringify(row);
|
|
82
|
+
if (typeof row.id !== "string" || typeof row.url !== "string" || typeof row.rev !== "string") throw new Error(`client-modules: boot manifest entry ${where} must carry string id/url/rev`);
|
|
83
|
+
const subject = `boot manifest entry ${where}`;
|
|
84
|
+
const inject = optionalStringArray(subject, "inject", row.inject);
|
|
85
|
+
const external = optionalStringArray(subject, "external", row.external);
|
|
86
|
+
if (row.immediately !== void 0 && typeof row.immediately !== "boolean") throw new Error(`client-modules: boot manifest entry ${where} immediately must be a boolean`);
|
|
87
|
+
modules.push({
|
|
88
|
+
id: row.id,
|
|
89
|
+
url: row.url,
|
|
90
|
+
rev: row.rev,
|
|
91
|
+
external: external === void 0 ? [] : [...external]
|
|
92
|
+
});
|
|
93
|
+
plugins.push({
|
|
94
|
+
id: row.id,
|
|
95
|
+
inject: inject === void 0 ? [] : [...inject],
|
|
96
|
+
immediately: row.immediately === true
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
rev: graph.rev,
|
|
101
|
+
modules,
|
|
102
|
+
plugins
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
//#endregion
|
|
106
|
+
//#region lib/types/client/system.js
|
|
107
|
+
/**
|
|
108
|
+
* ClientModuleSystem — the implementation behind the {@link ClientModuleLoader}
|
|
109
|
+
* contract. The conceptual contract (lazy CJS model, resolution branch order) is
|
|
110
|
+
* documented on the public interfaces in `./manifest.ts`; this file owns the
|
|
111
|
+
* state tables and the load/materialize machinery.
|
|
112
|
+
*/
|
|
113
|
+
/** Default bundle-load hook: same-origin external classic script. */
|
|
114
|
+
const defaultLoadBundle = (url) => new Promise((resolve, reject) => {
|
|
115
|
+
const el = document.createElement("script");
|
|
116
|
+
el.async = true;
|
|
117
|
+
el.src = url;
|
|
118
|
+
el.addEventListener("load", () => {
|
|
119
|
+
el.remove();
|
|
120
|
+
resolve();
|
|
121
|
+
}, { once: true });
|
|
122
|
+
el.addEventListener("error", () => {
|
|
123
|
+
el.remove();
|
|
124
|
+
reject(/* @__PURE__ */ new Error(`client-modules: bundle script ${url} failed to load`));
|
|
125
|
+
}, { once: true });
|
|
126
|
+
document.head.append(el);
|
|
127
|
+
});
|
|
128
|
+
/**
|
|
129
|
+
* Claim and inventory the <style> tags a factory injected during
|
|
130
|
+
* materialization: preset-emitted tags arrive pre-tagged with data-plugin;
|
|
131
|
+
* any untagged tag is claimed for the materializing plugin (HMR bookkeeping).
|
|
132
|
+
*/
|
|
133
|
+
const claimStyles = (id) => {
|
|
134
|
+
if (typeof document === "undefined") return [];
|
|
135
|
+
for (const el of document.querySelectorAll("style:not([data-plugin])")) el.setAttribute("data-plugin", id);
|
|
136
|
+
const owned = [];
|
|
137
|
+
for (const el of document.querySelectorAll(`style[data-plugin=${JSON.stringify(id)}]`)) owned.push(el.getAttribute("data-plugin-css") ?? id);
|
|
138
|
+
return owned;
|
|
139
|
+
};
|
|
140
|
+
/**
|
|
141
|
+
* The client module system: state tables plus the arrival/materialization
|
|
142
|
+
* machinery implementing {@link ClientModuleLoader} (whose members carry the
|
|
143
|
+
* contract documentation). Construction indexes the boot rows, retains the
|
|
144
|
+
* already-materialized bootstrap module, and switches the HTML-installed
|
|
145
|
+
* loader facade from its pending queue to live registration.
|
|
146
|
+
*/
|
|
147
|
+
var ClientModuleSystem = class {
|
|
148
|
+
version = "client";
|
|
149
|
+
manifest;
|
|
150
|
+
loadCache = /* @__PURE__ */ new Map();
|
|
151
|
+
seed;
|
|
152
|
+
factories = /* @__PURE__ */ new Map();
|
|
153
|
+
bootstrapIds = /* @__PURE__ */ new Set();
|
|
154
|
+
/** In-flight prefetch (script load) per id; concurrent callers share it. */
|
|
155
|
+
pendingArrival = /* @__PURE__ */ new Map();
|
|
156
|
+
/** Materialization re-entrancy guard: factory-form CJS cannot deliver partial exports, so a cycle is fatal. */
|
|
157
|
+
materializing = /* @__PURE__ */ new Set();
|
|
158
|
+
graphRows = /* @__PURE__ */ new Map();
|
|
159
|
+
loadBundle;
|
|
160
|
+
/**
|
|
161
|
+
* Build the module system over the parsed boot rows.
|
|
162
|
+
* @param options - Parsed graph, platform seed, bootstrap module, registration facade, and transport.
|
|
163
|
+
*/
|
|
164
|
+
constructor(options) {
|
|
165
|
+
this.manifest = options.manifest;
|
|
166
|
+
this.seed = new Map(Object.entries(options.staticModules));
|
|
167
|
+
this.loadBundle = options.loadBundle ?? defaultLoadBundle;
|
|
168
|
+
for (const row of options.manifest.modules) {
|
|
169
|
+
if (this.graphRows.has(row.id)) throw new Error(`client-modules: duplicate graph entry "${row.id}"`);
|
|
170
|
+
this.graphRows.set(row.id, row);
|
|
171
|
+
}
|
|
172
|
+
const bootstrapId = stripClientSuffix(options.bootstrapModule.id);
|
|
173
|
+
this.bootstrapIds.add(bootstrapId);
|
|
174
|
+
this.loadCache.set(bootstrapId, {
|
|
175
|
+
id: bootstrapId,
|
|
176
|
+
exports: options.bootstrapModule.exports,
|
|
177
|
+
styles: [],
|
|
178
|
+
edges: /* @__PURE__ */ new Set()
|
|
179
|
+
});
|
|
180
|
+
const target = options.registrationTarget;
|
|
181
|
+
if (target.mode !== "queue") throw new Error("client-modules: window.__ModuleLoader__.create called after module-system boot");
|
|
182
|
+
const pending = target.pendingQueue.splice(0);
|
|
183
|
+
target.mode = "live";
|
|
184
|
+
target.load = (registration) => {
|
|
185
|
+
this.register(registration);
|
|
186
|
+
};
|
|
187
|
+
for (const registration of pending) target.load(registration);
|
|
188
|
+
}
|
|
189
|
+
/** Register one bundle factory, rejecting a script that executes twice without invalidation. */
|
|
190
|
+
register(registration) {
|
|
191
|
+
const id = stripClientSuffix(registration.id);
|
|
192
|
+
if (this.bootstrapIds.has(id) || this.factories.has(id)) throw new Error(`client-modules: duplicate factory registration for "${registration.id}" (bundle executed twice without invalidate?)`);
|
|
193
|
+
this.factories.set(id, registration.factory);
|
|
194
|
+
}
|
|
195
|
+
/** Load one graph row so its factory is registered (idempotent per in-flight arrival). */
|
|
196
|
+
arrive(row) {
|
|
197
|
+
const { id, url } = row;
|
|
198
|
+
const pending = this.pendingArrival.get(id);
|
|
199
|
+
if (pending !== void 0) return pending;
|
|
200
|
+
if (this.loadCache.has(id) || this.factories.has(id)) return Promise.resolve();
|
|
201
|
+
const task = this.loadBundle(url).then(() => {
|
|
202
|
+
if (!this.factories.has(id)) throw new Error(`client-modules: bundle ${url} loaded without registering "${id}" via __ModuleLoader__.load`);
|
|
203
|
+
}).finally(() => {
|
|
204
|
+
this.pendingArrival.delete(id);
|
|
205
|
+
});
|
|
206
|
+
this.pendingArrival.set(id, task);
|
|
207
|
+
return task;
|
|
208
|
+
}
|
|
209
|
+
/** Register each unresolved dynamic request before registering its consumer. */
|
|
210
|
+
async arriveGraphRow(row, open = []) {
|
|
211
|
+
const cycleStart = open.indexOf(row.id);
|
|
212
|
+
if (cycleStart !== -1) throw new Error(`client-modules: module arrival cycle ${[...open.slice(cycleStart), row.id].join(" -> ")} (the host must reject this graph before serving it)`);
|
|
213
|
+
const next = [...open, row.id];
|
|
214
|
+
for (const request of row.external) {
|
|
215
|
+
const id = stripClientSuffix(request);
|
|
216
|
+
if (this.seed.has(request) || this.loadCache.has(id)) continue;
|
|
217
|
+
const dependency = this.graphRows.get(id);
|
|
218
|
+
if (dependency !== void 0) await this.arriveGraphRow(dependency, next);
|
|
219
|
+
}
|
|
220
|
+
await this.arrive(row);
|
|
221
|
+
}
|
|
222
|
+
/** Materialize a registered factory (synchronous; memoized in loadCache). */
|
|
223
|
+
materialize(id) {
|
|
224
|
+
const existing = this.loadCache.get(id);
|
|
225
|
+
if (existing !== void 0) return existing;
|
|
226
|
+
const registered = this.factories.get(id);
|
|
227
|
+
/* v8 ignore next -- callers check the factory branch before dispatching here. */
|
|
228
|
+
if (registered === void 0) throw new Error(`client-modules: no registered factory for "${id}"`);
|
|
229
|
+
if (this.materializing.has(id)) throw new Error(`client-modules: require cycle through "${id}" (factory-form CJS cannot deliver partial exports)`);
|
|
230
|
+
this.materializing.add(id);
|
|
231
|
+
try {
|
|
232
|
+
const edges = /* @__PURE__ */ new Set();
|
|
233
|
+
const record = {
|
|
234
|
+
id,
|
|
235
|
+
exports: registered(this.makeRequire(edges)),
|
|
236
|
+
styles: claimStyles(id),
|
|
237
|
+
edges
|
|
238
|
+
};
|
|
239
|
+
this.loadCache.set(id, record);
|
|
240
|
+
return record;
|
|
241
|
+
} finally {
|
|
242
|
+
this.materializing.delete(id);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* The synchronous require answered to factories: seed → memoized record →
|
|
247
|
+
* registered factory. Fetching is async and therefore unreachable
|
|
248
|
+
* from here; an external dynamic package must have arrived before its
|
|
249
|
+
* consumer materializes.
|
|
250
|
+
*/
|
|
251
|
+
makeRequire(edges) {
|
|
252
|
+
return (spec) => {
|
|
253
|
+
edges.add(spec);
|
|
254
|
+
if (this.seed.has(spec)) return this.seed.get(spec);
|
|
255
|
+
const id = stripClientSuffix(spec);
|
|
256
|
+
const record = this.loadCache.get(id);
|
|
257
|
+
if (record !== void 0) return record.exports;
|
|
258
|
+
if (this.factories.has(id)) return this.materialize(id).exports;
|
|
259
|
+
throw new Error(`client-modules: require("${spec}") missed the module table — not a platform seed word, not a materialized module, and no registered package factory (a build-time externals drift, or a dynamic dependency that did not arrive)`);
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
async import(specifier) {
|
|
263
|
+
if (this.seed.has(specifier)) return this.seed.get(specifier);
|
|
264
|
+
const id = stripClientSuffix(specifier);
|
|
265
|
+
const existing = this.loadCache.get(id);
|
|
266
|
+
if (existing !== void 0) return existing.exports;
|
|
267
|
+
const row = this.graphRows.get(id);
|
|
268
|
+
if (row !== void 0) await this.arriveGraphRow(row);
|
|
269
|
+
else if (!this.factories.has(id)) throw new Error(`client-modules: cannot resolve "${specifier}" — not a seed word, not a materialized module, and not a row in the boot graph (the runtime mirror of the bundle purity gate)`);
|
|
270
|
+
return this.materialize(id).exports;
|
|
271
|
+
}
|
|
272
|
+
async prefetch(id) {
|
|
273
|
+
const normalized = stripClientSuffix(id);
|
|
274
|
+
if (this.loadCache.has(normalized)) return;
|
|
275
|
+
const row = this.graphRows.get(normalized);
|
|
276
|
+
if (row === void 0) throw new Error(`client-modules: prefetch("${id}") — not a graph entry`);
|
|
277
|
+
await this.arriveGraphRow(row);
|
|
278
|
+
}
|
|
279
|
+
invalidate(id) {
|
|
280
|
+
const normalized = stripClientSuffix(id);
|
|
281
|
+
if (this.bootstrapIds.has(normalized)) return;
|
|
282
|
+
this.factories.delete(normalized);
|
|
283
|
+
this.loadCache.delete(normalized);
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
//#endregion
|
|
287
|
+
//#region lib/types/client/index.js
|
|
288
|
+
let moduleSystem;
|
|
289
|
+
/**
|
|
290
|
+
* Build the live module system from the HTML facade's materialized modules bundle.
|
|
291
|
+
* @param target - Stable registration facade whose pending queue becomes the live sink.
|
|
292
|
+
* @param bootstrapModule - This bundle's id and already-materialized exports.
|
|
293
|
+
* @param options - Raw boot graph, platform seed, and optional bundle transport.
|
|
294
|
+
* @returns The created module system, also published for this package's Cordis plugin face.
|
|
295
|
+
*/
|
|
296
|
+
function createClientModuleSystem(target, bootstrapModule, options) {
|
|
297
|
+
moduleSystem = new ClientModuleSystem({
|
|
298
|
+
manifest: parseBootManifest(options.boot),
|
|
299
|
+
staticModules: options.staticModules,
|
|
300
|
+
registrationTarget: target,
|
|
301
|
+
bootstrapModule,
|
|
302
|
+
...options.loadBundle === void 0 ? {} : { loadBundle: options.loadBundle }
|
|
303
|
+
});
|
|
304
|
+
return moduleSystem;
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Enroll the kernel-built module system as `ctx.modules`.
|
|
308
|
+
* @param ctx - client root context.
|
|
309
|
+
*/
|
|
310
|
+
function apply(ctx) {
|
|
311
|
+
if (moduleSystem === void 0) throw new Error("client-modules: createClientModuleSystem must run before plugin boot");
|
|
312
|
+
ctx.reflect.provide("modules", moduleSystem);
|
|
313
|
+
}
|
|
314
|
+
//#endregion
|
|
315
|
+
exports.ClientModuleSystem = ClientModuleSystem;
|
|
316
|
+
exports.apply = apply;
|
|
317
|
+
exports.createClientModuleSystem = createClientModuleSystem;
|
|
318
|
+
exports.parseBootManifest = parseBootManifest;
|
|
319
|
+
exports.stripClientSuffix = stripClientSuffix;
|
|
320
|
+
return module.exports;
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
//# sourceMappingURL=client.js.map
|