@async/framework 0.9.0 → 0.10.1
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 +20 -1
- package/README.md +115 -0
- package/browser.d.ts +69 -18
- package/browser.js +733 -71
- package/browser.min.js +1 -1
- package/browser.ts +733 -71
- package/browser.umd.js +733 -71
- package/browser.umd.min.js +1 -1
- package/package.json +11 -4
- package/server.d.ts +69 -18
- package/src/app.js +314 -46
- package/src/browser.js +2 -0
- package/src/component.js +19 -2
- package/src/elements.js +63 -0
- package/src/handlers.js +19 -2
- package/src/index.js +2 -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/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 = {}) {
|
|
@@ -3081,7 +3370,7 @@
|
|
|
3081
3370
|
if (renderingBoundaries.has(boundary)) {
|
|
3082
3371
|
continue;
|
|
3083
3372
|
}
|
|
3084
|
-
const id =
|
|
3373
|
+
const id = boundaryIdFor(boundary, attributeConfig);
|
|
3085
3374
|
if (id == null) {
|
|
3086
3375
|
continue;
|
|
3087
3376
|
}
|
|
@@ -3398,19 +3687,26 @@
|
|
|
3398
3687
|
function collectBoundaryTemplates(boundary, id, attributeConfig) {
|
|
3399
3688
|
const templates = {};
|
|
3400
3689
|
for (const template of [...boundary.children].filter((child) => child.tagName === "TEMPLATE")) {
|
|
3401
|
-
if (
|
|
3690
|
+
if (templateMatchesState(template, "loading", id, boundary, attributeConfig)) {
|
|
3402
3691
|
templates.loading = template;
|
|
3403
3692
|
}
|
|
3404
|
-
if (
|
|
3693
|
+
if (templateMatchesState(template, "ready", id, boundary, attributeConfig)) {
|
|
3405
3694
|
templates.ready = template;
|
|
3406
3695
|
}
|
|
3407
|
-
if (
|
|
3696
|
+
if (templateMatchesState(template, "error", id, boundary, attributeConfig)) {
|
|
3408
3697
|
templates.error = template;
|
|
3409
3698
|
}
|
|
3410
3699
|
}
|
|
3411
3700
|
return templates;
|
|
3412
3701
|
}
|
|
3413
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
|
+
|
|
3414
3710
|
function chooseBoundaryTemplate(templates, status) {
|
|
3415
3711
|
if (status === "ready") {
|
|
3416
3712
|
return templates.ready ?? templates.loading ?? templates.error;
|
|
@@ -3458,13 +3754,24 @@
|
|
|
3458
3754
|
|
|
3459
3755
|
function findBoundary(root, boundaryId, attributeConfig) {
|
|
3460
3756
|
for (const element of elementsIn(root)) {
|
|
3461
|
-
if (
|
|
3757
|
+
if (boundaryIdFor(element, attributeConfig) === String(boundaryId)) {
|
|
3462
3758
|
return element;
|
|
3463
3759
|
}
|
|
3464
3760
|
}
|
|
3465
3761
|
return null;
|
|
3466
3762
|
}
|
|
3467
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
|
+
|
|
3468
3775
|
function toFragment(value, documentRef) {
|
|
3469
3776
|
if (value?.nodeType === 11) {
|
|
3470
3777
|
return value;
|
|
@@ -3497,15 +3804,18 @@
|
|
|
3497
3804
|
const __partialsModule = (() => {
|
|
3498
3805
|
const { isTemplateResult, renderTemplate } = __htmlModule;
|
|
3499
3806
|
const { attachRegistryInspection, createRegistryStore } = __registryStoreModule;
|
|
3807
|
+
const { createLazyRegistry, isLazyDescriptor } = __lazyRegistryModule;
|
|
3500
3808
|
function createPartialRegistry(initialMap = {}, options = {}) {
|
|
3501
3809
|
const registryStore = options.registry ?? createRegistryStore();
|
|
3502
3810
|
const type = options.type ?? "partial";
|
|
3503
3811
|
const entries = registryStore._map(type);
|
|
3812
|
+
const lazyRegistry = options.lazyRegistry ?? createLazyRegistry(options);
|
|
3813
|
+
const lazyPartials = new Map();
|
|
3504
3814
|
|
|
3505
3815
|
const registry = attachRegistryInspection({
|
|
3506
3816
|
register(id, fn) {
|
|
3507
3817
|
assertId(id);
|
|
3508
|
-
if (typeof fn !== "function") {
|
|
3818
|
+
if (typeof fn !== "function" && !isLazyDescriptor(fn)) {
|
|
3509
3819
|
throw new TypeError(`Partial "${id}" must be a function.`);
|
|
3510
3820
|
}
|
|
3511
3821
|
if (entries.has(id)) {
|
|
@@ -3524,12 +3834,26 @@
|
|
|
3524
3834
|
|
|
3525
3835
|
unregister(id) {
|
|
3526
3836
|
assertId(id);
|
|
3837
|
+
lazyPartials.delete(id);
|
|
3527
3838
|
return entries.delete(id);
|
|
3528
3839
|
},
|
|
3529
3840
|
|
|
3530
3841
|
resolve(id) {
|
|
3531
3842
|
assertId(id);
|
|
3532
|
-
|
|
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);
|
|
3533
3857
|
},
|
|
3534
3858
|
|
|
3535
3859
|
async render(id, props = {}, context = {}) {
|
|
@@ -4199,7 +4523,8 @@
|
|
|
4199
4523
|
const { createSignal, createSignalRegistry } = __signalsModule;
|
|
4200
4524
|
const { createRegistryStore } = __registryStoreModule;
|
|
4201
4525
|
const { attributeName, normalizeAttributeConfig } = __attributesModule;
|
|
4202
|
-
const
|
|
4526
|
+
const { createLazyRegistry, defineRegistrySnapshot, sameRegistryValue } = __lazyRegistryModule;
|
|
4527
|
+
const registryTypes = new Set(["signal", "handler", "server", "partial", "route", "component", "asyncSignal"]);
|
|
4203
4528
|
|
|
4204
4529
|
function defineApp(initial, options = {}) {
|
|
4205
4530
|
const registry = createRegistryStore(undefined, { target: "browser" });
|
|
@@ -4228,6 +4553,27 @@
|
|
|
4228
4553
|
return runtime;
|
|
4229
4554
|
},
|
|
4230
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
|
+
|
|
4231
4577
|
_attach(runtime) {
|
|
4232
4578
|
runtimes.add(runtime);
|
|
4233
4579
|
return () => app._detach(runtime);
|
|
@@ -4253,23 +4599,32 @@
|
|
|
4253
4599
|
});
|
|
4254
4600
|
const ownsScheduler = !options.scheduler && !options.loader?.scheduler;
|
|
4255
4601
|
const attributes = normalizeAttributeConfig(options.attributes);
|
|
4602
|
+
const lazyRegistry = options.lazyRegistry ?? createLazyRegistry({
|
|
4603
|
+
registryAssets: options.registryAssets,
|
|
4604
|
+
importModule: options.importModule
|
|
4605
|
+
});
|
|
4256
4606
|
const registry = options.registry ?? app.registry.view({ target });
|
|
4257
|
-
const signals = options.signals ?? createSignalRegistry(undefined, { registry, type: "signal" });
|
|
4258
|
-
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 });
|
|
4259
4609
|
const serverCache = createCacheRegistry(undefined, { registry, type: "cache.server" });
|
|
4260
4610
|
const browserCache = createCacheRegistry(undefined, { registry, type: "cache.browser" });
|
|
4261
4611
|
const serverFactory = options.serverFactory ?? createServerReferenceRegistry;
|
|
4262
4612
|
const server = options.server ?? serverFactory(undefined, { registry, type: "server" });
|
|
4263
|
-
const partials = options.partials ?? createPartialRegistry(undefined, { registry, type: "partial" });
|
|
4613
|
+
const partials = options.partials ?? createPartialRegistry(undefined, { registry, type: "partial", lazyRegistry });
|
|
4264
4614
|
const routes = options.routes ?? createRouteRegistry(undefined, { registry, type: "route" });
|
|
4265
|
-
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;
|
|
4266
4618
|
let loader = options.loader;
|
|
4267
4619
|
let router = options.router;
|
|
4620
|
+
let routerStarted = false;
|
|
4268
4621
|
let detach = () => {};
|
|
4269
4622
|
let started = false;
|
|
4270
4623
|
let destroyed = false;
|
|
4624
|
+
const rootLoaders = new Map();
|
|
4271
4625
|
|
|
4272
|
-
|
|
4626
|
+
const snapshotRoot = startupRoot ?? globalThis.document;
|
|
4627
|
+
const initialSnapshot = options.snapshot ?? (target === "browser" ? readSnapshot(snapshotRoot, { attributes }) : undefined);
|
|
4273
4628
|
attachServerCache(server, serverCache);
|
|
4274
4629
|
|
|
4275
4630
|
const runtime = {
|
|
@@ -4298,54 +4653,112 @@
|
|
|
4298
4653
|
started = true;
|
|
4299
4654
|
|
|
4300
4655
|
if (target !== "server") {
|
|
4301
|
-
loader = loader ?? Loader({
|
|
4302
|
-
root: options.root,
|
|
4303
|
-
signals,
|
|
4304
|
-
handlers,
|
|
4305
|
-
server,
|
|
4306
|
-
cache: browserCache,
|
|
4307
|
-
scheduler,
|
|
4308
|
-
attributes
|
|
4309
|
-
});
|
|
4310
|
-
runtime.loader = loader;
|
|
4311
|
-
|
|
4312
4656
|
configureServerContext({ cache: browserCache });
|
|
4313
4657
|
signals._setContext?.({ server, loader, cache: browserCache, scheduler });
|
|
4314
4658
|
|
|
4315
|
-
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
|
+
},
|
|
4673
|
+
|
|
4674
|
+
use(typeOrModule, entries) {
|
|
4675
|
+
app.use(typeOrModule, entries);
|
|
4676
|
+
return runtime;
|
|
4677
|
+
},
|
|
4316
4678
|
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
|
|
4323
|
-
|
|
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,
|
|
4324
4695
|
signals,
|
|
4325
4696
|
handlers,
|
|
4326
4697
|
server,
|
|
4327
4698
|
cache: browserCache,
|
|
4328
|
-
partials,
|
|
4329
4699
|
scheduler,
|
|
4330
|
-
fetch: options.fetch,
|
|
4331
|
-
routeEndpoint: options.routeEndpoint,
|
|
4332
4700
|
attributes
|
|
4333
4701
|
});
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
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);
|
|
4338
4744
|
}
|
|
4339
|
-
} else {
|
|
4340
|
-
configureServerContext({ cache: serverCache });
|
|
4341
|
-
signals._setContext?.({ server, cache: serverCache, scheduler });
|
|
4342
4745
|
}
|
|
4343
|
-
|
|
4344
4746
|
return runtime;
|
|
4345
4747
|
},
|
|
4346
4748
|
|
|
4347
|
-
|
|
4348
|
-
|
|
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);
|
|
4349
4762
|
return runtime;
|
|
4350
4763
|
},
|
|
4351
4764
|
|
|
@@ -4407,7 +4820,14 @@
|
|
|
4407
4820
|
destroyed = true;
|
|
4408
4821
|
detach();
|
|
4409
4822
|
router?.destroy?.();
|
|
4410
|
-
|
|
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
|
+
}
|
|
4411
4831
|
signals.destroy?.();
|
|
4412
4832
|
if (ownsScheduler) {
|
|
4413
4833
|
scheduler.destroy();
|
|
@@ -4421,10 +4841,49 @@
|
|
|
4421
4841
|
|
|
4422
4842
|
server.cache = serverCache;
|
|
4423
4843
|
runtime.server.cache = serverCache;
|
|
4844
|
+
runtime.applySnapshot(initialSnapshot, { strict: options.strictSnapshots ?? true });
|
|
4424
4845
|
detach = app._attach(runtime);
|
|
4425
4846
|
|
|
4426
4847
|
return runtime;
|
|
4427
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
|
+
|
|
4428
4887
|
function configureServerContext(extra = {}) {
|
|
4429
4888
|
const cache = isLocalServerRegistry(server) ? serverCache : extra.cache;
|
|
4430
4889
|
server._setContext?.({
|
|
@@ -4467,6 +4926,7 @@
|
|
|
4467
4926
|
return {};
|
|
4468
4927
|
}
|
|
4469
4928
|
|
|
4929
|
+
const merged = {};
|
|
4470
4930
|
for (const searchRoot of new Set([rootNode, documentRef])) {
|
|
4471
4931
|
if (!searchRoot?.querySelectorAll) {
|
|
4472
4932
|
continue;
|
|
@@ -4477,17 +4937,19 @@
|
|
|
4477
4937
|
}
|
|
4478
4938
|
const source = script.textContent?.trim() ?? "";
|
|
4479
4939
|
if (!source) {
|
|
4480
|
-
|
|
4940
|
+
continue;
|
|
4481
4941
|
}
|
|
4942
|
+
let parsed;
|
|
4482
4943
|
try {
|
|
4483
|
-
|
|
4944
|
+
parsed = JSON.parse(source);
|
|
4484
4945
|
} catch (cause) {
|
|
4485
4946
|
throw new Error(`Could not parse Async snapshot: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
4486
4947
|
}
|
|
4948
|
+
mergeSnapshot(merged, parsed, { strict: true });
|
|
4487
4949
|
}
|
|
4488
4950
|
}
|
|
4489
4951
|
|
|
4490
|
-
return
|
|
4952
|
+
return merged;
|
|
4491
4953
|
}
|
|
4492
4954
|
|
|
4493
4955
|
function applyUseToRuntime(runtime, normalized) {
|
|
@@ -4497,10 +4959,22 @@
|
|
|
4497
4959
|
applyRegistryUse(runtime.partials, runtime.registry, normalized.partial);
|
|
4498
4960
|
applyRegistryUse(runtime.routes, runtime.registry, normalized.route);
|
|
4499
4961
|
applyRegistryUse(runtime.components, runtime.registry, normalized.component);
|
|
4962
|
+
applyRegistryStoreUse(runtime.registry, "asyncSignal", normalized.asyncSignal);
|
|
4500
4963
|
applyRegistryUse(runtime.browser.cache, runtime.registry, normalized.cache.browser);
|
|
4501
4964
|
applyRegistryUse(runtime.server.cache, runtime.registry, normalized.cache.server);
|
|
4502
4965
|
}
|
|
4503
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
|
+
|
|
4504
4978
|
function applyRegistryUse(registry, runtimeRegistry, entries) {
|
|
4505
4979
|
if (!entries || Object.keys(entries).length === 0) {
|
|
4506
4980
|
return;
|
|
@@ -4520,6 +4994,7 @@
|
|
|
4520
4994
|
partial: {},
|
|
4521
4995
|
route: {},
|
|
4522
4996
|
component: {},
|
|
4997
|
+
asyncSignal: {},
|
|
4523
4998
|
cache: {
|
|
4524
4999
|
browser: {},
|
|
4525
5000
|
server: {}
|
|
@@ -4575,11 +5050,128 @@
|
|
|
4575
5050
|
return Boolean(value && typeof value.use === "function" && typeof value.snapshot === "function" && value.registry);
|
|
4576
5051
|
}
|
|
4577
5052
|
|
|
4578
|
-
function
|
|
4579
|
-
|
|
4580
|
-
|
|
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;
|
|
4581
5174
|
}
|
|
4582
|
-
browserCache.restore(snapshot.cache?.browser);
|
|
4583
5175
|
}
|
|
4584
5176
|
|
|
4585
5177
|
function setOrRegisterSignal(signals, path, value) {
|
|
@@ -5064,6 +5656,72 @@
|
|
|
5064
5656
|
return { delay };
|
|
5065
5657
|
})();
|
|
5066
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
|
+
|
|
5067
5725
|
const { asyncSignal: asyncSignal } = __asyncSignalModule;
|
|
5068
5726
|
const { Async: Async } = __appModule;
|
|
5069
5727
|
const { createApp: createApp } = __appModule;
|
|
@@ -5078,8 +5736,12 @@
|
|
|
5078
5736
|
const { createComponentRegistry: createComponentRegistry } = __componentModule;
|
|
5079
5737
|
const { defineComponent: defineComponent } = __componentModule;
|
|
5080
5738
|
const { delay: delay } = __delayModule;
|
|
5739
|
+
const { defineAsyncContainerElement: defineAsyncContainerElement } = __elementsModule;
|
|
5740
|
+
const { defineAsyncSuspenseElement: defineAsyncSuspenseElement } = __elementsModule;
|
|
5081
5741
|
const { createHandlerRegistry: createHandlerRegistry } = __handlersModule;
|
|
5082
5742
|
const { html: html } = __htmlModule;
|
|
5743
|
+
const { createLazyRegistry: createLazyRegistry } = __lazyRegistryModule;
|
|
5744
|
+
const { defineRegistrySnapshot: defineRegistrySnapshot } = __lazyRegistryModule;
|
|
5083
5745
|
const { Loader: Loader } = __loaderModule;
|
|
5084
5746
|
const { AsyncLoader: AsyncLoader } = __loaderModule;
|
|
5085
5747
|
const { createPartialRegistry: createPartialRegistry } = __partialsModule;
|
|
@@ -5098,7 +5760,7 @@
|
|
|
5098
5760
|
const { createSignalRegistry: createSignalRegistry } = __signalsModule;
|
|
5099
5761
|
const { effect: effect } = __signalsModule;
|
|
5100
5762
|
const { signal: signal } = __signalsModule;
|
|
5101
|
-
const api = { asyncSignal, Async, createApp, defineApp, readSnapshot, attributeName, defineAttributeConfig, createBoundaryReceiver, 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 };
|
|
5102
5764
|
assertNoUmdNamespaceConflicts(api, Async);
|
|
5103
5765
|
Object.assign(Async, api);
|
|
5104
5766
|
Async.Async = Async;
|