@async/framework 0.6.0 → 0.7.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 CHANGED
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.7.0 - 2026-06-17
4
+
5
+ - Added router navigation abort/version guards so stale route partials cannot
6
+ clobber newer navigations, and route partial contexts receive `this.abort`.
7
+ - Added ranked route matching so static and dynamic routes win over wildcard
8
+ fallbacks regardless of registration order.
9
+ - Added `readSnapshot(...)` and automatic browser activation from SSR snapshot
10
+ scripts.
11
+ - Fixed repeated server result application when proxy/server envelopes pass
12
+ through namespace or handler callers.
13
+ - Added in-flight `cache.getOrSet(...)` deduplication and clear proxy errors for
14
+ `File`, `Blob`, and `FormData` values that the JSON transport cannot send.
15
+ - Changed `framework.ts` from a source facade to a bundled TypeScript source
16
+ entrypoint.
17
+
3
18
  ## 0.6.0 - 2026-06-17
4
19
 
5
20
  - Added `Loader` as the canonical public loader factory, including
package/README.md CHANGED
@@ -100,7 +100,7 @@ production:
100
100
  | `framework.min.js` | ESM | Compact browser module bundle |
101
101
  | `framework.umd.js` | UMD | Readable script-tag/CommonJS-style bundle |
102
102
  | `framework.umd.min.js` | UMD | Compact script-tag/CommonJS-style bundle and default CDN file |
103
- | `framework.ts` | TypeScript source facade | TS-aware runtimes and higher-layer tooling |
103
+ | `framework.ts` | Bundled TypeScript source | TS-aware runtimes and higher-layer tooling |
104
104
  | `framework.d.ts` | Type declarations | TypeScript declarations for the public API |
105
105
 
106
106
  ```html
@@ -215,6 +215,7 @@ import {
215
215
  delay,
216
216
  effect,
217
217
  html,
218
+ readSnapshot,
218
219
  route,
219
220
  signal
220
221
  } from "@async/framework";
@@ -411,6 +412,10 @@ await delay(250, this.abort);
411
412
  If a dependency read through `this.signals.get(...)` changes, the async signal
412
413
  reruns and the previous run is aborted.
413
414
 
415
+ Dependency reads are captured while the async signal function starts running.
416
+ Read signal dependencies before the first `await`; reads that happen later are
417
+ ordinary reads and do not create refresh subscriptions.
418
+
414
419
  ## HTML Protocol
415
420
 
416
421
  Loader scans regular HTML attributes:
@@ -819,11 +824,17 @@ hydrate, diff, patch, or rerender:
819
824
  ```js
820
825
  createApp(browserApp, {
821
826
  root: document,
822
- snapshot,
823
827
  server: createServerProxy({ endpoint: "/__async/server" })
824
828
  }).start();
825
829
  ```
826
830
 
831
+ If an `async:snapshot` script is present under the root or document,
832
+ `createApp(...)` reads it automatically. You can also inspect it directly:
833
+
834
+ ```js
835
+ const snapshot = readSnapshot(document);
836
+ ```
837
+
827
838
  ## Components
828
839
 
829
840
  Components are scoped fragment functions. They return strings or `html`
package/framework.d.ts CHANGED
@@ -262,6 +262,7 @@ export interface PartialContext {
262
262
  cache?: CacheRegistry;
263
263
  browserCache?: CacheRegistry;
264
264
  partials: PartialRegistry;
265
+ abort?: AbortSignal;
265
266
  request?: Request;
266
267
  locals?: unknown;
267
268
  [key: string]: unknown;
@@ -506,6 +507,7 @@ export interface AsyncNamespace extends AppHub {
506
507
  asyncSignal: typeof asyncSignal;
507
508
  createApp: typeof createApp;
508
509
  defineApp: typeof defineApp;
510
+ readSnapshot: typeof readSnapshot;
509
511
  attributeName: typeof attributeName;
510
512
  defineAttributeConfig: typeof defineAttributeConfig;
511
513
  createCacheRegistry: typeof createCacheRegistry;
@@ -537,6 +539,7 @@ export declare function asyncSignal<T = unknown>(id: string, fn: AsyncSignalFunc
537
539
  export declare const Async: AppHub;
538
540
  export declare function createApp(appOrDefinition?: AppHub | AppDefinition, options?: CreateAppOptions): AppRuntime;
539
541
  export declare function defineApp(initial?: AppDefinition): AppHub;
542
+ export declare function readSnapshot(root?: Document | Element, options?: { attributes?: AttributeConfig }): { signals?: Record<string, unknown>; cache?: { browser?: Record<string, unknown> } };
540
543
  export declare function attributeName(attributes: AttributeConfig | undefined, type: keyof NormalizedAttributeConfig, name: string): string;
541
544
  export declare function defineAttributeConfig(config?: AttributeConfig): NormalizedAttributeConfig;
542
545
  export declare function createCacheRegistry(initialMap?: Record<string, CacheDefinition | CacheDefinitionOptions>, options?: { now?: () => number; registry?: RegistryStore; type?: "cache.browser" | "cache.server" }): CacheRegistry;
package/framework.js CHANGED
@@ -536,6 +536,7 @@ const __cacheModule = (() => {
536
536
  const registryStore = registry ?? createRegistryStore();
537
537
  const definitions = registryStore._map(type);
538
538
  const entries = registryStore._map(`${type}.entries`);
539
+ const pending = new Map();
539
540
 
540
541
  const registryApi = attachRegistryInspection({
541
542
  register(id, definition = defineCache()) {
@@ -597,19 +598,37 @@ const __cacheModule = (() => {
597
598
  if (cached !== undefined) {
598
599
  return cached;
599
600
  }
600
- const value = await fn();
601
- registryApi.set(key, value, options);
602
- return value;
601
+ if (pending.has(key)) {
602
+ return pending.get(key);
603
+ }
604
+ let promise;
605
+ promise = Promise.resolve()
606
+ .then(fn)
607
+ .then((value) => {
608
+ if (pending.get(key) === promise) {
609
+ registryApi.set(key, value, options);
610
+ }
611
+ return value;
612
+ })
613
+ .finally(() => {
614
+ if (pending.get(key) === promise) {
615
+ pending.delete(key);
616
+ }
617
+ });
618
+ pending.set(key, promise);
619
+ return promise;
603
620
  },
604
621
 
605
622
  delete(key) {
606
623
  assertKey(key);
624
+ pending.delete(key);
607
625
  return entries.delete(key);
608
626
  },
609
627
 
610
628
  clear(prefix) {
611
629
  if (prefix === undefined) {
612
630
  entries.clear();
631
+ pending.clear();
613
632
  return registryApi;
614
633
  }
615
634
  for (const key of [...entries.keys()]) {
@@ -617,6 +636,11 @@ const __cacheModule = (() => {
617
636
  entries.delete(key);
618
637
  }
619
638
  }
639
+ for (const key of [...pending.keys()]) {
640
+ if (key.startsWith(prefix)) {
641
+ pending.delete(key);
642
+ }
643
+ }
620
644
  return registryApi;
621
645
  },
622
646
 
@@ -1752,6 +1776,8 @@ const __componentModule = (() => {
1752
1776
  const __serverModule = (() => {
1753
1777
  const { attachRegistryInspection, createRegistryStore } = __registryStoreModule;
1754
1778
  const serverEnvelopeKeys = new Set(["value", "signals", "boundary", "html", "redirect", "error"]);
1779
+ const appliedServerResult = Symbol.for("@async/framework.appliedServerResult");
1780
+ const appliedServerValues = new WeakSet();
1755
1781
 
1756
1782
  function createServerRegistry(initialMap = {}, options = {}) {
1757
1783
  const registryStore = options.registry ?? createRegistryStore();
@@ -1858,6 +1884,7 @@ const __serverModule = (() => {
1858
1884
  input: context.input ?? defaultInput(runContext),
1859
1885
  signals: context.signalValues ?? snapshotSignalPaths(context.signalPaths, runContext.signals)
1860
1886
  };
1887
+ assertJsonTransportable(body);
1861
1888
 
1862
1889
  const response = await fetchImpl(joinEndpoint(endpoint, id), {
1863
1890
  method: "POST",
@@ -1875,7 +1902,7 @@ const __serverModule = (() => {
1875
1902
 
1876
1903
  const result = await readServerResponse(response);
1877
1904
  await applyServerResult(result, runContext);
1878
- return unwrapServerResult(result);
1905
+ return markAppliedServerValue(unwrapServerResult(result));
1879
1906
  }
1880
1907
 
1881
1908
  return createServerNamespace(run, {
@@ -1910,6 +1937,9 @@ const __serverModule = (() => {
1910
1937
  if (!isServerEnvelope(result)) {
1911
1938
  return result;
1912
1939
  }
1940
+ if (result[appliedServerResult] || appliedServerValues.has(result)) {
1941
+ return result;
1942
+ }
1913
1943
 
1914
1944
  if (result.signals && context.signals) {
1915
1945
  for (const [path, value] of Object.entries(result.signals)) {
@@ -1933,6 +1963,12 @@ const __serverModule = (() => {
1933
1963
  throw toError(result.error);
1934
1964
  }
1935
1965
 
1966
+ Object.defineProperty(result, appliedServerResult, {
1967
+ configurable: true,
1968
+ enumerable: false,
1969
+ value: true
1970
+ });
1971
+
1936
1972
  return result;
1937
1973
  }
1938
1974
 
@@ -1943,6 +1979,13 @@ const __serverModule = (() => {
1943
1979
  return result;
1944
1980
  }
1945
1981
 
1982
+ function markAppliedServerValue(value) {
1983
+ if (value && typeof value === "object") {
1984
+ appliedServerValues.add(value);
1985
+ }
1986
+ return value;
1987
+ }
1988
+
1946
1989
  function defaultInput(context = {}) {
1947
1990
  const form = findForm(context);
1948
1991
  if (form) {
@@ -2120,6 +2163,30 @@ const __serverModule = (() => {
2120
2163
  return output;
2121
2164
  }
2122
2165
 
2166
+ function assertJsonTransportable(value, seen = new Set()) {
2167
+ if (value == null || typeof value !== "object") {
2168
+ return;
2169
+ }
2170
+ if (seen.has(value)) {
2171
+ return;
2172
+ }
2173
+ seen.add(value);
2174
+
2175
+ const tag = Object.prototype.toString.call(value);
2176
+ if (tag === "[object File]" || tag === "[object Blob]" || tag === "[object FormData]") {
2177
+ throw new Error("Server proxy JSON transport does not support File, Blob, or FormData values yet.");
2178
+ }
2179
+ if (Array.isArray(value)) {
2180
+ for (const item of value) {
2181
+ assertJsonTransportable(item, seen);
2182
+ }
2183
+ return;
2184
+ }
2185
+ for (const item of Object.values(value)) {
2186
+ assertJsonTransportable(item, seen);
2187
+ }
2188
+ }
2189
+
2123
2190
  function joinEndpoint(endpoint, id) {
2124
2191
  return `${String(endpoint).replace(/\/$/, "")}/${encodeURIComponent(id)}`;
2125
2192
  }
@@ -3219,6 +3286,7 @@ const __routerModule = (() => {
3219
3286
  const nextRoute = normalizeRoute(pattern, definition);
3220
3287
  entries.set(pattern, nextRoute.definition);
3221
3288
  routes.push(nextRoute);
3289
+ sortRoutes(routes);
3222
3290
  return nextRoute;
3223
3291
  },
3224
3292
 
@@ -3291,6 +3359,7 @@ const __routerModule = (() => {
3291
3359
  const nextRoute = normalizeRoute(pattern, definition);
3292
3360
  entries.set(pattern, nextRoute.definition);
3293
3361
  routes.push(nextRoute);
3362
+ sortRoutes(routes);
3294
3363
  }
3295
3364
  }
3296
3365
 
@@ -3327,6 +3396,8 @@ const __routerModule = (() => {
3327
3396
  const ownsLoader = !loader;
3328
3397
  const cleanups = new Set();
3329
3398
  let destroyed = false;
3399
+ let navigationVersion = 0;
3400
+ let activeNavigation;
3330
3401
 
3331
3402
  const api = {
3332
3403
  mode,
@@ -3366,7 +3437,7 @@ const __routerModule = (() => {
3366
3437
  },
3367
3438
 
3368
3439
  match(url) {
3369
- return routes.match(url);
3440
+ return routes.match(resolveUrl(url));
3370
3441
  },
3371
3442
 
3372
3443
  prefetch(url) {
@@ -3391,7 +3462,7 @@ const __routerModule = (() => {
3391
3462
  return null;
3392
3463
  }
3393
3464
 
3394
- const target = toUrl(url);
3465
+ const target = resolveUrl(url);
3395
3466
  if (mode === "ssr-spa") {
3396
3467
  return fetchRoutePartial(target, options);
3397
3468
  }
@@ -3403,6 +3474,7 @@ const __routerModule = (() => {
3403
3474
  return;
3404
3475
  }
3405
3476
  destroyed = true;
3477
+ activeNavigation?.controller.abort(new Error("Router has been destroyed."));
3406
3478
  for (const cleanup of cleanups) {
3407
3479
  cleanup();
3408
3480
  }
@@ -3442,24 +3514,37 @@ const __routerModule = (() => {
3442
3514
  async function renderLocalRoutePartial(target, options = {}) {
3443
3515
  const matched = api.match(target);
3444
3516
  if (!matched) {
3517
+ beginNavigation(target, null);
3445
3518
  setNoRouteError(target);
3446
3519
  return null;
3447
3520
  }
3448
3521
 
3522
+ const navigation = beginNavigation(target, matched);
3449
3523
  setMatchedRouterState(target, matched, { pending: true, error: null });
3450
3524
 
3451
3525
  try {
3452
3526
  if (!matched.route?.partial || !partials?.resolve?.(matched.route.partial)) {
3453
3527
  const error = new Error(`Route "${target.pathname}" does not have a registered partial.`);
3454
- setRouterState({ pending: false, error });
3528
+ if (isActiveNavigation(navigation)) {
3529
+ setRouterState({ pending: false, error });
3530
+ }
3455
3531
  return null;
3456
3532
  }
3457
3533
 
3458
- const result = await partials.render(matched.route.partial, matched.params, contextFor(matched));
3459
- await applyNavigationResult(result, target, options);
3534
+ const result = await partials.render(matched.route.partial, matched.params, contextFor(matched, navigation));
3535
+ if (!isActiveNavigation(navigation)) {
3536
+ return null;
3537
+ }
3538
+ await applyNavigationResult(result, target, options, navigation);
3539
+ if (!isActiveNavigation(navigation)) {
3540
+ return null;
3541
+ }
3460
3542
  setRouterState({ pending: false, error: null });
3461
3543
  return result;
3462
3544
  } catch (error) {
3545
+ if (!isActiveNavigation(navigation)) {
3546
+ return null;
3547
+ }
3463
3548
  setRouterState({ pending: false, error });
3464
3549
  throw error;
3465
3550
  }
@@ -3467,26 +3552,43 @@ const __routerModule = (() => {
3467
3552
 
3468
3553
  async function fetchRoutePartial(target, options = {}) {
3469
3554
  const matched = api.match(target);
3555
+ const navigation = beginNavigation(target, matched);
3470
3556
  setMatchedRouterState(target, matched, { pending: true, error: null });
3471
3557
 
3472
3558
  try {
3473
- const result = await fetchRoute(target.href);
3474
- await applyNavigationResult(result, target, options);
3559
+ const result = await fetchRoute(target.href, { signal: navigation.abort });
3560
+ if (!isActiveNavigation(navigation)) {
3561
+ return null;
3562
+ }
3563
+ await applyNavigationResult(result, target, options, navigation);
3564
+ if (!isActiveNavigation(navigation)) {
3565
+ return null;
3566
+ }
3475
3567
  setRouterState({ pending: false, error: null });
3476
3568
  return result;
3477
3569
  } catch (error) {
3570
+ if (!isActiveNavigation(navigation)) {
3571
+ return null;
3572
+ }
3478
3573
  setRouterState({ pending: false, error });
3479
3574
  throw error;
3480
3575
  }
3481
3576
  }
3482
3577
 
3483
- async function applyNavigationResult(result, target, options) {
3578
+ async function applyNavigationResult(result, target, options, navigation) {
3579
+ if (!isActiveNavigation(navigation)) {
3580
+ return;
3581
+ }
3484
3582
  await applyServerResult(result, {
3485
3583
  signals: signalRegistry,
3486
3584
  loader: loaderInstance,
3487
3585
  router: api,
3488
- cache
3586
+ cache,
3587
+ abort: navigation?.abort
3489
3588
  });
3589
+ if (!isActiveNavigation(navigation)) {
3590
+ return;
3591
+ }
3490
3592
  if (result?.html != null && !result.boundary && !result.redirect) {
3491
3593
  loaderInstance.swap(boundary, result.html);
3492
3594
  }
@@ -3500,14 +3602,15 @@ const __routerModule = (() => {
3500
3602
  documentRef.defaultView?.history?.pushState?.({}, "", target.href);
3501
3603
  }
3502
3604
 
3503
- async function fetchRoute(url, { prefetch = false } = {}) {
3605
+ async function fetchRoute(url, { prefetch = false, signal } = {}) {
3504
3606
  if (typeof fetchImpl !== "function") {
3505
3607
  throw new Error("Router navigation requires a partial registry or fetch.");
3506
3608
  }
3507
3609
  const response = await fetchImpl(`${routeEndpoint}?to=${encodeURIComponent(String(url))}`, {
3508
3610
  headers: {
3509
3611
  accept: "application/json, text/html"
3510
- }
3612
+ },
3613
+ signal
3511
3614
  });
3512
3615
  if (!response.ok) {
3513
3616
  throw new Error(`Route "${url}" failed with ${response.status}.`);
@@ -3522,7 +3625,7 @@ const __routerModule = (() => {
3522
3625
  return { boundary, html: await response.text() };
3523
3626
  }
3524
3627
 
3525
- function contextFor(matched) {
3628
+ function contextFor(matched, navigation) {
3526
3629
  return {
3527
3630
  params: matched.params,
3528
3631
  route: matched.route,
@@ -3532,8 +3635,26 @@ const __routerModule = (() => {
3532
3635
  loader: loaderInstance,
3533
3636
  server,
3534
3637
  cache,
3535
- abort: undefined
3638
+ abort: navigation?.abort
3639
+ };
3640
+ }
3641
+
3642
+ function beginNavigation(target, matched) {
3643
+ activeNavigation?.controller.abort(new Error(`Router navigation superseded by ${target.pathname}${target.search}.`));
3644
+ const controller = new AbortController();
3645
+ const navigation = {
3646
+ id: ++navigationVersion,
3647
+ controller,
3648
+ abort: controller.signal,
3649
+ target,
3650
+ matched
3536
3651
  };
3652
+ activeNavigation = navigation;
3653
+ return navigation;
3654
+ }
3655
+
3656
+ function isActiveNavigation(navigation) {
3657
+ return !destroyed && navigation && activeNavigation?.id === navigation.id && !navigation.abort.aborted;
3537
3658
  }
3538
3659
 
3539
3660
  function updateStateFromLocation() {
@@ -3570,7 +3691,14 @@ const __routerModule = (() => {
3570
3691
  }
3571
3692
 
3572
3693
  function currentUrl() {
3573
- return toUrl(documentRef.defaultView?.location?.href ?? "http://localhost/");
3694
+ return resolveUrl(documentRef.defaultView?.location?.href ?? "http://localhost/");
3695
+ }
3696
+
3697
+ function resolveUrl(url) {
3698
+ if (url instanceof URL) {
3699
+ return url;
3700
+ }
3701
+ return new URL(String(url), documentRef.defaultView?.location?.href ?? "http://localhost/");
3574
3702
  }
3575
3703
 
3576
3704
  function assertActive() {
@@ -3587,6 +3715,7 @@ const __routerModule = (() => {
3587
3715
  pattern,
3588
3716
  regex,
3589
3717
  keys,
3718
+ score: routeScore(pattern),
3590
3719
  definition: normalized
3591
3720
  };
3592
3721
  }
@@ -3655,6 +3784,28 @@ const __routerModule = (() => {
3655
3784
  return String(value).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3656
3785
  }
3657
3786
 
3787
+ function sortRoutes(routes) {
3788
+ routes.sort((left, right) => right.score - left.score || right.pattern.length - left.pattern.length);
3789
+ }
3790
+
3791
+ function routeScore(pattern) {
3792
+ if (pattern === "*") {
3793
+ return -1;
3794
+ }
3795
+ return pattern
3796
+ .split("/")
3797
+ .filter(Boolean)
3798
+ .reduce((score, segment) => {
3799
+ if (segment === "*") {
3800
+ return score;
3801
+ }
3802
+ if (segment.startsWith(":")) {
3803
+ return score + 2;
3804
+ }
3805
+ return score + 4;
3806
+ }, pattern === "/" ? 3 : 0);
3807
+ }
3808
+
3658
3809
  function assertPattern(pattern) {
3659
3810
  if (typeof pattern !== "string" || (pattern !== "*" && !pattern.startsWith("/"))) {
3660
3811
  throw new TypeError("Route pattern must be a path string or \"*\".");
@@ -3738,7 +3889,7 @@ const __appModule = (() => {
3738
3889
  let started = false;
3739
3890
  let destroyed = false;
3740
3891
 
3741
- applySnapshot(signals, browserCache, options.snapshot);
3892
+ applySnapshot(signals, browserCache, options.snapshot ?? (target === "browser" ? readSnapshot(options.root, { attributes }) : undefined));
3742
3893
  attachServerCache(server, serverCache);
3743
3894
 
3744
3895
  const runtime = {
@@ -3906,6 +4057,38 @@ const __appModule = (() => {
3906
4057
 
3907
4058
  const Async = defineApp();
3908
4059
 
4060
+ function readSnapshot(root = globalThis.document, { attributes } = {}) {
4061
+ const attributeConfig = normalizeAttributeConfig(attributes);
4062
+ const snapshotAttr = attributeName(attributeConfig, "async", "snapshot");
4063
+ const documentRef = root?.ownerDocument ?? root ?? globalThis.document;
4064
+ const rootNode = root ?? documentRef;
4065
+ if (!rootNode?.querySelectorAll && !documentRef?.querySelectorAll) {
4066
+ return {};
4067
+ }
4068
+
4069
+ for (const searchRoot of new Set([rootNode, documentRef])) {
4070
+ if (!searchRoot?.querySelectorAll) {
4071
+ continue;
4072
+ }
4073
+ for (const script of searchRoot.querySelectorAll("script[type='application/json'], script")) {
4074
+ if (!script.hasAttribute?.(snapshotAttr)) {
4075
+ continue;
4076
+ }
4077
+ const source = script.textContent?.trim() ?? "";
4078
+ if (!source) {
4079
+ return {};
4080
+ }
4081
+ try {
4082
+ return JSON.parse(source);
4083
+ } catch (cause) {
4084
+ throw new Error(`Could not parse Async snapshot: ${cause instanceof Error ? cause.message : String(cause)}`);
4085
+ }
4086
+ }
4087
+ }
4088
+
4089
+ return {};
4090
+ }
4091
+
3909
4092
  function applyUseToRuntime(runtime, normalized) {
3910
4093
  applyRegistryUse(runtime.signals, runtime.registry, normalized.signal);
3911
4094
  applyRegistryUse(runtime.handlers, runtime.registry, normalized.handler);
@@ -4066,7 +4249,7 @@ const __appModule = (() => {
4066
4249
  function escapeScriptJson(value) {
4067
4250
  return JSON.stringify(value).replaceAll("<", "\\u003c");
4068
4251
  }
4069
- return { defineApp, createApp, Async };
4252
+ return { defineApp, createApp, readSnapshot, Async };
4070
4253
  })();
4071
4254
 
4072
4255
  const __delayModule = (() => {
@@ -4107,6 +4290,7 @@ const { asyncSignal: asyncSignal } = __asyncSignalModule;
4107
4290
  const { Async: Async } = __appModule;
4108
4291
  const { createApp: createApp } = __appModule;
4109
4292
  const { defineApp: defineApp } = __appModule;
4293
+ const { readSnapshot: readSnapshot } = __appModule;
4110
4294
  const { attributeName: attributeName } = __attributesModule;
4111
4295
  const { defineAttributeConfig: defineAttributeConfig } = __attributesModule;
4112
4296
  const { createCacheRegistry: createCacheRegistry } = __cacheModule;
@@ -4133,4 +4317,4 @@ const { createSignalRegistry: createSignalRegistry } = __signalsModule;
4133
4317
  const { effect: effect } = __signalsModule;
4134
4318
  const { signal: signal } = __signalsModule;
4135
4319
 
4136
- export { asyncSignal, Async, createApp, defineApp, attributeName, defineAttributeConfig, createCacheRegistry, defineCache, component, createComponentRegistry, defineComponent, delay, createHandlerRegistry, html, Loader, AsyncLoader, createPartialRegistry, createRegistryStore, createRouteRegistry, createRouter, defineRoute, route, createServerProxy, createServerRegistry, computed, createSignal, createSignalRegistry, effect, signal };
4320
+ 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 };