@rangojs/router 0.24.3 → 0.25.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-shell-frame.d.ts +2 -0
- package/dist/types/cache/types.d.ts +8 -0
- package/dist/types/client-urls/client-root.d.ts +4 -1
- package/dist/types/client-urls/client-urls.d.ts +28 -2
- package/dist/types/client-urls/navigation.d.ts +6 -8
- package/dist/types/client-urls/revalidate-registry.d.ts +1 -1
- package/dist/types/client-urls/server-projection.d.ts +20 -5
- package/dist/types/client-urls/types.d.ts +35 -17
- 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/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/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 -6
- package/dist/types/router/router-options.d.ts +4 -7
- 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 +19 -2
- package/dist/types/router/tracing.d.ts +2 -2
- 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 +10 -1
- package/dist/types/rsc/shell-capture.d.ts +7 -0
- package/dist/types/rsc/types.d.ts +5 -0
- package/dist/types/segment-system.d.ts +15 -0
- package/dist/types/server/context.d.ts +64 -4
- package/dist/types/server/request-context.d.ts +41 -4
- package/dist/types/server/request-headers.d.ts +12 -0
- package/dist/types/server/rewrite-trail.d.ts +8 -1
- 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/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 +41 -4
- 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 +4 -3
- package/dist/types/types/route-entry.d.ts +2 -1
- package/dist/types/urls/path-helper-types.d.ts +4 -2
- package/dist/types/urls/path-helper.d.ts +5 -0
- 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 +6816 -6547
- 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/caching/SKILL.md +3 -1
- package/skills/catalog.json +12 -0
- package/skills/client-urls/SKILL.md +195 -32
- 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/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 +32 -10
- 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 +43 -8
- package/skills/prerender/SKILL.md +1 -2
- package/skills/rango/SKILL.md +3 -0
- package/skills/response-routes/SKILL.md +4 -1
- 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/response-routes.md +4 -1
- package/skills/testing/serve-shell-request.md +85 -21
- package/skills/ux-architecture/SKILL.md +980 -0
- 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 +119 -98
- 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-reverse.ts +4 -17
- 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 +2 -0
- package/src/cache/cf/cf-shell-frame.ts +3 -0
- package/src/cache/types.ts +8 -0
- package/src/cache/vercel/vercel-cache-store.ts +6 -1
- package/src/client-urls/client-root.tsx +10 -5
- package/src/client-urls/client-urls.ts +246 -166
- package/src/client-urls/navigation.ts +45 -38
- package/src/client-urls/revalidate-registry.ts +12 -3
- package/src/client-urls/server-projection.ts +286 -75
- package/src/client-urls/types.ts +40 -16
- package/src/client.rsc.tsx +11 -1
- package/src/client.tsx +9 -1
- package/src/decode-loader-results.ts +64 -31
- 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/reverse.ts +3 -3
- package/src/route-content-wrapper.tsx +19 -17
- package/src/route-definition/dsl-helpers.ts +27 -7
- 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 +6 -8
- 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 -7
- package/src/router/router-options.ts +4 -7
- package/src/router/segment-resolution/fresh.ts +6 -2
- package/src/router/segment-resolution/helpers.ts +7 -1
- 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 +37 -2
- package/src/router/tracing.ts +2 -2
- package/src/router.ts +2 -9
- package/src/rsc/full-payload.ts +26 -4
- package/src/rsc/handler.ts +83 -41
- 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 +22 -0
- package/src/rsc/shell-capture.ts +11 -0
- package/src/rsc/types.ts +5 -0
- package/src/segment-system.tsx +125 -119
- package/src/server/context.ts +96 -5
- package/src/server/handle-store.ts +3 -2
- package/src/server/request-context.ts +42 -3
- 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/dispatch.ts +4 -5
- 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/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 +77 -4
- 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 +4 -3
- 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/urls/path-helper.ts +16 -3
- 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/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/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,
|
|
@@ -973,6 +976,32 @@ export function createRSCHandler<
|
|
|
973
976
|
// "middleware-short-circuit").
|
|
974
977
|
getRequestContext()._requestMode = plan.mode;
|
|
975
978
|
|
|
979
|
+
// A rewritten pass of an action must reach the branch that runs it: the
|
|
980
|
+
// one that ran it re-renders from its outcome, and a JS action a global
|
|
981
|
+
// rewrite moved before it ran runs only on the action branch. Any other
|
|
982
|
+
// plan would serve the POST as a fresh request or drop the action; a
|
|
983
|
+
// reload (version mismatch, app switch) does neither.
|
|
984
|
+
const passCtx = getRequestContext();
|
|
985
|
+
const actionRan = passCtx._actionRan;
|
|
986
|
+
const actionMode = actionRan
|
|
987
|
+
? actionRan.kind === "action"
|
|
988
|
+
? "action"
|
|
989
|
+
: "pe-render"
|
|
990
|
+
: url.searchParams.has("_rsc_action") && rewriteTrail(passCtx)
|
|
991
|
+
? "action"
|
|
992
|
+
: undefined;
|
|
993
|
+
if (
|
|
994
|
+
actionMode &&
|
|
995
|
+
plan.mode !== actionMode &&
|
|
996
|
+
plan.mode !== "version-mismatch" &&
|
|
997
|
+
plan.mode !== "app-switch"
|
|
998
|
+
) {
|
|
999
|
+
throw new Error(
|
|
1000
|
+
`A rewrite of a server action or form post must target a page ` +
|
|
1001
|
+
`route; ${url.pathname} ${plan.mode === "response" ? "is a response route" : plan.mode === "redirect" ? "redirects" : "is not one"}.`,
|
|
1002
|
+
);
|
|
1003
|
+
}
|
|
1004
|
+
|
|
976
1005
|
// ---- 2. Terminal plans (no execution needed) ----
|
|
977
1006
|
if (plan.mode === "redirect") {
|
|
978
1007
|
// Redirects are handled by the pipeline (match/matchPartial),
|
|
@@ -1224,51 +1253,64 @@ export function createRSCHandler<
|
|
|
1224
1253
|
|
|
1225
1254
|
// ---- Action: execute action, then revalidate wrapped in route middleware ----
|
|
1226
1255
|
if (plan.mode === "action") {
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
//
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1256
|
+
const reqCtx = getRequestContext();
|
|
1257
|
+
const ran = reqCtx._actionRan;
|
|
1258
|
+
let actionContinuation: ActionContinuation;
|
|
1259
|
+
if (ran?.kind === "action") {
|
|
1260
|
+
// A rewrite pass after the action ran: never run it again.
|
|
1261
|
+
actionContinuation = await continuationForRewritePass(
|
|
1262
|
+
handlerCtx,
|
|
1263
|
+
request,
|
|
1264
|
+
env,
|
|
1265
|
+
ran.continuation,
|
|
1266
|
+
);
|
|
1267
|
+
} else {
|
|
1268
|
+
try {
|
|
1269
|
+
// Instrument the action execution as its own phase (action:<actionId> +
|
|
1270
|
+
// rango.action), so a POST shows the mutation time AND which action ran,
|
|
1271
|
+
// not just the downstream revalidation render. The action's own
|
|
1272
|
+
// loaders/fetches nest under rango.action.
|
|
1273
|
+
const actionOutcome = await withTimeout(
|
|
1274
|
+
observePhase(PHASES.action(plan.actionId), () =>
|
|
1275
|
+
executeServerAction(
|
|
1276
|
+
handlerCtx,
|
|
1277
|
+
request,
|
|
1278
|
+
env,
|
|
1279
|
+
url,
|
|
1280
|
+
plan.actionId,
|
|
1281
|
+
handleStore,
|
|
1282
|
+
),
|
|
1283
|
+
),
|
|
1284
|
+
router.timeouts.actionMs,
|
|
1285
|
+
"action",
|
|
1286
|
+
);
|
|
1287
|
+
if (actionOutcome.timedOut) {
|
|
1288
|
+
return handleTimeoutResponse(
|
|
1237
1289
|
request,
|
|
1238
1290
|
env,
|
|
1239
1291
|
url,
|
|
1292
|
+
"action",
|
|
1293
|
+
actionOutcome.durationMs,
|
|
1294
|
+
plan.route.routeKey,
|
|
1240
1295
|
plan.actionId,
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1296
|
+
);
|
|
1297
|
+
}
|
|
1298
|
+
const result = actionOutcome.result;
|
|
1299
|
+
// Response means redirect or error boundary — done.
|
|
1300
|
+
if (result instanceof Response) return result;
|
|
1301
|
+
actionContinuation = result;
|
|
1302
|
+
reqCtx._actionRan = { kind: "action", continuation: result };
|
|
1303
|
+
} catch (error) {
|
|
1304
|
+
callOnError(error, "action", {
|
|
1249
1305
|
request,
|
|
1250
|
-
env,
|
|
1251
1306
|
url,
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
);
|
|
1307
|
+
env,
|
|
1308
|
+
actionId: plan.actionId,
|
|
1309
|
+
handledByBoundary: false,
|
|
1310
|
+
});
|
|
1311
|
+
console.error(`[RSC] Action error:`, error);
|
|
1312
|
+
throw error;
|
|
1257
1313
|
}
|
|
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
1314
|
}
|
|
1273
1315
|
|
|
1274
1316
|
// Revalidation render wrapped in route middleware.
|
|
@@ -1423,7 +1465,7 @@ export function createRSCHandler<
|
|
|
1423
1465
|
|
|
1424
1466
|
const payload: RscPayload = {
|
|
1425
1467
|
metadata: {
|
|
1426
|
-
|
|
1468
|
+
...payloadLocation(getRequestContext(), url),
|
|
1427
1469
|
routerId: router.id,
|
|
1428
1470
|
basename: router.basename,
|
|
1429
1471
|
segments: [notFoundSegment],
|
|
@@ -1527,7 +1569,7 @@ export function createRSCHandler<
|
|
|
1527
1569
|
if (routeMiddleware.length > 0) {
|
|
1528
1570
|
const mwResponse = await executeMiddleware(
|
|
1529
1571
|
buildRouteMiddlewareEntries<TEnv>(routeMiddleware),
|
|
1530
|
-
request,
|
|
1572
|
+
actionContinuation ? actionRenderRequest(request) : request,
|
|
1531
1573
|
env,
|
|
1532
1574
|
variables,
|
|
1533
1575
|
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
|
+
}
|