@async/framework 0.6.0 → 0.8.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 +31 -0
- package/README.md +93 -15
- package/{framework.d.ts → browser.d.ts} +71 -4
- package/{framework.js → browser.js} +791 -147
- package/browser.min.js +1 -0
- package/browser.ts +4781 -0
- package/{framework.umd.js → browser.umd.js} +791 -147
- package/browser.umd.min.js +1 -0
- package/package.json +45 -30
- package/server.d.ts +640 -0
- package/src/app.js +143 -12
- package/src/async-signal.js +32 -4
- package/src/browser.js +15 -0
- package/src/cache.js +27 -3
- package/src/component.js +42 -7
- package/src/index.js +5 -2
- package/src/loader.js +42 -10
- package/src/request-context.js +40 -0
- package/src/router.js +113 -16
- package/src/scheduler.js +296 -0
- package/src/server-entry.js +20 -0
- package/src/server-registry.js +97 -0
- package/src/server.js +49 -89
- package/src/signals.js +38 -6
- package/framework.min.js +0 -3648
- package/framework.ts +0 -3
- package/framework.umd.min.js +0 -3671
|
@@ -94,7 +94,8 @@ const __asyncSignalModule = (() => {
|
|
|
94
94
|
router: context.router,
|
|
95
95
|
loader: context.loader,
|
|
96
96
|
cache: context.cache,
|
|
97
|
-
abort: activeAbort
|
|
97
|
+
abort: activeAbort,
|
|
98
|
+
scheduler: context.scheduler
|
|
98
99
|
});
|
|
99
100
|
}
|
|
100
101
|
return server;
|
|
@@ -108,6 +109,9 @@ const __asyncSignalModule = (() => {
|
|
|
108
109
|
get cache() {
|
|
109
110
|
return registry._context?.().cache;
|
|
110
111
|
},
|
|
112
|
+
get scheduler() {
|
|
113
|
+
return registry._context?.().scheduler;
|
|
114
|
+
},
|
|
111
115
|
get version() {
|
|
112
116
|
return runVersion;
|
|
113
117
|
},
|
|
@@ -184,11 +188,20 @@ const __asyncSignalModule = (() => {
|
|
|
184
188
|
_bindRegistry(nextRegistry, nextId) {
|
|
185
189
|
registry = nextRegistry;
|
|
186
190
|
registeredId = nextId;
|
|
187
|
-
|
|
191
|
+
const start = () => {
|
|
188
192
|
if (registry === nextRegistry && status === "idle") {
|
|
189
193
|
state.refresh();
|
|
190
194
|
}
|
|
191
|
-
}
|
|
195
|
+
};
|
|
196
|
+
const scheduler = registry._context?.().scheduler;
|
|
197
|
+
if (scheduler) {
|
|
198
|
+
scheduler.enqueue("async", start, {
|
|
199
|
+
scope: registeredId,
|
|
200
|
+
key: `asyncSignal:${registeredId}:initial`
|
|
201
|
+
});
|
|
202
|
+
} else {
|
|
203
|
+
queueMicrotask(start);
|
|
204
|
+
}
|
|
192
205
|
},
|
|
193
206
|
|
|
194
207
|
_dispose() {
|
|
@@ -224,11 +237,26 @@ const __asyncSignalModule = (() => {
|
|
|
224
237
|
for (const dependency of dependencies) {
|
|
225
238
|
const dependencyId = String(dependency).split(".")[0];
|
|
226
239
|
if (dependencyId && dependencyId !== registeredId) {
|
|
227
|
-
dependencyCleanups.add(registry.subscribe(dependency, () =>
|
|
240
|
+
dependencyCleanups.add(registry.subscribe(dependency, () => scheduleRefresh()));
|
|
228
241
|
}
|
|
229
242
|
}
|
|
230
243
|
}
|
|
231
244
|
|
|
245
|
+
function scheduleRefresh() {
|
|
246
|
+
if (activeAbort && !activeAbort.aborted) {
|
|
247
|
+
activeAbort.cancel(new Error(`Async signal "${registeredId}" dependency changed.`));
|
|
248
|
+
}
|
|
249
|
+
const scheduler = registry?._context?.().scheduler;
|
|
250
|
+
if (!scheduler) {
|
|
251
|
+
state.refresh();
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
scheduler.enqueue("async", () => state.refresh(), {
|
|
255
|
+
scope: registeredId,
|
|
256
|
+
key: `asyncSignal:${registeredId}:refresh`
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
|
|
232
260
|
function notify() {
|
|
233
261
|
for (const subscriber of [...subscribers]) {
|
|
234
262
|
subscriber(state);
|
|
@@ -536,6 +564,7 @@ const __cacheModule = (() => {
|
|
|
536
564
|
const registryStore = registry ?? createRegistryStore();
|
|
537
565
|
const definitions = registryStore._map(type);
|
|
538
566
|
const entries = registryStore._map(`${type}.entries`);
|
|
567
|
+
const pending = new Map();
|
|
539
568
|
|
|
540
569
|
const registryApi = attachRegistryInspection({
|
|
541
570
|
register(id, definition = defineCache()) {
|
|
@@ -597,19 +626,37 @@ const __cacheModule = (() => {
|
|
|
597
626
|
if (cached !== undefined) {
|
|
598
627
|
return cached;
|
|
599
628
|
}
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
629
|
+
if (pending.has(key)) {
|
|
630
|
+
return pending.get(key);
|
|
631
|
+
}
|
|
632
|
+
let promise;
|
|
633
|
+
promise = Promise.resolve()
|
|
634
|
+
.then(fn)
|
|
635
|
+
.then((value) => {
|
|
636
|
+
if (pending.get(key) === promise) {
|
|
637
|
+
registryApi.set(key, value, options);
|
|
638
|
+
}
|
|
639
|
+
return value;
|
|
640
|
+
})
|
|
641
|
+
.finally(() => {
|
|
642
|
+
if (pending.get(key) === promise) {
|
|
643
|
+
pending.delete(key);
|
|
644
|
+
}
|
|
645
|
+
});
|
|
646
|
+
pending.set(key, promise);
|
|
647
|
+
return promise;
|
|
603
648
|
},
|
|
604
649
|
|
|
605
650
|
delete(key) {
|
|
606
651
|
assertKey(key);
|
|
652
|
+
pending.delete(key);
|
|
607
653
|
return entries.delete(key);
|
|
608
654
|
},
|
|
609
655
|
|
|
610
656
|
clear(prefix) {
|
|
611
657
|
if (prefix === undefined) {
|
|
612
658
|
entries.clear();
|
|
659
|
+
pending.clear();
|
|
613
660
|
return registryApi;
|
|
614
661
|
}
|
|
615
662
|
for (const key of [...entries.keys()]) {
|
|
@@ -617,6 +664,11 @@ const __cacheModule = (() => {
|
|
|
617
664
|
entries.delete(key);
|
|
618
665
|
}
|
|
619
666
|
}
|
|
667
|
+
for (const key of [...pending.keys()]) {
|
|
668
|
+
if (key.startsWith(prefix)) {
|
|
669
|
+
pending.delete(key);
|
|
670
|
+
}
|
|
671
|
+
}
|
|
620
672
|
return registryApi;
|
|
621
673
|
},
|
|
622
674
|
|
|
@@ -841,7 +893,8 @@ const __signalsModule = (() => {
|
|
|
841
893
|
server: registry._context?.().server,
|
|
842
894
|
router: registry._context?.().router,
|
|
843
895
|
loader: registry._context?.().loader,
|
|
844
|
-
cache: registry._context?.().cache
|
|
896
|
+
cache: registry._context?.().cache,
|
|
897
|
+
scheduler: registry._context?.().scheduler
|
|
845
898
|
}));
|
|
846
899
|
});
|
|
847
900
|
}
|
|
@@ -869,6 +922,8 @@ const __signalsModule = (() => {
|
|
|
869
922
|
const registryCleanups = new Map();
|
|
870
923
|
const runtimeContext = {};
|
|
871
924
|
const boundEntries = new Set();
|
|
925
|
+
let subscriptionCounter = 0;
|
|
926
|
+
let effectCounter = 0;
|
|
872
927
|
|
|
873
928
|
const registry = attachRegistryInspection({
|
|
874
929
|
register(id, signalLike) {
|
|
@@ -944,17 +999,21 @@ const __signalsModule = (() => {
|
|
|
944
999
|
return createRef(registry, id);
|
|
945
1000
|
},
|
|
946
1001
|
|
|
947
|
-
subscribe(path, fn) {
|
|
1002
|
+
subscribe(path, fn, options = {}) {
|
|
948
1003
|
if (typeof fn !== "function") {
|
|
949
1004
|
throw new TypeError("subscribe(path, fn) requires a function.");
|
|
950
1005
|
}
|
|
951
1006
|
const parsed = parsePath(path, entries);
|
|
952
1007
|
const entry = requireEntry(entries, parsed.id);
|
|
1008
|
+
const subscriptionId = ++subscriptionCounter;
|
|
953
1009
|
return entry.subscribe(() => {
|
|
954
|
-
fn(registry.get(parsed.path), {
|
|
1010
|
+
scheduleCallback(() => fn(registry.get(parsed.path), {
|
|
955
1011
|
id: parsed.id,
|
|
956
1012
|
path: parsed.path,
|
|
957
1013
|
signal: entry
|
|
1014
|
+
}), {
|
|
1015
|
+
...options,
|
|
1016
|
+
key: options.key ?? `signal:${parsed.path}:${subscriptionId}`
|
|
958
1017
|
});
|
|
959
1018
|
});
|
|
960
1019
|
},
|
|
@@ -972,10 +1031,12 @@ const __signalsModule = (() => {
|
|
|
972
1031
|
return registry.ref(id);
|
|
973
1032
|
},
|
|
974
1033
|
|
|
975
|
-
effect(fn) {
|
|
1034
|
+
effect(fn, options = {}) {
|
|
976
1035
|
let cleanup;
|
|
977
1036
|
let dependencyCleanups = [];
|
|
978
1037
|
let stopped = false;
|
|
1038
|
+
const scheduler = options.scheduler;
|
|
1039
|
+
const effectId = ++effectCounter;
|
|
979
1040
|
|
|
980
1041
|
const run = () => {
|
|
981
1042
|
if (stopped) {
|
|
@@ -994,10 +1055,22 @@ const __signalsModule = (() => {
|
|
|
994
1055
|
server: runtimeContext.server,
|
|
995
1056
|
router: runtimeContext.router,
|
|
996
1057
|
loader: runtimeContext.loader,
|
|
997
|
-
cache: runtimeContext.cache
|
|
1058
|
+
cache: runtimeContext.cache,
|
|
1059
|
+
scheduler: runtimeContext.scheduler
|
|
998
1060
|
}));
|
|
999
1061
|
cleanup = outcome.value;
|
|
1000
|
-
dependencyCleanups = outcome.dependencies.map((dependency) => registry.subscribe(dependency,
|
|
1062
|
+
dependencyCleanups = outcome.dependencies.map((dependency) => registry.subscribe(dependency, scheduleRun));
|
|
1063
|
+
};
|
|
1064
|
+
|
|
1065
|
+
const scheduleRun = () => {
|
|
1066
|
+
if (!scheduler) {
|
|
1067
|
+
run();
|
|
1068
|
+
return;
|
|
1069
|
+
}
|
|
1070
|
+
scheduler.enqueue(options.phase ?? "effect", run, {
|
|
1071
|
+
scope: options.scope,
|
|
1072
|
+
key: options.key ?? `effect:${effectId}`
|
|
1073
|
+
});
|
|
1001
1074
|
};
|
|
1002
1075
|
|
|
1003
1076
|
run();
|
|
@@ -1074,6 +1147,17 @@ const __signalsModule = (() => {
|
|
|
1074
1147
|
registryCleanups.set(id, cleanup);
|
|
1075
1148
|
}
|
|
1076
1149
|
}
|
|
1150
|
+
|
|
1151
|
+
function scheduleCallback(fn, options = {}) {
|
|
1152
|
+
const scheduler = options.scheduler;
|
|
1153
|
+
if (!scheduler || options.phase === "sync") {
|
|
1154
|
+
return fn();
|
|
1155
|
+
}
|
|
1156
|
+
return scheduler.enqueue(options.phase ?? "effect", fn, {
|
|
1157
|
+
scope: options.scope,
|
|
1158
|
+
key: options.key
|
|
1159
|
+
});
|
|
1160
|
+
}
|
|
1077
1161
|
}
|
|
1078
1162
|
|
|
1079
1163
|
function normalizeSignal(signalLike) {
|
|
@@ -1540,10 +1624,15 @@ const __componentModule = (() => {
|
|
|
1540
1624
|
html,
|
|
1541
1625
|
attach(target) {
|
|
1542
1626
|
for (const hook of attachHooks) {
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1627
|
+
runtime.scheduler?.enqueue("lifecycle", () => {
|
|
1628
|
+
const cleanup = hook(target);
|
|
1629
|
+
if (typeof cleanup === "function") {
|
|
1630
|
+
cleanups.push(cleanup);
|
|
1631
|
+
}
|
|
1632
|
+
}, {
|
|
1633
|
+
scope,
|
|
1634
|
+
key: `attach:${attachHooks.indexOf(hook)}`
|
|
1635
|
+
}) ?? runAttachHook(hook, target);
|
|
1547
1636
|
}
|
|
1548
1637
|
},
|
|
1549
1638
|
mount(target) {
|
|
@@ -1551,7 +1640,17 @@ const __componentModule = (() => {
|
|
|
1551
1640
|
},
|
|
1552
1641
|
visible(target, observeVisible) {
|
|
1553
1642
|
for (const hook of visibleHooks) {
|
|
1554
|
-
const cleanup = observeVisible(target,
|
|
1643
|
+
const cleanup = observeVisible(target, () => {
|
|
1644
|
+
runtime.scheduler?.enqueue("lifecycle", () => {
|
|
1645
|
+
const hookCleanup = hook(target);
|
|
1646
|
+
if (typeof hookCleanup === "function") {
|
|
1647
|
+
cleanups.push(hookCleanup);
|
|
1648
|
+
}
|
|
1649
|
+
}, {
|
|
1650
|
+
scope,
|
|
1651
|
+
key: `visible:${visibleHooks.indexOf(hook)}`
|
|
1652
|
+
}) ?? runVisibleHook(hook, target);
|
|
1653
|
+
});
|
|
1555
1654
|
if (typeof cleanup === "function") {
|
|
1556
1655
|
cleanups.push(cleanup);
|
|
1557
1656
|
}
|
|
@@ -1561,6 +1660,7 @@ const __componentModule = (() => {
|
|
|
1561
1660
|
while (destroyHooks.length > 0) {
|
|
1562
1661
|
destroyHooks.pop()?.();
|
|
1563
1662
|
}
|
|
1663
|
+
runtime.scheduler?.markScopeDestroyed(scope);
|
|
1564
1664
|
while (cleanups.length > 0) {
|
|
1565
1665
|
cleanups.pop()?.();
|
|
1566
1666
|
}
|
|
@@ -1569,10 +1669,24 @@ const __componentModule = (() => {
|
|
|
1569
1669
|
}
|
|
1570
1670
|
}
|
|
1571
1671
|
};
|
|
1672
|
+
|
|
1673
|
+
function runAttachHook(hook, target) {
|
|
1674
|
+
const cleanup = hook(target);
|
|
1675
|
+
if (typeof cleanup === "function") {
|
|
1676
|
+
cleanups.push(cleanup);
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
|
|
1680
|
+
function runVisibleHook(hook, target) {
|
|
1681
|
+
const cleanup = hook(target);
|
|
1682
|
+
if (typeof cleanup === "function") {
|
|
1683
|
+
cleanups.push(cleanup);
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1572
1686
|
}
|
|
1573
1687
|
|
|
1574
1688
|
function createComponentContext({ runtime, scope, cleanups, attachHooks, visibleHooks, destroyHooks, renderScopedTemplate }) {
|
|
1575
|
-
const { signals, handlers, loader, server, router, cache } = runtime;
|
|
1689
|
+
const { signals, handlers, loader, server, router, cache, scheduler } = runtime;
|
|
1576
1690
|
const generatedHandlers = new WeakMap();
|
|
1577
1691
|
let generatedHandlerCounter = 0;
|
|
1578
1692
|
let generatedSignalCounter = 0;
|
|
@@ -1584,6 +1698,7 @@ const __componentModule = (() => {
|
|
|
1584
1698
|
server,
|
|
1585
1699
|
router,
|
|
1586
1700
|
cache,
|
|
1701
|
+
scheduler,
|
|
1587
1702
|
|
|
1588
1703
|
signal(name, initial) {
|
|
1589
1704
|
if (arguments.length === 1) {
|
|
@@ -1628,7 +1743,11 @@ const __componentModule = (() => {
|
|
|
1628
1743
|
},
|
|
1629
1744
|
|
|
1630
1745
|
effect(fn) {
|
|
1631
|
-
const cleanup = signals.effect(() => fn.call(context)
|
|
1746
|
+
const cleanup = signals.effect(() => fn.call(context), {
|
|
1747
|
+
scheduler,
|
|
1748
|
+
phase: "effect",
|
|
1749
|
+
scope
|
|
1750
|
+
});
|
|
1632
1751
|
cleanups.push(cleanup);
|
|
1633
1752
|
return cleanup;
|
|
1634
1753
|
},
|
|
@@ -1750,90 +1869,9 @@ const __componentModule = (() => {
|
|
|
1750
1869
|
})();
|
|
1751
1870
|
|
|
1752
1871
|
const __serverModule = (() => {
|
|
1753
|
-
const { attachRegistryInspection, createRegistryStore } = __registryStoreModule;
|
|
1754
1872
|
const serverEnvelopeKeys = new Set(["value", "signals", "boundary", "html", "redirect", "error"]);
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
const registryStore = options.registry ?? createRegistryStore();
|
|
1758
|
-
const type = options.type ?? "server";
|
|
1759
|
-
const entries = registryStore._map(type);
|
|
1760
|
-
const defaults = {};
|
|
1761
|
-
|
|
1762
|
-
const registry = attachRegistryInspection({
|
|
1763
|
-
register(id, fn) {
|
|
1764
|
-
assertServerId(id);
|
|
1765
|
-
if (typeof fn !== "function") {
|
|
1766
|
-
throw new TypeError(`Server function "${id}" must be a function.`);
|
|
1767
|
-
}
|
|
1768
|
-
if (entries.has(id)) {
|
|
1769
|
-
throw new Error(`Server function "${id}" is already registered.`);
|
|
1770
|
-
}
|
|
1771
|
-
entries.set(id, fn);
|
|
1772
|
-
return id;
|
|
1773
|
-
},
|
|
1774
|
-
|
|
1775
|
-
registerMany(map) {
|
|
1776
|
-
for (const [id, fn] of Object.entries(map ?? {})) {
|
|
1777
|
-
registry.register(id, fn);
|
|
1778
|
-
}
|
|
1779
|
-
return registry;
|
|
1780
|
-
},
|
|
1781
|
-
|
|
1782
|
-
unregister(id) {
|
|
1783
|
-
assertServerId(id);
|
|
1784
|
-
return entries.delete(id);
|
|
1785
|
-
},
|
|
1786
|
-
|
|
1787
|
-
resolve(id) {
|
|
1788
|
-
assertServerId(id);
|
|
1789
|
-
return entries.get(id);
|
|
1790
|
-
},
|
|
1791
|
-
|
|
1792
|
-
async run(id, args = [], context = {}) {
|
|
1793
|
-
assertServerId(id);
|
|
1794
|
-
const fn = registry.resolve(id);
|
|
1795
|
-
if (!fn) {
|
|
1796
|
-
throw new Error(`Server function "${id}" is not registered.`);
|
|
1797
|
-
}
|
|
1798
|
-
|
|
1799
|
-
let runContext;
|
|
1800
|
-
const server = createServerNamespace((childId, childArgs, childContext = {}) => {
|
|
1801
|
-
return registry.run(childId, childArgs, { ...runContext, ...childContext });
|
|
1802
|
-
}, {}, () => runContext);
|
|
1803
|
-
|
|
1804
|
-
const mergedContext = {
|
|
1805
|
-
...defaults,
|
|
1806
|
-
...context,
|
|
1807
|
-
cache: defaults.cache ?? context.cache
|
|
1808
|
-
};
|
|
1809
|
-
|
|
1810
|
-
runContext = {
|
|
1811
|
-
...mergedContext,
|
|
1812
|
-
id,
|
|
1813
|
-
args,
|
|
1814
|
-
input: mergedContext.input,
|
|
1815
|
-
signals: createSignalReader(mergedContext.signals),
|
|
1816
|
-
abort: mergedContext.abort,
|
|
1817
|
-
cache: mergedContext.cache,
|
|
1818
|
-
server
|
|
1819
|
-
};
|
|
1820
|
-
|
|
1821
|
-
return fn.call(runContext, ...args);
|
|
1822
|
-
},
|
|
1823
|
-
|
|
1824
|
-
_setContext(context = {}) {
|
|
1825
|
-
Object.assign(defaults, context);
|
|
1826
|
-
return registry;
|
|
1827
|
-
},
|
|
1828
|
-
|
|
1829
|
-
_adoptMany() {
|
|
1830
|
-
return registry;
|
|
1831
|
-
}
|
|
1832
|
-
}, registryStore, type);
|
|
1833
|
-
|
|
1834
|
-
registry.registerMany(initialMap);
|
|
1835
|
-
return createServerNamespace((id, args, context) => registry.run(id, args, context), registry, () => defaults);
|
|
1836
|
-
}
|
|
1873
|
+
const appliedServerResult = Symbol.for("@async/framework.appliedServerResult");
|
|
1874
|
+
const appliedServerValues = new WeakSet();
|
|
1837
1875
|
|
|
1838
1876
|
function createServerProxy({
|
|
1839
1877
|
endpoint = "/__async/server",
|
|
@@ -1842,13 +1880,14 @@ const __serverModule = (() => {
|
|
|
1842
1880
|
loader,
|
|
1843
1881
|
router,
|
|
1844
1882
|
cache,
|
|
1883
|
+
scheduler,
|
|
1845
1884
|
headers = {}
|
|
1846
1885
|
} = {}) {
|
|
1847
1886
|
if (typeof fetchImpl !== "function") {
|
|
1848
1887
|
throw new TypeError("createServerProxy(...) requires fetch to be available.");
|
|
1849
1888
|
}
|
|
1850
1889
|
|
|
1851
|
-
const defaults = { signals, loader, router, cache };
|
|
1890
|
+
const defaults = { signals, loader, router, cache, scheduler };
|
|
1852
1891
|
|
|
1853
1892
|
async function run(id, args = [], context = {}) {
|
|
1854
1893
|
assertServerId(id);
|
|
@@ -1858,6 +1897,7 @@ const __serverModule = (() => {
|
|
|
1858
1897
|
input: context.input ?? defaultInput(runContext),
|
|
1859
1898
|
signals: context.signalValues ?? snapshotSignalPaths(context.signalPaths, runContext.signals)
|
|
1860
1899
|
};
|
|
1900
|
+
assertJsonTransportable(body);
|
|
1861
1901
|
|
|
1862
1902
|
const response = await fetchImpl(joinEndpoint(endpoint, id), {
|
|
1863
1903
|
method: "POST",
|
|
@@ -1875,7 +1915,7 @@ const __serverModule = (() => {
|
|
|
1875
1915
|
|
|
1876
1916
|
const result = await readServerResponse(response);
|
|
1877
1917
|
await applyServerResult(result, runContext);
|
|
1878
|
-
return unwrapServerResult(result);
|
|
1918
|
+
return markAppliedServerValue(unwrapServerResult(result));
|
|
1879
1919
|
}
|
|
1880
1920
|
|
|
1881
1921
|
return createServerNamespace(run, {
|
|
@@ -1910,6 +1950,9 @@ const __serverModule = (() => {
|
|
|
1910
1950
|
if (!isServerEnvelope(result)) {
|
|
1911
1951
|
return result;
|
|
1912
1952
|
}
|
|
1953
|
+
if (result[appliedServerResult] || appliedServerValues.has(result)) {
|
|
1954
|
+
return result;
|
|
1955
|
+
}
|
|
1913
1956
|
|
|
1914
1957
|
if (result.signals && context.signals) {
|
|
1915
1958
|
for (const [path, value] of Object.entries(result.signals)) {
|
|
@@ -1933,6 +1976,12 @@ const __serverModule = (() => {
|
|
|
1933
1976
|
throw toError(result.error);
|
|
1934
1977
|
}
|
|
1935
1978
|
|
|
1979
|
+
Object.defineProperty(result, appliedServerResult, {
|
|
1980
|
+
configurable: true,
|
|
1981
|
+
enumerable: false,
|
|
1982
|
+
value: true
|
|
1983
|
+
});
|
|
1984
|
+
|
|
1936
1985
|
return result;
|
|
1937
1986
|
}
|
|
1938
1987
|
|
|
@@ -1943,6 +1992,13 @@ const __serverModule = (() => {
|
|
|
1943
1992
|
return result;
|
|
1944
1993
|
}
|
|
1945
1994
|
|
|
1995
|
+
function markAppliedServerValue(value) {
|
|
1996
|
+
if (value && typeof value === "object") {
|
|
1997
|
+
appliedServerValues.add(value);
|
|
1998
|
+
}
|
|
1999
|
+
return value;
|
|
2000
|
+
}
|
|
2001
|
+
|
|
1946
2002
|
function defaultInput(context = {}) {
|
|
1947
2003
|
const form = findForm(context);
|
|
1948
2004
|
if (form) {
|
|
@@ -2120,6 +2176,30 @@ const __serverModule = (() => {
|
|
|
2120
2176
|
return output;
|
|
2121
2177
|
}
|
|
2122
2178
|
|
|
2179
|
+
function assertJsonTransportable(value, seen = new Set()) {
|
|
2180
|
+
if (value == null || typeof value !== "object") {
|
|
2181
|
+
return;
|
|
2182
|
+
}
|
|
2183
|
+
if (seen.has(value)) {
|
|
2184
|
+
return;
|
|
2185
|
+
}
|
|
2186
|
+
seen.add(value);
|
|
2187
|
+
|
|
2188
|
+
const tag = Object.prototype.toString.call(value);
|
|
2189
|
+
if (tag === "[object File]" || tag === "[object Blob]" || tag === "[object FormData]") {
|
|
2190
|
+
throw new Error("Server proxy JSON transport does not support File, Blob, or FormData values yet.");
|
|
2191
|
+
}
|
|
2192
|
+
if (Array.isArray(value)) {
|
|
2193
|
+
for (const item of value) {
|
|
2194
|
+
assertJsonTransportable(item, seen);
|
|
2195
|
+
}
|
|
2196
|
+
return;
|
|
2197
|
+
}
|
|
2198
|
+
for (const item of Object.values(value)) {
|
|
2199
|
+
assertJsonTransportable(item, seen);
|
|
2200
|
+
}
|
|
2201
|
+
}
|
|
2202
|
+
|
|
2123
2203
|
function joinEndpoint(endpoint, id) {
|
|
2124
2204
|
return `${String(endpoint).replace(/\/$/, "")}/${encodeURIComponent(id)}`;
|
|
2125
2205
|
}
|
|
@@ -2146,7 +2226,7 @@ const __serverModule = (() => {
|
|
|
2146
2226
|
throw new TypeError("Server function id must be a non-empty string.");
|
|
2147
2227
|
}
|
|
2148
2228
|
}
|
|
2149
|
-
return {
|
|
2229
|
+
return { createServerProxy, resolveServerCommandArguments, applyServerResult, unwrapServerResult, defaultInput, createServerNamespace, createSignalReader, assertServerId };
|
|
2150
2230
|
})();
|
|
2151
2231
|
|
|
2152
2232
|
const __handlersModule = (() => {
|
|
@@ -2349,18 +2429,321 @@ const __handlersModule = (() => {
|
|
|
2349
2429
|
return { createHandlerRegistry, parseHandlerRef, isHandlerToken };
|
|
2350
2430
|
})();
|
|
2351
2431
|
|
|
2432
|
+
const __schedulerModule = (() => {
|
|
2433
|
+
const defaultPhases = ["binding", "lifecycle", "effect", "async", "post"];
|
|
2434
|
+
|
|
2435
|
+
function createScheduler(options = {}) {
|
|
2436
|
+
const phases = [...(options.phases ?? defaultPhases)];
|
|
2437
|
+
const queues = new Map(phases.map((phase) => [phase, []]));
|
|
2438
|
+
const keyedJobs = new Map();
|
|
2439
|
+
const destroyedScopes = new Set();
|
|
2440
|
+
const objectScopeIds = new WeakMap();
|
|
2441
|
+
const onError = typeof options.onError === "function" ? options.onError : undefined;
|
|
2442
|
+
const maxDepth = options.maxDepth ?? 100;
|
|
2443
|
+
const strategy = options.strategy ?? "microtask";
|
|
2444
|
+
let destroyed = false;
|
|
2445
|
+
let flushing = false;
|
|
2446
|
+
let scheduled = false;
|
|
2447
|
+
let batchDepth = 0;
|
|
2448
|
+
let jobCounter = 0;
|
|
2449
|
+
let scopeCounter = 0;
|
|
2450
|
+
|
|
2451
|
+
const api = {
|
|
2452
|
+
strategy,
|
|
2453
|
+
phases,
|
|
2454
|
+
|
|
2455
|
+
batch(fn) {
|
|
2456
|
+
if (typeof fn !== "function") {
|
|
2457
|
+
throw new TypeError("scheduler.batch(fn) requires a function.");
|
|
2458
|
+
}
|
|
2459
|
+
assertActive();
|
|
2460
|
+
batchDepth += 1;
|
|
2461
|
+
let asyncBatch = false;
|
|
2462
|
+
try {
|
|
2463
|
+
const value = fn();
|
|
2464
|
+
if (value && typeof value.then === "function") {
|
|
2465
|
+
asyncBatch = true;
|
|
2466
|
+
return value.finally(() => {
|
|
2467
|
+
batchDepth -= 1;
|
|
2468
|
+
requestFlush();
|
|
2469
|
+
});
|
|
2470
|
+
}
|
|
2471
|
+
return value;
|
|
2472
|
+
} finally {
|
|
2473
|
+
if (!asyncBatch && batchDepth > 0) {
|
|
2474
|
+
batchDepth -= 1;
|
|
2475
|
+
requestFlush();
|
|
2476
|
+
}
|
|
2477
|
+
}
|
|
2478
|
+
},
|
|
2479
|
+
|
|
2480
|
+
enqueue(phase, fn, options = {}) {
|
|
2481
|
+
assertActive();
|
|
2482
|
+
assertPhase(phase);
|
|
2483
|
+
if (typeof fn !== "function") {
|
|
2484
|
+
throw new TypeError("scheduler.enqueue(phase, fn) requires a function.");
|
|
2485
|
+
}
|
|
2486
|
+
const scope = options.scope;
|
|
2487
|
+
if (scope !== undefined && destroyedScopes.has(scope)) {
|
|
2488
|
+
return noop;
|
|
2489
|
+
}
|
|
2490
|
+
|
|
2491
|
+
const dedupeKey = options.key === undefined ? undefined : `${phase}:${scopeKey(scope)}:${String(options.key)}`;
|
|
2492
|
+
if (dedupeKey && keyedJobs.has(dedupeKey)) {
|
|
2493
|
+
return keyedJobs.get(dedupeKey).cancel;
|
|
2494
|
+
}
|
|
2495
|
+
|
|
2496
|
+
const job = {
|
|
2497
|
+
id: ++jobCounter,
|
|
2498
|
+
phase,
|
|
2499
|
+
fn,
|
|
2500
|
+
scope,
|
|
2501
|
+
boundary: options.boundary,
|
|
2502
|
+
key: dedupeKey,
|
|
2503
|
+
canceled: false,
|
|
2504
|
+
cancel() {
|
|
2505
|
+
job.canceled = true;
|
|
2506
|
+
if (job.key) {
|
|
2507
|
+
keyedJobs.delete(job.key);
|
|
2508
|
+
}
|
|
2509
|
+
}
|
|
2510
|
+
};
|
|
2511
|
+
queues.get(phase).push(job);
|
|
2512
|
+
if (job.key) {
|
|
2513
|
+
keyedJobs.set(job.key, job);
|
|
2514
|
+
}
|
|
2515
|
+
requestFlush();
|
|
2516
|
+
return job.cancel;
|
|
2517
|
+
},
|
|
2518
|
+
|
|
2519
|
+
afterFlush(fn, options = {}) {
|
|
2520
|
+
return api.enqueue("post", fn, options);
|
|
2521
|
+
},
|
|
2522
|
+
|
|
2523
|
+
async flush() {
|
|
2524
|
+
assertActive();
|
|
2525
|
+
if (flushing) {
|
|
2526
|
+
return;
|
|
2527
|
+
}
|
|
2528
|
+
scheduled = false;
|
|
2529
|
+
flushing = true;
|
|
2530
|
+
let depth = 0;
|
|
2531
|
+
try {
|
|
2532
|
+
while (hasJobs()) {
|
|
2533
|
+
depth += 1;
|
|
2534
|
+
if (depth > maxDepth) {
|
|
2535
|
+
throw new Error(`Scheduler exceeded maxDepth ${maxDepth}.`);
|
|
2536
|
+
}
|
|
2537
|
+
for (const phase of phases) {
|
|
2538
|
+
await flushPhase(phase);
|
|
2539
|
+
}
|
|
2540
|
+
}
|
|
2541
|
+
} finally {
|
|
2542
|
+
flushing = false;
|
|
2543
|
+
if (hasJobs()) {
|
|
2544
|
+
requestFlush();
|
|
2545
|
+
}
|
|
2546
|
+
}
|
|
2547
|
+
},
|
|
2548
|
+
|
|
2549
|
+
async flushScope(scope) {
|
|
2550
|
+
assertActive();
|
|
2551
|
+
if (flushing) {
|
|
2552
|
+
return;
|
|
2553
|
+
}
|
|
2554
|
+
scheduled = false;
|
|
2555
|
+
flushing = true;
|
|
2556
|
+
let depth = 0;
|
|
2557
|
+
try {
|
|
2558
|
+
while (hasJobsForScope(scope)) {
|
|
2559
|
+
depth += 1;
|
|
2560
|
+
if (depth > maxDepth) {
|
|
2561
|
+
throw new Error(`Scheduler exceeded maxDepth ${maxDepth}.`);
|
|
2562
|
+
}
|
|
2563
|
+
for (const phase of phases) {
|
|
2564
|
+
await flushPhase(phase, scope);
|
|
2565
|
+
}
|
|
2566
|
+
}
|
|
2567
|
+
} finally {
|
|
2568
|
+
flushing = false;
|
|
2569
|
+
if (hasJobs()) {
|
|
2570
|
+
requestFlush();
|
|
2571
|
+
}
|
|
2572
|
+
}
|
|
2573
|
+
},
|
|
2574
|
+
|
|
2575
|
+
cancelScope(scope) {
|
|
2576
|
+
if (scope === undefined) {
|
|
2577
|
+
return api;
|
|
2578
|
+
}
|
|
2579
|
+
for (const queue of queues.values()) {
|
|
2580
|
+
for (const job of queue) {
|
|
2581
|
+
if (job.scope === scope) {
|
|
2582
|
+
job.cancel();
|
|
2583
|
+
}
|
|
2584
|
+
}
|
|
2585
|
+
}
|
|
2586
|
+
return api;
|
|
2587
|
+
},
|
|
2588
|
+
|
|
2589
|
+
markScopeDestroyed(scope) {
|
|
2590
|
+
if (scope !== undefined) {
|
|
2591
|
+
destroyedScopes.add(scope);
|
|
2592
|
+
api.cancelScope(scope);
|
|
2593
|
+
}
|
|
2594
|
+
return api;
|
|
2595
|
+
},
|
|
2596
|
+
|
|
2597
|
+
inspect() {
|
|
2598
|
+
const counts = {};
|
|
2599
|
+
for (const [phase, queue] of queues) {
|
|
2600
|
+
counts[phase] = queue.filter((job) => !job.canceled).length;
|
|
2601
|
+
}
|
|
2602
|
+
return {
|
|
2603
|
+
strategy,
|
|
2604
|
+
phases: [...phases],
|
|
2605
|
+
pending: counts,
|
|
2606
|
+
scopesDestroyed: destroyedScopes.size,
|
|
2607
|
+
flushing,
|
|
2608
|
+
scheduled
|
|
2609
|
+
};
|
|
2610
|
+
},
|
|
2611
|
+
|
|
2612
|
+
destroy() {
|
|
2613
|
+
destroyed = true;
|
|
2614
|
+
for (const queue of queues.values()) {
|
|
2615
|
+
for (const job of queue) {
|
|
2616
|
+
job.cancel();
|
|
2617
|
+
}
|
|
2618
|
+
queue.length = 0;
|
|
2619
|
+
}
|
|
2620
|
+
keyedJobs.clear();
|
|
2621
|
+
destroyedScopes.clear();
|
|
2622
|
+
}
|
|
2623
|
+
};
|
|
2624
|
+
|
|
2625
|
+
return api;
|
|
2626
|
+
|
|
2627
|
+
function requestFlush() {
|
|
2628
|
+
if (strategy === "manual" || destroyed || flushing || batchDepth > 0 || scheduled) {
|
|
2629
|
+
return;
|
|
2630
|
+
}
|
|
2631
|
+
scheduled = true;
|
|
2632
|
+
scheduleMicrotask(() => {
|
|
2633
|
+
if (!destroyed) {
|
|
2634
|
+
void api.flush();
|
|
2635
|
+
}
|
|
2636
|
+
});
|
|
2637
|
+
}
|
|
2638
|
+
|
|
2639
|
+
async function flushPhase(phase, scope) {
|
|
2640
|
+
const queue = queues.get(phase);
|
|
2641
|
+
const remaining = [];
|
|
2642
|
+
const runnable = [];
|
|
2643
|
+
|
|
2644
|
+
for (const job of queue.splice(0)) {
|
|
2645
|
+
if (job.canceled) {
|
|
2646
|
+
continue;
|
|
2647
|
+
}
|
|
2648
|
+
if (scope !== undefined && job.scope !== scope) {
|
|
2649
|
+
remaining.push(job);
|
|
2650
|
+
continue;
|
|
2651
|
+
}
|
|
2652
|
+
runnable.push(job);
|
|
2653
|
+
}
|
|
2654
|
+
|
|
2655
|
+
queue.push(...remaining);
|
|
2656
|
+
|
|
2657
|
+
for (const job of runnable) {
|
|
2658
|
+
if (job.key) {
|
|
2659
|
+
keyedJobs.delete(job.key);
|
|
2660
|
+
}
|
|
2661
|
+
if (job.canceled || (job.scope !== undefined && destroyedScopes.has(job.scope))) {
|
|
2662
|
+
continue;
|
|
2663
|
+
}
|
|
2664
|
+
try {
|
|
2665
|
+
await job.fn();
|
|
2666
|
+
} catch (error) {
|
|
2667
|
+
if (onError) {
|
|
2668
|
+
onError(error, job);
|
|
2669
|
+
} else {
|
|
2670
|
+
throw error;
|
|
2671
|
+
}
|
|
2672
|
+
}
|
|
2673
|
+
}
|
|
2674
|
+
}
|
|
2675
|
+
|
|
2676
|
+
function hasJobs() {
|
|
2677
|
+
for (const queue of queues.values()) {
|
|
2678
|
+
if (queue.some((job) => !job.canceled)) {
|
|
2679
|
+
return true;
|
|
2680
|
+
}
|
|
2681
|
+
}
|
|
2682
|
+
return false;
|
|
2683
|
+
}
|
|
2684
|
+
|
|
2685
|
+
function hasJobsForScope(scope) {
|
|
2686
|
+
for (const queue of queues.values()) {
|
|
2687
|
+
if (queue.some((job) => !job.canceled && job.scope === scope)) {
|
|
2688
|
+
return true;
|
|
2689
|
+
}
|
|
2690
|
+
}
|
|
2691
|
+
return false;
|
|
2692
|
+
}
|
|
2693
|
+
|
|
2694
|
+
function assertActive() {
|
|
2695
|
+
if (destroyed) {
|
|
2696
|
+
throw new Error("Scheduler has been destroyed.");
|
|
2697
|
+
}
|
|
2698
|
+
}
|
|
2699
|
+
|
|
2700
|
+
function assertPhase(phase) {
|
|
2701
|
+
if (!queues.has(phase)) {
|
|
2702
|
+
throw new Error(`Unknown scheduler phase "${phase}".`);
|
|
2703
|
+
}
|
|
2704
|
+
}
|
|
2705
|
+
|
|
2706
|
+
function scopeKey(scope) {
|
|
2707
|
+
if (scope === undefined) {
|
|
2708
|
+
return "global";
|
|
2709
|
+
}
|
|
2710
|
+
if ((typeof scope === "object" && scope !== null) || typeof scope === "function") {
|
|
2711
|
+
if (!objectScopeIds.has(scope)) {
|
|
2712
|
+
objectScopeIds.set(scope, `scope:${++scopeCounter}`);
|
|
2713
|
+
}
|
|
2714
|
+
return objectScopeIds.get(scope);
|
|
2715
|
+
}
|
|
2716
|
+
return String(scope);
|
|
2717
|
+
}
|
|
2718
|
+
}
|
|
2719
|
+
|
|
2720
|
+
function scheduleMicrotask(fn) {
|
|
2721
|
+
if (typeof queueMicrotask === "function") {
|
|
2722
|
+
queueMicrotask(fn);
|
|
2723
|
+
return;
|
|
2724
|
+
}
|
|
2725
|
+
Promise.resolve().then(fn);
|
|
2726
|
+
}
|
|
2727
|
+
|
|
2728
|
+
function noop() {}
|
|
2729
|
+
return { createScheduler };
|
|
2730
|
+
})();
|
|
2731
|
+
|
|
2352
2732
|
const __loaderModule = (() => {
|
|
2353
2733
|
const { renderComponent } = __componentModule;
|
|
2354
2734
|
const { createHandlerRegistry } = __handlersModule;
|
|
2735
|
+
const { createScheduler } = __schedulerModule;
|
|
2355
2736
|
const { createSignalRegistry, isSignalRef } = __signalsModule;
|
|
2356
2737
|
const { matchAttribute, normalizeAttributeConfig, readAttribute } = __attributesModule;
|
|
2357
2738
|
const inlineBindingPrefix = "__async:inline:";
|
|
2358
2739
|
|
|
2359
|
-
function Loader({ root, signals, handlers, server, router, cache, attributes } = {}) {
|
|
2740
|
+
function Loader({ root, signals, handlers, server, router, cache, attributes, scheduler } = {}) {
|
|
2360
2741
|
const documentRef = root?.ownerDocument ?? root ?? globalThis.document;
|
|
2361
2742
|
const rootNode = root ?? documentRef;
|
|
2362
2743
|
const signalRegistry = signals ?? createSignalRegistry();
|
|
2363
2744
|
const handlerRegistry = handlers ?? createHandlerRegistry();
|
|
2745
|
+
const schedulerInstance = scheduler ?? createScheduler();
|
|
2746
|
+
const ownsScheduler = !scheduler;
|
|
2364
2747
|
const attributeConfig = normalizeAttributeConfig(attributes);
|
|
2365
2748
|
const cleanups = new Set();
|
|
2366
2749
|
const eventBindings = new WeakMap();
|
|
@@ -2381,6 +2764,7 @@ const __loaderModule = (() => {
|
|
|
2381
2764
|
server,
|
|
2382
2765
|
router,
|
|
2383
2766
|
cache,
|
|
2767
|
+
scheduler: schedulerInstance,
|
|
2384
2768
|
attributes: attributeConfig,
|
|
2385
2769
|
|
|
2386
2770
|
start() {
|
|
@@ -2420,6 +2804,7 @@ const __loaderModule = (() => {
|
|
|
2420
2804
|
server: api.server,
|
|
2421
2805
|
router: api.router,
|
|
2422
2806
|
cache: api.cache,
|
|
2807
|
+
scheduler: schedulerInstance,
|
|
2423
2808
|
attributes: attributeConfig
|
|
2424
2809
|
});
|
|
2425
2810
|
cleanupChildren(target);
|
|
@@ -2440,6 +2825,9 @@ const __loaderModule = (() => {
|
|
|
2440
2825
|
runCleanup(cleanup);
|
|
2441
2826
|
}
|
|
2442
2827
|
cleanups.clear();
|
|
2828
|
+
if (ownsScheduler) {
|
|
2829
|
+
schedulerInstance.destroy();
|
|
2830
|
+
}
|
|
2443
2831
|
},
|
|
2444
2832
|
|
|
2445
2833
|
_observeVisible(target, fn) {
|
|
@@ -2457,13 +2845,14 @@ const __loaderModule = (() => {
|
|
|
2457
2845
|
}
|
|
2458
2846
|
};
|
|
2459
2847
|
|
|
2460
|
-
signalRegistry._setContext?.({ server: api.server, router: api.router, loader: api, cache: api.cache });
|
|
2848
|
+
signalRegistry._setContext?.({ server: api.server, router: api.router, loader: api, cache: api.cache, scheduler: schedulerInstance });
|
|
2461
2849
|
api.server?._setContext?.({
|
|
2462
2850
|
signals: signalRegistry,
|
|
2463
2851
|
handlers: handlerRegistry,
|
|
2464
2852
|
loader: api,
|
|
2465
2853
|
router: api.router,
|
|
2466
|
-
cache: api.cache
|
|
2854
|
+
cache: api.cache,
|
|
2855
|
+
scheduler: schedulerInstance
|
|
2467
2856
|
});
|
|
2468
2857
|
|
|
2469
2858
|
function bindEventAttributes(scope) {
|
|
@@ -2495,18 +2884,19 @@ const __loaderModule = (() => {
|
|
|
2495
2884
|
|
|
2496
2885
|
const listener = async (event) => {
|
|
2497
2886
|
try {
|
|
2498
|
-
await handlerRegistry.run(ref, {
|
|
2887
|
+
await schedulerInstance.batch(() => handlerRegistry.run(ref, {
|
|
2499
2888
|
signals: signalRegistry,
|
|
2500
2889
|
handlers: handlerRegistry,
|
|
2501
2890
|
loader: api,
|
|
2502
2891
|
server: api.server,
|
|
2503
2892
|
router: api.router,
|
|
2504
2893
|
cache: api.cache,
|
|
2894
|
+
scheduler: schedulerInstance,
|
|
2505
2895
|
event,
|
|
2506
2896
|
element,
|
|
2507
2897
|
el: element,
|
|
2508
2898
|
root: rootNode
|
|
2509
|
-
});
|
|
2899
|
+
}));
|
|
2510
2900
|
} catch (error) {
|
|
2511
2901
|
dispatchAsyncError(element, error);
|
|
2512
2902
|
}
|
|
@@ -2622,7 +3012,12 @@ const __loaderModule = (() => {
|
|
|
2622
3012
|
|
|
2623
3013
|
const read = () => readBinding(path, options);
|
|
2624
3014
|
apply(read());
|
|
2625
|
-
addCleanup(subscribeBinding(path, () =>
|
|
3015
|
+
addCleanup(subscribeBinding(path, () => {
|
|
3016
|
+
schedulerInstance.enqueue("binding", () => apply(read()), {
|
|
3017
|
+
scope: element,
|
|
3018
|
+
key
|
|
3019
|
+
});
|
|
3020
|
+
}), element);
|
|
2626
3021
|
}
|
|
2627
3022
|
|
|
2628
3023
|
function bindValueWriter(element, path) {
|
|
@@ -2683,7 +3078,12 @@ const __loaderModule = (() => {
|
|
|
2683
3078
|
const state = {
|
|
2684
3079
|
id,
|
|
2685
3080
|
templates,
|
|
2686
|
-
cleanup: signalRegistry.subscribe(`${id}.$status`, () =>
|
|
3081
|
+
cleanup: signalRegistry.subscribe(`${id}.$status`, () => {
|
|
3082
|
+
schedulerInstance.enqueue("binding", () => renderBoundary(boundary), {
|
|
3083
|
+
scope: boundary,
|
|
3084
|
+
key: `boundary:${id}`
|
|
3085
|
+
});
|
|
3086
|
+
})
|
|
2687
3087
|
};
|
|
2688
3088
|
boundaryState.set(boundary, state);
|
|
2689
3089
|
addCleanup(state.cleanup, boundary);
|
|
@@ -2723,7 +3123,7 @@ const __loaderModule = (() => {
|
|
|
2723
3123
|
}
|
|
2724
3124
|
mountedElements.add(element);
|
|
2725
3125
|
for (const ref of refs) {
|
|
2726
|
-
runPseudo(element, ref);
|
|
3126
|
+
scheduleLifecycle(element, () => runPseudo(element, ref), `attach:${ref}`);
|
|
2727
3127
|
}
|
|
2728
3128
|
}
|
|
2729
3129
|
|
|
@@ -2736,7 +3136,7 @@ const __loaderModule = (() => {
|
|
|
2736
3136
|
continue;
|
|
2737
3137
|
}
|
|
2738
3138
|
visibleElements.add(element);
|
|
2739
|
-
addCleanup(observeVisible(element, () => runPseudo(element, ref)), element);
|
|
3139
|
+
addCleanup(observeVisible(element, () => scheduleLifecycle(element, () => runPseudo(element, ref), `visible:${ref}`)), element);
|
|
2740
3140
|
}
|
|
2741
3141
|
}
|
|
2742
3142
|
|
|
@@ -2760,6 +3160,7 @@ const __loaderModule = (() => {
|
|
|
2760
3160
|
server: api.server,
|
|
2761
3161
|
router: api.router,
|
|
2762
3162
|
cache: api.cache,
|
|
3163
|
+
scheduler: schedulerInstance,
|
|
2763
3164
|
element,
|
|
2764
3165
|
el: element,
|
|
2765
3166
|
root: rootNode
|
|
@@ -2778,10 +3179,13 @@ const __loaderModule = (() => {
|
|
|
2778
3179
|
const ownerWindow = target.ownerDocument?.defaultView ?? globalThis;
|
|
2779
3180
|
const Observer = ownerWindow.IntersectionObserver ?? globalThis.IntersectionObserver;
|
|
2780
3181
|
if (!Observer) {
|
|
2781
|
-
|
|
3182
|
+
schedulerInstance.enqueue("lifecycle", () => {
|
|
2782
3183
|
if (!destroyed) {
|
|
2783
3184
|
fn(target);
|
|
2784
3185
|
}
|
|
3186
|
+
}, {
|
|
3187
|
+
scope: target,
|
|
3188
|
+
key: "visible:fallback"
|
|
2785
3189
|
});
|
|
2786
3190
|
return () => {};
|
|
2787
3191
|
}
|
|
@@ -2836,6 +3240,7 @@ const __loaderModule = (() => {
|
|
|
2836
3240
|
}
|
|
2837
3241
|
for (const element of elementsIn(node)) {
|
|
2838
3242
|
runScopedCleanups(element);
|
|
3243
|
+
schedulerInstance.markScopeDestroyed(element);
|
|
2839
3244
|
}
|
|
2840
3245
|
}
|
|
2841
3246
|
|
|
@@ -2859,6 +3264,13 @@ const __loaderModule = (() => {
|
|
|
2859
3264
|
}
|
|
2860
3265
|
}
|
|
2861
3266
|
|
|
3267
|
+
function scheduleLifecycle(element, fn, key) {
|
|
3268
|
+
schedulerInstance.enqueue("lifecycle", fn, {
|
|
3269
|
+
scope: element,
|
|
3270
|
+
key
|
|
3271
|
+
});
|
|
3272
|
+
}
|
|
3273
|
+
|
|
2862
3274
|
return api;
|
|
2863
3275
|
}
|
|
2864
3276
|
|
|
@@ -3189,6 +3601,7 @@ const __partialsModule = (() => {
|
|
|
3189
3601
|
const __routerModule = (() => {
|
|
3190
3602
|
const { Loader } = __loaderModule;
|
|
3191
3603
|
const { createHandlerRegistry } = __handlersModule;
|
|
3604
|
+
const { createScheduler } = __schedulerModule;
|
|
3192
3605
|
const { createSignalRegistry } = __signalsModule;
|
|
3193
3606
|
const { applyServerResult } = __serverModule;
|
|
3194
3607
|
const { createRegistryStore } = __registryStoreModule;
|
|
@@ -3219,6 +3632,7 @@ const __routerModule = (() => {
|
|
|
3219
3632
|
const nextRoute = normalizeRoute(pattern, definition);
|
|
3220
3633
|
entries.set(pattern, nextRoute.definition);
|
|
3221
3634
|
routes.push(nextRoute);
|
|
3635
|
+
sortRoutes(routes);
|
|
3222
3636
|
return nextRoute;
|
|
3223
3637
|
},
|
|
3224
3638
|
|
|
@@ -3291,6 +3705,7 @@ const __routerModule = (() => {
|
|
|
3291
3705
|
const nextRoute = normalizeRoute(pattern, definition);
|
|
3292
3706
|
entries.set(pattern, nextRoute.definition);
|
|
3293
3707
|
routes.push(nextRoute);
|
|
3708
|
+
sortRoutes(routes);
|
|
3294
3709
|
}
|
|
3295
3710
|
}
|
|
3296
3711
|
|
|
@@ -3307,12 +3722,15 @@ const __routerModule = (() => {
|
|
|
3307
3722
|
partials,
|
|
3308
3723
|
fetch: fetchImpl = globalThis.fetch?.bind(globalThis),
|
|
3309
3724
|
routeEndpoint = "/__async/route",
|
|
3310
|
-
attributes
|
|
3725
|
+
attributes,
|
|
3726
|
+
scheduler
|
|
3311
3727
|
} = {}) {
|
|
3312
3728
|
const documentRef = root?.ownerDocument ?? root ?? globalThis.document;
|
|
3313
3729
|
const rootNode = root ?? documentRef;
|
|
3314
3730
|
const signalRegistry = signals ?? loader?.signals ?? createSignalRegistry();
|
|
3315
3731
|
const handlerRegistry = handlers ?? loader?.handlers ?? createHandlerRegistry();
|
|
3732
|
+
const schedulerInstance = scheduler ?? loader?.scheduler ?? createScheduler();
|
|
3733
|
+
const ownsScheduler = !scheduler && !loader?.scheduler;
|
|
3316
3734
|
const attributeConfig = normalizeAttributeConfig(attributes ?? loader?.attributes);
|
|
3317
3735
|
const loaderInstance =
|
|
3318
3736
|
loader ??
|
|
@@ -3322,11 +3740,14 @@ const __routerModule = (() => {
|
|
|
3322
3740
|
handlers: handlerRegistry,
|
|
3323
3741
|
server,
|
|
3324
3742
|
cache,
|
|
3743
|
+
scheduler: schedulerInstance,
|
|
3325
3744
|
attributes: attributeConfig
|
|
3326
3745
|
});
|
|
3327
3746
|
const ownsLoader = !loader;
|
|
3328
3747
|
const cleanups = new Set();
|
|
3329
3748
|
let destroyed = false;
|
|
3749
|
+
let navigationVersion = 0;
|
|
3750
|
+
let activeNavigation;
|
|
3330
3751
|
|
|
3331
3752
|
const api = {
|
|
3332
3753
|
mode,
|
|
@@ -3339,12 +3760,13 @@ const __routerModule = (() => {
|
|
|
3339
3760
|
server,
|
|
3340
3761
|
cache,
|
|
3341
3762
|
partials,
|
|
3763
|
+
scheduler: schedulerInstance,
|
|
3342
3764
|
attributes: attributeConfig,
|
|
3343
3765
|
|
|
3344
3766
|
start() {
|
|
3345
3767
|
assertActive();
|
|
3346
3768
|
loaderInstance.router = api;
|
|
3347
|
-
signalRegistry._setContext?.({ router: api, loader: loaderInstance, server, cache });
|
|
3769
|
+
signalRegistry._setContext?.({ router: api, loader: loaderInstance, server, cache, scheduler: schedulerInstance });
|
|
3348
3770
|
if (ownsLoader) {
|
|
3349
3771
|
loaderInstance.start();
|
|
3350
3772
|
}
|
|
@@ -3366,7 +3788,7 @@ const __routerModule = (() => {
|
|
|
3366
3788
|
},
|
|
3367
3789
|
|
|
3368
3790
|
match(url) {
|
|
3369
|
-
return routes.match(url);
|
|
3791
|
+
return routes.match(resolveUrl(url));
|
|
3370
3792
|
},
|
|
3371
3793
|
|
|
3372
3794
|
prefetch(url) {
|
|
@@ -3391,7 +3813,7 @@ const __routerModule = (() => {
|
|
|
3391
3813
|
return null;
|
|
3392
3814
|
}
|
|
3393
3815
|
|
|
3394
|
-
const target =
|
|
3816
|
+
const target = resolveUrl(url);
|
|
3395
3817
|
if (mode === "ssr-spa") {
|
|
3396
3818
|
return fetchRoutePartial(target, options);
|
|
3397
3819
|
}
|
|
@@ -3403,10 +3825,14 @@ const __routerModule = (() => {
|
|
|
3403
3825
|
return;
|
|
3404
3826
|
}
|
|
3405
3827
|
destroyed = true;
|
|
3828
|
+
activeNavigation?.controller.abort(new Error("Router has been destroyed."));
|
|
3406
3829
|
for (const cleanup of cleanups) {
|
|
3407
3830
|
cleanup();
|
|
3408
3831
|
}
|
|
3409
3832
|
cleanups.clear();
|
|
3833
|
+
if (ownsScheduler) {
|
|
3834
|
+
schedulerInstance.destroy();
|
|
3835
|
+
}
|
|
3410
3836
|
}
|
|
3411
3837
|
};
|
|
3412
3838
|
|
|
@@ -3442,24 +3868,37 @@ const __routerModule = (() => {
|
|
|
3442
3868
|
async function renderLocalRoutePartial(target, options = {}) {
|
|
3443
3869
|
const matched = api.match(target);
|
|
3444
3870
|
if (!matched) {
|
|
3871
|
+
beginNavigation(target, null);
|
|
3445
3872
|
setNoRouteError(target);
|
|
3446
3873
|
return null;
|
|
3447
3874
|
}
|
|
3448
3875
|
|
|
3876
|
+
const navigation = beginNavigation(target, matched);
|
|
3449
3877
|
setMatchedRouterState(target, matched, { pending: true, error: null });
|
|
3450
3878
|
|
|
3451
3879
|
try {
|
|
3452
3880
|
if (!matched.route?.partial || !partials?.resolve?.(matched.route.partial)) {
|
|
3453
3881
|
const error = new Error(`Route "${target.pathname}" does not have a registered partial.`);
|
|
3454
|
-
|
|
3882
|
+
if (isActiveNavigation(navigation)) {
|
|
3883
|
+
setRouterState({ pending: false, error });
|
|
3884
|
+
}
|
|
3455
3885
|
return null;
|
|
3456
3886
|
}
|
|
3457
3887
|
|
|
3458
|
-
const result = await partials.render(matched.route.partial, matched.params, contextFor(matched));
|
|
3459
|
-
|
|
3888
|
+
const result = await partials.render(matched.route.partial, matched.params, contextFor(matched, navigation));
|
|
3889
|
+
if (!isActiveNavigation(navigation)) {
|
|
3890
|
+
return null;
|
|
3891
|
+
}
|
|
3892
|
+
await applyNavigationResult(result, target, options, navigation);
|
|
3893
|
+
if (!isActiveNavigation(navigation)) {
|
|
3894
|
+
return null;
|
|
3895
|
+
}
|
|
3460
3896
|
setRouterState({ pending: false, error: null });
|
|
3461
3897
|
return result;
|
|
3462
3898
|
} catch (error) {
|
|
3899
|
+
if (!isActiveNavigation(navigation)) {
|
|
3900
|
+
return null;
|
|
3901
|
+
}
|
|
3463
3902
|
setRouterState({ pending: false, error });
|
|
3464
3903
|
throw error;
|
|
3465
3904
|
}
|
|
@@ -3467,28 +3906,48 @@ const __routerModule = (() => {
|
|
|
3467
3906
|
|
|
3468
3907
|
async function fetchRoutePartial(target, options = {}) {
|
|
3469
3908
|
const matched = api.match(target);
|
|
3909
|
+
const navigation = beginNavigation(target, matched);
|
|
3470
3910
|
setMatchedRouterState(target, matched, { pending: true, error: null });
|
|
3471
3911
|
|
|
3472
3912
|
try {
|
|
3473
|
-
const result = await fetchRoute(target.href);
|
|
3474
|
-
|
|
3913
|
+
const result = await fetchRoute(target.href, { signal: navigation.abort });
|
|
3914
|
+
if (!isActiveNavigation(navigation)) {
|
|
3915
|
+
return null;
|
|
3916
|
+
}
|
|
3917
|
+
await applyNavigationResult(result, target, options, navigation);
|
|
3918
|
+
if (!isActiveNavigation(navigation)) {
|
|
3919
|
+
return null;
|
|
3920
|
+
}
|
|
3475
3921
|
setRouterState({ pending: false, error: null });
|
|
3476
3922
|
return result;
|
|
3477
3923
|
} catch (error) {
|
|
3924
|
+
if (!isActiveNavigation(navigation)) {
|
|
3925
|
+
return null;
|
|
3926
|
+
}
|
|
3478
3927
|
setRouterState({ pending: false, error });
|
|
3479
3928
|
throw error;
|
|
3480
3929
|
}
|
|
3481
3930
|
}
|
|
3482
3931
|
|
|
3483
|
-
async function applyNavigationResult(result, target, options) {
|
|
3932
|
+
async function applyNavigationResult(result, target, options, navigation) {
|
|
3933
|
+
if (!isActiveNavigation(navigation)) {
|
|
3934
|
+
return;
|
|
3935
|
+
}
|
|
3484
3936
|
await applyServerResult(result, {
|
|
3485
3937
|
signals: signalRegistry,
|
|
3486
3938
|
loader: loaderInstance,
|
|
3487
3939
|
router: api,
|
|
3488
|
-
cache
|
|
3940
|
+
cache,
|
|
3941
|
+
scheduler: schedulerInstance,
|
|
3942
|
+
abort: navigation?.abort
|
|
3489
3943
|
});
|
|
3944
|
+
await schedulerInstance.flush();
|
|
3945
|
+
if (!isActiveNavigation(navigation)) {
|
|
3946
|
+
return;
|
|
3947
|
+
}
|
|
3490
3948
|
if (result?.html != null && !result.boundary && !result.redirect) {
|
|
3491
3949
|
loaderInstance.swap(boundary, result.html);
|
|
3950
|
+
await schedulerInstance.flush();
|
|
3492
3951
|
}
|
|
3493
3952
|
if (result?.redirect || options.history === false) {
|
|
3494
3953
|
return;
|
|
@@ -3500,14 +3959,15 @@ const __routerModule = (() => {
|
|
|
3500
3959
|
documentRef.defaultView?.history?.pushState?.({}, "", target.href);
|
|
3501
3960
|
}
|
|
3502
3961
|
|
|
3503
|
-
async function fetchRoute(url, { prefetch = false } = {}) {
|
|
3962
|
+
async function fetchRoute(url, { prefetch = false, signal } = {}) {
|
|
3504
3963
|
if (typeof fetchImpl !== "function") {
|
|
3505
3964
|
throw new Error("Router navigation requires a partial registry or fetch.");
|
|
3506
3965
|
}
|
|
3507
3966
|
const response = await fetchImpl(`${routeEndpoint}?to=${encodeURIComponent(String(url))}`, {
|
|
3508
3967
|
headers: {
|
|
3509
3968
|
accept: "application/json, text/html"
|
|
3510
|
-
}
|
|
3969
|
+
},
|
|
3970
|
+
signal
|
|
3511
3971
|
});
|
|
3512
3972
|
if (!response.ok) {
|
|
3513
3973
|
throw new Error(`Route "${url}" failed with ${response.status}.`);
|
|
@@ -3522,7 +3982,7 @@ const __routerModule = (() => {
|
|
|
3522
3982
|
return { boundary, html: await response.text() };
|
|
3523
3983
|
}
|
|
3524
3984
|
|
|
3525
|
-
function contextFor(matched) {
|
|
3985
|
+
function contextFor(matched, navigation) {
|
|
3526
3986
|
return {
|
|
3527
3987
|
params: matched.params,
|
|
3528
3988
|
route: matched.route,
|
|
@@ -3532,8 +3992,27 @@ const __routerModule = (() => {
|
|
|
3532
3992
|
loader: loaderInstance,
|
|
3533
3993
|
server,
|
|
3534
3994
|
cache,
|
|
3535
|
-
|
|
3995
|
+
scheduler: schedulerInstance,
|
|
3996
|
+
abort: navigation?.abort
|
|
3997
|
+
};
|
|
3998
|
+
}
|
|
3999
|
+
|
|
4000
|
+
function beginNavigation(target, matched) {
|
|
4001
|
+
activeNavigation?.controller.abort(new Error(`Router navigation superseded by ${target.pathname}${target.search}.`));
|
|
4002
|
+
const controller = new AbortController();
|
|
4003
|
+
const navigation = {
|
|
4004
|
+
id: ++navigationVersion,
|
|
4005
|
+
controller,
|
|
4006
|
+
abort: controller.signal,
|
|
4007
|
+
target,
|
|
4008
|
+
matched
|
|
3536
4009
|
};
|
|
4010
|
+
activeNavigation = navigation;
|
|
4011
|
+
return navigation;
|
|
4012
|
+
}
|
|
4013
|
+
|
|
4014
|
+
function isActiveNavigation(navigation) {
|
|
4015
|
+
return !destroyed && navigation && activeNavigation?.id === navigation.id && !navigation.abort.aborted;
|
|
3537
4016
|
}
|
|
3538
4017
|
|
|
3539
4018
|
function updateStateFromLocation() {
|
|
@@ -3570,7 +4049,14 @@ const __routerModule = (() => {
|
|
|
3570
4049
|
}
|
|
3571
4050
|
|
|
3572
4051
|
function currentUrl() {
|
|
3573
|
-
return
|
|
4052
|
+
return resolveUrl(documentRef.defaultView?.location?.href ?? "http://localhost/");
|
|
4053
|
+
}
|
|
4054
|
+
|
|
4055
|
+
function resolveUrl(url) {
|
|
4056
|
+
if (url instanceof URL) {
|
|
4057
|
+
return url;
|
|
4058
|
+
}
|
|
4059
|
+
return new URL(String(url), documentRef.defaultView?.location?.href ?? "http://localhost/");
|
|
3574
4060
|
}
|
|
3575
4061
|
|
|
3576
4062
|
function assertActive() {
|
|
@@ -3587,6 +4073,7 @@ const __routerModule = (() => {
|
|
|
3587
4073
|
pattern,
|
|
3588
4074
|
regex,
|
|
3589
4075
|
keys,
|
|
4076
|
+
score: routeScore(pattern),
|
|
3590
4077
|
definition: normalized
|
|
3591
4078
|
};
|
|
3592
4079
|
}
|
|
@@ -3655,6 +4142,28 @@ const __routerModule = (() => {
|
|
|
3655
4142
|
return String(value).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3656
4143
|
}
|
|
3657
4144
|
|
|
4145
|
+
function sortRoutes(routes) {
|
|
4146
|
+
routes.sort((left, right) => right.score - left.score || right.pattern.length - left.pattern.length);
|
|
4147
|
+
}
|
|
4148
|
+
|
|
4149
|
+
function routeScore(pattern) {
|
|
4150
|
+
if (pattern === "*") {
|
|
4151
|
+
return -1;
|
|
4152
|
+
}
|
|
4153
|
+
return pattern
|
|
4154
|
+
.split("/")
|
|
4155
|
+
.filter(Boolean)
|
|
4156
|
+
.reduce((score, segment) => {
|
|
4157
|
+
if (segment === "*") {
|
|
4158
|
+
return score;
|
|
4159
|
+
}
|
|
4160
|
+
if (segment.startsWith(":")) {
|
|
4161
|
+
return score + 2;
|
|
4162
|
+
}
|
|
4163
|
+
return score + 4;
|
|
4164
|
+
}, pattern === "/" ? 3 : 0);
|
|
4165
|
+
}
|
|
4166
|
+
|
|
3658
4167
|
function assertPattern(pattern) {
|
|
3659
4168
|
if (typeof pattern !== "string" || (pattern !== "*" && !pattern.startsWith("/"))) {
|
|
3660
4169
|
throw new TypeError("Route pattern must be a path string or \"*\".");
|
|
@@ -3670,15 +4179,17 @@ const __appModule = (() => {
|
|
|
3670
4179
|
const { Loader } = __loaderModule;
|
|
3671
4180
|
const { createPartialRegistry } = __partialsModule;
|
|
3672
4181
|
const { createRouteRegistry, createRouter } = __routerModule;
|
|
3673
|
-
const {
|
|
4182
|
+
const { createScheduler } = __schedulerModule;
|
|
4183
|
+
const { createServerNamespace } = __serverModule;
|
|
3674
4184
|
const { createSignal, createSignalRegistry } = __signalsModule;
|
|
3675
4185
|
const { createRegistryStore } = __registryStoreModule;
|
|
3676
4186
|
const { attributeName, normalizeAttributeConfig } = __attributesModule;
|
|
3677
4187
|
const registryTypes = new Set(["signal", "handler", "server", "partial", "route", "component"]);
|
|
3678
4188
|
|
|
3679
|
-
function defineApp(initial) {
|
|
4189
|
+
function defineApp(initial, options = {}) {
|
|
3680
4190
|
const registry = createRegistryStore(undefined, { target: "browser" });
|
|
3681
4191
|
const runtimes = new Set();
|
|
4192
|
+
const createRuntime = options.createRuntime ?? createApp;
|
|
3682
4193
|
|
|
3683
4194
|
const app = {
|
|
3684
4195
|
registry,
|
|
@@ -3697,7 +4208,7 @@ const __appModule = (() => {
|
|
|
3697
4208
|
},
|
|
3698
4209
|
|
|
3699
4210
|
start(options = {}) {
|
|
3700
|
-
const runtime =
|
|
4211
|
+
const runtime = createRuntime(app, options).start();
|
|
3701
4212
|
app.runtime = runtime;
|
|
3702
4213
|
return runtime;
|
|
3703
4214
|
},
|
|
@@ -3722,13 +4233,18 @@ const __appModule = (() => {
|
|
|
3722
4233
|
function createApp(appOrDefinition = Async, options = {}) {
|
|
3723
4234
|
const app = isAppHub(appOrDefinition) ? appOrDefinition : defineApp(appOrDefinition ?? {});
|
|
3724
4235
|
const target = options.target ?? "browser";
|
|
4236
|
+
const scheduler = options.scheduler ?? options.loader?.scheduler ?? createScheduler({
|
|
4237
|
+
strategy: target === "server" ? "manual" : "microtask"
|
|
4238
|
+
});
|
|
4239
|
+
const ownsScheduler = !options.scheduler && !options.loader?.scheduler;
|
|
3725
4240
|
const attributes = normalizeAttributeConfig(options.attributes);
|
|
3726
4241
|
const registry = options.registry ?? app.registry.view({ target });
|
|
3727
4242
|
const signals = options.signals ?? createSignalRegistry(undefined, { registry, type: "signal" });
|
|
3728
4243
|
const handlers = options.handlers ?? createHandlerRegistry(undefined, { registry, type: "handler" });
|
|
3729
4244
|
const serverCache = createCacheRegistry(undefined, { registry, type: "cache.server" });
|
|
3730
4245
|
const browserCache = createCacheRegistry(undefined, { registry, type: "cache.browser" });
|
|
3731
|
-
const
|
|
4246
|
+
const serverFactory = options.serverFactory ?? createServerReferenceRegistry;
|
|
4247
|
+
const server = options.server ?? serverFactory(undefined, { registry, type: "server" });
|
|
3732
4248
|
const partials = options.partials ?? createPartialRegistry(undefined, { registry, type: "partial" });
|
|
3733
4249
|
const routes = options.routes ?? createRouteRegistry(undefined, { registry, type: "route" });
|
|
3734
4250
|
const components = options.components ?? createComponentRegistry(undefined, { registry, type: "component" });
|
|
@@ -3738,7 +4254,7 @@ const __appModule = (() => {
|
|
|
3738
4254
|
let started = false;
|
|
3739
4255
|
let destroyed = false;
|
|
3740
4256
|
|
|
3741
|
-
applySnapshot(signals, browserCache, options.snapshot);
|
|
4257
|
+
applySnapshot(signals, browserCache, options.snapshot ?? (target === "browser" ? readSnapshot(options.root, { attributes }) : undefined));
|
|
3742
4258
|
attachServerCache(server, serverCache);
|
|
3743
4259
|
|
|
3744
4260
|
const runtime = {
|
|
@@ -3756,6 +4272,7 @@ const __appModule = (() => {
|
|
|
3756
4272
|
},
|
|
3757
4273
|
loader,
|
|
3758
4274
|
router,
|
|
4275
|
+
scheduler,
|
|
3759
4276
|
attributes,
|
|
3760
4277
|
|
|
3761
4278
|
start() {
|
|
@@ -3772,12 +4289,13 @@ const __appModule = (() => {
|
|
|
3772
4289
|
handlers,
|
|
3773
4290
|
server,
|
|
3774
4291
|
cache: browserCache,
|
|
4292
|
+
scheduler,
|
|
3775
4293
|
attributes
|
|
3776
4294
|
});
|
|
3777
4295
|
runtime.loader = loader;
|
|
3778
4296
|
|
|
3779
4297
|
configureServerContext({ cache: browserCache });
|
|
3780
|
-
signals._setContext?.({ server, loader, cache: browserCache });
|
|
4298
|
+
signals._setContext?.({ server, loader, cache: browserCache, scheduler });
|
|
3781
4299
|
|
|
3782
4300
|
loader.start();
|
|
3783
4301
|
|
|
@@ -3793,6 +4311,7 @@ const __appModule = (() => {
|
|
|
3793
4311
|
server,
|
|
3794
4312
|
cache: browserCache,
|
|
3795
4313
|
partials,
|
|
4314
|
+
scheduler,
|
|
3796
4315
|
fetch: options.fetch,
|
|
3797
4316
|
routeEndpoint: options.routeEndpoint,
|
|
3798
4317
|
attributes
|
|
@@ -3804,7 +4323,7 @@ const __appModule = (() => {
|
|
|
3804
4323
|
}
|
|
3805
4324
|
} else {
|
|
3806
4325
|
configureServerContext({ cache: serverCache });
|
|
3807
|
-
signals._setContext?.({ server, cache: serverCache });
|
|
4326
|
+
signals._setContext?.({ server, cache: serverCache, scheduler });
|
|
3808
4327
|
}
|
|
3809
4328
|
|
|
3810
4329
|
return runtime;
|
|
@@ -3818,9 +4337,10 @@ const __appModule = (() => {
|
|
|
3818
4337
|
async render(url) {
|
|
3819
4338
|
assertActive();
|
|
3820
4339
|
configureServerContext({ cache: serverCache });
|
|
3821
|
-
signals._setContext?.({ server, cache: serverCache });
|
|
4340
|
+
signals._setContext?.({ server, cache: serverCache, scheduler });
|
|
3822
4341
|
const matched = routes.match(url);
|
|
3823
4342
|
if (!matched) {
|
|
4343
|
+
await scheduler.flush();
|
|
3824
4344
|
return {
|
|
3825
4345
|
html: renderDocument("", { status: 404, signals, browserCache, boundary: options.boundary ?? "route", attributes }),
|
|
3826
4346
|
status: 404,
|
|
@@ -3840,8 +4360,8 @@ const __appModule = (() => {
|
|
|
3840
4360
|
cache: serverCache,
|
|
3841
4361
|
browserCache,
|
|
3842
4362
|
partials,
|
|
3843
|
-
|
|
3844
|
-
|
|
4363
|
+
scheduler,
|
|
4364
|
+
...currentRequestContext()
|
|
3845
4365
|
})
|
|
3846
4366
|
: { html: "" };
|
|
3847
4367
|
|
|
@@ -3854,6 +4374,8 @@ const __appModule = (() => {
|
|
|
3854
4374
|
browserCache.restore(result.cache.browser);
|
|
3855
4375
|
}
|
|
3856
4376
|
|
|
4377
|
+
await scheduler.flush();
|
|
4378
|
+
|
|
3857
4379
|
const status = result.status ?? 200;
|
|
3858
4380
|
return {
|
|
3859
4381
|
html: renderDocument(result.html, { status, signals, browserCache, boundary: result.boundary ?? options.boundary ?? "route", attributes }),
|
|
@@ -3872,6 +4394,9 @@ const __appModule = (() => {
|
|
|
3872
4394
|
router?.destroy?.();
|
|
3873
4395
|
loader?.destroy?.();
|
|
3874
4396
|
signals.destroy?.();
|
|
4397
|
+
if (ownsScheduler) {
|
|
4398
|
+
scheduler.destroy();
|
|
4399
|
+
}
|
|
3875
4400
|
},
|
|
3876
4401
|
|
|
3877
4402
|
_applyUse(normalized) {
|
|
@@ -3892,11 +4417,23 @@ const __appModule = (() => {
|
|
|
3892
4417
|
loader,
|
|
3893
4418
|
router,
|
|
3894
4419
|
cache,
|
|
3895
|
-
|
|
3896
|
-
|
|
4420
|
+
scheduler,
|
|
4421
|
+
requestContext: options.requestContext,
|
|
4422
|
+
...currentRequestContext()
|
|
3897
4423
|
});
|
|
3898
4424
|
}
|
|
3899
4425
|
|
|
4426
|
+
function currentRequestContext() {
|
|
4427
|
+
const context = readRequestContextLike(options.requestContext);
|
|
4428
|
+
return {
|
|
4429
|
+
requestContext: context,
|
|
4430
|
+
request: context.request ?? options.request,
|
|
4431
|
+
headers: context.headers ?? options.headers,
|
|
4432
|
+
cookies: context.cookies ?? options.cookies,
|
|
4433
|
+
locals: context.locals ?? options.locals
|
|
4434
|
+
};
|
|
4435
|
+
}
|
|
4436
|
+
|
|
3900
4437
|
function assertActive() {
|
|
3901
4438
|
if (destroyed) {
|
|
3902
4439
|
throw new Error("Async app runtime has been destroyed.");
|
|
@@ -3906,6 +4443,38 @@ const __appModule = (() => {
|
|
|
3906
4443
|
|
|
3907
4444
|
const Async = defineApp();
|
|
3908
4445
|
|
|
4446
|
+
function readSnapshot(root = globalThis.document, { attributes } = {}) {
|
|
4447
|
+
const attributeConfig = normalizeAttributeConfig(attributes);
|
|
4448
|
+
const snapshotAttr = attributeName(attributeConfig, "async", "snapshot");
|
|
4449
|
+
const documentRef = root?.ownerDocument ?? root ?? globalThis.document;
|
|
4450
|
+
const rootNode = root ?? documentRef;
|
|
4451
|
+
if (!rootNode?.querySelectorAll && !documentRef?.querySelectorAll) {
|
|
4452
|
+
return {};
|
|
4453
|
+
}
|
|
4454
|
+
|
|
4455
|
+
for (const searchRoot of new Set([rootNode, documentRef])) {
|
|
4456
|
+
if (!searchRoot?.querySelectorAll) {
|
|
4457
|
+
continue;
|
|
4458
|
+
}
|
|
4459
|
+
for (const script of searchRoot.querySelectorAll("script[type='application/json'], script")) {
|
|
4460
|
+
if (!script.hasAttribute?.(snapshotAttr)) {
|
|
4461
|
+
continue;
|
|
4462
|
+
}
|
|
4463
|
+
const source = script.textContent?.trim() ?? "";
|
|
4464
|
+
if (!source) {
|
|
4465
|
+
return {};
|
|
4466
|
+
}
|
|
4467
|
+
try {
|
|
4468
|
+
return JSON.parse(source);
|
|
4469
|
+
} catch (cause) {
|
|
4470
|
+
throw new Error(`Could not parse Async snapshot: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
4471
|
+
}
|
|
4472
|
+
}
|
|
4473
|
+
}
|
|
4474
|
+
|
|
4475
|
+
return {};
|
|
4476
|
+
}
|
|
4477
|
+
|
|
3909
4478
|
function applyUseToRuntime(runtime, normalized) {
|
|
3910
4479
|
applyRegistryUse(runtime.signals, runtime.registry, normalized.signal);
|
|
3911
4480
|
applyRegistryUse(runtime.handlers, runtime.registry, normalized.handler);
|
|
@@ -4018,6 +4587,77 @@ const __appModule = (() => {
|
|
|
4018
4587
|
}
|
|
4019
4588
|
}
|
|
4020
4589
|
|
|
4590
|
+
function createServerReferenceRegistry(initialMap = {}, options = {}) {
|
|
4591
|
+
const registry = options.registry ?? createRegistryStore();
|
|
4592
|
+
const type = options.type ?? "server";
|
|
4593
|
+
const defaults = {};
|
|
4594
|
+
|
|
4595
|
+
const reference = {
|
|
4596
|
+
registry,
|
|
4597
|
+
|
|
4598
|
+
register(id, value) {
|
|
4599
|
+
registry.register(type, id, value);
|
|
4600
|
+
return id;
|
|
4601
|
+
},
|
|
4602
|
+
|
|
4603
|
+
registerMany(map) {
|
|
4604
|
+
for (const [id, value] of Object.entries(map ?? {})) {
|
|
4605
|
+
reference.register(id, value);
|
|
4606
|
+
}
|
|
4607
|
+
return reference;
|
|
4608
|
+
},
|
|
4609
|
+
|
|
4610
|
+
unregister(id) {
|
|
4611
|
+
return registry.unregister(type, id);
|
|
4612
|
+
},
|
|
4613
|
+
|
|
4614
|
+
resolve() {
|
|
4615
|
+
return undefined;
|
|
4616
|
+
},
|
|
4617
|
+
|
|
4618
|
+
async run(id) {
|
|
4619
|
+
throw new Error(`Server command "${id}" cannot run without a server proxy or server registry.`);
|
|
4620
|
+
},
|
|
4621
|
+
|
|
4622
|
+
keys() {
|
|
4623
|
+
return registry.keys(type);
|
|
4624
|
+
},
|
|
4625
|
+
|
|
4626
|
+
entries() {
|
|
4627
|
+
return registry.entries(type);
|
|
4628
|
+
},
|
|
4629
|
+
|
|
4630
|
+
inspect() {
|
|
4631
|
+
return registry.entries(type);
|
|
4632
|
+
},
|
|
4633
|
+
|
|
4634
|
+
_setContext(context = {}) {
|
|
4635
|
+
Object.assign(defaults, context);
|
|
4636
|
+
return reference;
|
|
4637
|
+
},
|
|
4638
|
+
|
|
4639
|
+
_adoptMany() {
|
|
4640
|
+
return reference;
|
|
4641
|
+
}
|
|
4642
|
+
};
|
|
4643
|
+
|
|
4644
|
+
reference.registerMany(initialMap);
|
|
4645
|
+
return createServerNamespace((id, args, context) => reference.run(id, args, context), reference, () => defaults);
|
|
4646
|
+
}
|
|
4647
|
+
|
|
4648
|
+
function readRequestContextLike(store) {
|
|
4649
|
+
if (!store) {
|
|
4650
|
+
return {};
|
|
4651
|
+
}
|
|
4652
|
+
if (typeof store.get === "function") {
|
|
4653
|
+
return store.get() ?? {};
|
|
4654
|
+
}
|
|
4655
|
+
if (typeof store.getStore === "function") {
|
|
4656
|
+
return store.getStore() ?? {};
|
|
4657
|
+
}
|
|
4658
|
+
return {};
|
|
4659
|
+
}
|
|
4660
|
+
|
|
4021
4661
|
function normalizeEntries(type, entries = {}) {
|
|
4022
4662
|
if (type !== "signal") {
|
|
4023
4663
|
return { ...(entries ?? {}) };
|
|
@@ -4066,7 +4706,7 @@ const __appModule = (() => {
|
|
|
4066
4706
|
function escapeScriptJson(value) {
|
|
4067
4707
|
return JSON.stringify(value).replaceAll("<", "\\u003c");
|
|
4068
4708
|
}
|
|
4069
|
-
return { defineApp, createApp, Async };
|
|
4709
|
+
return { defineApp, createApp, readSnapshot, Async };
|
|
4070
4710
|
})();
|
|
4071
4711
|
|
|
4072
4712
|
const __delayModule = (() => {
|
|
@@ -4107,6 +4747,7 @@ const { asyncSignal: asyncSignal } = __asyncSignalModule;
|
|
|
4107
4747
|
const { Async: Async } = __appModule;
|
|
4108
4748
|
const { createApp: createApp } = __appModule;
|
|
4109
4749
|
const { defineApp: defineApp } = __appModule;
|
|
4750
|
+
const { readSnapshot: readSnapshot } = __appModule;
|
|
4110
4751
|
const { attributeName: attributeName } = __attributesModule;
|
|
4111
4752
|
const { defineAttributeConfig: defineAttributeConfig } = __attributesModule;
|
|
4112
4753
|
const { createCacheRegistry: createCacheRegistry } = __cacheModule;
|
|
@@ -4125,12 +4766,15 @@ const { createRouteRegistry: createRouteRegistry } = __routerModule;
|
|
|
4125
4766
|
const { createRouter: createRouter } = __routerModule;
|
|
4126
4767
|
const { defineRoute: defineRoute } = __routerModule;
|
|
4127
4768
|
const { route: route } = __routerModule;
|
|
4769
|
+
const { createScheduler: createScheduler } = __schedulerModule;
|
|
4770
|
+
const { applyServerResult: applyServerResult } = __serverModule;
|
|
4128
4771
|
const { createServerProxy: createServerProxy } = __serverModule;
|
|
4129
|
-
const {
|
|
4772
|
+
const { resolveServerCommandArguments: resolveServerCommandArguments } = __serverModule;
|
|
4773
|
+
const { unwrapServerResult: unwrapServerResult } = __serverModule;
|
|
4130
4774
|
const { computed: computed } = __signalsModule;
|
|
4131
4775
|
const { createSignal: createSignal } = __signalsModule;
|
|
4132
4776
|
const { createSignalRegistry: createSignalRegistry } = __signalsModule;
|
|
4133
4777
|
const { effect: effect } = __signalsModule;
|
|
4134
4778
|
const { signal: signal } = __signalsModule;
|
|
4135
4779
|
|
|
4136
|
-
export { asyncSignal, Async, createApp, defineApp, attributeName, defineAttributeConfig, createCacheRegistry, defineCache, component, createComponentRegistry, defineComponent, delay, createHandlerRegistry, html, Loader, AsyncLoader, createPartialRegistry, createRegistryStore, createRouteRegistry, createRouter, defineRoute, route, createServerProxy,
|
|
4780
|
+
export { 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 };
|