@async/framework 0.7.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 +16 -0
- package/README.md +81 -14
- package/{framework.d.ts → browser.d.ts} +68 -4
- package/{framework.js → browser.js} +587 -127
- package/browser.min.js +1 -0
- package/{framework.ts → browser.ts} +588 -128
- package/{framework.umd.js → browser.umd.js} +587 -127
- package/browser.umd.min.js +1 -0
- package/package.json +45 -30
- package/server.d.ts +640 -0
- package/src/app.js +110 -11
- package/src/async-signal.js +32 -4
- package/src/browser.js +15 -0
- 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 +15 -2
- package/src/scheduler.js +296 -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,321 @@
|
|
|
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
|
+
inspect() {
|
|
2609
|
+
const counts = {};
|
|
2610
|
+
for (const [phase, queue] of queues) {
|
|
2611
|
+
counts[phase] = queue.filter((job) => !job.canceled).length;
|
|
2612
|
+
}
|
|
2613
|
+
return {
|
|
2614
|
+
strategy,
|
|
2615
|
+
phases: [...phases],
|
|
2616
|
+
pending: counts,
|
|
2617
|
+
scopesDestroyed: destroyedScopes.size,
|
|
2618
|
+
flushing,
|
|
2619
|
+
scheduled
|
|
2620
|
+
};
|
|
2621
|
+
},
|
|
2622
|
+
|
|
2623
|
+
destroy() {
|
|
2624
|
+
destroyed = true;
|
|
2625
|
+
for (const queue of queues.values()) {
|
|
2626
|
+
for (const job of queue) {
|
|
2627
|
+
job.cancel();
|
|
2628
|
+
}
|
|
2629
|
+
queue.length = 0;
|
|
2630
|
+
}
|
|
2631
|
+
keyedJobs.clear();
|
|
2632
|
+
destroyedScopes.clear();
|
|
2633
|
+
}
|
|
2634
|
+
};
|
|
2635
|
+
|
|
2636
|
+
return api;
|
|
2637
|
+
|
|
2638
|
+
function requestFlush() {
|
|
2639
|
+
if (strategy === "manual" || destroyed || flushing || batchDepth > 0 || scheduled) {
|
|
2640
|
+
return;
|
|
2641
|
+
}
|
|
2642
|
+
scheduled = true;
|
|
2643
|
+
scheduleMicrotask(() => {
|
|
2644
|
+
if (!destroyed) {
|
|
2645
|
+
void api.flush();
|
|
2646
|
+
}
|
|
2647
|
+
});
|
|
2648
|
+
}
|
|
2649
|
+
|
|
2650
|
+
async function flushPhase(phase, scope) {
|
|
2651
|
+
const queue = queues.get(phase);
|
|
2652
|
+
const remaining = [];
|
|
2653
|
+
const runnable = [];
|
|
2654
|
+
|
|
2655
|
+
for (const job of queue.splice(0)) {
|
|
2656
|
+
if (job.canceled) {
|
|
2657
|
+
continue;
|
|
2658
|
+
}
|
|
2659
|
+
if (scope !== undefined && job.scope !== scope) {
|
|
2660
|
+
remaining.push(job);
|
|
2661
|
+
continue;
|
|
2662
|
+
}
|
|
2663
|
+
runnable.push(job);
|
|
2664
|
+
}
|
|
2665
|
+
|
|
2666
|
+
queue.push(...remaining);
|
|
2667
|
+
|
|
2668
|
+
for (const job of runnable) {
|
|
2669
|
+
if (job.key) {
|
|
2670
|
+
keyedJobs.delete(job.key);
|
|
2671
|
+
}
|
|
2672
|
+
if (job.canceled || (job.scope !== undefined && destroyedScopes.has(job.scope))) {
|
|
2673
|
+
continue;
|
|
2674
|
+
}
|
|
2675
|
+
try {
|
|
2676
|
+
await job.fn();
|
|
2677
|
+
} catch (error) {
|
|
2678
|
+
if (onError) {
|
|
2679
|
+
onError(error, job);
|
|
2680
|
+
} else {
|
|
2681
|
+
throw error;
|
|
2682
|
+
}
|
|
2683
|
+
}
|
|
2684
|
+
}
|
|
2685
|
+
}
|
|
2686
|
+
|
|
2687
|
+
function hasJobs() {
|
|
2688
|
+
for (const queue of queues.values()) {
|
|
2689
|
+
if (queue.some((job) => !job.canceled)) {
|
|
2690
|
+
return true;
|
|
2691
|
+
}
|
|
2692
|
+
}
|
|
2693
|
+
return false;
|
|
2694
|
+
}
|
|
2695
|
+
|
|
2696
|
+
function hasJobsForScope(scope) {
|
|
2697
|
+
for (const queue of queues.values()) {
|
|
2698
|
+
if (queue.some((job) => !job.canceled && job.scope === scope)) {
|
|
2699
|
+
return true;
|
|
2700
|
+
}
|
|
2701
|
+
}
|
|
2702
|
+
return false;
|
|
2703
|
+
}
|
|
2704
|
+
|
|
2705
|
+
function assertActive() {
|
|
2706
|
+
if (destroyed) {
|
|
2707
|
+
throw new Error("Scheduler has been destroyed.");
|
|
2708
|
+
}
|
|
2709
|
+
}
|
|
2710
|
+
|
|
2711
|
+
function assertPhase(phase) {
|
|
2712
|
+
if (!queues.has(phase)) {
|
|
2713
|
+
throw new Error(`Unknown scheduler phase "${phase}".`);
|
|
2714
|
+
}
|
|
2715
|
+
}
|
|
2716
|
+
|
|
2717
|
+
function scopeKey(scope) {
|
|
2718
|
+
if (scope === undefined) {
|
|
2719
|
+
return "global";
|
|
2720
|
+
}
|
|
2721
|
+
if ((typeof scope === "object" && scope !== null) || typeof scope === "function") {
|
|
2722
|
+
if (!objectScopeIds.has(scope)) {
|
|
2723
|
+
objectScopeIds.set(scope, `scope:${++scopeCounter}`);
|
|
2724
|
+
}
|
|
2725
|
+
return objectScopeIds.get(scope);
|
|
2726
|
+
}
|
|
2727
|
+
return String(scope);
|
|
2728
|
+
}
|
|
2729
|
+
}
|
|
2730
|
+
|
|
2731
|
+
function scheduleMicrotask(fn) {
|
|
2732
|
+
if (typeof queueMicrotask === "function") {
|
|
2733
|
+
queueMicrotask(fn);
|
|
2734
|
+
return;
|
|
2735
|
+
}
|
|
2736
|
+
Promise.resolve().then(fn);
|
|
2737
|
+
}
|
|
2738
|
+
|
|
2739
|
+
function noop() {}
|
|
2740
|
+
return { createScheduler };
|
|
2741
|
+
})();
|
|
2742
|
+
|
|
2430
2743
|
const __loaderModule = (() => {
|
|
2431
2744
|
const { renderComponent } = __componentModule;
|
|
2432
2745
|
const { createHandlerRegistry } = __handlersModule;
|
|
2746
|
+
const { createScheduler } = __schedulerModule;
|
|
2433
2747
|
const { createSignalRegistry, isSignalRef } = __signalsModule;
|
|
2434
2748
|
const { matchAttribute, normalizeAttributeConfig, readAttribute } = __attributesModule;
|
|
2435
2749
|
const inlineBindingPrefix = "__async:inline:";
|
|
2436
2750
|
|
|
2437
|
-
function Loader({ root, signals, handlers, server, router, cache, attributes } = {}) {
|
|
2751
|
+
function Loader({ root, signals, handlers, server, router, cache, attributes, scheduler } = {}) {
|
|
2438
2752
|
const documentRef = root?.ownerDocument ?? root ?? globalThis.document;
|
|
2439
2753
|
const rootNode = root ?? documentRef;
|
|
2440
2754
|
const signalRegistry = signals ?? createSignalRegistry();
|
|
2441
2755
|
const handlerRegistry = handlers ?? createHandlerRegistry();
|
|
2756
|
+
const schedulerInstance = scheduler ?? createScheduler();
|
|
2757
|
+
const ownsScheduler = !scheduler;
|
|
2442
2758
|
const attributeConfig = normalizeAttributeConfig(attributes);
|
|
2443
2759
|
const cleanups = new Set();
|
|
2444
2760
|
const eventBindings = new WeakMap();
|
|
@@ -2459,6 +2775,7 @@
|
|
|
2459
2775
|
server,
|
|
2460
2776
|
router,
|
|
2461
2777
|
cache,
|
|
2778
|
+
scheduler: schedulerInstance,
|
|
2462
2779
|
attributes: attributeConfig,
|
|
2463
2780
|
|
|
2464
2781
|
start() {
|
|
@@ -2498,6 +2815,7 @@
|
|
|
2498
2815
|
server: api.server,
|
|
2499
2816
|
router: api.router,
|
|
2500
2817
|
cache: api.cache,
|
|
2818
|
+
scheduler: schedulerInstance,
|
|
2501
2819
|
attributes: attributeConfig
|
|
2502
2820
|
});
|
|
2503
2821
|
cleanupChildren(target);
|
|
@@ -2518,6 +2836,9 @@
|
|
|
2518
2836
|
runCleanup(cleanup);
|
|
2519
2837
|
}
|
|
2520
2838
|
cleanups.clear();
|
|
2839
|
+
if (ownsScheduler) {
|
|
2840
|
+
schedulerInstance.destroy();
|
|
2841
|
+
}
|
|
2521
2842
|
},
|
|
2522
2843
|
|
|
2523
2844
|
_observeVisible(target, fn) {
|
|
@@ -2535,13 +2856,14 @@
|
|
|
2535
2856
|
}
|
|
2536
2857
|
};
|
|
2537
2858
|
|
|
2538
|
-
signalRegistry._setContext?.({ server: api.server, router: api.router, loader: api, cache: api.cache });
|
|
2859
|
+
signalRegistry._setContext?.({ server: api.server, router: api.router, loader: api, cache: api.cache, scheduler: schedulerInstance });
|
|
2539
2860
|
api.server?._setContext?.({
|
|
2540
2861
|
signals: signalRegistry,
|
|
2541
2862
|
handlers: handlerRegistry,
|
|
2542
2863
|
loader: api,
|
|
2543
2864
|
router: api.router,
|
|
2544
|
-
cache: api.cache
|
|
2865
|
+
cache: api.cache,
|
|
2866
|
+
scheduler: schedulerInstance
|
|
2545
2867
|
});
|
|
2546
2868
|
|
|
2547
2869
|
function bindEventAttributes(scope) {
|
|
@@ -2573,18 +2895,19 @@
|
|
|
2573
2895
|
|
|
2574
2896
|
const listener = async (event) => {
|
|
2575
2897
|
try {
|
|
2576
|
-
await handlerRegistry.run(ref, {
|
|
2898
|
+
await schedulerInstance.batch(() => handlerRegistry.run(ref, {
|
|
2577
2899
|
signals: signalRegistry,
|
|
2578
2900
|
handlers: handlerRegistry,
|
|
2579
2901
|
loader: api,
|
|
2580
2902
|
server: api.server,
|
|
2581
2903
|
router: api.router,
|
|
2582
2904
|
cache: api.cache,
|
|
2905
|
+
scheduler: schedulerInstance,
|
|
2583
2906
|
event,
|
|
2584
2907
|
element,
|
|
2585
2908
|
el: element,
|
|
2586
2909
|
root: rootNode
|
|
2587
|
-
});
|
|
2910
|
+
}));
|
|
2588
2911
|
} catch (error) {
|
|
2589
2912
|
dispatchAsyncError(element, error);
|
|
2590
2913
|
}
|
|
@@ -2700,7 +3023,12 @@
|
|
|
2700
3023
|
|
|
2701
3024
|
const read = () => readBinding(path, options);
|
|
2702
3025
|
apply(read());
|
|
2703
|
-
addCleanup(subscribeBinding(path, () =>
|
|
3026
|
+
addCleanup(subscribeBinding(path, () => {
|
|
3027
|
+
schedulerInstance.enqueue("binding", () => apply(read()), {
|
|
3028
|
+
scope: element,
|
|
3029
|
+
key
|
|
3030
|
+
});
|
|
3031
|
+
}), element);
|
|
2704
3032
|
}
|
|
2705
3033
|
|
|
2706
3034
|
function bindValueWriter(element, path) {
|
|
@@ -2761,7 +3089,12 @@
|
|
|
2761
3089
|
const state = {
|
|
2762
3090
|
id,
|
|
2763
3091
|
templates,
|
|
2764
|
-
cleanup: signalRegistry.subscribe(`${id}.$status`, () =>
|
|
3092
|
+
cleanup: signalRegistry.subscribe(`${id}.$status`, () => {
|
|
3093
|
+
schedulerInstance.enqueue("binding", () => renderBoundary(boundary), {
|
|
3094
|
+
scope: boundary,
|
|
3095
|
+
key: `boundary:${id}`
|
|
3096
|
+
});
|
|
3097
|
+
})
|
|
2765
3098
|
};
|
|
2766
3099
|
boundaryState.set(boundary, state);
|
|
2767
3100
|
addCleanup(state.cleanup, boundary);
|
|
@@ -2801,7 +3134,7 @@
|
|
|
2801
3134
|
}
|
|
2802
3135
|
mountedElements.add(element);
|
|
2803
3136
|
for (const ref of refs) {
|
|
2804
|
-
runPseudo(element, ref);
|
|
3137
|
+
scheduleLifecycle(element, () => runPseudo(element, ref), `attach:${ref}`);
|
|
2805
3138
|
}
|
|
2806
3139
|
}
|
|
2807
3140
|
|
|
@@ -2814,7 +3147,7 @@
|
|
|
2814
3147
|
continue;
|
|
2815
3148
|
}
|
|
2816
3149
|
visibleElements.add(element);
|
|
2817
|
-
addCleanup(observeVisible(element, () => runPseudo(element, ref)), element);
|
|
3150
|
+
addCleanup(observeVisible(element, () => scheduleLifecycle(element, () => runPseudo(element, ref), `visible:${ref}`)), element);
|
|
2818
3151
|
}
|
|
2819
3152
|
}
|
|
2820
3153
|
|
|
@@ -2838,6 +3171,7 @@
|
|
|
2838
3171
|
server: api.server,
|
|
2839
3172
|
router: api.router,
|
|
2840
3173
|
cache: api.cache,
|
|
3174
|
+
scheduler: schedulerInstance,
|
|
2841
3175
|
element,
|
|
2842
3176
|
el: element,
|
|
2843
3177
|
root: rootNode
|
|
@@ -2856,10 +3190,13 @@
|
|
|
2856
3190
|
const ownerWindow = target.ownerDocument?.defaultView ?? globalThis;
|
|
2857
3191
|
const Observer = ownerWindow.IntersectionObserver ?? globalThis.IntersectionObserver;
|
|
2858
3192
|
if (!Observer) {
|
|
2859
|
-
|
|
3193
|
+
schedulerInstance.enqueue("lifecycle", () => {
|
|
2860
3194
|
if (!destroyed) {
|
|
2861
3195
|
fn(target);
|
|
2862
3196
|
}
|
|
3197
|
+
}, {
|
|
3198
|
+
scope: target,
|
|
3199
|
+
key: "visible:fallback"
|
|
2863
3200
|
});
|
|
2864
3201
|
return () => {};
|
|
2865
3202
|
}
|
|
@@ -2914,6 +3251,7 @@
|
|
|
2914
3251
|
}
|
|
2915
3252
|
for (const element of elementsIn(node)) {
|
|
2916
3253
|
runScopedCleanups(element);
|
|
3254
|
+
schedulerInstance.markScopeDestroyed(element);
|
|
2917
3255
|
}
|
|
2918
3256
|
}
|
|
2919
3257
|
|
|
@@ -2937,6 +3275,13 @@
|
|
|
2937
3275
|
}
|
|
2938
3276
|
}
|
|
2939
3277
|
|
|
3278
|
+
function scheduleLifecycle(element, fn, key) {
|
|
3279
|
+
schedulerInstance.enqueue("lifecycle", fn, {
|
|
3280
|
+
scope: element,
|
|
3281
|
+
key
|
|
3282
|
+
});
|
|
3283
|
+
}
|
|
3284
|
+
|
|
2940
3285
|
return api;
|
|
2941
3286
|
}
|
|
2942
3287
|
|
|
@@ -3267,6 +3612,7 @@
|
|
|
3267
3612
|
const __routerModule = (() => {
|
|
3268
3613
|
const { Loader } = __loaderModule;
|
|
3269
3614
|
const { createHandlerRegistry } = __handlersModule;
|
|
3615
|
+
const { createScheduler } = __schedulerModule;
|
|
3270
3616
|
const { createSignalRegistry } = __signalsModule;
|
|
3271
3617
|
const { applyServerResult } = __serverModule;
|
|
3272
3618
|
const { createRegistryStore } = __registryStoreModule;
|
|
@@ -3387,12 +3733,15 @@
|
|
|
3387
3733
|
partials,
|
|
3388
3734
|
fetch: fetchImpl = globalThis.fetch?.bind(globalThis),
|
|
3389
3735
|
routeEndpoint = "/__async/route",
|
|
3390
|
-
attributes
|
|
3736
|
+
attributes,
|
|
3737
|
+
scheduler
|
|
3391
3738
|
} = {}) {
|
|
3392
3739
|
const documentRef = root?.ownerDocument ?? root ?? globalThis.document;
|
|
3393
3740
|
const rootNode = root ?? documentRef;
|
|
3394
3741
|
const signalRegistry = signals ?? loader?.signals ?? createSignalRegistry();
|
|
3395
3742
|
const handlerRegistry = handlers ?? loader?.handlers ?? createHandlerRegistry();
|
|
3743
|
+
const schedulerInstance = scheduler ?? loader?.scheduler ?? createScheduler();
|
|
3744
|
+
const ownsScheduler = !scheduler && !loader?.scheduler;
|
|
3396
3745
|
const attributeConfig = normalizeAttributeConfig(attributes ?? loader?.attributes);
|
|
3397
3746
|
const loaderInstance =
|
|
3398
3747
|
loader ??
|
|
@@ -3402,6 +3751,7 @@
|
|
|
3402
3751
|
handlers: handlerRegistry,
|
|
3403
3752
|
server,
|
|
3404
3753
|
cache,
|
|
3754
|
+
scheduler: schedulerInstance,
|
|
3405
3755
|
attributes: attributeConfig
|
|
3406
3756
|
});
|
|
3407
3757
|
const ownsLoader = !loader;
|
|
@@ -3421,12 +3771,13 @@
|
|
|
3421
3771
|
server,
|
|
3422
3772
|
cache,
|
|
3423
3773
|
partials,
|
|
3774
|
+
scheduler: schedulerInstance,
|
|
3424
3775
|
attributes: attributeConfig,
|
|
3425
3776
|
|
|
3426
3777
|
start() {
|
|
3427
3778
|
assertActive();
|
|
3428
3779
|
loaderInstance.router = api;
|
|
3429
|
-
signalRegistry._setContext?.({ router: api, loader: loaderInstance, server, cache });
|
|
3780
|
+
signalRegistry._setContext?.({ router: api, loader: loaderInstance, server, cache, scheduler: schedulerInstance });
|
|
3430
3781
|
if (ownsLoader) {
|
|
3431
3782
|
loaderInstance.start();
|
|
3432
3783
|
}
|
|
@@ -3490,6 +3841,9 @@
|
|
|
3490
3841
|
cleanup();
|
|
3491
3842
|
}
|
|
3492
3843
|
cleanups.clear();
|
|
3844
|
+
if (ownsScheduler) {
|
|
3845
|
+
schedulerInstance.destroy();
|
|
3846
|
+
}
|
|
3493
3847
|
}
|
|
3494
3848
|
};
|
|
3495
3849
|
|
|
@@ -3595,13 +3949,16 @@
|
|
|
3595
3949
|
loader: loaderInstance,
|
|
3596
3950
|
router: api,
|
|
3597
3951
|
cache,
|
|
3952
|
+
scheduler: schedulerInstance,
|
|
3598
3953
|
abort: navigation?.abort
|
|
3599
3954
|
});
|
|
3955
|
+
await schedulerInstance.flush();
|
|
3600
3956
|
if (!isActiveNavigation(navigation)) {
|
|
3601
3957
|
return;
|
|
3602
3958
|
}
|
|
3603
3959
|
if (result?.html != null && !result.boundary && !result.redirect) {
|
|
3604
3960
|
loaderInstance.swap(boundary, result.html);
|
|
3961
|
+
await schedulerInstance.flush();
|
|
3605
3962
|
}
|
|
3606
3963
|
if (result?.redirect || options.history === false) {
|
|
3607
3964
|
return;
|
|
@@ -3646,6 +4003,7 @@
|
|
|
3646
4003
|
loader: loaderInstance,
|
|
3647
4004
|
server,
|
|
3648
4005
|
cache,
|
|
4006
|
+
scheduler: schedulerInstance,
|
|
3649
4007
|
abort: navigation?.abort
|
|
3650
4008
|
};
|
|
3651
4009
|
}
|
|
@@ -3832,15 +4190,17 @@
|
|
|
3832
4190
|
const { Loader } = __loaderModule;
|
|
3833
4191
|
const { createPartialRegistry } = __partialsModule;
|
|
3834
4192
|
const { createRouteRegistry, createRouter } = __routerModule;
|
|
3835
|
-
const {
|
|
4193
|
+
const { createScheduler } = __schedulerModule;
|
|
4194
|
+
const { createServerNamespace } = __serverModule;
|
|
3836
4195
|
const { createSignal, createSignalRegistry } = __signalsModule;
|
|
3837
4196
|
const { createRegistryStore } = __registryStoreModule;
|
|
3838
4197
|
const { attributeName, normalizeAttributeConfig } = __attributesModule;
|
|
3839
4198
|
const registryTypes = new Set(["signal", "handler", "server", "partial", "route", "component"]);
|
|
3840
4199
|
|
|
3841
|
-
function defineApp(initial) {
|
|
4200
|
+
function defineApp(initial, options = {}) {
|
|
3842
4201
|
const registry = createRegistryStore(undefined, { target: "browser" });
|
|
3843
4202
|
const runtimes = new Set();
|
|
4203
|
+
const createRuntime = options.createRuntime ?? createApp;
|
|
3844
4204
|
|
|
3845
4205
|
const app = {
|
|
3846
4206
|
registry,
|
|
@@ -3859,7 +4219,7 @@
|
|
|
3859
4219
|
},
|
|
3860
4220
|
|
|
3861
4221
|
start(options = {}) {
|
|
3862
|
-
const runtime =
|
|
4222
|
+
const runtime = createRuntime(app, options).start();
|
|
3863
4223
|
app.runtime = runtime;
|
|
3864
4224
|
return runtime;
|
|
3865
4225
|
},
|
|
@@ -3884,13 +4244,18 @@
|
|
|
3884
4244
|
function createApp(appOrDefinition = Async, options = {}) {
|
|
3885
4245
|
const app = isAppHub(appOrDefinition) ? appOrDefinition : defineApp(appOrDefinition ?? {});
|
|
3886
4246
|
const target = options.target ?? "browser";
|
|
4247
|
+
const scheduler = options.scheduler ?? options.loader?.scheduler ?? createScheduler({
|
|
4248
|
+
strategy: target === "server" ? "manual" : "microtask"
|
|
4249
|
+
});
|
|
4250
|
+
const ownsScheduler = !options.scheduler && !options.loader?.scheduler;
|
|
3887
4251
|
const attributes = normalizeAttributeConfig(options.attributes);
|
|
3888
4252
|
const registry = options.registry ?? app.registry.view({ target });
|
|
3889
4253
|
const signals = options.signals ?? createSignalRegistry(undefined, { registry, type: "signal" });
|
|
3890
4254
|
const handlers = options.handlers ?? createHandlerRegistry(undefined, { registry, type: "handler" });
|
|
3891
4255
|
const serverCache = createCacheRegistry(undefined, { registry, type: "cache.server" });
|
|
3892
4256
|
const browserCache = createCacheRegistry(undefined, { registry, type: "cache.browser" });
|
|
3893
|
-
const
|
|
4257
|
+
const serverFactory = options.serverFactory ?? createServerReferenceRegistry;
|
|
4258
|
+
const server = options.server ?? serverFactory(undefined, { registry, type: "server" });
|
|
3894
4259
|
const partials = options.partials ?? createPartialRegistry(undefined, { registry, type: "partial" });
|
|
3895
4260
|
const routes = options.routes ?? createRouteRegistry(undefined, { registry, type: "route" });
|
|
3896
4261
|
const components = options.components ?? createComponentRegistry(undefined, { registry, type: "component" });
|
|
@@ -3918,6 +4283,7 @@
|
|
|
3918
4283
|
},
|
|
3919
4284
|
loader,
|
|
3920
4285
|
router,
|
|
4286
|
+
scheduler,
|
|
3921
4287
|
attributes,
|
|
3922
4288
|
|
|
3923
4289
|
start() {
|
|
@@ -3934,12 +4300,13 @@
|
|
|
3934
4300
|
handlers,
|
|
3935
4301
|
server,
|
|
3936
4302
|
cache: browserCache,
|
|
4303
|
+
scheduler,
|
|
3937
4304
|
attributes
|
|
3938
4305
|
});
|
|
3939
4306
|
runtime.loader = loader;
|
|
3940
4307
|
|
|
3941
4308
|
configureServerContext({ cache: browserCache });
|
|
3942
|
-
signals._setContext?.({ server, loader, cache: browserCache });
|
|
4309
|
+
signals._setContext?.({ server, loader, cache: browserCache, scheduler });
|
|
3943
4310
|
|
|
3944
4311
|
loader.start();
|
|
3945
4312
|
|
|
@@ -3955,6 +4322,7 @@
|
|
|
3955
4322
|
server,
|
|
3956
4323
|
cache: browserCache,
|
|
3957
4324
|
partials,
|
|
4325
|
+
scheduler,
|
|
3958
4326
|
fetch: options.fetch,
|
|
3959
4327
|
routeEndpoint: options.routeEndpoint,
|
|
3960
4328
|
attributes
|
|
@@ -3966,7 +4334,7 @@
|
|
|
3966
4334
|
}
|
|
3967
4335
|
} else {
|
|
3968
4336
|
configureServerContext({ cache: serverCache });
|
|
3969
|
-
signals._setContext?.({ server, cache: serverCache });
|
|
4337
|
+
signals._setContext?.({ server, cache: serverCache, scheduler });
|
|
3970
4338
|
}
|
|
3971
4339
|
|
|
3972
4340
|
return runtime;
|
|
@@ -3980,9 +4348,10 @@
|
|
|
3980
4348
|
async render(url) {
|
|
3981
4349
|
assertActive();
|
|
3982
4350
|
configureServerContext({ cache: serverCache });
|
|
3983
|
-
signals._setContext?.({ server, cache: serverCache });
|
|
4351
|
+
signals._setContext?.({ server, cache: serverCache, scheduler });
|
|
3984
4352
|
const matched = routes.match(url);
|
|
3985
4353
|
if (!matched) {
|
|
4354
|
+
await scheduler.flush();
|
|
3986
4355
|
return {
|
|
3987
4356
|
html: renderDocument("", { status: 404, signals, browserCache, boundary: options.boundary ?? "route", attributes }),
|
|
3988
4357
|
status: 404,
|
|
@@ -4002,8 +4371,8 @@
|
|
|
4002
4371
|
cache: serverCache,
|
|
4003
4372
|
browserCache,
|
|
4004
4373
|
partials,
|
|
4005
|
-
|
|
4006
|
-
|
|
4374
|
+
scheduler,
|
|
4375
|
+
...currentRequestContext()
|
|
4007
4376
|
})
|
|
4008
4377
|
: { html: "" };
|
|
4009
4378
|
|
|
@@ -4016,6 +4385,8 @@
|
|
|
4016
4385
|
browserCache.restore(result.cache.browser);
|
|
4017
4386
|
}
|
|
4018
4387
|
|
|
4388
|
+
await scheduler.flush();
|
|
4389
|
+
|
|
4019
4390
|
const status = result.status ?? 200;
|
|
4020
4391
|
return {
|
|
4021
4392
|
html: renderDocument(result.html, { status, signals, browserCache, boundary: result.boundary ?? options.boundary ?? "route", attributes }),
|
|
@@ -4034,6 +4405,9 @@
|
|
|
4034
4405
|
router?.destroy?.();
|
|
4035
4406
|
loader?.destroy?.();
|
|
4036
4407
|
signals.destroy?.();
|
|
4408
|
+
if (ownsScheduler) {
|
|
4409
|
+
scheduler.destroy();
|
|
4410
|
+
}
|
|
4037
4411
|
},
|
|
4038
4412
|
|
|
4039
4413
|
_applyUse(normalized) {
|
|
@@ -4054,11 +4428,23 @@
|
|
|
4054
4428
|
loader,
|
|
4055
4429
|
router,
|
|
4056
4430
|
cache,
|
|
4057
|
-
|
|
4058
|
-
|
|
4431
|
+
scheduler,
|
|
4432
|
+
requestContext: options.requestContext,
|
|
4433
|
+
...currentRequestContext()
|
|
4059
4434
|
});
|
|
4060
4435
|
}
|
|
4061
4436
|
|
|
4437
|
+
function currentRequestContext() {
|
|
4438
|
+
const context = readRequestContextLike(options.requestContext);
|
|
4439
|
+
return {
|
|
4440
|
+
requestContext: context,
|
|
4441
|
+
request: context.request ?? options.request,
|
|
4442
|
+
headers: context.headers ?? options.headers,
|
|
4443
|
+
cookies: context.cookies ?? options.cookies,
|
|
4444
|
+
locals: context.locals ?? options.locals
|
|
4445
|
+
};
|
|
4446
|
+
}
|
|
4447
|
+
|
|
4062
4448
|
function assertActive() {
|
|
4063
4449
|
if (destroyed) {
|
|
4064
4450
|
throw new Error("Async app runtime has been destroyed.");
|
|
@@ -4212,6 +4598,77 @@
|
|
|
4212
4598
|
}
|
|
4213
4599
|
}
|
|
4214
4600
|
|
|
4601
|
+
function createServerReferenceRegistry(initialMap = {}, options = {}) {
|
|
4602
|
+
const registry = options.registry ?? createRegistryStore();
|
|
4603
|
+
const type = options.type ?? "server";
|
|
4604
|
+
const defaults = {};
|
|
4605
|
+
|
|
4606
|
+
const reference = {
|
|
4607
|
+
registry,
|
|
4608
|
+
|
|
4609
|
+
register(id, value) {
|
|
4610
|
+
registry.register(type, id, value);
|
|
4611
|
+
return id;
|
|
4612
|
+
},
|
|
4613
|
+
|
|
4614
|
+
registerMany(map) {
|
|
4615
|
+
for (const [id, value] of Object.entries(map ?? {})) {
|
|
4616
|
+
reference.register(id, value);
|
|
4617
|
+
}
|
|
4618
|
+
return reference;
|
|
4619
|
+
},
|
|
4620
|
+
|
|
4621
|
+
unregister(id) {
|
|
4622
|
+
return registry.unregister(type, id);
|
|
4623
|
+
},
|
|
4624
|
+
|
|
4625
|
+
resolve() {
|
|
4626
|
+
return undefined;
|
|
4627
|
+
},
|
|
4628
|
+
|
|
4629
|
+
async run(id) {
|
|
4630
|
+
throw new Error(`Server command "${id}" cannot run without a server proxy or server registry.`);
|
|
4631
|
+
},
|
|
4632
|
+
|
|
4633
|
+
keys() {
|
|
4634
|
+
return registry.keys(type);
|
|
4635
|
+
},
|
|
4636
|
+
|
|
4637
|
+
entries() {
|
|
4638
|
+
return registry.entries(type);
|
|
4639
|
+
},
|
|
4640
|
+
|
|
4641
|
+
inspect() {
|
|
4642
|
+
return registry.entries(type);
|
|
4643
|
+
},
|
|
4644
|
+
|
|
4645
|
+
_setContext(context = {}) {
|
|
4646
|
+
Object.assign(defaults, context);
|
|
4647
|
+
return reference;
|
|
4648
|
+
},
|
|
4649
|
+
|
|
4650
|
+
_adoptMany() {
|
|
4651
|
+
return reference;
|
|
4652
|
+
}
|
|
4653
|
+
};
|
|
4654
|
+
|
|
4655
|
+
reference.registerMany(initialMap);
|
|
4656
|
+
return createServerNamespace((id, args, context) => reference.run(id, args, context), reference, () => defaults);
|
|
4657
|
+
}
|
|
4658
|
+
|
|
4659
|
+
function readRequestContextLike(store) {
|
|
4660
|
+
if (!store) {
|
|
4661
|
+
return {};
|
|
4662
|
+
}
|
|
4663
|
+
if (typeof store.get === "function") {
|
|
4664
|
+
return store.get() ?? {};
|
|
4665
|
+
}
|
|
4666
|
+
if (typeof store.getStore === "function") {
|
|
4667
|
+
return store.getStore() ?? {};
|
|
4668
|
+
}
|
|
4669
|
+
return {};
|
|
4670
|
+
}
|
|
4671
|
+
|
|
4215
4672
|
function normalizeEntries(type, entries = {}) {
|
|
4216
4673
|
if (type !== "signal") {
|
|
4217
4674
|
return { ...(entries ?? {}) };
|
|
@@ -4320,14 +4777,17 @@
|
|
|
4320
4777
|
const { createRouter: createRouter } = __routerModule;
|
|
4321
4778
|
const { defineRoute: defineRoute } = __routerModule;
|
|
4322
4779
|
const { route: route } = __routerModule;
|
|
4780
|
+
const { createScheduler: createScheduler } = __schedulerModule;
|
|
4781
|
+
const { applyServerResult: applyServerResult } = __serverModule;
|
|
4323
4782
|
const { createServerProxy: createServerProxy } = __serverModule;
|
|
4324
|
-
const {
|
|
4783
|
+
const { resolveServerCommandArguments: resolveServerCommandArguments } = __serverModule;
|
|
4784
|
+
const { unwrapServerResult: unwrapServerResult } = __serverModule;
|
|
4325
4785
|
const { computed: computed } = __signalsModule;
|
|
4326
4786
|
const { createSignal: createSignal } = __signalsModule;
|
|
4327
4787
|
const { createSignalRegistry: createSignalRegistry } = __signalsModule;
|
|
4328
4788
|
const { effect: effect } = __signalsModule;
|
|
4329
4789
|
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,
|
|
4790
|
+
const api = { asyncSignal, Async, createApp, defineApp, readSnapshot, attributeName, defineAttributeConfig, createCacheRegistry, defineCache, component, createComponentRegistry, defineComponent, delay, createHandlerRegistry, html, Loader, AsyncLoader, createPartialRegistry, createRegistryStore, createRouteRegistry, createRouter, defineRoute, route, createScheduler, applyServerResult, createServerProxy, resolveServerCommandArguments, unwrapServerResult, computed, createSignal, createSignalRegistry, effect, signal };
|
|
4331
4791
|
assertNoUmdNamespaceConflicts(api, Async);
|
|
4332
4792
|
Object.assign(Async, api);
|
|
4333
4793
|
Async.Async = Async;
|