@askrjs/askr 0.3.1 → 0.4.1

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.
Files changed (123) hide show
  1. package/CHANGELOG.md +898 -1
  2. package/README.md +61 -29
  3. package/capabilities.json +19 -9
  4. package/dist/actions/index.js +56 -12
  5. package/dist/benchmark.js +10 -15
  6. package/dist/boot/index.d.ts +3 -2
  7. package/dist/boot/index.js +1 -1
  8. package/dist/boot-C5HnQZio.js +4032 -0
  9. package/dist/browser-activity-2r5gA1Cb.js +222 -0
  10. package/dist/components/index.d.ts +8 -1
  11. package/dist/components/index.js +1 -43
  12. package/dist/{aria-hKjThHcp.js → compose-ref-ccRU59BM.js} +48 -5
  13. package/dist/control/index.d.ts +2 -0
  14. package/dist/control/index.js +1 -1
  15. package/dist/control-hr3B0QaD.js +211 -0
  16. package/dist/core.d.ts +5 -9
  17. package/dist/csp-nonce-D-XM_uuJ.js +35 -0
  18. package/dist/data/index.js +3 -3
  19. package/dist/{data-2edW0jhF.js → data-BsnOUpQ6.js} +294 -95
  20. package/dist/data-runtime-i-RD0Ffm.js +242 -0
  21. package/dist/{deferred-CbgNhf67.js → deferred-nGZglpj6.js} +42 -20
  22. package/dist/domains/actions.d.ts +8 -3
  23. package/dist/domains/component.d.ts +1 -107
  24. package/dist/domains/context.d.ts +0 -15
  25. package/dist/domains/control.d.ts +36 -249
  26. package/dist/domains/data.d.ts +31 -13
  27. package/dist/domains/diagnostics.d.ts +1 -2
  28. package/dist/domains/lifecycle.d.ts +2 -5
  29. package/dist/domains/refs.d.ts +0 -2
  30. package/dist/domains/routing.d.ts +43 -8
  31. package/dist/domains/scheduler.d.ts +1 -76
  32. package/dist/domains/server.d.ts +5 -2
  33. package/dist/domains/state.d.ts +2 -5
  34. package/dist/domains/telemetry.d.ts +0 -2
  35. package/dist/elements.d.ts +214 -51
  36. package/dist/error-boundary-ChXoIFVl.js +293 -0
  37. package/dist/foundations/icon/index.d.ts +0 -58
  38. package/dist/foundations/icon/index.js +2 -2
  39. package/dist/foundations/index.js +2 -1
  40. package/dist/foundations/interactions/index.js +1 -1
  41. package/dist/foundations/state/index.d.ts +3 -3
  42. package/dist/foundations/state/index.js +5 -3
  43. package/dist/foundations/structures/index.js +2 -1
  44. package/dist/foundations/utilities/index.d.ts +2 -0
  45. package/dist/foundations/utilities/index.js +1 -2
  46. package/dist/fx/index.d.ts +31 -58
  47. package/dist/fx/index.js +313 -195
  48. package/dist/hooks-kjLigwDJ.js +202 -0
  49. package/dist/index.d.ts +11 -74
  50. package/dist/index.js +8 -447
  51. package/dist/instance-DL-YKrj6.js +992 -0
  52. package/dist/{interactions-CTeACdT_.js → interactions-BAa2xuJ-.js} +3 -2
  53. package/dist/jsx-dev-runtime.d.ts +41 -18
  54. package/dist/jsx-dev-runtime.js +1 -6
  55. package/dist/{jsx-runtime-CPJXAOBF.js → jsx-runtime-C4idqHY0.js} +18 -7
  56. package/dist/jsx-runtime.js +1 -1
  57. package/dist/jsx.d.ts +61 -37
  58. package/dist/{manifest-D1P3UNWV.js → manifest-BNupb86f.js} +3 -3
  59. package/dist/{navigate-prC5V8XV.js → navigate-DsfO12hB.js} +468 -85
  60. package/dist/navigation.d.ts +12 -3
  61. package/dist/peer-types.d.ts +8 -0
  62. package/dist/portal-LYf4cbHd.js +349 -0
  63. package/dist/promise-Dz8iIHAm.js +16 -0
  64. package/dist/query-registry-tzSiVrV_.js +325 -0
  65. package/dist/readable-DFWOsTac.js +21 -0
  66. package/dist/{render-context-B7ohlh6t.js → render-context-DGHnEveC.js} +27 -11
  67. package/dist/render-sync-CUxvoVab.js +1256 -0
  68. package/dist/resolution-CF1BqHLm.js +1258 -0
  69. package/dist/resource-BhcaFqdB.js +377 -0
  70. package/dist/resources/index.d.ts +21 -11
  71. package/dist/resources/index.js +65 -266
  72. package/dist/router/index.d.ts +14 -5
  73. package/dist/router/index.js +22 -53
  74. package/dist/scope-ila_zaId.js +55 -0
  75. package/dist/{shared-kU8yvryp.js → shared-O2ZnmYAf.js} +1 -7
  76. package/dist/{snapshot-source-An4iKajG.js → snapshot-Cjx4N988.js} +6 -1
  77. package/dist/ssg/index.d.ts +1 -2
  78. package/dist/ssg/index.js +43 -22
  79. package/dist/ssr/index.d.ts +26 -28
  80. package/dist/ssr/index.js +4 -4
  81. package/dist/{ssr-CiNm6Vzl.js → ssr-9LOx_bEu.js} +133 -122
  82. package/dist/ssr-DPfdcq0e.js +229 -0
  83. package/dist/state-i9D4EtY6.js +250 -0
  84. package/dist/structures-CkehlOT3.js +231 -0
  85. package/dist/testing/index.d.ts +8 -4
  86. package/dist/testing/index.js +29 -34
  87. package/dist/url-C9xMpMMv.js +185 -0
  88. package/dist/utilities.d.ts +48 -2
  89. package/dist/verify-hydration-D_cyn1OX.js +152 -0
  90. package/package.json +19 -9
  91. package/dist/access-CEA2pOry.js +0 -1046
  92. package/dist/activity-oy2YFTFF.js +0 -201
  93. package/dist/boot-DLplW9h4.js +0 -1104
  94. package/dist/capabilities-Bbf0mlp9.js +0 -42
  95. package/dist/compose-ref-v0qGlHoN.js +0 -26
  96. package/dist/control-BR_mrX__.js +0 -173
  97. package/dist/control-C-ApnFs1.js +0 -20
  98. package/dist/csp-nonce-BfFWcd0w.js +0 -173
  99. package/dist/data-runtime-CxoY98lX.js +0 -149
  100. package/dist/default-portal-runtime-DvMvWJVp.js +0 -20
  101. package/dist/dom-renderer.d.ts +0 -77
  102. package/dist/domains/renderer.d.ts +0 -127
  103. package/dist/eager-control.d.ts +0 -8
  104. package/dist/effect-C3Zsc1k6.js +0 -292
  105. package/dist/for-state-BDykeOJL.js +0 -1753
  106. package/dist/instance-VAFYVJOa.js +0 -1027
  107. package/dist/notify-DxxuDPeY.js +0 -266
  108. package/dist/portal-w3oQ6oQU.js +0 -29
  109. package/dist/query-registry-LrYpQsDJ.js +0 -83
  110. package/dist/render-resolved-BC0W7_5b.js +0 -1123
  111. package/dist/renderer-CT0HeY58.js +0 -8623
  112. package/dist/resolution-3DskUkjG.js +0 -577
  113. package/dist/resource-18-sFRl9.js +0 -344
  114. package/dist/route-matching-CUzZ31gT.js +0 -551
  115. package/dist/router-internal-7nuNEHfG.js +0 -36
  116. package/dist/scope-Cq_iYbTR.js +0 -403
  117. package/dist/selector-BSPVkjKP.js +0 -311
  118. package/dist/ssr-EPjwJH5R.js +0 -29
  119. package/dist/state-BXJfudq1.js +0 -91
  120. package/dist/structures-CjEzfj4h.js +0 -654
  121. package/dist/timer-C7uqnJVt.js +0 -85
  122. package/dist/verify-hydration-ikaxotf7.js +0 -27
  123. package/dist/vnode-D24p6IW2.js +0 -7
@@ -1,9 +1,40 @@
1
- import { T as isProductionEnvironment, r as flushRuntimeScheduler, u as isRuntimeSchedulerExecuting, v as logger, w as isDevelopmentEnvironment } from "./access-CEA2pOry.js";
2
- import { P as isPromiseLike } from "./instance-VAFYVJOa.js";
3
- import { i as commitTransaction, l as registerCommitParticipant, o as discardTransaction, r as beginCommitTransaction } from "./notify-DxxuDPeY.js";
4
- import { i as syncCurrentRouteSnapshot } from "./activity-oy2YFTFF.js";
5
- import { a as resolveRouteRequest, i as hasRouteRenderData, n as getRouteRenderContext, r as getRouteRenderData } from "./resolution-3DskUkjG.js";
6
- import { L as lockRouteRegistration, b as getActiveRouteBasePath, o as addRouteBasePath, r as computeRouteActivityMatches } from "./route-matching-CUzZ31gT.js";
1
+ import { I as logger, L as isDevelopmentEnvironment, R as isProductionEnvironment, x as isFlushing, y as flushSync } from "./instance-DL-YKrj6.js";
2
+ import { b as reportUncaughtErrorLater, c as onCommit, i as currentComponent, n as createSource, o as hookSlot, r as currentAppRuntime, s as notify, u as readSource } from "./hooks-kjLigwDJ.js";
3
+ import { n as getActiveRenderContext } from "./render-context-DGHnEveC.js";
4
+ import { n as isPromiseLike } from "./promise-Dz8iIHAm.js";
5
+ import { C as removeRouteBasePath, E as parseLocation, M as getActiveRouteBasePath, N as getActiveRoutes, T as makeQuery, Y as lockRouteRegistration, a as resolveRouteRequest, b as addLogicalRouteBasePath, g as computeRouteActivityMatches, h as computeMatchesFromRoutes, i as hasRouteRenderData, n as getRouteRenderContext, r as getRouteRenderData, w as deepFreeze } from "./resolution-CF1BqHLm.js";
6
+ import { n as syncRouteActivitySnapshot } from "./route-activity-CNJfpwx-.js";
7
+ import { c as resolveNavigationUrl } from "./url-C9xMpMMv.js";
8
+ //#region src/common/app-render-runtime.ts
9
+ const stagedRouteLocations = /* @__PURE__ */ new WeakMap();
10
+ function createAppRenderRuntime(input = {}) {
11
+ return {
12
+ hydrationResources: input.hydrationResources,
13
+ framework: Object.freeze({ ...input.framework }),
14
+ route: input.route,
15
+ hasRoute: input.hasRoute ?? false,
16
+ dataRuntime: input.dataRuntime,
17
+ routeRegistry: input.routeRegistry,
18
+ routeAuth: input.routeAuth,
19
+ lifetime: input.lifetime
20
+ };
21
+ }
22
+ function stageAppRenderRouteLocation(runtime, href, state) {
23
+ stagedRouteLocations.set(runtime, {
24
+ href,
25
+ state
26
+ });
27
+ }
28
+ function getStagedAppRenderRouteLocation(runtime) {
29
+ return runtime ? stagedRouteLocations.get(runtime)?.href : void 0;
30
+ }
31
+ function getStagedAppRenderRouteState(runtime) {
32
+ return runtime ? stagedRouteLocations.get(runtime)?.state : void 0;
33
+ }
34
+ function clearStagedAppRenderRouteLocation(runtime) {
35
+ if (runtime) stagedRouteLocations.delete(runtime);
36
+ }
37
+ //#endregion
7
38
  //#region src/common/root-update.ts
8
39
  let rootUpdateHost;
9
40
  function configureRootUpdateHost(host) {
@@ -14,6 +45,169 @@ function prepareRootUpdate(root, input) {
14
45
  return rootUpdateHost.prepare(root, input);
15
46
  }
16
47
  //#endregion
48
+ //#region src/router/activity.ts
49
+ function routeSignature(route) {
50
+ return `${route.path}\u0000${JSON.stringify(route.query.toJSON())}\u0000${route.hash ?? ""}`;
51
+ }
52
+ let currentRouteSnapshot = buildRouteSnapshot("/", "", "");
53
+ const currentRouteSource = createSource();
54
+ /** Register a callback to run whenever the active route changes, with optional cleanup. */
55
+ function onRouteChange(fn, options = {}) {
56
+ const instance = currentComponent();
57
+ if (!instance) return;
58
+ const route = currentRoute();
59
+ const slot = hookSlot(instance, "onRouteChange", () => ({
60
+ kind: "route-change",
61
+ previous: null,
62
+ cleanup: null,
63
+ cleanupRegistered: false
64
+ }));
65
+ const immediate = options.immediate === true;
66
+ if (!slot.cleanupRegistered) {
67
+ instance.onCleanup(() => {
68
+ slot.cleanup?.();
69
+ slot.cleanup = null;
70
+ });
71
+ slot.cleanupRegistered = true;
72
+ }
73
+ onCommit(instance, () => {
74
+ const previous = slot.previous;
75
+ if (!previous) {
76
+ if (immediate) slot.cleanup = fn(route, null) ?? null;
77
+ slot.previous = route;
78
+ return;
79
+ }
80
+ if (routeSignature(previous) === routeSignature(route)) return;
81
+ slot.cleanup?.();
82
+ slot.cleanup = fn(route, previous) ?? null;
83
+ slot.previous = route;
84
+ });
85
+ }
86
+ let serverLocation = null;
87
+ function readLocationState() {
88
+ if (getActiveRenderContext()?.mode === "ssr") return {
89
+ hasState: false,
90
+ state: void 0
91
+ };
92
+ const staged = getStagedAppRenderRouteState(currentAppRuntime());
93
+ if (staged) return staged;
94
+ const historyState = typeof window !== "undefined" && window.history?.state ? window.history.state : null;
95
+ const hasState = historyState?.askrHasState === true;
96
+ return {
97
+ hasState,
98
+ state: hasState ? historyState.askrState : void 0
99
+ };
100
+ }
101
+ function logicalRoutePathname(pathname) {
102
+ const logical = removeRouteBasePath(pathname, getActiveRouteBasePath());
103
+ return logical === void 0 ? void 0 : parseLocation(logical).pathname;
104
+ }
105
+ function routePathname(pathname) {
106
+ const logical = logicalRoutePathname(pathname);
107
+ return logical === void 0 ? {
108
+ pathname: parseLocation(pathname).pathname,
109
+ withinBasePath: false
110
+ } : {
111
+ pathname: logical,
112
+ withinBasePath: true
113
+ };
114
+ }
115
+ function setServerLocation(url) {
116
+ serverLocation = url;
117
+ if (url) {
118
+ const location = routePathname(parseLocation(url).pathname);
119
+ syncRouteActivitySnapshot(location.pathname, location.withinBasePath ? computeMatchesFromRoutes(location.pathname, getActiveRoutes()) : []);
120
+ return;
121
+ }
122
+ syncRouteActivitySnapshot(currentRouteSnapshot.path, currentRouteSnapshot.matches);
123
+ }
124
+ function buildRouteSnapshot(pathname, search, hash) {
125
+ const location = routePathname(pathname);
126
+ pathname = location.pathname;
127
+ const query = makeQuery(search);
128
+ const matches = location.withinBasePath ? computeMatchesFromRoutes(pathname, getActiveRoutes()) : [];
129
+ const locationState = readLocationState();
130
+ return Object.freeze({
131
+ path: pathname,
132
+ params: deepFreeze({ ...matches[0]?.params }),
133
+ query,
134
+ hash: hash || null,
135
+ ...locationState,
136
+ matches: Object.freeze(matches)
137
+ });
138
+ }
139
+ function setCurrentRouteSnapshot(pathname, search, hash, activityMatches) {
140
+ currentRouteSnapshot = buildRouteSnapshot(pathname, search, hash);
141
+ syncRouteActivitySnapshot(currentRouteSnapshot.path, activityMatches ?? currentRouteSnapshot.matches);
142
+ notify(currentRouteSource);
143
+ }
144
+ function readCurrentRouteLocation() {
145
+ const renderContext = getActiveRenderContext();
146
+ if (renderContext?.url) {
147
+ const parsed = parseLocation(renderContext.url);
148
+ const location = routePathname(parsed.pathname);
149
+ return {
150
+ ...parsed,
151
+ ...location
152
+ };
153
+ }
154
+ const stagedRouteLocation = getStagedAppRenderRouteLocation(currentAppRuntime());
155
+ if (stagedRouteLocation) {
156
+ const parsed = parseLocation(stagedRouteLocation);
157
+ const location = routePathname(parsed.pathname);
158
+ return {
159
+ ...parsed,
160
+ ...location
161
+ };
162
+ }
163
+ if (typeof window !== "undefined" && window.location) return {
164
+ ...routePathname(window.location.pathname || "/"),
165
+ search: window.location.search || "",
166
+ hash: window.location.hash || ""
167
+ };
168
+ if (serverLocation) {
169
+ const parsed = parseLocation(serverLocation);
170
+ const location = routePathname(parsed.pathname);
171
+ return {
172
+ ...parsed,
173
+ ...location
174
+ };
175
+ }
176
+ return {
177
+ ...routePathname(currentRouteSnapshot.path),
178
+ search: "",
179
+ hash: currentRouteSnapshot.hash ?? ""
180
+ };
181
+ }
182
+ function readCurrentRouteSnapshot() {
183
+ if (!currentComponent()) throw new Error("currentRoute() can only be called during component render execution. Call currentRoute() from inside your component function.");
184
+ const { pathname, search, hash, withinBasePath } = readCurrentRouteLocation();
185
+ const query = makeQuery(search);
186
+ const matches = withinBasePath ? computeMatchesFromRoutes(pathname, getActiveRoutes()) : [];
187
+ const routeParams = (withinBasePath ? getActiveRenderContext()?.params : void 0) ?? matches[0]?.params ?? {};
188
+ const params = deepFreeze({ ...routeParams });
189
+ const locationState = readLocationState();
190
+ return Object.freeze({
191
+ path: pathname,
192
+ params,
193
+ query,
194
+ hash: hash || null,
195
+ ...locationState,
196
+ matches: Object.freeze(matches)
197
+ });
198
+ }
199
+ /** Read the currently active route's {@link RouteSnapshot}; reactive during component render. */
200
+ function currentRoute() {
201
+ const instance = currentComponent();
202
+ if (!instance) throw new Error("currentRoute() can only be called during component render execution. Call currentRoute() from inside your component function.");
203
+ if (typeof window === "undefined" || instance.server) return readCurrentRouteSnapshot();
204
+ readSource(currentRouteSource);
205
+ return readCurrentRouteSnapshot();
206
+ }
207
+ function syncCurrentRouteSnapshot(pathname, search, hash, activityMatches) {
208
+ setCurrentRouteSnapshot(pathname, search, hash, activityMatches);
209
+ }
210
+ //#endregion
17
211
  //#region src/router/navigation-registry.ts
18
212
  let currentInstance = null;
19
213
  let currentPathname = "/";
@@ -45,6 +239,29 @@ function parseTargetUrl(path) {
45
239
  const base = href && href !== "about:blank" && !href.startsWith("about:") && !href.startsWith("data:") ? href : `http://localhost${pathname}${search}${hash}`;
46
240
  return new URL(path, base);
47
241
  }
242
+ /** Whether a parsed target shares the origin of the current document. */
243
+ function isCurrentOrigin(target) {
244
+ return target.origin === parseTargetUrl("/").origin;
245
+ }
246
+ /**
247
+ * The absolute URL of a root-relative href on the current origin. Browsers
248
+ * read a root-relative `//host/x` as another host, so history writes and
249
+ * document loads never receive the root-relative form.
250
+ */
251
+ function toDocumentUrl(href) {
252
+ const origin = typeof window === "undefined" ? void 0 : window.location.origin;
253
+ return origin && origin !== "null" ? `${origin}${href}` : href;
254
+ }
255
+ /**
256
+ * Parse a navigation or redirect target. Only a target written with an
257
+ * explicit `http:`/`https:` scheme may leave the current origin: a path-like
258
+ * string that the URL parser resolves elsewhere (`/\\evil.example`,
259
+ * `//evil.example`, `/.//evil.example`) would otherwise turn an app path into
260
+ * an open redirect.
261
+ */
262
+ function parseNavigationTarget(path) {
263
+ return resolveNavigationUrl(path, parseTargetUrl("").href);
264
+ }
48
265
  function getRegisteredAppsSnapshot() {
49
266
  return [...registeredApps];
50
267
  }
@@ -217,13 +434,29 @@ function scrollToPosition(position) {
217
434
  if (typeof window === "undefined" || typeof window.scrollTo !== "function") return;
218
435
  window.scrollTo(position.x, position.y);
219
436
  }
437
+ /**
438
+ * Find the element a URL fragment indicates. Like the browser, try the raw
439
+ * fragment first, then its percent-decoded form. A fragment that is not valid
440
+ * percent-encoding (`#%E0`) has no decoded form, so only the raw id applies.
441
+ */
442
+ function findFragmentTarget(fragment) {
443
+ const raw = document.getElementById(fragment);
444
+ if (raw) return raw;
445
+ let decoded;
446
+ try {
447
+ decoded = decodeURIComponent(fragment);
448
+ } catch (error) {
449
+ if (error instanceof URIError) return null;
450
+ throw error;
451
+ }
452
+ return decoded === fragment ? null : document.getElementById(decoded);
453
+ }
220
454
  function applyNavigationScroll(behavior) {
221
455
  const nextBehavior = behavior ?? scrollRestorationOptions.navigation;
222
456
  if (!scrollRestorationOptions.enabled || nextBehavior === "preserve") return;
223
457
  const hash = typeof window !== "undefined" ? window.location.hash : "";
224
458
  if (hash && typeof document !== "undefined") {
225
- const id = decodeURIComponent(hash.slice(1));
226
- const target = document.getElementById(id);
459
+ const target = findFragmentTarget(hash.slice(1));
227
460
  if (target && typeof target.scrollIntoView === "function") {
228
461
  target.scrollIntoView({ behavior: "auto" });
229
462
  return;
@@ -410,6 +643,118 @@ function serializeRouteMeta(meta) {
410
643
  return output.join("");
411
644
  }
412
645
  //#endregion
646
+ //#region src/router/document-navigation.ts
647
+ /**
648
+ * Hand a location no registered app can render to the browser as a document
649
+ * load, keeping the requested history mode.
650
+ */
651
+ function loadDocument(href, history) {
652
+ if (history === "replace") window.location.replace(href);
653
+ else window.location.assign(href);
654
+ }
655
+ /** Reload the document at the current history entry. */
656
+ function reloadDocument() {
657
+ window.location.reload();
658
+ }
659
+ //#endregion
660
+ //#region src/router/history-index.ts
661
+ /**
662
+ * Positions in this document's session history.
663
+ *
664
+ * Askr stamps an index into every entry it writes. A failed back/forward
665
+ * traversal then returns with `history.go()` to the entry whose page is still
666
+ * rendered, instead of rewriting the entry the browser landed on. A position
667
+ * is `undefined` once it cannot be known, for example after other code pushed
668
+ * an entry; rollback then reloads rather than guess.
669
+ */
670
+ let landedIndex;
671
+ let renderedIndex;
672
+ let pendingReturnIndex;
673
+ let historyIndexInitialized = false;
674
+ function readHistoryIndex(state) {
675
+ if (!state || typeof state !== "object") return void 0;
676
+ const index = state.askrIndex;
677
+ return typeof index === "number" ? index : void 0;
678
+ }
679
+ function stampActiveEntry(state, index) {
680
+ window.history.replaceState({
681
+ ...state && typeof state === "object" ? state : {},
682
+ askrIndex: index
683
+ }, "", window.location.href);
684
+ }
685
+ /**
686
+ * The index an entry written with `mode` occupies. The active entry must still
687
+ * carry the index Askr last tracked; otherwise other code wrote an entry and
688
+ * positions are unknown from here on.
689
+ */
690
+ function nextHistoryIndex(mode) {
691
+ if (readHistoryIndex(window.history.state) !== landedIndex) landedIndex = renderedIndex = void 0;
692
+ if (landedIndex === void 0) return void 0;
693
+ return mode === "push" ? landedIndex + 1 : landedIndex;
694
+ }
695
+ /** Record that the entry at `index` is active and its page rendered. */
696
+ function commitHistoryIndex(index) {
697
+ landedIndex = renderedIndex = index;
698
+ }
699
+ /**
700
+ * Record the entry a popstate landed on, before its page renders. An entry
701
+ * without an index was added by a fragment navigation from the entry the user
702
+ * was on, so it is stamped as the next position.
703
+ */
704
+ function landOnHistoryEntry(state) {
705
+ let index = readHistoryIndex(state);
706
+ if (index === void 0 && landedIndex !== void 0) {
707
+ index = landedIndex + 1;
708
+ stampActiveEntry(state, index);
709
+ }
710
+ landedIndex = index;
711
+ return index;
712
+ }
713
+ /**
714
+ * Adopt the active entry's index. Only the document's first entry is stamped
715
+ * as index 0: stamping later would guess a position the user may not be at.
716
+ */
717
+ function initializeHistoryIndex() {
718
+ const state = window.history?.state;
719
+ const index = readHistoryIndex(state);
720
+ if (index !== void 0) {
721
+ historyIndexInitialized = true;
722
+ commitHistoryIndex(index);
723
+ return;
724
+ }
725
+ if (historyIndexInitialized || typeof window.history?.replaceState !== "function") return;
726
+ historyIndexInitialized = true;
727
+ commitHistoryIndex(0);
728
+ stampActiveEntry(state, 0);
729
+ }
730
+ /**
731
+ * Traverse back to the entry whose page is rendered. Returns false when the
732
+ * positions are unknown, leaving history untouched.
733
+ */
734
+ function returnToRenderedHistoryEntry() {
735
+ if (landedIndex === void 0 || renderedIndex === void 0) return false;
736
+ const delta = renderedIndex - landedIndex;
737
+ if (delta === 0) return false;
738
+ pendingReturnIndex = renderedIndex;
739
+ window.history.go(delta);
740
+ return true;
741
+ }
742
+ /** A newer navigation owns history, so a pending return must not skip it. */
743
+ function cancelHistoryReturn() {
744
+ pendingReturnIndex = void 0;
745
+ }
746
+ /**
747
+ * Whether this popstate completes a {@link returnToRenderedHistoryEntry}
748
+ * traversal, whose destination is already rendered.
749
+ */
750
+ function consumeHistoryReturn(state) {
751
+ const expected = pendingReturnIndex;
752
+ pendingReturnIndex = void 0;
753
+ if (expected === void 0 || readHistoryIndex(state) !== expected) return false;
754
+ commitHistoryIndex(expected);
755
+ return true;
756
+ }
757
+ //#endregion
413
758
  //#region src/router/navigation-targets.ts
414
759
  const MAX_NAVIGATION_REDIRECTS = 20;
415
760
  let activeRouteRequestId = 0;
@@ -506,7 +851,8 @@ function resolveAppRouteRequest(app, pathname, href, signal) {
506
851
  return resolveRouteRequest(href, {
507
852
  registry: app.registry,
508
853
  auth: app.auth,
509
- signal
854
+ signal,
855
+ dataRuntime: app.instance.appRuntime?.dataRuntime
510
856
  });
511
857
  }
512
858
  function getResolvedRouteHandler(resolved) {
@@ -518,11 +864,12 @@ function getResolvedRouteHandler(resolved) {
518
864
  params: resolved.params
519
865
  };
520
866
  }
521
- function prepareNavigationRoot(target, href, replaceLifetime) {
867
+ function prepareNavigationRoot(target, href, replaceLifetime, locationState) {
522
868
  const resolved = target.resolved;
523
869
  return prepareRootUpdate(target.app.instance, {
524
870
  handler: bindResolvedRouteHandler(getResolvedRouteHandler(resolved)),
525
871
  href,
872
+ locationState,
526
873
  routeData: isRenderResult(resolved) ? getRouteRenderData(resolved) : void 0,
527
874
  hasRouteData: isRenderResult(resolved) && hasRouteRenderData(resolved),
528
875
  replaceLifetime
@@ -562,8 +909,8 @@ function applyNavigationTargets(requestId, path, options, redirectState, pathnam
562
909
  for (const target of targets) {
563
910
  const resolved = target.resolved;
564
911
  if (!resolved || resolved.kind !== "redirect") continue;
565
- const redirectTarget = parseTargetUrl(resolved.to);
566
- const redirectHref = `${redirectTarget.pathname}${redirectTarget.search}${redirectTarget.hash}`;
912
+ const redirectTarget = parseNavigationTarget(resolved.to);
913
+ const redirectHref = isCurrentOrigin(redirectTarget) ? `${redirectTarget.pathname}${redirectTarget.search}${redirectTarget.hash}` : redirectTarget.href;
567
914
  if (redirectHref === href) {
568
915
  if (isDevelopmentEnvironment()) logger.warn(`Navigation guard redirected to the same path: ${redirectHref}`);
569
916
  return;
@@ -578,26 +925,40 @@ function applyNavigationTargets(requestId, path, options, redirectState, pathnam
578
925
  const matchedTargets = targets.filter((target) => target.resolved !== null);
579
926
  if (matchedTargets.length === 0) {
580
927
  if (isDevelopmentEnvironment()) logger.warn(`No route found for path: ${path}`);
928
+ if (href !== getWindowHref$1()) loadDocument(toDocumentUrl(href), getNavigationHistoryMode(options));
581
929
  return;
582
930
  }
583
931
  if (isStaleRouteRequest(requestId)) return;
584
- commitNavigationRoots(requestId, pathname, href, matchedTargets, () => {
932
+ const hasState = Object.prototype.hasOwnProperty.call(options, "state");
933
+ const locationState = {
934
+ hasState,
935
+ state: hasState ? options.state : void 0
936
+ };
937
+ commitNavigationRoots(requestId, pathname, href, matchedTargets, locationState, () => {
585
938
  saveScrollPosition(previousHref);
586
- const historyMethod = getNavigationHistoryMode(options) === "replace" ? "replaceState" : "pushState";
587
- window.history[historyMethod]({
939
+ const historyMode = getNavigationHistoryMode(options);
940
+ const historyIndex = nextHistoryIndex(historyMode);
941
+ window.history[historyMode === "replace" ? "replaceState" : "pushState"]({
588
942
  path: href,
589
- askrHasState: Object.prototype.hasOwnProperty.call(options, "state"),
590
- askrState: options.state
591
- }, "", href);
943
+ askrHasState: locationState.hasState,
944
+ askrState: locationState.state,
945
+ askrIndex: historyIndex
946
+ }, "", toDocumentUrl(href));
947
+ commitHistoryIndex(historyIndex);
592
948
  }, () => {
593
949
  if (pathname !== previousPathname || parseTargetUrl(href).hash) applyNavigationScroll(options.scroll);
594
950
  });
595
951
  }
596
- function applyPopStateNavigationTargets(requestId, previousHref, previousState, pathname, href, state, targets, navigate) {
952
+ function applyPopStateNavigationTargets(requestId, previousHref, historyIndex, pathname, href, state, targets, navigate) {
597
953
  if (isStaleRouteRequest(requestId)) return;
598
954
  const matchedTargets = targets.filter((target) => target.resolved !== null);
599
955
  if (matchedTargets.length === 0) {
956
+ if (href.split("#")[0] === previousHref.split("#")[0]) {
957
+ commitHistoryIndex(historyIndex);
958
+ return;
959
+ }
600
960
  if (isDevelopmentEnvironment()) logger.warn(`No route found for path: ${pathname}`);
961
+ reloadDocument();
601
962
  return;
602
963
  }
603
964
  for (const target of matchedTargets) {
@@ -607,14 +968,22 @@ function applyPopStateNavigationTargets(requestId, previousHref, previousState,
607
968
  return;
608
969
  }
609
970
  saveScrollPosition(previousHref);
610
- commitNavigationRoots(requestId, pathname, href, matchedTargets, () => {}, () => applyHistoryScroll(href, state), () => {
611
- window.history.replaceState(previousState, "", previousHref);
612
- syncRegisteredRouteSnapshot();
971
+ const historyState = state;
972
+ const hasState = historyState?.askrHasState === true;
973
+ commitNavigationRoots(requestId, pathname, href, matchedTargets, {
974
+ hasState,
975
+ state: hasState ? historyState?.askrState : void 0
976
+ }, () => commitHistoryIndex(historyIndex), () => applyHistoryScroll(href, state), () => {
977
+ if (isStaleRouteRequest(requestId)) return;
978
+ if (!returnToRenderedHistoryEntry()) reloadDocument();
613
979
  });
614
980
  }
615
- /** Every root joins one transaction. History remains after lifecycle work,
616
- * so a navigation started by that work can supersede this destination. */
617
- function commitNavigationRoots(requestId, pathname, href, targets, updateHistory, updateScroll, restoreHistory) {
981
+ /**
982
+ * Render every destination root, then commit all of them or none. Lifecycle
983
+ * work of the committed destination runs before history is updated, so a
984
+ * navigation it starts supersedes this one.
985
+ */
986
+ function commitNavigationRoots(requestId, pathname, href, targets, locationState, updateHistory, updateScroll, restoreHistory) {
618
987
  const previousPathname = getCurrentPathname();
619
988
  const previousHref = getCurrentHref();
620
989
  const roots = targets.map((target) => {
@@ -622,21 +991,13 @@ function commitNavigationRoots(requestId, pathname, href, targets, updateHistory
622
991
  return {
623
992
  target,
624
993
  replaceLifetime,
625
- previousPathname: target.app.pathname,
626
- previousHref: target.app.href,
627
- prepared: prepareNavigationRoot(target, href, replaceLifetime)
994
+ prepared: prepareNavigationRoot(target, href, replaceLifetime, locationState)
628
995
  };
629
996
  });
630
- const transaction = beginCommitTransaction();
631
- let completionFailure;
632
- registerCommitParticipant({
633
- rollback() {
997
+ try {
998
+ const rollback = () => {
634
999
  const errors = [];
635
1000
  for (let index = roots.length - 1; index >= 0; index--) errors.push(...roots[index].prepared.rollback());
636
- for (const root of roots) {
637
- root.target.app.pathname = root.previousPathname;
638
- root.target.app.href = root.previousHref;
639
- }
640
1001
  setCurrentRouteLocation(previousPathname, previousHref);
641
1002
  try {
642
1003
  restoreHistory?.();
@@ -644,48 +1005,48 @@ function commitNavigationRoots(requestId, pathname, href, targets, updateHistory
644
1005
  errors.push(error);
645
1006
  }
646
1007
  reportRouteCleanupErrors(errors);
647
- },
648
- settle() {
649
- const errors = [];
650
- for (const root of roots) errors.push(...root.prepared.retire());
651
- reportRouteCleanupErrors(errors);
652
- },
653
- complete() {
654
- if (isStaleRouteRequest(requestId)) return;
655
- try {
656
- updateHistory();
657
- setCurrentRouteLocation(pathname, href);
658
- syncRegisteredRouteSnapshot();
659
- reconcileNavigationMetadata(targets);
660
- updateScroll();
661
- } catch (error) {
662
- completionFailure = { error };
663
- throw error;
664
- } finally {
665
- for (const root of roots) root.prepared.publish();
666
- }
1008
+ };
1009
+ try {
1010
+ for (const replaceLifetime of [true, false]) for (const root of roots) if (root.replaceLifetime === replaceLifetime) root.prepared.apply();
1011
+ } catch (error) {
1012
+ rollback();
1013
+ logger.error("[Askr] navigation failed:", error);
1014
+ throw error;
667
1015
  }
668
- });
669
- try {
670
- for (const replaceLifetime of [true, false]) {
671
- for (const root of roots) {
672
- if (isStaleRouteRequest(requestId)) return;
673
- if (root.replaceLifetime === replaceLifetime) root.prepared.apply();
1016
+ if (isStaleRouteRequest(requestId)) {
1017
+ rollback();
1018
+ return;
1019
+ }
1020
+ const committedFailures = [];
1021
+ for (const root of roots) {
1022
+ const result = root.prepared.publish();
1023
+ if (result.aborted) {
1024
+ rollback();
1025
+ const failure = result.errors[0];
1026
+ logger.error("[Askr] navigation failed:", failure);
1027
+ throw failure;
674
1028
  }
675
- if (isStaleRouteRequest(requestId)) return;
676
- flushRuntimeScheduler();
677
- if (isStaleRouteRequest(requestId)) return;
1029
+ committedFailures.push(...result.errors);
1030
+ syncAppRegistrationLocation(root.target.app, pathname, href);
1031
+ }
1032
+ for (const failure of committedFailures) reportUncaughtErrorLater(failure);
1033
+ const retired = [];
1034
+ for (const root of roots) retired.push(...root.prepared.retire());
1035
+ if (retired.length) reportUncaughtErrorLater(retired.length === 1 ? retired[0] : new AggregateError(retired, "Route cleanup failed"));
1036
+ flushSync();
1037
+ if (isStaleRouteRequest(requestId)) return;
1038
+ try {
1039
+ updateHistory();
1040
+ setCurrentRouteLocation(pathname, href);
1041
+ syncRegisteredRouteSnapshot();
1042
+ reconcileNavigationMetadata(targets);
1043
+ updateScroll();
1044
+ } catch (error) {
1045
+ logger.error("[Askr] navigation failed:", error);
1046
+ throw error;
678
1047
  }
679
- registerCommitParticipant({ publish() {
680
- for (const root of roots) syncAppRegistrationLocation(root.target.app, pathname, href);
681
- } });
682
- commitTransaction(transaction);
683
- if (completionFailure) throw completionFailure.error;
684
- } catch (error) {
685
- logger.error("[Askr] navigation failed:", error);
686
- throw error;
687
1048
  } finally {
688
- discardTransaction(transaction);
1049
+ for (const root of roots) root.prepared.complete();
689
1050
  }
690
1051
  }
691
1052
  //#endregion
@@ -708,29 +1069,43 @@ function ensureNavigationRegistryHost() {
708
1069
  navigationRegistryHosted = true;
709
1070
  configureNavigationRegistryHost({ cancelRouteRequests });
710
1071
  }
711
- /** Navigate the client-side router to `path` using the History API. */
712
- function navigate(path, options = {}) {
1072
+ /**
1073
+ * Navigate the client-side router using the History API.
1074
+ *
1075
+ * A string is a logical path: a root-relative path gains the registry
1076
+ * `basePath`. A typed destination from `to()` already carries its public href.
1077
+ * A target on another origin is handed to the browser.
1078
+ */
1079
+ function navigate(target, options = {}) {
1080
+ navigateToPublicHref(typeof target === "string" ? addLogicalRouteBasePath(target, getActiveRouteBasePath()) : target.href, options);
1081
+ }
1082
+ /** Navigate to a public (already mounted) href without adding the `basePath`. */
1083
+ function navigateToPublicHref(href, options = {}) {
713
1084
  ensureNavigationRegistryHost();
714
1085
  if (typeof window === "undefined") return;
715
1086
  prepareNavigationFocus();
716
- const targetPath = addRouteBasePath(path, getActiveRouteBasePath());
717
- const initialTarget = parseTargetUrl(targetPath);
1087
+ cancelHistoryReturn();
1088
+ const initialTarget = parseTargetUrl(href);
718
1089
  const redirectState = {
719
1090
  redirects: 0,
720
1091
  visited: /* @__PURE__ */ new Set([`${initialTarget.pathname}${initialTarget.search}${initialTarget.hash}`])
721
1092
  };
722
- if (isRuntimeSchedulerExecuting()) {
1093
+ if (isFlushing()) {
723
1094
  queueMicrotask(() => {
724
- navigateWithRedirectState(targetPath, options, redirectState);
1095
+ navigateWithRedirectState(href, options, redirectState);
725
1096
  });
726
1097
  return;
727
1098
  }
728
- navigateWithRedirectState(targetPath, options, redirectState);
1099
+ navigateWithRedirectState(href, options, redirectState);
729
1100
  }
730
1101
  function navigateWithRedirectState(path, options, redirectState) {
731
1102
  if (typeof window === "undefined") return;
732
1103
  const request = beginRouteRequest();
733
- const target = parseTargetUrl(path);
1104
+ const target = parseNavigationTarget(path);
1105
+ if (!isCurrentOrigin(target)) {
1106
+ loadDocument(target.href, getNavigationHistoryMode(options));
1107
+ return;
1108
+ }
734
1109
  const pathname = target.pathname;
735
1110
  const href = `${target.pathname}${target.search}${target.hash}`;
736
1111
  const resolvedTargets = resolveNavigationTargetsForApps(pathname, href, request.signal);
@@ -751,15 +1126,20 @@ function navigateWithRedirectState(path, options, redirectState) {
751
1126
  applyNavigationTargets(request.id, path, options, redirectState, pathname, href, resolvedTargets, navigateWithRedirectState);
752
1127
  }
753
1128
  function handlePopState(event) {
1129
+ if (consumeHistoryReturn(event.state)) {
1130
+ syncRegisteredRouteSnapshot();
1131
+ return;
1132
+ }
754
1133
  beginHistoryFocusRestoration();
755
1134
  const request = beginRouteRequest();
756
1135
  const previousHref = getCurrentHref();
1136
+ const historyIndex = landOnHistoryEntry(event.state);
757
1137
  const pathname = window.location.pathname;
758
1138
  const href = `${window.location.pathname}${window.location.search}${window.location.hash}`;
759
1139
  if (!hasRegisteredApps()) return;
760
1140
  const applyResolved = (targets) => {
761
1141
  try {
762
- applyPopStateNavigationTargets(request.id, previousHref, { path: previousHref }, pathname, href, event.state, targets, navigate);
1142
+ applyPopStateNavigationTargets(request.id, previousHref, historyIndex, pathname, href, event.state, targets, navigateToPublicHref);
763
1143
  } catch (error) {
764
1144
  logger.error("[Askr] popstate navigation failed:", error);
765
1145
  }
@@ -771,6 +1151,7 @@ function handlePopState(event) {
771
1151
  }, (error) => {
772
1152
  if (isStaleRouteRequest(request.id)) return;
773
1153
  logger.error("[Askr] popstate navigation failed:", error);
1154
+ if (!returnToRenderedHistoryEntry()) reloadDocument();
774
1155
  });
775
1156
  return;
776
1157
  }
@@ -778,11 +1159,13 @@ function handlePopState(event) {
778
1159
  }
779
1160
  function initializeNavigation() {
780
1161
  ensureNavigationRegistryHost();
781
- if (typeof window === "undefined" || navigationInitialized) return;
1162
+ if (typeof window === "undefined") return;
1163
+ if (getRegisteredAppsSnapshot().length <= 1) initializeHistoryIndex();
1164
+ if (navigationInitialized) return;
782
1165
  navigationInitialized = true;
783
1166
  window.addEventListener("popstate", handlePopState);
784
1167
  document.addEventListener("pointerdown", captureNavigationFocus, true);
785
1168
  document.addEventListener("click", releaseNavigationFocusCapture);
786
1169
  }
787
1170
  //#endregion
788
- export { configureRootUpdateHost as _, serializeRouteMeta as a, getCurrentHref as c, parseTargetUrl as d, registerAppInstance as f, unregisterAppInstance as g, syncRegisteredRouteSnapshot as h, resolveRouteMeta as i, getRegisteredAppsSnapshot as l, syncAppRegistrationLocation as m, navigate as n, configureScrollRestoration as o, setCurrentRouteLocation as p, reconcileRouteMeta as r, saveScrollPosition as s, initializeNavigation as t, getWindowHref$1 as u };
1171
+ export { currentRoute as C, clearStagedAppRenderRouteLocation as D, configureRootUpdateHost as E, createAppRenderRuntime as O, unregisterAppInstance as S, setServerLocation as T, registerAppInstance as _, nextHistoryIndex as a, syncRegisteredRouteSnapshot as b, reconcileRouteMeta as c, configureScrollRestoration as d, saveScrollPosition as f, parseTargetUrl as g, getWindowHref$1 as h, commitHistoryIndex as i, stageAppRenderRouteLocation as k, resolveRouteMeta as l, getRegisteredAppsSnapshot as m, navigate as n, readHistoryIndex as o, getCurrentHref as p, navigateToPublicHref as r, loadDocument as s, initializeNavigation as t, serializeRouteMeta as u, setCurrentRouteLocation as v, onRouteChange as w, toDocumentUrl as x, syncAppRegistrationLocation as y };