@rangojs/router 0.18.0 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/types/browser/event-controller.d.ts +3 -3
- package/dist/types/browser/history-state.d.ts +2 -0
- package/dist/types/browser/navigation-transaction.d.ts +20 -2
- package/dist/types/browser/partial-update.d.ts +16 -1
- package/dist/types/browser/prefetch/cache.d.ts +6 -6
- package/dist/types/browser/react/Link.d.ts +19 -9
- package/dist/types/browser/react/context.d.ts +2 -2
- package/dist/types/browser/react/location-state-shared.d.ts +95 -56
- package/dist/types/browser/react/location-state.d.ts +1 -1
- package/dist/types/browser/transition-when.d.ts +80 -0
- package/dist/types/browser/types.d.ts +104 -9
- package/dist/types/build/generate-manifest.d.ts +8 -0
- package/dist/types/cache/background-task.d.ts +24 -2
- package/dist/types/cache/cache-exec-scope.d.ts +31 -0
- package/dist/types/cache/cache-key-utils.d.ts +44 -13
- package/dist/types/cache/cache-policy.d.ts +1 -1
- package/dist/types/cache/cache-runtime.d.ts +7 -0
- package/dist/types/cache/cache-scope.d.ts +117 -35
- package/dist/types/cache/cache-tag.d.ts +24 -4
- package/dist/types/cache/cf/cf-cache-store.d.ts +55 -44
- package/dist/types/cache/cf/cf-shell-frame.d.ts +1 -1
- package/dist/types/cache/cf/cf-tag-marker-memo.d.ts +0 -15
- package/dist/types/cache/document-cache.d.ts +10 -0
- package/dist/types/cache/handle-snapshot.d.ts +40 -8
- package/dist/types/cache/invalidation-order.d.ts +23 -0
- package/dist/types/cache/request-tag-mask.d.ts +54 -0
- package/dist/types/cache/shell-snapshot.d.ts +88 -141
- package/dist/types/cache/tag-invalidation.d.ts +38 -14
- package/dist/types/cache/types.d.ts +68 -61
- package/dist/types/cache/vercel/vercel-cache-store.d.ts +23 -16
- package/dist/types/client-urls/navigation.d.ts +28 -2
- package/dist/types/client-urls/server-projection.d.ts +5 -2
- package/dist/types/client-urls/types.d.ts +5 -5
- package/dist/types/client.d.ts +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.rsc.d.ts +1 -1
- package/dist/types/route-definition/helpers-types.d.ts +11 -11
- package/dist/types/route-name.d.ts +7 -0
- package/dist/types/router/error-handling.d.ts +1 -1
- package/dist/types/router/intercept-resolution.d.ts +2 -0
- package/dist/types/router/match-context.d.ts +5 -0
- package/dist/types/router/match-middleware/background-revalidation.d.ts +0 -4
- package/dist/types/router/router-options.d.ts +4 -1
- package/dist/types/router/segment-resolution/loader-cache.d.ts +18 -1
- package/dist/types/router/segment-resolution/loader-mask.d.ts +3 -4
- package/dist/types/router/segment-resolution/loader-snapshot.d.ts +14 -40
- package/dist/types/router/segment-resolution/mask-nested.d.ts +11 -3
- package/dist/types/router/segment-resolution/view-transition-default.d.ts +7 -5
- package/dist/types/router/transition-when.d.ts +24 -10
- package/dist/types/rsc/attach-transition-when.d.ts +18 -0
- package/dist/types/rsc/full-payload.d.ts +14 -7
- package/dist/types/rsc/response-cache-serve.d.ts +15 -12
- package/dist/types/rsc/shell-build-manifest.d.ts +4 -3
- package/dist/types/rsc/shell-capture-constants.d.ts +77 -20
- package/dist/types/rsc/shell-capture.d.ts +78 -81
- package/dist/types/rsc/shell-serve.d.ts +19 -48
- package/dist/types/rsc/types.d.ts +2 -0
- package/dist/types/segment-system.d.ts +7 -0
- package/dist/types/server/context.d.ts +89 -61
- package/dist/types/server/cookie-store.d.ts +30 -18
- package/dist/types/server/handle-store.d.ts +52 -1
- package/dist/types/server/request-context.d.ts +66 -45
- package/dist/types/server/request-headers.d.ts +44 -0
- package/dist/types/server.d.ts +1 -0
- package/dist/types/testing/e2e/index.d.ts +1 -1
- package/dist/types/testing/index.d.ts +3 -2
- package/dist/types/testing/internal/action-id.d.ts +7 -0
- package/dist/types/testing/location-state-key.d.ts +24 -0
- package/dist/types/testing/render-route.d.ts +17 -1
- package/dist/types/testing/run-transition-when.d.ts +44 -69
- package/dist/types/testing/serve-shell-request.d.ts +7 -6
- package/dist/types/testing/shell-status.d.ts +34 -22
- package/dist/types/transition-when-ref.d.ts +79 -0
- package/dist/types/types/cache-types.d.ts +15 -3
- package/dist/types/types/index.d.ts +1 -1
- package/dist/types/types/segments.d.ts +86 -71
- package/dist/types/urls/path-helper-types.d.ts +3 -4
- package/dist/types/urls/pattern-types.d.ts +3 -4
- package/dist/types/vite/plugins/transition-when-hoist.d.ts +45 -0
- package/dist/vite/index.js +960 -97
- package/package.json +1 -1
- package/skills/cache-guide/SKILL.md +76 -49
- package/skills/caching/SKILL.md +167 -69
- package/skills/client-urls/SKILL.md +1 -1
- package/skills/comparison/references/framework-comparison.md +8 -5
- package/skills/document-cache/SKILL.md +24 -0
- package/skills/hooks/SKILL.md +3 -1
- package/skills/hooks/navigation.md +23 -2
- package/skills/hooks/state.md +98 -6
- package/skills/intercept/SKILL.md +29 -12
- package/skills/loader/SKILL.md +52 -21
- package/skills/middleware/SKILL.md +18 -0
- package/skills/migrate-nextjs/SKILL.md +3 -3
- package/skills/observability/SKILL.md +6 -5
- package/skills/parallel/SKILL.md +1 -1
- package/skills/ppr/SKILL.md +129 -77
- package/skills/prerender/SKILL.md +3 -3
- package/skills/rango/SKILL.md +25 -6
- package/skills/route/SKILL.md +2 -0
- package/skills/testing/SKILL.md +27 -25
- package/skills/testing/cache-prerender.md +9 -9
- package/skills/testing/client-components.md +45 -3
- package/skills/testing/loader.md +3 -2
- package/skills/testing/middleware.md +1 -0
- package/skills/testing/predicates.md +49 -49
- package/skills/testing/render-handler.md +1 -0
- package/skills/testing/serve-shell-request.md +14 -0
- package/skills/testing/server-actions.md +9 -3
- package/skills/theme/SKILL.md +8 -4
- package/skills/typesafety/env-and-bindings.md +8 -0
- package/skills/use-cache/SKILL.md +64 -19
- package/skills/view-transitions/SKILL.md +150 -29
- package/src/__augment-tests__/augmented.check.ts +23 -0
- package/src/browser/event-controller.ts +4 -4
- package/src/browser/history-state.ts +52 -1
- package/src/browser/link-interceptor.ts +4 -0
- package/src/browser/navigation-bridge.ts +77 -18
- package/src/browser/navigation-client.ts +4 -3
- package/src/browser/navigation-store.ts +43 -1
- package/src/browser/navigation-transaction.ts +31 -11
- package/src/browser/partial-update.ts +146 -35
- package/src/browser/prefetch/cache.ts +6 -6
- package/src/browser/prefetch/fetch.ts +2 -2
- package/src/browser/react/Link.tsx +38 -33
- package/src/browser/react/NavigationProvider.tsx +2 -2
- package/src/browser/react/context.ts +2 -2
- package/src/browser/react/location-state-shared.ts +241 -49
- package/src/browser/react/location-state.ts +5 -2
- package/src/browser/rsc-router.tsx +4 -0
- package/src/browser/server-action-bridge.ts +84 -15
- package/src/browser/transition-when.ts +205 -0
- package/src/browser/types.ts +124 -10
- package/src/build/generate-manifest.ts +77 -1
- package/src/cache/background-task.ts +55 -2
- package/src/cache/cache-exec-scope.ts +48 -1
- package/src/cache/cache-key-utils.ts +56 -16
- package/src/cache/cache-policy.ts +9 -4
- package/src/cache/cache-runtime.ts +197 -144
- package/src/cache/cache-scope.ts +304 -112
- package/src/cache/cache-tag.ts +105 -13
- package/src/cache/cf/cf-cache-store.ts +152 -76
- package/src/cache/cf/cf-shell-frame.ts +2 -2
- package/src/cache/cf/cf-tag-marker-memo.ts +11 -100
- package/src/cache/document-cache.ts +53 -7
- package/src/cache/handle-snapshot.ts +52 -9
- package/src/cache/invalidation-order.ts +84 -0
- package/src/cache/request-tag-mask.ts +147 -0
- package/src/cache/shell-snapshot.ts +130 -348
- package/src/cache/tag-invalidation.ts +57 -51
- package/src/cache/types.ts +71 -67
- package/src/cache/vercel/vercel-cache-store.ts +61 -58
- package/src/client-urls/client-root.tsx +8 -4
- package/src/client-urls/client-urls.ts +8 -6
- package/src/client-urls/navigation.ts +55 -4
- package/src/client-urls/server-projection.ts +31 -10
- package/src/client-urls/types.ts +12 -5
- package/src/client.tsx +2 -0
- package/src/host/cookie-handler.ts +2 -1
- package/src/index.rsc.ts +3 -0
- package/src/index.ts +3 -0
- package/src/loader-redirect.tsx +7 -3
- package/src/prerender/build-shell-capture.ts +155 -146
- package/src/route-definition/dsl-helpers.ts +2 -0
- package/src/route-definition/helpers-types.ts +11 -11
- package/src/route-name.ts +12 -0
- package/src/router/content-negotiation.ts +4 -1
- package/src/router/error-handling.ts +5 -2
- package/src/router/handler-context.ts +10 -7
- package/src/router/intercept-resolution.ts +11 -1
- package/src/router/loader-resolution.ts +28 -3
- package/src/router/logging.ts +5 -3
- package/src/router/match-api.ts +62 -34
- package/src/router/match-context.ts +5 -0
- package/src/router/match-handlers.ts +9 -29
- package/src/router/match-middleware/background-revalidation.ts +3 -5
- package/src/router/match-middleware/cache-lookup.ts +35 -19
- package/src/router/match-middleware/cache-store.ts +15 -9
- package/src/router/match-result.ts +1 -0
- package/src/router/middleware.ts +2 -5
- package/src/router/navigation-snapshot.ts +6 -5
- package/src/router/prerender-match.ts +4 -0
- package/src/router/request-classification.ts +7 -4
- package/src/router/router-options.ts +4 -1
- package/src/router/segment-resolution/fresh.ts +0 -4
- package/src/router/segment-resolution/loader-cache.ts +150 -25
- package/src/router/segment-resolution/loader-mask.ts +3 -4
- package/src/router/segment-resolution/loader-snapshot.ts +15 -78
- package/src/router/segment-resolution/mask-nested.ts +47 -22
- package/src/router/segment-resolution/revalidation.ts +0 -3
- package/src/router/segment-resolution/view-transition-default.ts +6 -21
- package/src/router/telemetry.ts +6 -3
- package/src/router/transition-when.ts +34 -52
- package/src/router.ts +2 -1
- package/src/rsc/attach-transition-when.ts +47 -0
- package/src/rsc/full-payload.ts +30 -10
- package/src/rsc/handler.ts +12 -10
- package/src/rsc/loader-fetch.ts +2 -1
- package/src/rsc/manifest-init.ts +4 -0
- package/src/rsc/origin-guard.ts +8 -5
- package/src/rsc/progressive-enhancement.ts +7 -30
- package/src/rsc/response-cache-serve.ts +68 -80
- package/src/rsc/response-route-handler.ts +2 -2
- package/src/rsc/rsc-rendering.ts +206 -252
- package/src/rsc/server-action.ts +8 -20
- package/src/rsc/shell-build-manifest.ts +22 -20
- package/src/rsc/shell-capture-constants.ts +136 -20
- package/src/rsc/shell-capture.ts +352 -589
- package/src/rsc/shell-serve.ts +73 -109
- package/src/rsc/ssr-setup.ts +3 -2
- package/src/rsc/types.ts +2 -0
- package/src/segment-system.tsx +40 -7
- package/src/server/context.ts +189 -124
- package/src/server/cookie-store.ts +153 -127
- package/src/server/handle-store.ts +245 -27
- package/src/server/request-context.ts +123 -84
- package/src/server/request-headers.ts +134 -0
- package/src/server.ts +4 -0
- package/src/testing/dispatch.ts +4 -7
- package/src/testing/e2e/index.ts +1 -0
- package/src/testing/index.ts +5 -0
- package/src/testing/internal/action-id.ts +25 -0
- package/src/testing/internal/context.ts +1 -1
- package/src/testing/location-state-key.ts +45 -0
- package/src/testing/render-handler.ts +1 -1
- package/src/testing/render-route.tsx +109 -16
- package/src/testing/run-client-revalidate.ts +3 -19
- package/src/testing/run-loader.ts +5 -1
- package/src/testing/run-transition-when.ts +109 -172
- package/src/testing/serve-shell-request.ts +10 -7
- package/src/testing/shell-status.ts +60 -56
- package/src/transition-when-ref.ts +220 -0
- package/src/types/cache-types.ts +15 -3
- package/src/types/index.ts +3 -0
- package/src/types/segments.ts +94 -91
- package/src/urls/path-helper-types.ts +3 -4
- package/src/urls/pattern-types.ts +3 -4
- package/src/vite/discovery/discover-routers.ts +3 -0
- package/src/vite/discovery/shell-prerender-phase.ts +0 -2
- package/src/vite/discovery/virtual-module-codegen.ts +16 -6
- package/src/vite/plugins/transition-when-hoist.ts +1064 -0
- package/src/vite/rango.ts +2 -0
- package/src/vite/router-discovery.ts +75 -5
- package/dist/types/rsc/transition-gate.d.ts +0 -23
- package/src/rsc/transition-gate.ts +0 -86
|
@@ -49,7 +49,7 @@ import { getFetchableLoader } from "./fetchable-loader-store.js";
|
|
|
49
49
|
import type { SegmentCacheStore } from "../cache/types.js";
|
|
50
50
|
import type { Theme, ResolvedThemeConfig } from "../theme/types.js";
|
|
51
51
|
import type { ExecutionContext, RequestScope } from "../types/request-scope.js";
|
|
52
|
-
import type {
|
|
52
|
+
import type { TransitionWhenRecord } from "../router/transition-when.js";
|
|
53
53
|
import type { ResolvedTracing } from "../router/tracing.js";
|
|
54
54
|
import type { RenderMode, RenderPhase } from "../router/timeout.js";
|
|
55
55
|
import {
|
|
@@ -65,7 +65,12 @@ import {
|
|
|
65
65
|
assertNonCacheableReadAllowed,
|
|
66
66
|
clearPprHeaderScope,
|
|
67
67
|
} from "./context.js";
|
|
68
|
-
import {
|
|
68
|
+
import {
|
|
69
|
+
guardRawCookieRead,
|
|
70
|
+
guardRequestHeaders,
|
|
71
|
+
readGuardedTheme,
|
|
72
|
+
} from "./cookie-store.js";
|
|
73
|
+
import { requestHeaders } from "./request-headers.js";
|
|
69
74
|
import {
|
|
70
75
|
createReverseFunction,
|
|
71
76
|
stripInternalParams,
|
|
@@ -76,6 +81,10 @@ import {
|
|
|
76
81
|
getSearchSchema,
|
|
77
82
|
} from "../route-map-builder.js";
|
|
78
83
|
import { parseSearchParams } from "../search-params.js";
|
|
84
|
+
import {
|
|
85
|
+
executionStart,
|
|
86
|
+
type ExecutionStart,
|
|
87
|
+
} from "../cache/invalidation-order.js";
|
|
79
88
|
import { invariant } from "../errors.js";
|
|
80
89
|
import { isAutoGeneratedRouteName } from "../route-name.js";
|
|
81
90
|
|
|
@@ -146,10 +155,24 @@ export interface RequestContext<
|
|
|
146
155
|
/** @internal Request-local PPR opt-out marker set by ctx.dynamic(). */
|
|
147
156
|
_dynamic?: boolean;
|
|
148
157
|
|
|
149
|
-
/**
|
|
158
|
+
/**
|
|
159
|
+
* @internal Get a cookie value (effective: request + response mutations).
|
|
160
|
+
* Use cookies().get() instead. Guarded like cookies() (#976).
|
|
161
|
+
*/
|
|
150
162
|
cookie(name: string): string | undefined;
|
|
151
|
-
/**
|
|
163
|
+
/**
|
|
164
|
+
* @internal Get all cookies (effective merged view). Use
|
|
165
|
+
* cookies().getAll() instead. Guarded like cookies() (#976).
|
|
166
|
+
*/
|
|
152
167
|
cookies(): Record<string, string>;
|
|
168
|
+
/**
|
|
169
|
+
* @internal `cookie()` without the identity guard: the read behind the
|
|
170
|
+
* cookies() store, which guards at its own call and records per read
|
|
171
|
+
* method. Non-enumerable, like `_readTheme`.
|
|
172
|
+
*/
|
|
173
|
+
_readCookie(name: string): string | undefined;
|
|
174
|
+
/** @internal `cookies()` without the identity guard (see `_readCookie`). */
|
|
175
|
+
_readCookies(): Record<string, string>;
|
|
153
176
|
/** @internal Set a cookie on the response. Use cookies().set() instead. */
|
|
154
177
|
setCookie(name: string, value: string, options?: CookieOptions): void;
|
|
155
178
|
/** @internal Delete a cookie. Use cookies().delete() instead. */
|
|
@@ -182,19 +205,18 @@ export interface RequestContext<
|
|
|
182
205
|
/**
|
|
183
206
|
* @internal The context createRequestContext() returned. A context derived
|
|
184
207
|
* from it with Object.create (a PPR HIT tail, a shell capture, a background
|
|
185
|
-
* revalidation render) inherits it, so per-request invalidation state
|
|
186
|
-
*
|
|
187
|
-
*
|
|
208
|
+
* revalidation render) inherits it, so per-request invalidation state (the
|
|
209
|
+
* stores' masks) is keyed by the request, not by whichever derived object
|
|
210
|
+
* called or read (#973).
|
|
188
211
|
*/
|
|
189
212
|
_requestRoot?: RequestContext<any, any>;
|
|
190
213
|
/**
|
|
191
|
-
* @internal
|
|
192
|
-
*
|
|
193
|
-
*
|
|
194
|
-
*
|
|
195
|
-
* invalidated is neither joined nor written (cache-runtime.ts, #973).
|
|
214
|
+
* @internal Where the request started in the isolate's invalidation order
|
|
215
|
+
* (cache/invalidation-order.ts), taken when createRequestContext() ran. The
|
|
216
|
+
* document cache gates its write on it (#977): a middleware ahead of the
|
|
217
|
+
* document cache can read tagged data the document bakes.
|
|
196
218
|
*/
|
|
197
|
-
|
|
219
|
+
_requestStart?: ExecutionStart;
|
|
198
220
|
/**
|
|
199
221
|
* @internal The handler has handed the response to the host (rsc/handler.ts
|
|
200
222
|
* `rango.response`): header and cookie writes after this are lost, as from
|
|
@@ -242,16 +264,13 @@ export interface RequestContext<
|
|
|
242
264
|
readonly _handleStore: HandleStore;
|
|
243
265
|
|
|
244
266
|
/**
|
|
245
|
-
* @internal transition({ when })
|
|
246
|
-
* keyed by segment id.
|
|
247
|
-
*
|
|
248
|
-
*
|
|
249
|
-
*
|
|
267
|
+
* @internal transition({ when }) client references of the matched entry
|
|
268
|
+
* chain, keyed by segment id. Recorded at match time from the static route
|
|
269
|
+
* definition (router/transition-when.ts) and attached to the payload's
|
|
270
|
+
* segments right before Flight (rsc/attach-transition-when.ts). The server
|
|
271
|
+
* never calls them, and no cache or snapshot stores them.
|
|
250
272
|
*/
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
/** @internal PPR transition decisions evaluated before cache lookup/handlers. */
|
|
254
|
-
_pprTransitionDecisions?: Map<string, boolean>;
|
|
273
|
+
_transitionWhenRefs?: ReadonlyMap<string, TransitionWhenRecord>;
|
|
255
274
|
|
|
256
275
|
/**
|
|
257
276
|
* @internal Post-match serve-source truth for the PPR replay reporter.
|
|
@@ -279,8 +298,8 @@ export interface RequestContext<
|
|
|
279
298
|
* capture task's derived request context (built by shell-capture.ts). This is
|
|
280
299
|
* the switch every capture-specific behavior reads: loader masking
|
|
281
300
|
* (loader-mask.ts isShellCaptureActive / fresh.ts emitStreaming) and the
|
|
282
|
-
*
|
|
283
|
-
*
|
|
301
|
+
* identity-read capture guard (server/context.ts guardIdentityRead). The
|
|
302
|
+
* foreground render never sets it, so the
|
|
284
303
|
* served response is byte-identical to axis 1. The capture descriptor itself
|
|
285
304
|
* (key/ttl/swr/tags/store) is NOT threaded through the request context — the
|
|
286
305
|
* integrated PPR serve path (rsc/shell-serve.ts + rsc-rendering.ts) builds it
|
|
@@ -308,6 +327,16 @@ export interface RequestContext<
|
|
|
308
327
|
*/
|
|
309
328
|
_shellCaptureUnrecordedLoaderPush?: true;
|
|
310
329
|
|
|
330
|
+
/**
|
|
331
|
+
* @internal The lane of each loader the capture's matched route registers
|
|
332
|
+
* (loader-cache.ts routeLoaderLanes), set by createMatchContextForFull
|
|
333
|
+
* before any loader runs. The capture's push funnel credits a push made
|
|
334
|
+
* under a live-lane loader's body, at any depth, to that loader
|
|
335
|
+
* (shell-capture.ts deriveShellCaptureContext). Own property of the
|
|
336
|
+
* capture's derived context only.
|
|
337
|
+
*/
|
|
338
|
+
_shellCaptureLoaderLanes?: ReadonlyMap<string, "live" | "bake">;
|
|
339
|
+
|
|
311
340
|
/**
|
|
312
341
|
* @internal Record keys resolved for this request (CacheScope
|
|
313
342
|
* resolveKeyFrom): by the scope's `key()` (one result) or its store's
|
|
@@ -414,7 +443,9 @@ export interface RequestContext<
|
|
|
414
443
|
* scope replaces any route-derived cache() scope for the tail's match
|
|
415
444
|
* (resolveShellImplicitCacheScope), and a lookup that does not hit throws
|
|
416
445
|
* ShellRecordUnavailableError instead of resolving segments
|
|
417
|
-
* (withCacheLookup): a HIT never runs a handler.
|
|
446
|
+
* (withCacheLookup): a HIT never runs a handler. The record's
|
|
447
|
+
* loader-owned handle pushes restore as authoritative
|
|
448
|
+
* (HandleStore.pushRestored): the prelude rendered them.
|
|
418
449
|
*/
|
|
419
450
|
docTail?: true;
|
|
420
451
|
/**
|
|
@@ -468,8 +499,9 @@ export interface RequestContext<
|
|
|
468
499
|
|
|
469
500
|
/**
|
|
470
501
|
* @internal Set by the capture guard (server/context.ts
|
|
471
|
-
*
|
|
472
|
-
* ("cookies()", "headers()",
|
|
502
|
+
* guardIdentityRead) when it throws DURING a capture render: the read
|
|
503
|
+
* ("cookies()", "headers()", `ctx.get() for a non-cacheable variable`,
|
|
504
|
+
* "ctx.theme") and the fix the
|
|
473
505
|
* refusal warning gives for it. Load-bearing for the
|
|
474
506
|
* bake lane: a guard throw inside an executing loader is swallowed by
|
|
475
507
|
* wrapLoaderPromise into per-loader error UI, which would otherwise bake
|
|
@@ -611,12 +643,27 @@ export interface RequestContext<
|
|
|
611
643
|
/**
|
|
612
644
|
* @internal The theme cookie (or the default) read with no identity guard.
|
|
613
645
|
* The guarded reads (`theme` here, the handler and middleware `ctx.theme`)
|
|
614
|
-
* call it after
|
|
615
|
-
* through payloadInitialTheme (rsc/full-payload.ts)
|
|
646
|
+
* call it after guardIdentityRead (readGuardedTheme); the router's own payload read goes
|
|
647
|
+
* through payloadInitialTheme (rsc/full-payload.ts), except the unmatched
|
|
648
|
+
* route's 404 (rsc/handler.ts), which is never stored. Undefined without
|
|
616
649
|
* theme config.
|
|
617
650
|
*/
|
|
618
651
|
_readTheme(): Theme | undefined;
|
|
619
652
|
|
|
653
|
+
/**
|
|
654
|
+
* @internal Set by the document-cache middleware (cache/document-cache.ts)
|
|
655
|
+
* before it runs a render whose response it may store: the MISS and the
|
|
656
|
+
* stale refresh. payloadInitialTheme reads it (#978).
|
|
657
|
+
*/
|
|
658
|
+
_documentCacheRender?: boolean;
|
|
659
|
+
|
|
660
|
+
/**
|
|
661
|
+
* @internal Set by payloadInitialTheme when the document payload carries
|
|
662
|
+
* the visitor's theme and it differs from the no-cookie default. The
|
|
663
|
+
* document cache does not store that response (#978).
|
|
664
|
+
*/
|
|
665
|
+
_payloadVisitorTheme?: boolean;
|
|
666
|
+
|
|
620
667
|
/**
|
|
621
668
|
* Attach location state entries to the current response.
|
|
622
669
|
*
|
|
@@ -660,20 +707,6 @@ export interface RequestContext<
|
|
|
660
707
|
/** @internal Previous route key (from the navigation source), used for revalidation */
|
|
661
708
|
_prevRouteKey?: string;
|
|
662
709
|
|
|
663
|
-
/**
|
|
664
|
-
* @internal Navigation/action source data the transition({ when }) gate reads
|
|
665
|
-
* to build its ShouldRevalidateFn-shaped predicate context. currentUrl/Params
|
|
666
|
-
* come from the navigation snapshot (set at match time); action* are stashed
|
|
667
|
-
* at the action-bearing gate call sites. All undefined when there is no source
|
|
668
|
-
* (initial full load) or no action (plain navigation).
|
|
669
|
-
*/
|
|
670
|
-
_gateCurrentUrl?: URL;
|
|
671
|
-
_gateCurrentParams?: Record<string, string>;
|
|
672
|
-
_gateActionId?: string;
|
|
673
|
-
_gateActionUrl?: URL;
|
|
674
|
-
_gateActionResult?: unknown;
|
|
675
|
-
_gateFormData?: FormData;
|
|
676
|
-
|
|
677
710
|
/**
|
|
678
711
|
* @internal True while the post-action revalidation render is running (set by
|
|
679
712
|
* revalidateAfterAction). The "use cache" runtime reads this to prefer
|
|
@@ -872,11 +905,12 @@ export type PublicRequestContext<
|
|
|
872
905
|
RequestContext<TEnv, TParams>,
|
|
873
906
|
| "cookie"
|
|
874
907
|
| "cookies"
|
|
908
|
+
| "_readCookie"
|
|
909
|
+
| "_readCookies"
|
|
875
910
|
| "setCookie"
|
|
876
911
|
| "deleteCookie"
|
|
877
912
|
| "_handleStore"
|
|
878
|
-
| "
|
|
879
|
-
| "_pprTransitionDecisions"
|
|
913
|
+
| "_transitionWhenRefs"
|
|
880
914
|
| "_pprReplayPostMatchReason"
|
|
881
915
|
| "_cacheStore"
|
|
882
916
|
| "_searchParamsFilter"
|
|
@@ -885,6 +919,7 @@ export type PublicRequestContext<
|
|
|
885
919
|
| "_shellLoaderSeed"
|
|
886
920
|
| "_shellCaptureLoaderRecords"
|
|
887
921
|
| "_shellCaptureUnrecordedLoaderPush"
|
|
922
|
+
| "_shellCaptureLoaderLanes"
|
|
888
923
|
| "_resolvedCacheKeys"
|
|
889
924
|
| "_shellKey"
|
|
890
925
|
| "_shellForcedMiss"
|
|
@@ -901,15 +936,11 @@ export type PublicRequestContext<
|
|
|
901
936
|
| "_pendingBackgroundTasks"
|
|
902
937
|
| "_themeConfig"
|
|
903
938
|
| "_readTheme"
|
|
939
|
+
| "_documentCacheRender"
|
|
940
|
+
| "_payloadVisitorTheme"
|
|
904
941
|
| "_locationState"
|
|
905
942
|
| "_routeName"
|
|
906
943
|
| "_prevRouteKey"
|
|
907
|
-
| "_gateCurrentUrl"
|
|
908
|
-
| "_gateCurrentParams"
|
|
909
|
-
| "_gateActionId"
|
|
910
|
-
| "_gateActionUrl"
|
|
911
|
-
| "_gateActionResult"
|
|
912
|
-
| "_gateFormData"
|
|
913
944
|
| "_inActionRevalidation"
|
|
914
945
|
| "_reportedErrors"
|
|
915
946
|
| "_renderErrors"
|
|
@@ -938,7 +969,7 @@ export type PublicRequestContext<
|
|
|
938
969
|
| "_freshReads"
|
|
939
970
|
| "_setFreshReadsCookie"
|
|
940
971
|
| "_requestRoot"
|
|
941
|
-
| "
|
|
972
|
+
| "_requestStart"
|
|
942
973
|
| "_responseSent"
|
|
943
974
|
| "_variables"
|
|
944
975
|
| "_classifiedRoute"
|
|
@@ -1042,17 +1073,10 @@ export function setRequestContextParams(
|
|
|
1042
1073
|
*/
|
|
1043
1074
|
export function setRequestContextPrevRouteKey(
|
|
1044
1075
|
prevRouteKey: string | undefined,
|
|
1045
|
-
currentUrl?: URL,
|
|
1046
|
-
currentParams?: Record<string, string>,
|
|
1047
1076
|
): void {
|
|
1048
1077
|
const ctx = requestContextStorage.getStore();
|
|
1049
1078
|
if (!ctx) return;
|
|
1050
1079
|
if (prevRouteKey !== undefined) ctx._prevRouteKey = prevRouteKey;
|
|
1051
|
-
// Source URL/params for the transition({ when }) gate (effectiveFromUrl /
|
|
1052
|
-
// effectiveFromMatch.params from the navigation snapshot). Same write point as
|
|
1053
|
-
// _prevRouteKey, which doubles as fromRouteName.
|
|
1054
|
-
if (currentUrl !== undefined) ctx._gateCurrentUrl = currentUrl;
|
|
1055
|
-
if (currentParams !== undefined) ctx._gateCurrentParams = currentParams;
|
|
1056
1080
|
}
|
|
1057
1081
|
|
|
1058
1082
|
/**
|
|
@@ -1134,7 +1158,10 @@ export function createRequestContext<TEnv>(
|
|
|
1134
1158
|
stateCookieName,
|
|
1135
1159
|
version: stateVersion,
|
|
1136
1160
|
} = options;
|
|
1137
|
-
|
|
1161
|
+
// ctx.request.headers is guarded like headers() (#976); every router read
|
|
1162
|
+
// of this request's headers goes through requestHeaders().
|
|
1163
|
+
guardRequestHeaders(request);
|
|
1164
|
+
const cookieHeader = requestHeaders(request).get("Cookie");
|
|
1138
1165
|
let rangoStateRotated = false;
|
|
1139
1166
|
const freshCookie = stateCookieName
|
|
1140
1167
|
? freshReadsCookieName(stateCookieName)
|
|
@@ -1233,6 +1260,26 @@ export function createRequestContext<TEnv>(
|
|
|
1233
1260
|
return getParsedCookies()[name];
|
|
1234
1261
|
};
|
|
1235
1262
|
|
|
1263
|
+
// The effective jar: request cookies minus deletions plus same-request writes.
|
|
1264
|
+
const effectiveCookies = (): Record<string, string> => {
|
|
1265
|
+
const parsed = getParsedCookies();
|
|
1266
|
+
const mutations = getResponseCookies();
|
|
1267
|
+
if (mutations.size === 0) return { ...parsed };
|
|
1268
|
+
// Build result without delete (avoids V8 dictionary-mode de-opt)
|
|
1269
|
+
const deleted = new Set<string>();
|
|
1270
|
+
for (const [k, v] of mutations) {
|
|
1271
|
+
if (v === null) deleted.add(k);
|
|
1272
|
+
}
|
|
1273
|
+
const result: Record<string, string> = {};
|
|
1274
|
+
for (const key of Object.keys(parsed)) {
|
|
1275
|
+
if (!deleted.has(key)) result[key] = parsed[key];
|
|
1276
|
+
}
|
|
1277
|
+
for (const [k, v] of mutations) {
|
|
1278
|
+
if (v !== null) result[k] = v;
|
|
1279
|
+
}
|
|
1280
|
+
return result;
|
|
1281
|
+
};
|
|
1282
|
+
|
|
1236
1283
|
const getTheme = (): Theme | undefined => {
|
|
1237
1284
|
if (!themeConfig) return undefined;
|
|
1238
1285
|
|
|
@@ -1312,29 +1359,23 @@ export function createRequestContext<TEnv>(
|
|
|
1312
1359
|
);
|
|
1313
1360
|
},
|
|
1314
1361
|
|
|
1362
|
+
// The visitor's cookies, guarded like cookies() (#976): before, a plain
|
|
1363
|
+
// read that a ppr shell, a cache() entry or a "use cache" entry stored
|
|
1364
|
+
// for every visitor.
|
|
1315
1365
|
cookie(name: string): string | undefined {
|
|
1366
|
+
guardRawCookieRead(ctx, "getRequestContext().cookie()");
|
|
1316
1367
|
return effectiveCookie(name);
|
|
1317
1368
|
},
|
|
1318
1369
|
|
|
1319
1370
|
cookies(): Record<string, string> {
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
if (mutations.size === 0) return { ...parsed };
|
|
1323
|
-
// Build result without delete (avoids V8 dictionary-mode de-opt)
|
|
1324
|
-
const deleted = new Set<string>();
|
|
1325
|
-
for (const [k, v] of mutations) {
|
|
1326
|
-
if (v === null) deleted.add(k);
|
|
1327
|
-
}
|
|
1328
|
-
const result: Record<string, string> = {};
|
|
1329
|
-
for (const key of Object.keys(parsed)) {
|
|
1330
|
-
if (!deleted.has(key)) result[key] = parsed[key];
|
|
1331
|
-
}
|
|
1332
|
-
for (const [k, v] of mutations) {
|
|
1333
|
-
if (v !== null) result[k] = v;
|
|
1334
|
-
}
|
|
1335
|
-
return result;
|
|
1371
|
+
guardRawCookieRead(ctx, "getRequestContext().cookies()");
|
|
1372
|
+
return effectiveCookies();
|
|
1336
1373
|
},
|
|
1337
1374
|
|
|
1375
|
+
// Non-enumerable, defined after this literal.
|
|
1376
|
+
_readCookie: effectiveCookie,
|
|
1377
|
+
_readCookies: effectiveCookies,
|
|
1378
|
+
|
|
1338
1379
|
setCookie(name: string, value: string, options?: CookieOptions): void {
|
|
1339
1380
|
assertNotInsideCacheExec(ctx, "setCookie");
|
|
1340
1381
|
stubResponse.headers.append(
|
|
@@ -1383,7 +1424,7 @@ export function createRequestContext<TEnv>(
|
|
|
1383
1424
|
// decodeStateValue decodes exactly once) AND is the same parser the client
|
|
1384
1425
|
// mirror uses, so both seats read the same jar entry.
|
|
1385
1426
|
const prevRaw =
|
|
1386
|
-
(request.
|
|
1427
|
+
(requestHeaders(request).get("x-rango-state") || null) ??
|
|
1387
1428
|
getRawCookieValue(cookieHeader, stateCookieName);
|
|
1388
1429
|
const value = mintStateValue(stateVersion ?? "0", prevRaw);
|
|
1389
1430
|
// rawStubHeaders: guard-exempt internal writer — invalidateClientCache()
|
|
@@ -1460,7 +1501,6 @@ export function createRequestContext<TEnv>(
|
|
|
1460
1501
|
method: request.method,
|
|
1461
1502
|
|
|
1462
1503
|
_handleStore: handleStore,
|
|
1463
|
-
_transitionWhen: [],
|
|
1464
1504
|
_cacheStore: cacheStore,
|
|
1465
1505
|
_searchParamsFilter: searchParamsFilter,
|
|
1466
1506
|
_explicitTaggedStores: explicitTaggedStores,
|
|
@@ -1560,20 +1600,18 @@ export function createRequestContext<TEnv>(
|
|
|
1560
1600
|
// the ambient context, as cookies() does, and falls back to the receiver.
|
|
1561
1601
|
// _readTheme is the unguarded read: non-enumerable too, so a spread copy of
|
|
1562
1602
|
// the context (the fetchable-loader ctx, loader-fetch.ts) cannot carry it.
|
|
1603
|
+
// The same holds for the unguarded cookie reads (#976).
|
|
1563
1604
|
Object.defineProperties(ctx, {
|
|
1564
1605
|
theme: {
|
|
1565
1606
|
get(this: RequestContext<TEnv>): Theme | undefined {
|
|
1566
|
-
|
|
1567
|
-
assertThemeReadAllowed(
|
|
1568
|
-
_getRequestContext() ?? this,
|
|
1569
|
-
"getRequestContext().theme",
|
|
1570
|
-
);
|
|
1571
|
-
return getTheme();
|
|
1607
|
+
return readGuardedTheme(this, "getRequestContext().theme");
|
|
1572
1608
|
},
|
|
1573
1609
|
enumerable: false,
|
|
1574
1610
|
configurable: true,
|
|
1575
1611
|
},
|
|
1576
1612
|
_readTheme: { enumerable: false },
|
|
1613
|
+
_readCookie: { enumerable: false },
|
|
1614
|
+
_readCookies: { enumerable: false },
|
|
1577
1615
|
});
|
|
1578
1616
|
|
|
1579
1617
|
(ctx as any)[NOCACHE_SYMBOL] = true;
|
|
@@ -1585,6 +1623,7 @@ export function createRequestContext<TEnv>(
|
|
|
1585
1623
|
configurable: true,
|
|
1586
1624
|
writable: true,
|
|
1587
1625
|
});
|
|
1626
|
+
ctx._requestStart = executionStart();
|
|
1588
1627
|
return ctx;
|
|
1589
1628
|
}
|
|
1590
1629
|
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The guarded `ctx.request.headers` (#976) and the router's raw reads.
|
|
3
|
+
*
|
|
4
|
+
* The Request the router hands to user code (handler, middleware and loader
|
|
5
|
+
* `ctx.request`, `getRequestContext().request`) carries an own `headers`
|
|
6
|
+
* getter that shadows the prototype's and runs the identity guard
|
|
7
|
+
* (cookie-store.ts guardRequestHeaders). A Proxy around the Request is not an
|
|
8
|
+
* option: `fetch(request)`, `new Request(request)` and `request.clone()` read
|
|
9
|
+
* the platform's internal slots, which a Proxy does not have. The own getter
|
|
10
|
+
* keeps the object a real Request, and those operations never call it.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/** The Headers each shadowed request's getter returns. */
|
|
14
|
+
const RAW_HEADERS = new WeakMap<Request, Headers>();
|
|
15
|
+
|
|
16
|
+
const INSPECT = Symbol.for("nodejs.util.inspect.custom");
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Set while the platform or a request facade reads a shadowed request's
|
|
20
|
+
* headers for itself, not for user code (runUnguarded).
|
|
21
|
+
*/
|
|
22
|
+
let unguarded = false;
|
|
23
|
+
|
|
24
|
+
function runUnguarded<T>(fn: () => T): T {
|
|
25
|
+
const outer = unguarded;
|
|
26
|
+
unguarded = true;
|
|
27
|
+
try {
|
|
28
|
+
return fn();
|
|
29
|
+
} finally {
|
|
30
|
+
unguarded = outer;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* A request's headers, past the guard. The rule: router code never reads a
|
|
36
|
+
* request's headers through the getter, only through this, so the guard sees
|
|
37
|
+
* user reads alone. Several router reads run inside the scopes the guard
|
|
38
|
+
* refuses (the state cookie rotation at a ppr capture, match's HMR marker,
|
|
39
|
+
* a store's cf-ray inside a "use cache" body, telemetry inside a loader
|
|
40
|
+
* fill); the rest follow the rule so a new call site cannot drift into one.
|
|
41
|
+
*/
|
|
42
|
+
export function requestHeaders(request: Request): Headers {
|
|
43
|
+
return RAW_HEADERS.get(request) ?? request.headers;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Shadow `request.headers` with an own, non-enumerable getter that calls
|
|
48
|
+
* `beforeRead` and returns the request's own Headers (stable per request, so
|
|
49
|
+
* a header set through it stays visible to every reader). Idempotent. Leaves
|
|
50
|
+
* the request as is when `headers` is a non-configurable own property (a
|
|
51
|
+
* runtime that defines it per instance). Three inherited members are
|
|
52
|
+
* shadowed with it:
|
|
53
|
+
*
|
|
54
|
+
* - `clone()` returns a clone shadowed the same way. A plain clone was a
|
|
55
|
+
* Request without the getter, so `ctx.request.clone().headers` read the
|
|
56
|
+
* visitor's headers past the guard. (`new Request(ctx.request)` still
|
|
57
|
+
* returns a plain Request: the constructor cannot be intercepted.)
|
|
58
|
+
* - `_request`, on a lazy srvx NodeRequest (what @vitejs/plugin-rsc's Node
|
|
59
|
+
* dev and preview servers and the vercel-output launcher hand the
|
|
60
|
+
* router), runs unguarded: it builds the platform Request on first use
|
|
61
|
+
* from `this.headers`, for clone() and the body readers. Through the
|
|
62
|
+
* getter, a clone() inside a cache() boundary threw as a user read. It
|
|
63
|
+
* stays lazy, so a request whose body srvx reads from its buffered
|
|
64
|
+
* `rawBody` never builds one.
|
|
65
|
+
* - Node's util.inspect hook runs unguarded: undici's inspect reads
|
|
66
|
+
* `this.headers`, so `console.log(ctx.request)` inside a "use cache" body
|
|
67
|
+
* threw. A log is not a read into a shared entry.
|
|
68
|
+
*/
|
|
69
|
+
export function shadowRequestHeaders(
|
|
70
|
+
request: Request,
|
|
71
|
+
beforeRead: () => void,
|
|
72
|
+
): Request {
|
|
73
|
+
if (RAW_HEADERS.has(request)) return request;
|
|
74
|
+
const own = Object.getOwnPropertyDescriptor(request, "headers");
|
|
75
|
+
if (own && !own.configurable) return request;
|
|
76
|
+
const lazy = inheritedGetter(request, "_request");
|
|
77
|
+
if (lazy) {
|
|
78
|
+
Object.defineProperty(request, "_request", {
|
|
79
|
+
get(this: Request): unknown {
|
|
80
|
+
return runUnguarded(() => lazy.call(this));
|
|
81
|
+
},
|
|
82
|
+
configurable: true,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
const raw = request.headers;
|
|
86
|
+
RAW_HEADERS.set(request, raw);
|
|
87
|
+
Object.defineProperty(request, "headers", {
|
|
88
|
+
get(): Headers {
|
|
89
|
+
if (!unguarded) beforeRead();
|
|
90
|
+
return raw;
|
|
91
|
+
},
|
|
92
|
+
enumerable: false,
|
|
93
|
+
configurable: true,
|
|
94
|
+
});
|
|
95
|
+
const clone = request.clone;
|
|
96
|
+
defineMethod(request, "clone", function (this: Request): Request {
|
|
97
|
+
return shadowRequestHeaders(clone.call(this), beforeRead);
|
|
98
|
+
});
|
|
99
|
+
const inspect = (request as unknown as Record<symbol, unknown>)[INSPECT];
|
|
100
|
+
if (typeof inspect === "function") {
|
|
101
|
+
defineMethod(
|
|
102
|
+
request,
|
|
103
|
+
INSPECT,
|
|
104
|
+
function (this: Request, ...args: unknown[]) {
|
|
105
|
+
return runUnguarded(() => inspect.apply(this, args));
|
|
106
|
+
},
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
return request;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function defineMethod(
|
|
113
|
+
target: object,
|
|
114
|
+
key: PropertyKey,
|
|
115
|
+
value: (...args: never[]) => unknown,
|
|
116
|
+
): void {
|
|
117
|
+
Object.defineProperty(target, key, {
|
|
118
|
+
value,
|
|
119
|
+
configurable: true,
|
|
120
|
+
writable: true,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** The getter `key` resolves to on `target`'s prototype chain, if any. */
|
|
125
|
+
function inheritedGetter(
|
|
126
|
+
target: object,
|
|
127
|
+
key: string,
|
|
128
|
+
): (() => unknown) | undefined {
|
|
129
|
+
for (let o = Object.getPrototypeOf(target); o; o = Object.getPrototypeOf(o)) {
|
|
130
|
+
const found = Object.getOwnPropertyDescriptor(o, key);
|
|
131
|
+
if (found) return found.get;
|
|
132
|
+
}
|
|
133
|
+
return undefined;
|
|
134
|
+
}
|
package/src/server.ts
CHANGED
|
@@ -48,6 +48,10 @@ export {
|
|
|
48
48
|
// Component utilities (used internally for server/client boundary checks)
|
|
49
49
|
export { isClientComponent, assertClientComponent } from "./component-utils.js";
|
|
50
50
|
|
|
51
|
+
// transition({ when }) strict validation: the generated routes-manifest module
|
|
52
|
+
// enables it in the app's RSC realm (see transition-when-ref.ts).
|
|
53
|
+
export { enableTransitionWhenValidation } from "./transition-when-ref.js";
|
|
54
|
+
|
|
51
55
|
// Client URL projection registry (Vite discovery/bootstrap bridge)
|
|
52
56
|
export {
|
|
53
57
|
clearClientUrlProjections,
|
package/src/testing/dispatch.ts
CHANGED
|
@@ -627,9 +627,9 @@ export async function dispatch<TEnv = any>(
|
|
|
627
627
|
const manifestEntry = preview?.manifestEntry;
|
|
628
628
|
if (manifestEntry) {
|
|
629
629
|
// Lazy so the testing barrel's eager graph stays plugin-rsc-free (see the
|
|
630
|
-
// import note above): the leaf takes createCacheScope
|
|
631
|
-
//
|
|
632
|
-
//
|
|
630
|
+
// import note above): the leaf takes createCacheScope as an INJECTED
|
|
631
|
+
// dep so it never imports plugin-rsc; we hand it the lazily imported
|
|
632
|
+
// builder here, only once a response route actually matched.
|
|
633
633
|
const cacheScopeMod = await import("../cache/cache-scope.js");
|
|
634
634
|
const { serveResponseRouteWithCache } =
|
|
635
635
|
await import("../rsc/response-cache-serve.js");
|
|
@@ -645,10 +645,7 @@ export async function dispatch<TEnv = any>(
|
|
|
645
645
|
responseType: responseType as string,
|
|
646
646
|
url,
|
|
647
647
|
executeHandler,
|
|
648
|
-
deps: {
|
|
649
|
-
createCacheScope: cacheScopeMod.createCacheScope,
|
|
650
|
-
resolveCacheTags: cacheScopeMod.resolveCacheTags,
|
|
651
|
-
},
|
|
648
|
+
deps: { createCacheScope: cacheScopeMod.createCacheScope },
|
|
652
649
|
});
|
|
653
650
|
if (cached !== undefined) return cached;
|
|
654
651
|
}
|
package/src/testing/e2e/index.ts
CHANGED
package/src/testing/index.ts
CHANGED
|
@@ -49,6 +49,8 @@ export type {
|
|
|
49
49
|
|
|
50
50
|
export { runTransitionWhen } from "./run-transition-when.js";
|
|
51
51
|
export type {
|
|
52
|
+
RunTransitionWhenAction,
|
|
53
|
+
RunTransitionWhenLocation,
|
|
52
54
|
RunTransitionWhenOptions,
|
|
53
55
|
RunTransitionWhenResult,
|
|
54
56
|
} from "./run-transition-when.js";
|
|
@@ -91,12 +93,15 @@ export {
|
|
|
91
93
|
export type {
|
|
92
94
|
PprReplayBypassReason,
|
|
93
95
|
PprReplayStatus,
|
|
96
|
+
ShellCachePartition,
|
|
94
97
|
ShellStatus,
|
|
95
98
|
ShellStatusTarget,
|
|
96
99
|
} from "./shell-status.js";
|
|
97
100
|
|
|
98
101
|
export { collectHandle } from "./collect-handle.js";
|
|
99
102
|
|
|
103
|
+
export { withLocationStateKey } from "./location-state-key.js";
|
|
104
|
+
|
|
100
105
|
export {
|
|
101
106
|
diffGeneratedRoutes,
|
|
102
107
|
assertGeneratedRoutesMatch,
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { resolveActionRefId } from "../../router/is-action.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The id a testing primitive's `action` option names: a single imported
|
|
5
|
+
* server action (its `$id ?? $$id`, as the action boundary derives it) or a
|
|
6
|
+
* raw actionId string. A function without either id throws, naming the
|
|
7
|
+
* primitive.
|
|
8
|
+
*/
|
|
9
|
+
export function resolveTestActionId(
|
|
10
|
+
action: ((...args: never[]) => unknown) | string | undefined,
|
|
11
|
+
primitive: string,
|
|
12
|
+
): string | undefined {
|
|
13
|
+
if (action === undefined) return undefined;
|
|
14
|
+
if (typeof action === "string") return action;
|
|
15
|
+
const id = resolveActionRefId(action);
|
|
16
|
+
if (id === undefined) {
|
|
17
|
+
throw new Error(
|
|
18
|
+
`${primitive}: \`action\` must be a single imported server action ` +
|
|
19
|
+
"(carrying its build-injected id) or an actionId string. The passed " +
|
|
20
|
+
"function has no $id/$$id — outside a built app, pass the id string " +
|
|
21
|
+
'your predicate should match (e.g. "src/actions/cart.ts#addToCart").',
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
return id;
|
|
25
|
+
}
|
|
@@ -250,7 +250,7 @@ export function snapshotRunEffects<TEnv>(ctx: RequestContext<TEnv>): {
|
|
|
250
250
|
locationState: Record<string, unknown>;
|
|
251
251
|
} {
|
|
252
252
|
return {
|
|
253
|
-
cookies:
|
|
253
|
+
cookies: ctx._readCookies(),
|
|
254
254
|
locationState: resolveLocationStateEntries(ctx._locationState ?? []),
|
|
255
255
|
};
|
|
256
256
|
}
|