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.
- package/README.md +2 -5
- package/dist/build/assets-ignore.d.ts +32 -0
- package/dist/build/assets-ignore.js +48 -0
- package/dist/build/client-build-config.d.ts +33 -1
- package/dist/build/client-build-config.js +66 -1
- package/dist/check.js +4 -3
- package/dist/cli.js +2 -0
- package/dist/client/navigation-runtime.d.ts +11 -2
- package/dist/client/navigation-runtime.js +1 -1
- package/dist/client/vinext-next-data.d.ts +2 -1
- package/dist/client/window-next.d.ts +6 -4
- package/dist/config/config-matchers.d.ts +31 -5
- package/dist/config/config-matchers.js +50 -3
- package/dist/config/next-config.d.ts +29 -3
- package/dist/config/next-config.js +32 -2
- package/dist/deploy.js +47 -304
- package/dist/entries/app-rsc-entry.d.ts +8 -2
- package/dist/entries/app-rsc-entry.js +61 -5
- package/dist/entries/app-rsc-manifest.js +20 -2
- package/dist/entries/pages-client-entry.js +1 -1
- package/dist/entries/pages-server-entry.js +16 -7
- package/dist/index.d.ts +0 -2
- package/dist/index.js +233 -280
- package/dist/plugins/dynamic-preload-metadata.d.ts +13 -0
- package/dist/plugins/dynamic-preload-metadata.js +415 -0
- package/dist/plugins/og-assets.js +2 -2
- package/dist/plugins/optimize-imports.d.ts +8 -4
- package/dist/plugins/optimize-imports.js +16 -12
- package/dist/plugins/postcss.js +18 -14
- package/dist/plugins/require-context.d.ts +6 -0
- package/dist/plugins/require-context.js +184 -0
- package/dist/plugins/sass.d.ts +53 -24
- package/dist/plugins/sass.js +249 -1
- package/dist/plugins/wasm-module-import.d.ts +15 -0
- package/dist/plugins/wasm-module-import.js +50 -0
- package/dist/routing/app-route-graph.d.ts +35 -2
- package/dist/routing/app-route-graph.js +179 -8
- package/dist/routing/file-matcher.js +1 -1
- package/dist/routing/route-pattern.d.ts +2 -1
- package/dist/routing/route-pattern.js +16 -1
- package/dist/server/api-handler.js +4 -0
- package/dist/server/app-browser-entry.js +155 -215
- package/dist/server/app-browser-error.d.ts +4 -1
- package/dist/server/app-browser-error.js +7 -1
- package/dist/server/app-browser-history-controller.d.ts +104 -0
- package/dist/server/app-browser-history-controller.js +210 -0
- package/dist/server/app-browser-interception-context.d.ts +2 -1
- package/dist/server/app-browser-interception-context.js +15 -2
- package/dist/server/app-browser-navigation-controller.d.ts +13 -2
- package/dist/server/app-browser-navigation-controller.js +83 -4
- package/dist/server/app-browser-popstate.d.ts +12 -3
- package/dist/server/app-browser-popstate.js +19 -4
- package/dist/server/app-browser-rsc-redirect.d.ts +11 -2
- package/dist/server/app-browser-rsc-redirect.js +30 -8
- package/dist/server/app-browser-state.d.ts +3 -0
- package/dist/server/app-browser-state.js +10 -10
- package/dist/server/app-browser-visible-commit.js +10 -8
- package/dist/server/app-fallback-renderer.d.ts +2 -1
- package/dist/server/app-fallback-renderer.js +3 -1
- package/dist/server/app-history-state.d.ts +45 -1
- package/dist/server/app-history-state.js +109 -1
- package/dist/server/app-middleware.js +1 -0
- package/dist/server/app-optimistic-routing.js +22 -1
- package/dist/server/app-page-boundary-render.d.ts +2 -1
- package/dist/server/app-page-boundary-render.js +45 -21
- package/dist/server/app-page-cache.js +9 -7
- package/dist/server/app-page-dispatch.d.ts +14 -0
- package/dist/server/app-page-dispatch.js +21 -6
- package/dist/server/app-page-element-builder.d.ts +23 -2
- package/dist/server/app-page-element-builder.js +58 -17
- package/dist/server/app-page-execution.d.ts +1 -1
- package/dist/server/app-page-execution.js +32 -17
- package/dist/server/app-page-render.d.ts +7 -1
- package/dist/server/app-page-render.js +11 -16
- package/dist/server/app-page-request.d.ts +9 -6
- package/dist/server/app-page-request.js +14 -10
- package/dist/server/app-page-response.d.ts +2 -2
- package/dist/server/app-page-response.js +2 -2
- package/dist/server/app-page-route-wiring.d.ts +3 -1
- package/dist/server/app-page-route-wiring.js +10 -8
- package/dist/server/app-page-stream.d.ts +37 -7
- package/dist/server/app-page-stream.js +36 -6
- package/dist/server/app-pages-bridge.d.ts +16 -0
- package/dist/server/app-pages-bridge.js +23 -3
- package/dist/server/app-route-handler-cache.d.ts +1 -0
- package/dist/server/app-route-handler-cache.js +1 -0
- package/dist/server/app-route-handler-dispatch.d.ts +1 -0
- package/dist/server/app-route-handler-dispatch.js +2 -0
- package/dist/server/app-route-handler-execution.d.ts +1 -0
- package/dist/server/app-route-handler-execution.js +1 -0
- package/dist/server/app-route-handler-response.js +11 -10
- package/dist/server/app-route-handler-runtime.d.ts +1 -0
- package/dist/server/app-route-handler-runtime.js +15 -3
- package/dist/server/app-rsc-handler.d.ts +1 -0
- package/dist/server/app-rsc-handler.js +5 -4
- package/dist/server/app-rsc-response-finalizer.js +1 -1
- package/dist/server/app-rsc-route-matching.d.ts +20 -1
- package/dist/server/app-rsc-route-matching.js +29 -4
- package/dist/server/app-server-action-execution.d.ts +22 -1
- package/dist/server/app-server-action-execution.js +73 -12
- package/dist/server/app-ssr-entry.d.ts +6 -0
- package/dist/server/app-ssr-entry.js +19 -3
- package/dist/server/app-ssr-stream.js +9 -1
- package/dist/server/dev-lockfile.js +2 -1
- package/dist/server/dev-server.d.ts +1 -1
- package/dist/server/dev-server.js +97 -43
- package/dist/server/headers.d.ts +8 -1
- package/dist/server/headers.js +8 -1
- package/dist/server/instrumentation-runtime.d.ts +6 -0
- package/dist/server/instrumentation-runtime.js +8 -0
- package/dist/server/isr-cache.d.ts +37 -1
- package/dist/server/isr-cache.js +85 -1
- package/dist/server/isr-decision.d.ts +79 -0
- package/dist/server/isr-decision.js +70 -0
- package/dist/server/metadata-route-response.js +5 -3
- package/dist/server/middleware-runtime.d.ts +13 -0
- package/dist/server/middleware-runtime.js +11 -7
- package/dist/server/middleware.js +1 -0
- package/dist/server/navigation-planner.d.ts +62 -1
- package/dist/server/navigation-planner.js +193 -3
- package/dist/server/navigation-trace.d.ts +12 -2
- package/dist/server/navigation-trace.js +11 -1
- package/dist/server/normalize-path.d.ts +0 -8
- package/dist/server/normalize-path.js +3 -1
- package/dist/server/otel-tracer-extension.d.ts +45 -0
- package/dist/server/otel-tracer-extension.js +89 -0
- package/dist/server/pages-api-route.d.ts +14 -3
- package/dist/server/pages-api-route.js +6 -1
- package/dist/server/pages-asset-tags.d.ts +15 -4
- package/dist/server/pages-asset-tags.js +18 -12
- package/dist/server/pages-data-route.js +5 -1
- package/dist/server/pages-node-compat.d.ts +5 -11
- package/dist/server/pages-node-compat.js +175 -118
- package/dist/server/pages-page-data.d.ts +38 -7
- package/dist/server/pages-page-data.js +64 -18
- package/dist/server/pages-page-handler.d.ts +10 -2
- package/dist/server/pages-page-handler.js +49 -20
- package/dist/server/pages-page-response.d.ts +55 -2
- package/dist/server/pages-page-response.js +74 -6
- package/dist/server/pages-readiness.d.ts +36 -0
- package/dist/server/pages-readiness.js +21 -0
- package/dist/server/pages-request-pipeline.d.ts +113 -0
- package/dist/server/pages-request-pipeline.js +230 -0
- package/dist/server/pages-revalidate.d.ts +15 -0
- package/dist/server/pages-revalidate.js +19 -0
- package/dist/server/prod-server.d.ts +45 -3
- package/dist/server/prod-server.js +182 -234
- package/dist/server/socket-error-backstop.d.ts +19 -1
- package/dist/server/socket-error-backstop.js +77 -4
- package/dist/shims/app-router-scroll.js +22 -4
- package/dist/shims/cache-runtime.js +39 -2
- package/dist/shims/dynamic-preload-chunks.d.ts +8 -0
- package/dist/shims/dynamic-preload-chunks.js +77 -0
- package/dist/shims/dynamic.d.ts +4 -0
- package/dist/shims/dynamic.js +4 -2
- package/dist/shims/error-boundary.d.ts +17 -7
- package/dist/shims/error-boundary.js +8 -1
- package/dist/shims/error.js +37 -11
- package/dist/shims/fetch-cache.d.ts +22 -1
- package/dist/shims/fetch-cache.js +28 -1
- package/dist/shims/hash-scroll.d.ts +1 -0
- package/dist/shims/hash-scroll.js +3 -1
- package/dist/shims/head.js +6 -1
- package/dist/shims/headers.d.ts +16 -2
- package/dist/shims/headers.js +37 -1
- package/dist/shims/image-config.js +7 -1
- package/dist/shims/internal/app-route-detection.d.ts +6 -3
- package/dist/shims/internal/app-route-detection.js +10 -6
- package/dist/shims/internal/app-router-context.d.ts +5 -0
- package/dist/shims/internal/link-status-registry.d.ts +43 -0
- package/dist/shims/internal/link-status-registry.js +42 -0
- package/dist/shims/internal/route-pattern-for-warning.d.ts +27 -0
- package/dist/shims/internal/route-pattern-for-warning.js +40 -0
- package/dist/shims/internal/utils.d.ts +1 -0
- package/dist/shims/link.js +20 -6
- package/dist/shims/metadata.d.ts +6 -2
- package/dist/shims/metadata.js +32 -14
- package/dist/shims/navigation.d.ts +9 -18
- package/dist/shims/navigation.js +96 -23
- package/dist/shims/router-state.d.ts +1 -0
- package/dist/shims/router-state.js +2 -0
- package/dist/shims/router.d.ts +6 -3
- package/dist/shims/router.js +156 -22
- package/dist/shims/script-nonce-context.d.ts +1 -1
- package/dist/shims/script-nonce-context.js +11 -3
- package/dist/shims/server.d.ts +17 -1
- package/dist/shims/server.js +31 -6
- package/dist/shims/slot.js +1 -1
- package/dist/shims/unified-request-context.js +1 -0
- package/dist/typegen.js +1 -0
- package/dist/utils/client-build-manifest.d.ts +8 -1
- package/dist/utils/client-build-manifest.js +41 -6
- package/dist/utils/client-entry-manifest.d.ts +11 -0
- package/dist/utils/client-entry-manifest.js +29 -0
- package/dist/utils/client-runtime-metadata.d.ts +45 -0
- package/dist/utils/client-runtime-metadata.js +63 -0
- package/dist/utils/hash.d.ts +17 -1
- package/dist/utils/hash.js +36 -1
- package/dist/utils/lazy-chunks.d.ts +27 -1
- package/dist/utils/lazy-chunks.js +65 -1
- package/dist/utils/manifest-paths.d.ts +20 -2
- package/dist/utils/manifest-paths.js +38 -3
- package/dist/utils/path.d.ts +2 -1
- package/dist/utils/path.js +5 -1
- 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: () =>
|
|
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 (
|
|
35
|
+
if (shouldRestoreScrollForNavigation()) deps.restorePopstateScrollPosition(event.state, { shouldContinue: shouldRestoreScrollForNavigation });
|
|
21
36
|
});
|
|
22
37
|
pendingNavigation.finally(() => {
|
|
23
|
-
if (
|
|
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 };
|