vinext 0.1.0 → 0.1.2

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 (205) hide show
  1. package/README.md +2 -5
  2. package/dist/build/assets-ignore.d.ts +32 -0
  3. package/dist/build/assets-ignore.js +48 -0
  4. package/dist/build/client-build-config.d.ts +33 -1
  5. package/dist/build/client-build-config.js +66 -1
  6. package/dist/check.js +4 -3
  7. package/dist/cli.js +2 -0
  8. package/dist/client/navigation-runtime.d.ts +11 -2
  9. package/dist/client/navigation-runtime.js +1 -1
  10. package/dist/client/vinext-next-data.d.ts +2 -1
  11. package/dist/client/window-next.d.ts +6 -4
  12. package/dist/config/config-matchers.d.ts +31 -5
  13. package/dist/config/config-matchers.js +50 -3
  14. package/dist/config/next-config.d.ts +29 -3
  15. package/dist/config/next-config.js +32 -2
  16. package/dist/deploy.js +47 -304
  17. package/dist/entries/app-rsc-entry.d.ts +8 -2
  18. package/dist/entries/app-rsc-entry.js +61 -5
  19. package/dist/entries/app-rsc-manifest.js +20 -2
  20. package/dist/entries/pages-client-entry.js +1 -1
  21. package/dist/entries/pages-server-entry.js +16 -7
  22. package/dist/index.d.ts +0 -2
  23. package/dist/index.js +233 -280
  24. package/dist/plugins/dynamic-preload-metadata.d.ts +13 -0
  25. package/dist/plugins/dynamic-preload-metadata.js +415 -0
  26. package/dist/plugins/og-assets.js +2 -2
  27. package/dist/plugins/optimize-imports.d.ts +8 -4
  28. package/dist/plugins/optimize-imports.js +16 -12
  29. package/dist/plugins/postcss.js +18 -14
  30. package/dist/plugins/require-context.d.ts +6 -0
  31. package/dist/plugins/require-context.js +184 -0
  32. package/dist/plugins/sass.d.ts +53 -24
  33. package/dist/plugins/sass.js +249 -1
  34. package/dist/plugins/wasm-module-import.d.ts +15 -0
  35. package/dist/plugins/wasm-module-import.js +50 -0
  36. package/dist/routing/app-route-graph.d.ts +35 -2
  37. package/dist/routing/app-route-graph.js +179 -8
  38. package/dist/routing/file-matcher.js +1 -1
  39. package/dist/routing/route-pattern.d.ts +2 -1
  40. package/dist/routing/route-pattern.js +16 -1
  41. package/dist/server/api-handler.js +4 -0
  42. package/dist/server/app-browser-entry.js +155 -215
  43. package/dist/server/app-browser-error.d.ts +4 -1
  44. package/dist/server/app-browser-error.js +7 -1
  45. package/dist/server/app-browser-history-controller.d.ts +104 -0
  46. package/dist/server/app-browser-history-controller.js +210 -0
  47. package/dist/server/app-browser-interception-context.d.ts +2 -1
  48. package/dist/server/app-browser-interception-context.js +15 -2
  49. package/dist/server/app-browser-navigation-controller.d.ts +13 -2
  50. package/dist/server/app-browser-navigation-controller.js +83 -4
  51. package/dist/server/app-browser-popstate.d.ts +12 -3
  52. package/dist/server/app-browser-popstate.js +19 -4
  53. package/dist/server/app-browser-rsc-redirect.d.ts +11 -2
  54. package/dist/server/app-browser-rsc-redirect.js +30 -8
  55. package/dist/server/app-browser-state.d.ts +3 -0
  56. package/dist/server/app-browser-state.js +10 -10
  57. package/dist/server/app-browser-visible-commit.js +10 -8
  58. package/dist/server/app-fallback-renderer.d.ts +2 -1
  59. package/dist/server/app-fallback-renderer.js +3 -1
  60. package/dist/server/app-history-state.d.ts +45 -1
  61. package/dist/server/app-history-state.js +109 -1
  62. package/dist/server/app-middleware.js +1 -0
  63. package/dist/server/app-optimistic-routing.js +22 -1
  64. package/dist/server/app-page-boundary-render.d.ts +2 -1
  65. package/dist/server/app-page-boundary-render.js +45 -21
  66. package/dist/server/app-page-cache.js +9 -7
  67. package/dist/server/app-page-dispatch.d.ts +14 -0
  68. package/dist/server/app-page-dispatch.js +21 -6
  69. package/dist/server/app-page-element-builder.d.ts +23 -2
  70. package/dist/server/app-page-element-builder.js +58 -17
  71. package/dist/server/app-page-execution.d.ts +1 -1
  72. package/dist/server/app-page-execution.js +32 -17
  73. package/dist/server/app-page-render.d.ts +7 -1
  74. package/dist/server/app-page-render.js +11 -16
  75. package/dist/server/app-page-request.d.ts +9 -6
  76. package/dist/server/app-page-request.js +14 -10
  77. package/dist/server/app-page-response.d.ts +2 -2
  78. package/dist/server/app-page-response.js +2 -2
  79. package/dist/server/app-page-route-wiring.d.ts +3 -1
  80. package/dist/server/app-page-route-wiring.js +10 -8
  81. package/dist/server/app-page-stream.d.ts +37 -7
  82. package/dist/server/app-page-stream.js +36 -6
  83. package/dist/server/app-pages-bridge.d.ts +16 -0
  84. package/dist/server/app-pages-bridge.js +23 -3
  85. package/dist/server/app-route-handler-cache.d.ts +1 -0
  86. package/dist/server/app-route-handler-cache.js +1 -0
  87. package/dist/server/app-route-handler-dispatch.d.ts +1 -0
  88. package/dist/server/app-route-handler-dispatch.js +2 -0
  89. package/dist/server/app-route-handler-execution.d.ts +1 -0
  90. package/dist/server/app-route-handler-execution.js +1 -0
  91. package/dist/server/app-route-handler-response.js +11 -10
  92. package/dist/server/app-route-handler-runtime.d.ts +1 -0
  93. package/dist/server/app-route-handler-runtime.js +15 -3
  94. package/dist/server/app-rsc-handler.d.ts +1 -0
  95. package/dist/server/app-rsc-handler.js +5 -4
  96. package/dist/server/app-rsc-response-finalizer.js +1 -1
  97. package/dist/server/app-rsc-route-matching.d.ts +20 -1
  98. package/dist/server/app-rsc-route-matching.js +29 -4
  99. package/dist/server/app-server-action-execution.d.ts +22 -1
  100. package/dist/server/app-server-action-execution.js +73 -12
  101. package/dist/server/app-ssr-entry.d.ts +6 -0
  102. package/dist/server/app-ssr-entry.js +19 -3
  103. package/dist/server/app-ssr-stream.js +9 -1
  104. package/dist/server/dev-lockfile.js +2 -1
  105. package/dist/server/dev-server.d.ts +1 -1
  106. package/dist/server/dev-server.js +97 -43
  107. package/dist/server/headers.d.ts +8 -1
  108. package/dist/server/headers.js +8 -1
  109. package/dist/server/instrumentation-runtime.d.ts +6 -0
  110. package/dist/server/instrumentation-runtime.js +8 -0
  111. package/dist/server/isr-cache.d.ts +37 -1
  112. package/dist/server/isr-cache.js +85 -1
  113. package/dist/server/isr-decision.d.ts +79 -0
  114. package/dist/server/isr-decision.js +70 -0
  115. package/dist/server/metadata-route-response.js +5 -3
  116. package/dist/server/middleware-runtime.d.ts +13 -0
  117. package/dist/server/middleware-runtime.js +11 -7
  118. package/dist/server/middleware.js +1 -0
  119. package/dist/server/navigation-planner.d.ts +62 -1
  120. package/dist/server/navigation-planner.js +193 -3
  121. package/dist/server/navigation-trace.d.ts +12 -2
  122. package/dist/server/navigation-trace.js +11 -1
  123. package/dist/server/normalize-path.d.ts +0 -8
  124. package/dist/server/normalize-path.js +3 -1
  125. package/dist/server/otel-tracer-extension.d.ts +45 -0
  126. package/dist/server/otel-tracer-extension.js +89 -0
  127. package/dist/server/pages-api-route.d.ts +14 -3
  128. package/dist/server/pages-api-route.js +6 -1
  129. package/dist/server/pages-asset-tags.d.ts +15 -4
  130. package/dist/server/pages-asset-tags.js +18 -12
  131. package/dist/server/pages-data-route.js +5 -1
  132. package/dist/server/pages-node-compat.d.ts +5 -11
  133. package/dist/server/pages-node-compat.js +175 -118
  134. package/dist/server/pages-page-data.d.ts +38 -7
  135. package/dist/server/pages-page-data.js +64 -18
  136. package/dist/server/pages-page-handler.d.ts +10 -2
  137. package/dist/server/pages-page-handler.js +49 -20
  138. package/dist/server/pages-page-response.d.ts +55 -2
  139. package/dist/server/pages-page-response.js +74 -6
  140. package/dist/server/pages-readiness.d.ts +36 -0
  141. package/dist/server/pages-readiness.js +21 -0
  142. package/dist/server/pages-request-pipeline.d.ts +113 -0
  143. package/dist/server/pages-request-pipeline.js +230 -0
  144. package/dist/server/pages-revalidate.d.ts +15 -0
  145. package/dist/server/pages-revalidate.js +19 -0
  146. package/dist/server/prod-server.d.ts +45 -3
  147. package/dist/server/prod-server.js +182 -234
  148. package/dist/server/socket-error-backstop.d.ts +19 -1
  149. package/dist/server/socket-error-backstop.js +77 -4
  150. package/dist/shims/app-router-scroll.js +22 -4
  151. package/dist/shims/cache-runtime.js +39 -2
  152. package/dist/shims/dynamic-preload-chunks.d.ts +8 -0
  153. package/dist/shims/dynamic-preload-chunks.js +77 -0
  154. package/dist/shims/dynamic.d.ts +4 -0
  155. package/dist/shims/dynamic.js +4 -2
  156. package/dist/shims/error-boundary.d.ts +17 -7
  157. package/dist/shims/error-boundary.js +8 -1
  158. package/dist/shims/error.js +37 -11
  159. package/dist/shims/fetch-cache.d.ts +22 -1
  160. package/dist/shims/fetch-cache.js +28 -1
  161. package/dist/shims/hash-scroll.d.ts +1 -0
  162. package/dist/shims/hash-scroll.js +3 -1
  163. package/dist/shims/head.js +6 -1
  164. package/dist/shims/headers.d.ts +16 -2
  165. package/dist/shims/headers.js +37 -1
  166. package/dist/shims/image-config.js +7 -1
  167. package/dist/shims/internal/app-route-detection.d.ts +6 -3
  168. package/dist/shims/internal/app-route-detection.js +10 -6
  169. package/dist/shims/internal/app-router-context.d.ts +5 -0
  170. package/dist/shims/internal/link-status-registry.d.ts +43 -0
  171. package/dist/shims/internal/link-status-registry.js +42 -0
  172. package/dist/shims/internal/route-pattern-for-warning.d.ts +27 -0
  173. package/dist/shims/internal/route-pattern-for-warning.js +40 -0
  174. package/dist/shims/internal/utils.d.ts +1 -0
  175. package/dist/shims/link.js +20 -6
  176. package/dist/shims/metadata.d.ts +6 -2
  177. package/dist/shims/metadata.js +32 -14
  178. package/dist/shims/navigation.d.ts +9 -18
  179. package/dist/shims/navigation.js +96 -23
  180. package/dist/shims/router-state.d.ts +1 -0
  181. package/dist/shims/router-state.js +2 -0
  182. package/dist/shims/router.d.ts +6 -3
  183. package/dist/shims/router.js +156 -22
  184. package/dist/shims/script-nonce-context.d.ts +1 -1
  185. package/dist/shims/script-nonce-context.js +11 -3
  186. package/dist/shims/server.d.ts +17 -1
  187. package/dist/shims/server.js +31 -6
  188. package/dist/shims/slot.js +1 -1
  189. package/dist/shims/unified-request-context.js +1 -0
  190. package/dist/typegen.js +1 -0
  191. package/dist/utils/client-build-manifest.d.ts +8 -1
  192. package/dist/utils/client-build-manifest.js +41 -6
  193. package/dist/utils/client-entry-manifest.d.ts +11 -0
  194. package/dist/utils/client-entry-manifest.js +29 -0
  195. package/dist/utils/client-runtime-metadata.d.ts +45 -0
  196. package/dist/utils/client-runtime-metadata.js +63 -0
  197. package/dist/utils/hash.d.ts +17 -1
  198. package/dist/utils/hash.js +36 -1
  199. package/dist/utils/lazy-chunks.d.ts +27 -1
  200. package/dist/utils/lazy-chunks.js +65 -1
  201. package/dist/utils/manifest-paths.d.ts +20 -2
  202. package/dist/utils/manifest-paths.js +38 -3
  203. package/dist/utils/path.d.ts +2 -1
  204. package/dist/utils/path.js +5 -1
  205. package/package.json +6 -2
@@ -2,5 +2,8 @@
2
2
  declare function createOnUncaughtError(getRecoveryHref: () => string | null): (error: unknown, errorInfo: {
3
3
  componentStack?: string;
4
4
  }) => void;
5
+ declare function prodOnCaughtError(error: unknown, errorInfo: {
6
+ componentStack?: string;
7
+ }): void;
5
8
  //#endregion
6
- export { createOnUncaughtError };
9
+ export { createOnUncaughtError, prodOnCaughtError };
@@ -1,3 +1,4 @@
1
+ import { isNavigationSignalError } from "../utils/navigation-signal.js";
1
2
  //#region src/server/app-browser-error.ts
2
3
  function createOnUncaughtError(getRecoveryHref) {
3
4
  return (error, errorInfo) => {
@@ -7,5 +8,10 @@ function createOnUncaughtError(getRecoveryHref) {
7
8
  if (recoveryHref !== null) window.location.assign(recoveryHref);
8
9
  };
9
10
  }
11
+ function prodOnCaughtError(error, errorInfo) {
12
+ if (isNavigationSignalError(error)) return;
13
+ console.error(error);
14
+ if (errorInfo?.componentStack) console.error("The above error occurred in a React component:\n" + errorInfo.componentStack);
15
+ }
10
16
  //#endregion
11
- export { createOnUncaughtError };
17
+ export { createOnUncaughtError, prodOnCaughtError };
@@ -0,0 +1,104 @@
1
+ import { BfcacheIdMap, HistoryTraversalIntent } from "./app-history-state.js";
2
+ import { AppRouterState } from "./app-browser-state.js";
3
+ import { HistoryUpdateMode } from "./app-browser-navigation-controller.js";
4
+
5
+ //#region src/server/app-browser-history-controller.d.ts
6
+ /**
7
+ * Visible router-state metadata at the instant a hash-only navigation commits.
8
+ * `null` means the browser router tree has not committed yet, so the controller
9
+ * falls back to reading the same facts off the live history entry.
10
+ */
11
+ type VisibleNavigationMetadata = {
12
+ bfcacheIds: BfcacheIdMap | null;
13
+ previousNextUrl: string | null;
14
+ };
15
+ type AppBrowserHistoryControllerDeps = {
16
+ initialHistoryState: unknown;
17
+ maxHistoryStateSnapshots: number; /** Reads `window.history.state`. Injected so the controller stays unit-testable. */
18
+ readHistoryState: () => unknown; /** Reads `window.location.href`. Injected so the controller stays unit-testable. */
19
+ readCurrentHref: () => string; /** Wraps `pushHistoryStateWithoutNotify(state, "", href)`. */
20
+ pushHistoryState: (state: unknown, href: string) => void; /** Wraps `replaceHistoryStateWithoutNotify(state, "", href)`. */
21
+ replaceHistoryState: (state: unknown, href: string) => void;
22
+ readVisibleNavigationMetadata: () => VisibleNavigationMetadata | null;
23
+ };
24
+ /**
25
+ * Candidate visible state resolved from a restorable history snapshot, handed to
26
+ * the entry's approved-visible-restore callback. The controller resolves the
27
+ * candidate and owns the traversal-index commit; the entry owns the actual
28
+ * `AppBrowserNavigationController.restoreHistorySnapshotVisibleState()` call and
29
+ * the `ApprovedVisibleCommit` boundary.
30
+ */
31
+ type RestorableSnapshotCandidate = {
32
+ state: AppRouterState;
33
+ beforeCommit: () => void;
34
+ };
35
+ type RestoreHistorySnapshotOptions = {
36
+ historyState: unknown;
37
+ stageClientParams: (params: Record<string, string | string[]>) => void;
38
+ approveVisibleRestore: (candidate: RestorableSnapshotCandidate) => boolean;
39
+ };
40
+ type CommitNavigationHistoryOptions = {
41
+ bfcacheIds: BfcacheIdMap;
42
+ href: string;
43
+ historyUpdateMode: HistoryUpdateMode | undefined;
44
+ previousNextUrl: string | null;
45
+ targetHistoryIndex?: number | null;
46
+ stageClientParams: () => void;
47
+ };
48
+ /**
49
+ * Owns App Router browser-history metadata and traversal bookkeeping behind a
50
+ * typed seam: traversal index allocation/commit, push/replace/traverse/hash-only
51
+ * history-state writes, BFCache epoch/snapshot invalidation through
52
+ * `RestorableClientStateController`, and restorable-snapshot candidate
53
+ * resolution.
54
+ *
55
+ * Ownership boundary: this is not a second router or visible-state authority. It
56
+ * resolves history facts and delegates visible restoration through an injected
57
+ * approved-commit callback. It never sets router state directly, never imports
58
+ * `applyApprovedVisibleCommit()`, and never bypasses the `ApprovedVisibleCommit`
59
+ * boundary owned by `AppBrowserNavigationController`.
60
+ */
61
+ declare class AppBrowserHistoryController {
62
+ #private;
63
+ constructor(deps: AppBrowserHistoryControllerDeps);
64
+ get currentHistoryTraversalIndex(): number | null;
65
+ allocateNavigationHistoryTraversalIndex(historyUpdateMode: HistoryUpdateMode | undefined): number | null;
66
+ commitHistoryTraversalIndex(index: number | null): void;
67
+ commitTraversalIndexFromHistoryState(historyState: unknown): void;
68
+ resolveTraversalIntent(historyState: unknown): HistoryTraversalIntent;
69
+ readCurrentBfcacheVersionHistoryIds(historyState: unknown): BfcacheIdMap | null;
70
+ isCacheInvalidationGuarded(): boolean;
71
+ isCurrentBfcacheVersion(historyState: unknown): boolean;
72
+ beginCacheInvalidationGuard(): () => void;
73
+ invalidateRestorableClientState(): void;
74
+ rememberHistoryStateSnapshot(state: AppRouterState): void;
75
+ commitHashOnlyNavigation(href: string, historyUpdateMode: HistoryUpdateMode, scroll: boolean): void;
76
+ /**
77
+ * Writes the history entry for an approved push/replace/traverse commit and
78
+ * advances the traversal index. `stageClientParams` runs at the exact point it
79
+ * ran inline in the browser-entry commit effect so client-param staging stays
80
+ * ordered relative to the history write. Mirrors Next.js committing tree state
81
+ * into the history entry during the navigation commit.
82
+ */
83
+ commitNavigationHistory(options: CommitNavigationHistoryOptions): void;
84
+ syncCurrentHistoryStatePreviousNextUrl(previousNextUrl: string | null, bfcacheIds?: BfcacheIdMap | null): void;
85
+ /** Initial history write performed before hydration starts. */
86
+ writeBootstrapHistoryMetadata(): void;
87
+ /** History write performed on the first committed (hydrated) render. */
88
+ writeHydratedHistoryMetadata(options: {
89
+ bfcacheIds: BfcacheIdMap;
90
+ previousNextUrl: string | null;
91
+ }): void;
92
+ /**
93
+ * Resolves a restorable snapshot candidate for the given history entry and
94
+ * commits the traversal index after, and only after, the injected
95
+ * approved-visible-restore callback succeeds. The traversal-index commit and
96
+ * client-param staging run inside `beforeCommit`, which the
97
+ * `AppBrowserNavigationController` invokes only once the `ApprovedVisibleCommit`
98
+ * is approved. Returns false when no snapshot is restorable or the restore is
99
+ * not approved.
100
+ */
101
+ restoreHistorySnapshot(options: RestoreHistorySnapshotOptions): boolean;
102
+ }
103
+ //#endregion
104
+ export { AppBrowserHistoryController, RestorableSnapshotCandidate };
@@ -0,0 +1,210 @@
1
+ import { RestorableClientStateController, createHistoryStateWithNavigationMetadata, readHistoryStateBfcacheIds, readHistoryStatePreviousNextUrl, readHistoryStateTraversalIndex, resolveHistoryTraversalIntent } from "./app-history-state.js";
2
+ //#region src/server/app-browser-history-controller.ts
3
+ function stripVinextScrollState(state) {
4
+ if (!state || typeof state !== "object") return state;
5
+ const nextState = {};
6
+ for (const [key, value] of Object.entries(state)) {
7
+ if (key === "__vinext_scrollX" || key === "__vinext_scrollY") continue;
8
+ nextState[key] = value;
9
+ }
10
+ return Object.keys(nextState).length > 0 ? nextState : null;
11
+ }
12
+ /**
13
+ * Owns App Router browser-history metadata and traversal bookkeeping behind a
14
+ * typed seam: traversal index allocation/commit, push/replace/traverse/hash-only
15
+ * history-state writes, BFCache epoch/snapshot invalidation through
16
+ * `RestorableClientStateController`, and restorable-snapshot candidate
17
+ * resolution.
18
+ *
19
+ * Ownership boundary: this is not a second router or visible-state authority. It
20
+ * resolves history facts and delegates visible restoration through an injected
21
+ * approved-commit callback. It never sets router state directly, never imports
22
+ * `applyApprovedVisibleCommit()`, and never bypasses the `ApprovedVisibleCommit`
23
+ * boundary owned by `AppBrowserNavigationController`.
24
+ */
25
+ var AppBrowserHistoryController = class {
26
+ #restorableClientState;
27
+ #readHistoryState;
28
+ #readCurrentHref;
29
+ #pushHistoryState;
30
+ #replaceHistoryState;
31
+ #readVisibleNavigationMetadata;
32
+ #currentHistoryTraversalIndex;
33
+ #nextHistoryTraversalIndex;
34
+ constructor(deps) {
35
+ this.#readHistoryState = deps.readHistoryState;
36
+ this.#readCurrentHref = deps.readCurrentHref;
37
+ this.#pushHistoryState = deps.pushHistoryState;
38
+ this.#replaceHistoryState = deps.replaceHistoryState;
39
+ this.#readVisibleNavigationMetadata = deps.readVisibleNavigationMetadata;
40
+ this.#restorableClientState = new RestorableClientStateController({
41
+ initialHistoryState: deps.initialHistoryState,
42
+ maxHistoryStateSnapshots: deps.maxHistoryStateSnapshots
43
+ });
44
+ this.#currentHistoryTraversalIndex = readHistoryStateTraversalIndex(deps.initialHistoryState) ?? 0;
45
+ this.#nextHistoryTraversalIndex = this.#currentHistoryTraversalIndex;
46
+ }
47
+ get currentHistoryTraversalIndex() {
48
+ return this.#currentHistoryTraversalIndex;
49
+ }
50
+ allocateNavigationHistoryTraversalIndex(historyUpdateMode) {
51
+ switch (historyUpdateMode) {
52
+ case "push": return this.#nextHistoryTraversalIndex + 1;
53
+ case "replace": return this.#currentHistoryTraversalIndex;
54
+ case void 0: return null;
55
+ default: throw new Error("[vinext] Unknown history update mode: " + String(historyUpdateMode));
56
+ }
57
+ }
58
+ commitHistoryTraversalIndex(index) {
59
+ this.#currentHistoryTraversalIndex = index;
60
+ if (index !== null) this.#nextHistoryTraversalIndex = Math.max(this.#nextHistoryTraversalIndex, index);
61
+ }
62
+ commitTraversalIndexFromHistoryState(historyState) {
63
+ this.commitHistoryTraversalIndex(readHistoryStateTraversalIndex(historyState));
64
+ }
65
+ resolveTraversalIntent(historyState) {
66
+ return resolveHistoryTraversalIntent({
67
+ currentHistoryIndex: this.#currentHistoryTraversalIndex,
68
+ historyState
69
+ });
70
+ }
71
+ readCurrentBfcacheVersionHistoryIds(historyState) {
72
+ return this.#restorableClientState.readCurrentBfcacheVersionHistoryIds(historyState);
73
+ }
74
+ isCacheInvalidationGuarded() {
75
+ return this.#restorableClientState.isCacheInvalidationGuarded();
76
+ }
77
+ isCurrentBfcacheVersion(historyState) {
78
+ return this.#restorableClientState.isCurrentBfcacheVersion(historyState);
79
+ }
80
+ beginCacheInvalidationGuard() {
81
+ return this.#restorableClientState.beginCacheInvalidationGuard();
82
+ }
83
+ invalidateRestorableClientState() {
84
+ this.#restorableClientState.invalidateClientState();
85
+ }
86
+ rememberHistoryStateSnapshot(state) {
87
+ this.#restorableClientState.rememberHistoryStateSnapshot({
88
+ historyIndex: this.#currentHistoryTraversalIndex,
89
+ state
90
+ });
91
+ }
92
+ commitHashOnlyNavigation(href, historyUpdateMode, scroll) {
93
+ const navigationHistoryIndex = this.allocateNavigationHistoryTraversalIndex(historyUpdateMode);
94
+ const historyState = this.#readHistoryState();
95
+ const visible = this.#readVisibleNavigationMetadata();
96
+ const previousNextUrl = visible ? visible.previousNextUrl : readHistoryStatePreviousNextUrl(historyState);
97
+ const bfcacheIds = visible ? visible.bfcacheIds : this.#restorableClientState.readCurrentBfcacheVersionHistoryIds(historyState);
98
+ const nextHistoryState = createHistoryStateWithNavigationMetadata(this.#createHashOnlyNavigationBaseHistoryState(historyUpdateMode, scroll), {
99
+ bfcacheIds,
100
+ bfcacheVersion: bfcacheIds === null ? void 0 : this.#restorableClientState.currentBfcacheVersion,
101
+ previousNextUrl,
102
+ traversalIndex: navigationHistoryIndex
103
+ });
104
+ if (historyUpdateMode === "replace") this.#replaceHistoryState(nextHistoryState, href);
105
+ else this.#pushHistoryState(nextHistoryState, href);
106
+ this.commitHistoryTraversalIndex(navigationHistoryIndex);
107
+ }
108
+ #createHashOnlyNavigationBaseHistoryState(historyUpdateMode, scroll) {
109
+ if (historyUpdateMode !== "replace") return null;
110
+ const historyState = this.#readHistoryState();
111
+ return scroll ? stripVinextScrollState(historyState) : historyState;
112
+ }
113
+ /**
114
+ * Writes the history entry for an approved push/replace/traverse commit and
115
+ * advances the traversal index. `stageClientParams` runs at the exact point it
116
+ * ran inline in the browser-entry commit effect so client-param staging stays
117
+ * ordered relative to the history write. Mirrors Next.js committing tree state
118
+ * into the history entry during the navigation commit.
119
+ */
120
+ commitNavigationHistory(options) {
121
+ const currentHref = this.#readCurrentHref();
122
+ const origin = new URL(currentHref).origin;
123
+ const targetHref = new URL(options.href, origin).href;
124
+ const preserveExistingState = options.historyUpdateMode === "replace";
125
+ const navigationHistoryIndex = options.targetHistoryIndex !== void 0 ? options.targetHistoryIndex : this.allocateNavigationHistoryTraversalIndex(options.historyUpdateMode);
126
+ const historyState = createHistoryStateWithNavigationMetadata(preserveExistingState ? this.#readHistoryState() : null, {
127
+ bfcacheIds: options.bfcacheIds,
128
+ bfcacheVersion: this.#restorableClientState.currentBfcacheVersion,
129
+ previousNextUrl: options.previousNextUrl,
130
+ traversalIndex: navigationHistoryIndex
131
+ });
132
+ let wroteHistoryState = false;
133
+ if (options.historyUpdateMode === "replace" && currentHref !== targetHref) {
134
+ options.stageClientParams();
135
+ this.#replaceHistoryState(historyState, options.href);
136
+ wroteHistoryState = true;
137
+ this.commitHistoryTraversalIndex(navigationHistoryIndex);
138
+ } else if (options.historyUpdateMode === "push" && currentHref !== targetHref) {
139
+ options.stageClientParams();
140
+ this.#pushHistoryState(historyState, options.href);
141
+ wroteHistoryState = true;
142
+ this.commitHistoryTraversalIndex(navigationHistoryIndex);
143
+ }
144
+ if (!wroteHistoryState) {
145
+ this.syncCurrentHistoryStatePreviousNextUrl(options.previousNextUrl, options.bfcacheIds);
146
+ options.stageClientParams();
147
+ if (options.targetHistoryIndex !== void 0) this.commitHistoryTraversalIndex(options.targetHistoryIndex);
148
+ }
149
+ }
150
+ syncCurrentHistoryStatePreviousNextUrl(previousNextUrl, bfcacheIds) {
151
+ if (this.#isHistoryStateNavigationMetadataInSync(this.#readHistoryState(), previousNextUrl, bfcacheIds)) return;
152
+ const nextHistoryState = createHistoryStateWithNavigationMetadata(this.#readHistoryState(), {
153
+ bfcacheIds,
154
+ bfcacheVersion: bfcacheIds === void 0 ? void 0 : this.#restorableClientState.currentBfcacheVersion,
155
+ previousNextUrl
156
+ });
157
+ this.#replaceHistoryState(nextHistoryState, this.#readCurrentHref());
158
+ if (this.#isHistoryStateNavigationMetadataInSync(this.#readHistoryState(), previousNextUrl, bfcacheIds)) return;
159
+ this.#replaceHistoryState(nextHistoryState, this.#readCurrentHref());
160
+ }
161
+ #isHistoryStateNavigationMetadataInSync(state, previousNextUrl, bfcacheIds) {
162
+ return readHistoryStatePreviousNextUrl(state) === previousNextUrl && (bfcacheIds === void 0 || areBfcacheIdMapsEqual(readHistoryStateBfcacheIds(state), bfcacheIds) && this.#restorableClientState.isCurrentBfcacheVersion(state));
163
+ }
164
+ /** Initial history write performed before hydration starts. */
165
+ writeBootstrapHistoryMetadata() {
166
+ this.#replaceHistoryState(createHistoryStateWithNavigationMetadata(this.#readHistoryState(), {
167
+ previousNextUrl: null,
168
+ traversalIndex: this.#currentHistoryTraversalIndex
169
+ }), this.#readCurrentHref());
170
+ }
171
+ /** History write performed on the first committed (hydrated) render. */
172
+ writeHydratedHistoryMetadata(options) {
173
+ this.#replaceHistoryState(createHistoryStateWithNavigationMetadata(this.#readHistoryState(), {
174
+ bfcacheIds: options.bfcacheIds,
175
+ bfcacheVersion: this.#restorableClientState.currentBfcacheVersion,
176
+ previousNextUrl: options.previousNextUrl,
177
+ traversalIndex: this.#currentHistoryTraversalIndex
178
+ }), this.#readCurrentHref());
179
+ }
180
+ /**
181
+ * Resolves a restorable snapshot candidate for the given history entry and
182
+ * commits the traversal index after, and only after, the injected
183
+ * approved-visible-restore callback succeeds. The traversal-index commit and
184
+ * client-param staging run inside `beforeCommit`, which the
185
+ * `AppBrowserNavigationController` invokes only once the `ApprovedVisibleCommit`
186
+ * is approved. Returns false when no snapshot is restorable or the restore is
187
+ * not approved.
188
+ */
189
+ restoreHistorySnapshot(options) {
190
+ const decision = this.#restorableClientState.resolveHistoryStateSnapshotRestore(options.historyState);
191
+ if (decision.kind === "skip") return false;
192
+ return options.approveVisibleRestore({
193
+ state: decision.state,
194
+ beforeCommit: () => {
195
+ this.commitHistoryTraversalIndex(decision.targetHistoryIndex);
196
+ options.stageClientParams(decision.state.navigationSnapshot.params);
197
+ }
198
+ });
199
+ }
200
+ };
201
+ function areBfcacheIdMapsEqual(a, b) {
202
+ if (a === b) return true;
203
+ if (a === null || b === null) return false;
204
+ const aEntries = Object.entries(a);
205
+ const bEntries = Object.entries(b);
206
+ if (aEntries.length !== bEntries.length) return false;
207
+ return aEntries.every(([key, value]) => b[key] === value);
208
+ }
209
+ //#endregion
210
+ export { AppBrowserHistoryController };
@@ -19,5 +19,6 @@ type ResolveManifestNavigationInterceptionContextOptions = {
19
19
  * is owned by the deterministic route graph builder.
20
20
  */
21
21
  declare function resolveManifestNavigationInterceptionContext(options: ResolveManifestNavigationInterceptionContextOptions): string | null;
22
+ declare function resolveMiddlewareRewriteNavigationInterceptionContext(options: ResolveManifestNavigationInterceptionContextOptions): string | null;
22
23
  //#endregion
23
- export { resolveManifestNavigationInterceptionContext };
24
+ export { resolveManifestNavigationInterceptionContext, resolveMiddlewareRewriteNavigationInterceptionContext };
@@ -1,6 +1,6 @@
1
1
  import { splitPathnameForRouteMatch } from "../routing/utils.js";
2
2
  import { stripBasePath } from "../utils/base-path.js";
3
- import { matchRoutePattern, matchRoutePatternPrefix } from "../routing/route-pattern.js";
3
+ import { matchRoutePattern, matchRoutePatternPrefix, matchRoutePatternWithOptionalDynamicSegments } from "../routing/route-pattern.js";
4
4
  //#region src/server/app-browser-interception-context.ts
5
5
  /**
6
6
  * Resolve the first-hop interception context from declared route topology.
@@ -26,5 +26,18 @@ function resolveManifestNavigationInterceptionContext(options) {
26
26
  }
27
27
  return null;
28
28
  }
29
+ function resolveMiddlewareRewriteNavigationInterceptionContext(options) {
30
+ if (options.routeManifest === null) return null;
31
+ const currentPathname = stripBasePath(options.currentPathname, options.basePath);
32
+ const targetPathname = stripBasePath(options.targetPathname, options.basePath);
33
+ const sourceParts = splitPathnameForRouteMatch(currentPathname);
34
+ const targetParts = splitPathnameForRouteMatch(targetPathname);
35
+ for (const interception of options.routeManifest.segmentGraph.interceptions.values()) {
36
+ if (!matchRoutePatternPrefix(sourceParts, interception.sourcePatternParts)) continue;
37
+ if (!matchRoutePatternWithOptionalDynamicSegments(targetParts, interception.targetPatternParts)) continue;
38
+ return currentPathname;
39
+ }
40
+ return null;
41
+ }
29
42
  //#endregion
30
- export { resolveManifestNavigationInterceptionContext };
43
+ export { resolveManifestNavigationInterceptionContext, resolveMiddlewareRewriteNavigationInterceptionContext };
@@ -1,9 +1,10 @@
1
1
  import { RouteManifest } from "../routing/app-route-graph.js";
2
2
  import { AppRouterScrollIntent } from "../shims/app-router-scroll-state.js";
3
+ import { NavigationRuntimeVisibleCommitMode } from "../client/navigation-runtime.js";
3
4
  import { ServerActionRevalidationKind } from "./app-browser-action-result.js";
4
5
  import { AppElements } from "./app-elements-wire.js";
5
6
  import { OperationLane } from "./navigation-planner.js";
6
- import { ClientNavigationRenderSnapshot, commitClientNavigationState } from "../shims/navigation.js";
7
+ import { ClientNavigationRenderSnapshot, commitClientNavigationState, createSnapshotPathAndSearch } from "../shims/navigation.js";
7
8
  import { AppNavigationPayloadOrigin, AppRouterState } from "./app-browser-state.js";
8
9
  import { Dispatch, ReactNode } from "react";
9
10
 
@@ -35,6 +36,7 @@ type SameUrlServerActionLifecycleOptions = {
35
36
  targetHref?: string;
36
37
  };
37
38
  type BrowserNavigationControllerDeps = {
39
+ basePath?: string;
38
40
  commitClientNavigationState?: typeof commitClientNavigationState;
39
41
  performHardNavigation?: (href: string, mode?: HardNavigationMode) => boolean;
40
42
  getRouteManifest?: () => RouteManifest | null;
@@ -51,6 +53,12 @@ type BrowserNavigationController = {
51
53
  attachBrowserRouterState(setter: Dispatch<AppRouterState | Promise<AppRouterState>>, stateRef: BrowserRouterStateRef): () => void;
52
54
  beginPendingBrowserRouterState(): PendingBrowserRouterState;
53
55
  finalizeNavigation(navId: number, pending: PendingBrowserRouterState | null | undefined): void;
56
+ restoreHistorySnapshotVisibleState(options: {
57
+ beforeCommit?: () => void;
58
+ navId: number;
59
+ state: AppRouterState;
60
+ targetHref: string;
61
+ }): boolean;
54
62
  renderNavigationPayload(options: {
55
63
  actionType: "navigate" | "replace" | "traverse";
56
64
  createNavigationCommitEffect: BrowserNavigationCommitEffectFactory;
@@ -64,9 +72,11 @@ type BrowserNavigationController = {
64
72
  previousNextUrl: string | null;
65
73
  scrollIntent?: AppRouterScrollIntent | null;
66
74
  restoredBfcacheIds?: Readonly<Record<string, string>> | null;
75
+ reuseCurrentBfcacheIds?: boolean;
67
76
  targetHistoryIndex?: number | null;
68
77
  targetHref: string;
69
78
  navId: number;
79
+ visibleCommitMode?: NavigationRuntimeVisibleCommitMode;
70
80
  }): Promise<NavigationPayloadOutcome>;
71
81
  commitSameUrlNavigatePayload(nextElements: Promise<AppElements>, navigationSnapshot: ClientNavigationRenderSnapshot, returnValue?: {
72
82
  ok: boolean;
@@ -91,6 +101,7 @@ type BrowserNavigationController = {
91
101
  }): ReactNode;
92
102
  };
93
103
  declare function clearHardNavigationLoopGuard(): void;
104
+ declare function createBasePathStrippedPathAndSearch(url: URL, basePath: string): string;
94
105
  declare function createAppBrowserNavigationController(deps?: BrowserNavigationControllerDeps): BrowserNavigationController;
95
106
  //#endregion
96
- export { HistoryUpdateMode, NavigationPayloadOutcome, PendingBrowserRouterState, clearHardNavigationLoopGuard, createAppBrowserNavigationController };
107
+ export { HistoryUpdateMode, NavigationPayloadOutcome, PendingBrowserRouterState, clearHardNavigationLoopGuard, createAppBrowserNavigationController, createBasePathStrippedPathAndSearch, createSnapshotPathAndSearch };
@@ -1,9 +1,11 @@
1
+ import { stripBasePath } from "../utils/base-path.js";
1
2
  import { claimAppRouterScrollIntentForCommit, consumeAppRouterScrollIntent } from "../shims/app-router-scroll-state.js";
2
- import { activateNavigationSnapshot, clearPendingPathname, commitClientNavigationState } from "../shims/navigation.js";
3
+ import { activateNavigationSnapshot, clearPendingPathname, commitClientNavigationState, createSnapshotPathAndSearch } from "../shims/navigation.js";
3
4
  import { shouldScheduleRefreshForDiscardedServerAction } from "./app-browser-action-result.js";
4
5
  import { FRESH_APP_NAVIGATION_PAYLOAD_ORIGIN, createPendingNavigationCommit } from "./app-browser-state.js";
5
6
  import { applyApprovedVisibleCommit, approveHmrVisibleCommit, approvePendingNavigationCommit, resolveAndClassifyNavigationCommit } from "./app-browser-visible-commit.js";
6
7
  import { startTransition, useLayoutEffect } from "react";
8
+ import { flushSync } from "react-dom";
7
9
  //#region src/server/app-browser-navigation-controller.ts
8
10
  const HARD_NAVIGATION_LOOP_GUARD_KEY = "__vinext_hard_navigation_target__";
9
11
  function normalizeBrowserHref(href) {
@@ -49,7 +51,21 @@ function performHardNavigationWithLoopGuard(href, mode = "assign") {
49
51
  else window.location.assign(href);
50
52
  return true;
51
53
  }
54
+ function createBasePathStrippedPathAndSearch(url, basePath) {
55
+ const pathname = stripBasePath(url.pathname, basePath);
56
+ const query = new URLSearchParams(url.search).toString();
57
+ return query === "" ? pathname : `${pathname}?${query}`;
58
+ }
59
+ function isSnapshotTargetHref(basePath, snapshot, targetHref) {
60
+ try {
61
+ const baseHref = typeof window === "undefined" ? "http://localhost" : window.location.href;
62
+ return createBasePathStrippedPathAndSearch(new URL(targetHref, baseHref), basePath) === createSnapshotPathAndSearch(snapshot);
63
+ } catch {
64
+ return false;
65
+ }
66
+ }
52
67
  function createAppBrowserNavigationController(deps = {}) {
68
+ const basePath = deps.basePath ?? "";
53
69
  const commitClientNavigationStateImpl = deps.commitClientNavigationState ?? commitClientNavigationState;
54
70
  const performHardNavigation = deps.performHardNavigation ?? performHardNavigationWithLoopGuard;
55
71
  const getRouteManifest = deps.getRouteManifest ?? (() => null);
@@ -202,12 +218,18 @@ function createAppBrowserNavigationController(deps = {}) {
202
218
  }, [renderId]);
203
219
  return children;
204
220
  }
205
- function dispatchApprovedVisibleCommit(commit, pendingRouterState) {
221
+ function dispatchApprovedVisibleCommit(commit, pendingRouterState, visibleCommitMode) {
206
222
  const setter = getBrowserRouterStateSetter();
207
223
  if (pendingRouterState) {
208
224
  resolvePendingBrowserRouterState(pendingRouterState, commit);
209
225
  return;
210
226
  }
227
+ if (visibleCommitMode === "synchronous") {
228
+ flushSync(() => {
229
+ setter(applyApprovedVisibleCommit(getBrowserRouterState(), commit));
230
+ });
231
+ return;
232
+ }
211
233
  startTransition(() => {
212
234
  setter(applyApprovedVisibleCommit(getBrowserRouterState(), commit));
213
235
  });
@@ -215,6 +237,61 @@ function createAppBrowserNavigationController(deps = {}) {
215
237
  function dispatchSynchronousVisibleCommit(commit) {
216
238
  getBrowserRouterStateSetter()(applyApprovedVisibleCommit(getBrowserRouterState(), commit));
217
239
  }
240
+ function createRestoredHistorySnapshotCommit(options) {
241
+ const operation = {
242
+ id: options.renderId,
243
+ lane: "traverse",
244
+ startedVisibleCommitVersion: options.currentState.visibleCommitVersion,
245
+ state: "pending"
246
+ };
247
+ return {
248
+ action: {
249
+ bfcacheIds: options.restoredState.bfcacheIds,
250
+ elements: options.restoredState.elements,
251
+ interception: options.restoredState.interception,
252
+ interceptionContext: options.restoredState.interceptionContext,
253
+ layoutFlags: options.restoredState.layoutFlags,
254
+ layoutIds: options.restoredState.layoutIds,
255
+ navigationSnapshot: options.restoredState.navigationSnapshot,
256
+ operation,
257
+ previousNextUrl: options.restoredState.previousNextUrl,
258
+ renderId: options.renderId,
259
+ rootLayoutTreePath: options.restoredState.rootLayoutTreePath,
260
+ reuseCurrentBfcacheIds: false,
261
+ routeId: options.restoredState.routeId,
262
+ skippedLayoutIds: [],
263
+ slotBindings: options.restoredState.slotBindings,
264
+ type: "traverse"
265
+ },
266
+ interception: options.restoredState.interception,
267
+ interceptionContext: options.restoredState.interceptionContext,
268
+ previousNextUrl: options.restoredState.previousNextUrl,
269
+ rootLayoutTreePath: options.restoredState.rootLayoutTreePath,
270
+ routeId: options.restoredState.routeId,
271
+ skippedLayoutIds: []
272
+ };
273
+ }
274
+ function restoreHistorySnapshotVisibleState(options) {
275
+ if (!isSnapshotTargetHref(basePath, options.state.navigationSnapshot, options.targetHref)) return false;
276
+ const currentState = getBrowserRouterState();
277
+ const pending = createRestoredHistorySnapshotCommit({
278
+ currentState,
279
+ renderId: allocateRenderId(),
280
+ restoredState: options.state
281
+ });
282
+ const approval = approvePendingNavigationCommit({
283
+ activeNavigationId,
284
+ currentState,
285
+ pending,
286
+ routeManifest: getRouteManifest(),
287
+ startedNavigationId: options.navId,
288
+ targetHref: options.targetHref
289
+ });
290
+ if (approval.approvedCommit === null) return false;
291
+ options.beforeCommit?.();
292
+ dispatchSynchronousVisibleCommit(approval.approvedCommit);
293
+ return true;
294
+ }
218
295
  function notifyDiscardedServerActionRevalidation(lifecycleOptions) {
219
296
  if (!shouldScheduleRefreshForDiscardedServerAction(lifecycleOptions?.revalidation ?? "none")) return;
220
297
  lifecycleOptions?.onDiscardedRevalidation?.();
@@ -237,6 +314,7 @@ function createAppBrowserNavigationController(deps = {}) {
237
314
  previousNextUrl: options.previousNextUrl,
238
315
  renderId,
239
316
  restoredBfcacheIds: options.restoredBfcacheIds,
317
+ reuseCurrentBfcacheIds: options.reuseCurrentBfcacheIds,
240
318
  type: options.actionType
241
319
  });
242
320
  const approval = approvePendingNavigationCommit({
@@ -274,7 +352,7 @@ function createAppBrowserNavigationController(deps = {}) {
274
352
  claimAppRouterScrollIntentForCommit(options.scrollIntent, renderId);
275
353
  activateNavigationSnapshot();
276
354
  snapshotActivated = true;
277
- dispatchApprovedVisibleCommit(approvedCommit, options.pendingRouterState);
355
+ dispatchApprovedVisibleCommit(approvedCommit, options.pendingRouterState, options.visibleCommitMode ?? "transition");
278
356
  } catch (error) {
279
357
  pendingNavigationPrePaintEffects.delete(renderId);
280
358
  pendingNavigationCommits.delete(renderId);
@@ -354,6 +432,7 @@ function createAppBrowserNavigationController(deps = {}) {
354
432
  attachBrowserRouterState,
355
433
  beginPendingBrowserRouterState,
356
434
  finalizeNavigation,
435
+ restoreHistorySnapshotVisibleState,
357
436
  renderNavigationPayload,
358
437
  commitSameUrlNavigatePayload,
359
438
  hmrReplaceTree,
@@ -362,4 +441,4 @@ function createAppBrowserNavigationController(deps = {}) {
362
441
  };
363
442
  }
364
443
  //#endregion
365
- export { clearHardNavigationLoopGuard, createAppBrowserNavigationController };
444
+ export { clearHardNavigationLoopGuard, createAppBrowserNavigationController, createBasePathStrippedPathAndSearch, createSnapshotPathAndSearch };
@@ -1,17 +1,26 @@
1
+ import { NavigationRuntimeNavigate } from "../client/navigation-runtime.js";
2
+
1
3
  //#region src/server/app-browser-popstate.d.ts
2
4
  type RestoreScrollPosition = (state: unknown, options?: {
3
5
  shouldContinue?: () => boolean;
4
6
  }) => void;
5
- type NavigateRsc = (href: string, redirectDepth?: number, navigationKind?: "navigate" | "traverse" | "refresh") => Promise<void>;
6
7
  type BrowserPopstateRestoreDeps = {
7
8
  getActiveNavigationId: () => number;
8
9
  getPendingNavigation: () => Promise<void> | null | undefined;
9
- getNavigate: () => NavigateRsc | undefined;
10
+ getNavigate: () => NavigationRuntimeNavigate | undefined;
10
11
  isCurrentNavigation: (navId: number) => boolean;
11
12
  notifyAppRouterTransitionStart: (href: string) => void;
12
13
  restorePopstateScrollPosition: RestoreScrollPosition;
13
14
  setPendingNavigation: (pendingNavigation: Promise<void> | null) => void;
15
+ shouldSkipScrollRestore: (navId: number) => boolean;
14
16
  };
17
+ type SynchronousPopstateScrollRestoreDeps = {
18
+ getActiveNavigationId: () => number;
19
+ isCurrentNavigation: (navId: number) => boolean;
20
+ markScrollRestoreConsumed: (navId: number) => void;
21
+ restorePopstateScrollPosition: RestoreScrollPosition;
22
+ };
23
+ declare function restoreSynchronousPopstateScrollPosition(deps: SynchronousPopstateScrollRestoreDeps, state: unknown): void;
15
24
  declare function createPopstateRestoreHandler(deps: BrowserPopstateRestoreDeps): (event: PopStateEvent) => void;
16
25
  //#endregion
17
- export { createPopstateRestoreHandler };
26
+ export { createPopstateRestoreHandler, restoreSynchronousPopstateScrollPosition };
@@ -1,7 +1,14 @@
1
+ import { readHistoryStateTraversalIndex } from "./app-history-state.js";
2
+ import "./app-browser-state.js";
1
3
  //#region src/server/app-browser-popstate.ts
2
4
  function hasSavedScrollPosition(state) {
3
5
  return Boolean(state && typeof state === "object" && "__vinext_scrollY" in state);
4
6
  }
7
+ function restoreSynchronousPopstateScrollPosition(deps, state) {
8
+ const navId = deps.getActiveNavigationId();
9
+ deps.markScrollRestoreConsumed(navId);
10
+ deps.restorePopstateScrollPosition(state, { shouldContinue: () => deps.isCurrentNavigation(navId) });
11
+ }
5
12
  function scheduleAfterFrame(callback) {
6
13
  if (typeof window !== "undefined" && typeof window.requestAnimationFrame === "function") {
7
14
  window.requestAnimationFrame(callback);
@@ -9,21 +16,29 @@ function scheduleAfterFrame(callback) {
9
16
  }
10
17
  queueMicrotask(callback);
11
18
  }
19
+ function createPopstateTraversalIntent(historyState) {
20
+ return {
21
+ direction: "unknown",
22
+ historyState,
23
+ targetHistoryIndex: readHistoryStateTraversalIndex(historyState)
24
+ };
25
+ }
12
26
  function createPopstateRestoreHandler(deps) {
13
27
  return (event) => {
14
28
  deps.notifyAppRouterTransitionStart(window.location.href);
15
- const pendingNavigation = deps.getNavigate()?.(window.location.href, 0, "traverse") ?? Promise.resolve();
29
+ const pendingNavigation = deps.getNavigate()?.(window.location.href, 0, "traverse", void 0, void 0, false, createPopstateTraversalIntent(event.state)) ?? Promise.resolve();
16
30
  const popstateNavId = deps.getActiveNavigationId();
17
31
  deps.setPendingNavigation(pendingNavigation);
18
32
  const shouldRestoreSavedScroll = hasSavedScrollPosition(event.state);
33
+ const shouldRestoreScrollForNavigation = () => deps.isCurrentNavigation(popstateNavId) && !deps.shouldSkipScrollRestore(popstateNavId);
19
34
  if (shouldRestoreSavedScroll) scheduleAfterFrame(() => {
20
- if (deps.isCurrentNavigation(popstateNavId)) deps.restorePopstateScrollPosition(event.state, { shouldContinue: () => deps.isCurrentNavigation(popstateNavId) });
35
+ if (shouldRestoreScrollForNavigation()) deps.restorePopstateScrollPosition(event.state, { shouldContinue: shouldRestoreScrollForNavigation });
21
36
  });
22
37
  pendingNavigation.finally(() => {
23
- if (deps.isCurrentNavigation(popstateNavId) && !shouldRestoreSavedScroll) deps.restorePopstateScrollPosition(event.state);
38
+ if (shouldRestoreScrollForNavigation() && !shouldRestoreSavedScroll) deps.restorePopstateScrollPosition(event.state);
24
39
  if (deps.getPendingNavigation() === pendingNavigation) deps.setPendingNavigation(null);
25
40
  });
26
41
  };
27
42
  }
28
43
  //#endregion
29
- export { createPopstateRestoreHandler };
44
+ export { createPopstateRestoreHandler, restoreSynchronousPopstateScrollPosition };