@async/framework 0.8.0 → 0.10.0
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/CHANGELOG.md +18 -0
- package/README.md +156 -0
- package/browser.d.ts +119 -18
- package/browser.js +1044 -71
- package/browser.min.js +1 -1
- package/browser.ts +1044 -71
- package/browser.umd.js +1044 -71
- package/browser.umd.min.js +1 -1
- package/package.json +1 -1
- package/server.d.ts +119 -18
- package/src/app.js +314 -46
- package/src/boundary-receiver.js +302 -0
- package/src/browser.js +3 -0
- package/src/component.js +19 -2
- package/src/elements.js +63 -0
- package/src/handlers.js +19 -2
- package/src/index.js +3 -0
- package/src/lazy-registry.js +204 -0
- package/src/loader.js +23 -5
- package/src/partials.js +19 -2
- package/src/registry-store.js +15 -9
- package/src/scheduler.js +4 -0
- package/src/signals.js +46 -4
package/browser.umd.js
CHANGED
|
@@ -293,8 +293,217 @@
|
|
|
293
293
|
return { asyncSignal, isAsyncSignal };
|
|
294
294
|
})();
|
|
295
295
|
|
|
296
|
+
const __lazyRegistryModule = (() => {
|
|
297
|
+
const descriptorTypes = new Set(["handler", "component", "asyncSignal", "partial", "route"]);
|
|
298
|
+
const defaultBaseUrl = "_async";
|
|
299
|
+
|
|
300
|
+
function defineRegistrySnapshot(snapshot = {}) {
|
|
301
|
+
if (!snapshot || typeof snapshot !== "object" || Array.isArray(snapshot)) {
|
|
302
|
+
throw new TypeError("defineRegistrySnapshot(snapshot) requires an object.");
|
|
303
|
+
}
|
|
304
|
+
return snapshot;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function createLazyRegistry(options = {}) {
|
|
308
|
+
const registryAssets = normalizeRegistryAssets(options.registryAssets ?? options.assets);
|
|
309
|
+
const importModule = options.importModule ?? ((url) => import(url));
|
|
310
|
+
const moduleCache = new Map();
|
|
311
|
+
const exportCache = new Map();
|
|
312
|
+
|
|
313
|
+
return {
|
|
314
|
+
registryAssets,
|
|
315
|
+
|
|
316
|
+
resolveUrl(type, id, descriptor) {
|
|
317
|
+
return resolveDescriptorUrl(type, id, descriptor, registryAssets);
|
|
318
|
+
},
|
|
319
|
+
|
|
320
|
+
async resolve(type, id, descriptor) {
|
|
321
|
+
if (!isLazyDescriptor(descriptor)) {
|
|
322
|
+
return descriptor;
|
|
323
|
+
}
|
|
324
|
+
const cacheKey = `${type}:${id}`;
|
|
325
|
+
if (exportCache.has(cacheKey)) {
|
|
326
|
+
return exportCache.get(cacheKey);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const resolved = resolveDescriptorUrl(type, id, descriptor, registryAssets);
|
|
330
|
+
let modulePromise = moduleCache.get(resolved.moduleUrl);
|
|
331
|
+
if (!modulePromise) {
|
|
332
|
+
modulePromise = Promise.resolve(importModule(resolved.moduleUrl));
|
|
333
|
+
moduleCache.set(resolved.moduleUrl, modulePromise);
|
|
334
|
+
}
|
|
335
|
+
const module = await modulePromise;
|
|
336
|
+
const value = resolveExport(module, resolved.exportNames, type, id);
|
|
337
|
+
exportCache.set(cacheKey, value);
|
|
338
|
+
return value;
|
|
339
|
+
},
|
|
340
|
+
|
|
341
|
+
inspect() {
|
|
342
|
+
return {
|
|
343
|
+
registryAssets,
|
|
344
|
+
modules: [...moduleCache.keys()],
|
|
345
|
+
exports: [...exportCache.keys()]
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function normalizeRegistryAssets(options = {}) {
|
|
352
|
+
const baseUrl = normalizeBaseUrl(options.baseUrl ?? defaultBaseUrl);
|
|
353
|
+
const paths = {
|
|
354
|
+
component: "component",
|
|
355
|
+
handler: "handler",
|
|
356
|
+
asyncSignal: "asyncSignal",
|
|
357
|
+
partial: "partial",
|
|
358
|
+
route: "route",
|
|
359
|
+
...(options.paths ?? {})
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
for (const [type, value] of Object.entries(paths)) {
|
|
363
|
+
if (!descriptorTypes.has(type)) {
|
|
364
|
+
continue;
|
|
365
|
+
}
|
|
366
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
367
|
+
throw new TypeError(`Registry asset path for "${type}" must be a non-empty string.`);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
return {
|
|
372
|
+
baseUrl,
|
|
373
|
+
paths
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
function isLazyDescriptor(value) {
|
|
378
|
+
return Boolean(
|
|
379
|
+
value &&
|
|
380
|
+
typeof value === "object" &&
|
|
381
|
+
!Array.isArray(value) &&
|
|
382
|
+
typeof value.url === "string"
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function sameRegistryValue(left, right) {
|
|
387
|
+
if (left === right) {
|
|
388
|
+
return true;
|
|
389
|
+
}
|
|
390
|
+
if (isLazyDescriptor(left) && isLazyDescriptor(right)) {
|
|
391
|
+
return stableStringify(left) === stableStringify(right);
|
|
392
|
+
}
|
|
393
|
+
return false;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
function publicRegistryValue(value, id) {
|
|
397
|
+
if (isLazyDescriptor(value)) {
|
|
398
|
+
return { ...value };
|
|
399
|
+
}
|
|
400
|
+
return { id };
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
function resolveDescriptorUrl(type, id, descriptor, registryAssets) {
|
|
404
|
+
if (!descriptorTypes.has(type)) {
|
|
405
|
+
throw new Error(`Registry type "${type}" does not support lazy descriptors.`);
|
|
406
|
+
}
|
|
407
|
+
if (!isLazyDescriptor(descriptor)) {
|
|
408
|
+
throw new TypeError(`Registry descriptor for "${type}:${id}" requires a url.`);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
const { path, hash } = splitHash(descriptor.url);
|
|
412
|
+
const moduleUrl = resolveModuleUrl(type, path, registryAssets);
|
|
413
|
+
const exportNames = hash
|
|
414
|
+
? [hash]
|
|
415
|
+
: inferredExportNames(id, path);
|
|
416
|
+
|
|
417
|
+
return {
|
|
418
|
+
moduleUrl,
|
|
419
|
+
exportNames,
|
|
420
|
+
url: hash ? `${moduleUrl}#${hash}` : moduleUrl
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
function resolveModuleUrl(type, path, registryAssets) {
|
|
425
|
+
if (isAbsoluteUrl(path) || path.startsWith("/") || path.startsWith("./") || path.startsWith("../")) {
|
|
426
|
+
return path;
|
|
427
|
+
}
|
|
428
|
+
const typePath = registryAssets.paths[type] ?? type;
|
|
429
|
+
return joinUrl(registryAssets.baseUrl, typePath, path);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
function resolveExport(module, exportNames, type, id) {
|
|
433
|
+
for (const name of exportNames) {
|
|
434
|
+
if (name in module) {
|
|
435
|
+
return module[name];
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
throw new Error(`Lazy ${type} "${id}" did not export ${exportNames.map((name) => `"${name}"`).join(", ")}.`);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
function inferredExportNames(id, path) {
|
|
442
|
+
const names = [];
|
|
443
|
+
const leaf = id.split(".").filter(Boolean).at(-1);
|
|
444
|
+
const basename = path
|
|
445
|
+
.split("/")
|
|
446
|
+
.filter(Boolean)
|
|
447
|
+
.at(-1)
|
|
448
|
+
?.replace(/\.[^.]+$/, "");
|
|
449
|
+
for (const name of [leaf, basename, "default"]) {
|
|
450
|
+
if (name && !names.includes(name)) {
|
|
451
|
+
names.push(name);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
return names;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
function splitHash(url) {
|
|
458
|
+
const index = url.indexOf("#");
|
|
459
|
+
if (index === -1) {
|
|
460
|
+
return { path: url, hash: "" };
|
|
461
|
+
}
|
|
462
|
+
return {
|
|
463
|
+
path: url.slice(0, index),
|
|
464
|
+
hash: url.slice(index + 1)
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
function normalizeBaseUrl(baseUrl) {
|
|
469
|
+
if (typeof baseUrl !== "string" || baseUrl.length === 0) {
|
|
470
|
+
throw new TypeError("registryAssets.baseUrl must be a non-empty string.");
|
|
471
|
+
}
|
|
472
|
+
if (isAbsoluteUrl(baseUrl) || baseUrl.startsWith("/") || baseUrl.startsWith("./") || baseUrl.startsWith("../")) {
|
|
473
|
+
return stripTrailingSlash(baseUrl);
|
|
474
|
+
}
|
|
475
|
+
return `/${stripSlashes(baseUrl)}`;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
function joinUrl(...parts) {
|
|
479
|
+
const [first, ...rest] = parts;
|
|
480
|
+
return [stripTrailingSlash(first), ...rest.map(stripSlashes)].filter(Boolean).join("/");
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
function stripSlashes(value) {
|
|
484
|
+
return String(value).replace(/^\/+|\/+$/g, "");
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
function stripTrailingSlash(value) {
|
|
488
|
+
return String(value).replace(/\/+$/g, "");
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
function isAbsoluteUrl(value) {
|
|
492
|
+
return /^[A-Za-z][A-Za-z\d+.-]*:/.test(value);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
function stableStringify(value) {
|
|
496
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
497
|
+
return JSON.stringify(value);
|
|
498
|
+
}
|
|
499
|
+
return JSON.stringify(Object.keys(value).sort().map((key) => [key, value[key]]));
|
|
500
|
+
}
|
|
501
|
+
return { defineRegistrySnapshot, createLazyRegistry, normalizeRegistryAssets, isLazyDescriptor, sameRegistryValue, publicRegistryValue };
|
|
502
|
+
})();
|
|
503
|
+
|
|
296
504
|
const __registryStoreModule = (() => {
|
|
297
|
-
const
|
|
505
|
+
const { publicRegistryValue } = __lazyRegistryModule;
|
|
506
|
+
const declarationTypes = new Set(["signal", "handler", "server", "partial", "route", "component", "asyncSignal"]);
|
|
298
507
|
const cacheTypes = new Set(["cache.browser", "cache.server"]);
|
|
299
508
|
const cacheEntryTypes = new Set(["cache.browser.entries", "cache.server.entries"]);
|
|
300
509
|
const allTypes = new Set([...declarationTypes, ...cacheTypes, ...cacheEntryTypes]);
|
|
@@ -381,11 +590,12 @@
|
|
|
381
590
|
const snapshotTarget = snapshotOptions.target ?? target;
|
|
382
591
|
return {
|
|
383
592
|
signal: snapshotSignals(backing.signal),
|
|
384
|
-
handler: snapshotDescriptors(backing.handler
|
|
385
|
-
server: snapshotDescriptors(backing.server
|
|
386
|
-
partial: snapshotDescriptors(backing.partial
|
|
593
|
+
handler: snapshotDescriptors(backing.handler),
|
|
594
|
+
server: snapshotDescriptors(backing.server),
|
|
595
|
+
partial: snapshotDescriptors(backing.partial),
|
|
387
596
|
route: snapshotPlain(backing.route),
|
|
388
|
-
component: snapshotDescriptors(backing.component
|
|
597
|
+
component: snapshotDescriptors(backing.component),
|
|
598
|
+
asyncSignal: snapshotDescriptors(backing.asyncSignal),
|
|
389
599
|
cache: {
|
|
390
600
|
browser: snapshotPlain(backing.cache.browser),
|
|
391
601
|
server: snapshotPlain(backing.cache.server)
|
|
@@ -405,6 +615,7 @@
|
|
|
405
615
|
partial: Object.fromEntries(backing.partial),
|
|
406
616
|
route: Object.fromEntries(backing.route),
|
|
407
617
|
component: Object.fromEntries(backing.component),
|
|
618
|
+
asyncSignal: Object.fromEntries(backing.asyncSignal),
|
|
408
619
|
cache: {
|
|
409
620
|
browser: Object.fromEntries(backing.cache.browser),
|
|
410
621
|
server: Object.fromEntries(backing.cache.server)
|
|
@@ -464,6 +675,7 @@
|
|
|
464
675
|
partial: new Map(),
|
|
465
676
|
route: new Map(),
|
|
466
677
|
component: new Map(),
|
|
678
|
+
asyncSignal: new Map(),
|
|
467
679
|
cache: {
|
|
468
680
|
browser: new Map(),
|
|
469
681
|
server: new Map()
|
|
@@ -482,6 +694,7 @@
|
|
|
482
694
|
registry.registerMany("partial", initial.partial);
|
|
483
695
|
registry.registerMany("route", initial.route);
|
|
484
696
|
registry.registerMany("component", initial.component);
|
|
697
|
+
registry.registerMany("asyncSignal", initial.asyncSignal);
|
|
485
698
|
registry.registerMany("cache.browser", initial.cache?.browser);
|
|
486
699
|
registry.registerMany("cache.server", initial.cache?.server);
|
|
487
700
|
|
|
@@ -509,7 +722,7 @@
|
|
|
509
722
|
|
|
510
723
|
function publicValue(type, id, value, options) {
|
|
511
724
|
if (type === "server" && options.target === "browser") {
|
|
512
|
-
return
|
|
725
|
+
return publicRegistryValue(value, id);
|
|
513
726
|
}
|
|
514
727
|
if (cacheEntryTypes.has(type)) {
|
|
515
728
|
return value?.value;
|
|
@@ -529,10 +742,10 @@
|
|
|
529
742
|
return snapshot;
|
|
530
743
|
}
|
|
531
744
|
|
|
532
|
-
function snapshotDescriptors(map
|
|
745
|
+
function snapshotDescriptors(map) {
|
|
533
746
|
const snapshot = {};
|
|
534
|
-
for (const id of map
|
|
535
|
-
snapshot[id] =
|
|
747
|
+
for (const [id, value] of map) {
|
|
748
|
+
snapshot[id] = publicRegistryValue(value, id);
|
|
536
749
|
}
|
|
537
750
|
return snapshot;
|
|
538
751
|
}
|
|
@@ -809,6 +1022,7 @@
|
|
|
809
1022
|
const __signalsModule = (() => {
|
|
810
1023
|
const { asyncSignal: createAsyncSignal, isAsyncSignal } = __asyncSignalModule;
|
|
811
1024
|
const { attachRegistryInspection, createRegistryStore } = __registryStoreModule;
|
|
1025
|
+
const { createLazyRegistry, isLazyDescriptor } = __lazyRegistryModule;
|
|
812
1026
|
const signalKind = Symbol.for("@async/framework.signal");
|
|
813
1027
|
const computedKind = Symbol.for("@async/framework.computed");
|
|
814
1028
|
const effectKind = Symbol.for("@async/framework.effect");
|
|
@@ -930,6 +1144,8 @@
|
|
|
930
1144
|
const registryStore = options.registry ?? createRegistryStore();
|
|
931
1145
|
const type = options.type ?? "signal";
|
|
932
1146
|
const entries = registryStore._map(type);
|
|
1147
|
+
const asyncDescriptors = registryStore._map("asyncSignal");
|
|
1148
|
+
const lazyRegistry = options.lazyRegistry ?? createLazyRegistry(options);
|
|
933
1149
|
const registryCleanups = new Map();
|
|
934
1150
|
const runtimeContext = {};
|
|
935
1151
|
const boundEntries = new Set();
|
|
@@ -970,6 +1186,7 @@
|
|
|
970
1186
|
|
|
971
1187
|
ensure(id, initial) {
|
|
972
1188
|
assertId(id);
|
|
1189
|
+
materializeAsyncSignal(id);
|
|
973
1190
|
if (!entries.has(id)) {
|
|
974
1191
|
registry.register(id, createSignal(initial));
|
|
975
1192
|
}
|
|
@@ -977,18 +1194,18 @@
|
|
|
977
1194
|
},
|
|
978
1195
|
|
|
979
1196
|
has(id) {
|
|
980
|
-
return entries.has(id);
|
|
1197
|
+
return entries.has(id) || asyncDescriptors.has(id);
|
|
981
1198
|
},
|
|
982
1199
|
|
|
983
1200
|
get(path) {
|
|
984
|
-
const parsed =
|
|
1201
|
+
const parsed = parseRegistryPath(path);
|
|
985
1202
|
track(parsed.path);
|
|
986
1203
|
const entry = requireEntry(entries, parsed.id);
|
|
987
1204
|
return readEntry(entry, parsed.parts);
|
|
988
1205
|
},
|
|
989
1206
|
|
|
990
1207
|
set(path, value) {
|
|
991
|
-
const parsed =
|
|
1208
|
+
const parsed = parseRegistryPath(path);
|
|
992
1209
|
const entry = requireEntry(entries, parsed.id);
|
|
993
1210
|
if (parsed.parts.length === 0) {
|
|
994
1211
|
return entry.set(value);
|
|
@@ -1007,6 +1224,7 @@
|
|
|
1007
1224
|
|
|
1008
1225
|
ref(id) {
|
|
1009
1226
|
assertId(id);
|
|
1227
|
+
materializeAsyncSignal(id);
|
|
1010
1228
|
return createRef(registry, id);
|
|
1011
1229
|
},
|
|
1012
1230
|
|
|
@@ -1014,7 +1232,7 @@
|
|
|
1014
1232
|
if (typeof fn !== "function") {
|
|
1015
1233
|
throw new TypeError("subscribe(path, fn) requires a function.");
|
|
1016
1234
|
}
|
|
1017
|
-
const parsed =
|
|
1235
|
+
const parsed = parseRegistryPath(path);
|
|
1018
1236
|
const entry = requireEntry(entries, parsed.id);
|
|
1019
1237
|
const subscriptionId = ++subscriptionCounter;
|
|
1020
1238
|
return entry.subscribe(() => {
|
|
@@ -1120,6 +1338,7 @@
|
|
|
1120
1338
|
},
|
|
1121
1339
|
|
|
1122
1340
|
_entry(id) {
|
|
1341
|
+
materializeAsyncSignal(id);
|
|
1123
1342
|
return requireEntry(entries, id);
|
|
1124
1343
|
},
|
|
1125
1344
|
|
|
@@ -1159,6 +1378,42 @@
|
|
|
1159
1378
|
}
|
|
1160
1379
|
}
|
|
1161
1380
|
|
|
1381
|
+
function parseRegistryPath(path) {
|
|
1382
|
+
if (typeof path !== "string" || path.length === 0) {
|
|
1383
|
+
throw new TypeError("Signal path must be a non-empty string.");
|
|
1384
|
+
}
|
|
1385
|
+
const segments = path.split(".");
|
|
1386
|
+
for (let end = segments.length; end > 0; end -= 1) {
|
|
1387
|
+
const id = segments.slice(0, end).join(".");
|
|
1388
|
+
if (entries.has(id) || asyncDescriptors.has(id)) {
|
|
1389
|
+
materializeAsyncSignal(id);
|
|
1390
|
+
return { id, parts: segments.slice(end), path };
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
const [id, ...parts] = segments;
|
|
1394
|
+
return { id, parts, path };
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
function materializeAsyncSignal(id) {
|
|
1398
|
+
if (entries.has(id) || !asyncDescriptors.has(id)) {
|
|
1399
|
+
return;
|
|
1400
|
+
}
|
|
1401
|
+
const descriptor = asyncDescriptors.get(id);
|
|
1402
|
+
if (!isLazyDescriptor(descriptor) && typeof descriptor !== "function") {
|
|
1403
|
+
throw new TypeError(`Async signal "${id}" must be a function or lazy descriptor.`);
|
|
1404
|
+
}
|
|
1405
|
+
const loader = async function runLazyAsyncSignal(...args) {
|
|
1406
|
+
const resolved = await lazyRegistry.resolve("asyncSignal", id, descriptor);
|
|
1407
|
+
if (typeof resolved !== "function") {
|
|
1408
|
+
throw new TypeError(`Async signal "${id}" did not resolve to a function.`);
|
|
1409
|
+
}
|
|
1410
|
+
return resolved.apply(this, args);
|
|
1411
|
+
};
|
|
1412
|
+
const entry = createAsyncSignal(id, loader);
|
|
1413
|
+
entries.set(id, entry);
|
|
1414
|
+
bindEntry(id, entry);
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1162
1417
|
function scheduleCallback(fn, options = {}) {
|
|
1163
1418
|
const scheduler = options.scheduler;
|
|
1164
1419
|
if (!scheduler || options.phase === "sync") {
|
|
@@ -1524,6 +1779,7 @@
|
|
|
1524
1779
|
const { attributeName } = __attributesModule;
|
|
1525
1780
|
const { escapeHtml, rawHtml, renderTemplate } = __htmlModule;
|
|
1526
1781
|
const { attachRegistryInspection, createRegistryStore } = __registryStoreModule;
|
|
1782
|
+
const { createLazyRegistry, isLazyDescriptor } = __lazyRegistryModule;
|
|
1527
1783
|
const componentKind = Symbol.for("@async/framework.component");
|
|
1528
1784
|
let componentCounter = 0;
|
|
1529
1785
|
|
|
@@ -1544,13 +1800,15 @@
|
|
|
1544
1800
|
const registryStore = options.registry ?? createRegistryStore();
|
|
1545
1801
|
const type = options.type ?? "component";
|
|
1546
1802
|
const entries = registryStore._map(type);
|
|
1803
|
+
const lazyRegistry = options.lazyRegistry ?? createLazyRegistry(options);
|
|
1804
|
+
const lazyComponents = new Map();
|
|
1547
1805
|
|
|
1548
1806
|
const registry = attachRegistryInspection({
|
|
1549
1807
|
register(id, Component) {
|
|
1550
1808
|
if (typeof id !== "string" || id.length === 0) {
|
|
1551
1809
|
throw new TypeError("Component id must be a non-empty string.");
|
|
1552
1810
|
}
|
|
1553
|
-
if (!isComponent(Component) && typeof Component !== "function") {
|
|
1811
|
+
if (!isComponent(Component) && typeof Component !== "function" && !isLazyDescriptor(Component)) {
|
|
1554
1812
|
throw new TypeError(`Component "${id}" must be a component function.`);
|
|
1555
1813
|
}
|
|
1556
1814
|
if (entries.has(id)) {
|
|
@@ -1571,6 +1829,7 @@
|
|
|
1571
1829
|
if (typeof id !== "string" || id.length === 0) {
|
|
1572
1830
|
throw new TypeError("Component id must be a non-empty string.");
|
|
1573
1831
|
}
|
|
1832
|
+
lazyComponents.delete(id);
|
|
1574
1833
|
return entries.delete(id);
|
|
1575
1834
|
},
|
|
1576
1835
|
|
|
@@ -1578,7 +1837,20 @@
|
|
|
1578
1837
|
if (typeof id !== "string" || id.length === 0) {
|
|
1579
1838
|
throw new TypeError("Component id must be a non-empty string.");
|
|
1580
1839
|
}
|
|
1581
|
-
|
|
1840
|
+
const Component = entries.get(id);
|
|
1841
|
+
if (!isLazyDescriptor(Component)) {
|
|
1842
|
+
return Component;
|
|
1843
|
+
}
|
|
1844
|
+
if (!lazyComponents.has(id)) {
|
|
1845
|
+
lazyComponents.set(id, async function LazyComponent(...args) {
|
|
1846
|
+
const resolved = await lazyRegistry.resolve(type, id, Component);
|
|
1847
|
+
if (typeof resolved !== "function") {
|
|
1848
|
+
throw new TypeError(`Component "${id}" did not resolve to a function.`);
|
|
1849
|
+
}
|
|
1850
|
+
return resolved.apply(this, args);
|
|
1851
|
+
});
|
|
1852
|
+
}
|
|
1853
|
+
return lazyComponents.get(id);
|
|
1582
1854
|
},
|
|
1583
1855
|
|
|
1584
1856
|
_adoptMany() {
|
|
@@ -2243,6 +2515,7 @@
|
|
|
2243
2515
|
const __handlersModule = (() => {
|
|
2244
2516
|
const { applyServerResult, defaultInput, resolveServerCommandArguments, unwrapServerResult } = __serverModule;
|
|
2245
2517
|
const { attachRegistryInspection, createRegistryStore } = __registryStoreModule;
|
|
2518
|
+
const { createLazyRegistry, isLazyDescriptor } = __lazyRegistryModule;
|
|
2246
2519
|
const builtInTokens = new Set(["prevent", "preventDefault", "stopPropagation", "stopImmediatePropagation"]);
|
|
2247
2520
|
const builtInHandlers = {
|
|
2248
2521
|
prevent: preventDefault,
|
|
@@ -2263,11 +2536,13 @@
|
|
|
2263
2536
|
const registryStore = options.registry ?? createRegistryStore();
|
|
2264
2537
|
const type = options.type ?? "handler";
|
|
2265
2538
|
const handlers = registryStore._map(type);
|
|
2539
|
+
const lazyRegistry = options.lazyRegistry ?? createLazyRegistry(options);
|
|
2540
|
+
const lazyHandlers = new Map();
|
|
2266
2541
|
|
|
2267
2542
|
const registry = attachRegistryInspection({
|
|
2268
2543
|
register(id, fn) {
|
|
2269
2544
|
assertId(id);
|
|
2270
|
-
if (typeof fn !== "function") {
|
|
2545
|
+
if (typeof fn !== "function" && !isLazyDescriptor(fn)) {
|
|
2271
2546
|
throw new TypeError(`Handler "${id}" must be a function.`);
|
|
2272
2547
|
}
|
|
2273
2548
|
if (handlers.has(id)) {
|
|
@@ -2286,12 +2561,26 @@
|
|
|
2286
2561
|
|
|
2287
2562
|
unregister(id) {
|
|
2288
2563
|
assertId(id);
|
|
2564
|
+
lazyHandlers.delete(id);
|
|
2289
2565
|
return handlers.delete(id);
|
|
2290
2566
|
},
|
|
2291
2567
|
|
|
2292
2568
|
resolve(id) {
|
|
2293
2569
|
assertId(id);
|
|
2294
|
-
|
|
2570
|
+
const handler = handlers.get(id);
|
|
2571
|
+
if (!isLazyDescriptor(handler)) {
|
|
2572
|
+
return handler;
|
|
2573
|
+
}
|
|
2574
|
+
if (!lazyHandlers.has(id)) {
|
|
2575
|
+
lazyHandlers.set(id, async function runLazyHandler(...args) {
|
|
2576
|
+
const resolved = await lazyRegistry.resolve(type, id, handler);
|
|
2577
|
+
if (typeof resolved !== "function") {
|
|
2578
|
+
throw new TypeError(`Handler "${id}" did not resolve to a function.`);
|
|
2579
|
+
}
|
|
2580
|
+
return resolved.apply(this, args);
|
|
2581
|
+
});
|
|
2582
|
+
}
|
|
2583
|
+
return lazyHandlers.get(id);
|
|
2295
2584
|
},
|
|
2296
2585
|
|
|
2297
2586
|
async run(ref, context = {}) {
|
|
@@ -2605,6 +2894,10 @@
|
|
|
2605
2894
|
return api;
|
|
2606
2895
|
},
|
|
2607
2896
|
|
|
2897
|
+
isScopeDestroyed(scope) {
|
|
2898
|
+
return scope !== undefined && destroyedScopes.has(scope);
|
|
2899
|
+
},
|
|
2900
|
+
|
|
2608
2901
|
inspect() {
|
|
2609
2902
|
const counts = {};
|
|
2610
2903
|
for (const [phase, queue] of queues) {
|
|
@@ -3077,7 +3370,7 @@
|
|
|
3077
3370
|
if (renderingBoundaries.has(boundary)) {
|
|
3078
3371
|
continue;
|
|
3079
3372
|
}
|
|
3080
|
-
const id =
|
|
3373
|
+
const id = boundaryIdFor(boundary, attributeConfig);
|
|
3081
3374
|
if (id == null) {
|
|
3082
3375
|
continue;
|
|
3083
3376
|
}
|
|
@@ -3394,19 +3687,26 @@
|
|
|
3394
3687
|
function collectBoundaryTemplates(boundary, id, attributeConfig) {
|
|
3395
3688
|
const templates = {};
|
|
3396
3689
|
for (const template of [...boundary.children].filter((child) => child.tagName === "TEMPLATE")) {
|
|
3397
|
-
if (
|
|
3690
|
+
if (templateMatchesState(template, "loading", id, boundary, attributeConfig)) {
|
|
3398
3691
|
templates.loading = template;
|
|
3399
3692
|
}
|
|
3400
|
-
if (
|
|
3693
|
+
if (templateMatchesState(template, "ready", id, boundary, attributeConfig)) {
|
|
3401
3694
|
templates.ready = template;
|
|
3402
3695
|
}
|
|
3403
|
-
if (
|
|
3696
|
+
if (templateMatchesState(template, "error", id, boundary, attributeConfig)) {
|
|
3404
3697
|
templates.error = template;
|
|
3405
3698
|
}
|
|
3406
3699
|
}
|
|
3407
3700
|
return templates;
|
|
3408
3701
|
}
|
|
3409
3702
|
|
|
3703
|
+
function templateMatchesState(template, state, id, boundary, attributeConfig) {
|
|
3704
|
+
if (readAttribute(template, attributeConfig, "async", state) === id) {
|
|
3705
|
+
return true;
|
|
3706
|
+
}
|
|
3707
|
+
return isAsyncSuspense(boundary) && template.hasAttribute?.(state);
|
|
3708
|
+
}
|
|
3709
|
+
|
|
3410
3710
|
function chooseBoundaryTemplate(templates, status) {
|
|
3411
3711
|
if (status === "ready") {
|
|
3412
3712
|
return templates.ready ?? templates.loading ?? templates.error;
|
|
@@ -3454,13 +3754,24 @@
|
|
|
3454
3754
|
|
|
3455
3755
|
function findBoundary(root, boundaryId, attributeConfig) {
|
|
3456
3756
|
for (const element of elementsIn(root)) {
|
|
3457
|
-
if (
|
|
3757
|
+
if (boundaryIdFor(element, attributeConfig) === String(boundaryId)) {
|
|
3458
3758
|
return element;
|
|
3459
3759
|
}
|
|
3460
3760
|
}
|
|
3461
3761
|
return null;
|
|
3462
3762
|
}
|
|
3463
3763
|
|
|
3764
|
+
function boundaryIdFor(element, attributeConfig) {
|
|
3765
|
+
if (isAsyncSuspense(element) && element.hasAttribute?.("for")) {
|
|
3766
|
+
return element.getAttribute("for");
|
|
3767
|
+
}
|
|
3768
|
+
return readAttribute(element, attributeConfig, "async", "boundary");
|
|
3769
|
+
}
|
|
3770
|
+
|
|
3771
|
+
function isAsyncSuspense(element) {
|
|
3772
|
+
return element?.tagName === "ASYNC-SUSPENSE";
|
|
3773
|
+
}
|
|
3774
|
+
|
|
3464
3775
|
function toFragment(value, documentRef) {
|
|
3465
3776
|
if (value?.nodeType === 11) {
|
|
3466
3777
|
return value;
|
|
@@ -3493,15 +3804,18 @@
|
|
|
3493
3804
|
const __partialsModule = (() => {
|
|
3494
3805
|
const { isTemplateResult, renderTemplate } = __htmlModule;
|
|
3495
3806
|
const { attachRegistryInspection, createRegistryStore } = __registryStoreModule;
|
|
3807
|
+
const { createLazyRegistry, isLazyDescriptor } = __lazyRegistryModule;
|
|
3496
3808
|
function createPartialRegistry(initialMap = {}, options = {}) {
|
|
3497
3809
|
const registryStore = options.registry ?? createRegistryStore();
|
|
3498
3810
|
const type = options.type ?? "partial";
|
|
3499
3811
|
const entries = registryStore._map(type);
|
|
3812
|
+
const lazyRegistry = options.lazyRegistry ?? createLazyRegistry(options);
|
|
3813
|
+
const lazyPartials = new Map();
|
|
3500
3814
|
|
|
3501
3815
|
const registry = attachRegistryInspection({
|
|
3502
3816
|
register(id, fn) {
|
|
3503
3817
|
assertId(id);
|
|
3504
|
-
if (typeof fn !== "function") {
|
|
3818
|
+
if (typeof fn !== "function" && !isLazyDescriptor(fn)) {
|
|
3505
3819
|
throw new TypeError(`Partial "${id}" must be a function.`);
|
|
3506
3820
|
}
|
|
3507
3821
|
if (entries.has(id)) {
|
|
@@ -3520,12 +3834,26 @@
|
|
|
3520
3834
|
|
|
3521
3835
|
unregister(id) {
|
|
3522
3836
|
assertId(id);
|
|
3837
|
+
lazyPartials.delete(id);
|
|
3523
3838
|
return entries.delete(id);
|
|
3524
3839
|
},
|
|
3525
3840
|
|
|
3526
3841
|
resolve(id) {
|
|
3527
3842
|
assertId(id);
|
|
3528
|
-
|
|
3843
|
+
const partial = entries.get(id);
|
|
3844
|
+
if (!isLazyDescriptor(partial)) {
|
|
3845
|
+
return partial;
|
|
3846
|
+
}
|
|
3847
|
+
if (!lazyPartials.has(id)) {
|
|
3848
|
+
lazyPartials.set(id, async function runLazyPartial(...args) {
|
|
3849
|
+
const resolved = await lazyRegistry.resolve(type, id, partial);
|
|
3850
|
+
if (typeof resolved !== "function") {
|
|
3851
|
+
throw new TypeError(`Partial "${id}" did not resolve to a function.`);
|
|
3852
|
+
}
|
|
3853
|
+
return resolved.apply(this, args);
|
|
3854
|
+
});
|
|
3855
|
+
}
|
|
3856
|
+
return lazyPartials.get(id);
|
|
3529
3857
|
},
|
|
3530
3858
|
|
|
3531
3859
|
async render(id, props = {}, context = {}) {
|
|
@@ -4195,7 +4523,8 @@
|
|
|
4195
4523
|
const { createSignal, createSignalRegistry } = __signalsModule;
|
|
4196
4524
|
const { createRegistryStore } = __registryStoreModule;
|
|
4197
4525
|
const { attributeName, normalizeAttributeConfig } = __attributesModule;
|
|
4198
|
-
const
|
|
4526
|
+
const { createLazyRegistry, defineRegistrySnapshot, sameRegistryValue } = __lazyRegistryModule;
|
|
4527
|
+
const registryTypes = new Set(["signal", "handler", "server", "partial", "route", "component", "asyncSignal"]);
|
|
4199
4528
|
|
|
4200
4529
|
function defineApp(initial, options = {}) {
|
|
4201
4530
|
const registry = createRegistryStore(undefined, { target: "browser" });
|
|
@@ -4224,6 +4553,27 @@
|
|
|
4224
4553
|
return runtime;
|
|
4225
4554
|
},
|
|
4226
4555
|
|
|
4556
|
+
attachRoot(root) {
|
|
4557
|
+
return ensureRuntime(app).attachRoot(root);
|
|
4558
|
+
},
|
|
4559
|
+
|
|
4560
|
+
detachRoot(root) {
|
|
4561
|
+
return app.runtime?.detachRoot(root) ?? app;
|
|
4562
|
+
},
|
|
4563
|
+
|
|
4564
|
+
applySnapshot(snapshot, snapshotOptions = {}) {
|
|
4565
|
+
if (app.runtime) {
|
|
4566
|
+
app.runtime.applySnapshot(snapshot, snapshotOptions);
|
|
4567
|
+
return app;
|
|
4568
|
+
}
|
|
4569
|
+
appendSnapshotDeclarations(registry, snapshot, snapshotOptions);
|
|
4570
|
+
return app;
|
|
4571
|
+
},
|
|
4572
|
+
|
|
4573
|
+
inspectRoots() {
|
|
4574
|
+
return app.runtime?.inspectRoots() ?? { count: 0, roots: [] };
|
|
4575
|
+
},
|
|
4576
|
+
|
|
4227
4577
|
_attach(runtime) {
|
|
4228
4578
|
runtimes.add(runtime);
|
|
4229
4579
|
return () => app._detach(runtime);
|
|
@@ -4249,23 +4599,32 @@
|
|
|
4249
4599
|
});
|
|
4250
4600
|
const ownsScheduler = !options.scheduler && !options.loader?.scheduler;
|
|
4251
4601
|
const attributes = normalizeAttributeConfig(options.attributes);
|
|
4602
|
+
const lazyRegistry = options.lazyRegistry ?? createLazyRegistry({
|
|
4603
|
+
registryAssets: options.registryAssets,
|
|
4604
|
+
importModule: options.importModule
|
|
4605
|
+
});
|
|
4252
4606
|
const registry = options.registry ?? app.registry.view({ target });
|
|
4253
|
-
const signals = options.signals ?? createSignalRegistry(undefined, { registry, type: "signal" });
|
|
4254
|
-
const handlers = options.handlers ?? createHandlerRegistry(undefined, { registry, type: "handler" });
|
|
4607
|
+
const signals = options.signals ?? createSignalRegistry(undefined, { registry, type: "signal", lazyRegistry });
|
|
4608
|
+
const handlers = options.handlers ?? createHandlerRegistry(undefined, { registry, type: "handler", lazyRegistry });
|
|
4255
4609
|
const serverCache = createCacheRegistry(undefined, { registry, type: "cache.server" });
|
|
4256
4610
|
const browserCache = createCacheRegistry(undefined, { registry, type: "cache.browser" });
|
|
4257
4611
|
const serverFactory = options.serverFactory ?? createServerReferenceRegistry;
|
|
4258
4612
|
const server = options.server ?? serverFactory(undefined, { registry, type: "server" });
|
|
4259
|
-
const partials = options.partials ?? createPartialRegistry(undefined, { registry, type: "partial" });
|
|
4613
|
+
const partials = options.partials ?? createPartialRegistry(undefined, { registry, type: "partial", lazyRegistry });
|
|
4260
4614
|
const routes = options.routes ?? createRouteRegistry(undefined, { registry, type: "route" });
|
|
4261
|
-
const components = options.components ?? createComponentRegistry(undefined, { registry, type: "component" });
|
|
4615
|
+
const components = options.components ?? createComponentRegistry(undefined, { registry, type: "component", lazyRegistry });
|
|
4616
|
+
const hasStartupRoot = options.loader || Object.hasOwn(options, "root");
|
|
4617
|
+
const startupRoot = hasStartupRoot ? options.root : null;
|
|
4262
4618
|
let loader = options.loader;
|
|
4263
4619
|
let router = options.router;
|
|
4620
|
+
let routerStarted = false;
|
|
4264
4621
|
let detach = () => {};
|
|
4265
4622
|
let started = false;
|
|
4266
4623
|
let destroyed = false;
|
|
4624
|
+
const rootLoaders = new Map();
|
|
4267
4625
|
|
|
4268
|
-
|
|
4626
|
+
const snapshotRoot = startupRoot ?? globalThis.document;
|
|
4627
|
+
const initialSnapshot = options.snapshot ?? (target === "browser" ? readSnapshot(snapshotRoot, { attributes }) : undefined);
|
|
4269
4628
|
attachServerCache(server, serverCache);
|
|
4270
4629
|
|
|
4271
4630
|
const runtime = {
|
|
@@ -4294,54 +4653,112 @@
|
|
|
4294
4653
|
started = true;
|
|
4295
4654
|
|
|
4296
4655
|
if (target !== "server") {
|
|
4297
|
-
loader = loader ?? Loader({
|
|
4298
|
-
root: options.root,
|
|
4299
|
-
signals,
|
|
4300
|
-
handlers,
|
|
4301
|
-
server,
|
|
4302
|
-
cache: browserCache,
|
|
4303
|
-
scheduler,
|
|
4304
|
-
attributes
|
|
4305
|
-
});
|
|
4306
|
-
runtime.loader = loader;
|
|
4307
|
-
|
|
4308
4656
|
configureServerContext({ cache: browserCache });
|
|
4309
4657
|
signals._setContext?.({ server, loader, cache: browserCache, scheduler });
|
|
4310
4658
|
|
|
4311
|
-
loader
|
|
4659
|
+
if (loader) {
|
|
4660
|
+
registerRootLoader(loader.root, loader);
|
|
4661
|
+
loader.start();
|
|
4662
|
+
startRouterFor(loader.root);
|
|
4663
|
+
} else if (startupRoot != null) {
|
|
4664
|
+
runtime.attachRoot(startupRoot);
|
|
4665
|
+
}
|
|
4666
|
+
} else {
|
|
4667
|
+
configureServerContext({ cache: serverCache });
|
|
4668
|
+
signals._setContext?.({ server, cache: serverCache, scheduler });
|
|
4669
|
+
}
|
|
4670
|
+
|
|
4671
|
+
return runtime;
|
|
4672
|
+
},
|
|
4312
4673
|
|
|
4313
|
-
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4674
|
+
use(typeOrModule, entries) {
|
|
4675
|
+
app.use(typeOrModule, entries);
|
|
4676
|
+
return runtime;
|
|
4677
|
+
},
|
|
4678
|
+
|
|
4679
|
+
attachRoot(root) {
|
|
4680
|
+
assertActive();
|
|
4681
|
+
if (target === "server") {
|
|
4682
|
+
throw new Error("Server runtimes cannot attach DOM roots.");
|
|
4683
|
+
}
|
|
4684
|
+
if (!root) {
|
|
4685
|
+
throw new TypeError("runtime.attachRoot(root) requires a root.");
|
|
4686
|
+
}
|
|
4687
|
+
if (rootLoaders.has(root)) {
|
|
4688
|
+
return runtime;
|
|
4689
|
+
}
|
|
4690
|
+
|
|
4691
|
+
const rootLoader = rootLoaders.size === 0 && loader
|
|
4692
|
+
? loader
|
|
4693
|
+
: Loader({
|
|
4694
|
+
root,
|
|
4320
4695
|
signals,
|
|
4321
4696
|
handlers,
|
|
4322
4697
|
server,
|
|
4323
4698
|
cache: browserCache,
|
|
4324
|
-
partials,
|
|
4325
4699
|
scheduler,
|
|
4326
|
-
fetch: options.fetch,
|
|
4327
|
-
routeEndpoint: options.routeEndpoint,
|
|
4328
4700
|
attributes
|
|
4329
4701
|
});
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4702
|
+
registerRootLoader(root, rootLoader);
|
|
4703
|
+
rootLoader.start();
|
|
4704
|
+
configureServerContext({ cache: browserCache });
|
|
4705
|
+
signals._setContext?.({ server, loader: runtime.loader, cache: browserCache, scheduler });
|
|
4706
|
+
startRouterFor(root);
|
|
4707
|
+
return runtime;
|
|
4708
|
+
},
|
|
4709
|
+
|
|
4710
|
+
detachRoot(root) {
|
|
4711
|
+
assertActive();
|
|
4712
|
+
if (target === "server") {
|
|
4713
|
+
return runtime;
|
|
4714
|
+
}
|
|
4715
|
+
if (root == null) {
|
|
4716
|
+
for (const rootLoader of new Set(rootLoaders.values())) {
|
|
4717
|
+
rootLoader.destroy?.();
|
|
4718
|
+
}
|
|
4719
|
+
rootLoaders.clear();
|
|
4720
|
+
router?.destroy?.();
|
|
4721
|
+
router = undefined;
|
|
4722
|
+
routerStarted = false;
|
|
4723
|
+
loader = undefined;
|
|
4724
|
+
runtime.loader = undefined;
|
|
4725
|
+
runtime.router = undefined;
|
|
4726
|
+
return runtime;
|
|
4727
|
+
}
|
|
4728
|
+
const rootLoader = rootLoaders.get(root);
|
|
4729
|
+
if (!rootLoader) {
|
|
4730
|
+
return runtime;
|
|
4731
|
+
}
|
|
4732
|
+
rootLoader.destroy?.();
|
|
4733
|
+
rootLoaders.delete(root);
|
|
4734
|
+
if (loader === rootLoader) {
|
|
4735
|
+
router?.destroy?.();
|
|
4736
|
+
router = undefined;
|
|
4737
|
+
routerStarted = false;
|
|
4738
|
+
const next = rootLoaders.values().next().value;
|
|
4739
|
+
loader = next;
|
|
4740
|
+
runtime.loader = next;
|
|
4741
|
+
runtime.router = undefined;
|
|
4742
|
+
if (next) {
|
|
4743
|
+
startRouterFor(next.root);
|
|
4334
4744
|
}
|
|
4335
|
-
} else {
|
|
4336
|
-
configureServerContext({ cache: serverCache });
|
|
4337
|
-
signals._setContext?.({ server, cache: serverCache, scheduler });
|
|
4338
4745
|
}
|
|
4339
|
-
|
|
4340
4746
|
return runtime;
|
|
4341
4747
|
},
|
|
4342
4748
|
|
|
4343
|
-
|
|
4344
|
-
|
|
4749
|
+
inspectRoots() {
|
|
4750
|
+
return {
|
|
4751
|
+
count: rootLoaders.size,
|
|
4752
|
+
roots: [...rootLoaders].map(([root, rootLoader]) => ({
|
|
4753
|
+
root,
|
|
4754
|
+
loader: rootLoader,
|
|
4755
|
+
primary: rootLoader === loader
|
|
4756
|
+
}))
|
|
4757
|
+
};
|
|
4758
|
+
},
|
|
4759
|
+
|
|
4760
|
+
applySnapshot(snapshot, snapshotOptions = {}) {
|
|
4761
|
+
applySnapshotToRuntime(runtime, snapshot, snapshotOptions);
|
|
4345
4762
|
return runtime;
|
|
4346
4763
|
},
|
|
4347
4764
|
|
|
@@ -4403,7 +4820,14 @@
|
|
|
4403
4820
|
destroyed = true;
|
|
4404
4821
|
detach();
|
|
4405
4822
|
router?.destroy?.();
|
|
4406
|
-
|
|
4823
|
+
const destroyedLoaders = new Set(rootLoaders.values());
|
|
4824
|
+
for (const rootLoader of destroyedLoaders) {
|
|
4825
|
+
rootLoader.destroy?.();
|
|
4826
|
+
}
|
|
4827
|
+
rootLoaders.clear();
|
|
4828
|
+
if (loader && !destroyedLoaders.has(loader)) {
|
|
4829
|
+
loader?.destroy?.();
|
|
4830
|
+
}
|
|
4407
4831
|
signals.destroy?.();
|
|
4408
4832
|
if (ownsScheduler) {
|
|
4409
4833
|
scheduler.destroy();
|
|
@@ -4417,10 +4841,49 @@
|
|
|
4417
4841
|
|
|
4418
4842
|
server.cache = serverCache;
|
|
4419
4843
|
runtime.server.cache = serverCache;
|
|
4844
|
+
runtime.applySnapshot(initialSnapshot, { strict: options.strictSnapshots ?? true });
|
|
4420
4845
|
detach = app._attach(runtime);
|
|
4421
4846
|
|
|
4422
4847
|
return runtime;
|
|
4423
4848
|
|
|
4849
|
+
function registerRootLoader(root, rootLoader) {
|
|
4850
|
+
rootLoaders.set(root, rootLoader);
|
|
4851
|
+
if (!loader) {
|
|
4852
|
+
loader = rootLoader;
|
|
4853
|
+
runtime.loader = rootLoader;
|
|
4854
|
+
}
|
|
4855
|
+
rootLoader.server = server;
|
|
4856
|
+
rootLoader.cache = browserCache;
|
|
4857
|
+
rootLoader.scheduler = scheduler;
|
|
4858
|
+
}
|
|
4859
|
+
|
|
4860
|
+
function startRouterFor(root) {
|
|
4861
|
+
if (router === false || routerStarted || !(router || shouldStartRouter(routes, options)) || !runtime.loader) {
|
|
4862
|
+
return;
|
|
4863
|
+
}
|
|
4864
|
+
router = router ?? createRouter({
|
|
4865
|
+
mode: options.mode ?? "ssr-spa",
|
|
4866
|
+
root,
|
|
4867
|
+
boundary: options.boundary ?? "route",
|
|
4868
|
+
routes,
|
|
4869
|
+
loader: runtime.loader,
|
|
4870
|
+
signals,
|
|
4871
|
+
handlers,
|
|
4872
|
+
server,
|
|
4873
|
+
cache: browserCache,
|
|
4874
|
+
partials,
|
|
4875
|
+
scheduler,
|
|
4876
|
+
fetch: options.fetch,
|
|
4877
|
+
routeEndpoint: options.routeEndpoint,
|
|
4878
|
+
attributes
|
|
4879
|
+
});
|
|
4880
|
+
runtime.router = router;
|
|
4881
|
+
runtime.loader.router = router;
|
|
4882
|
+
configureServerContext({ cache: browserCache, router });
|
|
4883
|
+
router.start();
|
|
4884
|
+
routerStarted = true;
|
|
4885
|
+
}
|
|
4886
|
+
|
|
4424
4887
|
function configureServerContext(extra = {}) {
|
|
4425
4888
|
const cache = isLocalServerRegistry(server) ? serverCache : extra.cache;
|
|
4426
4889
|
server._setContext?.({
|
|
@@ -4463,6 +4926,7 @@
|
|
|
4463
4926
|
return {};
|
|
4464
4927
|
}
|
|
4465
4928
|
|
|
4929
|
+
const merged = {};
|
|
4466
4930
|
for (const searchRoot of new Set([rootNode, documentRef])) {
|
|
4467
4931
|
if (!searchRoot?.querySelectorAll) {
|
|
4468
4932
|
continue;
|
|
@@ -4473,17 +4937,19 @@
|
|
|
4473
4937
|
}
|
|
4474
4938
|
const source = script.textContent?.trim() ?? "";
|
|
4475
4939
|
if (!source) {
|
|
4476
|
-
|
|
4940
|
+
continue;
|
|
4477
4941
|
}
|
|
4942
|
+
let parsed;
|
|
4478
4943
|
try {
|
|
4479
|
-
|
|
4944
|
+
parsed = JSON.parse(source);
|
|
4480
4945
|
} catch (cause) {
|
|
4481
4946
|
throw new Error(`Could not parse Async snapshot: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
4482
4947
|
}
|
|
4948
|
+
mergeSnapshot(merged, parsed, { strict: true });
|
|
4483
4949
|
}
|
|
4484
4950
|
}
|
|
4485
4951
|
|
|
4486
|
-
return
|
|
4952
|
+
return merged;
|
|
4487
4953
|
}
|
|
4488
4954
|
|
|
4489
4955
|
function applyUseToRuntime(runtime, normalized) {
|
|
@@ -4493,10 +4959,22 @@
|
|
|
4493
4959
|
applyRegistryUse(runtime.partials, runtime.registry, normalized.partial);
|
|
4494
4960
|
applyRegistryUse(runtime.routes, runtime.registry, normalized.route);
|
|
4495
4961
|
applyRegistryUse(runtime.components, runtime.registry, normalized.component);
|
|
4962
|
+
applyRegistryStoreUse(runtime.registry, "asyncSignal", normalized.asyncSignal);
|
|
4496
4963
|
applyRegistryUse(runtime.browser.cache, runtime.registry, normalized.cache.browser);
|
|
4497
4964
|
applyRegistryUse(runtime.server.cache, runtime.registry, normalized.cache.server);
|
|
4498
4965
|
}
|
|
4499
4966
|
|
|
4967
|
+
function applyRegistryStoreUse(registry, type, entries) {
|
|
4968
|
+
if (!entries || Object.keys(entries).length === 0) {
|
|
4969
|
+
return;
|
|
4970
|
+
}
|
|
4971
|
+
for (const [id, value] of Object.entries(entries)) {
|
|
4972
|
+
if (!registry.has(type, id)) {
|
|
4973
|
+
registry.register(type, id, value);
|
|
4974
|
+
}
|
|
4975
|
+
}
|
|
4976
|
+
}
|
|
4977
|
+
|
|
4500
4978
|
function applyRegistryUse(registry, runtimeRegistry, entries) {
|
|
4501
4979
|
if (!entries || Object.keys(entries).length === 0) {
|
|
4502
4980
|
return;
|
|
@@ -4516,6 +4994,7 @@
|
|
|
4516
4994
|
partial: {},
|
|
4517
4995
|
route: {},
|
|
4518
4996
|
component: {},
|
|
4997
|
+
asyncSignal: {},
|
|
4519
4998
|
cache: {
|
|
4520
4999
|
browser: {},
|
|
4521
5000
|
server: {}
|
|
@@ -4571,11 +5050,128 @@
|
|
|
4571
5050
|
return Boolean(value && typeof value.use === "function" && typeof value.snapshot === "function" && value.registry);
|
|
4572
5051
|
}
|
|
4573
5052
|
|
|
4574
|
-
function
|
|
4575
|
-
|
|
4576
|
-
|
|
5053
|
+
function ensureRuntime(app) {
|
|
5054
|
+
if (!app.runtime) {
|
|
5055
|
+
app.start();
|
|
5056
|
+
}
|
|
5057
|
+
return app.runtime;
|
|
5058
|
+
}
|
|
5059
|
+
|
|
5060
|
+
function applySnapshotToRuntime(runtime, snapshot = {}, options = {}) {
|
|
5061
|
+
const normalized = normalizeSnapshot(snapshot);
|
|
5062
|
+
for (const [path, value] of Object.entries(normalized.signal)) {
|
|
5063
|
+
setOrRegisterSignal(runtime.signals, path, value);
|
|
5064
|
+
}
|
|
5065
|
+
runtime.browser.cache.restore(normalized.cache.browser);
|
|
5066
|
+
mergeRegistryEntries(runtime, "handler", normalized.handler, runtime.handlers, options);
|
|
5067
|
+
mergeRegistryEntries(runtime, "server", normalized.server, runtime.server, options);
|
|
5068
|
+
mergeRegistryEntries(runtime, "partial", normalized.partial, runtime.partials, options);
|
|
5069
|
+
mergeRegistryEntries(runtime, "route", normalized.route, runtime.routes, options);
|
|
5070
|
+
mergeRegistryEntries(runtime, "component", normalized.component, runtime.components, options);
|
|
5071
|
+
mergeRegistryEntries(runtime, "asyncSignal", normalized.asyncSignal, null, options);
|
|
5072
|
+
return runtime;
|
|
5073
|
+
}
|
|
5074
|
+
|
|
5075
|
+
function appendSnapshotDeclarations(registry, snapshot = {}, options = {}) {
|
|
5076
|
+
const normalized = normalizeSnapshot(snapshot);
|
|
5077
|
+
for (const [id, value] of Object.entries(normalized.signal)) {
|
|
5078
|
+
registerSnapshotEntry(registry, "signal", id, createSignal(value), options);
|
|
5079
|
+
}
|
|
5080
|
+
for (const type of ["handler", "server", "partial", "route", "component", "asyncSignal"]) {
|
|
5081
|
+
for (const [id, value] of Object.entries(normalized[type])) {
|
|
5082
|
+
registerSnapshotEntry(registry, type, id, value, options);
|
|
5083
|
+
}
|
|
5084
|
+
}
|
|
5085
|
+
}
|
|
5086
|
+
|
|
5087
|
+
function mergeRegistryEntries(runtime, type, entries, concreteRegistry, options = {}) {
|
|
5088
|
+
if (!entries || Object.keys(entries).length === 0) {
|
|
5089
|
+
return;
|
|
5090
|
+
}
|
|
5091
|
+
for (const [id, value] of Object.entries(entries)) {
|
|
5092
|
+
registerSnapshotEntry(runtime.registry, type, id, value, options);
|
|
5093
|
+
}
|
|
5094
|
+
concreteRegistry?._adoptMany?.(entries);
|
|
5095
|
+
}
|
|
5096
|
+
|
|
5097
|
+
function registerSnapshotEntry(registry, type, id, value, options = {}) {
|
|
5098
|
+
const strict = options.strict ?? true;
|
|
5099
|
+
const map = registry._map(type);
|
|
5100
|
+
if (map.has(id)) {
|
|
5101
|
+
if (sameRegistryValue(map.get(id), value) || sameSnapshotValue(map.get(id), value)) {
|
|
5102
|
+
return;
|
|
5103
|
+
}
|
|
5104
|
+
if (strict) {
|
|
5105
|
+
throw new Error(`${type} "${id}" is already registered with a different value.`);
|
|
5106
|
+
}
|
|
5107
|
+
return;
|
|
5108
|
+
}
|
|
5109
|
+
registry.set(type, id, value);
|
|
5110
|
+
}
|
|
5111
|
+
|
|
5112
|
+
function normalizeSnapshot(snapshot = {}) {
|
|
5113
|
+
const normalized = {
|
|
5114
|
+
signal: {
|
|
5115
|
+
...(snapshot.signals ?? {}),
|
|
5116
|
+
...(snapshot.signal ?? {})
|
|
5117
|
+
},
|
|
5118
|
+
handler: { ...(snapshot.handler ?? {}) },
|
|
5119
|
+
server: { ...(snapshot.server ?? {}) },
|
|
5120
|
+
partial: { ...(snapshot.partial ?? {}) },
|
|
5121
|
+
route: { ...(snapshot.route ?? {}) },
|
|
5122
|
+
component: { ...(snapshot.component ?? {}) },
|
|
5123
|
+
asyncSignal: { ...(snapshot.asyncSignal ?? {}) },
|
|
5124
|
+
cache: {
|
|
5125
|
+
browser: {
|
|
5126
|
+
...(snapshot.entries?.browser ?? {}),
|
|
5127
|
+
...(snapshot.cache?.browser ?? {})
|
|
5128
|
+
}
|
|
5129
|
+
}
|
|
5130
|
+
};
|
|
5131
|
+
return normalized;
|
|
5132
|
+
}
|
|
5133
|
+
|
|
5134
|
+
function mergeSnapshot(target, source, options = {}) {
|
|
5135
|
+
const normalized = normalizeSnapshot(defineRegistrySnapshot(source));
|
|
5136
|
+
target.signal = {
|
|
5137
|
+
...(target.signal ?? target.signals ?? {}),
|
|
5138
|
+
...normalized.signal
|
|
5139
|
+
};
|
|
5140
|
+
target.signals = target.signal;
|
|
5141
|
+
target.cache = {
|
|
5142
|
+
...(target.cache ?? {}),
|
|
5143
|
+
browser: {
|
|
5144
|
+
...(target.cache?.browser ?? {}),
|
|
5145
|
+
...normalized.cache.browser
|
|
5146
|
+
}
|
|
5147
|
+
};
|
|
5148
|
+
for (const type of ["handler", "server", "partial", "route", "component", "asyncSignal"]) {
|
|
5149
|
+
target[type] = target[type] ?? {};
|
|
5150
|
+
for (const [id, value] of Object.entries(normalized[type])) {
|
|
5151
|
+
if (Object.hasOwn(target[type], id)) {
|
|
5152
|
+
if (sameRegistryValue(target[type][id], value) || sameSnapshotValue(target[type][id], value)) {
|
|
5153
|
+
continue;
|
|
5154
|
+
}
|
|
5155
|
+
if (options.strict ?? true) {
|
|
5156
|
+
throw new Error(`${type} "${id}" is already declared with a different value.`);
|
|
5157
|
+
}
|
|
5158
|
+
continue;
|
|
5159
|
+
}
|
|
5160
|
+
target[type][id] = value;
|
|
5161
|
+
}
|
|
5162
|
+
}
|
|
5163
|
+
return target;
|
|
5164
|
+
}
|
|
5165
|
+
|
|
5166
|
+
function sameSnapshotValue(left, right) {
|
|
5167
|
+
if (left === right) {
|
|
5168
|
+
return true;
|
|
5169
|
+
}
|
|
5170
|
+
try {
|
|
5171
|
+
return JSON.stringify(left) === JSON.stringify(right);
|
|
5172
|
+
} catch {
|
|
5173
|
+
return false;
|
|
4577
5174
|
}
|
|
4578
|
-
browserCache.restore(snapshot.cache?.browser);
|
|
4579
5175
|
}
|
|
4580
5176
|
|
|
4581
5177
|
function setOrRegisterSignal(signals, path, value) {
|
|
@@ -4720,6 +5316,312 @@
|
|
|
4720
5316
|
return { defineApp, createApp, readSnapshot, Async };
|
|
4721
5317
|
})();
|
|
4722
5318
|
|
|
5319
|
+
const __boundaryReceiverModule = (() => {
|
|
5320
|
+
const defaultRecentLimit = 50;
|
|
5321
|
+
|
|
5322
|
+
function createBoundaryReceiver(options = {}) {
|
|
5323
|
+
const loader = options.loader;
|
|
5324
|
+
const signals = options.signals ?? loader?.signals;
|
|
5325
|
+
const cache = options.cache ?? loader?.cache;
|
|
5326
|
+
const scheduler = options.scheduler ?? loader?.scheduler;
|
|
5327
|
+
const router = options.router ?? loader?.router;
|
|
5328
|
+
const recentLimit = options.recentLimit ?? defaultRecentLimit;
|
|
5329
|
+
const throwOnError = options.throwOnError === true;
|
|
5330
|
+
const onApply = typeof options.onApply === "function" ? options.onApply : undefined;
|
|
5331
|
+
const onIgnore = typeof options.onIgnore === "function" ? options.onIgnore : undefined;
|
|
5332
|
+
const onError = typeof options.onError === "function" ? options.onError : undefined;
|
|
5333
|
+
const isScopeDestroyed = typeof options.isScopeDestroyed === "function"
|
|
5334
|
+
? options.isScopeDestroyed
|
|
5335
|
+
: (scope) => scheduler?.isScopeDestroyed?.(scope) ?? scheduler?.inspectDestroyed?.(scope) ?? false;
|
|
5336
|
+
|
|
5337
|
+
if (!loader || typeof loader.swap !== "function") {
|
|
5338
|
+
throw new TypeError("createBoundaryReceiver(...) requires a loader with swap(boundary, html).");
|
|
5339
|
+
}
|
|
5340
|
+
if (!Number.isInteger(recentLimit) || recentLimit < 0) {
|
|
5341
|
+
throw new TypeError("createBoundaryReceiver(...) recentLimit must be a non-negative integer.");
|
|
5342
|
+
}
|
|
5343
|
+
|
|
5344
|
+
const boundaries = new Map();
|
|
5345
|
+
const recent = [];
|
|
5346
|
+
let destroyed = false;
|
|
5347
|
+
|
|
5348
|
+
const receiver = {
|
|
5349
|
+
async apply(patch) {
|
|
5350
|
+
if (destroyed) {
|
|
5351
|
+
throw new Error("Boundary receiver has been destroyed.");
|
|
5352
|
+
}
|
|
5353
|
+
|
|
5354
|
+
const normalized = validatePatch(patch);
|
|
5355
|
+
const record = boundaryRecord(normalized.boundary);
|
|
5356
|
+
if (normalized.seq <= record.lastSeq) {
|
|
5357
|
+
const result = {
|
|
5358
|
+
status: "ignored-stale",
|
|
5359
|
+
boundary: normalized.boundary,
|
|
5360
|
+
seq: normalized.seq,
|
|
5361
|
+
lastSeq: record.lastSeq
|
|
5362
|
+
};
|
|
5363
|
+
record.ignored += 1;
|
|
5364
|
+
record.lastStatus = result.status;
|
|
5365
|
+
remember(result);
|
|
5366
|
+
onIgnore?.(result, patch);
|
|
5367
|
+
return result;
|
|
5368
|
+
}
|
|
5369
|
+
|
|
5370
|
+
if (normalized.parentScope !== undefined && isScopeDestroyed(normalized.parentScope)) {
|
|
5371
|
+
const result = {
|
|
5372
|
+
status: "ignored-destroyed",
|
|
5373
|
+
boundary: normalized.boundary,
|
|
5374
|
+
seq: normalized.seq,
|
|
5375
|
+
parentScope: normalized.parentScope
|
|
5376
|
+
};
|
|
5377
|
+
record.ignored += 1;
|
|
5378
|
+
record.lastStatus = result.status;
|
|
5379
|
+
remember(result);
|
|
5380
|
+
onIgnore?.(result, patch);
|
|
5381
|
+
return result;
|
|
5382
|
+
}
|
|
5383
|
+
|
|
5384
|
+
record.lastSeq = normalized.seq;
|
|
5385
|
+
|
|
5386
|
+
if (Object.hasOwn(normalized, "error")) {
|
|
5387
|
+
const error = toStableError(normalized.error);
|
|
5388
|
+
const result = {
|
|
5389
|
+
status: "errored",
|
|
5390
|
+
boundary: normalized.boundary,
|
|
5391
|
+
seq: normalized.seq,
|
|
5392
|
+
error
|
|
5393
|
+
};
|
|
5394
|
+
record.errored += 1;
|
|
5395
|
+
record.lastStatus = result.status;
|
|
5396
|
+
remember(result);
|
|
5397
|
+
onError?.(error, result, patch);
|
|
5398
|
+
if (throwOnError) {
|
|
5399
|
+
throw error;
|
|
5400
|
+
}
|
|
5401
|
+
return result;
|
|
5402
|
+
}
|
|
5403
|
+
|
|
5404
|
+
if (normalized.signals) {
|
|
5405
|
+
if (!signals || typeof signals.set !== "function") {
|
|
5406
|
+
throw new Error("Boundary patch includes signals, but no signal registry is available.");
|
|
5407
|
+
}
|
|
5408
|
+
for (const [path, value] of Object.entries(normalized.signals)) {
|
|
5409
|
+
signals.set(path, value);
|
|
5410
|
+
}
|
|
5411
|
+
}
|
|
5412
|
+
|
|
5413
|
+
if (normalized.cache?.browser) {
|
|
5414
|
+
if (!cache || typeof cache.restore !== "function") {
|
|
5415
|
+
throw new Error("Boundary patch includes browser cache, but no cache registry is available.");
|
|
5416
|
+
}
|
|
5417
|
+
cache.restore(normalized.cache.browser);
|
|
5418
|
+
}
|
|
5419
|
+
|
|
5420
|
+
if (normalized.html != null) {
|
|
5421
|
+
loader.swap(normalized.boundary, normalized.html);
|
|
5422
|
+
}
|
|
5423
|
+
|
|
5424
|
+
await flushScheduler(scheduler, normalized.scope);
|
|
5425
|
+
|
|
5426
|
+
if (normalized.redirect) {
|
|
5427
|
+
await followRedirect(normalized.redirect, router, loader);
|
|
5428
|
+
const result = {
|
|
5429
|
+
status: "redirected",
|
|
5430
|
+
boundary: normalized.boundary,
|
|
5431
|
+
seq: normalized.seq,
|
|
5432
|
+
redirect: normalized.redirect
|
|
5433
|
+
};
|
|
5434
|
+
record.applied += 1;
|
|
5435
|
+
record.lastStatus = result.status;
|
|
5436
|
+
remember(result);
|
|
5437
|
+
onApply?.(result, patch);
|
|
5438
|
+
return result;
|
|
5439
|
+
}
|
|
5440
|
+
|
|
5441
|
+
const result = {
|
|
5442
|
+
status: "applied",
|
|
5443
|
+
boundary: normalized.boundary,
|
|
5444
|
+
seq: normalized.seq
|
|
5445
|
+
};
|
|
5446
|
+
record.applied += 1;
|
|
5447
|
+
record.lastStatus = result.status;
|
|
5448
|
+
remember(result);
|
|
5449
|
+
onApply?.(result, patch);
|
|
5450
|
+
return result;
|
|
5451
|
+
},
|
|
5452
|
+
|
|
5453
|
+
inspect() {
|
|
5454
|
+
const snapshot = {};
|
|
5455
|
+
for (const [boundary, record] of boundaries) {
|
|
5456
|
+
snapshot[boundary] = {
|
|
5457
|
+
lastSeq: record.lastSeq,
|
|
5458
|
+
applied: record.applied,
|
|
5459
|
+
ignored: record.ignored,
|
|
5460
|
+
lastStatus: record.lastStatus
|
|
5461
|
+
};
|
|
5462
|
+
if (record.errored > 0) {
|
|
5463
|
+
snapshot[boundary].errored = record.errored;
|
|
5464
|
+
}
|
|
5465
|
+
}
|
|
5466
|
+
return {
|
|
5467
|
+
destroyed,
|
|
5468
|
+
boundaries: snapshot,
|
|
5469
|
+
recent: recent.map((entry) => ({ ...entry }))
|
|
5470
|
+
};
|
|
5471
|
+
},
|
|
5472
|
+
|
|
5473
|
+
reset(boundary) {
|
|
5474
|
+
if (boundary === undefined) {
|
|
5475
|
+
boundaries.clear();
|
|
5476
|
+
recent.length = 0;
|
|
5477
|
+
return receiver;
|
|
5478
|
+
}
|
|
5479
|
+
assertBoundary(boundary);
|
|
5480
|
+
boundaries.delete(boundary);
|
|
5481
|
+
for (let index = recent.length - 1; index >= 0; index -= 1) {
|
|
5482
|
+
if (recent[index].boundary === boundary) {
|
|
5483
|
+
recent.splice(index, 1);
|
|
5484
|
+
}
|
|
5485
|
+
}
|
|
5486
|
+
return receiver;
|
|
5487
|
+
},
|
|
5488
|
+
|
|
5489
|
+
destroy() {
|
|
5490
|
+
destroyed = true;
|
|
5491
|
+
boundaries.clear();
|
|
5492
|
+
recent.length = 0;
|
|
5493
|
+
}
|
|
5494
|
+
};
|
|
5495
|
+
|
|
5496
|
+
return receiver;
|
|
5497
|
+
|
|
5498
|
+
function boundaryRecord(boundary) {
|
|
5499
|
+
if (!boundaries.has(boundary)) {
|
|
5500
|
+
boundaries.set(boundary, {
|
|
5501
|
+
lastSeq: -Infinity,
|
|
5502
|
+
applied: 0,
|
|
5503
|
+
ignored: 0,
|
|
5504
|
+
errored: 0,
|
|
5505
|
+
lastStatus: undefined
|
|
5506
|
+
});
|
|
5507
|
+
}
|
|
5508
|
+
return boundaries.get(boundary);
|
|
5509
|
+
}
|
|
5510
|
+
|
|
5511
|
+
function remember(result) {
|
|
5512
|
+
if (recentLimit === 0) {
|
|
5513
|
+
return;
|
|
5514
|
+
}
|
|
5515
|
+
recent.push(toRecentEntry(result));
|
|
5516
|
+
while (recent.length > recentLimit) {
|
|
5517
|
+
recent.shift();
|
|
5518
|
+
}
|
|
5519
|
+
}
|
|
5520
|
+
}
|
|
5521
|
+
|
|
5522
|
+
function validatePatch(patch) {
|
|
5523
|
+
if (!patch || typeof patch !== "object" || Array.isArray(patch)) {
|
|
5524
|
+
throw new TypeError("receiver.apply(patch) requires a boundary patch object.");
|
|
5525
|
+
}
|
|
5526
|
+
|
|
5527
|
+
assertBoundary(patch.boundary);
|
|
5528
|
+
if (typeof patch.seq !== "number" || !Number.isFinite(patch.seq)) {
|
|
5529
|
+
throw new TypeError("Boundary patch seq must be a finite number.");
|
|
5530
|
+
}
|
|
5531
|
+
|
|
5532
|
+
if (patch.signals !== undefined && !isPlainObject(patch.signals)) {
|
|
5533
|
+
throw new TypeError("Boundary patch signals must be an object.");
|
|
5534
|
+
}
|
|
5535
|
+
if (patch.cache !== undefined && !isPlainObject(patch.cache)) {
|
|
5536
|
+
throw new TypeError("Boundary patch cache must be an object.");
|
|
5537
|
+
}
|
|
5538
|
+
if (patch.cache?.browser !== undefined && !isPlainObject(patch.cache.browser)) {
|
|
5539
|
+
throw new TypeError("Boundary patch cache.browser must be an object.");
|
|
5540
|
+
}
|
|
5541
|
+
if (patch.redirect !== undefined && (typeof patch.redirect !== "string" || patch.redirect.length === 0)) {
|
|
5542
|
+
throw new TypeError("Boundary patch redirect must be a non-empty string.");
|
|
5543
|
+
}
|
|
5544
|
+
if (patch.parentScope !== undefined && typeof patch.parentScope !== "string") {
|
|
5545
|
+
throw new TypeError("Boundary patch parentScope must be a string.");
|
|
5546
|
+
}
|
|
5547
|
+
if (patch.scope !== undefined && typeof patch.scope !== "string") {
|
|
5548
|
+
throw new TypeError("Boundary patch scope must be a string.");
|
|
5549
|
+
}
|
|
5550
|
+
|
|
5551
|
+
const hasHtml = Object.hasOwn(patch, "html") && patch.html != null;
|
|
5552
|
+
const hasSignals = patch.signals && Object.keys(patch.signals).length > 0;
|
|
5553
|
+
const hasBrowserCache = patch.cache?.browser && Object.keys(patch.cache.browser).length > 0;
|
|
5554
|
+
const hasRedirect = Boolean(patch.redirect);
|
|
5555
|
+
const hasError = Object.hasOwn(patch, "error");
|
|
5556
|
+
if (!hasHtml && !hasSignals && !hasBrowserCache && !hasRedirect && !hasError) {
|
|
5557
|
+
throw new TypeError("Boundary patch must include html, signals, cache.browser, redirect, or error.");
|
|
5558
|
+
}
|
|
5559
|
+
|
|
5560
|
+
return patch;
|
|
5561
|
+
}
|
|
5562
|
+
|
|
5563
|
+
function assertBoundary(boundary) {
|
|
5564
|
+
if (typeof boundary !== "string" || boundary.length === 0) {
|
|
5565
|
+
throw new TypeError("Boundary patch boundary must be a non-empty string.");
|
|
5566
|
+
}
|
|
5567
|
+
}
|
|
5568
|
+
|
|
5569
|
+
async function flushScheduler(scheduler, scope) {
|
|
5570
|
+
if (!scheduler) {
|
|
5571
|
+
return;
|
|
5572
|
+
}
|
|
5573
|
+
if (scope !== undefined && typeof scheduler.flushScope === "function") {
|
|
5574
|
+
await scheduler.flushScope(scope);
|
|
5575
|
+
return;
|
|
5576
|
+
}
|
|
5577
|
+
if (typeof scheduler.flush === "function") {
|
|
5578
|
+
await scheduler.flush();
|
|
5579
|
+
}
|
|
5580
|
+
}
|
|
5581
|
+
|
|
5582
|
+
async function followRedirect(redirect, router, loader) {
|
|
5583
|
+
if (router && typeof router.navigate === "function") {
|
|
5584
|
+
await router.navigate(redirect);
|
|
5585
|
+
return;
|
|
5586
|
+
}
|
|
5587
|
+
const location = loader?.root?.ownerDocument?.defaultView?.location ?? globalThis.location;
|
|
5588
|
+
location?.assign?.(redirect);
|
|
5589
|
+
}
|
|
5590
|
+
|
|
5591
|
+
function toStableError(value) {
|
|
5592
|
+
if (value instanceof Error) {
|
|
5593
|
+
return value;
|
|
5594
|
+
}
|
|
5595
|
+
if (value && typeof value === "object" && typeof value.message === "string") {
|
|
5596
|
+
return Object.assign(new Error(value.message), value);
|
|
5597
|
+
}
|
|
5598
|
+
return new Error(String(value));
|
|
5599
|
+
}
|
|
5600
|
+
|
|
5601
|
+
function toRecentEntry(result) {
|
|
5602
|
+
const entry = {
|
|
5603
|
+
boundary: result.boundary,
|
|
5604
|
+
seq: result.seq,
|
|
5605
|
+
status: result.status
|
|
5606
|
+
};
|
|
5607
|
+
if (result.status === "ignored-stale") {
|
|
5608
|
+
entry.lastSeq = result.lastSeq;
|
|
5609
|
+
}
|
|
5610
|
+
if (result.status === "ignored-destroyed" && result.parentScope !== undefined) {
|
|
5611
|
+
entry.parentScope = result.parentScope;
|
|
5612
|
+
}
|
|
5613
|
+
if (result.status === "redirected") {
|
|
5614
|
+
entry.redirect = result.redirect;
|
|
5615
|
+
}
|
|
5616
|
+
return entry;
|
|
5617
|
+
}
|
|
5618
|
+
|
|
5619
|
+
function isPlainObject(value) {
|
|
5620
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
5621
|
+
}
|
|
5622
|
+
return { createBoundaryReceiver };
|
|
5623
|
+
})();
|
|
5624
|
+
|
|
4723
5625
|
const __delayModule = (() => {
|
|
4724
5626
|
function delay(ms, signal) {
|
|
4725
5627
|
if (signal?.aborted) {
|
|
@@ -4754,6 +5656,72 @@
|
|
|
4754
5656
|
return { delay };
|
|
4755
5657
|
})();
|
|
4756
5658
|
|
|
5659
|
+
const __elementsModule = (() => {
|
|
5660
|
+
const { Async } = __appModule;
|
|
5661
|
+
function defineAsyncContainerElement(options = {}) {
|
|
5662
|
+
const tagName = options.tagName ?? "async-container";
|
|
5663
|
+
const registry = options.customElements ?? globalThis.customElements;
|
|
5664
|
+
if (!registry) {
|
|
5665
|
+
throw new Error("defineAsyncContainerElement(...) requires customElements.");
|
|
5666
|
+
}
|
|
5667
|
+
const existing = registry.get(tagName);
|
|
5668
|
+
if (existing) {
|
|
5669
|
+
return existing;
|
|
5670
|
+
}
|
|
5671
|
+
const app = options.app ?? options.Async ?? Async;
|
|
5672
|
+
const HTMLElementBase = options.HTMLElement ?? options.window?.HTMLElement ?? globalThis.HTMLElement;
|
|
5673
|
+
if (!HTMLElementBase) {
|
|
5674
|
+
throw new Error("defineAsyncContainerElement(...) requires HTMLElement.");
|
|
5675
|
+
}
|
|
5676
|
+
|
|
5677
|
+
class AsyncContainerElement extends HTMLElementBase {
|
|
5678
|
+
connectedCallback() {
|
|
5679
|
+
if (this.__asyncAttached) {
|
|
5680
|
+
return;
|
|
5681
|
+
}
|
|
5682
|
+
const runtime = app.runtime ?? app.start?.();
|
|
5683
|
+
runtime?.attachRoot?.(this);
|
|
5684
|
+
this.__asyncRuntime = runtime;
|
|
5685
|
+
this.__asyncAttached = true;
|
|
5686
|
+
}
|
|
5687
|
+
|
|
5688
|
+
disconnectedCallback() {
|
|
5689
|
+
if (!this.__asyncAttached) {
|
|
5690
|
+
return;
|
|
5691
|
+
}
|
|
5692
|
+
this.__asyncRuntime?.detachRoot?.(this);
|
|
5693
|
+
this.__asyncRuntime = undefined;
|
|
5694
|
+
this.__asyncAttached = false;
|
|
5695
|
+
}
|
|
5696
|
+
}
|
|
5697
|
+
|
|
5698
|
+
registry.define(tagName, AsyncContainerElement);
|
|
5699
|
+
return AsyncContainerElement;
|
|
5700
|
+
}
|
|
5701
|
+
|
|
5702
|
+
function defineAsyncSuspenseElement(options = {}) {
|
|
5703
|
+
const tagName = options.tagName ?? "async-suspense";
|
|
5704
|
+
const registry = options.customElements ?? globalThis.customElements;
|
|
5705
|
+
if (!registry) {
|
|
5706
|
+
throw new Error("defineAsyncSuspenseElement(...) requires customElements.");
|
|
5707
|
+
}
|
|
5708
|
+
const existing = registry.get(tagName);
|
|
5709
|
+
if (existing) {
|
|
5710
|
+
return existing;
|
|
5711
|
+
}
|
|
5712
|
+
const HTMLElementBase = options.HTMLElement ?? options.window?.HTMLElement ?? globalThis.HTMLElement;
|
|
5713
|
+
if (!HTMLElementBase) {
|
|
5714
|
+
throw new Error("defineAsyncSuspenseElement(...) requires HTMLElement.");
|
|
5715
|
+
}
|
|
5716
|
+
|
|
5717
|
+
class AsyncSuspenseElement extends HTMLElementBase {}
|
|
5718
|
+
|
|
5719
|
+
registry.define(tagName, AsyncSuspenseElement);
|
|
5720
|
+
return AsyncSuspenseElement;
|
|
5721
|
+
}
|
|
5722
|
+
return { defineAsyncContainerElement, defineAsyncSuspenseElement };
|
|
5723
|
+
})();
|
|
5724
|
+
|
|
4757
5725
|
const { asyncSignal: asyncSignal } = __asyncSignalModule;
|
|
4758
5726
|
const { Async: Async } = __appModule;
|
|
4759
5727
|
const { createApp: createApp } = __appModule;
|
|
@@ -4761,14 +5729,19 @@
|
|
|
4761
5729
|
const { readSnapshot: readSnapshot } = __appModule;
|
|
4762
5730
|
const { attributeName: attributeName } = __attributesModule;
|
|
4763
5731
|
const { defineAttributeConfig: defineAttributeConfig } = __attributesModule;
|
|
5732
|
+
const { createBoundaryReceiver: createBoundaryReceiver } = __boundaryReceiverModule;
|
|
4764
5733
|
const { createCacheRegistry: createCacheRegistry } = __cacheModule;
|
|
4765
5734
|
const { defineCache: defineCache } = __cacheModule;
|
|
4766
5735
|
const { component: component } = __componentModule;
|
|
4767
5736
|
const { createComponentRegistry: createComponentRegistry } = __componentModule;
|
|
4768
5737
|
const { defineComponent: defineComponent } = __componentModule;
|
|
4769
5738
|
const { delay: delay } = __delayModule;
|
|
5739
|
+
const { defineAsyncContainerElement: defineAsyncContainerElement } = __elementsModule;
|
|
5740
|
+
const { defineAsyncSuspenseElement: defineAsyncSuspenseElement } = __elementsModule;
|
|
4770
5741
|
const { createHandlerRegistry: createHandlerRegistry } = __handlersModule;
|
|
4771
5742
|
const { html: html } = __htmlModule;
|
|
5743
|
+
const { createLazyRegistry: createLazyRegistry } = __lazyRegistryModule;
|
|
5744
|
+
const { defineRegistrySnapshot: defineRegistrySnapshot } = __lazyRegistryModule;
|
|
4772
5745
|
const { Loader: Loader } = __loaderModule;
|
|
4773
5746
|
const { AsyncLoader: AsyncLoader } = __loaderModule;
|
|
4774
5747
|
const { createPartialRegistry: createPartialRegistry } = __partialsModule;
|
|
@@ -4787,7 +5760,7 @@
|
|
|
4787
5760
|
const { createSignalRegistry: createSignalRegistry } = __signalsModule;
|
|
4788
5761
|
const { effect: effect } = __signalsModule;
|
|
4789
5762
|
const { signal: signal } = __signalsModule;
|
|
4790
|
-
const api = { asyncSignal, Async, createApp, defineApp, readSnapshot, attributeName, defineAttributeConfig, createCacheRegistry, defineCache, component, createComponentRegistry, defineComponent, delay, createHandlerRegistry, html, Loader, AsyncLoader, createPartialRegistry, createRegistryStore, createRouteRegistry, createRouter, defineRoute, route, createScheduler, applyServerResult, createServerProxy, resolveServerCommandArguments, unwrapServerResult, computed, createSignal, createSignalRegistry, effect, signal };
|
|
5763
|
+
const api = { asyncSignal, Async, createApp, defineApp, readSnapshot, attributeName, defineAttributeConfig, createBoundaryReceiver, createCacheRegistry, defineCache, component, createComponentRegistry, defineComponent, delay, defineAsyncContainerElement, defineAsyncSuspenseElement, createHandlerRegistry, html, createLazyRegistry, defineRegistrySnapshot, Loader, AsyncLoader, createPartialRegistry, createRegistryStore, createRouteRegistry, createRouter, defineRoute, route, createScheduler, applyServerResult, createServerProxy, resolveServerCommandArguments, unwrapServerResult, computed, createSignal, createSignalRegistry, effect, signal };
|
|
4791
5764
|
assertNoUmdNamespaceConflicts(api, Async);
|
|
4792
5765
|
Object.assign(Async, api);
|
|
4793
5766
|
Async.Async = Async;
|