@rangojs/router 0.0.0-experimental.18 → 0.0.0-experimental.19
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 +46 -8
- package/dist/bin/rango.js +105 -18
- package/dist/vite/index.js +227 -93
- package/package.json +15 -14
- package/skills/hooks/SKILL.md +1 -1
- package/skills/intercept/SKILL.md +79 -0
- package/skills/layout/SKILL.md +62 -2
- package/skills/loader/SKILL.md +94 -1
- package/skills/middleware/SKILL.md +81 -0
- package/skills/parallel/SKILL.md +57 -2
- package/skills/prerender/SKILL.md +187 -17
- package/skills/route/SKILL.md +42 -1
- package/skills/router-setup/SKILL.md +77 -0
- package/src/__internal.ts +1 -1
- package/src/bin/rango.ts +38 -19
- package/src/browser/action-coordinator.ts +97 -0
- package/src/browser/event-controller.ts +25 -27
- package/src/browser/history-state.ts +80 -0
- package/src/browser/intercept-utils.ts +1 -1
- package/src/browser/link-interceptor.ts +0 -3
- package/src/browser/merge-segment-loaders.ts +9 -2
- package/src/browser/navigation-bridge.ts +46 -13
- package/src/browser/navigation-client.ts +32 -61
- package/src/browser/navigation-store.ts +1 -31
- package/src/browser/navigation-transaction.ts +46 -207
- package/src/browser/partial-update.ts +102 -150
- package/src/browser/{prefetch-cache.ts → prefetch/cache.ts} +23 -4
- package/src/browser/{prefetch-fetch.ts → prefetch/fetch.ts} +36 -8
- package/src/browser/prefetch/policy.ts +42 -0
- package/src/browser/{prefetch-queue.ts → prefetch/queue.ts} +10 -3
- package/src/browser/react/Link.tsx +28 -23
- package/src/browser/react/NavigationProvider.tsx +9 -1
- package/src/browser/react/index.ts +2 -6
- package/src/browser/react/location-state-shared.ts +1 -1
- package/src/browser/react/location-state.ts +2 -0
- package/src/browser/react/nonce-context.ts +23 -0
- package/src/browser/react/use-action.ts +9 -1
- package/src/browser/react/use-handle.ts +3 -25
- package/src/browser/react/use-params.ts +2 -4
- package/src/browser/react/use-pathname.ts +2 -3
- package/src/browser/react/use-router.ts +1 -1
- package/src/browser/react/use-search-params.ts +2 -1
- package/src/browser/react/use-segments.ts +7 -60
- package/src/browser/response-adapter.ts +73 -0
- package/src/browser/rsc-router.tsx +29 -23
- package/src/browser/scroll-restoration.ts +10 -7
- package/src/browser/server-action-bridge.ts +115 -96
- package/src/browser/types.ts +1 -31
- package/src/browser/validate-redirect-origin.ts +29 -0
- package/src/build/generate-manifest.ts +5 -0
- package/src/build/generate-route-types.ts +2 -0
- package/src/build/route-types/codegen.ts +13 -4
- package/src/build/route-types/include-resolution.ts +13 -0
- package/src/build/route-types/per-module-writer.ts +15 -3
- package/src/build/route-types/router-processing.ts +45 -3
- package/src/build/runtime-discovery.ts +13 -1
- package/src/cache/background-task.ts +34 -0
- package/src/cache/cache-key-utils.ts +44 -0
- package/src/cache/cache-policy.ts +125 -0
- package/src/cache/cache-runtime.ts +132 -96
- package/src/cache/cache-scope.ts +71 -73
- package/src/cache/cf/cf-cache-store.ts +9 -4
- package/src/cache/document-cache.ts +72 -47
- package/src/cache/handle-capture.ts +81 -0
- package/src/cache/memory-segment-store.ts +18 -7
- package/src/cache/profile-registry.ts +43 -8
- package/src/cache/read-through-swr.ts +134 -0
- package/src/cache/segment-codec.ts +101 -112
- package/src/cache/taint.ts +26 -0
- package/src/client.tsx +53 -30
- package/src/errors.ts +6 -1
- package/src/handle.ts +1 -1
- package/src/handles/MetaTags.tsx +5 -2
- package/src/host/cookie-handler.ts +8 -3
- package/src/host/router.ts +14 -1
- package/src/href-client.ts +3 -1
- package/src/index.rsc.ts +33 -1
- package/src/index.ts +27 -0
- package/src/loader.rsc.ts +12 -4
- package/src/loader.ts +8 -0
- package/src/prerender/store.ts +4 -3
- package/src/prerender.ts +76 -18
- package/src/reverse.ts +11 -7
- package/src/root-error-boundary.tsx +30 -26
- package/src/route-definition/dsl-helpers.ts +9 -6
- package/src/route-definition/redirect.ts +15 -3
- package/src/route-map-builder.ts +38 -2
- package/src/route-name.ts +53 -0
- package/src/route-types.ts +7 -0
- package/src/router/content-negotiation.ts +1 -1
- package/src/router/debug-manifest.ts +16 -3
- package/src/router/handler-context.ts +94 -15
- package/src/router/intercept-resolution.ts +6 -4
- package/src/router/lazy-includes.ts +4 -0
- package/src/router/loader-resolution.ts +1 -0
- package/src/router/logging.ts +100 -3
- package/src/router/manifest.ts +32 -3
- package/src/router/match-api.ts +61 -7
- package/src/router/match-context.ts +3 -0
- package/src/router/match-handlers.ts +185 -11
- package/src/router/match-middleware/background-revalidation.ts +65 -85
- package/src/router/match-middleware/cache-lookup.ts +69 -4
- package/src/router/match-middleware/cache-store.ts +2 -0
- package/src/router/match-pipelines.ts +8 -43
- package/src/router/middleware-types.ts +7 -0
- package/src/router/middleware.ts +93 -8
- package/src/router/pattern-matching.ts +41 -5
- package/src/router/prerender-match.ts +34 -6
- package/src/router/preview-match.ts +7 -1
- package/src/router/revalidation.ts +61 -2
- package/src/router/router-context.ts +15 -0
- package/src/router/router-interfaces.ts +34 -0
- package/src/router/router-options.ts +200 -0
- package/src/router/segment-resolution/fresh.ts +123 -30
- package/src/router/segment-resolution/helpers.ts +19 -0
- package/src/router/segment-resolution/loader-cache.ts +37 -146
- package/src/router/segment-resolution/revalidation.ts +358 -94
- package/src/router/segment-wrappers.ts +3 -0
- package/src/router/telemetry-otel.ts +299 -0
- package/src/router/telemetry.ts +300 -0
- package/src/router/timeout.ts +148 -0
- package/src/router/types.ts +7 -1
- package/src/router.ts +155 -11
- package/src/rsc/handler-context.ts +11 -0
- package/src/rsc/handler.ts +380 -88
- package/src/rsc/helpers.ts +25 -16
- package/src/rsc/loader-fetch.ts +84 -42
- package/src/rsc/origin-guard.ts +141 -0
- package/src/rsc/progressive-enhancement.ts +232 -19
- package/src/rsc/response-route-handler.ts +37 -26
- package/src/rsc/rsc-rendering.ts +12 -5
- package/src/rsc/runtime-warnings.ts +42 -0
- package/src/rsc/server-action.ts +134 -58
- package/src/rsc/types.ts +8 -0
- package/src/search-params.ts +22 -10
- package/src/server/context.ts +53 -5
- package/src/server/fetchable-loader-store.ts +11 -6
- package/src/server/handle-store.ts +66 -9
- package/src/server/loader-registry.ts +11 -46
- package/src/server/request-context.ts +90 -9
- package/src/ssr/index.tsx +63 -27
- package/src/static-handler.ts +7 -0
- package/src/theme/ThemeProvider.tsx +6 -1
- package/src/theme/index.ts +1 -6
- package/src/theme/theme-context.ts +1 -28
- package/src/theme/theme-script.ts +2 -1
- package/src/types/cache-types.ts +5 -0
- package/src/types/error-types.ts +3 -0
- package/src/types/global-namespace.ts +9 -0
- package/src/types/handler-context.ts +35 -13
- package/src/types/loader-types.ts +7 -0
- package/src/types/route-entry.ts +28 -0
- package/src/urls/include-helper.ts +49 -8
- package/src/urls/index.ts +1 -0
- package/src/urls/path-helper-types.ts +30 -12
- package/src/urls/path-helper.ts +17 -2
- package/src/urls/pattern-types.ts +21 -1
- package/src/urls/response-types.ts +27 -2
- package/src/urls/type-extraction.ts +23 -15
- package/src/use-loader.tsx +12 -4
- package/src/vite/discovery/bundle-postprocess.ts +12 -7
- package/src/vite/discovery/discover-routers.ts +30 -18
- package/src/vite/discovery/prerender-collection.ts +24 -27
- package/src/vite/discovery/route-types-writer.ts +7 -7
- package/src/vite/discovery/virtual-module-codegen.ts +5 -2
- package/src/vite/plugins/client-ref-hashing.ts +3 -3
- package/src/vite/plugins/use-cache-transform.ts +91 -3
- package/src/vite/rango.ts +3 -3
- package/src/vite/router-discovery.ts +99 -36
- package/src/vite/utils/prerender-utils.ts +21 -0
- package/src/vite/utils/shared-utils.ts +3 -1
- package/src/browser/request-controller.ts +0 -164
- package/src/href-context.ts +0 -33
- package/src/router.gen.ts +0 -6
- package/src/static-handler.gen.ts +0 -5
- package/src/urls.gen.ts +0 -8
- /package/src/browser/{prefetch-observer.ts → prefetch/observer.ts} +0 -0
|
@@ -4,10 +4,6 @@ import type {
|
|
|
4
4
|
ResolvedSegment,
|
|
5
5
|
StreamingToken,
|
|
6
6
|
} from "./types.js";
|
|
7
|
-
import {
|
|
8
|
-
isLocationStateEntry,
|
|
9
|
-
resolveLocationStateEntries,
|
|
10
|
-
} from "./react/location-state-shared.js";
|
|
11
7
|
import { generateHistoryKey } from "./navigation-store.js";
|
|
12
8
|
import {
|
|
13
9
|
handleNavigationStart,
|
|
@@ -16,70 +12,18 @@ import {
|
|
|
16
12
|
} from "./scroll-restoration.js";
|
|
17
13
|
import type { EventController, NavigationHandle } from "./event-controller.js";
|
|
18
14
|
import { debugLog } from "./logging.js";
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
if (state
|
|
27
|
-
return
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
* Resolve navigation state - handles both LocationStateEntry[] and plain formats
|
|
32
|
-
*/
|
|
33
|
-
export function resolveNavigationState(state: unknown): unknown {
|
|
34
|
-
// Check if it's an array of LocationStateEntry
|
|
35
|
-
if (
|
|
36
|
-
Array.isArray(state) &&
|
|
37
|
-
state.length > 0 &&
|
|
38
|
-
isLocationStateEntry(state[0])
|
|
39
|
-
) {
|
|
40
|
-
return resolveLocationStateEntries(state);
|
|
41
|
-
}
|
|
42
|
-
// Return as-is for plain state formats
|
|
43
|
-
return state;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Build history state object from user state
|
|
48
|
-
* - Typed state: spread directly into history.state
|
|
49
|
-
* - Plain state: store in history.state.state
|
|
50
|
-
*/
|
|
51
|
-
function buildHistoryState(
|
|
52
|
-
userState: unknown,
|
|
53
|
-
routerState?: { intercept?: boolean; sourceUrl?: string },
|
|
54
|
-
serverState?: Record<string, unknown>,
|
|
55
|
-
): Record<string, unknown> | null {
|
|
56
|
-
const result: Record<string, unknown> = {};
|
|
57
|
-
|
|
58
|
-
// Add router internal state
|
|
59
|
-
if (routerState?.intercept) {
|
|
60
|
-
result.intercept = true;
|
|
61
|
-
if (routerState.sourceUrl) {
|
|
62
|
-
result.sourceUrl = routerState.sourceUrl;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Add user state
|
|
67
|
-
if (userState !== undefined) {
|
|
68
|
-
if (isTypedLocationState(userState)) {
|
|
69
|
-
// Typed state: spread directly
|
|
70
|
-
Object.assign(result, userState);
|
|
71
|
-
} else {
|
|
72
|
-
// Plain state: store in .state
|
|
73
|
-
result.state = userState;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Merge server-set location state (from ctx.setLocationState on non-redirect responses)
|
|
78
|
-
if (serverState) {
|
|
79
|
-
Object.assign(result, serverState);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
return Object.keys(result).length > 0 ? result : null;
|
|
15
|
+
import { buildHistoryState } from "./history-state.js";
|
|
16
|
+
|
|
17
|
+
// Re-export for consumers that import from navigation-transaction
|
|
18
|
+
export { resolveNavigationState } from "./history-state.js";
|
|
19
|
+
|
|
20
|
+
/** Check if a history state object contains location state keys. */
|
|
21
|
+
function hasLocationState(state: unknown): boolean {
|
|
22
|
+
if (!state || typeof state !== "object") return false;
|
|
23
|
+
return (
|
|
24
|
+
"state" in state ||
|
|
25
|
+
Object.keys(state).some((k) => k.startsWith("__rsc_ls_"))
|
|
26
|
+
);
|
|
83
27
|
}
|
|
84
28
|
|
|
85
29
|
// Polyfill Symbol.dispose for Safari and older browsers
|
|
@@ -87,11 +31,6 @@ if (typeof Symbol.dispose === "undefined") {
|
|
|
87
31
|
(Symbol as any).dispose = Symbol("Symbol.dispose");
|
|
88
32
|
}
|
|
89
33
|
|
|
90
|
-
// Monotonic counter for tagging early-pushed history entries.
|
|
91
|
-
// Used by disposal to verify ownership without URL comparison,
|
|
92
|
-
// which breaks when two navigations target the same URL.
|
|
93
|
-
let navStamp = 0;
|
|
94
|
-
|
|
95
34
|
/**
|
|
96
35
|
* Options for committing a navigation transaction
|
|
97
36
|
*/
|
|
@@ -154,9 +93,6 @@ export interface BoundTransaction {
|
|
|
154
93
|
* Uses the event controller handle for lifecycle management
|
|
155
94
|
*/
|
|
156
95
|
interface NavigationTransaction extends Disposable {
|
|
157
|
-
/** Optimistically commit from cache - instant render before revalidation */
|
|
158
|
-
optimisticCommit(options: CommitOptions): void;
|
|
159
|
-
/** Final commit with server data (or reconciliation after optimistic) */
|
|
160
96
|
commit(options: CommitOptions): void;
|
|
161
97
|
with(
|
|
162
98
|
options: Omit<CommitOptions, "segmentIds" | "segments">,
|
|
@@ -168,9 +104,6 @@ interface NavigationTransaction extends Disposable {
|
|
|
168
104
|
/**
|
|
169
105
|
* Creates a navigation transaction that coordinates with the event controller.
|
|
170
106
|
* Handles loading state transitions and cleanup on completion/abort.
|
|
171
|
-
*
|
|
172
|
-
* Supports optimistic navigation: render from cache immediately,
|
|
173
|
-
* then revalidate in background and reconcile if data changed.
|
|
174
107
|
*/
|
|
175
108
|
export function createNavigationTransaction(
|
|
176
109
|
store: NavigationStore,
|
|
@@ -179,102 +112,29 @@ export function createNavigationTransaction(
|
|
|
179
112
|
options?: NavigateOptions & { skipLoadingState?: boolean },
|
|
180
113
|
): NavigationTransaction {
|
|
181
114
|
let committed = false;
|
|
182
|
-
let optimisticallyCommitted = false;
|
|
183
|
-
let earlyStatePushed = false;
|
|
184
|
-
let earlyStateStamp: number | null = null;
|
|
185
115
|
const currentUrl = window.location.href;
|
|
186
|
-
const currentHistoryState = window.history.state;
|
|
187
116
|
|
|
188
117
|
// Start navigation in event controller (this sets loading state)
|
|
189
118
|
const handle = eventController.startNavigation(url, options);
|
|
190
119
|
|
|
191
|
-
// If state is provided, push it to history immediately so loading UI can access it
|
|
192
|
-
// This enables "optimistic state" - showing product names in skeletons etc.
|
|
193
|
-
if (options?.state !== undefined && !options?.replace) {
|
|
194
|
-
earlyStateStamp = ++navStamp;
|
|
195
|
-
const earlyHistoryState = buildHistoryState(options.state);
|
|
196
|
-
if (earlyHistoryState) {
|
|
197
|
-
(earlyHistoryState as any).__navStamp = earlyStateStamp;
|
|
198
|
-
}
|
|
199
|
-
window.history.pushState(
|
|
200
|
-
earlyHistoryState ?? { __navStamp: earlyStateStamp },
|
|
201
|
-
"",
|
|
202
|
-
url,
|
|
203
|
-
);
|
|
204
|
-
earlyStatePushed = true;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* Optimistically commit from cache - renders immediately before revalidation
|
|
209
|
-
* Sets optimisticallyCommitted flag so final commit() knows to reconcile
|
|
210
|
-
*/
|
|
211
|
-
function optimisticCommit(opts: CommitOptions): void {
|
|
212
|
-
optimisticallyCommitted = true;
|
|
213
|
-
|
|
214
|
-
const { url, segmentIds, segments, replace, scroll } = opts;
|
|
215
|
-
const parsedUrl = new URL(url, window.location.origin);
|
|
216
|
-
|
|
217
|
-
// Save current scroll position before navigating
|
|
218
|
-
handleNavigationStart();
|
|
219
|
-
|
|
220
|
-
// Update segment state
|
|
221
|
-
store.setSegmentIds(segmentIds);
|
|
222
|
-
store.setCurrentUrl(url);
|
|
223
|
-
store.setPath(parsedUrl.pathname);
|
|
224
|
-
|
|
225
|
-
// Generate history key from URL
|
|
226
|
-
const historyKey = generateHistoryKey(url);
|
|
227
|
-
store.setHistoryKey(historyKey);
|
|
228
|
-
|
|
229
|
-
// Cache segments with current handleData (will be overwritten by fresh data on final commit)
|
|
230
|
-
const currentHandleData = eventController.getHandleState().data;
|
|
231
|
-
store.cacheSegmentsForHistory(historyKey, segments, currentHandleData);
|
|
232
|
-
|
|
233
|
-
// Build history state with user state if provided
|
|
234
|
-
const historyState = buildHistoryState(opts.state);
|
|
235
|
-
|
|
236
|
-
// Update browser URL
|
|
237
|
-
// Use replaceState if we already pushed early (for optimistic state access)
|
|
238
|
-
if (replace || earlyStatePushed) {
|
|
239
|
-
window.history.replaceState(historyState, "", url);
|
|
240
|
-
} else {
|
|
241
|
-
window.history.pushState(historyState, "", url);
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
// Ensure new history entry has a scroll restoration key
|
|
245
|
-
ensureHistoryKey();
|
|
246
|
-
|
|
247
|
-
// Complete the navigation in event controller (sets idle state)
|
|
248
|
-
handle.complete(parsedUrl);
|
|
249
|
-
|
|
250
|
-
// Handle scroll after navigation
|
|
251
|
-
handleNavigationEnd({ scroll });
|
|
252
|
-
|
|
253
|
-
debugLog("[Browser] Optimistic commit from cache, historyKey:", historyKey);
|
|
254
|
-
}
|
|
255
|
-
|
|
256
120
|
/**
|
|
257
121
|
* Commit the navigation - updates store and URL atomically
|
|
258
|
-
* If optimisticCommit was called, this becomes a reconciliation
|
|
259
122
|
*/
|
|
260
123
|
function commit(opts: CommitOptions): void {
|
|
261
124
|
committed = true;
|
|
262
125
|
|
|
263
|
-
// If optimistic commit already done, adjust options for reconciliation
|
|
264
|
-
const isReconciliation = optimisticallyCommitted;
|
|
265
126
|
const {
|
|
266
127
|
url,
|
|
267
128
|
segmentIds,
|
|
268
129
|
segments,
|
|
130
|
+
replace,
|
|
131
|
+
scroll,
|
|
269
132
|
storeOnly,
|
|
270
133
|
intercept,
|
|
271
134
|
interceptSourceUrl,
|
|
272
135
|
cacheOnly,
|
|
273
136
|
serverState,
|
|
274
137
|
} = opts;
|
|
275
|
-
// For reconciliation: always replace (URL already pushed), no scroll
|
|
276
|
-
const replace = isReconciliation ? true : opts.replace;
|
|
277
|
-
const scroll = isReconciliation ? false : opts.scroll;
|
|
278
138
|
|
|
279
139
|
const parsedUrl = new URL(url, window.location.origin);
|
|
280
140
|
|
|
@@ -286,14 +146,15 @@ export function createNavigationTransaction(
|
|
|
286
146
|
if (cacheOnly) {
|
|
287
147
|
const currentHandleData = eventController.getHandleState().data;
|
|
288
148
|
store.cacheSegmentsForHistory(historyKey, segments, currentHandleData);
|
|
149
|
+
// Complete the navigation handle so currentNavigation is cleared.
|
|
150
|
+
// Without this, the entry lingers and weakens state-machine invariants.
|
|
151
|
+
handle.complete(parsedUrl);
|
|
289
152
|
debugLog("[Browser] Cache-only commit, historyKey:", historyKey);
|
|
290
153
|
return;
|
|
291
154
|
}
|
|
292
155
|
|
|
293
|
-
// Save current scroll position before navigating
|
|
294
|
-
|
|
295
|
-
handleNavigationStart();
|
|
296
|
-
}
|
|
156
|
+
// Save current scroll position before navigating
|
|
157
|
+
handleNavigationStart();
|
|
297
158
|
|
|
298
159
|
// Update segment state atomically
|
|
299
160
|
store.setSegmentIds(segmentIds);
|
|
@@ -302,7 +163,7 @@ export function createNavigationTransaction(
|
|
|
302
163
|
|
|
303
164
|
store.setHistoryKey(historyKey);
|
|
304
165
|
|
|
305
|
-
// Cache segments with current handleData for this history entry
|
|
166
|
+
// Cache segments with current handleData for this history entry
|
|
306
167
|
const currentHandleData = eventController.getHandleState().data;
|
|
307
168
|
store.cacheSegmentsForHistory(historyKey, segments, currentHandleData);
|
|
308
169
|
|
|
@@ -321,50 +182,41 @@ export function createNavigationTransaction(
|
|
|
321
182
|
serverState,
|
|
322
183
|
);
|
|
323
184
|
|
|
324
|
-
//
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
if (replace || earlyStatePushed) {
|
|
328
|
-
window.history.replaceState(historyState, "", url);
|
|
329
|
-
} else {
|
|
330
|
-
window.history.pushState(historyState, "", url);
|
|
331
|
-
}
|
|
332
|
-
// Ensure new history entry has a scroll restoration key
|
|
333
|
-
ensureHistoryKey();
|
|
185
|
+
// Snapshot old state before pushState/replaceState overwrites it.
|
|
186
|
+
// Used to detect when location state is being cleared.
|
|
187
|
+
const oldState = window.history.state;
|
|
334
188
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
189
|
+
// Update browser URL
|
|
190
|
+
if (replace) {
|
|
191
|
+
window.history.replaceState(historyState, "", url);
|
|
192
|
+
} else {
|
|
193
|
+
window.history.pushState(historyState, "", url);
|
|
194
|
+
}
|
|
195
|
+
// Ensure new history entry has a scroll restoration key
|
|
196
|
+
ensureHistoryKey();
|
|
197
|
+
|
|
198
|
+
// Notify location state hooks when either old or new state carries
|
|
199
|
+
// location state. This covers both "set new state" and "clear old state"
|
|
200
|
+
// for same-page navigations where components don't remount.
|
|
201
|
+
if (hasLocationState(oldState) || hasLocationState(historyState)) {
|
|
202
|
+
window.dispatchEvent(new Event("__rsc_locationstate"));
|
|
344
203
|
}
|
|
345
204
|
|
|
346
205
|
// Complete the navigation in event controller (sets idle state, updates location)
|
|
347
206
|
handle.complete(parsedUrl);
|
|
348
207
|
|
|
349
|
-
// Handle scroll after navigation
|
|
350
|
-
|
|
351
|
-
handleNavigationEnd({ scroll });
|
|
352
|
-
}
|
|
208
|
+
// Handle scroll after navigation
|
|
209
|
+
handleNavigationEnd({ scroll });
|
|
353
210
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
historyKey,
|
|
360
|
-
intercept ? "(intercept)" : "",
|
|
361
|
-
);
|
|
362
|
-
}
|
|
211
|
+
debugLog(
|
|
212
|
+
"[Browser] Navigation committed, historyKey:",
|
|
213
|
+
historyKey,
|
|
214
|
+
intercept ? "(intercept)" : "",
|
|
215
|
+
);
|
|
363
216
|
}
|
|
364
217
|
|
|
365
218
|
return {
|
|
366
219
|
handle,
|
|
367
|
-
optimisticCommit,
|
|
368
220
|
commit,
|
|
369
221
|
|
|
370
222
|
/**
|
|
@@ -428,27 +280,14 @@ export function createNavigationTransaction(
|
|
|
428
280
|
},
|
|
429
281
|
|
|
430
282
|
[Symbol.dispose]() {
|
|
431
|
-
// Superseded: another navigation took over.
|
|
432
|
-
// so the new navigation starts from a clean history position.
|
|
433
|
-
// Guard: only rollback if our early-pushed state is still the current
|
|
434
|
-
// history entry. Compare by stamp (monotonic counter embedded in state)
|
|
435
|
-
// so that a newer navigation targeting the same URL is not clobbered.
|
|
283
|
+
// Superseded: another navigation took over.
|
|
436
284
|
if (handle.signal.aborted) {
|
|
437
|
-
if (
|
|
438
|
-
earlyStatePushed &&
|
|
439
|
-
!committed &&
|
|
440
|
-
!optimisticallyCommitted &&
|
|
441
|
-
earlyStateStamp !== null &&
|
|
442
|
-
window.history.state?.__navStamp === earlyStateStamp
|
|
443
|
-
) {
|
|
444
|
-
window.history.replaceState(currentHistoryState, "", currentUrl);
|
|
445
|
-
}
|
|
446
285
|
return;
|
|
447
286
|
}
|
|
448
287
|
|
|
449
288
|
// Failed (not committed): keep the target URL -- the error UI owns it.
|
|
450
289
|
// Just reset the event controller to idle.
|
|
451
|
-
if (!committed
|
|
290
|
+
if (!committed) {
|
|
452
291
|
handle[Symbol.dispose]();
|
|
453
292
|
}
|
|
454
293
|
},
|