@rangojs/router 0.24.4 → 0.26.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/README.md +1 -0
- package/dist/bin/rango.js +363 -39
- package/dist/testing/vitest.js +37 -4
- package/dist/types/browser/event-controller.d.ts +24 -5
- package/dist/types/browser/intercept-utils.d.ts +5 -2
- package/dist/types/browser/merge-segment-loaders.d.ts +0 -23
- package/dist/types/browser/navigation-transaction.d.ts +5 -5
- package/dist/types/browser/partial-update.d.ts +21 -13
- package/dist/types/browser/prefetch/cache.d.ts +8 -0
- package/dist/types/browser/rango-marker.d.ts +7 -0
- package/dist/types/browser/react/filter-segment-order.d.ts +10 -0
- package/dist/types/browser/react/use-handle.d.ts +20 -0
- package/dist/types/browser/react/use-href.d.ts +6 -0
- package/dist/types/browser/react/use-router.d.ts +6 -1
- package/dist/types/browser/response-adapter.d.ts +5 -3
- package/dist/types/browser/scroll-restoration.d.ts +9 -3
- package/dist/types/browser/segment-reconciler.d.ts +10 -13
- package/dist/types/browser/types.d.ts +38 -8
- package/dist/types/build/route-types/per-module-writer.d.ts +12 -0
- package/dist/types/cache/cf/cf-cache-store.d.ts +0 -2
- package/dist/types/cache/cf/cf-cache-types.d.ts +0 -16
- package/dist/types/cache/cf/cf-shell-frame.d.ts +2 -0
- package/dist/types/cache/handle-snapshot.d.ts +2 -2
- package/dist/types/cache/types.d.ts +16 -6
- package/dist/types/cache/vercel/vercel-cache-store.d.ts +1 -15
- package/dist/types/client-urls/client-urls.d.ts +6 -2
- package/dist/types/client-urls/revalidate-registry.d.ts +1 -1
- package/dist/types/client-urls/server-projection.d.ts +9 -5
- package/dist/types/client-urls/types.d.ts +29 -16
- package/dist/types/client.d.ts +2 -2
- package/dist/types/client.rsc.d.ts +2 -2
- package/dist/types/decode-loader-results.d.ts +28 -7
- package/dist/types/handles/deferred-resolution.d.ts +3 -2
- package/dist/types/handles/meta.d.ts +5 -0
- package/dist/types/href-client.d.ts +3 -1
- package/dist/types/index.rsc.d.ts +1 -1
- package/dist/types/loader-redirect.d.ts +2 -3
- package/dist/types/outlet-context.d.ts +7 -6
- package/dist/types/prefetch-markers.d.ts +14 -5
- package/dist/types/prerender/build-shell-capture.d.ts +1 -2
- package/dist/types/prerender/writable-store.d.ts +2 -3
- package/dist/types/route-content-wrapper.d.ts +17 -14
- package/dist/types/route-definition/helpers-types.d.ts +7 -2
- package/dist/types/route-definition/resolve-handler-use.d.ts +6 -1
- package/dist/types/route-definition/rewrite.d.ts +48 -5
- package/dist/types/route-types.d.ts +2 -2
- package/dist/types/router/handler-context.d.ts +14 -2
- package/dist/types/router/intercept-resolution.d.ts +19 -46
- package/dist/types/router/lazy-includes.d.ts +7 -0
- package/dist/types/router/match-middleware/intercept-resolution.d.ts +2 -2
- package/dist/types/router/middleware.d.ts +0 -16
- package/dist/types/router/navigation-snapshot.d.ts +2 -0
- package/dist/types/router/prefetch-cache-ttl.d.ts +8 -11
- package/dist/types/router/revalidation.d.ts +8 -1
- package/dist/types/router/router-context.d.ts +4 -16
- package/dist/types/router/router-interfaces.d.ts +0 -11
- package/dist/types/router/router-options.d.ts +4 -27
- package/dist/types/router/segment-resolution/mask-nested.d.ts +5 -3
- package/dist/types/router/segment-resolution/prefetch-deferral.d.ts +31 -8
- package/dist/types/router/segment-resolution/revalidation.d.ts +38 -4
- package/dist/types/router/segment-resolution.d.ts +1 -1
- package/dist/types/router/segment-wrappers.d.ts +3 -5
- package/dist/types/router/substitute-pattern-params.d.ts +3 -2
- package/dist/types/router/tracing.d.ts +2 -2
- package/dist/types/router-versions.d.ts +8 -6
- package/dist/types/rsc/full-payload.d.ts +14 -2
- package/dist/types/rsc/helpers.d.ts +8 -0
- package/dist/types/rsc/manifest-init.d.ts +20 -0
- package/dist/types/rsc/prefetch-render-error.d.ts +22 -0
- package/dist/types/rsc/progressive-enhancement.d.ts +20 -0
- package/dist/types/rsc/rewrite-passes.d.ts +12 -2
- package/dist/types/rsc/server-action.d.ts +6 -0
- package/dist/types/rsc/shell-capture-constants.d.ts +14 -4
- package/dist/types/rsc/shell-capture.d.ts +23 -2
- package/dist/types/rsc/types.d.ts +5 -8
- package/dist/types/segment-system.d.ts +15 -0
- package/dist/types/server/build-version-table.d.ts +10 -13
- package/dist/types/server/context.d.ts +90 -6
- package/dist/types/server/handle-store.d.ts +17 -6
- package/dist/types/server/request-context.d.ts +45 -7
- package/dist/types/server/request-headers.d.ts +12 -0
- package/dist/types/server/rewrite-trail.d.ts +8 -1
- package/dist/types/testing/build-versions.d.ts +0 -3
- package/dist/types/testing/e2e/index.d.ts +1 -1
- package/dist/types/testing/flight.entry.d.ts +1 -1
- package/dist/types/testing/index.d.ts +1 -1
- package/dist/types/testing/internal/client-urls-projection.d.ts +4 -3
- package/dist/types/testing/internal/seed-vars.d.ts +4 -4
- package/dist/types/testing/render-route.d.ts +21 -11
- package/dist/types/testing/run-loader.d.ts +7 -0
- package/dist/types/testing/run-middleware.d.ts +7 -0
- package/dist/types/testing/run-revalidate.d.ts +5 -4
- package/dist/types/testing/serve-shell-request.d.ts +42 -5
- package/dist/types/testing/shell-status.d.ts +17 -0
- package/dist/types/testing/vitest-stubs/version.d.ts +1 -0
- package/dist/types/testing/vitest.d.ts +10 -1
- package/dist/types/thenable-status.d.ts +0 -6
- package/dist/types/types/handler-context.d.ts +12 -1
- package/dist/types/types/loader-types.d.ts +10 -4
- package/dist/types/types/route-entry.d.ts +2 -1
- package/dist/types/urls/path-helper-types.d.ts +4 -2
- package/dist/types/vite/discovery/client-urls-projection.d.ts +55 -5
- package/dist/types/vite/discovery/state.d.ts +20 -0
- package/dist/types/vite/plugins/client-urls-revalidate-hoist.d.ts +28 -16
- package/dist/types/vite/plugins/hoist/source.d.ts +8 -7
- package/dist/types/vite/plugins/virtual-entries.d.ts +6 -0
- package/dist/types/vite/utils/prerender-utils.d.ts +7 -0
- package/dist/vite/index.js +6819 -6555
- package/package.json +1 -1
- package/skills/app-structure/SKILL.md +255 -0
- package/skills/app-structure/content-site.md +333 -0
- package/skills/app-structure/e-commerce.md +435 -0
- package/skills/app-structure/large-app.md +885 -0
- package/skills/app-structure/multi-app.md +280 -0
- package/skills/breadcrumbs/SKILL.md +6 -1
- package/skills/cache-guide/SKILL.md +4 -8
- package/skills/caching/SKILL.md +6 -6
- package/skills/catalog.json +12 -0
- package/skills/client-urls/SKILL.md +185 -32
- package/skills/comparison/references/framework-comparison.md +1 -1
- package/skills/composability/SKILL.md +10 -8
- package/skills/handler-use/SKILL.md +48 -9
- package/skills/hooks/handle-and-actions.md +104 -0
- package/skills/hooks/navigation.md +24 -10
- package/skills/hooks/urls.md +1 -1
- package/skills/host-router/SKILL.md +1 -1
- package/skills/i18n/SKILL.md +2 -1
- package/skills/intercept/SKILL.md +60 -29
- package/skills/layout/SKILL.md +6 -3
- package/skills/links/SKILL.md +151 -17
- package/skills/loader/SKILL.md +42 -19
- package/skills/middleware/SKILL.md +111 -15
- package/skills/observability/SKILL.md +11 -0
- package/skills/parallel/SKILL.md +5 -5
- package/skills/ppr/SKILL.md +103 -52
- package/skills/prerender/SKILL.md +4 -5
- package/skills/rango/SKILL.md +3 -0
- package/skills/response-routes/SKILL.md +4 -1
- package/skills/router-setup/SKILL.md +0 -2
- package/skills/scripts/SKILL.md +12 -1
- package/skills/server-actions/SKILL.md +60 -20
- package/skills/testing/SKILL.md +1 -0
- package/skills/testing/client-components.md +13 -10
- package/skills/testing/loader.md +25 -24
- package/skills/testing/middleware.md +11 -11
- package/skills/testing/render-handler.md +17 -17
- package/skills/testing/response-routes.md +4 -1
- package/skills/testing/serve-shell-request.md +86 -22
- package/skills/testing/server-actions.md +16 -16
- package/skills/ux-architecture/SKILL.md +980 -0
- package/skills/vercel/SKILL.md +1 -1
- package/skills/view-transitions/SKILL.md +43 -10
- package/src/__augment-tests__/augmented.check.ts +28 -0
- package/src/browser/event-controller.ts +44 -18
- package/src/browser/intercept-utils.ts +3 -2
- package/src/browser/merge-segment-loaders.ts +0 -85
- package/src/browser/navigation-bridge.ts +112 -84
- package/src/browser/navigation-client.ts +20 -15
- package/src/browser/navigation-store.ts +4 -2
- package/src/browser/navigation-transaction.ts +9 -8
- package/src/browser/partial-update.ts +90 -32
- package/src/browser/prefetch/cache.ts +8 -0
- package/src/browser/prefetch/fetch.ts +13 -3
- package/src/browser/rango-marker.ts +13 -0
- package/src/browser/react/NavigationProvider.tsx +25 -7
- package/src/browser/react/filter-segment-order.ts +13 -0
- package/src/browser/react/use-action.ts +9 -15
- package/src/browser/react/use-handle.ts +63 -71
- package/src/browser/react/use-href.tsx +24 -5
- package/src/browser/react/use-link-status.ts +1 -1
- package/src/browser/react/use-navigation.ts +30 -40
- package/src/browser/react/use-router.ts +37 -6
- package/src/browser/react/use-segments.ts +8 -12
- package/src/browser/response-adapter.ts +11 -5
- package/src/browser/rsc-router.tsx +3 -1
- package/src/browser/scroll-restoration.ts +34 -20
- package/src/browser/segment-reconciler.ts +11 -29
- package/src/browser/server-action-bridge.ts +52 -16
- package/src/browser/types.ts +40 -8
- package/src/build/generate-manifest.ts +12 -1
- package/src/build/route-types/codegen.ts +6 -2
- package/src/build/route-types/per-module-writer.ts +94 -61
- package/src/cache/cache-runtime.ts +9 -0
- package/src/cache/cache-scope.ts +2 -1
- package/src/cache/cf/cf-cache-store.ts +6 -7
- package/src/cache/cf/cf-cache-types.ts +0 -17
- package/src/cache/cf/cf-shell-frame.ts +3 -0
- package/src/cache/handle-snapshot.ts +2 -2
- package/src/cache/types.ts +16 -6
- package/src/cache/vercel/vercel-cache-store.ts +8 -19
- package/src/client-urls/client-urls.ts +202 -153
- package/src/client-urls/revalidate-registry.ts +12 -3
- package/src/client-urls/server-projection.ts +137 -66
- package/src/client-urls/types.ts +30 -15
- package/src/client.rsc.tsx +11 -1
- package/src/client.tsx +9 -1
- package/src/decode-loader-results.ts +64 -31
- package/src/handles/deferred-resolution.ts +3 -2
- package/src/handles/meta.ts +5 -0
- package/src/href-client.ts +3 -1
- package/src/index.rsc.ts +1 -0
- package/src/index.ts +12 -4
- package/src/loader-redirect.tsx +2 -3
- package/src/outlet-context.ts +7 -6
- package/src/prefetch-markers.ts +15 -5
- package/src/prerender/build-shell-capture.ts +1 -2
- package/src/prerender/writable-store.ts +2 -3
- package/src/reverse.ts +3 -3
- package/src/route-content-wrapper.tsx +19 -17
- package/src/route-definition/dsl-helpers.ts +41 -21
- package/src/route-definition/helpers-types.ts +7 -2
- package/src/route-definition/resolve-handler-use.ts +23 -10
- package/src/route-definition/rewrite.ts +86 -19
- package/src/route-types.ts +1 -2
- package/src/router/find-match.ts +37 -1
- package/src/router/handler-context.ts +85 -40
- package/src/router/intercept-resolution.ts +161 -307
- package/src/router/lazy-includes.ts +43 -27
- package/src/router/loader-resolution.ts +21 -1
- package/src/router/manifest.ts +15 -10
- package/src/router/match-api.ts +11 -1
- package/src/router/match-middleware/background-revalidation.ts +2 -4
- package/src/router/match-middleware/cache-lookup.ts +16 -20
- package/src/router/match-middleware/intercept-resolution.ts +41 -60
- package/src/router/match-result.ts +31 -2
- package/src/router/middleware.ts +6 -147
- package/src/router/navigation-snapshot.ts +38 -2
- package/src/router/prefetch-cache-ttl.ts +10 -18
- package/src/router/prerender-match.ts +2 -1
- package/src/router/revalidation.ts +24 -3
- package/src/router/router-context.ts +5 -18
- package/src/router/router-interfaces.ts +0 -13
- package/src/router/router-options.ts +4 -28
- package/src/router/segment-resolution/fresh.ts +6 -2
- package/src/router/segment-resolution/helpers.ts +7 -1
- package/src/router/segment-resolution/loader-cache.ts +4 -3
- package/src/router/segment-resolution/mask-nested.ts +5 -3
- package/src/router/segment-resolution/prefetch-deferral.ts +62 -12
- package/src/router/segment-resolution/revalidation.ts +75 -35
- package/src/router/segment-resolution.ts +0 -1
- package/src/router/segment-wrappers.ts +8 -13
- package/src/router/substitute-pattern-params.ts +3 -2
- package/src/router/tracing.ts +2 -2
- package/src/router-versions.ts +7 -7
- package/src/router.ts +5 -17
- package/src/rsc/full-payload.ts +26 -4
- package/src/rsc/handler.ts +86 -48
- package/src/rsc/helpers.ts +13 -0
- package/src/rsc/manifest-init.ts +56 -3
- package/src/rsc/prefetch-render-error.ts +38 -0
- package/src/rsc/progressive-enhancement.ts +161 -211
- package/src/rsc/rewrite-passes.ts +56 -9
- package/src/rsc/rsc-rendering.ts +79 -23
- package/src/rsc/server-action.ts +25 -2
- package/src/rsc/shell-capture-constants.ts +26 -3
- package/src/rsc/shell-capture.ts +137 -51
- package/src/rsc/types.ts +5 -9
- package/src/segment-system.tsx +125 -119
- package/src/server/build-version-table.ts +12 -24
- package/src/server/context.ts +160 -7
- package/src/server/handle-store.ts +80 -12
- package/src/server/request-context.ts +46 -6
- package/src/server/request-headers.ts +32 -0
- package/src/server/rewrite-trail.ts +55 -9
- package/src/ssr/ssr-root.tsx +6 -10
- package/src/testing/build-versions.ts +0 -3
- package/src/testing/dispatch.ts +5 -8
- package/src/testing/e2e/index.ts +1 -0
- package/src/testing/flight.entry.ts +1 -0
- package/src/testing/index.ts +1 -0
- package/src/testing/internal/client-urls-projection.ts +31 -11
- package/src/testing/internal/seed-vars.ts +4 -4
- package/src/testing/render-route.tsx +83 -40
- package/src/testing/run-loader.ts +26 -2
- package/src/testing/run-middleware.ts +10 -0
- package/src/testing/run-revalidate.ts +13 -6
- package/src/testing/serve-shell-request.ts +79 -9
- package/src/testing/shell-status.ts +40 -0
- package/src/testing/vitest-stubs/version.ts +1 -0
- package/src/testing/vitest.ts +74 -7
- package/src/thenable-status.ts +0 -11
- package/src/types/handler-context.ts +12 -1
- package/src/types/loader-types.ts +10 -4
- package/src/types/route-entry.ts +2 -1
- package/src/urls/include-helper.ts +38 -11
- package/src/urls/path-helper-types.ts +4 -2
- package/src/vite/discovery/client-urls-projection.ts +205 -16
- package/src/vite/discovery/discover-routers.ts +2 -2
- package/src/vite/discovery/prerender-collection.ts +2 -6
- package/src/vite/discovery/shell-prerender-phase.ts +1 -5
- package/src/vite/discovery/state.ts +26 -0
- package/src/vite/plugins/client-urls-revalidate-hoist.ts +190 -77
- package/src/vite/plugins/expose-internal-ids.ts +46 -40
- package/src/vite/plugins/hoist/source.ts +8 -7
- package/src/vite/plugins/transition-when-hoist.ts +1 -0
- package/src/vite/plugins/version-plugin.ts +7 -0
- package/src/vite/plugins/version.d.ts +7 -0
- package/src/vite/plugins/virtual-entries.ts +20 -0
- package/src/vite/rango.ts +4 -1
- package/src/vite/router-discovery.ts +39 -7
- package/src/vite/utils/prerender-utils.ts +17 -0
package/src/rsc/full-payload.ts
CHANGED
|
@@ -15,6 +15,7 @@ import type { RequestContext } from "../server/request-context.js";
|
|
|
15
15
|
import type { HandleStore } from "../server/handle-store.js";
|
|
16
16
|
import type { Theme } from "../theme/types.js";
|
|
17
17
|
import {
|
|
18
|
+
pathKey,
|
|
18
19
|
rendersForSharedAudience,
|
|
19
20
|
rewriteTrail,
|
|
20
21
|
visitorUrl,
|
|
@@ -42,7 +43,7 @@ export function buildFullPayload(
|
|
|
42
43
|
): RscPayload {
|
|
43
44
|
return {
|
|
44
45
|
metadata: {
|
|
45
|
-
|
|
46
|
+
...payloadLocation(reqCtx, url),
|
|
46
47
|
routerId: ctx.router.id,
|
|
47
48
|
basename: ctx.router.basename,
|
|
48
49
|
segments: attachTransitionWhen(m.segments, reqCtx),
|
|
@@ -95,7 +96,8 @@ export function buildFullPayload(
|
|
|
95
96
|
* bar) agree on `usePathname()` and `useSearchParams()`. A render other
|
|
96
97
|
* visitors may be served shows `url`, the target, as a direct visit does: a
|
|
97
98
|
* shell capture, and a render the document cache stores (its key is the
|
|
98
|
-
* target)
|
|
99
|
+
* target), except a visitor-keyed shell capture, whose key covers the
|
|
100
|
+
* visitor's URL. Any other render that shows the visitor's URL marks
|
|
99
101
|
* `_payloadVisitorLocation`, and the document cache does not store it.
|
|
100
102
|
*/
|
|
101
103
|
export function renderedLocation(
|
|
@@ -104,6 +106,7 @@ export function renderedLocation(
|
|
|
104
106
|
RequestContext<any>,
|
|
105
107
|
| "_variables"
|
|
106
108
|
| "_shellCaptureRun"
|
|
109
|
+
| "_shellCaptureForVisitor"
|
|
107
110
|
| "_documentCacheRender"
|
|
108
111
|
| "_payloadVisitorLocation"
|
|
109
112
|
| "res"
|
|
@@ -111,13 +114,32 @@ export function renderedLocation(
|
|
|
111
114
|
| undefined,
|
|
112
115
|
url: URL,
|
|
113
116
|
): URL {
|
|
114
|
-
if (!reqCtx || !rewriteTrail(reqCtx)
|
|
115
|
-
|
|
117
|
+
if (!reqCtx || !rewriteTrail(reqCtx)) return url;
|
|
118
|
+
if (rendersForSharedAudience(reqCtx)) {
|
|
119
|
+
return reqCtx._shellCaptureForVisitor ? visitorUrl(reqCtx, url) : url;
|
|
116
120
|
}
|
|
117
121
|
reqCtx._payloadVisitorLocation = true;
|
|
118
122
|
return visitorUrl(reqCtx, url);
|
|
119
123
|
}
|
|
120
124
|
|
|
125
|
+
/**
|
|
126
|
+
* A payload's `pathname` (renderedLocation) and, when middleware rewrote the
|
|
127
|
+
* request, `rewriteTarget`: the path and search the route rendered (the
|
|
128
|
+
* trail's last URL). The browser keeps it per history entry and sends it
|
|
129
|
+
* back as `_rsc_rt`, so the next request from the page diffs against the
|
|
130
|
+
* route that rendered it (navigation-snapshot.ts resolveNavigation, #1144).
|
|
131
|
+
*/
|
|
132
|
+
export function payloadLocation(
|
|
133
|
+
reqCtx: Parameters<typeof renderedLocation>[0],
|
|
134
|
+
url: URL,
|
|
135
|
+
): { pathname: string; rewriteTarget?: string } {
|
|
136
|
+
const pathname = renderedLocation(reqCtx, url).pathname;
|
|
137
|
+
const trail = rewriteTrail(reqCtx);
|
|
138
|
+
return trail
|
|
139
|
+
? { pathname, rewriteTarget: pathKey(new URL(trail[trail.length - 1]!)) }
|
|
140
|
+
: { pathname };
|
|
141
|
+
}
|
|
142
|
+
|
|
121
143
|
/**
|
|
122
144
|
* The theme a document payload carries as `metadata.initialTheme` (the
|
|
123
145
|
* ThemeProvider's first state), read with no identity guard: it is the
|
package/src/rsc/handler.ts
CHANGED
|
@@ -18,7 +18,8 @@ import {
|
|
|
18
18
|
captureRequestEntryContext,
|
|
19
19
|
} from "../server/request-context.js";
|
|
20
20
|
import { REWRITE_HEADER, runRewritePasses } from "./rewrite-passes.js";
|
|
21
|
-
import {
|
|
21
|
+
import { rewriteTrail } from "../server/rewrite-trail.js";
|
|
22
|
+
import { payloadLocation } from "./full-payload.js";
|
|
22
23
|
import * as rscDeps from "@vitejs/plugin-rsc/rsc/server";
|
|
23
24
|
import type {
|
|
24
25
|
RscPayload,
|
|
@@ -27,6 +28,7 @@ import type {
|
|
|
27
28
|
SSRModule,
|
|
28
29
|
} from "./types.js";
|
|
29
30
|
import {
|
|
31
|
+
actionRenderRequest,
|
|
30
32
|
createReloadResponse,
|
|
31
33
|
createResponseWithMergedHeaders,
|
|
32
34
|
finalizeResponse,
|
|
@@ -79,6 +81,7 @@ import type { SegmentCacheStore } from "../cache/types.js";
|
|
|
79
81
|
import { buildRouterTrieFromUrlpatterns } from "./manifest-init.js";
|
|
80
82
|
import { handleProgressiveEnhancement } from "./progressive-enhancement.js";
|
|
81
83
|
import {
|
|
84
|
+
continuationForRewritePass,
|
|
82
85
|
executeServerAction,
|
|
83
86
|
revalidateAfterAction,
|
|
84
87
|
type ActionContinuation,
|
|
@@ -162,12 +165,8 @@ export function createRSCHandler<
|
|
|
162
165
|
TRoutes extends Record<string, string> = Record<string, string>,
|
|
163
166
|
>(options: CreateRSCHandlerOptions<TEnv, TRoutes>) {
|
|
164
167
|
const { router, nonce: nonceProvider } = options;
|
|
165
|
-
//
|
|
166
|
-
|
|
167
|
-
const versions = resolveRouterVersions(
|
|
168
|
-
router.id,
|
|
169
|
-
options.version ?? router.version,
|
|
170
|
-
);
|
|
168
|
+
// The router's own pair from the build.
|
|
169
|
+
const versions = resolveRouterVersions(router.id);
|
|
171
170
|
// Everything the handler itself versions is tied to the HTML a tab holds:
|
|
172
171
|
// payload metadata (what the browser echoes as `_rsc_v`), the reload check,
|
|
173
172
|
// shell stamping and gating, the rango state value. The data version is only
|
|
@@ -545,7 +544,7 @@ export function createRSCHandler<
|
|
|
545
544
|
resolvedPrerender = {
|
|
546
545
|
config: prerenderConfig,
|
|
547
546
|
routerId: router.id,
|
|
548
|
-
version: resolvePrerenderVersion(router.id
|
|
547
|
+
version: resolvePrerenderVersion(router.id),
|
|
549
548
|
};
|
|
550
549
|
}
|
|
551
550
|
}
|
|
@@ -973,6 +972,32 @@ export function createRSCHandler<
|
|
|
973
972
|
// "middleware-short-circuit").
|
|
974
973
|
getRequestContext()._requestMode = plan.mode;
|
|
975
974
|
|
|
975
|
+
// A rewritten pass of an action must reach the branch that runs it: the
|
|
976
|
+
// one that ran it re-renders from its outcome, and a JS action a global
|
|
977
|
+
// rewrite moved before it ran runs only on the action branch. Any other
|
|
978
|
+
// plan would serve the POST as a fresh request or drop the action; a
|
|
979
|
+
// reload (version mismatch, app switch) does neither.
|
|
980
|
+
const passCtx = getRequestContext();
|
|
981
|
+
const actionRan = passCtx._actionRan;
|
|
982
|
+
const actionMode = actionRan
|
|
983
|
+
? actionRan.kind === "action"
|
|
984
|
+
? "action"
|
|
985
|
+
: "pe-render"
|
|
986
|
+
: url.searchParams.has("_rsc_action") && rewriteTrail(passCtx)
|
|
987
|
+
? "action"
|
|
988
|
+
: undefined;
|
|
989
|
+
if (
|
|
990
|
+
actionMode &&
|
|
991
|
+
plan.mode !== actionMode &&
|
|
992
|
+
plan.mode !== "version-mismatch" &&
|
|
993
|
+
plan.mode !== "app-switch"
|
|
994
|
+
) {
|
|
995
|
+
throw new Error(
|
|
996
|
+
`A rewrite of a server action or form post must target a page ` +
|
|
997
|
+
`route; ${url.pathname} ${plan.mode === "response" ? "is a response route" : plan.mode === "redirect" ? "redirects" : "is not one"}.`,
|
|
998
|
+
);
|
|
999
|
+
}
|
|
1000
|
+
|
|
976
1001
|
// ---- 2. Terminal plans (no execution needed) ----
|
|
977
1002
|
if (plan.mode === "redirect") {
|
|
978
1003
|
// Redirects are handled by the pipeline (match/matchPartial),
|
|
@@ -1224,51 +1249,64 @@ export function createRSCHandler<
|
|
|
1224
1249
|
|
|
1225
1250
|
// ---- Action: execute action, then revalidate wrapped in route middleware ----
|
|
1226
1251
|
if (plan.mode === "action") {
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
//
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1252
|
+
const reqCtx = getRequestContext();
|
|
1253
|
+
const ran = reqCtx._actionRan;
|
|
1254
|
+
let actionContinuation: ActionContinuation;
|
|
1255
|
+
if (ran?.kind === "action") {
|
|
1256
|
+
// A rewrite pass after the action ran: never run it again.
|
|
1257
|
+
actionContinuation = await continuationForRewritePass(
|
|
1258
|
+
handlerCtx,
|
|
1259
|
+
request,
|
|
1260
|
+
env,
|
|
1261
|
+
ran.continuation,
|
|
1262
|
+
);
|
|
1263
|
+
} else {
|
|
1264
|
+
try {
|
|
1265
|
+
// Instrument the action execution as its own phase (action:<actionId> +
|
|
1266
|
+
// rango.action), so a POST shows the mutation time AND which action ran,
|
|
1267
|
+
// not just the downstream revalidation render. The action's own
|
|
1268
|
+
// loaders/fetches nest under rango.action.
|
|
1269
|
+
const actionOutcome = await withTimeout(
|
|
1270
|
+
observePhase(PHASES.action(plan.actionId), () =>
|
|
1271
|
+
executeServerAction(
|
|
1272
|
+
handlerCtx,
|
|
1273
|
+
request,
|
|
1274
|
+
env,
|
|
1275
|
+
url,
|
|
1276
|
+
plan.actionId,
|
|
1277
|
+
handleStore,
|
|
1278
|
+
),
|
|
1279
|
+
),
|
|
1280
|
+
router.timeouts.actionMs,
|
|
1281
|
+
"action",
|
|
1282
|
+
);
|
|
1283
|
+
if (actionOutcome.timedOut) {
|
|
1284
|
+
return handleTimeoutResponse(
|
|
1237
1285
|
request,
|
|
1238
1286
|
env,
|
|
1239
1287
|
url,
|
|
1288
|
+
"action",
|
|
1289
|
+
actionOutcome.durationMs,
|
|
1290
|
+
plan.route.routeKey,
|
|
1240
1291
|
plan.actionId,
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1292
|
+
);
|
|
1293
|
+
}
|
|
1294
|
+
const result = actionOutcome.result;
|
|
1295
|
+
// Response means redirect or error boundary — done.
|
|
1296
|
+
if (result instanceof Response) return result;
|
|
1297
|
+
actionContinuation = result;
|
|
1298
|
+
reqCtx._actionRan = { kind: "action", continuation: result };
|
|
1299
|
+
} catch (error) {
|
|
1300
|
+
callOnError(error, "action", {
|
|
1249
1301
|
request,
|
|
1250
|
-
env,
|
|
1251
1302
|
url,
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
);
|
|
1303
|
+
env,
|
|
1304
|
+
actionId: plan.actionId,
|
|
1305
|
+
handledByBoundary: false,
|
|
1306
|
+
});
|
|
1307
|
+
console.error(`[RSC] Action error:`, error);
|
|
1308
|
+
throw error;
|
|
1257
1309
|
}
|
|
1258
|
-
const result = actionOutcome.result;
|
|
1259
|
-
// Response means redirect or error boundary — done.
|
|
1260
|
-
if (result instanceof Response) return result;
|
|
1261
|
-
actionContinuation = result;
|
|
1262
|
-
} catch (error) {
|
|
1263
|
-
callOnError(error, "action", {
|
|
1264
|
-
request,
|
|
1265
|
-
url,
|
|
1266
|
-
env,
|
|
1267
|
-
actionId: plan.actionId,
|
|
1268
|
-
handledByBoundary: false,
|
|
1269
|
-
});
|
|
1270
|
-
console.error(`[RSC] Action error:`, error);
|
|
1271
|
-
throw error;
|
|
1272
1310
|
}
|
|
1273
1311
|
|
|
1274
1312
|
// Revalidation render wrapped in route middleware.
|
|
@@ -1423,7 +1461,7 @@ export function createRSCHandler<
|
|
|
1423
1461
|
|
|
1424
1462
|
const payload: RscPayload = {
|
|
1425
1463
|
metadata: {
|
|
1426
|
-
|
|
1464
|
+
...payloadLocation(getRequestContext(), url),
|
|
1427
1465
|
routerId: router.id,
|
|
1428
1466
|
basename: router.basename,
|
|
1429
1467
|
segments: [notFoundSegment],
|
|
@@ -1527,7 +1565,7 @@ export function createRSCHandler<
|
|
|
1527
1565
|
if (routeMiddleware.length > 0) {
|
|
1528
1566
|
const mwResponse = await executeMiddleware(
|
|
1529
1567
|
buildRouteMiddlewareEntries<TEnv>(routeMiddleware),
|
|
1530
|
-
request,
|
|
1568
|
+
actionContinuation ? actionRenderRequest(request) : request,
|
|
1531
1569
|
env,
|
|
1532
1570
|
variables,
|
|
1533
1571
|
renderHandler,
|
package/src/rsc/helpers.ts
CHANGED
|
@@ -25,6 +25,8 @@ import type { MiddlewareEntry, MiddlewareFn } from "../router/middleware.js";
|
|
|
25
25
|
import { formatCacheSignalHeader } from "../router/telemetry.js";
|
|
26
26
|
import type { RscPayload } from "./types.js";
|
|
27
27
|
import { rewriteTrail, visitorUrl } from "../server/rewrite-trail.js";
|
|
28
|
+
import { guardRequestHeaders } from "../server/cookie-store.js";
|
|
29
|
+
import { bodylessRequest } from "../server/request-headers.js";
|
|
28
30
|
import type { HandlerContext } from "./handler-context.js";
|
|
29
31
|
|
|
30
32
|
/**
|
|
@@ -380,6 +382,17 @@ export function buildRouteMiddlewareEntries<TEnv>(
|
|
|
380
382
|
}));
|
|
381
383
|
}
|
|
382
384
|
|
|
385
|
+
/**
|
|
386
|
+
* The request route middleware sees around the render after a server action
|
|
387
|
+
* or a no-JS form post: the page's GET (URL, headers without body framing or
|
|
388
|
+
* content-type, signal, workerd `cf`; no body). Route middleware never wraps the action,
|
|
389
|
+
* only that render, so it decides as for a GET of the page: a rewrite it
|
|
390
|
+
* skips for mutations still moves the render to its target (#1145).
|
|
391
|
+
*/
|
|
392
|
+
export function actionRenderRequest(request: Request): Request {
|
|
393
|
+
return guardRequestHeaders(bodylessRequest(request.url, request, "GET"));
|
|
394
|
+
}
|
|
395
|
+
|
|
383
396
|
/**
|
|
384
397
|
* Merge stub headers from the request context onto an existing Response in
|
|
385
398
|
* place, then drain onResponse callbacks. Used when a Response cannot flow
|
package/src/rsc/manifest-init.ts
CHANGED
|
@@ -6,8 +6,12 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import {
|
|
9
|
+
ensureRouterManifest,
|
|
9
10
|
getGlobalRouteMap,
|
|
11
|
+
getRouterManifest,
|
|
12
|
+
getRouterTrie,
|
|
10
13
|
hasCachedManifest,
|
|
14
|
+
isRouterTrieAuthoritative,
|
|
11
15
|
setCachedManifest,
|
|
12
16
|
setRouterManifest,
|
|
13
17
|
setRouterTrie,
|
|
@@ -15,6 +19,55 @@ import {
|
|
|
15
19
|
} from "../route-map-builder.js";
|
|
16
20
|
import { mergeFullManifests } from "../build/merge-full-manifests.js";
|
|
17
21
|
import { isTransitionWhenValidationEnabled } from "../transition-when-ref.js";
|
|
22
|
+
import type { TrieNode } from "../router/route-trie-builder.js";
|
|
23
|
+
|
|
24
|
+
// A zero-route router builds no trie; recording that keeps it from rebuilding.
|
|
25
|
+
const NO_TRIE: unique symbol = Symbol("rango.noTrie");
|
|
26
|
+
|
|
27
|
+
// The trie each router object last installed. Router ids need not be unique
|
|
28
|
+
// across instances (a test helper calling createRouter({ id }) per test), and
|
|
29
|
+
// the per-id trie map cannot tell which instance a trie came from.
|
|
30
|
+
const installedTries = new WeakMap<object, TrieNode | typeof NO_TRIE>();
|
|
31
|
+
|
|
32
|
+
/** True when the trie installed for `router.id` was built from this router. */
|
|
33
|
+
export function isRouterTrieBuiltFrom(router: { id?: string }): boolean {
|
|
34
|
+
if (router.id === undefined) return false;
|
|
35
|
+
const installed = installedTries.get(router);
|
|
36
|
+
return (
|
|
37
|
+
installed !== undefined &&
|
|
38
|
+
installed === (getRouterTrie(router.id) ?? NO_TRIE)
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** The router fields ensureRouterTrie reads. */
|
|
43
|
+
export interface TrieSource {
|
|
44
|
+
id?: string;
|
|
45
|
+
urlpatterns?: unknown;
|
|
46
|
+
routeMap: Record<string, unknown>;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Install the trie `router` matches through, as a request finds it: the build
|
|
51
|
+
* manifest's trie when one is registered for its id, otherwise one built from
|
|
52
|
+
* its routes the way the dev handler builds it (rsc/handler.ts). Ids can repeat
|
|
53
|
+
* across router objects (tests), and another object's trie names other routes,
|
|
54
|
+
* so a trie this object did not build is rebuilt. Seeds the per-router manifest
|
|
55
|
+
* from `router.routeMap` when neither source set one, for reverse(). testing's
|
|
56
|
+
* dispatch matches through it, never the regex fallback alone (#1104, #1107).
|
|
57
|
+
*/
|
|
58
|
+
export async function ensureRouterTrie(router: TrieSource): Promise<void> {
|
|
59
|
+
const id = router.id;
|
|
60
|
+
if (!id) return;
|
|
61
|
+
if (!isRouterTrieAuthoritative(id) && !isRouterTrieBuiltFrom(router)) {
|
|
62
|
+
await ensureRouterManifest(id);
|
|
63
|
+
if (router.urlpatterns && !isRouterTrieAuthoritative(id)) {
|
|
64
|
+
await buildRouterTrieFromUrlpatterns(router);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (!getRouterManifest(id)) {
|
|
68
|
+
setRouterManifest(id, router.routeMap as Record<string, string>);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
18
71
|
|
|
19
72
|
/**
|
|
20
73
|
* Build a fresh route trie from the router's URL-pattern mounts and store it in
|
|
@@ -63,9 +116,9 @@ export async function buildRouterTrieFromUrlpatterns(
|
|
|
63
116
|
// are no routes.
|
|
64
117
|
const { buildPerRouterTrie } = await import("../build/route-trie.js");
|
|
65
118
|
const trie = buildPerRouterTrie(generated);
|
|
66
|
-
if (trie)
|
|
67
|
-
|
|
68
|
-
|
|
119
|
+
if (trie) setRouterTrie(router.id, trie);
|
|
120
|
+
// Re-read: setRouterTrie stores a copy (nullPrototypeStaticChildren).
|
|
121
|
+
installedTries.set(router, trie ? getRouterTrie(router.id)! : NO_TRIE);
|
|
69
122
|
setRouterManifest(router.id, generated.routeManifest);
|
|
70
123
|
|
|
71
124
|
// Match the production discovery path: precompute leaf-include entries so the
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A prefetch whose render reported an error is not kept (#1170).
|
|
3
|
+
*
|
|
4
|
+
* WHY: its 200 left with the headers before a streamed part failed, so the
|
|
5
|
+
* client stored the error boundary as a healthy prefetch, served it to a
|
|
6
|
+
* click and respawned it. The status cannot change any more, and the body
|
|
7
|
+
* cannot end with an error: workerd delivers an errored body as a clean end,
|
|
8
|
+
* and in Chromium the error drops the rows a click that adopted the prefetch
|
|
9
|
+
* has not decoded yet (a Flight "network error" in place of the page).
|
|
10
|
+
*
|
|
11
|
+
* So the body ends cleanly with one more row, PREFETCH_FAILED_ROW, which the
|
|
12
|
+
* Flight decoder skips and teeWithCompletion (browser/response-adapter.ts)
|
|
13
|
+
* reads as a stream that did not end cleanly: the client evicts the prefetch
|
|
14
|
+
* and never respawns it. Prefetch responses carry no Cache-Control, so the
|
|
15
|
+
* browser HTTP cache keeps none of it either.
|
|
16
|
+
*
|
|
17
|
+
* The errors are the render's `_renderErrors`: a Flight error row, a streamed
|
|
18
|
+
* handler recovered into its boundary, a loader that threw (redirect() and
|
|
19
|
+
* notFound() are not errors). Each is recorded before Flight writes the row
|
|
20
|
+
* that carries it, so the list is complete once Flight closes the stream.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { PREFETCH_FAILED_ROW } from "../prefetch-markers.js";
|
|
24
|
+
|
|
25
|
+
export function markPrefetchOnRenderError(
|
|
26
|
+
response: Response,
|
|
27
|
+
renderErrors: readonly unknown[],
|
|
28
|
+
): Response {
|
|
29
|
+
if (!response.body) return response;
|
|
30
|
+
const marked = new TransformStream<Uint8Array, Uint8Array>({
|
|
31
|
+
flush(controller) {
|
|
32
|
+
if (renderErrors.length > 0) {
|
|
33
|
+
controller.enqueue(new TextEncoder().encode(PREFETCH_FAILED_ROW));
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
return new Response(response.body.pipeThrough(marked), response);
|
|
38
|
+
}
|