@async/framework 0.7.0 → 0.9.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 +22 -0
- package/README.md +122 -14
- package/{framework.d.ts → browser.d.ts} +118 -4
- package/{framework.js → browser.js} +898 -127
- package/browser.min.js +1 -0
- package/{framework.ts → browser.ts} +899 -128
- package/{framework.umd.js → browser.umd.js} +898 -127
- package/browser.umd.min.js +1 -0
- package/package.json +45 -30
- package/server.d.ts +690 -0
- package/src/app.js +110 -11
- package/src/async-signal.js +32 -4
- package/src/boundary-receiver.js +302 -0
- package/src/browser.js +16 -0
- package/src/component.js +42 -7
- package/src/index.js +6 -2
- package/src/loader.js +42 -10
- package/src/request-context.js +40 -0
- package/src/router.js +15 -2
- package/src/scheduler.js +300 -0
- package/src/server-entry.js +20 -0
- package/src/server-registry.js +97 -0
- package/src/server.js +5 -88
- package/src/signals.js +38 -6
- package/framework.min.js +0 -3820
- package/framework.umd.min.js +0 -3843
|
@@ -105,7 +105,8 @@
|
|
|
105
105
|
router: context.router,
|
|
106
106
|
loader: context.loader,
|
|
107
107
|
cache: context.cache,
|
|
108
|
-
abort: activeAbort
|
|
108
|
+
abort: activeAbort,
|
|
109
|
+
scheduler: context.scheduler
|
|
109
110
|
});
|
|
110
111
|
}
|
|
111
112
|
return server;
|
|
@@ -119,6 +120,9 @@
|
|
|
119
120
|
get cache() {
|
|
120
121
|
return registry._context?.().cache;
|
|
121
122
|
},
|
|
123
|
+
get scheduler() {
|
|
124
|
+
return registry._context?.().scheduler;
|
|
125
|
+
},
|
|
122
126
|
get version() {
|
|
123
127
|
return runVersion;
|
|
124
128
|
},
|
|
@@ -195,11 +199,20 @@
|
|
|
195
199
|
_bindRegistry(nextRegistry, nextId) {
|
|
196
200
|
registry = nextRegistry;
|
|
197
201
|
registeredId = nextId;
|
|
198
|
-
|
|
202
|
+
const start = () => {
|
|
199
203
|
if (registry === nextRegistry && status === "idle") {
|
|
200
204
|
state.refresh();
|
|
201
205
|
}
|
|
202
|
-
}
|
|
206
|
+
};
|
|
207
|
+
const scheduler = registry._context?.().scheduler;
|
|
208
|
+
if (scheduler) {
|
|
209
|
+
scheduler.enqueue("async", start, {
|
|
210
|
+
scope: registeredId,
|
|
211
|
+
key: `asyncSignal:${registeredId}:initial`
|
|
212
|
+
});
|
|
213
|
+
} else {
|
|
214
|
+
queueMicrotask(start);
|
|
215
|
+
}
|
|
203
216
|
},
|
|
204
217
|
|
|
205
218
|
_dispose() {
|
|
@@ -235,11 +248,26 @@
|
|
|
235
248
|
for (const dependency of dependencies) {
|
|
236
249
|
const dependencyId = String(dependency).split(".")[0];
|
|
237
250
|
if (dependencyId && dependencyId !== registeredId) {
|
|
238
|
-
dependencyCleanups.add(registry.subscribe(dependency, () =>
|
|
251
|
+
dependencyCleanups.add(registry.subscribe(dependency, () => scheduleRefresh()));
|
|
239
252
|
}
|
|
240
253
|
}
|
|
241
254
|
}
|
|
242
255
|
|
|
256
|
+
function scheduleRefresh() {
|
|
257
|
+
if (activeAbort && !activeAbort.aborted) {
|
|
258
|
+
activeAbort.cancel(new Error(`Async signal "${registeredId}" dependency changed.`));
|
|
259
|
+
}
|
|
260
|
+
const scheduler = registry?._context?.().scheduler;
|
|
261
|
+
if (!scheduler) {
|
|
262
|
+
state.refresh();
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
scheduler.enqueue("async", () => state.refresh(), {
|
|
266
|
+
scope: registeredId,
|
|
267
|
+
key: `asyncSignal:${registeredId}:refresh`
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
|
|
243
271
|
function notify() {
|
|
244
272
|
for (const subscriber of [...subscribers]) {
|
|
245
273
|
subscriber(state);
|
|
@@ -876,7 +904,8 @@
|
|
|
876
904
|
server: registry._context?.().server,
|
|
877
905
|
router: registry._context?.().router,
|
|
878
906
|
loader: registry._context?.().loader,
|
|
879
|
-
cache: registry._context?.().cache
|
|
907
|
+
cache: registry._context?.().cache,
|
|
908
|
+
scheduler: registry._context?.().scheduler
|
|
880
909
|
}));
|
|
881
910
|
});
|
|
882
911
|
}
|
|
@@ -904,6 +933,8 @@
|
|
|
904
933
|
const registryCleanups = new Map();
|
|
905
934
|
const runtimeContext = {};
|
|
906
935
|
const boundEntries = new Set();
|
|
936
|
+
let subscriptionCounter = 0;
|
|
937
|
+
let effectCounter = 0;
|
|
907
938
|
|
|
908
939
|
const registry = attachRegistryInspection({
|
|
909
940
|
register(id, signalLike) {
|
|
@@ -979,17 +1010,21 @@
|
|
|
979
1010
|
return createRef(registry, id);
|
|
980
1011
|
},
|
|
981
1012
|
|
|
982
|
-
subscribe(path, fn) {
|
|
1013
|
+
subscribe(path, fn, options = {}) {
|
|
983
1014
|
if (typeof fn !== "function") {
|
|
984
1015
|
throw new TypeError("subscribe(path, fn) requires a function.");
|
|
985
1016
|
}
|
|
986
1017
|
const parsed = parsePath(path, entries);
|
|
987
1018
|
const entry = requireEntry(entries, parsed.id);
|
|
1019
|
+
const subscriptionId = ++subscriptionCounter;
|
|
988
1020
|
return entry.subscribe(() => {
|
|
989
|
-
fn(registry.get(parsed.path), {
|
|
1021
|
+
scheduleCallback(() => fn(registry.get(parsed.path), {
|
|
990
1022
|
id: parsed.id,
|
|
991
1023
|
path: parsed.path,
|
|
992
1024
|
signal: entry
|
|
1025
|
+
}), {
|
|
1026
|
+
...options,
|
|
1027
|
+
key: options.key ?? `signal:${parsed.path}:${subscriptionId}`
|
|
993
1028
|
});
|
|
994
1029
|
});
|
|
995
1030
|
},
|
|
@@ -1007,10 +1042,12 @@
|
|
|
1007
1042
|
return registry.ref(id);
|
|
1008
1043
|
},
|
|
1009
1044
|
|
|
1010
|
-
effect(fn) {
|
|
1045
|
+
effect(fn, options = {}) {
|
|
1011
1046
|
let cleanup;
|
|
1012
1047
|
let dependencyCleanups = [];
|
|
1013
1048
|
let stopped = false;
|
|
1049
|
+
const scheduler = options.scheduler;
|
|
1050
|
+
const effectId = ++effectCounter;
|
|
1014
1051
|
|
|
1015
1052
|
const run = () => {
|
|
1016
1053
|
if (stopped) {
|
|
@@ -1029,10 +1066,22 @@
|
|
|
1029
1066
|
server: runtimeContext.server,
|
|
1030
1067
|
router: runtimeContext.router,
|
|
1031
1068
|
loader: runtimeContext.loader,
|
|
1032
|
-
cache: runtimeContext.cache
|
|
1069
|
+
cache: runtimeContext.cache,
|
|
1070
|
+
scheduler: runtimeContext.scheduler
|
|
1033
1071
|
}));
|
|
1034
1072
|
cleanup = outcome.value;
|
|
1035
|
-
dependencyCleanups = outcome.dependencies.map((dependency) => registry.subscribe(dependency,
|
|
1073
|
+
dependencyCleanups = outcome.dependencies.map((dependency) => registry.subscribe(dependency, scheduleRun));
|
|
1074
|
+
};
|
|
1075
|
+
|
|
1076
|
+
const scheduleRun = () => {
|
|
1077
|
+
if (!scheduler) {
|
|
1078
|
+
run();
|
|
1079
|
+
return;
|
|
1080
|
+
}
|
|
1081
|
+
scheduler.enqueue(options.phase ?? "effect", run, {
|
|
1082
|
+
scope: options.scope,
|
|
1083
|
+
key: options.key ?? `effect:${effectId}`
|
|
1084
|
+
});
|
|
1036
1085
|
};
|
|
1037
1086
|
|
|
1038
1087
|
run();
|
|
@@ -1109,6 +1158,17 @@
|
|
|
1109
1158
|
registryCleanups.set(id, cleanup);
|
|
1110
1159
|
}
|
|
1111
1160
|
}
|
|
1161
|
+
|
|
1162
|
+
function scheduleCallback(fn, options = {}) {
|
|
1163
|
+
const scheduler = options.scheduler;
|
|
1164
|
+
if (!scheduler || options.phase === "sync") {
|
|
1165
|
+
return fn();
|
|
1166
|
+
}
|
|
1167
|
+
return scheduler.enqueue(options.phase ?? "effect", fn, {
|
|
1168
|
+
scope: options.scope,
|
|
1169
|
+
key: options.key
|
|
1170
|
+
});
|
|
1171
|
+
}
|
|
1112
1172
|
}
|
|
1113
1173
|
|
|
1114
1174
|
function normalizeSignal(signalLike) {
|
|
@@ -1575,10 +1635,15 @@
|
|
|
1575
1635
|
html,
|
|
1576
1636
|
attach(target) {
|
|
1577
1637
|
for (const hook of attachHooks) {
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1638
|
+
runtime.scheduler?.enqueue("lifecycle", () => {
|
|
1639
|
+
const cleanup = hook(target);
|
|
1640
|
+
if (typeof cleanup === "function") {
|
|
1641
|
+
cleanups.push(cleanup);
|
|
1642
|
+
}
|
|
1643
|
+
}, {
|
|
1644
|
+
scope,
|
|
1645
|
+
key: `attach:${attachHooks.indexOf(hook)}`
|
|
1646
|
+
}) ?? runAttachHook(hook, target);
|
|
1582
1647
|
}
|
|
1583
1648
|
},
|
|
1584
1649
|
mount(target) {
|
|
@@ -1586,7 +1651,17 @@
|
|
|
1586
1651
|
},
|
|
1587
1652
|
visible(target, observeVisible) {
|
|
1588
1653
|
for (const hook of visibleHooks) {
|
|
1589
|
-
const cleanup = observeVisible(target,
|
|
1654
|
+
const cleanup = observeVisible(target, () => {
|
|
1655
|
+
runtime.scheduler?.enqueue("lifecycle", () => {
|
|
1656
|
+
const hookCleanup = hook(target);
|
|
1657
|
+
if (typeof hookCleanup === "function") {
|
|
1658
|
+
cleanups.push(hookCleanup);
|
|
1659
|
+
}
|
|
1660
|
+
}, {
|
|
1661
|
+
scope,
|
|
1662
|
+
key: `visible:${visibleHooks.indexOf(hook)}`
|
|
1663
|
+
}) ?? runVisibleHook(hook, target);
|
|
1664
|
+
});
|
|
1590
1665
|
if (typeof cleanup === "function") {
|
|
1591
1666
|
cleanups.push(cleanup);
|
|
1592
1667
|
}
|
|
@@ -1596,6 +1671,7 @@
|
|
|
1596
1671
|
while (destroyHooks.length > 0) {
|
|
1597
1672
|
destroyHooks.pop()?.();
|
|
1598
1673
|
}
|
|
1674
|
+
runtime.scheduler?.markScopeDestroyed(scope);
|
|
1599
1675
|
while (cleanups.length > 0) {
|
|
1600
1676
|
cleanups.pop()?.();
|
|
1601
1677
|
}
|
|
@@ -1604,10 +1680,24 @@
|
|
|
1604
1680
|
}
|
|
1605
1681
|
}
|
|
1606
1682
|
};
|
|
1683
|
+
|
|
1684
|
+
function runAttachHook(hook, target) {
|
|
1685
|
+
const cleanup = hook(target);
|
|
1686
|
+
if (typeof cleanup === "function") {
|
|
1687
|
+
cleanups.push(cleanup);
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
|
|
1691
|
+
function runVisibleHook(hook, target) {
|
|
1692
|
+
const cleanup = hook(target);
|
|
1693
|
+
if (typeof cleanup === "function") {
|
|
1694
|
+
cleanups.push(cleanup);
|
|
1695
|
+
}
|
|
1696
|
+
}
|
|
1607
1697
|
}
|
|
1608
1698
|
|
|
1609
1699
|
function createComponentContext({ runtime, scope, cleanups, attachHooks, visibleHooks, destroyHooks, renderScopedTemplate }) {
|
|
1610
|
-
const { signals, handlers, loader, server, router, cache } = runtime;
|
|
1700
|
+
const { signals, handlers, loader, server, router, cache, scheduler } = runtime;
|
|
1611
1701
|
const generatedHandlers = new WeakMap();
|
|
1612
1702
|
let generatedHandlerCounter = 0;
|
|
1613
1703
|
let generatedSignalCounter = 0;
|
|
@@ -1619,6 +1709,7 @@
|
|
|
1619
1709
|
server,
|
|
1620
1710
|
router,
|
|
1621
1711
|
cache,
|
|
1712
|
+
scheduler,
|
|
1622
1713
|
|
|
1623
1714
|
signal(name, initial) {
|
|
1624
1715
|
if (arguments.length === 1) {
|
|
@@ -1663,7 +1754,11 @@
|
|
|
1663
1754
|
},
|
|
1664
1755
|
|
|
1665
1756
|
effect(fn) {
|
|
1666
|
-
const cleanup = signals.effect(() => fn.call(context)
|
|
1757
|
+
const cleanup = signals.effect(() => fn.call(context), {
|
|
1758
|
+
scheduler,
|
|
1759
|
+
phase: "effect",
|
|
1760
|
+
scope
|
|
1761
|
+
});
|
|
1667
1762
|
cleanups.push(cleanup);
|
|
1668
1763
|
return cleanup;
|
|
1669
1764
|
},
|
|
@@ -1785,93 +1880,10 @@
|
|
|
1785
1880
|
})();
|
|
1786
1881
|
|
|
1787
1882
|
const __serverModule = (() => {
|
|
1788
|
-
const { attachRegistryInspection, createRegistryStore } = __registryStoreModule;
|
|
1789
1883
|
const serverEnvelopeKeys = new Set(["value", "signals", "boundary", "html", "redirect", "error"]);
|
|
1790
1884
|
const appliedServerResult = Symbol.for("@async/framework.appliedServerResult");
|
|
1791
1885
|
const appliedServerValues = new WeakSet();
|
|
1792
1886
|
|
|
1793
|
-
function createServerRegistry(initialMap = {}, options = {}) {
|
|
1794
|
-
const registryStore = options.registry ?? createRegistryStore();
|
|
1795
|
-
const type = options.type ?? "server";
|
|
1796
|
-
const entries = registryStore._map(type);
|
|
1797
|
-
const defaults = {};
|
|
1798
|
-
|
|
1799
|
-
const registry = attachRegistryInspection({
|
|
1800
|
-
register(id, fn) {
|
|
1801
|
-
assertServerId(id);
|
|
1802
|
-
if (typeof fn !== "function") {
|
|
1803
|
-
throw new TypeError(`Server function "${id}" must be a function.`);
|
|
1804
|
-
}
|
|
1805
|
-
if (entries.has(id)) {
|
|
1806
|
-
throw new Error(`Server function "${id}" is already registered.`);
|
|
1807
|
-
}
|
|
1808
|
-
entries.set(id, fn);
|
|
1809
|
-
return id;
|
|
1810
|
-
},
|
|
1811
|
-
|
|
1812
|
-
registerMany(map) {
|
|
1813
|
-
for (const [id, fn] of Object.entries(map ?? {})) {
|
|
1814
|
-
registry.register(id, fn);
|
|
1815
|
-
}
|
|
1816
|
-
return registry;
|
|
1817
|
-
},
|
|
1818
|
-
|
|
1819
|
-
unregister(id) {
|
|
1820
|
-
assertServerId(id);
|
|
1821
|
-
return entries.delete(id);
|
|
1822
|
-
},
|
|
1823
|
-
|
|
1824
|
-
resolve(id) {
|
|
1825
|
-
assertServerId(id);
|
|
1826
|
-
return entries.get(id);
|
|
1827
|
-
},
|
|
1828
|
-
|
|
1829
|
-
async run(id, args = [], context = {}) {
|
|
1830
|
-
assertServerId(id);
|
|
1831
|
-
const fn = registry.resolve(id);
|
|
1832
|
-
if (!fn) {
|
|
1833
|
-
throw new Error(`Server function "${id}" is not registered.`);
|
|
1834
|
-
}
|
|
1835
|
-
|
|
1836
|
-
let runContext;
|
|
1837
|
-
const server = createServerNamespace((childId, childArgs, childContext = {}) => {
|
|
1838
|
-
return registry.run(childId, childArgs, { ...runContext, ...childContext });
|
|
1839
|
-
}, {}, () => runContext);
|
|
1840
|
-
|
|
1841
|
-
const mergedContext = {
|
|
1842
|
-
...defaults,
|
|
1843
|
-
...context,
|
|
1844
|
-
cache: defaults.cache ?? context.cache
|
|
1845
|
-
};
|
|
1846
|
-
|
|
1847
|
-
runContext = {
|
|
1848
|
-
...mergedContext,
|
|
1849
|
-
id,
|
|
1850
|
-
args,
|
|
1851
|
-
input: mergedContext.input,
|
|
1852
|
-
signals: createSignalReader(mergedContext.signals),
|
|
1853
|
-
abort: mergedContext.abort,
|
|
1854
|
-
cache: mergedContext.cache,
|
|
1855
|
-
server
|
|
1856
|
-
};
|
|
1857
|
-
|
|
1858
|
-
return fn.call(runContext, ...args);
|
|
1859
|
-
},
|
|
1860
|
-
|
|
1861
|
-
_setContext(context = {}) {
|
|
1862
|
-
Object.assign(defaults, context);
|
|
1863
|
-
return registry;
|
|
1864
|
-
},
|
|
1865
|
-
|
|
1866
|
-
_adoptMany() {
|
|
1867
|
-
return registry;
|
|
1868
|
-
}
|
|
1869
|
-
}, registryStore, type);
|
|
1870
|
-
|
|
1871
|
-
registry.registerMany(initialMap);
|
|
1872
|
-
return createServerNamespace((id, args, context) => registry.run(id, args, context), registry, () => defaults);
|
|
1873
|
-
}
|
|
1874
|
-
|
|
1875
1887
|
function createServerProxy({
|
|
1876
1888
|
endpoint = "/__async/server",
|
|
1877
1889
|
fetch: fetchImpl = globalThis.fetch?.bind(globalThis),
|
|
@@ -1879,13 +1891,14 @@
|
|
|
1879
1891
|
loader,
|
|
1880
1892
|
router,
|
|
1881
1893
|
cache,
|
|
1894
|
+
scheduler,
|
|
1882
1895
|
headers = {}
|
|
1883
1896
|
} = {}) {
|
|
1884
1897
|
if (typeof fetchImpl !== "function") {
|
|
1885
1898
|
throw new TypeError("createServerProxy(...) requires fetch to be available.");
|
|
1886
1899
|
}
|
|
1887
1900
|
|
|
1888
|
-
const defaults = { signals, loader, router, cache };
|
|
1901
|
+
const defaults = { signals, loader, router, cache, scheduler };
|
|
1889
1902
|
|
|
1890
1903
|
async function run(id, args = [], context = {}) {
|
|
1891
1904
|
assertServerId(id);
|
|
@@ -2224,7 +2237,7 @@
|
|
|
2224
2237
|
throw new TypeError("Server function id must be a non-empty string.");
|
|
2225
2238
|
}
|
|
2226
2239
|
}
|
|
2227
|
-
return {
|
|
2240
|
+
return { createServerProxy, resolveServerCommandArguments, applyServerResult, unwrapServerResult, defaultInput, createServerNamespace, createSignalReader, assertServerId };
|
|
2228
2241
|
})();
|
|
2229
2242
|
|
|
2230
2243
|
const __handlersModule = (() => {
|
|
@@ -2427,18 +2440,325 @@
|
|
|
2427
2440
|
return { createHandlerRegistry, parseHandlerRef, isHandlerToken };
|
|
2428
2441
|
})();
|
|
2429
2442
|
|
|
2443
|
+
const __schedulerModule = (() => {
|
|
2444
|
+
const defaultPhases = ["binding", "lifecycle", "effect", "async", "post"];
|
|
2445
|
+
|
|
2446
|
+
function createScheduler(options = {}) {
|
|
2447
|
+
const phases = [...(options.phases ?? defaultPhases)];
|
|
2448
|
+
const queues = new Map(phases.map((phase) => [phase, []]));
|
|
2449
|
+
const keyedJobs = new Map();
|
|
2450
|
+
const destroyedScopes = new Set();
|
|
2451
|
+
const objectScopeIds = new WeakMap();
|
|
2452
|
+
const onError = typeof options.onError === "function" ? options.onError : undefined;
|
|
2453
|
+
const maxDepth = options.maxDepth ?? 100;
|
|
2454
|
+
const strategy = options.strategy ?? "microtask";
|
|
2455
|
+
let destroyed = false;
|
|
2456
|
+
let flushing = false;
|
|
2457
|
+
let scheduled = false;
|
|
2458
|
+
let batchDepth = 0;
|
|
2459
|
+
let jobCounter = 0;
|
|
2460
|
+
let scopeCounter = 0;
|
|
2461
|
+
|
|
2462
|
+
const api = {
|
|
2463
|
+
strategy,
|
|
2464
|
+
phases,
|
|
2465
|
+
|
|
2466
|
+
batch(fn) {
|
|
2467
|
+
if (typeof fn !== "function") {
|
|
2468
|
+
throw new TypeError("scheduler.batch(fn) requires a function.");
|
|
2469
|
+
}
|
|
2470
|
+
assertActive();
|
|
2471
|
+
batchDepth += 1;
|
|
2472
|
+
let asyncBatch = false;
|
|
2473
|
+
try {
|
|
2474
|
+
const value = fn();
|
|
2475
|
+
if (value && typeof value.then === "function") {
|
|
2476
|
+
asyncBatch = true;
|
|
2477
|
+
return value.finally(() => {
|
|
2478
|
+
batchDepth -= 1;
|
|
2479
|
+
requestFlush();
|
|
2480
|
+
});
|
|
2481
|
+
}
|
|
2482
|
+
return value;
|
|
2483
|
+
} finally {
|
|
2484
|
+
if (!asyncBatch && batchDepth > 0) {
|
|
2485
|
+
batchDepth -= 1;
|
|
2486
|
+
requestFlush();
|
|
2487
|
+
}
|
|
2488
|
+
}
|
|
2489
|
+
},
|
|
2490
|
+
|
|
2491
|
+
enqueue(phase, fn, options = {}) {
|
|
2492
|
+
assertActive();
|
|
2493
|
+
assertPhase(phase);
|
|
2494
|
+
if (typeof fn !== "function") {
|
|
2495
|
+
throw new TypeError("scheduler.enqueue(phase, fn) requires a function.");
|
|
2496
|
+
}
|
|
2497
|
+
const scope = options.scope;
|
|
2498
|
+
if (scope !== undefined && destroyedScopes.has(scope)) {
|
|
2499
|
+
return noop;
|
|
2500
|
+
}
|
|
2501
|
+
|
|
2502
|
+
const dedupeKey = options.key === undefined ? undefined : `${phase}:${scopeKey(scope)}:${String(options.key)}`;
|
|
2503
|
+
if (dedupeKey && keyedJobs.has(dedupeKey)) {
|
|
2504
|
+
return keyedJobs.get(dedupeKey).cancel;
|
|
2505
|
+
}
|
|
2506
|
+
|
|
2507
|
+
const job = {
|
|
2508
|
+
id: ++jobCounter,
|
|
2509
|
+
phase,
|
|
2510
|
+
fn,
|
|
2511
|
+
scope,
|
|
2512
|
+
boundary: options.boundary,
|
|
2513
|
+
key: dedupeKey,
|
|
2514
|
+
canceled: false,
|
|
2515
|
+
cancel() {
|
|
2516
|
+
job.canceled = true;
|
|
2517
|
+
if (job.key) {
|
|
2518
|
+
keyedJobs.delete(job.key);
|
|
2519
|
+
}
|
|
2520
|
+
}
|
|
2521
|
+
};
|
|
2522
|
+
queues.get(phase).push(job);
|
|
2523
|
+
if (job.key) {
|
|
2524
|
+
keyedJobs.set(job.key, job);
|
|
2525
|
+
}
|
|
2526
|
+
requestFlush();
|
|
2527
|
+
return job.cancel;
|
|
2528
|
+
},
|
|
2529
|
+
|
|
2530
|
+
afterFlush(fn, options = {}) {
|
|
2531
|
+
return api.enqueue("post", fn, options);
|
|
2532
|
+
},
|
|
2533
|
+
|
|
2534
|
+
async flush() {
|
|
2535
|
+
assertActive();
|
|
2536
|
+
if (flushing) {
|
|
2537
|
+
return;
|
|
2538
|
+
}
|
|
2539
|
+
scheduled = false;
|
|
2540
|
+
flushing = true;
|
|
2541
|
+
let depth = 0;
|
|
2542
|
+
try {
|
|
2543
|
+
while (hasJobs()) {
|
|
2544
|
+
depth += 1;
|
|
2545
|
+
if (depth > maxDepth) {
|
|
2546
|
+
throw new Error(`Scheduler exceeded maxDepth ${maxDepth}.`);
|
|
2547
|
+
}
|
|
2548
|
+
for (const phase of phases) {
|
|
2549
|
+
await flushPhase(phase);
|
|
2550
|
+
}
|
|
2551
|
+
}
|
|
2552
|
+
} finally {
|
|
2553
|
+
flushing = false;
|
|
2554
|
+
if (hasJobs()) {
|
|
2555
|
+
requestFlush();
|
|
2556
|
+
}
|
|
2557
|
+
}
|
|
2558
|
+
},
|
|
2559
|
+
|
|
2560
|
+
async flushScope(scope) {
|
|
2561
|
+
assertActive();
|
|
2562
|
+
if (flushing) {
|
|
2563
|
+
return;
|
|
2564
|
+
}
|
|
2565
|
+
scheduled = false;
|
|
2566
|
+
flushing = true;
|
|
2567
|
+
let depth = 0;
|
|
2568
|
+
try {
|
|
2569
|
+
while (hasJobsForScope(scope)) {
|
|
2570
|
+
depth += 1;
|
|
2571
|
+
if (depth > maxDepth) {
|
|
2572
|
+
throw new Error(`Scheduler exceeded maxDepth ${maxDepth}.`);
|
|
2573
|
+
}
|
|
2574
|
+
for (const phase of phases) {
|
|
2575
|
+
await flushPhase(phase, scope);
|
|
2576
|
+
}
|
|
2577
|
+
}
|
|
2578
|
+
} finally {
|
|
2579
|
+
flushing = false;
|
|
2580
|
+
if (hasJobs()) {
|
|
2581
|
+
requestFlush();
|
|
2582
|
+
}
|
|
2583
|
+
}
|
|
2584
|
+
},
|
|
2585
|
+
|
|
2586
|
+
cancelScope(scope) {
|
|
2587
|
+
if (scope === undefined) {
|
|
2588
|
+
return api;
|
|
2589
|
+
}
|
|
2590
|
+
for (const queue of queues.values()) {
|
|
2591
|
+
for (const job of queue) {
|
|
2592
|
+
if (job.scope === scope) {
|
|
2593
|
+
job.cancel();
|
|
2594
|
+
}
|
|
2595
|
+
}
|
|
2596
|
+
}
|
|
2597
|
+
return api;
|
|
2598
|
+
},
|
|
2599
|
+
|
|
2600
|
+
markScopeDestroyed(scope) {
|
|
2601
|
+
if (scope !== undefined) {
|
|
2602
|
+
destroyedScopes.add(scope);
|
|
2603
|
+
api.cancelScope(scope);
|
|
2604
|
+
}
|
|
2605
|
+
return api;
|
|
2606
|
+
},
|
|
2607
|
+
|
|
2608
|
+
isScopeDestroyed(scope) {
|
|
2609
|
+
return scope !== undefined && destroyedScopes.has(scope);
|
|
2610
|
+
},
|
|
2611
|
+
|
|
2612
|
+
inspect() {
|
|
2613
|
+
const counts = {};
|
|
2614
|
+
for (const [phase, queue] of queues) {
|
|
2615
|
+
counts[phase] = queue.filter((job) => !job.canceled).length;
|
|
2616
|
+
}
|
|
2617
|
+
return {
|
|
2618
|
+
strategy,
|
|
2619
|
+
phases: [...phases],
|
|
2620
|
+
pending: counts,
|
|
2621
|
+
scopesDestroyed: destroyedScopes.size,
|
|
2622
|
+
flushing,
|
|
2623
|
+
scheduled
|
|
2624
|
+
};
|
|
2625
|
+
},
|
|
2626
|
+
|
|
2627
|
+
destroy() {
|
|
2628
|
+
destroyed = true;
|
|
2629
|
+
for (const queue of queues.values()) {
|
|
2630
|
+
for (const job of queue) {
|
|
2631
|
+
job.cancel();
|
|
2632
|
+
}
|
|
2633
|
+
queue.length = 0;
|
|
2634
|
+
}
|
|
2635
|
+
keyedJobs.clear();
|
|
2636
|
+
destroyedScopes.clear();
|
|
2637
|
+
}
|
|
2638
|
+
};
|
|
2639
|
+
|
|
2640
|
+
return api;
|
|
2641
|
+
|
|
2642
|
+
function requestFlush() {
|
|
2643
|
+
if (strategy === "manual" || destroyed || flushing || batchDepth > 0 || scheduled) {
|
|
2644
|
+
return;
|
|
2645
|
+
}
|
|
2646
|
+
scheduled = true;
|
|
2647
|
+
scheduleMicrotask(() => {
|
|
2648
|
+
if (!destroyed) {
|
|
2649
|
+
void api.flush();
|
|
2650
|
+
}
|
|
2651
|
+
});
|
|
2652
|
+
}
|
|
2653
|
+
|
|
2654
|
+
async function flushPhase(phase, scope) {
|
|
2655
|
+
const queue = queues.get(phase);
|
|
2656
|
+
const remaining = [];
|
|
2657
|
+
const runnable = [];
|
|
2658
|
+
|
|
2659
|
+
for (const job of queue.splice(0)) {
|
|
2660
|
+
if (job.canceled) {
|
|
2661
|
+
continue;
|
|
2662
|
+
}
|
|
2663
|
+
if (scope !== undefined && job.scope !== scope) {
|
|
2664
|
+
remaining.push(job);
|
|
2665
|
+
continue;
|
|
2666
|
+
}
|
|
2667
|
+
runnable.push(job);
|
|
2668
|
+
}
|
|
2669
|
+
|
|
2670
|
+
queue.push(...remaining);
|
|
2671
|
+
|
|
2672
|
+
for (const job of runnable) {
|
|
2673
|
+
if (job.key) {
|
|
2674
|
+
keyedJobs.delete(job.key);
|
|
2675
|
+
}
|
|
2676
|
+
if (job.canceled || (job.scope !== undefined && destroyedScopes.has(job.scope))) {
|
|
2677
|
+
continue;
|
|
2678
|
+
}
|
|
2679
|
+
try {
|
|
2680
|
+
await job.fn();
|
|
2681
|
+
} catch (error) {
|
|
2682
|
+
if (onError) {
|
|
2683
|
+
onError(error, job);
|
|
2684
|
+
} else {
|
|
2685
|
+
throw error;
|
|
2686
|
+
}
|
|
2687
|
+
}
|
|
2688
|
+
}
|
|
2689
|
+
}
|
|
2690
|
+
|
|
2691
|
+
function hasJobs() {
|
|
2692
|
+
for (const queue of queues.values()) {
|
|
2693
|
+
if (queue.some((job) => !job.canceled)) {
|
|
2694
|
+
return true;
|
|
2695
|
+
}
|
|
2696
|
+
}
|
|
2697
|
+
return false;
|
|
2698
|
+
}
|
|
2699
|
+
|
|
2700
|
+
function hasJobsForScope(scope) {
|
|
2701
|
+
for (const queue of queues.values()) {
|
|
2702
|
+
if (queue.some((job) => !job.canceled && job.scope === scope)) {
|
|
2703
|
+
return true;
|
|
2704
|
+
}
|
|
2705
|
+
}
|
|
2706
|
+
return false;
|
|
2707
|
+
}
|
|
2708
|
+
|
|
2709
|
+
function assertActive() {
|
|
2710
|
+
if (destroyed) {
|
|
2711
|
+
throw new Error("Scheduler has been destroyed.");
|
|
2712
|
+
}
|
|
2713
|
+
}
|
|
2714
|
+
|
|
2715
|
+
function assertPhase(phase) {
|
|
2716
|
+
if (!queues.has(phase)) {
|
|
2717
|
+
throw new Error(`Unknown scheduler phase "${phase}".`);
|
|
2718
|
+
}
|
|
2719
|
+
}
|
|
2720
|
+
|
|
2721
|
+
function scopeKey(scope) {
|
|
2722
|
+
if (scope === undefined) {
|
|
2723
|
+
return "global";
|
|
2724
|
+
}
|
|
2725
|
+
if ((typeof scope === "object" && scope !== null) || typeof scope === "function") {
|
|
2726
|
+
if (!objectScopeIds.has(scope)) {
|
|
2727
|
+
objectScopeIds.set(scope, `scope:${++scopeCounter}`);
|
|
2728
|
+
}
|
|
2729
|
+
return objectScopeIds.get(scope);
|
|
2730
|
+
}
|
|
2731
|
+
return String(scope);
|
|
2732
|
+
}
|
|
2733
|
+
}
|
|
2734
|
+
|
|
2735
|
+
function scheduleMicrotask(fn) {
|
|
2736
|
+
if (typeof queueMicrotask === "function") {
|
|
2737
|
+
queueMicrotask(fn);
|
|
2738
|
+
return;
|
|
2739
|
+
}
|
|
2740
|
+
Promise.resolve().then(fn);
|
|
2741
|
+
}
|
|
2742
|
+
|
|
2743
|
+
function noop() {}
|
|
2744
|
+
return { createScheduler };
|
|
2745
|
+
})();
|
|
2746
|
+
|
|
2430
2747
|
const __loaderModule = (() => {
|
|
2431
2748
|
const { renderComponent } = __componentModule;
|
|
2432
2749
|
const { createHandlerRegistry } = __handlersModule;
|
|
2750
|
+
const { createScheduler } = __schedulerModule;
|
|
2433
2751
|
const { createSignalRegistry, isSignalRef } = __signalsModule;
|
|
2434
2752
|
const { matchAttribute, normalizeAttributeConfig, readAttribute } = __attributesModule;
|
|
2435
2753
|
const inlineBindingPrefix = "__async:inline:";
|
|
2436
2754
|
|
|
2437
|
-
function Loader({ root, signals, handlers, server, router, cache, attributes } = {}) {
|
|
2755
|
+
function Loader({ root, signals, handlers, server, router, cache, attributes, scheduler } = {}) {
|
|
2438
2756
|
const documentRef = root?.ownerDocument ?? root ?? globalThis.document;
|
|
2439
2757
|
const rootNode = root ?? documentRef;
|
|
2440
2758
|
const signalRegistry = signals ?? createSignalRegistry();
|
|
2441
2759
|
const handlerRegistry = handlers ?? createHandlerRegistry();
|
|
2760
|
+
const schedulerInstance = scheduler ?? createScheduler();
|
|
2761
|
+
const ownsScheduler = !scheduler;
|
|
2442
2762
|
const attributeConfig = normalizeAttributeConfig(attributes);
|
|
2443
2763
|
const cleanups = new Set();
|
|
2444
2764
|
const eventBindings = new WeakMap();
|
|
@@ -2459,6 +2779,7 @@
|
|
|
2459
2779
|
server,
|
|
2460
2780
|
router,
|
|
2461
2781
|
cache,
|
|
2782
|
+
scheduler: schedulerInstance,
|
|
2462
2783
|
attributes: attributeConfig,
|
|
2463
2784
|
|
|
2464
2785
|
start() {
|
|
@@ -2498,6 +2819,7 @@
|
|
|
2498
2819
|
server: api.server,
|
|
2499
2820
|
router: api.router,
|
|
2500
2821
|
cache: api.cache,
|
|
2822
|
+
scheduler: schedulerInstance,
|
|
2501
2823
|
attributes: attributeConfig
|
|
2502
2824
|
});
|
|
2503
2825
|
cleanupChildren(target);
|
|
@@ -2518,6 +2840,9 @@
|
|
|
2518
2840
|
runCleanup(cleanup);
|
|
2519
2841
|
}
|
|
2520
2842
|
cleanups.clear();
|
|
2843
|
+
if (ownsScheduler) {
|
|
2844
|
+
schedulerInstance.destroy();
|
|
2845
|
+
}
|
|
2521
2846
|
},
|
|
2522
2847
|
|
|
2523
2848
|
_observeVisible(target, fn) {
|
|
@@ -2535,13 +2860,14 @@
|
|
|
2535
2860
|
}
|
|
2536
2861
|
};
|
|
2537
2862
|
|
|
2538
|
-
signalRegistry._setContext?.({ server: api.server, router: api.router, loader: api, cache: api.cache });
|
|
2863
|
+
signalRegistry._setContext?.({ server: api.server, router: api.router, loader: api, cache: api.cache, scheduler: schedulerInstance });
|
|
2539
2864
|
api.server?._setContext?.({
|
|
2540
2865
|
signals: signalRegistry,
|
|
2541
2866
|
handlers: handlerRegistry,
|
|
2542
2867
|
loader: api,
|
|
2543
2868
|
router: api.router,
|
|
2544
|
-
cache: api.cache
|
|
2869
|
+
cache: api.cache,
|
|
2870
|
+
scheduler: schedulerInstance
|
|
2545
2871
|
});
|
|
2546
2872
|
|
|
2547
2873
|
function bindEventAttributes(scope) {
|
|
@@ -2573,18 +2899,19 @@
|
|
|
2573
2899
|
|
|
2574
2900
|
const listener = async (event) => {
|
|
2575
2901
|
try {
|
|
2576
|
-
await handlerRegistry.run(ref, {
|
|
2902
|
+
await schedulerInstance.batch(() => handlerRegistry.run(ref, {
|
|
2577
2903
|
signals: signalRegistry,
|
|
2578
2904
|
handlers: handlerRegistry,
|
|
2579
2905
|
loader: api,
|
|
2580
2906
|
server: api.server,
|
|
2581
2907
|
router: api.router,
|
|
2582
2908
|
cache: api.cache,
|
|
2909
|
+
scheduler: schedulerInstance,
|
|
2583
2910
|
event,
|
|
2584
2911
|
element,
|
|
2585
2912
|
el: element,
|
|
2586
2913
|
root: rootNode
|
|
2587
|
-
});
|
|
2914
|
+
}));
|
|
2588
2915
|
} catch (error) {
|
|
2589
2916
|
dispatchAsyncError(element, error);
|
|
2590
2917
|
}
|
|
@@ -2700,7 +3027,12 @@
|
|
|
2700
3027
|
|
|
2701
3028
|
const read = () => readBinding(path, options);
|
|
2702
3029
|
apply(read());
|
|
2703
|
-
addCleanup(subscribeBinding(path, () =>
|
|
3030
|
+
addCleanup(subscribeBinding(path, () => {
|
|
3031
|
+
schedulerInstance.enqueue("binding", () => apply(read()), {
|
|
3032
|
+
scope: element,
|
|
3033
|
+
key
|
|
3034
|
+
});
|
|
3035
|
+
}), element);
|
|
2704
3036
|
}
|
|
2705
3037
|
|
|
2706
3038
|
function bindValueWriter(element, path) {
|
|
@@ -2761,7 +3093,12 @@
|
|
|
2761
3093
|
const state = {
|
|
2762
3094
|
id,
|
|
2763
3095
|
templates,
|
|
2764
|
-
cleanup: signalRegistry.subscribe(`${id}.$status`, () =>
|
|
3096
|
+
cleanup: signalRegistry.subscribe(`${id}.$status`, () => {
|
|
3097
|
+
schedulerInstance.enqueue("binding", () => renderBoundary(boundary), {
|
|
3098
|
+
scope: boundary,
|
|
3099
|
+
key: `boundary:${id}`
|
|
3100
|
+
});
|
|
3101
|
+
})
|
|
2765
3102
|
};
|
|
2766
3103
|
boundaryState.set(boundary, state);
|
|
2767
3104
|
addCleanup(state.cleanup, boundary);
|
|
@@ -2801,7 +3138,7 @@
|
|
|
2801
3138
|
}
|
|
2802
3139
|
mountedElements.add(element);
|
|
2803
3140
|
for (const ref of refs) {
|
|
2804
|
-
runPseudo(element, ref);
|
|
3141
|
+
scheduleLifecycle(element, () => runPseudo(element, ref), `attach:${ref}`);
|
|
2805
3142
|
}
|
|
2806
3143
|
}
|
|
2807
3144
|
|
|
@@ -2814,7 +3151,7 @@
|
|
|
2814
3151
|
continue;
|
|
2815
3152
|
}
|
|
2816
3153
|
visibleElements.add(element);
|
|
2817
|
-
addCleanup(observeVisible(element, () => runPseudo(element, ref)), element);
|
|
3154
|
+
addCleanup(observeVisible(element, () => scheduleLifecycle(element, () => runPseudo(element, ref), `visible:${ref}`)), element);
|
|
2818
3155
|
}
|
|
2819
3156
|
}
|
|
2820
3157
|
|
|
@@ -2838,6 +3175,7 @@
|
|
|
2838
3175
|
server: api.server,
|
|
2839
3176
|
router: api.router,
|
|
2840
3177
|
cache: api.cache,
|
|
3178
|
+
scheduler: schedulerInstance,
|
|
2841
3179
|
element,
|
|
2842
3180
|
el: element,
|
|
2843
3181
|
root: rootNode
|
|
@@ -2856,10 +3194,13 @@
|
|
|
2856
3194
|
const ownerWindow = target.ownerDocument?.defaultView ?? globalThis;
|
|
2857
3195
|
const Observer = ownerWindow.IntersectionObserver ?? globalThis.IntersectionObserver;
|
|
2858
3196
|
if (!Observer) {
|
|
2859
|
-
|
|
3197
|
+
schedulerInstance.enqueue("lifecycle", () => {
|
|
2860
3198
|
if (!destroyed) {
|
|
2861
3199
|
fn(target);
|
|
2862
3200
|
}
|
|
3201
|
+
}, {
|
|
3202
|
+
scope: target,
|
|
3203
|
+
key: "visible:fallback"
|
|
2863
3204
|
});
|
|
2864
3205
|
return () => {};
|
|
2865
3206
|
}
|
|
@@ -2914,6 +3255,7 @@
|
|
|
2914
3255
|
}
|
|
2915
3256
|
for (const element of elementsIn(node)) {
|
|
2916
3257
|
runScopedCleanups(element);
|
|
3258
|
+
schedulerInstance.markScopeDestroyed(element);
|
|
2917
3259
|
}
|
|
2918
3260
|
}
|
|
2919
3261
|
|
|
@@ -2937,6 +3279,13 @@
|
|
|
2937
3279
|
}
|
|
2938
3280
|
}
|
|
2939
3281
|
|
|
3282
|
+
function scheduleLifecycle(element, fn, key) {
|
|
3283
|
+
schedulerInstance.enqueue("lifecycle", fn, {
|
|
3284
|
+
scope: element,
|
|
3285
|
+
key
|
|
3286
|
+
});
|
|
3287
|
+
}
|
|
3288
|
+
|
|
2940
3289
|
return api;
|
|
2941
3290
|
}
|
|
2942
3291
|
|
|
@@ -3267,6 +3616,7 @@
|
|
|
3267
3616
|
const __routerModule = (() => {
|
|
3268
3617
|
const { Loader } = __loaderModule;
|
|
3269
3618
|
const { createHandlerRegistry } = __handlersModule;
|
|
3619
|
+
const { createScheduler } = __schedulerModule;
|
|
3270
3620
|
const { createSignalRegistry } = __signalsModule;
|
|
3271
3621
|
const { applyServerResult } = __serverModule;
|
|
3272
3622
|
const { createRegistryStore } = __registryStoreModule;
|
|
@@ -3387,12 +3737,15 @@
|
|
|
3387
3737
|
partials,
|
|
3388
3738
|
fetch: fetchImpl = globalThis.fetch?.bind(globalThis),
|
|
3389
3739
|
routeEndpoint = "/__async/route",
|
|
3390
|
-
attributes
|
|
3740
|
+
attributes,
|
|
3741
|
+
scheduler
|
|
3391
3742
|
} = {}) {
|
|
3392
3743
|
const documentRef = root?.ownerDocument ?? root ?? globalThis.document;
|
|
3393
3744
|
const rootNode = root ?? documentRef;
|
|
3394
3745
|
const signalRegistry = signals ?? loader?.signals ?? createSignalRegistry();
|
|
3395
3746
|
const handlerRegistry = handlers ?? loader?.handlers ?? createHandlerRegistry();
|
|
3747
|
+
const schedulerInstance = scheduler ?? loader?.scheduler ?? createScheduler();
|
|
3748
|
+
const ownsScheduler = !scheduler && !loader?.scheduler;
|
|
3396
3749
|
const attributeConfig = normalizeAttributeConfig(attributes ?? loader?.attributes);
|
|
3397
3750
|
const loaderInstance =
|
|
3398
3751
|
loader ??
|
|
@@ -3402,6 +3755,7 @@
|
|
|
3402
3755
|
handlers: handlerRegistry,
|
|
3403
3756
|
server,
|
|
3404
3757
|
cache,
|
|
3758
|
+
scheduler: schedulerInstance,
|
|
3405
3759
|
attributes: attributeConfig
|
|
3406
3760
|
});
|
|
3407
3761
|
const ownsLoader = !loader;
|
|
@@ -3421,12 +3775,13 @@
|
|
|
3421
3775
|
server,
|
|
3422
3776
|
cache,
|
|
3423
3777
|
partials,
|
|
3778
|
+
scheduler: schedulerInstance,
|
|
3424
3779
|
attributes: attributeConfig,
|
|
3425
3780
|
|
|
3426
3781
|
start() {
|
|
3427
3782
|
assertActive();
|
|
3428
3783
|
loaderInstance.router = api;
|
|
3429
|
-
signalRegistry._setContext?.({ router: api, loader: loaderInstance, server, cache });
|
|
3784
|
+
signalRegistry._setContext?.({ router: api, loader: loaderInstance, server, cache, scheduler: schedulerInstance });
|
|
3430
3785
|
if (ownsLoader) {
|
|
3431
3786
|
loaderInstance.start();
|
|
3432
3787
|
}
|
|
@@ -3490,6 +3845,9 @@
|
|
|
3490
3845
|
cleanup();
|
|
3491
3846
|
}
|
|
3492
3847
|
cleanups.clear();
|
|
3848
|
+
if (ownsScheduler) {
|
|
3849
|
+
schedulerInstance.destroy();
|
|
3850
|
+
}
|
|
3493
3851
|
}
|
|
3494
3852
|
};
|
|
3495
3853
|
|
|
@@ -3595,13 +3953,16 @@
|
|
|
3595
3953
|
loader: loaderInstance,
|
|
3596
3954
|
router: api,
|
|
3597
3955
|
cache,
|
|
3956
|
+
scheduler: schedulerInstance,
|
|
3598
3957
|
abort: navigation?.abort
|
|
3599
3958
|
});
|
|
3959
|
+
await schedulerInstance.flush();
|
|
3600
3960
|
if (!isActiveNavigation(navigation)) {
|
|
3601
3961
|
return;
|
|
3602
3962
|
}
|
|
3603
3963
|
if (result?.html != null && !result.boundary && !result.redirect) {
|
|
3604
3964
|
loaderInstance.swap(boundary, result.html);
|
|
3965
|
+
await schedulerInstance.flush();
|
|
3605
3966
|
}
|
|
3606
3967
|
if (result?.redirect || options.history === false) {
|
|
3607
3968
|
return;
|
|
@@ -3646,6 +4007,7 @@
|
|
|
3646
4007
|
loader: loaderInstance,
|
|
3647
4008
|
server,
|
|
3648
4009
|
cache,
|
|
4010
|
+
scheduler: schedulerInstance,
|
|
3649
4011
|
abort: navigation?.abort
|
|
3650
4012
|
};
|
|
3651
4013
|
}
|
|
@@ -3832,15 +4194,17 @@
|
|
|
3832
4194
|
const { Loader } = __loaderModule;
|
|
3833
4195
|
const { createPartialRegistry } = __partialsModule;
|
|
3834
4196
|
const { createRouteRegistry, createRouter } = __routerModule;
|
|
3835
|
-
const {
|
|
4197
|
+
const { createScheduler } = __schedulerModule;
|
|
4198
|
+
const { createServerNamespace } = __serverModule;
|
|
3836
4199
|
const { createSignal, createSignalRegistry } = __signalsModule;
|
|
3837
4200
|
const { createRegistryStore } = __registryStoreModule;
|
|
3838
4201
|
const { attributeName, normalizeAttributeConfig } = __attributesModule;
|
|
3839
4202
|
const registryTypes = new Set(["signal", "handler", "server", "partial", "route", "component"]);
|
|
3840
4203
|
|
|
3841
|
-
function defineApp(initial) {
|
|
4204
|
+
function defineApp(initial, options = {}) {
|
|
3842
4205
|
const registry = createRegistryStore(undefined, { target: "browser" });
|
|
3843
4206
|
const runtimes = new Set();
|
|
4207
|
+
const createRuntime = options.createRuntime ?? createApp;
|
|
3844
4208
|
|
|
3845
4209
|
const app = {
|
|
3846
4210
|
registry,
|
|
@@ -3859,7 +4223,7 @@
|
|
|
3859
4223
|
},
|
|
3860
4224
|
|
|
3861
4225
|
start(options = {}) {
|
|
3862
|
-
const runtime =
|
|
4226
|
+
const runtime = createRuntime(app, options).start();
|
|
3863
4227
|
app.runtime = runtime;
|
|
3864
4228
|
return runtime;
|
|
3865
4229
|
},
|
|
@@ -3884,13 +4248,18 @@
|
|
|
3884
4248
|
function createApp(appOrDefinition = Async, options = {}) {
|
|
3885
4249
|
const app = isAppHub(appOrDefinition) ? appOrDefinition : defineApp(appOrDefinition ?? {});
|
|
3886
4250
|
const target = options.target ?? "browser";
|
|
4251
|
+
const scheduler = options.scheduler ?? options.loader?.scheduler ?? createScheduler({
|
|
4252
|
+
strategy: target === "server" ? "manual" : "microtask"
|
|
4253
|
+
});
|
|
4254
|
+
const ownsScheduler = !options.scheduler && !options.loader?.scheduler;
|
|
3887
4255
|
const attributes = normalizeAttributeConfig(options.attributes);
|
|
3888
4256
|
const registry = options.registry ?? app.registry.view({ target });
|
|
3889
4257
|
const signals = options.signals ?? createSignalRegistry(undefined, { registry, type: "signal" });
|
|
3890
4258
|
const handlers = options.handlers ?? createHandlerRegistry(undefined, { registry, type: "handler" });
|
|
3891
4259
|
const serverCache = createCacheRegistry(undefined, { registry, type: "cache.server" });
|
|
3892
4260
|
const browserCache = createCacheRegistry(undefined, { registry, type: "cache.browser" });
|
|
3893
|
-
const
|
|
4261
|
+
const serverFactory = options.serverFactory ?? createServerReferenceRegistry;
|
|
4262
|
+
const server = options.server ?? serverFactory(undefined, { registry, type: "server" });
|
|
3894
4263
|
const partials = options.partials ?? createPartialRegistry(undefined, { registry, type: "partial" });
|
|
3895
4264
|
const routes = options.routes ?? createRouteRegistry(undefined, { registry, type: "route" });
|
|
3896
4265
|
const components = options.components ?? createComponentRegistry(undefined, { registry, type: "component" });
|
|
@@ -3918,6 +4287,7 @@
|
|
|
3918
4287
|
},
|
|
3919
4288
|
loader,
|
|
3920
4289
|
router,
|
|
4290
|
+
scheduler,
|
|
3921
4291
|
attributes,
|
|
3922
4292
|
|
|
3923
4293
|
start() {
|
|
@@ -3934,12 +4304,13 @@
|
|
|
3934
4304
|
handlers,
|
|
3935
4305
|
server,
|
|
3936
4306
|
cache: browserCache,
|
|
4307
|
+
scheduler,
|
|
3937
4308
|
attributes
|
|
3938
4309
|
});
|
|
3939
4310
|
runtime.loader = loader;
|
|
3940
4311
|
|
|
3941
4312
|
configureServerContext({ cache: browserCache });
|
|
3942
|
-
signals._setContext?.({ server, loader, cache: browserCache });
|
|
4313
|
+
signals._setContext?.({ server, loader, cache: browserCache, scheduler });
|
|
3943
4314
|
|
|
3944
4315
|
loader.start();
|
|
3945
4316
|
|
|
@@ -3955,6 +4326,7 @@
|
|
|
3955
4326
|
server,
|
|
3956
4327
|
cache: browserCache,
|
|
3957
4328
|
partials,
|
|
4329
|
+
scheduler,
|
|
3958
4330
|
fetch: options.fetch,
|
|
3959
4331
|
routeEndpoint: options.routeEndpoint,
|
|
3960
4332
|
attributes
|
|
@@ -3966,7 +4338,7 @@
|
|
|
3966
4338
|
}
|
|
3967
4339
|
} else {
|
|
3968
4340
|
configureServerContext({ cache: serverCache });
|
|
3969
|
-
signals._setContext?.({ server, cache: serverCache });
|
|
4341
|
+
signals._setContext?.({ server, cache: serverCache, scheduler });
|
|
3970
4342
|
}
|
|
3971
4343
|
|
|
3972
4344
|
return runtime;
|
|
@@ -3980,9 +4352,10 @@
|
|
|
3980
4352
|
async render(url) {
|
|
3981
4353
|
assertActive();
|
|
3982
4354
|
configureServerContext({ cache: serverCache });
|
|
3983
|
-
signals._setContext?.({ server, cache: serverCache });
|
|
4355
|
+
signals._setContext?.({ server, cache: serverCache, scheduler });
|
|
3984
4356
|
const matched = routes.match(url);
|
|
3985
4357
|
if (!matched) {
|
|
4358
|
+
await scheduler.flush();
|
|
3986
4359
|
return {
|
|
3987
4360
|
html: renderDocument("", { status: 404, signals, browserCache, boundary: options.boundary ?? "route", attributes }),
|
|
3988
4361
|
status: 404,
|
|
@@ -4002,8 +4375,8 @@
|
|
|
4002
4375
|
cache: serverCache,
|
|
4003
4376
|
browserCache,
|
|
4004
4377
|
partials,
|
|
4005
|
-
|
|
4006
|
-
|
|
4378
|
+
scheduler,
|
|
4379
|
+
...currentRequestContext()
|
|
4007
4380
|
})
|
|
4008
4381
|
: { html: "" };
|
|
4009
4382
|
|
|
@@ -4016,6 +4389,8 @@
|
|
|
4016
4389
|
browserCache.restore(result.cache.browser);
|
|
4017
4390
|
}
|
|
4018
4391
|
|
|
4392
|
+
await scheduler.flush();
|
|
4393
|
+
|
|
4019
4394
|
const status = result.status ?? 200;
|
|
4020
4395
|
return {
|
|
4021
4396
|
html: renderDocument(result.html, { status, signals, browserCache, boundary: result.boundary ?? options.boundary ?? "route", attributes }),
|
|
@@ -4034,6 +4409,9 @@
|
|
|
4034
4409
|
router?.destroy?.();
|
|
4035
4410
|
loader?.destroy?.();
|
|
4036
4411
|
signals.destroy?.();
|
|
4412
|
+
if (ownsScheduler) {
|
|
4413
|
+
scheduler.destroy();
|
|
4414
|
+
}
|
|
4037
4415
|
},
|
|
4038
4416
|
|
|
4039
4417
|
_applyUse(normalized) {
|
|
@@ -4054,11 +4432,23 @@
|
|
|
4054
4432
|
loader,
|
|
4055
4433
|
router,
|
|
4056
4434
|
cache,
|
|
4057
|
-
|
|
4058
|
-
|
|
4435
|
+
scheduler,
|
|
4436
|
+
requestContext: options.requestContext,
|
|
4437
|
+
...currentRequestContext()
|
|
4059
4438
|
});
|
|
4060
4439
|
}
|
|
4061
4440
|
|
|
4441
|
+
function currentRequestContext() {
|
|
4442
|
+
const context = readRequestContextLike(options.requestContext);
|
|
4443
|
+
return {
|
|
4444
|
+
requestContext: context,
|
|
4445
|
+
request: context.request ?? options.request,
|
|
4446
|
+
headers: context.headers ?? options.headers,
|
|
4447
|
+
cookies: context.cookies ?? options.cookies,
|
|
4448
|
+
locals: context.locals ?? options.locals
|
|
4449
|
+
};
|
|
4450
|
+
}
|
|
4451
|
+
|
|
4062
4452
|
function assertActive() {
|
|
4063
4453
|
if (destroyed) {
|
|
4064
4454
|
throw new Error("Async app runtime has been destroyed.");
|
|
@@ -4212,6 +4602,77 @@
|
|
|
4212
4602
|
}
|
|
4213
4603
|
}
|
|
4214
4604
|
|
|
4605
|
+
function createServerReferenceRegistry(initialMap = {}, options = {}) {
|
|
4606
|
+
const registry = options.registry ?? createRegistryStore();
|
|
4607
|
+
const type = options.type ?? "server";
|
|
4608
|
+
const defaults = {};
|
|
4609
|
+
|
|
4610
|
+
const reference = {
|
|
4611
|
+
registry,
|
|
4612
|
+
|
|
4613
|
+
register(id, value) {
|
|
4614
|
+
registry.register(type, id, value);
|
|
4615
|
+
return id;
|
|
4616
|
+
},
|
|
4617
|
+
|
|
4618
|
+
registerMany(map) {
|
|
4619
|
+
for (const [id, value] of Object.entries(map ?? {})) {
|
|
4620
|
+
reference.register(id, value);
|
|
4621
|
+
}
|
|
4622
|
+
return reference;
|
|
4623
|
+
},
|
|
4624
|
+
|
|
4625
|
+
unregister(id) {
|
|
4626
|
+
return registry.unregister(type, id);
|
|
4627
|
+
},
|
|
4628
|
+
|
|
4629
|
+
resolve() {
|
|
4630
|
+
return undefined;
|
|
4631
|
+
},
|
|
4632
|
+
|
|
4633
|
+
async run(id) {
|
|
4634
|
+
throw new Error(`Server command "${id}" cannot run without a server proxy or server registry.`);
|
|
4635
|
+
},
|
|
4636
|
+
|
|
4637
|
+
keys() {
|
|
4638
|
+
return registry.keys(type);
|
|
4639
|
+
},
|
|
4640
|
+
|
|
4641
|
+
entries() {
|
|
4642
|
+
return registry.entries(type);
|
|
4643
|
+
},
|
|
4644
|
+
|
|
4645
|
+
inspect() {
|
|
4646
|
+
return registry.entries(type);
|
|
4647
|
+
},
|
|
4648
|
+
|
|
4649
|
+
_setContext(context = {}) {
|
|
4650
|
+
Object.assign(defaults, context);
|
|
4651
|
+
return reference;
|
|
4652
|
+
},
|
|
4653
|
+
|
|
4654
|
+
_adoptMany() {
|
|
4655
|
+
return reference;
|
|
4656
|
+
}
|
|
4657
|
+
};
|
|
4658
|
+
|
|
4659
|
+
reference.registerMany(initialMap);
|
|
4660
|
+
return createServerNamespace((id, args, context) => reference.run(id, args, context), reference, () => defaults);
|
|
4661
|
+
}
|
|
4662
|
+
|
|
4663
|
+
function readRequestContextLike(store) {
|
|
4664
|
+
if (!store) {
|
|
4665
|
+
return {};
|
|
4666
|
+
}
|
|
4667
|
+
if (typeof store.get === "function") {
|
|
4668
|
+
return store.get() ?? {};
|
|
4669
|
+
}
|
|
4670
|
+
if (typeof store.getStore === "function") {
|
|
4671
|
+
return store.getStore() ?? {};
|
|
4672
|
+
}
|
|
4673
|
+
return {};
|
|
4674
|
+
}
|
|
4675
|
+
|
|
4215
4676
|
function normalizeEntries(type, entries = {}) {
|
|
4216
4677
|
if (type !== "signal") {
|
|
4217
4678
|
return { ...(entries ?? {}) };
|
|
@@ -4263,6 +4724,312 @@
|
|
|
4263
4724
|
return { defineApp, createApp, readSnapshot, Async };
|
|
4264
4725
|
})();
|
|
4265
4726
|
|
|
4727
|
+
const __boundaryReceiverModule = (() => {
|
|
4728
|
+
const defaultRecentLimit = 50;
|
|
4729
|
+
|
|
4730
|
+
function createBoundaryReceiver(options = {}) {
|
|
4731
|
+
const loader = options.loader;
|
|
4732
|
+
const signals = options.signals ?? loader?.signals;
|
|
4733
|
+
const cache = options.cache ?? loader?.cache;
|
|
4734
|
+
const scheduler = options.scheduler ?? loader?.scheduler;
|
|
4735
|
+
const router = options.router ?? loader?.router;
|
|
4736
|
+
const recentLimit = options.recentLimit ?? defaultRecentLimit;
|
|
4737
|
+
const throwOnError = options.throwOnError === true;
|
|
4738
|
+
const onApply = typeof options.onApply === "function" ? options.onApply : undefined;
|
|
4739
|
+
const onIgnore = typeof options.onIgnore === "function" ? options.onIgnore : undefined;
|
|
4740
|
+
const onError = typeof options.onError === "function" ? options.onError : undefined;
|
|
4741
|
+
const isScopeDestroyed = typeof options.isScopeDestroyed === "function"
|
|
4742
|
+
? options.isScopeDestroyed
|
|
4743
|
+
: (scope) => scheduler?.isScopeDestroyed?.(scope) ?? scheduler?.inspectDestroyed?.(scope) ?? false;
|
|
4744
|
+
|
|
4745
|
+
if (!loader || typeof loader.swap !== "function") {
|
|
4746
|
+
throw new TypeError("createBoundaryReceiver(...) requires a loader with swap(boundary, html).");
|
|
4747
|
+
}
|
|
4748
|
+
if (!Number.isInteger(recentLimit) || recentLimit < 0) {
|
|
4749
|
+
throw new TypeError("createBoundaryReceiver(...) recentLimit must be a non-negative integer.");
|
|
4750
|
+
}
|
|
4751
|
+
|
|
4752
|
+
const boundaries = new Map();
|
|
4753
|
+
const recent = [];
|
|
4754
|
+
let destroyed = false;
|
|
4755
|
+
|
|
4756
|
+
const receiver = {
|
|
4757
|
+
async apply(patch) {
|
|
4758
|
+
if (destroyed) {
|
|
4759
|
+
throw new Error("Boundary receiver has been destroyed.");
|
|
4760
|
+
}
|
|
4761
|
+
|
|
4762
|
+
const normalized = validatePatch(patch);
|
|
4763
|
+
const record = boundaryRecord(normalized.boundary);
|
|
4764
|
+
if (normalized.seq <= record.lastSeq) {
|
|
4765
|
+
const result = {
|
|
4766
|
+
status: "ignored-stale",
|
|
4767
|
+
boundary: normalized.boundary,
|
|
4768
|
+
seq: normalized.seq,
|
|
4769
|
+
lastSeq: record.lastSeq
|
|
4770
|
+
};
|
|
4771
|
+
record.ignored += 1;
|
|
4772
|
+
record.lastStatus = result.status;
|
|
4773
|
+
remember(result);
|
|
4774
|
+
onIgnore?.(result, patch);
|
|
4775
|
+
return result;
|
|
4776
|
+
}
|
|
4777
|
+
|
|
4778
|
+
if (normalized.parentScope !== undefined && isScopeDestroyed(normalized.parentScope)) {
|
|
4779
|
+
const result = {
|
|
4780
|
+
status: "ignored-destroyed",
|
|
4781
|
+
boundary: normalized.boundary,
|
|
4782
|
+
seq: normalized.seq,
|
|
4783
|
+
parentScope: normalized.parentScope
|
|
4784
|
+
};
|
|
4785
|
+
record.ignored += 1;
|
|
4786
|
+
record.lastStatus = result.status;
|
|
4787
|
+
remember(result);
|
|
4788
|
+
onIgnore?.(result, patch);
|
|
4789
|
+
return result;
|
|
4790
|
+
}
|
|
4791
|
+
|
|
4792
|
+
record.lastSeq = normalized.seq;
|
|
4793
|
+
|
|
4794
|
+
if (Object.hasOwn(normalized, "error")) {
|
|
4795
|
+
const error = toStableError(normalized.error);
|
|
4796
|
+
const result = {
|
|
4797
|
+
status: "errored",
|
|
4798
|
+
boundary: normalized.boundary,
|
|
4799
|
+
seq: normalized.seq,
|
|
4800
|
+
error
|
|
4801
|
+
};
|
|
4802
|
+
record.errored += 1;
|
|
4803
|
+
record.lastStatus = result.status;
|
|
4804
|
+
remember(result);
|
|
4805
|
+
onError?.(error, result, patch);
|
|
4806
|
+
if (throwOnError) {
|
|
4807
|
+
throw error;
|
|
4808
|
+
}
|
|
4809
|
+
return result;
|
|
4810
|
+
}
|
|
4811
|
+
|
|
4812
|
+
if (normalized.signals) {
|
|
4813
|
+
if (!signals || typeof signals.set !== "function") {
|
|
4814
|
+
throw new Error("Boundary patch includes signals, but no signal registry is available.");
|
|
4815
|
+
}
|
|
4816
|
+
for (const [path, value] of Object.entries(normalized.signals)) {
|
|
4817
|
+
signals.set(path, value);
|
|
4818
|
+
}
|
|
4819
|
+
}
|
|
4820
|
+
|
|
4821
|
+
if (normalized.cache?.browser) {
|
|
4822
|
+
if (!cache || typeof cache.restore !== "function") {
|
|
4823
|
+
throw new Error("Boundary patch includes browser cache, but no cache registry is available.");
|
|
4824
|
+
}
|
|
4825
|
+
cache.restore(normalized.cache.browser);
|
|
4826
|
+
}
|
|
4827
|
+
|
|
4828
|
+
if (normalized.html != null) {
|
|
4829
|
+
loader.swap(normalized.boundary, normalized.html);
|
|
4830
|
+
}
|
|
4831
|
+
|
|
4832
|
+
await flushScheduler(scheduler, normalized.scope);
|
|
4833
|
+
|
|
4834
|
+
if (normalized.redirect) {
|
|
4835
|
+
await followRedirect(normalized.redirect, router, loader);
|
|
4836
|
+
const result = {
|
|
4837
|
+
status: "redirected",
|
|
4838
|
+
boundary: normalized.boundary,
|
|
4839
|
+
seq: normalized.seq,
|
|
4840
|
+
redirect: normalized.redirect
|
|
4841
|
+
};
|
|
4842
|
+
record.applied += 1;
|
|
4843
|
+
record.lastStatus = result.status;
|
|
4844
|
+
remember(result);
|
|
4845
|
+
onApply?.(result, patch);
|
|
4846
|
+
return result;
|
|
4847
|
+
}
|
|
4848
|
+
|
|
4849
|
+
const result = {
|
|
4850
|
+
status: "applied",
|
|
4851
|
+
boundary: normalized.boundary,
|
|
4852
|
+
seq: normalized.seq
|
|
4853
|
+
};
|
|
4854
|
+
record.applied += 1;
|
|
4855
|
+
record.lastStatus = result.status;
|
|
4856
|
+
remember(result);
|
|
4857
|
+
onApply?.(result, patch);
|
|
4858
|
+
return result;
|
|
4859
|
+
},
|
|
4860
|
+
|
|
4861
|
+
inspect() {
|
|
4862
|
+
const snapshot = {};
|
|
4863
|
+
for (const [boundary, record] of boundaries) {
|
|
4864
|
+
snapshot[boundary] = {
|
|
4865
|
+
lastSeq: record.lastSeq,
|
|
4866
|
+
applied: record.applied,
|
|
4867
|
+
ignored: record.ignored,
|
|
4868
|
+
lastStatus: record.lastStatus
|
|
4869
|
+
};
|
|
4870
|
+
if (record.errored > 0) {
|
|
4871
|
+
snapshot[boundary].errored = record.errored;
|
|
4872
|
+
}
|
|
4873
|
+
}
|
|
4874
|
+
return {
|
|
4875
|
+
destroyed,
|
|
4876
|
+
boundaries: snapshot,
|
|
4877
|
+
recent: recent.map((entry) => ({ ...entry }))
|
|
4878
|
+
};
|
|
4879
|
+
},
|
|
4880
|
+
|
|
4881
|
+
reset(boundary) {
|
|
4882
|
+
if (boundary === undefined) {
|
|
4883
|
+
boundaries.clear();
|
|
4884
|
+
recent.length = 0;
|
|
4885
|
+
return receiver;
|
|
4886
|
+
}
|
|
4887
|
+
assertBoundary(boundary);
|
|
4888
|
+
boundaries.delete(boundary);
|
|
4889
|
+
for (let index = recent.length - 1; index >= 0; index -= 1) {
|
|
4890
|
+
if (recent[index].boundary === boundary) {
|
|
4891
|
+
recent.splice(index, 1);
|
|
4892
|
+
}
|
|
4893
|
+
}
|
|
4894
|
+
return receiver;
|
|
4895
|
+
},
|
|
4896
|
+
|
|
4897
|
+
destroy() {
|
|
4898
|
+
destroyed = true;
|
|
4899
|
+
boundaries.clear();
|
|
4900
|
+
recent.length = 0;
|
|
4901
|
+
}
|
|
4902
|
+
};
|
|
4903
|
+
|
|
4904
|
+
return receiver;
|
|
4905
|
+
|
|
4906
|
+
function boundaryRecord(boundary) {
|
|
4907
|
+
if (!boundaries.has(boundary)) {
|
|
4908
|
+
boundaries.set(boundary, {
|
|
4909
|
+
lastSeq: -Infinity,
|
|
4910
|
+
applied: 0,
|
|
4911
|
+
ignored: 0,
|
|
4912
|
+
errored: 0,
|
|
4913
|
+
lastStatus: undefined
|
|
4914
|
+
});
|
|
4915
|
+
}
|
|
4916
|
+
return boundaries.get(boundary);
|
|
4917
|
+
}
|
|
4918
|
+
|
|
4919
|
+
function remember(result) {
|
|
4920
|
+
if (recentLimit === 0) {
|
|
4921
|
+
return;
|
|
4922
|
+
}
|
|
4923
|
+
recent.push(toRecentEntry(result));
|
|
4924
|
+
while (recent.length > recentLimit) {
|
|
4925
|
+
recent.shift();
|
|
4926
|
+
}
|
|
4927
|
+
}
|
|
4928
|
+
}
|
|
4929
|
+
|
|
4930
|
+
function validatePatch(patch) {
|
|
4931
|
+
if (!patch || typeof patch !== "object" || Array.isArray(patch)) {
|
|
4932
|
+
throw new TypeError("receiver.apply(patch) requires a boundary patch object.");
|
|
4933
|
+
}
|
|
4934
|
+
|
|
4935
|
+
assertBoundary(patch.boundary);
|
|
4936
|
+
if (typeof patch.seq !== "number" || !Number.isFinite(patch.seq)) {
|
|
4937
|
+
throw new TypeError("Boundary patch seq must be a finite number.");
|
|
4938
|
+
}
|
|
4939
|
+
|
|
4940
|
+
if (patch.signals !== undefined && !isPlainObject(patch.signals)) {
|
|
4941
|
+
throw new TypeError("Boundary patch signals must be an object.");
|
|
4942
|
+
}
|
|
4943
|
+
if (patch.cache !== undefined && !isPlainObject(patch.cache)) {
|
|
4944
|
+
throw new TypeError("Boundary patch cache must be an object.");
|
|
4945
|
+
}
|
|
4946
|
+
if (patch.cache?.browser !== undefined && !isPlainObject(patch.cache.browser)) {
|
|
4947
|
+
throw new TypeError("Boundary patch cache.browser must be an object.");
|
|
4948
|
+
}
|
|
4949
|
+
if (patch.redirect !== undefined && (typeof patch.redirect !== "string" || patch.redirect.length === 0)) {
|
|
4950
|
+
throw new TypeError("Boundary patch redirect must be a non-empty string.");
|
|
4951
|
+
}
|
|
4952
|
+
if (patch.parentScope !== undefined && typeof patch.parentScope !== "string") {
|
|
4953
|
+
throw new TypeError("Boundary patch parentScope must be a string.");
|
|
4954
|
+
}
|
|
4955
|
+
if (patch.scope !== undefined && typeof patch.scope !== "string") {
|
|
4956
|
+
throw new TypeError("Boundary patch scope must be a string.");
|
|
4957
|
+
}
|
|
4958
|
+
|
|
4959
|
+
const hasHtml = Object.hasOwn(patch, "html") && patch.html != null;
|
|
4960
|
+
const hasSignals = patch.signals && Object.keys(patch.signals).length > 0;
|
|
4961
|
+
const hasBrowserCache = patch.cache?.browser && Object.keys(patch.cache.browser).length > 0;
|
|
4962
|
+
const hasRedirect = Boolean(patch.redirect);
|
|
4963
|
+
const hasError = Object.hasOwn(patch, "error");
|
|
4964
|
+
if (!hasHtml && !hasSignals && !hasBrowserCache && !hasRedirect && !hasError) {
|
|
4965
|
+
throw new TypeError("Boundary patch must include html, signals, cache.browser, redirect, or error.");
|
|
4966
|
+
}
|
|
4967
|
+
|
|
4968
|
+
return patch;
|
|
4969
|
+
}
|
|
4970
|
+
|
|
4971
|
+
function assertBoundary(boundary) {
|
|
4972
|
+
if (typeof boundary !== "string" || boundary.length === 0) {
|
|
4973
|
+
throw new TypeError("Boundary patch boundary must be a non-empty string.");
|
|
4974
|
+
}
|
|
4975
|
+
}
|
|
4976
|
+
|
|
4977
|
+
async function flushScheduler(scheduler, scope) {
|
|
4978
|
+
if (!scheduler) {
|
|
4979
|
+
return;
|
|
4980
|
+
}
|
|
4981
|
+
if (scope !== undefined && typeof scheduler.flushScope === "function") {
|
|
4982
|
+
await scheduler.flushScope(scope);
|
|
4983
|
+
return;
|
|
4984
|
+
}
|
|
4985
|
+
if (typeof scheduler.flush === "function") {
|
|
4986
|
+
await scheduler.flush();
|
|
4987
|
+
}
|
|
4988
|
+
}
|
|
4989
|
+
|
|
4990
|
+
async function followRedirect(redirect, router, loader) {
|
|
4991
|
+
if (router && typeof router.navigate === "function") {
|
|
4992
|
+
await router.navigate(redirect);
|
|
4993
|
+
return;
|
|
4994
|
+
}
|
|
4995
|
+
const location = loader?.root?.ownerDocument?.defaultView?.location ?? globalThis.location;
|
|
4996
|
+
location?.assign?.(redirect);
|
|
4997
|
+
}
|
|
4998
|
+
|
|
4999
|
+
function toStableError(value) {
|
|
5000
|
+
if (value instanceof Error) {
|
|
5001
|
+
return value;
|
|
5002
|
+
}
|
|
5003
|
+
if (value && typeof value === "object" && typeof value.message === "string") {
|
|
5004
|
+
return Object.assign(new Error(value.message), value);
|
|
5005
|
+
}
|
|
5006
|
+
return new Error(String(value));
|
|
5007
|
+
}
|
|
5008
|
+
|
|
5009
|
+
function toRecentEntry(result) {
|
|
5010
|
+
const entry = {
|
|
5011
|
+
boundary: result.boundary,
|
|
5012
|
+
seq: result.seq,
|
|
5013
|
+
status: result.status
|
|
5014
|
+
};
|
|
5015
|
+
if (result.status === "ignored-stale") {
|
|
5016
|
+
entry.lastSeq = result.lastSeq;
|
|
5017
|
+
}
|
|
5018
|
+
if (result.status === "ignored-destroyed" && result.parentScope !== undefined) {
|
|
5019
|
+
entry.parentScope = result.parentScope;
|
|
5020
|
+
}
|
|
5021
|
+
if (result.status === "redirected") {
|
|
5022
|
+
entry.redirect = result.redirect;
|
|
5023
|
+
}
|
|
5024
|
+
return entry;
|
|
5025
|
+
}
|
|
5026
|
+
|
|
5027
|
+
function isPlainObject(value) {
|
|
5028
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
5029
|
+
}
|
|
5030
|
+
return { createBoundaryReceiver };
|
|
5031
|
+
})();
|
|
5032
|
+
|
|
4266
5033
|
const __delayModule = (() => {
|
|
4267
5034
|
function delay(ms, signal) {
|
|
4268
5035
|
if (signal?.aborted) {
|
|
@@ -4304,6 +5071,7 @@
|
|
|
4304
5071
|
const { readSnapshot: readSnapshot } = __appModule;
|
|
4305
5072
|
const { attributeName: attributeName } = __attributesModule;
|
|
4306
5073
|
const { defineAttributeConfig: defineAttributeConfig } = __attributesModule;
|
|
5074
|
+
const { createBoundaryReceiver: createBoundaryReceiver } = __boundaryReceiverModule;
|
|
4307
5075
|
const { createCacheRegistry: createCacheRegistry } = __cacheModule;
|
|
4308
5076
|
const { defineCache: defineCache } = __cacheModule;
|
|
4309
5077
|
const { component: component } = __componentModule;
|
|
@@ -4320,14 +5088,17 @@
|
|
|
4320
5088
|
const { createRouter: createRouter } = __routerModule;
|
|
4321
5089
|
const { defineRoute: defineRoute } = __routerModule;
|
|
4322
5090
|
const { route: route } = __routerModule;
|
|
5091
|
+
const { createScheduler: createScheduler } = __schedulerModule;
|
|
5092
|
+
const { applyServerResult: applyServerResult } = __serverModule;
|
|
4323
5093
|
const { createServerProxy: createServerProxy } = __serverModule;
|
|
4324
|
-
const {
|
|
5094
|
+
const { resolveServerCommandArguments: resolveServerCommandArguments } = __serverModule;
|
|
5095
|
+
const { unwrapServerResult: unwrapServerResult } = __serverModule;
|
|
4325
5096
|
const { computed: computed } = __signalsModule;
|
|
4326
5097
|
const { createSignal: createSignal } = __signalsModule;
|
|
4327
5098
|
const { createSignalRegistry: createSignalRegistry } = __signalsModule;
|
|
4328
5099
|
const { effect: effect } = __signalsModule;
|
|
4329
5100
|
const { signal: signal } = __signalsModule;
|
|
4330
|
-
const api = { asyncSignal, Async, createApp, defineApp, readSnapshot, attributeName, defineAttributeConfig, createCacheRegistry, defineCache, component, createComponentRegistry, defineComponent, delay, createHandlerRegistry, html, Loader, AsyncLoader, createPartialRegistry, createRegistryStore, createRouteRegistry, createRouter, defineRoute, route, createServerProxy,
|
|
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 };
|
|
4331
5102
|
assertNoUmdNamespaceConflicts(api, Async);
|
|
4332
5103
|
Object.assign(Async, api);
|
|
4333
5104
|
Async.Async = Async;
|