@askrjs/askr 0.2.3 → 0.3.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.
Files changed (117) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/access-CEA2pOry.js +1046 -0
  3. package/dist/actions/index.d.ts +16 -2
  4. package/dist/actions/index.js +5 -5
  5. package/dist/{activity-ZxJXI9hF.js → activity-oy2YFTFF.js} +22 -16
  6. package/dist/benchmark.js +8 -9
  7. package/dist/boot/index.d.ts +21 -9
  8. package/dist/boot/index.js +1 -1
  9. package/dist/{boot-D4GaLA0d.js → boot-BMBMWUGI.js} +680 -440
  10. package/dist/capabilities-Bbf0mlp9.js +42 -0
  11. package/dist/components/index.d.ts +11 -8
  12. package/dist/components/index.js +1 -1
  13. package/dist/{compose-ref-Bfaf4kUC.js → compose-ref-v0qGlHoN.js} +7 -1
  14. package/dist/control/index.d.ts +20 -2
  15. package/dist/control/index.js +1 -1
  16. package/dist/{control-57x3wa4J.js → control-BR_mrX__.js} +4 -4
  17. package/dist/core.d.ts +163 -0
  18. package/dist/csp-nonce-BfFWcd0w.js +173 -0
  19. package/dist/data/index.d.ts +74 -2
  20. package/dist/data/index.js +3 -3
  21. package/dist/{data-B7XCDeC8.js → data-2edW0jhF.js} +133 -138
  22. package/dist/{data-runtime-7t_Y0iv3.js → data-runtime-CxoY98lX.js} +22 -15
  23. package/dist/{deferred-BELyvMsC.js → deferred-CbgNhf67.js} +5 -5
  24. package/dist/dom-renderer.d.ts +77 -0
  25. package/dist/domains/actions.d.ts +70 -0
  26. package/dist/domains/component.d.ts +119 -0
  27. package/dist/domains/context.d.ts +73 -0
  28. package/dist/domains/control.d.ts +328 -0
  29. package/dist/domains/data.d.ts +461 -0
  30. package/dist/domains/diagnostics.d.ts +21 -0
  31. package/dist/domains/lifecycle.d.ts +112 -0
  32. package/dist/domains/refs.d.ts +18 -0
  33. package/dist/domains/renderer.d.ts +127 -0
  34. package/dist/domains/routing.d.ts +501 -0
  35. package/dist/domains/scheduler.d.ts +79 -0
  36. package/dist/domains/server.d.ts +112 -0
  37. package/dist/domains/state.d.ts +129 -0
  38. package/dist/domains/telemetry.d.ts +29 -0
  39. package/dist/eager-control.d.ts +8 -0
  40. package/dist/{effect-4LwBwV58.js → effect-C3Zsc1k6.js} +14 -41
  41. package/dist/{types-DkVJhVs9.d.ts → elements.d.ts} +84 -42
  42. package/dist/{for-internal-DeoSg_AV.js → for-state-BDykeOJL.js} +623 -511
  43. package/dist/foundations/icon/index.d.ts +67 -16
  44. package/dist/foundations/icon/index.js +1 -1
  45. package/dist/foundations/index.d.ts +26 -3
  46. package/dist/foundations/index.js +1 -1
  47. package/dist/foundations/interactions/index.d.ts +73 -34
  48. package/dist/foundations/interactions/index.js +1 -1
  49. package/dist/foundations/state/index.d.ts +12 -5
  50. package/dist/foundations/state/index.js +1 -1
  51. package/dist/foundations/structures/index.d.ts +44 -3
  52. package/dist/foundations/structures/index.js +1 -1
  53. package/dist/foundations/utilities/index.d.ts +34 -3
  54. package/dist/foundations/utilities/index.js +1 -1
  55. package/dist/fx/index.d.ts +72 -25
  56. package/dist/fx/index.js +8 -8
  57. package/dist/index.d.ts +121 -4
  58. package/dist/index.js +316 -47
  59. package/dist/{component-internal-Dl02WQKS.js → instance-VAFYVJOa.js} +495 -283
  60. package/dist/{interactions-D102JxBR.js → interactions-CTeACdT_.js} +1 -1
  61. package/dist/jsx-dev-runtime.d.ts +51 -11
  62. package/dist/jsx-globals.d.ts +2 -0
  63. package/dist/jsx-runtime-CPJXAOBF.js +43 -0
  64. package/dist/jsx-runtime.d.ts +17 -3
  65. package/dist/jsx-runtime.js +2 -42
  66. package/dist/jsx.d.ts +108 -0
  67. package/dist/{manifest-D6rfj_jC.js → manifest-D1P3UNWV.js} +4 -9
  68. package/dist/{navigate-C46GtScG.js → navigate-prC5V8XV.js} +204 -476
  69. package/dist/{navigate-wel1e6cU.d.ts → navigation.d.ts} +10 -8
  70. package/dist/{readable-CEcteIDE.js → notify-DxxuDPeY.js} +96 -34
  71. package/dist/{query-registry-Bua3cDyb.js → query-registry-LrYpQsDJ.js} +1 -1
  72. package/dist/{compose-ref-DOJHoW5X.d.ts → refs.d.ts} +11 -7
  73. package/dist/{ssr-BaC5qHEw.js → render-resolved-BC0W7_5b.js} +165 -558
  74. package/dist/{renderer-BhXofszH.js → renderer-CT0HeY58.js} +6866 -5715
  75. package/dist/{resolution-t8J0yNaU.js → resolution-3DskUkjG.js} +189 -180
  76. package/dist/{resource-operation-B8q-QRUx.js → resource-18-sFRl9.js} +98 -58
  77. package/dist/resources/index.d.ts +64 -46
  78. package/dist/resources/index.js +215 -9
  79. package/dist/route-activity.d.ts +21 -0
  80. package/dist/{route-matching-Bo6UqG-5.js → route-matching-CUzZ31gT.js} +68 -82
  81. package/dist/router/index.d.ts +378 -79
  82. package/dist/router/index.js +9 -9
  83. package/dist/router-internal-7nuNEHfG.js +36 -0
  84. package/dist/scope-Cq_iYbTR.js +403 -0
  85. package/dist/{selector-C7rIpOnN.js → selector-BSPVkjKP.js} +41 -41
  86. package/dist/{shared-CGrx2AOL.js → shared-kU8yvryp.js} +3 -6
  87. package/dist/{snapshot-source-BB42xgwb.js → snapshot-source-An4iKajG.js} +1 -1
  88. package/dist/ssg/index.d.ts +51 -15
  89. package/dist/ssg/index.js +269 -209
  90. package/dist/ssr/index.d.ts +121 -68
  91. package/dist/ssr/index.js +2 -1
  92. package/dist/ssr-CiNm6Vzl.js +393 -0
  93. package/dist/{ssr-DSHd_Q9K.js → ssr-EPjwJH5R.js} +5 -1
  94. package/dist/{state-BzhOW2TX.js → state-BXJfudq1.js} +10 -15
  95. package/dist/{structures-BwfeO4Iz.js → structures-CjEzfj4h.js} +37 -57
  96. package/dist/{index-r5THqzPN.d.ts → structures.d.ts} +57 -36
  97. package/dist/testing/index.d.ts +125 -24
  98. package/dist/testing/index.js +26 -62
  99. package/dist/timer-C7uqnJVt.js +85 -0
  100. package/dist/{index-B1gu0_m0.d.ts → utilities.d.ts} +24 -13
  101. package/dist/{verify-hydration-_Q6avFp0.js → verify-hydration-ikaxotf7.js} +8 -5
  102. package/package.json +13 -15
  103. package/dist/access-Rr7CMa06.js +0 -507
  104. package/dist/activity-CAJ5eho-.d.ts +0 -14
  105. package/dist/benchmark.d.ts +0 -26
  106. package/dist/cleanup-DsrbV-rr.js +0 -535
  107. package/dist/component-cleanup-DjpwO9WX.js +0 -143
  108. package/dist/component-scope-4I3yEMkb.js +0 -195
  109. package/dist/control-DdFw-Caz.d.ts +0 -8
  110. package/dist/csp-nonce-C9OuML0U.js +0 -72
  111. package/dist/css-BuPaTOfe.js +0 -70
  112. package/dist/fastlane-Buz1Rvlr.js +0 -166
  113. package/dist/index-DSqXWc77.d.ts +0 -1402
  114. package/dist/jsx-runtime-BTzDdBJ_.d.ts +0 -2
  115. package/dist/jsx-runtime-Cdd6Gkb0.d.ts +0 -38
  116. package/dist/lifecycle-batch-Be-CLDjV.js +0 -253
  117. package/dist/lifecycle-operations-C5m03NQc.js +0 -195
@@ -1,15 +1,19 @@
1
- import { S as isProductionEnvironment, r as flushRuntimeScheduler, x as isDevelopmentEnvironment, y as logger } from "./access-Rr7CMa06.js";
2
- import { n as Fragment, t as ELEMENT_TYPE } from "./jsx-DRS-qjiN.js";
3
- import { r as executeComponent } from "./component-internal-Dl02WQKS.js";
4
- import { t as cleanupReadableSubscriptionSources } from "./readable-CEcteIDE.js";
5
- import { n as cleanupComponentGeneration, t as cleanupComponent } from "./component-cleanup-DjpwO9WX.js";
6
- import { i as drainLifecycleCommitErrors, p as isPromiseLike, r as discardLifecycleCommitBatch, s as flushLifecycleCommitBatch, t as beginLifecycleCommitBatch } from "./lifecycle-batch-Be-CLDjV.js";
7
- import { _ as teardownNodeSubtree } from "./cleanup-DsrbV-rr.js";
8
- import { t as CspNonceScope } from "./csp-nonce-C9OuML0U.js";
9
- import { I as lockRouteRegistration, o as addRouteBasePath, r as computeRouteActivityMatches, y as getActiveRouteBasePath } from "./route-matching-Bo6UqG-5.js";
10
- import { a as resolveRouteRequest, i as hasRouteRenderData, n as getRouteRenderContext, r as getRouteRenderData, u as withoutRouteHydrationMetadata } from "./resolution-t8J0yNaU.js";
11
- import { a as clearStagedAppRenderRouteLocation, i as syncCurrentRouteSnapshot, o as createAppRenderRuntime, s as stageAppRenderRouteLocation } from "./activity-ZxJXI9hF.js";
12
- import { r as getDefaultPortalHost, t as clearRegisteredDefaultPortalForInstance } from "./default-portal-runtime-DvMvWJVp.js";
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";
7
+ //#region src/common/root-update.ts
8
+ let rootUpdateHost;
9
+ function configureRootUpdateHost(host) {
10
+ rootUpdateHost = host;
11
+ }
12
+ function prepareRootUpdate(root, input) {
13
+ if (!rootUpdateHost) throw new Error("[Askr] Root update host is not configured.");
14
+ return rootUpdateHost.prepare(root, input);
15
+ }
16
+ //#endregion
13
17
  //#region src/router/navigation-registry.ts
14
18
  let currentInstance = null;
15
19
  let currentPathname = "/";
@@ -112,6 +116,58 @@ const DEFAULT_SCROLL_RESTORATION = {
112
116
  };
113
117
  let scrollRestorationOptions = { ...DEFAULT_SCROLL_RESTORATION };
114
118
  const scrollPositions = /* @__PURE__ */ new Map();
119
+ let capturedActivationFocus;
120
+ let pendingNavigationFocus;
121
+ let focusAtHistoryNavigationStart = null;
122
+ function readFocusDescriptor() {
123
+ if (typeof document === "undefined" || !(document.activeElement instanceof HTMLElement)) return;
124
+ const active = document.activeElement;
125
+ for (const attribute of [
126
+ "data-askr-focus-key",
127
+ "id",
128
+ "name",
129
+ "aria-label"
130
+ ]) {
131
+ const value = active.getAttribute(attribute);
132
+ if (value) return {
133
+ attribute,
134
+ value
135
+ };
136
+ }
137
+ if ("labels" in active) {
138
+ const label = Array.from(active.labels ?? []).map((element) => element.textContent?.trim()).find(Boolean);
139
+ if (label) return {
140
+ attribute: "label",
141
+ value: label
142
+ };
143
+ }
144
+ }
145
+ function clearCapturedNavigationFocus() {
146
+ capturedActivationFocus = void 0;
147
+ }
148
+ function isPrimaryPointerActivation(event) {
149
+ if (event instanceof MouseEvent && (event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey)) return false;
150
+ return true;
151
+ }
152
+ /** Stage entry-owned focus for the duration of one primary pointer activation. */
153
+ function captureNavigationFocus(event) {
154
+ clearCapturedNavigationFocus();
155
+ if (!isPrimaryPointerActivation(event)) return;
156
+ capturedActivationFocus = readFocusDescriptor();
157
+ }
158
+ /** Release pointer activation state after its click has finished dispatching. */
159
+ function releaseNavigationFocusCapture() {
160
+ clearCapturedNavigationFocus();
161
+ }
162
+ /** Preserve the active control before route content can be replaced. */
163
+ function prepareNavigationFocus() {
164
+ pendingNavigationFocus = capturedActivationFocus ?? readFocusDescriptor();
165
+ clearCapturedNavigationFocus();
166
+ }
167
+ /** Mark the active element so restoration can avoid overwriting focus moved during route commit. */
168
+ function beginHistoryFocusRestoration() {
169
+ focusAtHistoryNavigationStart = typeof document === "undefined" ? null : document.activeElement;
170
+ }
115
171
  function getWindowHref() {
116
172
  if (typeof window === "undefined") return "/";
117
173
  return `${window.location.pathname}${window.location.search}${window.location.hash}`;
@@ -146,8 +202,10 @@ function writeHistoryScrollPosition(href, position) {
146
202
  window.history.replaceState({
147
203
  ...state,
148
204
  path: href,
149
- scroll: position
205
+ scroll: position,
206
+ focus: pendingNavigationFocus ?? readFocusDescriptor()
150
207
  }, "", href);
208
+ pendingNavigationFocus = void 0;
151
209
  }
152
210
  function saveScrollPosition(href) {
153
211
  if (!scrollRestorationOptions.enabled || typeof window === "undefined") return;
@@ -194,6 +252,12 @@ function applyHistoryScroll(href, state) {
194
252
  x: 0,
195
253
  y: 0
196
254
  });
255
+ const focus = state && typeof state === "object" && state.focus && typeof state.focus === "object" ? state.focus : void 0;
256
+ if (focus && (focus.attribute === "data-askr-focus-key" || focus.attribute === "id" || focus.attribute === "name" || focus.attribute === "aria-label" || focus.attribute === "label") && typeof focus.value === "string" && typeof document !== "undefined" && (document.activeElement === document.body || document.activeElement === document.documentElement || document.activeElement === focusAtHistoryNavigationStart)) {
257
+ const target = focus.attribute === "label" ? Array.from(document.querySelectorAll("input,select,textarea,button")).find((element) => "labels" in element && Array.from(element.labels ?? []).some((label) => label.textContent?.trim() === focus.value)) : Array.from(document.querySelectorAll(`[${focus.attribute}]`)).find((element) => element.getAttribute(focus.attribute) === focus.value);
258
+ if (target?.isConnected && !target.hasAttribute("disabled") && target.getAttribute("aria-hidden") !== "true" && target.getClientRects().length > 0) target.focus({ preventScroll: true });
259
+ }
260
+ focusAtHistoryNavigationStart = null;
197
261
  }
198
262
  function configureScrollRestoration(options) {
199
263
  scrollRestorationOptions = normalizeScrollRestorationOptions(options);
@@ -362,6 +426,11 @@ function beginRouteRequest() {
362
426
  function isStaleRouteRequest(requestId) {
363
427
  return requestId !== activeRouteRequestId;
364
428
  }
429
+ /**
430
+ * @internal Teardown-only cancellation. Prefer aborting through the signal
431
+ * returned by `beginRouteRequest()`; this exists for registry teardown, where
432
+ * no request owns the abort.
433
+ */
365
434
  function cancelRouteRequests() {
366
435
  activeRouteRequestId += 1;
367
436
  activeRouteRequestController?.abort();
@@ -419,29 +488,6 @@ function createDeniedResolvedRoute(status) {
419
488
  function bindResolvedRouteHandler(resolved) {
420
489
  return () => resolved.handler(resolved.params);
421
490
  }
422
- function wrapRootRouteHandler(componentFn, cspNonce) {
423
- const wrappedFn = (props, ctx) => {
424
- const out = componentFn(props, ctx);
425
- if (isPromiseLike(out)) throw new Error("Async components are not supported. Components must return synchronously.");
426
- const portalVNode = {
427
- $$typeof: ELEMENT_TYPE,
428
- type: getDefaultPortalHost(),
429
- props: { __askrAutoDefaultPortal: true },
430
- key: "__default_portal"
431
- };
432
- const root = {
433
- $$typeof: ELEMENT_TYPE,
434
- type: Fragment,
435
- props: { children: out === void 0 || out === null ? [portalVNode] : [out, portalVNode] }
436
- };
437
- return cspNonce === void 0 ? root : CspNonceScope({
438
- value: cspNonce,
439
- children: root
440
- });
441
- };
442
- Object.defineProperty(wrappedFn, "name", { value: componentFn.name || "Component" });
443
- return wrappedFn;
444
- }
445
491
  function flattenLifecycleErrors(error, result) {
446
492
  if (error instanceof AggregateError) {
447
493
  for (const nested of error.errors) flattenLifecycleErrors(nested, result);
@@ -456,255 +502,6 @@ function reportRouteCleanupErrors(errors) {
456
502
  for (const cleanupError of flattened.length > 0 ? flattened : [error]) logger.error("[Askr] route cleanup failed:", cleanupError);
457
503
  }
458
504
  }
459
- function captureRouteDom(root) {
460
- if (!root) return null;
461
- const nodes = [];
462
- const visit = (node) => {
463
- nodes.push({
464
- node,
465
- children: Array.from(node.childNodes),
466
- attributes: node instanceof Element ? Array.from(node.attributes).map((attribute) => [attribute.name, attribute.value]) : null,
467
- nodeValue: node.nodeValue
468
- });
469
- for (const child of Array.from(node.childNodes)) visit(child);
470
- };
471
- visit(root);
472
- return {
473
- root,
474
- nodes
475
- };
476
- }
477
- function collectProvisionalRouteNodes(snapshot) {
478
- const originalNodes = new Set(snapshot.nodes.map((entry) => entry.node));
479
- const provisional = [];
480
- const visit = (node) => {
481
- if (!originalNodes.has(node)) {
482
- provisional.push(node);
483
- return;
484
- }
485
- for (const child of Array.from(node.childNodes)) visit(child);
486
- };
487
- for (const child of Array.from(snapshot.root.childNodes)) visit(child);
488
- return provisional;
489
- }
490
- function restoreRouteDom(snapshot) {
491
- if (!snapshot) return [];
492
- const errors = [];
493
- for (const node of collectProvisionalRouteNodes(snapshot)) try {
494
- teardownNodeSubtree(node, { strict: true });
495
- } catch (error) {
496
- errors.push(error);
497
- }
498
- for (const entry of snapshot.nodes) {
499
- const { node, attributes } = entry;
500
- try {
501
- if (attributes && node instanceof Element) {
502
- const expected = new Map(attributes);
503
- for (const attribute of Array.from(node.attributes)) if (!expected.has(attribute.name)) node.removeAttribute(attribute.name);
504
- for (const [name, value] of attributes) if (node.getAttribute(name) !== value) node.setAttribute(name, value);
505
- } else if (!(node instanceof Element)) node.nodeValue = entry.nodeValue;
506
- if (node instanceof Element || node instanceof DocumentFragment) node.replaceChildren(...entry.children);
507
- } catch (error) {
508
- errors.push(error);
509
- }
510
- }
511
- return errors;
512
- }
513
- function collectHostInstances(root) {
514
- const instances = [];
515
- const seen = /* @__PURE__ */ new Set();
516
- const pending = [root];
517
- while (pending.length > 0) {
518
- const node = pending.pop();
519
- const host = node;
520
- for (const instance of host.__ASKR_INSTANCES ?? []) if (!seen.has(instance)) {
521
- seen.add(instance);
522
- instances.push(instance);
523
- }
524
- if (host.__ASKR_INSTANCE && !seen.has(host.__ASKR_INSTANCE)) {
525
- seen.add(host.__ASKR_INSTANCE);
526
- instances.push(host.__ASKR_INSTANCE);
527
- }
528
- for (let child = node.lastChild; child; child = child.previousSibling) pending.push(child);
529
- }
530
- return instances;
531
- }
532
- function captureDeferredRouteCleanup(instance) {
533
- return {
534
- instance,
535
- previousOwnershipGeneration: instance._ownershipGeneration,
536
- previousFn: instance.fn,
537
- previousProps: instance.props,
538
- previousExpectedStateIndices: instance.expectedStateIndices,
539
- previousFirstRenderComplete: instance.firstRenderComplete,
540
- previousStateIndexCheck: instance.stateIndexCheck,
541
- previousErrorBoundaryState: instance.errorBoundaryState,
542
- previousTarget: instance.target,
543
- previousDom: captureRouteDom(instance.target),
544
- previousStateValues: instance.stateValues,
545
- previousCleanupFns: instance.cleanupFns,
546
- previousOwnedChildScopes: instance._ownedChildScopes,
547
- previousAbortController: instance.abortController,
548
- previousMountOperations: instance.mountOperations,
549
- previousCommitOperations: instance.commitOperations,
550
- previousLifecycleSlots: instance.lifecycleSlots,
551
- previousLifecycleGeneration: instance.lifecycleGeneration,
552
- previousEvaluationGeneration: instance.evaluationGeneration,
553
- previousHasPendingUpdate: instance.hasPendingUpdate,
554
- previousNotifyUpdate: instance.notifyUpdate,
555
- previousPlaceholder: instance._placeholder,
556
- previousMounted: instance.mounted,
557
- previousCurrentRenderToken: instance._currentRenderToken,
558
- previousLastRenderToken: instance.lastRenderToken,
559
- previousPendingReadSources: instance._pendingReadSources,
560
- previousPendingReadSourceVersions: instance._pendingReadSourceVersions,
561
- previousLastReadSources: instance._lastReadSources,
562
- previousReaderEntries: Array.from(instance._lastReadSources ?? []).map((source) => ({
563
- source,
564
- reader: source._readers?.get(instance)
565
- })),
566
- previousAppRenderRuntime: instance._appRenderRuntime,
567
- previousInstances: instance.target ? collectHostInstances(instance.target) : [],
568
- previousDisposed: false
569
- };
570
- }
571
- function restoreDeferredRouteInstance(deferred) {
572
- const instance = deferred.instance;
573
- const errors = [];
574
- const activeGeneration = instance._ownershipGeneration;
575
- const activeReadSources = instance._lastReadSources;
576
- if (activeGeneration !== deferred.previousOwnershipGeneration) {
577
- try {
578
- cleanupReadableSubscriptionSources(instance, activeReadSources, activeGeneration);
579
- } catch (error) {
580
- errors.push(error);
581
- }
582
- instance._lastReadSources = void 0;
583
- try {
584
- cleanupComponent(instance);
585
- } catch (error) {
586
- errors.push(error);
587
- }
588
- }
589
- errors.push(...restoreRouteDom(deferred.previousDom));
590
- instance.fn = deferred.previousFn;
591
- instance._ownershipGeneration = deferred.previousOwnershipGeneration;
592
- instance.props = deferred.previousProps;
593
- instance.expectedStateIndices = deferred.previousExpectedStateIndices;
594
- instance.firstRenderComplete = deferred.previousFirstRenderComplete;
595
- instance.stateIndexCheck = deferred.previousStateIndexCheck;
596
- instance.errorBoundaryState = deferred.previousErrorBoundaryState;
597
- instance.target = deferred.previousTarget;
598
- instance.stateValues = deferred.previousStateValues;
599
- instance.cleanupFns = deferred.previousCleanupFns;
600
- instance._ownedChildScopes = deferred.previousOwnedChildScopes;
601
- instance.abortController = deferred.previousAbortController;
602
- instance.mountOperations = deferred.previousMountOperations;
603
- instance.commitOperations = deferred.previousCommitOperations;
604
- instance.lifecycleSlots = deferred.previousLifecycleSlots;
605
- instance.lifecycleGeneration = deferred.previousLifecycleGeneration;
606
- instance.evaluationGeneration = deferred.previousEvaluationGeneration;
607
- instance.hasPendingUpdate = deferred.previousHasPendingUpdate;
608
- instance.notifyUpdate = deferred.previousNotifyUpdate;
609
- instance._placeholder = deferred.previousPlaceholder;
610
- instance.mounted = deferred.previousMounted;
611
- instance._currentRenderToken = deferred.previousCurrentRenderToken;
612
- instance.lastRenderToken = deferred.previousLastRenderToken;
613
- instance._pendingReadSources = deferred.previousPendingReadSources;
614
- instance._pendingReadSourceVersions = deferred.previousPendingReadSourceVersions;
615
- instance._lastReadSources = deferred.previousLastReadSources;
616
- instance._appRenderRuntime = deferred.previousAppRenderRuntime;
617
- const previousSources = new Set(deferred.previousReaderEntries.map(({ source }) => source));
618
- for (const source of activeReadSources ?? []) {
619
- if (previousSources.has(source)) continue;
620
- if ((source._readers?.get(instance))?.generation === activeGeneration) {
621
- if (source._readers?.delete(instance) && false);
622
- }
623
- }
624
- for (const { source, reader } of deferred.previousReaderEntries) {
625
- if (!reader) {
626
- if (source._readers?.delete(instance) && false);
627
- continue;
628
- }
629
- let readers = source._readers;
630
- if (!readers) {
631
- readers = /* @__PURE__ */ new Map();
632
- source._readers = readers;
633
- }
634
- readers.has(instance);
635
- readers.set(instance, reader);
636
- }
637
- return errors;
638
- }
639
- function restoreDeferredRouteInstances(deferredCleanups) {
640
- const errors = [];
641
- for (let index = deferredCleanups.length - 1; index >= 0; index -= 1) errors.push(...restoreDeferredRouteInstance(deferredCleanups[index]));
642
- if (errors.length > 0) reportRouteCleanupErrors(errors);
643
- }
644
- function cleanupDeferredRouteInstance(deferred) {
645
- const errors = [];
646
- const instance = deferred.instance;
647
- if (!deferred.previousDisposed) {
648
- deferred.previousDisposed = true;
649
- try {
650
- cleanupComponentGeneration(instance, {
651
- ownershipGeneration: deferred.previousOwnershipGeneration,
652
- cleanupFns: deferred.previousCleanupFns,
653
- ownedChildScopes: deferred.previousOwnedChildScopes,
654
- abortController: deferred.previousAbortController,
655
- readSources: deferred.previousLastReadSources
656
- });
657
- } catch (error) {
658
- errors.push(error);
659
- }
660
- }
661
- const root = instance.target;
662
- const liveInstances = root ? new Set(collectHostInstances(root)) : /* @__PURE__ */ new Set();
663
- for (let index = deferred.previousInstances.length - 1; index >= 0; index--) {
664
- const previous = deferred.previousInstances[index];
665
- if (previous === instance || liveInstances.has(previous)) continue;
666
- try {
667
- cleanupComponent(previous);
668
- } catch (error) {
669
- errors.push(error);
670
- }
671
- }
672
- return errors;
673
- }
674
- function remountResolvedRoute(instance, resolved, pathname, href, existingSnapshot) {
675
- const deferredCleanup = existingSnapshot ?? captureDeferredRouteCleanup(instance);
676
- clearRegisteredDefaultPortalForInstance(instance);
677
- instance.fn = wrapRootRouteHandler(bindResolvedRouteHandler(resolved), instance._cspNonce);
678
- instance.props = {};
679
- instance._ownershipGeneration = {};
680
- instance.stateValues = [];
681
- instance.expectedStateIndices = [];
682
- instance.firstRenderComplete = false;
683
- instance.stateIndexCheck = -1;
684
- instance.evaluationGeneration++;
685
- instance.lifecycleGeneration++;
686
- instance.notifyUpdate = null;
687
- instance.mountOperations = [];
688
- instance.commitOperations = [];
689
- instance.lifecycleSlots = [];
690
- instance.cleanupFns = [];
691
- instance._ownedChildScopes = /* @__PURE__ */ new Set();
692
- instance._placeholder = void 0;
693
- instance.hasPendingUpdate = false;
694
- instance._currentRenderToken = void 0;
695
- instance.lastRenderToken = 0;
696
- instance._pendingReadSources = void 0;
697
- instance._lastReadSources = void 0;
698
- instance.abortController = null;
699
- executeComponent(instance);
700
- setCurrentRouteLocation(pathname, href);
701
- return deferredCleanup;
702
- }
703
- function rerenderResolvedRoute(instance, pathname, href) {
704
- setCurrentRouteLocation(pathname, href);
705
- instance._enqueueRun?.();
706
- return true;
707
- }
708
505
  function resolveAppRouteRequest(app, pathname, href, signal) {
709
506
  return resolveRouteRequest(href, {
710
507
  registry: app.registry,
@@ -721,21 +518,15 @@ function getResolvedRouteHandler(resolved) {
721
518
  params: resolved.params
722
519
  };
723
520
  }
724
- function updateRouteRuntime(instance, resolved, href) {
725
- const current = instance._appRenderRuntime;
726
- const runtime = createAppRenderRuntime({
727
- framework: withoutRouteHydrationMetadata(current?.framework),
728
- dataRuntime: current?.dataRuntime,
729
- routeRegistry: current?.routeRegistry,
730
- routeAuth: current?.routeAuth,
731
- route: resolved?.kind === "render" ? getRouteRenderData(resolved) : void 0,
732
- hasRoute: resolved?.kind === "render" && hasRouteRenderData(resolved)
521
+ function prepareNavigationRoot(target, href, replaceLifetime) {
522
+ const resolved = target.resolved;
523
+ return prepareRootUpdate(target.app.instance, {
524
+ handler: bindResolvedRouteHandler(getResolvedRouteHandler(resolved)),
525
+ href,
526
+ routeData: isRenderResult(resolved) ? getRouteRenderData(resolved) : void 0,
527
+ hasRouteData: isRenderResult(resolved) && hasRouteRenderData(resolved),
528
+ replaceLifetime
733
529
  });
734
- stageAppRenderRouteLocation(runtime, href);
735
- instance._appRenderRuntime = runtime;
736
- }
737
- function clearStagedRouteLocations(targets) {
738
- for (const target of targets) clearStagedAppRenderRouteLocation(target.app.instance._appRenderRuntime);
739
530
  }
740
531
  function resolveNavigationTargetsForApps(pathname, href, signal) {
741
532
  const apps = getRegisteredAppsSnapshot();
@@ -790,94 +581,17 @@ function applyNavigationTargets(requestId, path, options, redirectState, pathnam
790
581
  return;
791
582
  }
792
583
  if (isStaleRouteRequest(requestId)) return;
793
- const samePathTargets = [];
794
- const rootsToPrepare = [];
795
- for (const target of matchedTargets) {
796
- const resolved = target.resolved;
797
- if (resolved.kind === "redirect") continue;
798
- if (pathname === target.app.pathname && isRenderResult(resolved)) {
799
- samePathTargets.push(target);
800
- continue;
801
- }
802
- rootsToPrepare.push(target);
803
- }
804
- const rollbackSnapshots = matchedTargets.map((target) => captureDeferredRouteCleanup(target.app.instance));
805
- const rollbackSnapshotByInstance = new Map(rollbackSnapshots.map((snapshot) => [snapshot.instance, snapshot]));
806
- const deferredCleanups = [];
807
- const cleanupErrors = [];
808
- const previousAppLocations = matchedTargets.map((target) => ({
809
- app: target.app,
810
- pathname: target.app.pathname,
811
- href: target.app.href
812
- }));
813
- const restoreOnExit = () => {
814
- if (!navigationBatchClosed) {
815
- discardLifecycleCommitBatch(navigationBatch);
816
- navigationBatchClosed = true;
817
- }
818
- restoreDeferredRouteInstances(rollbackSnapshots);
819
- for (const previous of previousAppLocations) {
820
- previous.app.pathname = previous.pathname;
821
- previous.app.href = previous.href;
822
- }
823
- setCurrentRouteLocation(previousPathname, previousHref);
824
- };
825
- const navigationBatch = beginLifecycleCommitBatch();
826
- let navigationBatchClosed = false;
827
- try {
828
- for (const target of rootsToPrepare) {
829
- if (isStaleRouteRequest(requestId)) {
830
- restoreOnExit();
831
- return;
832
- }
833
- updateRouteRuntime(target.app.instance, target.resolved, href);
834
- const deferredCleanup = remountResolvedRoute(target.app.instance, getResolvedRouteHandler(target.resolved), pathname, href, rollbackSnapshotByInstance.get(target.app.instance));
835
- deferredCleanups.push(deferredCleanup);
836
- }
837
- if (isStaleRouteRequest(requestId)) {
838
- restoreOnExit();
839
- return;
840
- }
841
- flushRuntimeScheduler();
842
- cleanupErrors.push(...drainLifecycleCommitErrors());
843
- if (isStaleRouteRequest(requestId)) {
844
- restoreOnExit();
845
- return;
846
- }
847
- for (const target of samePathTargets) {
848
- if (isStaleRouteRequest(requestId)) {
849
- restoreOnExit();
850
- return;
851
- }
852
- updateRouteRuntime(target.app.instance, target.resolved, href);
853
- rerenderResolvedRoute(target.app.instance, pathname, href);
854
- }
855
- flushRuntimeScheduler();
856
- cleanupErrors.push(...drainLifecycleCommitErrors());
857
- if (isStaleRouteRequest(requestId)) {
858
- restoreOnExit();
859
- return;
860
- }
861
- flushLifecycleCommitBatch(navigationBatch);
862
- navigationBatchClosed = true;
863
- cleanupErrors.push(...drainLifecycleCommitErrors());
864
- for (const target of matchedTargets) if (target.resolved && target.resolved.kind !== "redirect") syncAppRegistrationLocation(target.app, pathname, href);
865
- if (isStaleRouteRequest(requestId)) return;
584
+ commitNavigationRoots(requestId, pathname, href, matchedTargets, () => {
866
585
  saveScrollPosition(previousHref);
867
586
  const historyMethod = getNavigationHistoryMode(options) === "replace" ? "replaceState" : "pushState";
868
- window.history[historyMethod]({ path: href }, "", href);
869
- setCurrentRouteLocation(pathname, href);
870
- clearStagedRouteLocations(matchedTargets);
871
- syncRegisteredRouteSnapshot();
872
- reconcileNavigationMetadata(matchedTargets);
587
+ window.history[historyMethod]({
588
+ path: href,
589
+ askrHasState: Object.prototype.hasOwnProperty.call(options, "state"),
590
+ askrState: options.state
591
+ }, "", href);
592
+ }, () => {
873
593
  if (pathname !== previousPathname || parseTargetUrl(href).hash) applyNavigationScroll(options.scroll);
874
- for (const deferredCleanup of deferredCleanups) cleanupErrors.push(...cleanupDeferredRouteInstance(deferredCleanup));
875
- for (const error of cleanupErrors) reportRouteCleanupErrors([error]);
876
- } catch (error) {
877
- restoreOnExit();
878
- logger.error("[Askr] navigation failed:", error);
879
- throw error;
880
- }
594
+ });
881
595
  }
882
596
  function applyPopStateNavigationTargets(requestId, previousHref, previousState, pathname, href, state, targets, navigate) {
883
597
  if (isStaleRouteRequest(requestId)) return;
@@ -892,100 +606,86 @@ function applyPopStateNavigationTargets(requestId, previousHref, previousState,
892
606
  navigate(resolved.to, { history: getRedirectHistoryMode(resolved.replace) });
893
607
  return;
894
608
  }
895
- const samePathTargets = [];
896
- const rootsToPrepare = [];
897
- for (const target of matchedTargets) {
898
- const resolved = target.resolved;
899
- if (pathname === target.app.pathname && isRenderResult(resolved)) {
900
- samePathTargets.push(target);
901
- continue;
902
- }
903
- if (resolved.kind === "redirect") continue;
904
- rootsToPrepare.push(target);
905
- }
906
- const previousPathname = getCurrentPathname();
907
609
  saveScrollPosition(previousHref);
908
- const rollbackSnapshots = matchedTargets.map((target) => captureDeferredRouteCleanup(target.app.instance));
909
- const rollbackSnapshotByInstance = new Map(rollbackSnapshots.map((snapshot) => [snapshot.instance, snapshot]));
910
- const deferredCleanups = [];
911
- const cleanupErrors = [];
912
- const previousAppLocations = matchedTargets.map((target) => ({
913
- app: target.app,
914
- pathname: target.app.pathname,
915
- href: target.app.href
916
- }));
917
- const restoreOnExit = () => {
918
- if (!navigationBatchClosed) {
919
- discardLifecycleCommitBatch(navigationBatch);
920
- navigationBatchClosed = true;
921
- }
922
- restoreDeferredRouteInstances(rollbackSnapshots);
923
- for (const previous of previousAppLocations) {
924
- previous.app.pathname = previous.pathname;
925
- previous.app.href = previous.href;
926
- }
927
- setCurrentRouteLocation(previousPathname, previousHref);
928
- };
929
- const navigationBatch = beginLifecycleCommitBatch();
930
- let navigationBatchClosed = false;
931
- try {
932
- for (const target of rootsToPrepare) {
933
- if (isStaleRouteRequest(requestId)) {
934
- restoreOnExit();
935
- return;
610
+ commitNavigationRoots(requestId, pathname, href, matchedTargets, () => {}, () => applyHistoryScroll(href, state), () => {
611
+ window.history.replaceState(previousState, "", previousHref);
612
+ syncRegisteredRouteSnapshot();
613
+ });
614
+ }
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) {
618
+ const previousPathname = getCurrentPathname();
619
+ const previousHref = getCurrentHref();
620
+ const roots = targets.map((target) => {
621
+ const replaceLifetime = pathname !== target.app.pathname || !isRenderResult(target.resolved);
622
+ return {
623
+ target,
624
+ replaceLifetime,
625
+ previousPathname: target.app.pathname,
626
+ previousHref: target.app.href,
627
+ prepared: prepareNavigationRoot(target, href, replaceLifetime)
628
+ };
629
+ });
630
+ const transaction = beginCommitTransaction();
631
+ let completionFailure;
632
+ registerCommitParticipant({
633
+ rollback() {
634
+ const errors = [];
635
+ 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;
936
639
  }
937
- updateRouteRuntime(target.app.instance, target.resolved, href);
938
- const deferredCleanup = remountResolvedRoute(target.app.instance, getResolvedRouteHandler(target.resolved), pathname, href, rollbackSnapshotByInstance.get(target.app.instance));
939
- deferredCleanups.push(deferredCleanup);
940
- }
941
- if (isStaleRouteRequest(requestId)) {
942
- restoreOnExit();
943
- return;
944
- }
945
- flushRuntimeScheduler();
946
- cleanupErrors.push(...drainLifecycleCommitErrors());
947
- if (isStaleRouteRequest(requestId)) {
948
- restoreOnExit();
949
- return;
950
- }
951
- for (const target of samePathTargets) {
952
- if (isStaleRouteRequest(requestId)) {
953
- restoreOnExit();
954
- return;
640
+ setCurrentRouteLocation(previousPathname, previousHref);
641
+ try {
642
+ restoreHistory?.();
643
+ } catch (error) {
644
+ errors.push(error);
645
+ }
646
+ 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();
955
666
  }
956
- updateRouteRuntime(target.app.instance, target.resolved, href);
957
- rerenderResolvedRoute(target.app.instance, pathname, href);
958
- }
959
- flushRuntimeScheduler();
960
- cleanupErrors.push(...drainLifecycleCommitErrors());
961
- if (isStaleRouteRequest(requestId)) {
962
- restoreOnExit();
963
- return;
964
- }
965
- flushLifecycleCommitBatch(navigationBatch);
966
- navigationBatchClosed = true;
967
- cleanupErrors.push(...drainLifecycleCommitErrors());
968
- for (const target of matchedTargets) if (target.resolved && target.resolved.kind !== "redirect") syncAppRegistrationLocation(target.app, pathname, href);
969
- if (isStaleRouteRequest(requestId)) {
970
- restoreOnExit();
971
- return;
972
667
  }
973
- setCurrentRouteLocation(pathname, href);
974
- clearStagedRouteLocations(matchedTargets);
975
- syncRegisteredRouteSnapshot();
976
- reconcileNavigationMetadata(matchedTargets);
977
- applyHistoryScroll(href, state);
978
- for (const deferredCleanup of deferredCleanups) cleanupErrors.push(...cleanupDeferredRouteInstance(deferredCleanup));
979
- for (const error of cleanupErrors) reportRouteCleanupErrors([error]);
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();
674
+ }
675
+ if (isStaleRouteRequest(requestId)) return;
676
+ flushRuntimeScheduler();
677
+ if (isStaleRouteRequest(requestId)) return;
678
+ }
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;
980
684
  } catch (error) {
981
- restoreOnExit();
982
- try {
983
- window.history.replaceState(previousState, "", previousHref);
984
- syncRegisteredRouteSnapshot();
985
- } catch (rollbackError) {
986
- logger.error("[Askr] failed to restore URL after popstate failure:", rollbackError);
987
- }
685
+ logger.error("[Askr] navigation failed:", error);
988
686
  throw error;
687
+ } finally {
688
+ discardTransaction(transaction);
989
689
  }
990
690
  }
991
691
  //#endregion
@@ -994,16 +694,38 @@ function applyPopStateNavigationTargets(requestId, previousHref, previousState,
994
694
  * Client-side navigation with History API
995
695
  */
996
696
  let navigationInitialized = false;
997
- configureNavigationRegistryHost({ cancelRouteRequests });
697
+ let navigationRegistryHosted = false;
698
+ /**
699
+ * Publish request cancellation to the navigation registry.
700
+ *
701
+ * The registry cannot import this module (it is imported *by* it), so the
702
+ * capability is injected. Doing that at module scope meant whether an app
703
+ * could cancel its in-flight route requests depended on whether anything had
704
+ * imported this module yet; every entry point that can navigate composes it.
705
+ */
706
+ function ensureNavigationRegistryHost() {
707
+ if (navigationRegistryHosted) return;
708
+ navigationRegistryHosted = true;
709
+ configureNavigationRegistryHost({ cancelRouteRequests });
710
+ }
998
711
  /** Navigate the client-side router to `path` using the History API. */
999
712
  function navigate(path, options = {}) {
713
+ ensureNavigationRegistryHost();
1000
714
  if (typeof window === "undefined") return;
715
+ prepareNavigationFocus();
1001
716
  const targetPath = addRouteBasePath(path, getActiveRouteBasePath());
1002
717
  const initialTarget = parseTargetUrl(targetPath);
1003
- navigateWithRedirectState(targetPath, options, {
718
+ const redirectState = {
1004
719
  redirects: 0,
1005
720
  visited: /* @__PURE__ */ new Set([`${initialTarget.pathname}${initialTarget.search}${initialTarget.hash}`])
1006
- });
721
+ };
722
+ if (isRuntimeSchedulerExecuting()) {
723
+ queueMicrotask(() => {
724
+ navigateWithRedirectState(targetPath, options, redirectState);
725
+ });
726
+ return;
727
+ }
728
+ navigateWithRedirectState(targetPath, options, redirectState);
1007
729
  }
1008
730
  function navigateWithRedirectState(path, options, redirectState) {
1009
731
  if (typeof window === "undefined") return;
@@ -1021,6 +743,7 @@ function navigateWithRedirectState(path, options, redirectState) {
1021
743
  logger.error("[Askr] navigation failed:", error);
1022
744
  }
1023
745
  }, (error) => {
746
+ if (isStaleRouteRequest(request.id)) return;
1024
747
  logger.error("[Askr] navigation failed:", error);
1025
748
  });
1026
749
  return;
@@ -1028,6 +751,7 @@ function navigateWithRedirectState(path, options, redirectState) {
1028
751
  applyNavigationTargets(request.id, path, options, redirectState, pathname, href, resolvedTargets, navigateWithRedirectState);
1029
752
  }
1030
753
  function handlePopState(event) {
754
+ beginHistoryFocusRestoration();
1031
755
  const request = beginRouteRequest();
1032
756
  const previousHref = getCurrentHref();
1033
757
  const pathname = window.location.pathname;
@@ -1045,6 +769,7 @@ function handlePopState(event) {
1045
769
  Promise.resolve(resolvedTargets).then((next) => {
1046
770
  applyResolved(next);
1047
771
  }, (error) => {
772
+ if (isStaleRouteRequest(request.id)) return;
1048
773
  logger.error("[Askr] popstate navigation failed:", error);
1049
774
  });
1050
775
  return;
@@ -1052,9 +777,12 @@ function handlePopState(event) {
1052
777
  applyResolved(resolvedTargets);
1053
778
  }
1054
779
  function initializeNavigation() {
780
+ ensureNavigationRegistryHost();
1055
781
  if (typeof window === "undefined" || navigationInitialized) return;
1056
782
  navigationInitialized = true;
1057
783
  window.addEventListener("popstate", handlePopState);
784
+ document.addEventListener("pointerdown", captureNavigationFocus, true);
785
+ document.addEventListener("click", releaseNavigationFocusCapture);
1058
786
  }
1059
787
  //#endregion
1060
- export { 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 };
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 };