From 42cfc71bcb07287cd4e2c3850b82d6d0f7271c48 Mon Sep 17 00:00:00 2001 From: Nathan Nguyen <146415969+NathanDrake2406@users.noreply.github.com> Date: Sun, 14 Jun 2026 15:24:04 +1000 Subject: [PATCH] refactor(navigation): drop V0 suffix from planner types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The navigation planner's public and internal types were suffixed `V0` (RouteSnapshotV0, NavigationDecisionV0, FlightResultV0, and 19 others). The suffix implied a versioning scheme that never materialized — there is no V1, and the schema-version concerns it hinted at are carried explicitly elsewhere (NAVIGATION_TRACE_SCHEMA_VERSION, CACHE_PROOF_MODEL_SCHEMA_VERSION). The bare suffix was pure noise at every call site. Rename all 22 `…V0` planner types to their unsuffixed names across the planner, the browser state/entry/action modules, and the planner test suite. Pure mechanical rename: no behavior change, all 295 planner and browser-entry tests pass unchanged. --- .../src/server/app-browser-action-result.ts | 4 +- .../vinext/src/server/app-browser-entry.ts | 12 +- .../app-browser-server-action-navigation.ts | 6 +- .../vinext/src/server/app-browser-state.ts | 22 +-- .../vinext/src/server/navigation-planner.ts | 174 +++++++++--------- tests/navigation-planner-early-intent.test.ts | 14 +- .../navigation-planner-prefetch-reuse.test.ts | 14 +- ...avigation-planner-rsc-fetch-result.test.ts | 10 +- ...ation-planner-server-action-result.test.ts | 22 +-- tests/navigation-planner.test.ts | 144 +++++++-------- 10 files changed, 206 insertions(+), 216 deletions(-) diff --git a/packages/vinext/src/server/app-browser-action-result.ts b/packages/vinext/src/server/app-browser-action-result.ts index 3c465572f..cf56e93be 100644 --- a/packages/vinext/src/server/app-browser-action-result.ts +++ b/packages/vinext/src/server/app-browser-action-result.ts @@ -1,6 +1,6 @@ import { ACTION_REVALIDATED_HEADER } from "./headers.js"; import { VINEXT_RSC_CONTENT_TYPE } from "./app-rsc-cache-busting.js"; -import { ServerActionResultFactsV0 } from "./navigation-planner.js"; +import { ServerActionResultFacts } from "./navigation-planner.js"; export type AppBrowserServerActionResult = { root?: TRoot; @@ -124,7 +124,7 @@ export type ServerActionResultResponseFactsInput = { */ export function createServerActionResultFacts( input: ServerActionResultResponseFactsInput, -): ServerActionResultFactsV0 { +): ServerActionResultFacts { return { actionRedirectHref: input.actionRedirectHref, actionRedirectType: input.actionRedirectType === "push" ? "push" : "replace", diff --git a/packages/vinext/src/server/app-browser-entry.ts b/packages/vinext/src/server/app-browser-entry.ts index 6e2a24a2a..1f7077e0d 100644 --- a/packages/vinext/src/server/app-browser-entry.ts +++ b/packages/vinext/src/server/app-browser-entry.ts @@ -177,8 +177,8 @@ import { import { removeStylesheetLinksCoveredByInlineCss } from "./app-inline-css-client.js"; import { navigationPlanner, - type NavigationReuseFactsV0, - type VisitedResponseCacheCandidateFactsV0, + type NavigationReuseFacts, + type VisitedResponseCacheCandidateFacts, } from "./navigation-planner.js"; type SearchParamInput = ConstructorParameters[0]; @@ -714,12 +714,12 @@ type VisitedResponseCacheCandidate = | { cacheKey: string; entry: VisitedResponseCacheEntry; - facts: Extract; + facts: Extract; } | { cacheKey: string; entry: null; - facts: Extract; + facts: Extract; }; function readVisitedResponseCacheCandidate( @@ -1791,7 +1791,7 @@ function bootstrapHydration(rscStream: ReadableStream): void { facts: { candidate: "missing", navigationKind, - } satisfies Extract, + } satisfies Extract, } : readVisitedResponseCacheCandidate( rscUrl, @@ -1806,7 +1806,7 @@ function bootstrapHydration(rscStream: ReadableStream): void { visitedResponseCandidate, visitedResponseDecision, ); - const visitedResponse: NavigationReuseFactsV0["visitedResponse"] = + const visitedResponse: NavigationReuseFacts["visitedResponse"] = cachedRoute === null ? { status: "unavailable" } : { status: "available" }; const prefetchProbeDecision = navigationPlanner.classifyNavigationPrefetchProbe({ bypassNavigationCache: shouldBypassNavigationCache, diff --git a/packages/vinext/src/server/app-browser-server-action-navigation.ts b/packages/vinext/src/server/app-browser-server-action-navigation.ts index 028276c60..ec4e8e912 100644 --- a/packages/vinext/src/server/app-browser-server-action-navigation.ts +++ b/packages/vinext/src/server/app-browser-server-action-navigation.ts @@ -1,11 +1,11 @@ -import type { ServerActionResultDecisionV0 } from "./navigation-planner.js"; +import type { ServerActionResultDecision } from "./navigation-planner.js"; -// Dispatches the executor effects implied by a ServerActionResultDecisionV0. +// Dispatches the executor effects implied by a ServerActionResultDecision. // Returns true if a hard-navigation was triggered (the caller should return early); // false if the decision is "proceed" and normal action processing should continue. // Both callbacks are injected so this function is testable without browser globals. export function applyServerActionResultDecision( - decision: ServerActionResultDecisionV0, + decision: ServerActionResultDecision, clearCaches: () => void, performHardNavigation: (url: string, historyMode?: "assign" | "replace") => void, ): boolean { diff --git a/packages/vinext/src/server/app-browser-state.ts b/packages/vinext/src/server/app-browser-state.ts index 0fe07ee53..b7ba821b5 100644 --- a/packages/vinext/src/server/app-browser-state.ts +++ b/packages/vinext/src/server/app-browser-state.ts @@ -27,11 +27,11 @@ import { createCacheEntryReuseProof, type CacheEntryReuseProof } from "./cache-p import { navigationPlanner, resolveDefaultOrUnmatchedSlotPersistenceForLayouts, - type MountedParallelSlotSnapshotV0, - type NavigationDecisionV0, + type MountedParallelSlotSnapshot, + type NavigationDecision, type OperationLane, type OperationToken, - type RouteSnapshotV0, + type RouteSnapshot, } from "./navigation-planner.js"; import { createSnapshotPathAndSearch, @@ -569,8 +569,8 @@ function createPendingNavigationTraceFields(options: { function createMountedParallelSlotSnapshots( elements: AppElements, -): readonly MountedParallelSlotSnapshotV0[] { - const snapshots: MountedParallelSlotSnapshotV0[] = []; +): readonly MountedParallelSlotSnapshot[] { + const snapshots: MountedParallelSlotSnapshot[] = []; for (const slotId of getMountedSlotIds(elements)) { const parsed = AppElementsWire.parseElementKey(slotId); if (parsed?.kind !== "slot") continue; @@ -582,7 +582,7 @@ function createMountedParallelSlotSnapshots( return snapshots; } -function createVisibleRouteSnapshot(state: AppRouterState): RouteSnapshotV0 { +function createVisibleRouteSnapshot(state: AppRouterState): RouteSnapshot { const displayUrl = createSnapshotPathAndSearch(state.navigationSnapshot); const matchedUrl = normalizeNavigationSnapshotMatchedUrl(state.navigationSnapshot.pathname); return { @@ -605,7 +605,7 @@ function createVisibleRouteSnapshot(state: AppRouterState): RouteSnapshotV0 { }; } -function createPendingRouteSnapshot(pending: PendingNavigationCommit): RouteSnapshotV0 { +function createPendingRouteSnapshot(pending: PendingNavigationCommit): RouteSnapshot { const displayUrl = createSnapshotPathAndSearch(pending.action.navigationSnapshot); const matchedUrl = normalizeNavigationSnapshotMatchedUrl( pending.action.navigationSnapshot.pathname, @@ -631,7 +631,7 @@ function createPendingRouteSnapshot(pending: PendingNavigationCommit): RouteSnap function createPendingNavigationOperationToken(options: { pending: PendingNavigationCommit; routeManifest: RouteManifest | null; - targetSnapshot: RouteSnapshotV0; + targetSnapshot: RouteSnapshot; }): OperationToken { return { baseVisibleCommitVersion: options.pending.action.operation.startedVisibleCommitVersion, @@ -643,7 +643,7 @@ function createPendingNavigationOperationToken(options: { }; } -function createRootBoundarySnapshotFingerprint(snapshot: RouteSnapshotV0): string { +function createRootBoundarySnapshotFingerprint(snapshot: RouteSnapshot): string { return `${snapshot.routeId}|root:${snapshot.rootBoundaryId ?? "unknown"}`; } @@ -653,7 +653,7 @@ function planPendingRootBoundaryFlightResponse(options: { routeManifest: RouteManifest | null; targetHref?: string; traceFields: NavigationTraceFields; -}): NavigationDecisionV0 { +}): NavigationDecision { const targetSnapshot = createPendingRouteSnapshot(options.pending); const token = createPendingNavigationOperationToken({ pending: options.pending, @@ -691,7 +691,7 @@ function planPendingRootBoundaryFlightResponse(options: { } function mapNavigationDecisionToPendingDisposition( - decision: NavigationDecisionV0, + decision: NavigationDecision, ): PendingNavigationCommitDispositionDecision { switch (decision.kind) { case "proposeCommit": diff --git a/packages/vinext/src/server/navigation-planner.ts b/packages/vinext/src/server/navigation-planner.ts index 22e0fb5d3..b7cba02fa 100644 --- a/packages/vinext/src/server/navigation-planner.ts +++ b/packages/vinext/src/server/navigation-planner.ts @@ -51,21 +51,21 @@ export type OperationToken = { cacheVariantFingerprint?: string; }; -export type RouteSnapshotV0 = { - interception: InterceptionSnapshotV0 | null; +export type RouteSnapshot = { + interception: InterceptionSnapshot | null; interceptionContext: string | null; routeId: string; // Ordered ancestor-first, with the root layout at index 0. Same-layout // persistence uses prefix comparison, so callers must preserve this order. layoutIds: readonly string[]; - mountedParallelSlots: readonly MountedParallelSlotSnapshotV0[]; + mountedParallelSlots: readonly MountedParallelSlotSnapshot[]; rootBoundaryId: string | null; displayUrl: string; matchedUrl: string; - slotBindings: readonly ParallelSlotBindingSnapshotV0[]; + slotBindings: readonly ParallelSlotBindingSnapshot[]; }; -export type InterceptionSnapshotV0 = { +export type InterceptionSnapshot = { sourceMatchedUrl: string; sourceRouteId: string; slotId: string; @@ -73,7 +73,7 @@ export type InterceptionSnapshotV0 = { targetRouteId: string; }; -export type MountedParallelSlotSnapshotV0 = { +export type MountedParallelSlotSnapshot = { slotId: string; ownerLayoutId: string | null; }; @@ -82,20 +82,20 @@ export type MountedParallelSlotSnapshotV0 = { // AppElements metadata. Keep the alias explicit so route-state and transport // readers cannot drift into structurally identical but semantically separate // shapes. -export type ParallelSlotBindingSnapshotV0 = AppElementsSlotBinding; +export type ParallelSlotBindingSnapshot = AppElementsSlotBinding; -export type NavigationPlannerStateV0 = { - // V0 keeps a single state shape so intent events and result events can move - // through one planner surface. flightResponseArrived uses event.token; later - // #726 slices can split this by event kind once more result paths are routed - // through the planner. +export type NavigationPlannerState = { + // A single state shape lets intent events and result events move through one + // planner surface. flightResponseArrived uses event.token; later #726 slices + // can split this by event kind once more result paths are routed through the + // planner. nextOperationToken: OperationToken; // Callers that have lifecycle authority should pass the complete trace // context. When absent, the planner emits the stable root-boundary facts it // can derive from the event and visible snapshot. traceFields?: NavigationTraceFields; visibleCommitVersion: number; - visibleSnapshot: RouteSnapshotV0; + visibleSnapshot: RouteSnapshot; }; export type RefreshScope = "visible"; @@ -106,7 +106,7 @@ export type NavigationEvent = | { kind: "refresh"; scope: RefreshScope } | { kind: "traverse"; direction: TraverseDirection; historyState: unknown } | { kind: "prefetch"; href: string } - | { kind: "flightResponseArrived"; token: OperationToken; result: FlightResultV0 }; + | { kind: "flightResponseArrived"; token: OperationToken; result: FlightResult }; type RequestedWork = | { kind: "flight"; href: string; mode: "push" | "replace" | "refresh" } @@ -119,7 +119,7 @@ type CommitProposal = { preserveElementIds: readonly string[]; preservePreviousSlotIds: readonly string[]; reason: "currentRootBoundary" | "interceptedCurrentRootBoundary" | "unprovenTopologyFallback"; - targetSnapshot: RouteSnapshotV0; + targetSnapshot: RouteSnapshot; }; type NoCommitReason = "prefetchOnly"; @@ -132,7 +132,7 @@ export type RootBoundaryTransition = | "rootBoundaryChanged" | "rootBoundaryUnknown"; -export type NavigationDecisionV0 = +export type NavigationDecision = | { kind: "requestWork"; token: OperationToken; @@ -159,16 +159,16 @@ export type NavigationDecisionV0 = trace: NavigationTrace; }; -export type FlightResultV0 = { +export type FlightResult = { cacheEntryReuseProof?: CacheEntryReuseProof; href: string; - targetSnapshot: RouteSnapshotV0; + targetSnapshot: RouteSnapshot; }; type RscFetchResultSource = "cached" | "live"; type RscRedirectSignal = "response-url" | "streamed-header"; -export type RscFetchResultFactsV0 = { +export type RscFetchResultFacts = { source: RscFetchResultSource; currentHref: string; origin: string; @@ -184,7 +184,7 @@ export type RscFetchResultFactsV0 = { streamedRedirectTarget: string | null; }; -type RscRedirectFollowV0 = { +type RscRedirectFollow = { href: string; historyUpdateMode: "push" | "replace"; previousNextUrl: string | null; @@ -198,12 +198,12 @@ type RscFetchResultHardNavReason = | "redirectDepthExhausted" | "streamedRedirectLoop"; -export type RscFetchResultDecisionV0 = +export type RscFetchResultDecision = | { kind: "proceedToCommit"; discardBody: false; trace: NavigationTrace } | { kind: "followRedirect"; discardBody: boolean; - redirect: RscRedirectFollowV0; + redirect: RscRedirectFollow; trace: NavigationTrace; } | { @@ -220,10 +220,10 @@ export type RscFetchResultDecisionV0 = // outcome (same-document scroll vs cache-bypassing flight vs ordinary flight), // the executor owns the effects (history mutation, scroll, RSC fetch). // -// V0 only needs the URL delta plus history/scroll intent. Richer planner inputs -// (route manifest, mounted slots) join later slices once prefetch reuse and the -// remaining hard-navigation causes route through this surface. -export type EarlyNavigationIntentFactsV0 = { +// This surface only needs the URL delta plus history/scroll intent. Richer +// planner inputs (route manifest, mounted slots) join later slices once prefetch +// reuse and the remaining hard-navigation causes route through this surface. +export type EarlyNavigationIntentFacts = { // App basePath, stripped from both pathnames before comparison. basePath: string; // The current visible document URL (window.location.href at navigation start), @@ -237,7 +237,7 @@ export type EarlyNavigationIntentFactsV0 = { targetHref: string; }; -export type EarlyNavigationIntentDecisionV0 = +export type EarlyNavigationIntentDecision = | { kind: "sameDocumentScroll"; // Always non-empty: same-document scroll is only chosen for a hash target. @@ -256,7 +256,7 @@ export type EarlyNavigationIntentDecisionV0 = type NavigationReuseNavigationKind = "navigate" | "refresh" | "traverse"; -export type VisitedResponseCacheCandidateFactsV0 = +export type VisitedResponseCacheCandidateFacts = | { candidate: "missing"; navigationKind: NavigationReuseNavigationKind; @@ -268,7 +268,7 @@ export type VisitedResponseCacheCandidateFactsV0 = navigationKind: NavigationReuseNavigationKind; }; -type VisitedResponseCacheCandidateDecisionV0 = +type VisitedResponseCacheCandidateDecision = | { kind: "miss" } | { kind: "evict"; @@ -281,7 +281,7 @@ type OptimisticRouteShellCandidateAvailability = | { status: "available" } | { status: "unavailable"; reason: "routeManifestMissing" }; -export type NavigationReuseFactsV0 = { +export type NavigationReuseFacts = { bypassNavigationCache: boolean; navigationKind: NavigationReuseNavigationKind; optimisticRouteShell: OptimisticRouteShellCandidateAvailability; @@ -292,19 +292,19 @@ export type NavigationReuseFactsV0 = { type FreshFetchReason = "cacheBypassed" | "cacheMiss" | "refresh" | "routeManifestMissing"; -export type NavigationReuseDecisionV0 = +export type NavigationReuseDecision = | { kind: "reuseVisitedResponse"; trace: NavigationTrace } | { kind: "consumePrefetch"; trace: NavigationTrace } | { kind: "attemptOptimisticRouteShell"; trace: NavigationTrace } | { kind: "fetchFresh"; reason: FreshFetchReason; trace: NavigationTrace }; -type NavigationPrefetchProbeFactsV0 = { +type NavigationPrefetchProbeFacts = { bypassNavigationCache: boolean; navigationKind: NavigationReuseNavigationKind; visitedResponse: NavigationReuseCandidateAvailability; }; -type NavigationPrefetchProbeDecisionV0 = +type NavigationPrefetchProbeDecision = | { kind: "probe" } | { kind: "skip"; reason: "cacheBypassed" | "refresh" | "visitedResponseAvailable" }; @@ -313,7 +313,7 @@ export type NavigationPlannerInput = { // decisions whenever the caller can supply it. Null keeps the legacy // snapshot-only path for low-level tests and unknown route shapes. routeManifest: RouteManifest | null; - state: NavigationPlannerStateV0; + state: NavigationPlannerState; event: NavigationEvent; }; @@ -321,7 +321,7 @@ type RouteTopologySnapshot = { layoutIds: readonly string[]; rootBoundaryId: string | null; rootLayoutTreePath: string | null; - slotBindings: readonly ParallelSlotBindingSnapshotV0[]; + slotBindings: readonly ParallelSlotBindingSnapshot[]; }; type RouteTopologyResolution = @@ -352,9 +352,9 @@ const CACHE_ENTRY_PROOF_MISSING_CODE = function createRequestWorkDecision(options: { eventKind: NavigationEvent["kind"]; - state: NavigationPlannerStateV0; + state: NavigationPlannerState; work: RequestedWork; -}): NavigationDecisionV0 { +}): NavigationDecision { const traverseFields = options.work.kind === "traverseFlight" ? { traverseDirection: options.work.direction } : {}; return { @@ -384,7 +384,7 @@ function getRequestedWorkTargetHref(work: RequestedWork): string | null { } function createRscFetchResultTraceFields( - facts: RscFetchResultFactsV0, + facts: RscFetchResultFacts, fields: NavigationTraceFields = {}, ): NavigationTraceFields { return { @@ -395,12 +395,12 @@ function createRscFetchResultTraceFields( function createRscFetchResultHardNavigationDecision(options: { discardBody: boolean; - facts: RscFetchResultFactsV0; + facts: RscFetchResultFacts; reason: RscFetchResultHardNavReason; reasonCode: NavigationTraceReasonCode; redirectSignal?: RscRedirectSignal; url: string; -}): RscFetchResultDecisionV0 { +}): RscFetchResultDecision { return { discardBody: options.discardBody, kind: "hardNavigate", @@ -421,10 +421,10 @@ function createRscFetchResultHardNavigationDecision(options: { function createRscFetchResultFollowRedirectDecision(options: { discardBody: boolean; - facts: RscFetchResultFactsV0; - redirect: RscRedirectFollowV0; + facts: RscFetchResultFacts; + redirect: RscRedirectFollow; redirectSignal: RscRedirectSignal; -}): RscFetchResultDecisionV0 { +}): RscFetchResultDecision { return { discardBody: options.discardBody, kind: "followRedirect", @@ -462,7 +462,7 @@ function mapRscRedirectTerminalReason(reason: "externalRedirect" | "maxRedirects } } -function classifyRscFetchResult(facts: RscFetchResultFactsV0): RscFetchResultDecisionV0 { +function classifyRscFetchResult(facts: RscFetchResultFacts): RscFetchResultDecision { if (!facts.responseOk || !facts.isRscContentType || !facts.hasBody) { const url = resolveHardNavigationTargetFromRscResponse( facts.responseUrl, @@ -586,14 +586,14 @@ function classifyRscFetchResult(facts: RscFetchResultFactsV0): RscFetchResultDec function createEarlyNavigationIntentTrace( reasonCode: NavigationTraceReasonCode, - facts: EarlyNavigationIntentFactsV0, + facts: EarlyNavigationIntentFacts, ): NavigationTrace { return createNavigationTrace(reasonCode, { targetHref: facts.targetHref }); } function classifyEarlyNavigationIntent( - facts: EarlyNavigationIntentFactsV0, -): EarlyNavigationIntentDecisionV0 { + facts: EarlyNavigationIntentFacts, +): EarlyNavigationIntentDecision { let current: URL; let next: URL; try { @@ -656,8 +656,8 @@ function classifyEarlyNavigationIntent( } function classifyVisitedResponseCacheCandidate( - facts: VisitedResponseCacheCandidateFactsV0, -): VisitedResponseCacheCandidateDecisionV0 { + facts: VisitedResponseCacheCandidateFacts, +): VisitedResponseCacheCandidateDecision { if (facts.candidate === "missing") { return { kind: "miss" }; } @@ -688,7 +688,7 @@ function classifyVisitedResponseCacheCandidate( function createNavigationReuseTrace( code: NavigationTraceReasonCode, - facts: NavigationReuseFactsV0, + facts: NavigationReuseFacts, fields: NavigationTraceFields = {}, ): NavigationTrace { return createNavigationTrace(code, { @@ -699,9 +699,9 @@ function createNavigationReuseTrace( } function createFreshFetchDecision( - facts: NavigationReuseFactsV0, + facts: NavigationReuseFacts, reason: FreshFetchReason, -): NavigationReuseDecisionV0 { +): NavigationReuseDecision { return { kind: "fetchFresh", reason, @@ -711,7 +711,7 @@ function createFreshFetchDecision( }; } -function classifyNavigationReuse(facts: NavigationReuseFactsV0): NavigationReuseDecisionV0 { +function classifyNavigationReuse(facts: NavigationReuseFacts): NavigationReuseDecision { if (facts.navigationKind === "refresh") { return createFreshFetchDecision(facts, "refresh"); } @@ -752,8 +752,8 @@ function classifyNavigationReuse(facts: NavigationReuseFactsV0): NavigationReuse } function classifyNavigationPrefetchProbe( - facts: NavigationPrefetchProbeFactsV0, -): NavigationPrefetchProbeDecisionV0 { + facts: NavigationPrefetchProbeFacts, +): NavigationPrefetchProbeDecision { if (facts.visitedResponse.status === "available") { return { kind: "skip", reason: "visitedResponseAvailable" }; } @@ -769,7 +769,7 @@ function classifyNavigationPrefetchProbe( return { kind: "probe" }; } -function createSnapshotRouteTopology(snapshot: RouteSnapshotV0): RouteTopologySnapshot { +function createSnapshotRouteTopology(snapshot: RouteSnapshot): RouteTopologySnapshot { return { layoutIds: snapshot.layoutIds, rootBoundaryId: snapshot.rootBoundaryId, @@ -835,7 +835,7 @@ function findRouteManifestRouteByIdOrMatchedUrl(options: { function findRouteManifestRouteForSnapshot( routeManifest: RouteManifest, - snapshot: RouteSnapshotV0, + snapshot: RouteSnapshot, ): RouteManifestRoute | null { if (snapshot.interception !== null) { return findRouteManifestRouteByIdOrMatchedUrl({ @@ -855,8 +855,8 @@ function findRouteManifestRouteForSnapshot( function resolveRouteManifestSlotBindings( routeManifest: RouteManifest, route: RouteManifestRoute, -): readonly ParallelSlotBindingSnapshotV0[] { - const bindings: ParallelSlotBindingSnapshotV0[] = []; +): readonly ParallelSlotBindingSnapshot[] { + const bindings: ParallelSlotBindingSnapshot[] = []; for (const slotId of route.slotIds) { const binding = routeManifest.segmentGraph.slotBindings.get(`${route.id}::${slotId}`); if (!binding) continue; @@ -881,7 +881,7 @@ function resolveRouteManifestRootLayoutTreePath( function resolveRouteTopologySnapshot(options: { routeManifest: RouteManifest | null; slotBindingSource: RouteTopologySlotBindingSource; - snapshot: RouteSnapshotV0; + snapshot: RouteSnapshot; }): RouteTopologyResolution { const route = options.routeManifest === null @@ -911,7 +911,7 @@ function resolveRouteTopologySnapshot(options: { function findRouteManifestInterceptionForProof( routeManifest: RouteManifest, - proof: InterceptionSnapshotV0, + proof: InterceptionSnapshot, ): RouteManifestInterception | null { const sourceParts = splitMatchedUrlIntoRouteParts(proof.sourceMatchedUrl); const targetParts = splitMatchedUrlIntoRouteParts(proof.targetMatchedUrl); @@ -951,7 +951,7 @@ function createRootBoundaryTraceFields(options: { currentRootLayoutTreePath: string | null; event: Extract; nextRootLayoutTreePath: string | null; - state: NavigationPlannerStateV0; + state: NavigationPlannerState; }): NavigationTraceFields { // Browser commit approval supplies lifecycle trace context before calling // the planner. This fallback exists for pure planner callers and tests; it @@ -986,8 +986,8 @@ function classifyRootBoundaryTransition( } function resolveSameLayoutAncestorPersistence( - currentSnapshot: RouteSnapshotV0, - targetSnapshot: RouteSnapshotV0, + currentSnapshot: RouteSnapshot, + targetSnapshot: RouteSnapshot, ): readonly string[] { return resolveSameLayoutAncestorPersistenceForTopologies( createSnapshotRouteTopology(currentSnapshot), @@ -1019,15 +1019,15 @@ function resolveSameLayoutAncestorPersistenceForTopologies( } function resolveMountedParallelSlotPersistence( - currentSnapshot: RouteSnapshotV0, - targetSnapshot: RouteSnapshotV0, + currentSnapshot: RouteSnapshot, + targetSnapshot: RouteSnapshot, ): readonly string[] { const preservedLayoutIds = resolveSameLayoutAncestorPersistence(currentSnapshot, targetSnapshot); return resolveMountedParallelSlotPersistenceForLayouts(currentSnapshot, preservedLayoutIds); } function resolveMountedParallelSlotPersistenceForLayouts( - currentSnapshot: RouteSnapshotV0, + currentSnapshot: RouteSnapshot, preservedLayoutIds: readonly string[], ): readonly string[] { if (preservedLayoutIds.length === 0) return []; @@ -1047,8 +1047,8 @@ function resolveMountedParallelSlotPersistenceForLayouts( } function resolveCurrentRootBoundaryElementPersistence( - currentSnapshot: RouteSnapshotV0, - targetSnapshot: RouteSnapshotV0, + currentSnapshot: RouteSnapshot, + targetSnapshot: RouteSnapshot, ): readonly string[] { const preservedLayoutIds = resolveSameLayoutAncestorPersistence(currentSnapshot, targetSnapshot); // Non-commit consumers still receive the legacy mounted-slot element list. @@ -1108,9 +1108,9 @@ function resolveCurrentRootBoundaryCommitSlotPersistence(options: { * Wire absence and UNMATCHED_SLOT markers are not semantic proof. */ export function resolveDefaultOrUnmatchedSlotPersistenceForLayouts(options: { - currentSlotBindings: readonly ParallelSlotBindingSnapshotV0[]; + currentSlotBindings: readonly ParallelSlotBindingSnapshot[]; preservedLayoutIds: readonly string[]; - targetSlotBindings: readonly ParallelSlotBindingSnapshotV0[]; + targetSlotBindings: readonly ParallelSlotBindingSnapshot[]; }): readonly string[] { const preservedLayoutIdSet = new Set(options.preservedLayoutIds); const slotIdsWithContent = new Set(); @@ -1151,7 +1151,7 @@ type InterceptedPreservationValidation = }; function getVisibleInterceptionSourceIdentity( - snapshot: RouteSnapshotV0, + snapshot: RouteSnapshot, ): VisibleInterceptionSourceIdentity { if (snapshot.interception) { return { @@ -1169,7 +1169,7 @@ function createInterceptionProofRejectedDecision(options: { event: Extract; reasonCode: NavigationTraceReasonCode; traceFields: NavigationTraceFields; -}): NavigationDecisionV0 { +}): NavigationDecision { return { kind: "hardNavigate", reason: "interceptionProofRejected", @@ -1232,7 +1232,7 @@ function createCacheProofRejectedDecision(options: { event: Extract; rejection: Extract; traceFields: NavigationTraceFields; -}): NavigationDecisionV0 { +}): NavigationDecision { return { kind: "hardNavigate", reason: "cacheProofRejected", @@ -1267,10 +1267,10 @@ function createCacheEntryProposalFields( } function validateInterceptedPreservation(options: { - currentSnapshot: RouteSnapshotV0; + currentSnapshot: RouteSnapshot; currentTopology: RouteTopologySnapshot; routeManifest: RouteManifest | null; - targetSnapshot: RouteSnapshotV0; + targetSnapshot: RouteSnapshot; targetTopology: RouteTopologySnapshot; }): InterceptedPreservationValidation { const proof = options.targetSnapshot.interception; @@ -1362,8 +1362,8 @@ function validateInterceptedPreservation(options: { function planFlightResponseArrived(options: { event: Extract; routeManifest: RouteManifest | null; - state: NavigationPlannerStateV0; -}): NavigationDecisionV0 { + state: NavigationPlannerState; +}): NavigationDecision { const targetSnapshot = options.event.result.targetSnapshot; const currentTopology = resolveRouteTopologySnapshot({ routeManifest: options.routeManifest, @@ -1526,7 +1526,7 @@ function planFlightResponseArrived(options: { }; } -function planNavigation(input: NavigationPlannerInput): NavigationDecisionV0 { +function planNavigation(input: NavigationPlannerInput): NavigationDecision { switch (input.event.kind) { case "navigate": return createRequestWorkDecision({ @@ -1580,7 +1580,7 @@ function planNavigation(input: NavigationPlannerInput): NavigationDecisionV0 { } } -export type ServerActionResultFactsV0 = { +export type ServerActionResultFacts = { actionRedirectHref: string | null; actionRedirectType: "push" | "replace"; clientCompatibilityId: string | null; @@ -1591,7 +1591,7 @@ export type ServerActionResultFactsV0 = { responseUrl: string | null; }; -export type ServerActionResultDecisionV0 = +export type ServerActionResultDecision = | { kind: "proceed"; trace: NavigationTrace } | { kind: "hardNavigate"; @@ -1602,20 +1602,18 @@ export type ServerActionResultDecisionV0 = trace: NavigationTrace; }; -export type RscNavigationErrorFactsV0 = { +export type RscNavigationErrorFacts = { currentHref: string; }; -export type RscNavigationErrorDecisionV0 = { +export type RscNavigationErrorDecision = { kind: "hardNavigate"; url: string; reason: "rscNavigationError"; trace: NavigationTrace; }; -function classifyServerActionResult( - facts: ServerActionResultFactsV0, -): ServerActionResultDecisionV0 { +function classifyServerActionResult(facts: ServerActionResultFacts): ServerActionResultDecision { // A client without a compatibility id cannot prove skew. if (facts.clientCompatibilityId === null) { return { @@ -1682,9 +1680,7 @@ function classifyServerActionResult( }; } -function classifyRscNavigationError( - facts: RscNavigationErrorFactsV0, -): RscNavigationErrorDecisionV0 { +function classifyRscNavigationError(facts: RscNavigationErrorFacts): RscNavigationErrorDecision { return { kind: "hardNavigate", url: facts.currentHref, diff --git a/tests/navigation-planner-early-intent.test.ts b/tests/navigation-planner-early-intent.test.ts index 52bd9737a..51ba94f23 100644 --- a/tests/navigation-planner-early-intent.test.ts +++ b/tests/navigation-planner-early-intent.test.ts @@ -5,13 +5,13 @@ import { } from "../packages/vinext/src/server/navigation-trace.js"; import { navigationPlanner, - type EarlyNavigationIntentDecisionV0, - type EarlyNavigationIntentFactsV0, + type EarlyNavigationIntentDecision, + type EarlyNavigationIntentFacts, } from "../packages/vinext/src/server/navigation-planner.js"; function createFacts( - overrides: Partial = {}, -): EarlyNavigationIntentFactsV0 { + overrides: Partial = {}, +): EarlyNavigationIntentFacts { return { basePath: "", currentHref: "https://example.com/docs?q=1", @@ -23,13 +23,13 @@ function createFacts( } function classify( - overrides: Partial = {}, -): EarlyNavigationIntentDecisionV0 { + overrides: Partial = {}, +): EarlyNavigationIntentDecision { return navigationPlanner.classifyEarlyNavigationIntent(createFacts(overrides)); } function expectSingleTraceEntry( - decision: EarlyNavigationIntentDecisionV0, + decision: EarlyNavigationIntentDecision, code: string, fields: Record, ): void { diff --git a/tests/navigation-planner-prefetch-reuse.test.ts b/tests/navigation-planner-prefetch-reuse.test.ts index b5f9ad262..9c487d7b4 100644 --- a/tests/navigation-planner-prefetch-reuse.test.ts +++ b/tests/navigation-planner-prefetch-reuse.test.ts @@ -5,12 +5,12 @@ import { } from "../packages/vinext/src/server/navigation-trace.js"; import { navigationPlanner, - type NavigationReuseDecisionV0, - type NavigationReuseFactsV0, - type VisitedResponseCacheCandidateFactsV0, + type NavigationReuseDecision, + type NavigationReuseFacts, + type VisitedResponseCacheCandidateFacts, } from "../packages/vinext/src/server/navigation-planner.js"; -function createReuseFacts(overrides: Partial = {}): NavigationReuseFactsV0 { +function createReuseFacts(overrides: Partial = {}): NavigationReuseFacts { return { bypassNavigationCache: false, navigationKind: "navigate", @@ -22,7 +22,7 @@ function createReuseFacts(overrides: Partial = {}): Navi }; } -function classifyReuse(overrides: Partial = {}): NavigationReuseDecisionV0 { +function classifyReuse(overrides: Partial = {}): NavigationReuseDecision { return navigationPlanner.classifyNavigationReuse(createReuseFacts(overrides)); } @@ -202,9 +202,7 @@ describe("navigationPlanner prefetch reuse classification", () => { describe("navigationPlanner visited-response cache candidate classification", () => { function classifyVisited( - overrides: Partial< - Extract - > = {}, + overrides: Partial> = {}, ) { return navigationPlanner.classifyVisitedResponseCacheCandidate({ candidate: "present", diff --git a/tests/navigation-planner-rsc-fetch-result.test.ts b/tests/navigation-planner-rsc-fetch-result.test.ts index 257952325..73ad2069a 100644 --- a/tests/navigation-planner-rsc-fetch-result.test.ts +++ b/tests/navigation-planner-rsc-fetch-result.test.ts @@ -5,11 +5,11 @@ import { } from "../packages/vinext/src/server/navigation-trace.js"; import { navigationPlanner, - type RscFetchResultDecisionV0, - type RscFetchResultFactsV0, + type RscFetchResultDecision, + type RscFetchResultFacts, } from "../packages/vinext/src/server/navigation-planner.js"; -function createFacts(overrides: Partial = {}): RscFetchResultFactsV0 { +function createFacts(overrides: Partial = {}): RscFetchResultFacts { return { clientCompatibilityId: "client-build", compatibilityIdHeader: "client-build", @@ -28,12 +28,12 @@ function createFacts(overrides: Partial = {}): RscFetchRe }; } -function classify(overrides: Partial = {}): RscFetchResultDecisionV0 { +function classify(overrides: Partial = {}): RscFetchResultDecision { return navigationPlanner.classifyRscFetchResult(createFacts(overrides)); } function expectSingleTraceEntry( - decision: RscFetchResultDecisionV0, + decision: RscFetchResultDecision, code: string, fields: Record, ): void { diff --git a/tests/navigation-planner-server-action-result.test.ts b/tests/navigation-planner-server-action-result.test.ts index 3cc9b3d0e..60042173c 100644 --- a/tests/navigation-planner-server-action-result.test.ts +++ b/tests/navigation-planner-server-action-result.test.ts @@ -5,15 +5,13 @@ import { } from "../packages/vinext/src/server/navigation-trace.js"; import { navigationPlanner, - type RscNavigationErrorDecisionV0, - type RscNavigationErrorFactsV0, - type ServerActionResultDecisionV0, - type ServerActionResultFactsV0, + type RscNavigationErrorDecision, + type RscNavigationErrorFacts, + type ServerActionResultDecision, + type ServerActionResultFacts, } from "../packages/vinext/src/server/navigation-planner.js"; -function createFacts( - overrides: Partial = {}, -): ServerActionResultFactsV0 { +function createFacts(overrides: Partial = {}): ServerActionResultFacts { return { actionRedirectHref: null, actionRedirectType: "replace", @@ -27,14 +25,12 @@ function createFacts( }; } -function classify( - overrides: Partial = {}, -): ServerActionResultDecisionV0 { +function classify(overrides: Partial = {}): ServerActionResultDecision { return navigationPlanner.classifyServerActionResult(createFacts(overrides)); } function expectSingleTraceEntry( - decision: ServerActionResultDecisionV0 | RscNavigationErrorDecisionV0, + decision: ServerActionResultDecision | RscNavigationErrorDecision, code: string, fields: Record, ): void { @@ -158,8 +154,8 @@ describe("navigationPlanner server-action result classification", () => { describe("navigationPlanner RSC navigation error classification", () => { function classifyError( - overrides: Partial = {}, - ): RscNavigationErrorDecisionV0 { + overrides: Partial = {}, + ): RscNavigationErrorDecision { return navigationPlanner.classifyRscNavigationError({ currentHref: "https://example.com/current", ...overrides, diff --git a/tests/navigation-planner.test.ts b/tests/navigation-planner.test.ts index 41530e918..2a3e47871 100644 --- a/tests/navigation-planner.test.ts +++ b/tests/navigation-planner.test.ts @@ -5,17 +5,17 @@ import { } from "../packages/vinext/src/server/navigation-trace.js"; import { navigationPlanner, - type FlightResultV0, - type MountedParallelSlotSnapshotV0, - type NavigationDecisionV0, + type FlightResult, + type MountedParallelSlotSnapshot, + type NavigationDecision, type NavigationEvent, type NavigationPlannerInput, - type NavigationPlannerStateV0, + type NavigationPlannerState, type OperationToken, - type ParallelSlotBindingSnapshotV0, + type ParallelSlotBindingSnapshot, type RefreshScope, - type RouteSnapshotV0, - type InterceptionSnapshotV0, + type RouteSnapshot, + type InterceptionSnapshot, type RootBoundaryTransition, } from "../packages/vinext/src/server/navigation-planner.js"; import type { @@ -39,7 +39,7 @@ type TestManifestRoute = { pattern: string; patternParts?: readonly string[]; rootBoundaryId: string | null; - slotBindings?: readonly ParallelSlotBindingSnapshotV0[]; + slotBindings?: readonly ParallelSlotBindingSnapshot[]; interceptions?: readonly TestManifestInterception[]; }; @@ -53,9 +53,9 @@ type TestManifestInterception = { function createRouteSnapshot( rootBoundaryId: string | null, layoutIds: readonly string[] = rootBoundaryId === null ? [] : [`layout:${rootBoundaryId}`], - mountedParallelSlots: readonly MountedParallelSlotSnapshotV0[] = [], - slotBindings: readonly ParallelSlotBindingSnapshotV0[] = [], -): RouteSnapshotV0 { + mountedParallelSlots: readonly MountedParallelSlotSnapshot[] = [], + slotBindings: readonly ParallelSlotBindingSnapshot[] = [], +): RouteSnapshot { return { displayUrl: "https://example.com/dashboard", interception: null, @@ -70,8 +70,8 @@ function createRouteSnapshot( } function createInterceptionSnapshot( - overrides: Partial = {}, -): InterceptionSnapshotV0 { + overrides: Partial = {}, +): InterceptionSnapshot { return { sourceMatchedUrl: "/feed", sourceRouteId: "route:/feed", @@ -97,8 +97,8 @@ function createAcceptedStaticLayoutCacheEntryReuseProof(): CacheEntryReuseProof function createSlotBinding( slotId: string, ownerLayoutId: string, - state: ParallelSlotBindingSnapshotV0["state"] = "active", -): ParallelSlotBindingSnapshotV0 { + state: ParallelSlotBindingSnapshot["state"] = "active", +): ParallelSlotBindingSnapshot { return { ownerLayoutId, slotId, state }; } @@ -212,8 +212,8 @@ function rootBoundaryIdForManifest(rootBoundaryId: string | null): string | null } function createRouteManifestForSnapshots( - currentSnapshot: RouteSnapshotV0, - targetSnapshot: RouteSnapshotV0, + currentSnapshot: RouteSnapshot, + targetSnapshot: RouteSnapshot, ): RouteManifest { if (targetSnapshot.interception !== null) { const proof = targetSnapshot.interception; @@ -309,7 +309,7 @@ function createRejectedCacheEntryReuseProof( }; } -function planFlightResponse(rootBoundaryId: string | null): NavigationDecisionV0 { +function planFlightResponse(rootBoundaryId: string | null): NavigationDecision { const token = createOperationToken({ targetSnapshotFingerprint: `route:/dashboard|root:${rootBoundaryId ?? "unknown"}`, }); @@ -325,7 +325,7 @@ function planFlightResponse(rootBoundaryId: string | null): NavigationDecisionV0 rootBoundaryId: rootBoundaryId === null ? null : `root-boundary:${rootBoundaryId}`, }, ]); - const result: FlightResultV0 = { + const result: FlightResult = { href: "https://example.com/dashboard", targetSnapshot: { ...createRouteSnapshot(rootBoundaryId), @@ -333,7 +333,7 @@ function planFlightResponse(rootBoundaryId: string | null): NavigationDecisionV0 routeId: "route:/dashboard", }, }; - const state: NavigationPlannerStateV0 = { + const state: NavigationPlannerState = { nextOperationToken: token, traceFields: { currentRootLayoutTreePath: "/", @@ -366,7 +366,7 @@ function planFlightResponse(rootBoundaryId: string | null): NavigationDecisionV0 function planFlightResponseFromRootBoundaries(options: { currentRootBoundaryId: string | null; nextRootBoundaryId: string | null; -}): NavigationDecisionV0 { +}): NavigationDecision { const token = createOperationToken({ targetSnapshotFingerprint: `route:/dashboard|root:${options.nextRootBoundaryId ?? "unknown"}`, }); @@ -425,12 +425,12 @@ function planFlightResponseFromRootBoundaries(options: { function planFlightResponseFromSnapshots(options: { cacheEntryReuseProof?: CacheEntryReuseProof; - currentSnapshot: RouteSnapshotV0; + currentSnapshot: RouteSnapshot; lane?: OperationToken["lane"]; routeManifest?: RouteManifest | null; - targetSnapshot: RouteSnapshotV0; - traceFields?: NavigationPlannerStateV0["traceFields"]; -}): NavigationDecisionV0 { + targetSnapshot: RouteSnapshot; + traceFields?: NavigationPlannerState["traceFields"]; +}): NavigationDecision { const token = createOperationToken({ lane: options.lane ?? "navigation", targetSnapshotFingerprint: `${options.targetSnapshot.routeId}|root:${ @@ -495,13 +495,13 @@ describe("navigationPlanner root-boundary decisions", () => { }); it("rejects runtime cache entries when the cache proof is missing", () => { - const currentSnapshot: RouteSnapshotV0 = { + const currentSnapshot: RouteSnapshot = { ...createRouteSnapshot("/"), displayUrl: "https://example.com/dashboard/profile", matchedUrl: "/dashboard/profile", routeId: "route:/dashboard/profile", }; - const targetSnapshot: RouteSnapshotV0 = { + const targetSnapshot: RouteSnapshot = { ...createRouteSnapshot("/"), displayUrl: "https://example.com/dashboard/settings", matchedUrl: "/dashboard/settings", @@ -535,13 +535,13 @@ describe("navigationPlanner root-boundary decisions", () => { }); it("rejects incompatible runtime cache entries before route-topology commit approval", () => { - const currentSnapshot: RouteSnapshotV0 = { + const currentSnapshot: RouteSnapshot = { ...createRouteSnapshot("/"), displayUrl: "https://example.com/dashboard/profile", matchedUrl: "/dashboard/profile", routeId: "route:/dashboard/profile", }; - const targetSnapshot: RouteSnapshotV0 = { + const targetSnapshot: RouteSnapshot = { ...createRouteSnapshot("/"), displayUrl: "https://example.com/dashboard/settings", matchedUrl: "/dashboard/settings", @@ -576,13 +576,13 @@ describe("navigationPlanner root-boundary decisions", () => { }); it("keeps accepted runtime cache proof visible on the commit proposal", () => { - const currentSnapshot: RouteSnapshotV0 = { + const currentSnapshot: RouteSnapshot = { ...createRouteSnapshot("/"), displayUrl: "https://example.com/dashboard/profile", matchedUrl: "/dashboard/profile", routeId: "route:/dashboard/profile", }; - const targetSnapshot: RouteSnapshotV0 = { + const targetSnapshot: RouteSnapshot = { ...createRouteSnapshot("/"), displayUrl: "https://example.com/dashboard/settings", matchedUrl: "/dashboard/settings", @@ -642,13 +642,13 @@ describe("navigationPlanner root-boundary decisions", () => { }); it("keeps accepted runtime cache proof fields on later root-boundary rejections", () => { - const currentSnapshot: RouteSnapshotV0 = { + const currentSnapshot: RouteSnapshot = { ...createRouteSnapshot("/"), displayUrl: "https://example.com/current", matchedUrl: "/current", routeId: "route:/current", }; - const targetSnapshot: RouteSnapshotV0 = { + const targetSnapshot: RouteSnapshot = { ...createRouteSnapshot("/(dashboard)"), displayUrl: "https://example.com/dashboard", matchedUrl: "/dashboard", @@ -777,7 +777,7 @@ describe("navigationPlanner root-boundary decisions", () => { }); it("does not approve mounted parallel slot preservation for traverse commits", () => { - const currentSnapshot: RouteSnapshotV0 = { + const currentSnapshot: RouteSnapshot = { ...createRouteSnapshot( "/", ["layout:/", "layout:/feed"], @@ -787,7 +787,7 @@ describe("navigationPlanner root-boundary decisions", () => { matchedUrl: "/feed", routeId: "route:/feed", }; - const targetSnapshot: RouteSnapshotV0 = { + const targetSnapshot: RouteSnapshot = { ...createRouteSnapshot("/", ["layout:/", "layout:/feed", "layout:/feed/comments"]), displayUrl: "https://example.com/feed/comments", matchedUrl: "/feed/comments", @@ -853,7 +853,7 @@ describe("navigationPlanner root-boundary decisions", () => { createSlotBinding("slot:reports:/dashboard", "layout:/dashboard", "active"), ], ); - const targetSettingsSnapshot: RouteSnapshotV0 = { + const targetSettingsSnapshot: RouteSnapshot = { ...targetSnapshot, displayUrl: "https://example.com/dashboard/settings", matchedUrl: "/dashboard/settings", @@ -913,7 +913,7 @@ describe("navigationPlanner root-boundary decisions", () => { createSlotBinding("slot:analytics:/dashboard", "layout:/dashboard", "unmatched"), ], ); - const targetSettingsSnapshot: RouteSnapshotV0 = { + const targetSettingsSnapshot: RouteSnapshot = { ...targetSnapshot, displayUrl: "https://example.com/dashboard/settings", matchedUrl: "/dashboard/settings", @@ -948,7 +948,7 @@ describe("navigationPlanner root-boundary decisions", () => { }); it("does not preserve default target slots when their owner layout is not retained", () => { - const currentSnapshot: RouteSnapshotV0 = { + const currentSnapshot: RouteSnapshot = { ...createRouteSnapshot( "/", ["layout:/", "layout:/feed"], @@ -1035,12 +1035,12 @@ describe("navigationPlanner root-boundary decisions", () => { rootBoundaryId: "root-boundary:/marketing", }, ]); - const currentSnapshot: RouteSnapshotV0 = { + const currentSnapshot: RouteSnapshot = { ...createRouteSnapshot("/", ["layout:/stale-app"]), matchedUrl: "/app", routeId: "route:/app", }; - const targetSnapshot: RouteSnapshotV0 = { + const targetSnapshot: RouteSnapshot = { ...createRouteSnapshot("/", ["layout:/stale-app", "layout:/stale-marketing"]), displayUrl: "https://example.com/marketing", matchedUrl: "/marketing", @@ -1081,12 +1081,12 @@ describe("navigationPlanner root-boundary decisions", () => { rootBoundaryId: "root-boundary:/", }, ]); - const currentSnapshot: RouteSnapshotV0 = { + const currentSnapshot: RouteSnapshot = { ...createRouteSnapshot(null, []), matchedUrl: "/dashboard", routeId: "route:/dashboard", }; - const targetSnapshot: RouteSnapshotV0 = { + const targetSnapshot: RouteSnapshot = { ...createRouteSnapshot(null, []), displayUrl: "https://example.com/dashboard/settings", matchedUrl: "/dashboard/settings", @@ -1121,13 +1121,13 @@ describe("navigationPlanner root-boundary decisions", () => { rootBoundaryId: "root-boundary:/marketing", }, ]); - const currentSnapshot: RouteSnapshotV0 = { + const currentSnapshot: RouteSnapshot = { ...createRouteSnapshot(null, []), displayUrl: "https://example.com/app", matchedUrl: "/app", routeId: "route:/app", }; - const targetSnapshot: RouteSnapshotV0 = { + const targetSnapshot: RouteSnapshot = { ...createRouteSnapshot(null, []), displayUrl: "https://example.com/app", matchedUrl: "/app", @@ -1156,13 +1156,13 @@ describe("navigationPlanner root-boundary decisions", () => { rootBoundaryId: "root-boundary:/", }, ]); - const currentSnapshot: RouteSnapshotV0 = { + const currentSnapshot: RouteSnapshot = { ...createRouteSnapshot(null, []), displayUrl: "https://example.com/blog/hello", matchedUrl: "/blog/hello", routeId: "route:/blog/hello", }; - const targetSnapshot: RouteSnapshotV0 = { + const targetSnapshot: RouteSnapshot = { ...createRouteSnapshot(null, []), displayUrl: "https://example.com/blog/world", matchedUrl: "/blog/world", @@ -1199,7 +1199,7 @@ describe("navigationPlanner root-boundary decisions", () => { slotBindings: [createSlotBinding(modalSlot, "layout:/dashboard", "default")], }, ]); - const currentSnapshot: RouteSnapshotV0 = { + const currentSnapshot: RouteSnapshot = { ...createRouteSnapshot( null, [], @@ -1212,7 +1212,7 @@ describe("navigationPlanner root-boundary decisions", () => { matchedUrl: "/dashboard", routeId: "route:/dashboard", }; - const targetSnapshot: RouteSnapshotV0 = { + const targetSnapshot: RouteSnapshot = { ...createRouteSnapshot( null, [], @@ -1253,12 +1253,12 @@ describe("navigationPlanner root-boundary decisions", () => { slotBindings: [createSlotBinding(modalSlot, "layout:/dashboard", "default")], }, ]); - const currentSnapshot: RouteSnapshotV0 = { + const currentSnapshot: RouteSnapshot = { ...createRouteSnapshot(null, [], [], []), matchedUrl: "/dashboard", routeId: "route:/dashboard", }; - const targetSnapshot: RouteSnapshotV0 = { + const targetSnapshot: RouteSnapshot = { ...createRouteSnapshot(null, [], [], []), displayUrl: "https://example.com/dashboard/settings", matchedUrl: "/dashboard/settings", @@ -1299,12 +1299,12 @@ describe("navigationPlanner root-boundary decisions", () => { rootBoundaryId: "root-boundary:/photos", }, ]); - const currentSnapshot: RouteSnapshotV0 = { + const currentSnapshot: RouteSnapshot = { ...createRouteSnapshot(null, []), matchedUrl: "/feed", routeId: "route:/feed", }; - const targetSnapshot: RouteSnapshotV0 = { + const targetSnapshot: RouteSnapshot = { ...createRouteSnapshot( null, [], @@ -1353,12 +1353,12 @@ describe("navigationPlanner root-boundary decisions", () => { rootBoundaryId: "root-boundary:/", }, ]); - const currentSnapshot: RouteSnapshotV0 = { + const currentSnapshot: RouteSnapshot = { ...createRouteSnapshot(null, []), matchedUrl: "/en/feed", routeId: "route:/en/feed", }; - const targetSnapshot: RouteSnapshotV0 = { + const targetSnapshot: RouteSnapshot = { ...createRouteSnapshot( null, [], @@ -1409,12 +1409,12 @@ describe("navigationPlanner root-boundary decisions", () => { rootBoundaryId: "root-boundary:/", }, ]); - const currentSnapshot: RouteSnapshotV0 = { + const currentSnapshot: RouteSnapshot = { ...createRouteSnapshot(null, []), matchedUrl: "/feed", routeId: "route:/feed", }; - const targetSnapshot: RouteSnapshotV0 = { + const targetSnapshot: RouteSnapshot = { ...createRouteSnapshot( null, [], @@ -1470,12 +1470,12 @@ describe("navigationPlanner root-boundary decisions", () => { rootBoundaryId: "root-boundary:/", }, ]); - const currentSnapshot: RouteSnapshotV0 = { + const currentSnapshot: RouteSnapshot = { ...createRouteSnapshot(null, []), matchedUrl: "/feed", routeId: "route:/feed", }; - const targetSnapshot: RouteSnapshotV0 = { + const targetSnapshot: RouteSnapshot = { ...createRouteSnapshot( null, [], @@ -1509,7 +1509,7 @@ describe("navigationPlanner root-boundary decisions", () => { // Core-15 oracle, porting the visible behavior from Next.js: // test/e2e/app-dir/parallel-routes-and-interception-catchall/parallel-routes-and-interception-catchall.test.ts // https://github.com/vercel/next.js/blob/canary/test/e2e/app-dir/parallel-routes-and-interception-catchall/parallel-routes-and-interception-catchall.test.ts - const currentSnapshot: RouteSnapshotV0 = { + const currentSnapshot: RouteSnapshot = { ...createRouteSnapshot( "/", ["layout:/", "layout:/feed"], @@ -1523,7 +1523,7 @@ describe("navigationPlanner root-boundary decisions", () => { matchedUrl: "/feed", routeId: "route:/feed", }; - const targetSnapshot: RouteSnapshotV0 = { + const targetSnapshot: RouteSnapshot = { ...createRouteSnapshot( "/", ["layout:/", "layout:/feed"], @@ -1556,12 +1556,12 @@ describe("navigationPlanner root-boundary decisions", () => { }); it("does not treat legacy context-only payloads as intercepted preservation proof", () => { - const currentSnapshot: RouteSnapshotV0 = { + const currentSnapshot: RouteSnapshot = { ...createRouteSnapshot("/", ["layout:/", "layout:/feed"]), matchedUrl: "/feed", routeId: "route:/feed", }; - const targetSnapshot: RouteSnapshotV0 = { + const targetSnapshot: RouteSnapshot = { ...createRouteSnapshot("/", ["layout:/", "layout:/feed"]), displayUrl: "https://example.com/photos/42", interceptionContext: "/feed", @@ -1582,12 +1582,12 @@ describe("navigationPlanner root-boundary decisions", () => { }); it("rejects intercepted preservation when the visible source route is stale", () => { - const currentSnapshot: RouteSnapshotV0 = { + const currentSnapshot: RouteSnapshot = { ...createRouteSnapshot("/", ["layout:/", "layout:/gallery"]), matchedUrl: "/gallery", routeId: "route:/gallery", }; - const targetSnapshot: RouteSnapshotV0 = { + const targetSnapshot: RouteSnapshot = { ...createRouteSnapshot( "/", ["layout:/", "layout:/feed"], @@ -1614,12 +1614,12 @@ describe("navigationPlanner root-boundary decisions", () => { }); it("rejects intercepted preservation when proof target does not match the rendered route", () => { - const currentSnapshot: RouteSnapshotV0 = { + const currentSnapshot: RouteSnapshot = { ...createRouteSnapshot("/", ["layout:/", "layout:/feed"]), matchedUrl: "/feed", routeId: "route:/feed", }; - const targetSnapshot: RouteSnapshotV0 = { + const targetSnapshot: RouteSnapshot = { ...createRouteSnapshot( "/", ["layout:/", "layout:/feed"], @@ -1646,12 +1646,12 @@ describe("navigationPlanner root-boundary decisions", () => { }); it("does not use intercepted snapshot root topology as preservation authority", () => { - const currentSnapshot: RouteSnapshotV0 = { + const currentSnapshot: RouteSnapshot = { ...createRouteSnapshot("/", ["layout:/", "layout:/feed"]), matchedUrl: "/feed", routeId: "route:/feed", }; - const targetSnapshot: RouteSnapshotV0 = { + const targetSnapshot: RouteSnapshot = { ...createRouteSnapshot( "/marketing", ["layout:/marketing"], @@ -1678,12 +1678,12 @@ describe("navigationPlanner root-boundary decisions", () => { }); it("rejects intercepted preservation when the target slot is not proven active", () => { - const currentSnapshot: RouteSnapshotV0 = { + const currentSnapshot: RouteSnapshot = { ...createRouteSnapshot("/", ["layout:/", "layout:/feed"]), matchedUrl: "/feed", routeId: "route:/feed", }; - const targetSnapshot: RouteSnapshotV0 = { + const targetSnapshot: RouteSnapshot = { ...createRouteSnapshot( "/", ["layout:/", "layout:/feed"], @@ -1710,7 +1710,7 @@ describe("navigationPlanner root-boundary decisions", () => { }); it("allows traverse to restore an intercepted visible world only with proof", () => { - const currentSnapshot: RouteSnapshotV0 = { + const currentSnapshot: RouteSnapshot = { ...createRouteSnapshot( "/", ["layout:/", "layout:/feed"], @@ -1721,7 +1721,7 @@ describe("navigationPlanner root-boundary decisions", () => { matchedUrl: "/feed", routeId: "route:/feed", }; - const targetSnapshot: RouteSnapshotV0 = { + const targetSnapshot: RouteSnapshot = { ...createRouteSnapshot( "/", ["layout:/", "layout:/feed"],