@rangojs/router 0.30.1 → 0.32.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.
Files changed (65) hide show
  1. package/dist/bin/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  2. package/dist/bin/rango.js +7891 -197
  3. package/dist/types/browser/history-state.d.ts +13 -2
  4. package/dist/types/browser/react/Link.d.ts +10 -13
  5. package/dist/types/browser/react/location-state-shared.d.ts +2 -1
  6. package/dist/types/build/generate-route-types.d.ts +1 -1
  7. package/dist/types/build/route-types/include-resolution.d.ts +1 -1
  8. package/dist/types/build/route-types/router-processing.d.ts +3 -7
  9. package/dist/types/cache/document-cache-policy.d.ts +1 -1
  10. package/dist/types/route-definition/redirect.d.ts +5 -0
  11. package/dist/types/router/middleware-types.d.ts +6 -0
  12. package/dist/types/rsc/helpers.d.ts +5 -3
  13. package/dist/types/rsc/request-stages.d.ts +2 -0
  14. package/dist/types/server/document-location-state.d.ts +56 -0
  15. package/dist/types/server/request-context.d.ts +23 -6
  16. package/dist/types/testing/render-route.d.ts +17 -0
  17. package/dist/types/testing/serve-shell-request.d.ts +9 -0
  18. package/dist/types/types/handler-context.d.ts +18 -3
  19. package/dist/types/types/segments.d.ts +6 -0
  20. package/dist/types/vite/temp-rsc-server.d.ts +8 -4
  21. package/dist/types/vite/utils/forward-user-plugins.d.ts +33 -1
  22. package/dist/vite/index.js +121 -38
  23. package/package.json +2 -2
  24. package/skills/comparison/references/framework-comparison.md +3 -2
  25. package/skills/document-cache/SKILL.md +5 -0
  26. package/skills/hooks/state.md +110 -6
  27. package/skills/intercept/SKILL.md +2 -1
  28. package/skills/ppr/SKILL.md +12 -0
  29. package/skills/rango/SKILL.md +5 -5
  30. package/skills/server-actions/SKILL.md +8 -0
  31. package/skills/testing/client-components.md +44 -19
  32. package/skills/testing/serve-shell-request.md +38 -13
  33. package/skills/typesafety/generated-files-and-cli.md +2 -1
  34. package/skills/ux-architecture/SKILL.md +6 -3
  35. package/src/bin/rango.ts +13 -8
  36. package/src/browser/history-state.ts +24 -2
  37. package/src/browser/react/Link.tsx +10 -13
  38. package/src/browser/react/location-state-shared.ts +8 -1
  39. package/src/browser/rsc-router.tsx +5 -2
  40. package/src/build/generate-route-types.ts +0 -1
  41. package/src/build/route-types/include-resolution.ts +2 -1
  42. package/src/build/route-types/router-processing.ts +213 -49
  43. package/src/build/runtime-discovery.ts +128 -41
  44. package/src/cache/document-cache-policy.ts +10 -1
  45. package/src/cache/document-cache.ts +7 -0
  46. package/src/client-urls/server-projection.ts +3 -1
  47. package/src/route-definition/redirect.ts +5 -0
  48. package/src/router/match-middleware/cache-lookup.ts +23 -6
  49. package/src/router/match-result.ts +9 -1
  50. package/src/router/middleware-types.ts +6 -0
  51. package/src/router/revalidation.ts +23 -3
  52. package/src/rsc/helpers.ts +13 -4
  53. package/src/rsc/progressive-enhancement.ts +5 -0
  54. package/src/rsc/request-stages.ts +28 -1
  55. package/src/rsc/rsc-rendering.ts +39 -9
  56. package/src/server/document-location-state.ts +313 -0
  57. package/src/server/request-context.ts +24 -5
  58. package/src/testing/dispatch.ts +3 -0
  59. package/src/testing/render-route.tsx +40 -6
  60. package/src/testing/run-revalidate.ts +11 -1
  61. package/src/testing/serve-shell-request.ts +16 -0
  62. package/src/types/handler-context.ts +18 -3
  63. package/src/types/segments.ts +6 -0
  64. package/src/vite/temp-rsc-server.ts +9 -4
  65. package/src/vite/utils/forward-user-plugins.ts +15 -5
@@ -35,8 +35,9 @@ export declare function stripShellMissMarker(): void;
35
35
  * the server rendered this document without them. One pass before hydration
36
36
  * (initBrowserApp; renderRoute's `hydrate` mode), so no reader needs to know
37
37
  * the page began as a document load, and a slot nobody reads is removed too.
38
- * No server-set state exists yet to lose: a document response carries none
39
- * (rsc-rendering.ts attaches it to partial payloads only).
38
+ * Server-set state is not lost to it: the state a document response carries
39
+ * is merged after this pass (applyDocumentLocationState), so a `~r` slot the
40
+ * server set for this load survives this load and no later one.
40
41
  *
41
42
  * A slot is recognised by its key alone (the `~r` suffix): at start-up the
42
43
  * module that defines it may not be loaded.
@@ -46,6 +47,16 @@ export declare function stripShellMissMarker(): void;
46
47
  * is a full page for later Back/Forward.
47
48
  */
48
49
  export declare function clearLocationStateOnDocumentLoad(): void;
50
+ /**
51
+ * Merge the location state a document response carried (its bootstrap
52
+ * payload's `metadata.locationState`, server/document-location-state.ts)
53
+ * into the entry the document loaded on. Start-up runs it after
54
+ * clearLocationStateOnDocumentLoad and the version is set, before the entry
55
+ * is committed for readers (initBrowserApp; renderRoute's `hydrate` mode),
56
+ * so readers get it once they have hydrated, as stored state. Only typed
57
+ * slots are taken.
58
+ */
59
+ export declare function applyDocumentLocationState(locationState: Record<string, unknown> | undefined): void;
49
60
  /**
50
61
  * Merge server-set location state into the current history entry and return
51
62
  * the entry state written. Readers see it when the caller commits it
@@ -66,23 +66,20 @@ export interface LinkProps extends Omit<React.AnchorHTMLAttributes<HTMLAnchorEle
66
66
  * keyed on Rango state + target URL. This is correct for routes whose
67
67
  * response shape doesn't depend on where the user navigates from.
68
68
  *
69
- * Set `":source"` when this Link's response would legitimately differ
70
- * based on the source page — typically when the target route (or one
71
- * of its layouts) uses a custom `revalidate()` handler that reads
72
- * `currentUrl` / `currentParams`, and the wildcard entry would
73
- * therefore serve the wrong diff to a navigation from a different
74
- * source.
69
+ * Set `":source"` to scope every prefetch of this Link to the page that
70
+ * sent it, when its response would differ by source page in a way the
71
+ * server cannot see.
75
72
  *
76
- * A route an intercept targets is scoped to the source automatically
77
- * (server-side `x-rsc-prefetch-scope: source`), whether or not the
78
- * intercept applies from this page: the modal and the full page are
79
- * both source-specific. Every other route stays shared, so `":source"`
80
- * is only needed for custom revalidation logic.
73
+ * The server scopes a prefetch to its source automatically
74
+ * (`x-rsc-prefetch-scope: source`) when an intercept targets the route,
75
+ * whether or not it applies from this page (the modal and the full page
76
+ * are both source-specific), and when a `revalidate()` of the target
77
+ * read `currentParams`, `currentUrl` or `fromRouteName` and kept the
78
+ * segment: that diff is the source page's (#1331). Every other response
79
+ * stays shared.
81
80
  *
82
81
  * @example
83
82
  * ```tsx
84
- * // Route uses a `revalidate()` that branches on currentUrl — opt in
85
- * // so prefetches don't bleed across source pages.
86
83
  * <Link to="/dashboard" prefetch="hover" prefetchKey=":source" />
87
84
  * ```
88
85
  */
@@ -269,7 +269,8 @@ export declare function isLocationStateDefinition(value: unknown): value is Loca
269
269
  */
270
270
  export declare function isLocationStateEntry(value: unknown): value is LocationStateEntry;
271
271
  /**
272
- * Resolve state entries into a flat object for history.state
272
+ * Resolve state entries into a flat object for history.state. One slot per
273
+ * definition: a later entry of the same definition replaces an earlier one.
273
274
  */
274
275
  export declare function resolveLocationStateEntries(entries: LocationStateEntry[]): Record<string, unknown>;
275
276
  export {};
@@ -4,5 +4,5 @@ export { generatePerModuleTypesSource, generateRouteTypesSource, } from "./route
4
4
  export { DEFAULT_EXCLUDE_PATTERNS, type ScanFilter, createScanFilter, findTsFiles, isInSkippedScanDir, } from "./route-types/scan-filter.js";
5
5
  export { writePerModuleRouteTypes, writePerModuleRouteTypesForFile, } from "./route-types/per-module-writer.js";
6
6
  export { type UnresolvableReason, type UnresolvableInclude, extractIncludesWithDiagnostics, } from "./route-types/include-resolution.js";
7
- export { extractUrlsFromRouter, extractBasenameFromRouter, type UrlsExtractionResult, buildCombinedRouteMapForRouterFile, detectUnresolvableIncludes, detectUnresolvableIncludesForUrlsFile, findNestedRouterConflict, formatNestedRouterConflictError, findRouterFiles, findHostRouterFiles, writeCombinedRouteTypes, genFileTsPath, resolveSearchSchemas, } from "./route-types/router-processing.js";
7
+ export { extractUrlsFromRouter, type UrlsExtractionResult, buildCombinedRouteMapForRouterFile, detectUnresolvableIncludes, detectUnresolvableIncludesForUrlsFile, findNestedRouterConflict, formatNestedRouterConflictError, findRouterFiles, findHostRouterFiles, writeCombinedRouteTypes, genFileTsPath, resolveSearchSchemas, } from "./route-types/router-processing.js";
8
8
  export { findUrlsVariableNames } from "./route-types/per-module-writer.js";
@@ -1,5 +1,5 @@
1
1
  import ts from "typescript";
2
- export type UnresolvableReason = "factory-call" | "dynamic-expression" | "unresolvable-import" | "file-not-found";
2
+ export type UnresolvableReason = "factory-call" | "dynamic-expression" | "unresolvable-import" | "file-not-found" | "unresolvable-basename";
3
3
  export interface UnresolvableInclude {
4
4
  pathPrefix: string;
5
5
  namePrefix: string | null;
@@ -30,11 +30,6 @@ export type UrlsExtractionResult = {
30
30
  * Returns either a variable name or an inline code block.
31
31
  */
32
32
  export declare function extractUrlsFromRouter(code: string): UrlsExtractionResult | null;
33
- /**
34
- * Extract the `basename` string literal from createRouter({ basename: "..." }).
35
- * Returns the basename value or undefined if not present.
36
- */
37
- export declare function extractBasenameFromRouter(code: string): string | undefined;
38
33
  export declare function genFileTsPath(sourceFile: string): string;
39
34
  export declare function resolveSearchSchemas(publicRouteNames: string[], runtimeSchemas: Record<string, Record<string, string>> | undefined, sourceFile: string): Record<string, Record<string, string>> | undefined;
40
35
  /**
@@ -47,8 +42,9 @@ export declare function buildCombinedRouteMapForRouterFile(routerFilePath: strin
47
42
  };
48
43
  /**
49
44
  * Walk the full include tree starting from a router file and detect
50
- * all includes that the static parser cannot resolve.
51
- * Returns an array of diagnostics; empty means fully resolvable.
45
+ * all includes that the static parser cannot resolve, plus an explicit
46
+ * createRouter `basename` it cannot read (every pattern would lack the
47
+ * prefix). Returns an array of diagnostics; empty means fully resolvable.
52
48
  */
53
49
  export declare function detectUnresolvableIncludes(routerFilePath: string): UnresolvableInclude[];
54
50
  /**
@@ -12,7 +12,7 @@ export interface CacheDirectives {
12
12
  /**
13
13
  * Check if response should be cached based on Cache-Control headers
14
14
  */
15
- export declare function shouldCacheResponse(response: Response, requestCtx?: Pick<RequestContext, "_payloadVisitorTheme" | "_payloadDeferred" | "_payloadVisitorLocation">): CacheDirectives | null;
15
+ export declare function shouldCacheResponse(response: Response, requestCtx?: Pick<RequestContext, "_payloadVisitorTheme" | "_payloadDeferred" | "_payloadVisitorLocation" | "_documentLocationState">): CacheDirectives | null;
16
16
  /**
17
17
  * Whether this cache will store the response the current render produces, as
18
18
  * far as the render can tell: the middleware runs it (a MISS or a stale
@@ -9,6 +9,11 @@ import type { LocationStateEntry } from "../browser/react/location-state-shared.
9
9
  * Supports an optional state parameter to carry location state through the
10
10
  * redirect. On the target page, state can be read via useLocationState()
11
11
  * (use { flash: true } in createLocationState for read-once flash messages).
12
+ * On a client navigation or action the state travels in the redirect's
13
+ * payload. On a document request (a page load, a no-JS form post) it travels
14
+ * in a one-shot cookie the target's document request reads and clears, as
15
+ * JSON: give it plain JSON data there. `{ external: true }` carries none.
16
+ * Two entries of the same definition keep only the last.
12
17
  *
13
18
  * @param url - The URL to redirect to
14
19
  * @param statusOrOptions - HTTP status code (default: 302) or options object
@@ -56,6 +56,12 @@ export interface MiddlewareContext<TEnv = any, TParams = Record<string, string |
56
56
  */
57
57
  readonly theme?: Theme;
58
58
  setTheme?: (theme: Theme) => void;
59
+ /**
60
+ * Attach location state entries to this response (see the handler
61
+ * context's `setLocationState`). Entries accumulate across definitions;
62
+ * for one definition the last write wins. Set it here rather than in a
63
+ * handler on a `ppr` route: a shell HIT runs no handler.
64
+ */
59
65
  setLocationState(entries: LocationStateEntry | LocationStateEntry[]): void;
60
66
  /** Global route names only; see {@link GlobalReverseFunction}. */
61
67
  reverse: GlobalReverseFunction<DefaultReverseRouteMap>;
@@ -95,10 +95,12 @@ export declare function interceptRedirectForPartial(response: Response, createRe
95
95
  /**
96
96
  * Attach location state set during a request to a payload's metadata.
97
97
  * No-op if no location state was set. Callers must ensure payload.metadata
98
- * is populated (the non-null assertion holds for the partial/action payloads
99
- * that reach this helper).
98
+ * is populated. A `document` payload (a page load's or a no-JS form post's
99
+ * bootstrap payload) that carries state marks the request
100
+ * `_documentLocationState`: it is the visitor's, so no shared cache keeps it
101
+ * (server/document-location-state.ts).
100
102
  */
101
- export declare function attachLocationStateIfPresent(payload: RscPayload): void;
103
+ export declare function attachLocationStateIfPresent(payload: RscPayload, document?: boolean): void;
102
104
  /**
103
105
  * Only cache successful responses. Non-200 statuses (errors, redirects) are
104
106
  * not cached -- notFound() produces 500 in response routes, and explicit
@@ -36,6 +36,8 @@ export interface StageRouter<TEnv = unknown> {
36
36
  readonly telemetry?: TelemetrySink;
37
37
  readonly timeouts?: ResolvedTimeouts;
38
38
  readonly onTimeout?: OnTimeoutCallback<TEnv>;
39
+ /** Names the cookie location state crosses a document redirect in. */
40
+ readonly resolvedStateCookieName: string;
39
41
  }
40
42
  /** Report an error through the router's onError. */
41
43
  export type ReportError<TEnv = unknown> = (error: unknown, phase: ErrorPhase, context: InvokeOnErrorContext<TEnv>) => void;
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Location state on document responses (#1330).
3
+ *
4
+ * A client navigation or an action carries server-set location state in its
5
+ * Flight payload, and a redirect on either becomes a Flight redirect that
6
+ * carries it too. A document request has no such channel for a 3xx: the
7
+ * browser follows it with a plain GET. This module carries the state across
8
+ * that one hop in a short-lived cookie:
9
+ *
10
+ * - `carryLocationStateAcrossRedirect` (rsc/request-stages.ts
11
+ * finalizeRequestResponse) writes it on a document request's same-origin
12
+ * 3xx when the request set location state;
13
+ * - `consumeDocumentLocationState` (rsc/request-stages.ts runRequestPasses)
14
+ * reads it on the next GET document request that navigates to the target
15
+ * pathname (`Sec-Fetch-Dest` a browsing context, where sent), seeds
16
+ * `_locationState` with its entries (so the render attaches them to the
17
+ * bootstrap payload, rsc/rsc-rendering.ts) and clears it.
18
+ *
19
+ * A document that carries location state is per visitor: it is marked
20
+ * `_documentLocationState`, never stored by the document cache or as a PPR
21
+ * shell, and leaves with `Cache-Control: private, no-store`
22
+ * (`markDocumentResponsePrivate`).
23
+ *
24
+ * The value is JSON, not Flight: a cookie holds text, and the client merges
25
+ * it as `history.state` would hold it. `Path=/` because the `__Host-` prefix
26
+ * (https) requires it, which keeps a sibling subdomain from planting state;
27
+ * the target pathname inside the value decides who consumes it.
28
+ */
29
+ import { type RequestContext } from "./request-context.js";
30
+ /** The cookie's name for a router: `__Host-` on https. */
31
+ export declare function documentLocationStateCookieName(stateCookieName: string, secure: boolean): string;
32
+ /**
33
+ * Read the cookie a document redirect left for this request: seed
34
+ * `_locationState` with its slots and clear it. Only a GET document request
35
+ * of the target pathname that navigates consumes it (the request the browser
36
+ * makes for a 301/302/303, see isNavigationRequest); any other request, a
37
+ * form POST to that path or a prefetch of it included, leaves it to its
38
+ * expiry. A malformed value is cleared and ignored, and only `__rsc_ls_*`
39
+ * slots are taken from it (the value comes from the browser).
40
+ */
41
+ export declare function consumeDocumentLocationState(reqCtx: Pick<RequestContext, "_locationState" | "setCookie">, request: Request, url: URL, stateCookieName: string): void;
42
+ /**
43
+ * Write the cookie on a document request's redirect that set location state,
44
+ * and mark the redirect private. Not for a partial or action request (their
45
+ * redirect is a Flight payload), an external or cross-origin target (the
46
+ * cookie would never reach it) or a response that is not a 3xx with a
47
+ * Location. Returns the response to send: a new one when its headers are
48
+ * immutable.
49
+ */
50
+ export declare function carryLocationStateAcrossRedirect(response: Response, reqCtx: Pick<RequestContext, "_locationState">, request: Request, url: URL, stateCookieName: string): Response;
51
+ /**
52
+ * `Cache-Control: private, no-store` on a document that carries location
53
+ * state, after every middleware: a shared cache must never store or replay
54
+ * one visitor's state. Returns the response to send.
55
+ */
56
+ export declare function markDocumentResponsePrivate(response: Response, reqCtx: Pick<RequestContext, "_documentLocationState">): Response;
@@ -547,12 +547,22 @@ export interface RequestContext<TEnv = DefaultEnv, TParams = Record<string, stri
547
547
  /**
548
548
  * Attach location state entries to the current response.
549
549
  *
550
- * For partial (SPA) requests, the state is included in the RSC payload
551
- * metadata and merged into history.pushState on the client. For redirect
552
- * responses, the state travels through the redirect payload so the target
553
- * page can read it via useLocationState.
550
+ * For a client navigation or an action, the state is included in the RSC
551
+ * payload metadata and merged into the entry's history.state on the
552
+ * client. For a document request (a page load, a no-JS form post), it rides
553
+ * the document's bootstrap payload and is merged into the entry when the
554
+ * client starts; readers get it once the page has hydrated. For a redirect,
555
+ * the state travels with the redirect: in its Flight payload on a
556
+ * navigation or action, in a one-shot cookie the target's document request
557
+ * reads on a document request. A document that carries state is sent
558
+ * `Cache-Control: private, no-store` and never stored by the document cache
559
+ * or as a PPR shell.
554
560
  *
555
- * Multiple calls accumulate entries.
561
+ * Multiple calls accumulate entries across definitions. For one definition
562
+ * the last write wins: two entries of the same definition share its slot
563
+ * in history.state, so a later call (or a later entry in one array)
564
+ * replaces an earlier one. To combine writes, make the state an array and
565
+ * write it once.
556
566
  *
557
567
  * @example
558
568
  * ```typescript
@@ -562,6 +572,13 @@ export interface RequestContext<TEnv = DefaultEnv, TParams = Record<string, stri
562
572
  setLocationState(entries: LocationStateEntry | LocationStateEntry[]): void;
563
573
  /** @internal Accumulated location state entries */
564
574
  _locationState?: LocationStateEntry[];
575
+ /**
576
+ * @internal Set when a document's payload carries location state
577
+ * (rsc/rsc-rendering.ts, rsc/progressive-enhancement.ts), a shell HIT's
578
+ * included. The document is per visitor: the document cache refuses it,
579
+ * and it leaves `private, no-store` (server/document-location-state.ts).
580
+ */
581
+ _documentLocationState?: boolean;
565
582
  /**
566
583
  * The matched route name, if the route has an explicit name.
567
584
  * Undefined before route matching or for unnamed routes.
@@ -795,7 +812,7 @@ export interface RequestContext<TEnv = DefaultEnv, TParams = Record<string, stri
795
812
  * This is the type exported to library consumers. Internal code should
796
813
  * use the full RequestContext interface directly.
797
814
  */
798
- export type PublicRequestContext<TEnv = DefaultEnv, TParams = Record<string, string>> = Omit<RequestContext<TEnv, TParams>, "cookie" | "cookies" | "_readCookie" | "_readCookies" | "setCookie" | "deleteCookie" | "_handleStore" | "_transitionWhenRefs" | "_pprReplayPostMatchReason" | "_cacheStore" | "_prerender" | "_onDemandProducer" | "_searchParamsFilter" | "_shellCaptureState" | "_shellImplicitCache" | "_shellLoaderSeed" | "_resolvedCacheKeys" | "_shellKey" | "_shellForcedMiss" | "_prerenderWarm" | "_runAtRequestEntry" | "_shellFragmentPayload" | "_explicitTaggedStores" | "_requestTags" | "_recordTagOwners" | "_recordLoaderTags" | "_cacheProfiles" | "_onResponseCallbacks" | "_pendingBackgroundTasks" | "_themeConfig" | "_readTheme" | "_documentCacheRender" | "_payloadVisitorTheme" | "_payloadDeferred" | "_requestKind" | "_payloadVisitorLocation" | "_locationState" | "_routeName" | "_prevRouteKey" | "_visitorMoved" | "_inActionRevalidation" | "_actionRan" | "_reportedErrors" | "_throwers" | "_printedErrors" | "_renderErrors" | "_recoveredHandlerErrors" | "_renderBarrier" | "_resolveRenderBarrier" | "_renderBarrierSegmentOrder" | "_treeHasStreaming" | "_renderBarrierWaiters" | "_handlerLoaderDeps" | "_awaitBeforeFlushLoaderIds" | "_renderBarrierHandleSnapshot" | "_renderBarrierGuardClosed" | "_reportBackgroundError" | "_debugPerformance" | "_metricsStore" | "_renderForeground" | "_activeRoutine" | "_renderDiagnosticsEnabled" | "_handlerStart" | "_handlerTiming" | "_tracing" | "_basename" | "_routerId" | "_versions" | "_setStatus" | "_rotateStateCookie" | "_setKeepCacheDirective" | "_freshReads" | "_setFreshReadsCookie" | "_requestRoot" | "_requestStart" | "_responseSent" | "_variables" | "_classifiedRoute" | "_requestMode" | "_cacheSignal" | "_dynamic" | "res">;
815
+ export type PublicRequestContext<TEnv = DefaultEnv, TParams = Record<string, string>> = Omit<RequestContext<TEnv, TParams>, "cookie" | "cookies" | "_readCookie" | "_readCookies" | "setCookie" | "deleteCookie" | "_handleStore" | "_transitionWhenRefs" | "_pprReplayPostMatchReason" | "_cacheStore" | "_prerender" | "_onDemandProducer" | "_searchParamsFilter" | "_shellCaptureState" | "_shellImplicitCache" | "_shellLoaderSeed" | "_resolvedCacheKeys" | "_shellKey" | "_shellForcedMiss" | "_prerenderWarm" | "_runAtRequestEntry" | "_shellFragmentPayload" | "_explicitTaggedStores" | "_requestTags" | "_recordTagOwners" | "_recordLoaderTags" | "_cacheProfiles" | "_onResponseCallbacks" | "_pendingBackgroundTasks" | "_themeConfig" | "_readTheme" | "_documentCacheRender" | "_payloadVisitorTheme" | "_payloadDeferred" | "_requestKind" | "_payloadVisitorLocation" | "_locationState" | "_documentLocationState" | "_routeName" | "_prevRouteKey" | "_visitorMoved" | "_inActionRevalidation" | "_actionRan" | "_reportedErrors" | "_throwers" | "_printedErrors" | "_renderErrors" | "_recoveredHandlerErrors" | "_renderBarrier" | "_resolveRenderBarrier" | "_renderBarrierSegmentOrder" | "_treeHasStreaming" | "_renderBarrierWaiters" | "_handlerLoaderDeps" | "_awaitBeforeFlushLoaderIds" | "_renderBarrierHandleSnapshot" | "_renderBarrierGuardClosed" | "_reportBackgroundError" | "_debugPerformance" | "_metricsStore" | "_renderForeground" | "_activeRoutine" | "_renderDiagnosticsEnabled" | "_handlerStart" | "_handlerTiming" | "_tracing" | "_basename" | "_routerId" | "_versions" | "_setStatus" | "_rotateStateCookie" | "_setKeepCacheDirective" | "_freshReads" | "_setFreshReadsCookie" | "_requestRoot" | "_requestStart" | "_responseSent" | "_variables" | "_classifiedRoute" | "_requestMode" | "_cacheSignal" | "_dynamic" | "res">;
799
816
  /**
800
817
  * Marker for a waitUntil-scheduled fn whose task promise must NOT enter
801
818
  * _pendingBackgroundTasks. Used by the PPR shell capture for its own task:
@@ -185,6 +185,23 @@ export interface RenderRouteOptions {
185
185
  * });
186
186
  */
187
187
  locationState?: ReadonlyArray<readonly [LocationStateDefinition<any, any>, unknown]>;
188
+ /**
189
+ * Location state the document response carries, as `[def, value]` pairs:
190
+ * what a handler, middleware or no-JS form action set with
191
+ * `ctx.setLocationState()` (or a document redirect carried) for this page
192
+ * load. Merged into the entry at start-up as initBrowserApp merges the
193
+ * bootstrap payload's: after `locationState` and, with `hydrate: true`,
194
+ * after the `clearOnReload` pass, so a `clearOnReload` slot given here is
195
+ * applied. With `hydrate: true` a reader hydrates as `undefined` and gets
196
+ * the value once the root has hydrated.
197
+ *
198
+ * @example
199
+ * await renderRoute([{ path: "/", Component: FlashBanner }], {
200
+ * hydrate: true,
201
+ * documentLocationState: [[FlashMessage, { text: "Payment declined" }]],
202
+ * });
203
+ */
204
+ documentLocationState?: ReadonlyArray<readonly [LocationStateDefinition<any, any>, unknown]>;
188
205
  /**
189
206
  * Handles to seed by REFERENCE, for components that read handle output via
190
207
  * `useHandle(SomeHandle)` (e.g. a client `Breadcrumbs` trail). Each entry is
@@ -229,6 +229,15 @@ export interface ServeShellRequestResult {
229
229
  * hydrates without it.
230
230
  */
231
231
  readPayloadFormState(): Promise<unknown>;
232
+ /**
233
+ * The payload's location state as the browser reads it: the slots
234
+ * (`Def.__rsc_ls_key` to value) a document's start-up merges into its
235
+ * entry (or a navigation's commit, for a `partial` request). Undefined when
236
+ * the payload carries none (or no Flight was rendered). A redirect carries
237
+ * a document request's state in a `Set-Cookie` instead: pass that cookie
238
+ * on the request of its target to read what the target's payload carries.
239
+ */
240
+ readLocationState(): Promise<Record<string, unknown> | undefined>;
232
241
  /**
233
242
  * For an `action` request, the call's answer as the browser reads it: its
234
243
  * result and the `revalidationError` its loaders' reruns report.
@@ -274,9 +274,16 @@ export type HandlerContext<TParams = {}, TEnv = DefaultEnv, TSearch extends Sear
274
274
  */
275
275
  setTheme?: (theme: Theme) => void;
276
276
  /**
277
- * Attach location state entries to this response.
278
- * State is delivered to the client via history.pushState and accessible
279
- * through the useLocationState() hook.
277
+ * Attach location state entries to this response, for the useLocationState()
278
+ * hook to read on the entry this response renders. A client navigation
279
+ * delivers it with its payload; a document load (a link from outside the
280
+ * app, a no-JS form post) with its bootstrap payload, once the page has
281
+ * hydrated; a redirect carries it to its target (in a one-shot cookie on a
282
+ * document request). A document carrying it is sent
283
+ * `Cache-Control: private, no-store`.
284
+ *
285
+ * Entries accumulate across definitions; for one definition the last write
286
+ * wins (an earlier entry of it is replaced, not merged).
280
287
  *
281
288
  * @example
282
289
  * ```typescript
@@ -390,6 +397,14 @@ export type InternalHandlerContext<TParams = {}, TEnv = DefaultEnv, TSearch exte
390
397
  * request is a fill. See router/segment-resolution/prefetch-deferral.ts.
391
398
  */
392
399
  _prefetchDeferral?: import("../router/segment-resolution/prefetch-deferral.js").PrefetchDeferral;
400
+ /**
401
+ * @internal A revalidate() predicate that read the source page
402
+ * (`currentParams`, `currentUrl`, `fromRouteName`) kept a held segment, so
403
+ * this match's diff is right for that source only (#1331). Set by
404
+ * evaluateRevalidation (router/revalidation.ts); match-result.ts turns it
405
+ * into `sourceHeld`, which scopes a prefetch of the response to its source.
406
+ */
407
+ _sourceHeld?: true;
393
408
  /**
394
409
  * @internal DSL loaders registered and not started, by `$$id`: a prefetch
395
410
  * deferred them, or a pass that skips loaders (`skipLoaders`) did not run
@@ -274,6 +274,12 @@ export interface MatchResult {
274
274
  * source, so the RSC handler scopes its prefetch entry to it (#1007).
275
275
  */
276
276
  interceptTargeted?: true;
277
+ /**
278
+ * Set on a partial match when a revalidate() predicate read the source page
279
+ * and kept a held segment: the diff is that source's. The RSC handler
280
+ * scopes its prefetch entry to it, like `interceptTargeted` (#1331).
281
+ */
282
+ sourceHeld?: true;
277
283
  /**
278
284
  * Redirect URL for trailing slash normalization.
279
285
  * When set, the RSC handler should return a 308 redirect to this URL
@@ -1,12 +1,14 @@
1
1
  /**
2
2
  * The discovery temp RSC server: a minimal Vite server whose module runner
3
3
  * imports the user's router files. Shared by dev discovery
4
- * (discovery/dev-server-phase.ts) and build discovery
5
- * (discovery/build-discovery-phase.ts).
4
+ * (discovery/dev-server-phase.ts), build discovery
5
+ * (discovery/build-discovery-phase.ts) and `rango generate --runtime`
6
+ * (build/runtime-discovery.ts).
6
7
  *
7
8
  * Sits in src/vite/, not src/vite/discovery/: the loader hook URL below
8
- * resolves against this module's import.meta.url, and the published bundle
9
- * (dist/vite/index.js) ships the hook at the same relative path.
9
+ * resolves against this module's import.meta.url, and the published bundles
10
+ * (dist/vite/index.js, dist/bin/rango.js) ship the hook at the same relative
11
+ * path.
10
12
  */
11
13
  import type { ViteDevServer } from "vite";
12
14
  import type { DiscoveryState } from "./discovery/state.js";
@@ -36,4 +38,6 @@ export declare function createTempRscServer(state: DiscoveryState, options?: {
36
38
  * shell capture actually runs.
37
39
  */
38
40
  realSsrEntry?: boolean;
41
+ /** Caller-specific plugins, appended after the forwarded user plugins. */
42
+ plugins?: import("vite").Plugin[];
39
43
  }): Promise<ViteDevServer>;
@@ -1,4 +1,34 @@
1
1
  import type { Plugin, ResolvedConfig, UserConfig } from "vite";
2
+ /**
3
+ * Whether a user plugin must NOT be forwarded into the discovery temp server.
4
+ *
5
+ * Framework-owned plugins are matched precisely -- by exact name or a
6
+ * namespaced prefix -- rather than by a loose substring/word prefix, so an
7
+ * unrelated user resolver named e.g. `rsc-paths` or `cloudflare-kv-alias` is
8
+ * still forwarded (it would otherwise reproduce issue #500).
9
+ *
10
+ * - `vite:*` Vite core + official plugins (incl.
11
+ * @vitejs/plugin-react's `vite:react-*`). The temp
12
+ * server already provides its own core; forwarding
13
+ * these would duplicate or conflict with it.
14
+ * - `rsc` / `rsc:*` @vitejs/plugin-rsc. createTempRscServer instantiates
15
+ * its own rsc() plugin; forwarding would duplicate it.
16
+ * Matched exactly (`rsc`) or by the `rsc:` namespace --
17
+ * NOT every `rsc`-prefixed name.
18
+ * - `@rangojs/router*` Our own plugins. The discovery plugin spawns the
19
+ * temp server, so forwarding would recurse infinitely.
20
+ * - `@cloudflare/vite-plugin*` / @cloudflare/vite-plugin (emits the scoped
21
+ * `vite-plugin-cloudflare*` `@cloudflare/vite-plugin` and unscoped
22
+ * `vite-plugin-cloudflare` / `vite-plugin-cloudflare:*`).
23
+ * Forwarding re-inits workerd, defeating the Node temp
24
+ * server. Matched specifically so a scoped user resolver
25
+ * like `@cloudflare/kv-alias` is still forwarded.
26
+ *
27
+ * `rango generate --runtime` drops these before it resolves the user's config,
28
+ * so rango()'s configResolved (which writes the static gen files) and the
29
+ * Cloudflare plugin's config hooks never run there.
30
+ */
31
+ export declare function isFrameworkPlugin(name: string): boolean;
2
32
  /**
3
33
  * Pick the user's resolution plugins from the resolved plugin list, denylist
4
34
  * framework-owned plugins, keep only those with resolution hooks, and strip
@@ -20,6 +50,8 @@ export interface ForwardedRunnerConfig {
20
50
  /**
21
51
  * Extract the data-only config slice to mirror into the discovery temp server.
22
52
  * `alias` is included here so callers no longer need to thread it separately.
53
+ * Accepts the unresolved config too: `rango generate --runtime` passes the
54
+ * config file's own values, without what plugin `config` hooks add.
23
55
  *
24
56
  * `tsconfigPaths` is forwarded so Vite 8's native tsconfig `paths` resolution
25
57
  * (a top-level `resolve` flag, off by default) reaches the temp server. The
@@ -34,4 +66,4 @@ export interface ForwardedRunnerConfig {
34
66
  * replaced the deprecated `esbuild` transform option with `oxc`, so we read and
35
67
  * forward `oxc` exclusively — no `esbuild` field is touched.
36
68
  */
37
- export declare function pickForwardedRunnerConfig(config: ResolvedConfig): ForwardedRunnerConfig;
69
+ export declare function pickForwardedRunnerConfig(config: Pick<ResolvedConfig | UserConfig, "resolve" | "define" | "oxc">): ForwardedRunnerConfig;