@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.
@@ -1,6 +1,6 @@
1
1
  // Generated by scripts/build-framework-bundle.js. Do not edit by hand.
2
2
  // @ts-nocheck
3
- // Bundled TypeScript source entry for TS-aware runtimes and higher-layer tooling.
3
+ // Bundled browser TypeScript source entry for TS-aware runtimes and higher-layer tooling.
4
4
 
5
5
  const __asyncSignalModule = (() => {
6
6
  const asyncSignalKind = Symbol.for("@async/framework.asyncSignal");
@@ -95,7 +95,8 @@ const __asyncSignalModule = (() => {
95
95
  router: context.router,
96
96
  loader: context.loader,
97
97
  cache: context.cache,
98
- abort: activeAbort
98
+ abort: activeAbort,
99
+ scheduler: context.scheduler
99
100
  });
100
101
  }
101
102
  return server;
@@ -109,6 +110,9 @@ const __asyncSignalModule = (() => {
109
110
  get cache() {
110
111
  return registry._context?.().cache;
111
112
  },
113
+ get scheduler() {
114
+ return registry._context?.().scheduler;
115
+ },
112
116
  get version() {
113
117
  return runVersion;
114
118
  },
@@ -185,11 +189,20 @@ const __asyncSignalModule = (() => {
185
189
  _bindRegistry(nextRegistry, nextId) {
186
190
  registry = nextRegistry;
187
191
  registeredId = nextId;
188
- queueMicrotask(() => {
192
+ const start = () => {
189
193
  if (registry === nextRegistry && status === "idle") {
190
194
  state.refresh();
191
195
  }
192
- });
196
+ };
197
+ const scheduler = registry._context?.().scheduler;
198
+ if (scheduler) {
199
+ scheduler.enqueue("async", start, {
200
+ scope: registeredId,
201
+ key: `asyncSignal:${registeredId}:initial`
202
+ });
203
+ } else {
204
+ queueMicrotask(start);
205
+ }
193
206
  },
194
207
 
195
208
  _dispose() {
@@ -225,11 +238,26 @@ const __asyncSignalModule = (() => {
225
238
  for (const dependency of dependencies) {
226
239
  const dependencyId = String(dependency).split(".")[0];
227
240
  if (dependencyId && dependencyId !== registeredId) {
228
- dependencyCleanups.add(registry.subscribe(dependency, () => state.refresh()));
241
+ dependencyCleanups.add(registry.subscribe(dependency, () => scheduleRefresh()));
229
242
  }
230
243
  }
231
244
  }
232
245
 
246
+ function scheduleRefresh() {
247
+ if (activeAbort && !activeAbort.aborted) {
248
+ activeAbort.cancel(new Error(`Async signal "${registeredId}" dependency changed.`));
249
+ }
250
+ const scheduler = registry?._context?.().scheduler;
251
+ if (!scheduler) {
252
+ state.refresh();
253
+ return;
254
+ }
255
+ scheduler.enqueue("async", () => state.refresh(), {
256
+ scope: registeredId,
257
+ key: `asyncSignal:${registeredId}:refresh`
258
+ });
259
+ }
260
+
233
261
  function notify() {
234
262
  for (const subscriber of [...subscribers]) {
235
263
  subscriber(state);
@@ -866,7 +894,8 @@ const __signalsModule = (() => {
866
894
  server: registry._context?.().server,
867
895
  router: registry._context?.().router,
868
896
  loader: registry._context?.().loader,
869
- cache: registry._context?.().cache
897
+ cache: registry._context?.().cache,
898
+ scheduler: registry._context?.().scheduler
870
899
  }));
871
900
  });
872
901
  }
@@ -894,6 +923,8 @@ const __signalsModule = (() => {
894
923
  const registryCleanups = new Map();
895
924
  const runtimeContext = {};
896
925
  const boundEntries = new Set();
926
+ let subscriptionCounter = 0;
927
+ let effectCounter = 0;
897
928
 
898
929
  const registry = attachRegistryInspection({
899
930
  register(id, signalLike) {
@@ -969,17 +1000,21 @@ const __signalsModule = (() => {
969
1000
  return createRef(registry, id);
970
1001
  },
971
1002
 
972
- subscribe(path, fn) {
1003
+ subscribe(path, fn, options = {}) {
973
1004
  if (typeof fn !== "function") {
974
1005
  throw new TypeError("subscribe(path, fn) requires a function.");
975
1006
  }
976
1007
  const parsed = parsePath(path, entries);
977
1008
  const entry = requireEntry(entries, parsed.id);
1009
+ const subscriptionId = ++subscriptionCounter;
978
1010
  return entry.subscribe(() => {
979
- fn(registry.get(parsed.path), {
1011
+ scheduleCallback(() => fn(registry.get(parsed.path), {
980
1012
  id: parsed.id,
981
1013
  path: parsed.path,
982
1014
  signal: entry
1015
+ }), {
1016
+ ...options,
1017
+ key: options.key ?? `signal:${parsed.path}:${subscriptionId}`
983
1018
  });
984
1019
  });
985
1020
  },
@@ -997,10 +1032,12 @@ const __signalsModule = (() => {
997
1032
  return registry.ref(id);
998
1033
  },
999
1034
 
1000
- effect(fn) {
1035
+ effect(fn, options = {}) {
1001
1036
  let cleanup;
1002
1037
  let dependencyCleanups = [];
1003
1038
  let stopped = false;
1039
+ const scheduler = options.scheduler;
1040
+ const effectId = ++effectCounter;
1004
1041
 
1005
1042
  const run = () => {
1006
1043
  if (stopped) {
@@ -1019,10 +1056,22 @@ const __signalsModule = (() => {
1019
1056
  server: runtimeContext.server,
1020
1057
  router: runtimeContext.router,
1021
1058
  loader: runtimeContext.loader,
1022
- cache: runtimeContext.cache
1059
+ cache: runtimeContext.cache,
1060
+ scheduler: runtimeContext.scheduler
1023
1061
  }));
1024
1062
  cleanup = outcome.value;
1025
- dependencyCleanups = outcome.dependencies.map((dependency) => registry.subscribe(dependency, run));
1063
+ dependencyCleanups = outcome.dependencies.map((dependency) => registry.subscribe(dependency, scheduleRun));
1064
+ };
1065
+
1066
+ const scheduleRun = () => {
1067
+ if (!scheduler) {
1068
+ run();
1069
+ return;
1070
+ }
1071
+ scheduler.enqueue(options.phase ?? "effect", run, {
1072
+ scope: options.scope,
1073
+ key: options.key ?? `effect:${effectId}`
1074
+ });
1026
1075
  };
1027
1076
 
1028
1077
  run();
@@ -1099,6 +1148,17 @@ const __signalsModule = (() => {
1099
1148
  registryCleanups.set(id, cleanup);
1100
1149
  }
1101
1150
  }
1151
+
1152
+ function scheduleCallback(fn, options = {}) {
1153
+ const scheduler = options.scheduler;
1154
+ if (!scheduler || options.phase === "sync") {
1155
+ return fn();
1156
+ }
1157
+ return scheduler.enqueue(options.phase ?? "effect", fn, {
1158
+ scope: options.scope,
1159
+ key: options.key
1160
+ });
1161
+ }
1102
1162
  }
1103
1163
 
1104
1164
  function normalizeSignal(signalLike) {
@@ -1565,10 +1625,15 @@ const __componentModule = (() => {
1565
1625
  html,
1566
1626
  attach(target) {
1567
1627
  for (const hook of attachHooks) {
1568
- const cleanup = hook(target);
1569
- if (typeof cleanup === "function") {
1570
- cleanups.push(cleanup);
1571
- }
1628
+ runtime.scheduler?.enqueue("lifecycle", () => {
1629
+ const cleanup = hook(target);
1630
+ if (typeof cleanup === "function") {
1631
+ cleanups.push(cleanup);
1632
+ }
1633
+ }, {
1634
+ scope,
1635
+ key: `attach:${attachHooks.indexOf(hook)}`
1636
+ }) ?? runAttachHook(hook, target);
1572
1637
  }
1573
1638
  },
1574
1639
  mount(target) {
@@ -1576,7 +1641,17 @@ const __componentModule = (() => {
1576
1641
  },
1577
1642
  visible(target, observeVisible) {
1578
1643
  for (const hook of visibleHooks) {
1579
- const cleanup = observeVisible(target, hook);
1644
+ const cleanup = observeVisible(target, () => {
1645
+ runtime.scheduler?.enqueue("lifecycle", () => {
1646
+ const hookCleanup = hook(target);
1647
+ if (typeof hookCleanup === "function") {
1648
+ cleanups.push(hookCleanup);
1649
+ }
1650
+ }, {
1651
+ scope,
1652
+ key: `visible:${visibleHooks.indexOf(hook)}`
1653
+ }) ?? runVisibleHook(hook, target);
1654
+ });
1580
1655
  if (typeof cleanup === "function") {
1581
1656
  cleanups.push(cleanup);
1582
1657
  }
@@ -1586,6 +1661,7 @@ const __componentModule = (() => {
1586
1661
  while (destroyHooks.length > 0) {
1587
1662
  destroyHooks.pop()?.();
1588
1663
  }
1664
+ runtime.scheduler?.markScopeDestroyed(scope);
1589
1665
  while (cleanups.length > 0) {
1590
1666
  cleanups.pop()?.();
1591
1667
  }
@@ -1594,10 +1670,24 @@ const __componentModule = (() => {
1594
1670
  }
1595
1671
  }
1596
1672
  };
1673
+
1674
+ function runAttachHook(hook, target) {
1675
+ const cleanup = hook(target);
1676
+ if (typeof cleanup === "function") {
1677
+ cleanups.push(cleanup);
1678
+ }
1679
+ }
1680
+
1681
+ function runVisibleHook(hook, target) {
1682
+ const cleanup = hook(target);
1683
+ if (typeof cleanup === "function") {
1684
+ cleanups.push(cleanup);
1685
+ }
1686
+ }
1597
1687
  }
1598
1688
 
1599
1689
  function createComponentContext({ runtime, scope, cleanups, attachHooks, visibleHooks, destroyHooks, renderScopedTemplate }) {
1600
- const { signals, handlers, loader, server, router, cache } = runtime;
1690
+ const { signals, handlers, loader, server, router, cache, scheduler } = runtime;
1601
1691
  const generatedHandlers = new WeakMap();
1602
1692
  let generatedHandlerCounter = 0;
1603
1693
  let generatedSignalCounter = 0;
@@ -1609,6 +1699,7 @@ const __componentModule = (() => {
1609
1699
  server,
1610
1700
  router,
1611
1701
  cache,
1702
+ scheduler,
1612
1703
 
1613
1704
  signal(name, initial) {
1614
1705
  if (arguments.length === 1) {
@@ -1653,7 +1744,11 @@ const __componentModule = (() => {
1653
1744
  },
1654
1745
 
1655
1746
  effect(fn) {
1656
- const cleanup = signals.effect(() => fn.call(context));
1747
+ const cleanup = signals.effect(() => fn.call(context), {
1748
+ scheduler,
1749
+ phase: "effect",
1750
+ scope
1751
+ });
1657
1752
  cleanups.push(cleanup);
1658
1753
  return cleanup;
1659
1754
  },
@@ -1775,93 +1870,10 @@ const __componentModule = (() => {
1775
1870
  })();
1776
1871
 
1777
1872
  const __serverModule = (() => {
1778
- const { attachRegistryInspection, createRegistryStore } = __registryStoreModule;
1779
1873
  const serverEnvelopeKeys = new Set(["value", "signals", "boundary", "html", "redirect", "error"]);
1780
1874
  const appliedServerResult = Symbol.for("@async/framework.appliedServerResult");
1781
1875
  const appliedServerValues = new WeakSet();
1782
1876
 
1783
- function createServerRegistry(initialMap = {}, options = {}) {
1784
- const registryStore = options.registry ?? createRegistryStore();
1785
- const type = options.type ?? "server";
1786
- const entries = registryStore._map(type);
1787
- const defaults = {};
1788
-
1789
- const registry = attachRegistryInspection({
1790
- register(id, fn) {
1791
- assertServerId(id);
1792
- if (typeof fn !== "function") {
1793
- throw new TypeError(`Server function "${id}" must be a function.`);
1794
- }
1795
- if (entries.has(id)) {
1796
- throw new Error(`Server function "${id}" is already registered.`);
1797
- }
1798
- entries.set(id, fn);
1799
- return id;
1800
- },
1801
-
1802
- registerMany(map) {
1803
- for (const [id, fn] of Object.entries(map ?? {})) {
1804
- registry.register(id, fn);
1805
- }
1806
- return registry;
1807
- },
1808
-
1809
- unregister(id) {
1810
- assertServerId(id);
1811
- return entries.delete(id);
1812
- },
1813
-
1814
- resolve(id) {
1815
- assertServerId(id);
1816
- return entries.get(id);
1817
- },
1818
-
1819
- async run(id, args = [], context = {}) {
1820
- assertServerId(id);
1821
- const fn = registry.resolve(id);
1822
- if (!fn) {
1823
- throw new Error(`Server function "${id}" is not registered.`);
1824
- }
1825
-
1826
- let runContext;
1827
- const server = createServerNamespace((childId, childArgs, childContext = {}) => {
1828
- return registry.run(childId, childArgs, { ...runContext, ...childContext });
1829
- }, {}, () => runContext);
1830
-
1831
- const mergedContext = {
1832
- ...defaults,
1833
- ...context,
1834
- cache: defaults.cache ?? context.cache
1835
- };
1836
-
1837
- runContext = {
1838
- ...mergedContext,
1839
- id,
1840
- args,
1841
- input: mergedContext.input,
1842
- signals: createSignalReader(mergedContext.signals),
1843
- abort: mergedContext.abort,
1844
- cache: mergedContext.cache,
1845
- server
1846
- };
1847
-
1848
- return fn.call(runContext, ...args);
1849
- },
1850
-
1851
- _setContext(context = {}) {
1852
- Object.assign(defaults, context);
1853
- return registry;
1854
- },
1855
-
1856
- _adoptMany() {
1857
- return registry;
1858
- }
1859
- }, registryStore, type);
1860
-
1861
- registry.registerMany(initialMap);
1862
- return createServerNamespace((id, args, context) => registry.run(id, args, context), registry, () => defaults);
1863
- }
1864
-
1865
1877
  function createServerProxy({
1866
1878
  endpoint = "/__async/server",
1867
1879
  fetch: fetchImpl = globalThis.fetch?.bind(globalThis),
@@ -1869,13 +1881,14 @@ const __serverModule = (() => {
1869
1881
  loader,
1870
1882
  router,
1871
1883
  cache,
1884
+ scheduler,
1872
1885
  headers = {}
1873
1886
  } = {}) {
1874
1887
  if (typeof fetchImpl !== "function") {
1875
1888
  throw new TypeError("createServerProxy(...) requires fetch to be available.");
1876
1889
  }
1877
1890
 
1878
- const defaults = { signals, loader, router, cache };
1891
+ const defaults = { signals, loader, router, cache, scheduler };
1879
1892
 
1880
1893
  async function run(id, args = [], context = {}) {
1881
1894
  assertServerId(id);
@@ -2214,7 +2227,7 @@ const __serverModule = (() => {
2214
2227
  throw new TypeError("Server function id must be a non-empty string.");
2215
2228
  }
2216
2229
  }
2217
- return { createServerRegistry, createServerProxy, resolveServerCommandArguments, applyServerResult, unwrapServerResult, defaultInput };
2230
+ return { createServerProxy, resolveServerCommandArguments, applyServerResult, unwrapServerResult, defaultInput, createServerNamespace, createSignalReader, assertServerId };
2218
2231
  })();
2219
2232
 
2220
2233
  const __handlersModule = (() => {
@@ -2417,18 +2430,325 @@ const __handlersModule = (() => {
2417
2430
  return { createHandlerRegistry, parseHandlerRef, isHandlerToken };
2418
2431
  })();
2419
2432
 
2433
+ const __schedulerModule = (() => {
2434
+ const defaultPhases = ["binding", "lifecycle", "effect", "async", "post"];
2435
+
2436
+ function createScheduler(options = {}) {
2437
+ const phases = [...(options.phases ?? defaultPhases)];
2438
+ const queues = new Map(phases.map((phase) => [phase, []]));
2439
+ const keyedJobs = new Map();
2440
+ const destroyedScopes = new Set();
2441
+ const objectScopeIds = new WeakMap();
2442
+ const onError = typeof options.onError === "function" ? options.onError : undefined;
2443
+ const maxDepth = options.maxDepth ?? 100;
2444
+ const strategy = options.strategy ?? "microtask";
2445
+ let destroyed = false;
2446
+ let flushing = false;
2447
+ let scheduled = false;
2448
+ let batchDepth = 0;
2449
+ let jobCounter = 0;
2450
+ let scopeCounter = 0;
2451
+
2452
+ const api = {
2453
+ strategy,
2454
+ phases,
2455
+
2456
+ batch(fn) {
2457
+ if (typeof fn !== "function") {
2458
+ throw new TypeError("scheduler.batch(fn) requires a function.");
2459
+ }
2460
+ assertActive();
2461
+ batchDepth += 1;
2462
+ let asyncBatch = false;
2463
+ try {
2464
+ const value = fn();
2465
+ if (value && typeof value.then === "function") {
2466
+ asyncBatch = true;
2467
+ return value.finally(() => {
2468
+ batchDepth -= 1;
2469
+ requestFlush();
2470
+ });
2471
+ }
2472
+ return value;
2473
+ } finally {
2474
+ if (!asyncBatch && batchDepth > 0) {
2475
+ batchDepth -= 1;
2476
+ requestFlush();
2477
+ }
2478
+ }
2479
+ },
2480
+
2481
+ enqueue(phase, fn, options = {}) {
2482
+ assertActive();
2483
+ assertPhase(phase);
2484
+ if (typeof fn !== "function") {
2485
+ throw new TypeError("scheduler.enqueue(phase, fn) requires a function.");
2486
+ }
2487
+ const scope = options.scope;
2488
+ if (scope !== undefined && destroyedScopes.has(scope)) {
2489
+ return noop;
2490
+ }
2491
+
2492
+ const dedupeKey = options.key === undefined ? undefined : `${phase}:${scopeKey(scope)}:${String(options.key)}`;
2493
+ if (dedupeKey && keyedJobs.has(dedupeKey)) {
2494
+ return keyedJobs.get(dedupeKey).cancel;
2495
+ }
2496
+
2497
+ const job = {
2498
+ id: ++jobCounter,
2499
+ phase,
2500
+ fn,
2501
+ scope,
2502
+ boundary: options.boundary,
2503
+ key: dedupeKey,
2504
+ canceled: false,
2505
+ cancel() {
2506
+ job.canceled = true;
2507
+ if (job.key) {
2508
+ keyedJobs.delete(job.key);
2509
+ }
2510
+ }
2511
+ };
2512
+ queues.get(phase).push(job);
2513
+ if (job.key) {
2514
+ keyedJobs.set(job.key, job);
2515
+ }
2516
+ requestFlush();
2517
+ return job.cancel;
2518
+ },
2519
+
2520
+ afterFlush(fn, options = {}) {
2521
+ return api.enqueue("post", fn, options);
2522
+ },
2523
+
2524
+ async flush() {
2525
+ assertActive();
2526
+ if (flushing) {
2527
+ return;
2528
+ }
2529
+ scheduled = false;
2530
+ flushing = true;
2531
+ let depth = 0;
2532
+ try {
2533
+ while (hasJobs()) {
2534
+ depth += 1;
2535
+ if (depth > maxDepth) {
2536
+ throw new Error(`Scheduler exceeded maxDepth ${maxDepth}.`);
2537
+ }
2538
+ for (const phase of phases) {
2539
+ await flushPhase(phase);
2540
+ }
2541
+ }
2542
+ } finally {
2543
+ flushing = false;
2544
+ if (hasJobs()) {
2545
+ requestFlush();
2546
+ }
2547
+ }
2548
+ },
2549
+
2550
+ async flushScope(scope) {
2551
+ assertActive();
2552
+ if (flushing) {
2553
+ return;
2554
+ }
2555
+ scheduled = false;
2556
+ flushing = true;
2557
+ let depth = 0;
2558
+ try {
2559
+ while (hasJobsForScope(scope)) {
2560
+ depth += 1;
2561
+ if (depth > maxDepth) {
2562
+ throw new Error(`Scheduler exceeded maxDepth ${maxDepth}.`);
2563
+ }
2564
+ for (const phase of phases) {
2565
+ await flushPhase(phase, scope);
2566
+ }
2567
+ }
2568
+ } finally {
2569
+ flushing = false;
2570
+ if (hasJobs()) {
2571
+ requestFlush();
2572
+ }
2573
+ }
2574
+ },
2575
+
2576
+ cancelScope(scope) {
2577
+ if (scope === undefined) {
2578
+ return api;
2579
+ }
2580
+ for (const queue of queues.values()) {
2581
+ for (const job of queue) {
2582
+ if (job.scope === scope) {
2583
+ job.cancel();
2584
+ }
2585
+ }
2586
+ }
2587
+ return api;
2588
+ },
2589
+
2590
+ markScopeDestroyed(scope) {
2591
+ if (scope !== undefined) {
2592
+ destroyedScopes.add(scope);
2593
+ api.cancelScope(scope);
2594
+ }
2595
+ return api;
2596
+ },
2597
+
2598
+ isScopeDestroyed(scope) {
2599
+ return scope !== undefined && destroyedScopes.has(scope);
2600
+ },
2601
+
2602
+ inspect() {
2603
+ const counts = {};
2604
+ for (const [phase, queue] of queues) {
2605
+ counts[phase] = queue.filter((job) => !job.canceled).length;
2606
+ }
2607
+ return {
2608
+ strategy,
2609
+ phases: [...phases],
2610
+ pending: counts,
2611
+ scopesDestroyed: destroyedScopes.size,
2612
+ flushing,
2613
+ scheduled
2614
+ };
2615
+ },
2616
+
2617
+ destroy() {
2618
+ destroyed = true;
2619
+ for (const queue of queues.values()) {
2620
+ for (const job of queue) {
2621
+ job.cancel();
2622
+ }
2623
+ queue.length = 0;
2624
+ }
2625
+ keyedJobs.clear();
2626
+ destroyedScopes.clear();
2627
+ }
2628
+ };
2629
+
2630
+ return api;
2631
+
2632
+ function requestFlush() {
2633
+ if (strategy === "manual" || destroyed || flushing || batchDepth > 0 || scheduled) {
2634
+ return;
2635
+ }
2636
+ scheduled = true;
2637
+ scheduleMicrotask(() => {
2638
+ if (!destroyed) {
2639
+ void api.flush();
2640
+ }
2641
+ });
2642
+ }
2643
+
2644
+ async function flushPhase(phase, scope) {
2645
+ const queue = queues.get(phase);
2646
+ const remaining = [];
2647
+ const runnable = [];
2648
+
2649
+ for (const job of queue.splice(0)) {
2650
+ if (job.canceled) {
2651
+ continue;
2652
+ }
2653
+ if (scope !== undefined && job.scope !== scope) {
2654
+ remaining.push(job);
2655
+ continue;
2656
+ }
2657
+ runnable.push(job);
2658
+ }
2659
+
2660
+ queue.push(...remaining);
2661
+
2662
+ for (const job of runnable) {
2663
+ if (job.key) {
2664
+ keyedJobs.delete(job.key);
2665
+ }
2666
+ if (job.canceled || (job.scope !== undefined && destroyedScopes.has(job.scope))) {
2667
+ continue;
2668
+ }
2669
+ try {
2670
+ await job.fn();
2671
+ } catch (error) {
2672
+ if (onError) {
2673
+ onError(error, job);
2674
+ } else {
2675
+ throw error;
2676
+ }
2677
+ }
2678
+ }
2679
+ }
2680
+
2681
+ function hasJobs() {
2682
+ for (const queue of queues.values()) {
2683
+ if (queue.some((job) => !job.canceled)) {
2684
+ return true;
2685
+ }
2686
+ }
2687
+ return false;
2688
+ }
2689
+
2690
+ function hasJobsForScope(scope) {
2691
+ for (const queue of queues.values()) {
2692
+ if (queue.some((job) => !job.canceled && job.scope === scope)) {
2693
+ return true;
2694
+ }
2695
+ }
2696
+ return false;
2697
+ }
2698
+
2699
+ function assertActive() {
2700
+ if (destroyed) {
2701
+ throw new Error("Scheduler has been destroyed.");
2702
+ }
2703
+ }
2704
+
2705
+ function assertPhase(phase) {
2706
+ if (!queues.has(phase)) {
2707
+ throw new Error(`Unknown scheduler phase "${phase}".`);
2708
+ }
2709
+ }
2710
+
2711
+ function scopeKey(scope) {
2712
+ if (scope === undefined) {
2713
+ return "global";
2714
+ }
2715
+ if ((typeof scope === "object" && scope !== null) || typeof scope === "function") {
2716
+ if (!objectScopeIds.has(scope)) {
2717
+ objectScopeIds.set(scope, `scope:${++scopeCounter}`);
2718
+ }
2719
+ return objectScopeIds.get(scope);
2720
+ }
2721
+ return String(scope);
2722
+ }
2723
+ }
2724
+
2725
+ function scheduleMicrotask(fn) {
2726
+ if (typeof queueMicrotask === "function") {
2727
+ queueMicrotask(fn);
2728
+ return;
2729
+ }
2730
+ Promise.resolve().then(fn);
2731
+ }
2732
+
2733
+ function noop() {}
2734
+ return { createScheduler };
2735
+ })();
2736
+
2420
2737
  const __loaderModule = (() => {
2421
2738
  const { renderComponent } = __componentModule;
2422
2739
  const { createHandlerRegistry } = __handlersModule;
2740
+ const { createScheduler } = __schedulerModule;
2423
2741
  const { createSignalRegistry, isSignalRef } = __signalsModule;
2424
2742
  const { matchAttribute, normalizeAttributeConfig, readAttribute } = __attributesModule;
2425
2743
  const inlineBindingPrefix = "__async:inline:";
2426
2744
 
2427
- function Loader({ root, signals, handlers, server, router, cache, attributes } = {}) {
2745
+ function Loader({ root, signals, handlers, server, router, cache, attributes, scheduler } = {}) {
2428
2746
  const documentRef = root?.ownerDocument ?? root ?? globalThis.document;
2429
2747
  const rootNode = root ?? documentRef;
2430
2748
  const signalRegistry = signals ?? createSignalRegistry();
2431
2749
  const handlerRegistry = handlers ?? createHandlerRegistry();
2750
+ const schedulerInstance = scheduler ?? createScheduler();
2751
+ const ownsScheduler = !scheduler;
2432
2752
  const attributeConfig = normalizeAttributeConfig(attributes);
2433
2753
  const cleanups = new Set();
2434
2754
  const eventBindings = new WeakMap();
@@ -2449,6 +2769,7 @@ const __loaderModule = (() => {
2449
2769
  server,
2450
2770
  router,
2451
2771
  cache,
2772
+ scheduler: schedulerInstance,
2452
2773
  attributes: attributeConfig,
2453
2774
 
2454
2775
  start() {
@@ -2488,6 +2809,7 @@ const __loaderModule = (() => {
2488
2809
  server: api.server,
2489
2810
  router: api.router,
2490
2811
  cache: api.cache,
2812
+ scheduler: schedulerInstance,
2491
2813
  attributes: attributeConfig
2492
2814
  });
2493
2815
  cleanupChildren(target);
@@ -2508,6 +2830,9 @@ const __loaderModule = (() => {
2508
2830
  runCleanup(cleanup);
2509
2831
  }
2510
2832
  cleanups.clear();
2833
+ if (ownsScheduler) {
2834
+ schedulerInstance.destroy();
2835
+ }
2511
2836
  },
2512
2837
 
2513
2838
  _observeVisible(target, fn) {
@@ -2525,13 +2850,14 @@ const __loaderModule = (() => {
2525
2850
  }
2526
2851
  };
2527
2852
 
2528
- signalRegistry._setContext?.({ server: api.server, router: api.router, loader: api, cache: api.cache });
2853
+ signalRegistry._setContext?.({ server: api.server, router: api.router, loader: api, cache: api.cache, scheduler: schedulerInstance });
2529
2854
  api.server?._setContext?.({
2530
2855
  signals: signalRegistry,
2531
2856
  handlers: handlerRegistry,
2532
2857
  loader: api,
2533
2858
  router: api.router,
2534
- cache: api.cache
2859
+ cache: api.cache,
2860
+ scheduler: schedulerInstance
2535
2861
  });
2536
2862
 
2537
2863
  function bindEventAttributes(scope) {
@@ -2563,18 +2889,19 @@ const __loaderModule = (() => {
2563
2889
 
2564
2890
  const listener = async (event) => {
2565
2891
  try {
2566
- await handlerRegistry.run(ref, {
2892
+ await schedulerInstance.batch(() => handlerRegistry.run(ref, {
2567
2893
  signals: signalRegistry,
2568
2894
  handlers: handlerRegistry,
2569
2895
  loader: api,
2570
2896
  server: api.server,
2571
2897
  router: api.router,
2572
2898
  cache: api.cache,
2899
+ scheduler: schedulerInstance,
2573
2900
  event,
2574
2901
  element,
2575
2902
  el: element,
2576
2903
  root: rootNode
2577
- });
2904
+ }));
2578
2905
  } catch (error) {
2579
2906
  dispatchAsyncError(element, error);
2580
2907
  }
@@ -2690,7 +3017,12 @@ const __loaderModule = (() => {
2690
3017
 
2691
3018
  const read = () => readBinding(path, options);
2692
3019
  apply(read());
2693
- addCleanup(subscribeBinding(path, () => apply(read())), element);
3020
+ addCleanup(subscribeBinding(path, () => {
3021
+ schedulerInstance.enqueue("binding", () => apply(read()), {
3022
+ scope: element,
3023
+ key
3024
+ });
3025
+ }), element);
2694
3026
  }
2695
3027
 
2696
3028
  function bindValueWriter(element, path) {
@@ -2751,7 +3083,12 @@ const __loaderModule = (() => {
2751
3083
  const state = {
2752
3084
  id,
2753
3085
  templates,
2754
- cleanup: signalRegistry.subscribe(`${id}.$status`, () => renderBoundary(boundary))
3086
+ cleanup: signalRegistry.subscribe(`${id}.$status`, () => {
3087
+ schedulerInstance.enqueue("binding", () => renderBoundary(boundary), {
3088
+ scope: boundary,
3089
+ key: `boundary:${id}`
3090
+ });
3091
+ })
2755
3092
  };
2756
3093
  boundaryState.set(boundary, state);
2757
3094
  addCleanup(state.cleanup, boundary);
@@ -2791,7 +3128,7 @@ const __loaderModule = (() => {
2791
3128
  }
2792
3129
  mountedElements.add(element);
2793
3130
  for (const ref of refs) {
2794
- runPseudo(element, ref);
3131
+ scheduleLifecycle(element, () => runPseudo(element, ref), `attach:${ref}`);
2795
3132
  }
2796
3133
  }
2797
3134
 
@@ -2804,7 +3141,7 @@ const __loaderModule = (() => {
2804
3141
  continue;
2805
3142
  }
2806
3143
  visibleElements.add(element);
2807
- addCleanup(observeVisible(element, () => runPseudo(element, ref)), element);
3144
+ addCleanup(observeVisible(element, () => scheduleLifecycle(element, () => runPseudo(element, ref), `visible:${ref}`)), element);
2808
3145
  }
2809
3146
  }
2810
3147
 
@@ -2828,6 +3165,7 @@ const __loaderModule = (() => {
2828
3165
  server: api.server,
2829
3166
  router: api.router,
2830
3167
  cache: api.cache,
3168
+ scheduler: schedulerInstance,
2831
3169
  element,
2832
3170
  el: element,
2833
3171
  root: rootNode
@@ -2846,10 +3184,13 @@ const __loaderModule = (() => {
2846
3184
  const ownerWindow = target.ownerDocument?.defaultView ?? globalThis;
2847
3185
  const Observer = ownerWindow.IntersectionObserver ?? globalThis.IntersectionObserver;
2848
3186
  if (!Observer) {
2849
- queueMicrotask(() => {
3187
+ schedulerInstance.enqueue("lifecycle", () => {
2850
3188
  if (!destroyed) {
2851
3189
  fn(target);
2852
3190
  }
3191
+ }, {
3192
+ scope: target,
3193
+ key: "visible:fallback"
2853
3194
  });
2854
3195
  return () => {};
2855
3196
  }
@@ -2904,6 +3245,7 @@ const __loaderModule = (() => {
2904
3245
  }
2905
3246
  for (const element of elementsIn(node)) {
2906
3247
  runScopedCleanups(element);
3248
+ schedulerInstance.markScopeDestroyed(element);
2907
3249
  }
2908
3250
  }
2909
3251
 
@@ -2927,6 +3269,13 @@ const __loaderModule = (() => {
2927
3269
  }
2928
3270
  }
2929
3271
 
3272
+ function scheduleLifecycle(element, fn, key) {
3273
+ schedulerInstance.enqueue("lifecycle", fn, {
3274
+ scope: element,
3275
+ key
3276
+ });
3277
+ }
3278
+
2930
3279
  return api;
2931
3280
  }
2932
3281
 
@@ -3257,6 +3606,7 @@ const __partialsModule = (() => {
3257
3606
  const __routerModule = (() => {
3258
3607
  const { Loader } = __loaderModule;
3259
3608
  const { createHandlerRegistry } = __handlersModule;
3609
+ const { createScheduler } = __schedulerModule;
3260
3610
  const { createSignalRegistry } = __signalsModule;
3261
3611
  const { applyServerResult } = __serverModule;
3262
3612
  const { createRegistryStore } = __registryStoreModule;
@@ -3377,12 +3727,15 @@ const __routerModule = (() => {
3377
3727
  partials,
3378
3728
  fetch: fetchImpl = globalThis.fetch?.bind(globalThis),
3379
3729
  routeEndpoint = "/__async/route",
3380
- attributes
3730
+ attributes,
3731
+ scheduler
3381
3732
  } = {}) {
3382
3733
  const documentRef = root?.ownerDocument ?? root ?? globalThis.document;
3383
3734
  const rootNode = root ?? documentRef;
3384
3735
  const signalRegistry = signals ?? loader?.signals ?? createSignalRegistry();
3385
3736
  const handlerRegistry = handlers ?? loader?.handlers ?? createHandlerRegistry();
3737
+ const schedulerInstance = scheduler ?? loader?.scheduler ?? createScheduler();
3738
+ const ownsScheduler = !scheduler && !loader?.scheduler;
3386
3739
  const attributeConfig = normalizeAttributeConfig(attributes ?? loader?.attributes);
3387
3740
  const loaderInstance =
3388
3741
  loader ??
@@ -3392,6 +3745,7 @@ const __routerModule = (() => {
3392
3745
  handlers: handlerRegistry,
3393
3746
  server,
3394
3747
  cache,
3748
+ scheduler: schedulerInstance,
3395
3749
  attributes: attributeConfig
3396
3750
  });
3397
3751
  const ownsLoader = !loader;
@@ -3411,12 +3765,13 @@ const __routerModule = (() => {
3411
3765
  server,
3412
3766
  cache,
3413
3767
  partials,
3768
+ scheduler: schedulerInstance,
3414
3769
  attributes: attributeConfig,
3415
3770
 
3416
3771
  start() {
3417
3772
  assertActive();
3418
3773
  loaderInstance.router = api;
3419
- signalRegistry._setContext?.({ router: api, loader: loaderInstance, server, cache });
3774
+ signalRegistry._setContext?.({ router: api, loader: loaderInstance, server, cache, scheduler: schedulerInstance });
3420
3775
  if (ownsLoader) {
3421
3776
  loaderInstance.start();
3422
3777
  }
@@ -3480,6 +3835,9 @@ const __routerModule = (() => {
3480
3835
  cleanup();
3481
3836
  }
3482
3837
  cleanups.clear();
3838
+ if (ownsScheduler) {
3839
+ schedulerInstance.destroy();
3840
+ }
3483
3841
  }
3484
3842
  };
3485
3843
 
@@ -3585,13 +3943,16 @@ const __routerModule = (() => {
3585
3943
  loader: loaderInstance,
3586
3944
  router: api,
3587
3945
  cache,
3946
+ scheduler: schedulerInstance,
3588
3947
  abort: navigation?.abort
3589
3948
  });
3949
+ await schedulerInstance.flush();
3590
3950
  if (!isActiveNavigation(navigation)) {
3591
3951
  return;
3592
3952
  }
3593
3953
  if (result?.html != null && !result.boundary && !result.redirect) {
3594
3954
  loaderInstance.swap(boundary, result.html);
3955
+ await schedulerInstance.flush();
3595
3956
  }
3596
3957
  if (result?.redirect || options.history === false) {
3597
3958
  return;
@@ -3636,6 +3997,7 @@ const __routerModule = (() => {
3636
3997
  loader: loaderInstance,
3637
3998
  server,
3638
3999
  cache,
4000
+ scheduler: schedulerInstance,
3639
4001
  abort: navigation?.abort
3640
4002
  };
3641
4003
  }
@@ -3822,15 +4184,17 @@ const __appModule = (() => {
3822
4184
  const { Loader } = __loaderModule;
3823
4185
  const { createPartialRegistry } = __partialsModule;
3824
4186
  const { createRouteRegistry, createRouter } = __routerModule;
3825
- const { createServerRegistry } = __serverModule;
4187
+ const { createScheduler } = __schedulerModule;
4188
+ const { createServerNamespace } = __serverModule;
3826
4189
  const { createSignal, createSignalRegistry } = __signalsModule;
3827
4190
  const { createRegistryStore } = __registryStoreModule;
3828
4191
  const { attributeName, normalizeAttributeConfig } = __attributesModule;
3829
4192
  const registryTypes = new Set(["signal", "handler", "server", "partial", "route", "component"]);
3830
4193
 
3831
- function defineApp(initial) {
4194
+ function defineApp(initial, options = {}) {
3832
4195
  const registry = createRegistryStore(undefined, { target: "browser" });
3833
4196
  const runtimes = new Set();
4197
+ const createRuntime = options.createRuntime ?? createApp;
3834
4198
 
3835
4199
  const app = {
3836
4200
  registry,
@@ -3849,7 +4213,7 @@ const __appModule = (() => {
3849
4213
  },
3850
4214
 
3851
4215
  start(options = {}) {
3852
- const runtime = createApp(app, options).start();
4216
+ const runtime = createRuntime(app, options).start();
3853
4217
  app.runtime = runtime;
3854
4218
  return runtime;
3855
4219
  },
@@ -3874,13 +4238,18 @@ const __appModule = (() => {
3874
4238
  function createApp(appOrDefinition = Async, options = {}) {
3875
4239
  const app = isAppHub(appOrDefinition) ? appOrDefinition : defineApp(appOrDefinition ?? {});
3876
4240
  const target = options.target ?? "browser";
4241
+ const scheduler = options.scheduler ?? options.loader?.scheduler ?? createScheduler({
4242
+ strategy: target === "server" ? "manual" : "microtask"
4243
+ });
4244
+ const ownsScheduler = !options.scheduler && !options.loader?.scheduler;
3877
4245
  const attributes = normalizeAttributeConfig(options.attributes);
3878
4246
  const registry = options.registry ?? app.registry.view({ target });
3879
4247
  const signals = options.signals ?? createSignalRegistry(undefined, { registry, type: "signal" });
3880
4248
  const handlers = options.handlers ?? createHandlerRegistry(undefined, { registry, type: "handler" });
3881
4249
  const serverCache = createCacheRegistry(undefined, { registry, type: "cache.server" });
3882
4250
  const browserCache = createCacheRegistry(undefined, { registry, type: "cache.browser" });
3883
- const server = options.server ?? createServerRegistry(undefined, { registry, type: "server" });
4251
+ const serverFactory = options.serverFactory ?? createServerReferenceRegistry;
4252
+ const server = options.server ?? serverFactory(undefined, { registry, type: "server" });
3884
4253
  const partials = options.partials ?? createPartialRegistry(undefined, { registry, type: "partial" });
3885
4254
  const routes = options.routes ?? createRouteRegistry(undefined, { registry, type: "route" });
3886
4255
  const components = options.components ?? createComponentRegistry(undefined, { registry, type: "component" });
@@ -3908,6 +4277,7 @@ const __appModule = (() => {
3908
4277
  },
3909
4278
  loader,
3910
4279
  router,
4280
+ scheduler,
3911
4281
  attributes,
3912
4282
 
3913
4283
  start() {
@@ -3924,12 +4294,13 @@ const __appModule = (() => {
3924
4294
  handlers,
3925
4295
  server,
3926
4296
  cache: browserCache,
4297
+ scheduler,
3927
4298
  attributes
3928
4299
  });
3929
4300
  runtime.loader = loader;
3930
4301
 
3931
4302
  configureServerContext({ cache: browserCache });
3932
- signals._setContext?.({ server, loader, cache: browserCache });
4303
+ signals._setContext?.({ server, loader, cache: browserCache, scheduler });
3933
4304
 
3934
4305
  loader.start();
3935
4306
 
@@ -3945,6 +4316,7 @@ const __appModule = (() => {
3945
4316
  server,
3946
4317
  cache: browserCache,
3947
4318
  partials,
4319
+ scheduler,
3948
4320
  fetch: options.fetch,
3949
4321
  routeEndpoint: options.routeEndpoint,
3950
4322
  attributes
@@ -3956,7 +4328,7 @@ const __appModule = (() => {
3956
4328
  }
3957
4329
  } else {
3958
4330
  configureServerContext({ cache: serverCache });
3959
- signals._setContext?.({ server, cache: serverCache });
4331
+ signals._setContext?.({ server, cache: serverCache, scheduler });
3960
4332
  }
3961
4333
 
3962
4334
  return runtime;
@@ -3970,9 +4342,10 @@ const __appModule = (() => {
3970
4342
  async render(url) {
3971
4343
  assertActive();
3972
4344
  configureServerContext({ cache: serverCache });
3973
- signals._setContext?.({ server, cache: serverCache });
4345
+ signals._setContext?.({ server, cache: serverCache, scheduler });
3974
4346
  const matched = routes.match(url);
3975
4347
  if (!matched) {
4348
+ await scheduler.flush();
3976
4349
  return {
3977
4350
  html: renderDocument("", { status: 404, signals, browserCache, boundary: options.boundary ?? "route", attributes }),
3978
4351
  status: 404,
@@ -3992,8 +4365,8 @@ const __appModule = (() => {
3992
4365
  cache: serverCache,
3993
4366
  browserCache,
3994
4367
  partials,
3995
- request: options.request,
3996
- locals: options.locals
4368
+ scheduler,
4369
+ ...currentRequestContext()
3997
4370
  })
3998
4371
  : { html: "" };
3999
4372
 
@@ -4006,6 +4379,8 @@ const __appModule = (() => {
4006
4379
  browserCache.restore(result.cache.browser);
4007
4380
  }
4008
4381
 
4382
+ await scheduler.flush();
4383
+
4009
4384
  const status = result.status ?? 200;
4010
4385
  return {
4011
4386
  html: renderDocument(result.html, { status, signals, browserCache, boundary: result.boundary ?? options.boundary ?? "route", attributes }),
@@ -4024,6 +4399,9 @@ const __appModule = (() => {
4024
4399
  router?.destroy?.();
4025
4400
  loader?.destroy?.();
4026
4401
  signals.destroy?.();
4402
+ if (ownsScheduler) {
4403
+ scheduler.destroy();
4404
+ }
4027
4405
  },
4028
4406
 
4029
4407
  _applyUse(normalized) {
@@ -4044,11 +4422,23 @@ const __appModule = (() => {
4044
4422
  loader,
4045
4423
  router,
4046
4424
  cache,
4047
- request: options.request,
4048
- locals: options.locals
4425
+ scheduler,
4426
+ requestContext: options.requestContext,
4427
+ ...currentRequestContext()
4049
4428
  });
4050
4429
  }
4051
4430
 
4431
+ function currentRequestContext() {
4432
+ const context = readRequestContextLike(options.requestContext);
4433
+ return {
4434
+ requestContext: context,
4435
+ request: context.request ?? options.request,
4436
+ headers: context.headers ?? options.headers,
4437
+ cookies: context.cookies ?? options.cookies,
4438
+ locals: context.locals ?? options.locals
4439
+ };
4440
+ }
4441
+
4052
4442
  function assertActive() {
4053
4443
  if (destroyed) {
4054
4444
  throw new Error("Async app runtime has been destroyed.");
@@ -4202,6 +4592,77 @@ const __appModule = (() => {
4202
4592
  }
4203
4593
  }
4204
4594
 
4595
+ function createServerReferenceRegistry(initialMap = {}, options = {}) {
4596
+ const registry = options.registry ?? createRegistryStore();
4597
+ const type = options.type ?? "server";
4598
+ const defaults = {};
4599
+
4600
+ const reference = {
4601
+ registry,
4602
+
4603
+ register(id, value) {
4604
+ registry.register(type, id, value);
4605
+ return id;
4606
+ },
4607
+
4608
+ registerMany(map) {
4609
+ for (const [id, value] of Object.entries(map ?? {})) {
4610
+ reference.register(id, value);
4611
+ }
4612
+ return reference;
4613
+ },
4614
+
4615
+ unregister(id) {
4616
+ return registry.unregister(type, id);
4617
+ },
4618
+
4619
+ resolve() {
4620
+ return undefined;
4621
+ },
4622
+
4623
+ async run(id) {
4624
+ throw new Error(`Server command "${id}" cannot run without a server proxy or server registry.`);
4625
+ },
4626
+
4627
+ keys() {
4628
+ return registry.keys(type);
4629
+ },
4630
+
4631
+ entries() {
4632
+ return registry.entries(type);
4633
+ },
4634
+
4635
+ inspect() {
4636
+ return registry.entries(type);
4637
+ },
4638
+
4639
+ _setContext(context = {}) {
4640
+ Object.assign(defaults, context);
4641
+ return reference;
4642
+ },
4643
+
4644
+ _adoptMany() {
4645
+ return reference;
4646
+ }
4647
+ };
4648
+
4649
+ reference.registerMany(initialMap);
4650
+ return createServerNamespace((id, args, context) => reference.run(id, args, context), reference, () => defaults);
4651
+ }
4652
+
4653
+ function readRequestContextLike(store) {
4654
+ if (!store) {
4655
+ return {};
4656
+ }
4657
+ if (typeof store.get === "function") {
4658
+ return store.get() ?? {};
4659
+ }
4660
+ if (typeof store.getStore === "function") {
4661
+ return store.getStore() ?? {};
4662
+ }
4663
+ return {};
4664
+ }
4665
+
4205
4666
  function normalizeEntries(type, entries = {}) {
4206
4667
  if (type !== "signal") {
4207
4668
  return { ...(entries ?? {}) };
@@ -4253,6 +4714,312 @@ const __appModule = (() => {
4253
4714
  return { defineApp, createApp, readSnapshot, Async };
4254
4715
  })();
4255
4716
 
4717
+ const __boundaryReceiverModule = (() => {
4718
+ const defaultRecentLimit = 50;
4719
+
4720
+ function createBoundaryReceiver(options = {}) {
4721
+ const loader = options.loader;
4722
+ const signals = options.signals ?? loader?.signals;
4723
+ const cache = options.cache ?? loader?.cache;
4724
+ const scheduler = options.scheduler ?? loader?.scheduler;
4725
+ const router = options.router ?? loader?.router;
4726
+ const recentLimit = options.recentLimit ?? defaultRecentLimit;
4727
+ const throwOnError = options.throwOnError === true;
4728
+ const onApply = typeof options.onApply === "function" ? options.onApply : undefined;
4729
+ const onIgnore = typeof options.onIgnore === "function" ? options.onIgnore : undefined;
4730
+ const onError = typeof options.onError === "function" ? options.onError : undefined;
4731
+ const isScopeDestroyed = typeof options.isScopeDestroyed === "function"
4732
+ ? options.isScopeDestroyed
4733
+ : (scope) => scheduler?.isScopeDestroyed?.(scope) ?? scheduler?.inspectDestroyed?.(scope) ?? false;
4734
+
4735
+ if (!loader || typeof loader.swap !== "function") {
4736
+ throw new TypeError("createBoundaryReceiver(...) requires a loader with swap(boundary, html).");
4737
+ }
4738
+ if (!Number.isInteger(recentLimit) || recentLimit < 0) {
4739
+ throw new TypeError("createBoundaryReceiver(...) recentLimit must be a non-negative integer.");
4740
+ }
4741
+
4742
+ const boundaries = new Map();
4743
+ const recent = [];
4744
+ let destroyed = false;
4745
+
4746
+ const receiver = {
4747
+ async apply(patch) {
4748
+ if (destroyed) {
4749
+ throw new Error("Boundary receiver has been destroyed.");
4750
+ }
4751
+
4752
+ const normalized = validatePatch(patch);
4753
+ const record = boundaryRecord(normalized.boundary);
4754
+ if (normalized.seq <= record.lastSeq) {
4755
+ const result = {
4756
+ status: "ignored-stale",
4757
+ boundary: normalized.boundary,
4758
+ seq: normalized.seq,
4759
+ lastSeq: record.lastSeq
4760
+ };
4761
+ record.ignored += 1;
4762
+ record.lastStatus = result.status;
4763
+ remember(result);
4764
+ onIgnore?.(result, patch);
4765
+ return result;
4766
+ }
4767
+
4768
+ if (normalized.parentScope !== undefined && isScopeDestroyed(normalized.parentScope)) {
4769
+ const result = {
4770
+ status: "ignored-destroyed",
4771
+ boundary: normalized.boundary,
4772
+ seq: normalized.seq,
4773
+ parentScope: normalized.parentScope
4774
+ };
4775
+ record.ignored += 1;
4776
+ record.lastStatus = result.status;
4777
+ remember(result);
4778
+ onIgnore?.(result, patch);
4779
+ return result;
4780
+ }
4781
+
4782
+ record.lastSeq = normalized.seq;
4783
+
4784
+ if (Object.hasOwn(normalized, "error")) {
4785
+ const error = toStableError(normalized.error);
4786
+ const result = {
4787
+ status: "errored",
4788
+ boundary: normalized.boundary,
4789
+ seq: normalized.seq,
4790
+ error
4791
+ };
4792
+ record.errored += 1;
4793
+ record.lastStatus = result.status;
4794
+ remember(result);
4795
+ onError?.(error, result, patch);
4796
+ if (throwOnError) {
4797
+ throw error;
4798
+ }
4799
+ return result;
4800
+ }
4801
+
4802
+ if (normalized.signals) {
4803
+ if (!signals || typeof signals.set !== "function") {
4804
+ throw new Error("Boundary patch includes signals, but no signal registry is available.");
4805
+ }
4806
+ for (const [path, value] of Object.entries(normalized.signals)) {
4807
+ signals.set(path, value);
4808
+ }
4809
+ }
4810
+
4811
+ if (normalized.cache?.browser) {
4812
+ if (!cache || typeof cache.restore !== "function") {
4813
+ throw new Error("Boundary patch includes browser cache, but no cache registry is available.");
4814
+ }
4815
+ cache.restore(normalized.cache.browser);
4816
+ }
4817
+
4818
+ if (normalized.html != null) {
4819
+ loader.swap(normalized.boundary, normalized.html);
4820
+ }
4821
+
4822
+ await flushScheduler(scheduler, normalized.scope);
4823
+
4824
+ if (normalized.redirect) {
4825
+ await followRedirect(normalized.redirect, router, loader);
4826
+ const result = {
4827
+ status: "redirected",
4828
+ boundary: normalized.boundary,
4829
+ seq: normalized.seq,
4830
+ redirect: normalized.redirect
4831
+ };
4832
+ record.applied += 1;
4833
+ record.lastStatus = result.status;
4834
+ remember(result);
4835
+ onApply?.(result, patch);
4836
+ return result;
4837
+ }
4838
+
4839
+ const result = {
4840
+ status: "applied",
4841
+ boundary: normalized.boundary,
4842
+ seq: normalized.seq
4843
+ };
4844
+ record.applied += 1;
4845
+ record.lastStatus = result.status;
4846
+ remember(result);
4847
+ onApply?.(result, patch);
4848
+ return result;
4849
+ },
4850
+
4851
+ inspect() {
4852
+ const snapshot = {};
4853
+ for (const [boundary, record] of boundaries) {
4854
+ snapshot[boundary] = {
4855
+ lastSeq: record.lastSeq,
4856
+ applied: record.applied,
4857
+ ignored: record.ignored,
4858
+ lastStatus: record.lastStatus
4859
+ };
4860
+ if (record.errored > 0) {
4861
+ snapshot[boundary].errored = record.errored;
4862
+ }
4863
+ }
4864
+ return {
4865
+ destroyed,
4866
+ boundaries: snapshot,
4867
+ recent: recent.map((entry) => ({ ...entry }))
4868
+ };
4869
+ },
4870
+
4871
+ reset(boundary) {
4872
+ if (boundary === undefined) {
4873
+ boundaries.clear();
4874
+ recent.length = 0;
4875
+ return receiver;
4876
+ }
4877
+ assertBoundary(boundary);
4878
+ boundaries.delete(boundary);
4879
+ for (let index = recent.length - 1; index >= 0; index -= 1) {
4880
+ if (recent[index].boundary === boundary) {
4881
+ recent.splice(index, 1);
4882
+ }
4883
+ }
4884
+ return receiver;
4885
+ },
4886
+
4887
+ destroy() {
4888
+ destroyed = true;
4889
+ boundaries.clear();
4890
+ recent.length = 0;
4891
+ }
4892
+ };
4893
+
4894
+ return receiver;
4895
+
4896
+ function boundaryRecord(boundary) {
4897
+ if (!boundaries.has(boundary)) {
4898
+ boundaries.set(boundary, {
4899
+ lastSeq: -Infinity,
4900
+ applied: 0,
4901
+ ignored: 0,
4902
+ errored: 0,
4903
+ lastStatus: undefined
4904
+ });
4905
+ }
4906
+ return boundaries.get(boundary);
4907
+ }
4908
+
4909
+ function remember(result) {
4910
+ if (recentLimit === 0) {
4911
+ return;
4912
+ }
4913
+ recent.push(toRecentEntry(result));
4914
+ while (recent.length > recentLimit) {
4915
+ recent.shift();
4916
+ }
4917
+ }
4918
+ }
4919
+
4920
+ function validatePatch(patch) {
4921
+ if (!patch || typeof patch !== "object" || Array.isArray(patch)) {
4922
+ throw new TypeError("receiver.apply(patch) requires a boundary patch object.");
4923
+ }
4924
+
4925
+ assertBoundary(patch.boundary);
4926
+ if (typeof patch.seq !== "number" || !Number.isFinite(patch.seq)) {
4927
+ throw new TypeError("Boundary patch seq must be a finite number.");
4928
+ }
4929
+
4930
+ if (patch.signals !== undefined && !isPlainObject(patch.signals)) {
4931
+ throw new TypeError("Boundary patch signals must be an object.");
4932
+ }
4933
+ if (patch.cache !== undefined && !isPlainObject(patch.cache)) {
4934
+ throw new TypeError("Boundary patch cache must be an object.");
4935
+ }
4936
+ if (patch.cache?.browser !== undefined && !isPlainObject(patch.cache.browser)) {
4937
+ throw new TypeError("Boundary patch cache.browser must be an object.");
4938
+ }
4939
+ if (patch.redirect !== undefined && (typeof patch.redirect !== "string" || patch.redirect.length === 0)) {
4940
+ throw new TypeError("Boundary patch redirect must be a non-empty string.");
4941
+ }
4942
+ if (patch.parentScope !== undefined && typeof patch.parentScope !== "string") {
4943
+ throw new TypeError("Boundary patch parentScope must be a string.");
4944
+ }
4945
+ if (patch.scope !== undefined && typeof patch.scope !== "string") {
4946
+ throw new TypeError("Boundary patch scope must be a string.");
4947
+ }
4948
+
4949
+ const hasHtml = Object.hasOwn(patch, "html") && patch.html != null;
4950
+ const hasSignals = patch.signals && Object.keys(patch.signals).length > 0;
4951
+ const hasBrowserCache = patch.cache?.browser && Object.keys(patch.cache.browser).length > 0;
4952
+ const hasRedirect = Boolean(patch.redirect);
4953
+ const hasError = Object.hasOwn(patch, "error");
4954
+ if (!hasHtml && !hasSignals && !hasBrowserCache && !hasRedirect && !hasError) {
4955
+ throw new TypeError("Boundary patch must include html, signals, cache.browser, redirect, or error.");
4956
+ }
4957
+
4958
+ return patch;
4959
+ }
4960
+
4961
+ function assertBoundary(boundary) {
4962
+ if (typeof boundary !== "string" || boundary.length === 0) {
4963
+ throw new TypeError("Boundary patch boundary must be a non-empty string.");
4964
+ }
4965
+ }
4966
+
4967
+ async function flushScheduler(scheduler, scope) {
4968
+ if (!scheduler) {
4969
+ return;
4970
+ }
4971
+ if (scope !== undefined && typeof scheduler.flushScope === "function") {
4972
+ await scheduler.flushScope(scope);
4973
+ return;
4974
+ }
4975
+ if (typeof scheduler.flush === "function") {
4976
+ await scheduler.flush();
4977
+ }
4978
+ }
4979
+
4980
+ async function followRedirect(redirect, router, loader) {
4981
+ if (router && typeof router.navigate === "function") {
4982
+ await router.navigate(redirect);
4983
+ return;
4984
+ }
4985
+ const location = loader?.root?.ownerDocument?.defaultView?.location ?? globalThis.location;
4986
+ location?.assign?.(redirect);
4987
+ }
4988
+
4989
+ function toStableError(value) {
4990
+ if (value instanceof Error) {
4991
+ return value;
4992
+ }
4993
+ if (value && typeof value === "object" && typeof value.message === "string") {
4994
+ return Object.assign(new Error(value.message), value);
4995
+ }
4996
+ return new Error(String(value));
4997
+ }
4998
+
4999
+ function toRecentEntry(result) {
5000
+ const entry = {
5001
+ boundary: result.boundary,
5002
+ seq: result.seq,
5003
+ status: result.status
5004
+ };
5005
+ if (result.status === "ignored-stale") {
5006
+ entry.lastSeq = result.lastSeq;
5007
+ }
5008
+ if (result.status === "ignored-destroyed" && result.parentScope !== undefined) {
5009
+ entry.parentScope = result.parentScope;
5010
+ }
5011
+ if (result.status === "redirected") {
5012
+ entry.redirect = result.redirect;
5013
+ }
5014
+ return entry;
5015
+ }
5016
+
5017
+ function isPlainObject(value) {
5018
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
5019
+ }
5020
+ return { createBoundaryReceiver };
5021
+ })();
5022
+
4256
5023
  const __delayModule = (() => {
4257
5024
  function delay(ms, signal) {
4258
5025
  if (signal?.aborted) {
@@ -4294,6 +5061,7 @@ const { defineApp: defineApp } = __appModule;
4294
5061
  const { readSnapshot: readSnapshot } = __appModule;
4295
5062
  const { attributeName: attributeName } = __attributesModule;
4296
5063
  const { defineAttributeConfig: defineAttributeConfig } = __attributesModule;
5064
+ const { createBoundaryReceiver: createBoundaryReceiver } = __boundaryReceiverModule;
4297
5065
  const { createCacheRegistry: createCacheRegistry } = __cacheModule;
4298
5066
  const { defineCache: defineCache } = __cacheModule;
4299
5067
  const { component: component } = __componentModule;
@@ -4310,12 +5078,15 @@ const { createRouteRegistry: createRouteRegistry } = __routerModule;
4310
5078
  const { createRouter: createRouter } = __routerModule;
4311
5079
  const { defineRoute: defineRoute } = __routerModule;
4312
5080
  const { route: route } = __routerModule;
5081
+ const { createScheduler: createScheduler } = __schedulerModule;
5082
+ const { applyServerResult: applyServerResult } = __serverModule;
4313
5083
  const { createServerProxy: createServerProxy } = __serverModule;
4314
- const { createServerRegistry: createServerRegistry } = __serverModule;
5084
+ const { resolveServerCommandArguments: resolveServerCommandArguments } = __serverModule;
5085
+ const { unwrapServerResult: unwrapServerResult } = __serverModule;
4315
5086
  const { computed: computed } = __signalsModule;
4316
5087
  const { createSignal: createSignal } = __signalsModule;
4317
5088
  const { createSignalRegistry: createSignalRegistry } = __signalsModule;
4318
5089
  const { effect: effect } = __signalsModule;
4319
5090
  const { signal: signal } = __signalsModule;
4320
5091
 
4321
- export { asyncSignal, Async, createApp, defineApp, readSnapshot, attributeName, defineAttributeConfig, createCacheRegistry, defineCache, component, createComponentRegistry, defineComponent, delay, createHandlerRegistry, html, Loader, AsyncLoader, createPartialRegistry, createRegistryStore, createRouteRegistry, createRouter, defineRoute, route, createServerProxy, createServerRegistry, computed, createSignal, createSignalRegistry, effect, signal };
5092
+ export { 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 };