@rangojs/router 0.18.0 → 0.19.1
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 +37 -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 +48 -5
- 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 +126 -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 +962 -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 +67 -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 +54 -1
- package/src/cache/cache-key-utils.ts +56 -16
- package/src/cache/cache-policy.ts +9 -4
- package/src/cache/cache-runtime.ts +202 -144
- package/src/cache/cache-scope.ts +304 -112
- package/src/cache/cache-tag.ts +209 -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 +42 -5
- 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 +152 -26
- 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 +263 -124
- package/src/server/cookie-store.ts +153 -127
- package/src/server/handle-store.ts +245 -27
- package/src/server/request-context.ts +134 -87
- 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 {
|
|
@@ -64,8 +64,16 @@ import {
|
|
|
64
64
|
assertCachedHeaderWriteAllowed,
|
|
65
65
|
assertNonCacheableReadAllowed,
|
|
66
66
|
clearPprHeaderScope,
|
|
67
|
+
runInsideLoaderRun,
|
|
68
|
+
type LoaderRunIdentity,
|
|
67
69
|
} from "./context.js";
|
|
68
|
-
import {
|
|
70
|
+
import { readStartedLoaderValue, trackLoaderRun } from "../cache/cache-tag.js";
|
|
71
|
+
import {
|
|
72
|
+
guardRawCookieRead,
|
|
73
|
+
guardRequestHeaders,
|
|
74
|
+
readGuardedTheme,
|
|
75
|
+
} from "./cookie-store.js";
|
|
76
|
+
import { requestHeaders } from "./request-headers.js";
|
|
69
77
|
import {
|
|
70
78
|
createReverseFunction,
|
|
71
79
|
stripInternalParams,
|
|
@@ -76,6 +84,10 @@ import {
|
|
|
76
84
|
getSearchSchema,
|
|
77
85
|
} from "../route-map-builder.js";
|
|
78
86
|
import { parseSearchParams } from "../search-params.js";
|
|
87
|
+
import {
|
|
88
|
+
executionStart,
|
|
89
|
+
type ExecutionStart,
|
|
90
|
+
} from "../cache/invalidation-order.js";
|
|
79
91
|
import { invariant } from "../errors.js";
|
|
80
92
|
import { isAutoGeneratedRouteName } from "../route-name.js";
|
|
81
93
|
|
|
@@ -146,10 +158,24 @@ export interface RequestContext<
|
|
|
146
158
|
/** @internal Request-local PPR opt-out marker set by ctx.dynamic(). */
|
|
147
159
|
_dynamic?: boolean;
|
|
148
160
|
|
|
149
|
-
/**
|
|
161
|
+
/**
|
|
162
|
+
* @internal Get a cookie value (effective: request + response mutations).
|
|
163
|
+
* Use cookies().get() instead. Guarded like cookies() (#976).
|
|
164
|
+
*/
|
|
150
165
|
cookie(name: string): string | undefined;
|
|
151
|
-
/**
|
|
166
|
+
/**
|
|
167
|
+
* @internal Get all cookies (effective merged view). Use
|
|
168
|
+
* cookies().getAll() instead. Guarded like cookies() (#976).
|
|
169
|
+
*/
|
|
152
170
|
cookies(): Record<string, string>;
|
|
171
|
+
/**
|
|
172
|
+
* @internal `cookie()` without the identity guard: the read behind the
|
|
173
|
+
* cookies() store, which guards at its own call and records per read
|
|
174
|
+
* method. Non-enumerable, like `_readTheme`.
|
|
175
|
+
*/
|
|
176
|
+
_readCookie(name: string): string | undefined;
|
|
177
|
+
/** @internal `cookies()` without the identity guard (see `_readCookie`). */
|
|
178
|
+
_readCookies(): Record<string, string>;
|
|
153
179
|
/** @internal Set a cookie on the response. Use cookies().set() instead. */
|
|
154
180
|
setCookie(name: string, value: string, options?: CookieOptions): void;
|
|
155
181
|
/** @internal Delete a cookie. Use cookies().delete() instead. */
|
|
@@ -182,19 +208,18 @@ export interface RequestContext<
|
|
|
182
208
|
/**
|
|
183
209
|
* @internal The context createRequestContext() returned. A context derived
|
|
184
210
|
* 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
|
-
*
|
|
211
|
+
* revalidation render) inherits it, so per-request invalidation state (the
|
|
212
|
+
* stores' masks) is keyed by the request, not by whichever derived object
|
|
213
|
+
* called or read (#973).
|
|
188
214
|
*/
|
|
189
215
|
_requestRoot?: RequestContext<any, any>;
|
|
190
216
|
/**
|
|
191
|
-
* @internal
|
|
192
|
-
*
|
|
193
|
-
*
|
|
194
|
-
*
|
|
195
|
-
* invalidated is neither joined nor written (cache-runtime.ts, #973).
|
|
217
|
+
* @internal Where the request started in the isolate's invalidation order
|
|
218
|
+
* (cache/invalidation-order.ts), taken when createRequestContext() ran. The
|
|
219
|
+
* document cache gates its write on it (#977): a middleware ahead of the
|
|
220
|
+
* document cache can read tagged data the document bakes.
|
|
196
221
|
*/
|
|
197
|
-
|
|
222
|
+
_requestStart?: ExecutionStart;
|
|
198
223
|
/**
|
|
199
224
|
* @internal The handler has handed the response to the host (rsc/handler.ts
|
|
200
225
|
* `rango.response`): header and cookie writes after this are lost, as from
|
|
@@ -242,16 +267,13 @@ export interface RequestContext<
|
|
|
242
267
|
readonly _handleStore: HandleStore;
|
|
243
268
|
|
|
244
269
|
/**
|
|
245
|
-
* @internal transition({ when })
|
|
246
|
-
* keyed by segment id.
|
|
247
|
-
*
|
|
248
|
-
*
|
|
249
|
-
*
|
|
270
|
+
* @internal transition({ when }) client references of the matched entry
|
|
271
|
+
* chain, keyed by segment id. Recorded at match time from the static route
|
|
272
|
+
* definition (router/transition-when.ts) and attached to the payload's
|
|
273
|
+
* segments right before Flight (rsc/attach-transition-when.ts). The server
|
|
274
|
+
* never calls them, and no cache or snapshot stores them.
|
|
250
275
|
*/
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
/** @internal PPR transition decisions evaluated before cache lookup/handlers. */
|
|
254
|
-
_pprTransitionDecisions?: Map<string, boolean>;
|
|
276
|
+
_transitionWhenRefs?: ReadonlyMap<string, TransitionWhenRecord>;
|
|
255
277
|
|
|
256
278
|
/**
|
|
257
279
|
* @internal Post-match serve-source truth for the PPR replay reporter.
|
|
@@ -279,8 +301,8 @@ export interface RequestContext<
|
|
|
279
301
|
* capture task's derived request context (built by shell-capture.ts). This is
|
|
280
302
|
* the switch every capture-specific behavior reads: loader masking
|
|
281
303
|
* (loader-mask.ts isShellCaptureActive / fresh.ts emitStreaming) and the
|
|
282
|
-
*
|
|
283
|
-
*
|
|
304
|
+
* identity-read capture guard (server/context.ts guardIdentityRead). The
|
|
305
|
+
* foreground render never sets it, so the
|
|
284
306
|
* served response is byte-identical to axis 1. The capture descriptor itself
|
|
285
307
|
* (key/ttl/swr/tags/store) is NOT threaded through the request context — the
|
|
286
308
|
* integrated PPR serve path (rsc/shell-serve.ts + rsc-rendering.ts) builds it
|
|
@@ -308,6 +330,16 @@ export interface RequestContext<
|
|
|
308
330
|
*/
|
|
309
331
|
_shellCaptureUnrecordedLoaderPush?: true;
|
|
310
332
|
|
|
333
|
+
/**
|
|
334
|
+
* @internal The lane of each loader the capture's matched route registers
|
|
335
|
+
* (loader-cache.ts routeLoaderLanes), set by createMatchContextForFull
|
|
336
|
+
* before any loader runs. The capture's push funnel credits a push made
|
|
337
|
+
* under a live-lane loader's body, at any depth, to that loader
|
|
338
|
+
* (shell-capture.ts deriveShellCaptureContext). Own property of the
|
|
339
|
+
* capture's derived context only.
|
|
340
|
+
*/
|
|
341
|
+
_shellCaptureLoaderLanes?: ReadonlyMap<string, "live" | "bake">;
|
|
342
|
+
|
|
311
343
|
/**
|
|
312
344
|
* @internal Record keys resolved for this request (CacheScope
|
|
313
345
|
* resolveKeyFrom): by the scope's `key()` (one result) or its store's
|
|
@@ -414,7 +446,9 @@ export interface RequestContext<
|
|
|
414
446
|
* scope replaces any route-derived cache() scope for the tail's match
|
|
415
447
|
* (resolveShellImplicitCacheScope), and a lookup that does not hit throws
|
|
416
448
|
* ShellRecordUnavailableError instead of resolving segments
|
|
417
|
-
* (withCacheLookup): a HIT never runs a handler.
|
|
449
|
+
* (withCacheLookup): a HIT never runs a handler. The record's
|
|
450
|
+
* loader-owned handle pushes restore as authoritative
|
|
451
|
+
* (HandleStore.pushRestored): the prelude rendered them.
|
|
418
452
|
*/
|
|
419
453
|
docTail?: true;
|
|
420
454
|
/**
|
|
@@ -468,8 +502,9 @@ export interface RequestContext<
|
|
|
468
502
|
|
|
469
503
|
/**
|
|
470
504
|
* @internal Set by the capture guard (server/context.ts
|
|
471
|
-
*
|
|
472
|
-
* ("cookies()", "headers()",
|
|
505
|
+
* guardIdentityRead) when it throws DURING a capture render: the read
|
|
506
|
+
* ("cookies()", "headers()", `ctx.get() for a non-cacheable variable`,
|
|
507
|
+
* "ctx.theme") and the fix the
|
|
473
508
|
* refusal warning gives for it. Load-bearing for the
|
|
474
509
|
* bake lane: a guard throw inside an executing loader is swallowed by
|
|
475
510
|
* wrapLoaderPromise into per-loader error UI, which would otherwise bake
|
|
@@ -611,12 +646,27 @@ export interface RequestContext<
|
|
|
611
646
|
/**
|
|
612
647
|
* @internal The theme cookie (or the default) read with no identity guard.
|
|
613
648
|
* The guarded reads (`theme` here, the handler and middleware `ctx.theme`)
|
|
614
|
-
* call it after
|
|
615
|
-
* through payloadInitialTheme (rsc/full-payload.ts)
|
|
649
|
+
* call it after guardIdentityRead (readGuardedTheme); the router's own payload read goes
|
|
650
|
+
* through payloadInitialTheme (rsc/full-payload.ts), except the unmatched
|
|
651
|
+
* route's 404 (rsc/handler.ts), which is never stored. Undefined without
|
|
616
652
|
* theme config.
|
|
617
653
|
*/
|
|
618
654
|
_readTheme(): Theme | undefined;
|
|
619
655
|
|
|
656
|
+
/**
|
|
657
|
+
* @internal Set by the document-cache middleware (cache/document-cache.ts)
|
|
658
|
+
* before it runs a render whose response it may store: the MISS and the
|
|
659
|
+
* stale refresh. payloadInitialTheme reads it (#978).
|
|
660
|
+
*/
|
|
661
|
+
_documentCacheRender?: boolean;
|
|
662
|
+
|
|
663
|
+
/**
|
|
664
|
+
* @internal Set by payloadInitialTheme when the document payload carries
|
|
665
|
+
* the visitor's theme and it differs from the no-cookie default. The
|
|
666
|
+
* document cache does not store that response (#978).
|
|
667
|
+
*/
|
|
668
|
+
_payloadVisitorTheme?: boolean;
|
|
669
|
+
|
|
620
670
|
/**
|
|
621
671
|
* Attach location state entries to the current response.
|
|
622
672
|
*
|
|
@@ -660,20 +710,6 @@ export interface RequestContext<
|
|
|
660
710
|
/** @internal Previous route key (from the navigation source), used for revalidation */
|
|
661
711
|
_prevRouteKey?: string;
|
|
662
712
|
|
|
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
713
|
/**
|
|
678
714
|
* @internal True while the post-action revalidation render is running (set by
|
|
679
715
|
* revalidateAfterAction). The "use cache" runtime reads this to prefer
|
|
@@ -872,11 +908,12 @@ export type PublicRequestContext<
|
|
|
872
908
|
RequestContext<TEnv, TParams>,
|
|
873
909
|
| "cookie"
|
|
874
910
|
| "cookies"
|
|
911
|
+
| "_readCookie"
|
|
912
|
+
| "_readCookies"
|
|
875
913
|
| "setCookie"
|
|
876
914
|
| "deleteCookie"
|
|
877
915
|
| "_handleStore"
|
|
878
|
-
| "
|
|
879
|
-
| "_pprTransitionDecisions"
|
|
916
|
+
| "_transitionWhenRefs"
|
|
880
917
|
| "_pprReplayPostMatchReason"
|
|
881
918
|
| "_cacheStore"
|
|
882
919
|
| "_searchParamsFilter"
|
|
@@ -885,6 +922,7 @@ export type PublicRequestContext<
|
|
|
885
922
|
| "_shellLoaderSeed"
|
|
886
923
|
| "_shellCaptureLoaderRecords"
|
|
887
924
|
| "_shellCaptureUnrecordedLoaderPush"
|
|
925
|
+
| "_shellCaptureLoaderLanes"
|
|
888
926
|
| "_resolvedCacheKeys"
|
|
889
927
|
| "_shellKey"
|
|
890
928
|
| "_shellForcedMiss"
|
|
@@ -901,15 +939,11 @@ export type PublicRequestContext<
|
|
|
901
939
|
| "_pendingBackgroundTasks"
|
|
902
940
|
| "_themeConfig"
|
|
903
941
|
| "_readTheme"
|
|
942
|
+
| "_documentCacheRender"
|
|
943
|
+
| "_payloadVisitorTheme"
|
|
904
944
|
| "_locationState"
|
|
905
945
|
| "_routeName"
|
|
906
946
|
| "_prevRouteKey"
|
|
907
|
-
| "_gateCurrentUrl"
|
|
908
|
-
| "_gateCurrentParams"
|
|
909
|
-
| "_gateActionId"
|
|
910
|
-
| "_gateActionUrl"
|
|
911
|
-
| "_gateActionResult"
|
|
912
|
-
| "_gateFormData"
|
|
913
947
|
| "_inActionRevalidation"
|
|
914
948
|
| "_reportedErrors"
|
|
915
949
|
| "_renderErrors"
|
|
@@ -938,7 +972,7 @@ export type PublicRequestContext<
|
|
|
938
972
|
| "_freshReads"
|
|
939
973
|
| "_setFreshReadsCookie"
|
|
940
974
|
| "_requestRoot"
|
|
941
|
-
| "
|
|
975
|
+
| "_requestStart"
|
|
942
976
|
| "_responseSent"
|
|
943
977
|
| "_variables"
|
|
944
978
|
| "_classifiedRoute"
|
|
@@ -1042,17 +1076,10 @@ export function setRequestContextParams(
|
|
|
1042
1076
|
*/
|
|
1043
1077
|
export function setRequestContextPrevRouteKey(
|
|
1044
1078
|
prevRouteKey: string | undefined,
|
|
1045
|
-
currentUrl?: URL,
|
|
1046
|
-
currentParams?: Record<string, string>,
|
|
1047
1079
|
): void {
|
|
1048
1080
|
const ctx = requestContextStorage.getStore();
|
|
1049
1081
|
if (!ctx) return;
|
|
1050
1082
|
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
1083
|
}
|
|
1057
1084
|
|
|
1058
1085
|
/**
|
|
@@ -1134,7 +1161,10 @@ export function createRequestContext<TEnv>(
|
|
|
1134
1161
|
stateCookieName,
|
|
1135
1162
|
version: stateVersion,
|
|
1136
1163
|
} = options;
|
|
1137
|
-
|
|
1164
|
+
// ctx.request.headers is guarded like headers() (#976); every router read
|
|
1165
|
+
// of this request's headers goes through requestHeaders().
|
|
1166
|
+
guardRequestHeaders(request);
|
|
1167
|
+
const cookieHeader = requestHeaders(request).get("Cookie");
|
|
1138
1168
|
let rangoStateRotated = false;
|
|
1139
1169
|
const freshCookie = stateCookieName
|
|
1140
1170
|
? freshReadsCookieName(stateCookieName)
|
|
@@ -1233,6 +1263,26 @@ export function createRequestContext<TEnv>(
|
|
|
1233
1263
|
return getParsedCookies()[name];
|
|
1234
1264
|
};
|
|
1235
1265
|
|
|
1266
|
+
// The effective jar: request cookies minus deletions plus same-request writes.
|
|
1267
|
+
const effectiveCookies = (): Record<string, string> => {
|
|
1268
|
+
const parsed = getParsedCookies();
|
|
1269
|
+
const mutations = getResponseCookies();
|
|
1270
|
+
if (mutations.size === 0) return { ...parsed };
|
|
1271
|
+
// Build result without delete (avoids V8 dictionary-mode de-opt)
|
|
1272
|
+
const deleted = new Set<string>();
|
|
1273
|
+
for (const [k, v] of mutations) {
|
|
1274
|
+
if (v === null) deleted.add(k);
|
|
1275
|
+
}
|
|
1276
|
+
const result: Record<string, string> = {};
|
|
1277
|
+
for (const key of Object.keys(parsed)) {
|
|
1278
|
+
if (!deleted.has(key)) result[key] = parsed[key];
|
|
1279
|
+
}
|
|
1280
|
+
for (const [k, v] of mutations) {
|
|
1281
|
+
if (v !== null) result[k] = v;
|
|
1282
|
+
}
|
|
1283
|
+
return result;
|
|
1284
|
+
};
|
|
1285
|
+
|
|
1236
1286
|
const getTheme = (): Theme | undefined => {
|
|
1237
1287
|
if (!themeConfig) return undefined;
|
|
1238
1288
|
|
|
@@ -1312,29 +1362,23 @@ export function createRequestContext<TEnv>(
|
|
|
1312
1362
|
);
|
|
1313
1363
|
},
|
|
1314
1364
|
|
|
1365
|
+
// The visitor's cookies, guarded like cookies() (#976): before, a plain
|
|
1366
|
+
// read that a ppr shell, a cache() entry or a "use cache" entry stored
|
|
1367
|
+
// for every visitor.
|
|
1315
1368
|
cookie(name: string): string | undefined {
|
|
1369
|
+
guardRawCookieRead(ctx, "getRequestContext().cookie()");
|
|
1316
1370
|
return effectiveCookie(name);
|
|
1317
1371
|
},
|
|
1318
1372
|
|
|
1319
1373
|
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;
|
|
1374
|
+
guardRawCookieRead(ctx, "getRequestContext().cookies()");
|
|
1375
|
+
return effectiveCookies();
|
|
1336
1376
|
},
|
|
1337
1377
|
|
|
1378
|
+
// Non-enumerable, defined after this literal.
|
|
1379
|
+
_readCookie: effectiveCookie,
|
|
1380
|
+
_readCookies: effectiveCookies,
|
|
1381
|
+
|
|
1338
1382
|
setCookie(name: string, value: string, options?: CookieOptions): void {
|
|
1339
1383
|
assertNotInsideCacheExec(ctx, "setCookie");
|
|
1340
1384
|
stubResponse.headers.append(
|
|
@@ -1383,7 +1427,7 @@ export function createRequestContext<TEnv>(
|
|
|
1383
1427
|
// decodeStateValue decodes exactly once) AND is the same parser the client
|
|
1384
1428
|
// mirror uses, so both seats read the same jar entry.
|
|
1385
1429
|
const prevRaw =
|
|
1386
|
-
(request.
|
|
1430
|
+
(requestHeaders(request).get("x-rango-state") || null) ??
|
|
1387
1431
|
getRawCookieValue(cookieHeader, stateCookieName);
|
|
1388
1432
|
const value = mintStateValue(stateVersion ?? "0", prevRaw);
|
|
1389
1433
|
// rawStubHeaders: guard-exempt internal writer — invalidateClientCache()
|
|
@@ -1460,7 +1504,6 @@ export function createRequestContext<TEnv>(
|
|
|
1460
1504
|
method: request.method,
|
|
1461
1505
|
|
|
1462
1506
|
_handleStore: handleStore,
|
|
1463
|
-
_transitionWhen: [],
|
|
1464
1507
|
_cacheStore: cacheStore,
|
|
1465
1508
|
_searchParamsFilter: searchParamsFilter,
|
|
1466
1509
|
_explicitTaggedStores: explicitTaggedStores,
|
|
@@ -1560,20 +1603,18 @@ export function createRequestContext<TEnv>(
|
|
|
1560
1603
|
// the ambient context, as cookies() does, and falls back to the receiver.
|
|
1561
1604
|
// _readTheme is the unguarded read: non-enumerable too, so a spread copy of
|
|
1562
1605
|
// the context (the fetchable-loader ctx, loader-fetch.ts) cannot carry it.
|
|
1606
|
+
// The same holds for the unguarded cookie reads (#976).
|
|
1563
1607
|
Object.defineProperties(ctx, {
|
|
1564
1608
|
theme: {
|
|
1565
1609
|
get(this: RequestContext<TEnv>): Theme | undefined {
|
|
1566
|
-
|
|
1567
|
-
assertThemeReadAllowed(
|
|
1568
|
-
_getRequestContext() ?? this,
|
|
1569
|
-
"getRequestContext().theme",
|
|
1570
|
-
);
|
|
1571
|
-
return getTheme();
|
|
1610
|
+
return readGuardedTheme(this, "getRequestContext().theme");
|
|
1572
1611
|
},
|
|
1573
1612
|
enumerable: false,
|
|
1574
1613
|
configurable: true,
|
|
1575
1614
|
},
|
|
1576
1615
|
_readTheme: { enumerable: false },
|
|
1616
|
+
_readCookie: { enumerable: false },
|
|
1617
|
+
_readCookies: { enumerable: false },
|
|
1577
1618
|
});
|
|
1578
1619
|
|
|
1579
1620
|
(ctx as any)[NOCACHE_SYMBOL] = true;
|
|
@@ -1585,6 +1626,7 @@ export function createRequestContext<TEnv>(
|
|
|
1585
1626
|
configurable: true,
|
|
1586
1627
|
writable: true,
|
|
1587
1628
|
});
|
|
1629
|
+
ctx._requestStart = executionStart();
|
|
1588
1630
|
return ctx;
|
|
1589
1631
|
}
|
|
1590
1632
|
|
|
@@ -1820,8 +1862,11 @@ export function createUseFunction<TEnv>(
|
|
|
1820
1862
|
|
|
1821
1863
|
const loader = item as LoaderDefinition<any, any>;
|
|
1822
1864
|
|
|
1823
|
-
|
|
1824
|
-
|
|
1865
|
+
const memo = loaderPromises.get(loader.$$id);
|
|
1866
|
+
if (memo) {
|
|
1867
|
+
// Same refusal as the handler ctx.use memo hit (#1011): a "use cache"
|
|
1868
|
+
// body reading a value whose run read request identity.
|
|
1869
|
+
return readStartedLoaderValue(memo, loader.$$id);
|
|
1825
1870
|
}
|
|
1826
1871
|
|
|
1827
1872
|
let loaderFn = loader.fn;
|
|
@@ -1908,9 +1953,11 @@ export function createUseFunction<TEnv>(
|
|
|
1908
1953
|
// (observePhase), so a loader resolved via this base request-context ctx.use
|
|
1909
1954
|
// co-emits the "loader:<id>" perf metric AND the "rango.loader" span — no
|
|
1910
1955
|
// drift between the two ctx.use implementations.
|
|
1956
|
+
const run: LoaderRunIdentity = { loaderId: loader.$$id };
|
|
1911
1957
|
const promise = observePhase(PHASES.loader(loader.$$id), () =>
|
|
1912
|
-
Promise.resolve(loaderFn(loaderCtx)),
|
|
1958
|
+
Promise.resolve(runInsideLoaderRun(run, () => loaderFn(loaderCtx))),
|
|
1913
1959
|
);
|
|
1960
|
+
trackLoaderRun(promise, run);
|
|
1914
1961
|
|
|
1915
1962
|
loaderPromises.set(loader.$$id, promise);
|
|
1916
1963
|
|
|
@@ -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
|
}
|