@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/router/middleware.ts
CHANGED
|
@@ -34,11 +34,7 @@ import { assertNonCacheableReadAllowed } from "../server/context.js";
|
|
|
34
34
|
import { readGuardedTheme } from "../server/cookie-store.js";
|
|
35
35
|
import { defineSlot, slotAccessor } from "../server/slot-accessors.js";
|
|
36
36
|
import type { Theme } from "../theme/types.js";
|
|
37
|
-
import {
|
|
38
|
-
isRewrite,
|
|
39
|
-
rewriteNotSupported,
|
|
40
|
-
type Rewrite,
|
|
41
|
-
} from "../route-definition/rewrite.js";
|
|
37
|
+
import { isRewrite, type Rewrite } from "../route-definition/rewrite.js";
|
|
42
38
|
import { defineRewrittenFrom } from "../server/rewrite-trail.js";
|
|
43
39
|
|
|
44
40
|
// Re-export types consumed through this module's path.
|
|
@@ -324,8 +320,8 @@ export function createMiddlewareContext<TEnv>(
|
|
|
324
320
|
waitUntil: reqCtx ? reqCtx.waitUntil.bind(reqCtx) : fireAndForgetWaitUntil,
|
|
325
321
|
|
|
326
322
|
get: ((keyOrVar: any) => {
|
|
327
|
-
// "use cache" only:
|
|
328
|
-
//
|
|
323
|
+
// "use cache" only: middleware runs before any cache() subtree
|
|
324
|
+
// renders, so the cache() read guard never applies to it.
|
|
329
325
|
if (
|
|
330
326
|
isNonCacheable(variables, keyOrVar) &&
|
|
331
327
|
getCacheExecScope() !== undefined
|
|
@@ -517,13 +513,11 @@ function mergeReqCtxStub(
|
|
|
517
513
|
// Clone `base` with stub headers merged into a fresh Headers (the clone keeps
|
|
518
514
|
// the body mutable for post-next() modifications). Set-Cookie is always
|
|
519
515
|
// appended; other headers obey stubOverridesNonCookie (see mergeStubHeaders).
|
|
520
|
-
//
|
|
521
|
-
// short-circuit path passes false (its reqCtx headers are not merged here),
|
|
522
|
-
// which is the one deliberate divergence between the call sites.
|
|
516
|
+
// RequestContext stub cookies/headers are folded in last.
|
|
523
517
|
function mergeResponse(
|
|
524
518
|
base: Response,
|
|
525
519
|
stub: Headers,
|
|
526
|
-
opts: { stubOverridesNonCookie: boolean
|
|
520
|
+
opts: { stubOverridesNonCookie: boolean },
|
|
527
521
|
): Response {
|
|
528
522
|
const mergedHeaders = new Headers(base.headers);
|
|
529
523
|
// The reserved external-redirect marker is never a trust signal and must never
|
|
@@ -532,9 +526,7 @@ function mergeResponse(
|
|
|
532
526
|
// guard would not touch) to the client. The opt-in is the out-of-band brand.
|
|
533
527
|
mergedHeaders.delete(EXTERNAL_REDIRECT_MARKER);
|
|
534
528
|
mergeStubHeaders(mergedHeaders, stub, opts.stubOverridesNonCookie);
|
|
535
|
-
|
|
536
|
-
mergeReqCtxStub(mergedHeaders, _getRequestContext());
|
|
537
|
-
}
|
|
529
|
+
mergeReqCtxStub(mergedHeaders, _getRequestContext());
|
|
538
530
|
const merged = new Response(base.body, {
|
|
539
531
|
status: base.status,
|
|
540
532
|
statusText: base.statusText,
|
|
@@ -597,7 +589,6 @@ export async function executeMiddleware<TEnv>(
|
|
|
597
589
|
// and reqCtx stub headers are merged in.
|
|
598
590
|
responseHolder.response = mergeResponse(response, stubResponse.headers, {
|
|
599
591
|
stubOverridesNonCookie: true,
|
|
600
|
-
mergeReqCtx: true,
|
|
601
592
|
});
|
|
602
593
|
|
|
603
594
|
return responseHolder.response;
|
|
@@ -740,7 +731,6 @@ export async function executeMiddleware<TEnv>(
|
|
|
740
731
|
// (stubOverridesNonCookie=false); reqCtx stub headers still merge in.
|
|
741
732
|
const merged = mergeResponse(result, stubResponse.headers, {
|
|
742
733
|
stubOverridesNonCookie: false,
|
|
743
|
-
mergeReqCtx: true,
|
|
744
734
|
});
|
|
745
735
|
responseHolder.response = merged;
|
|
746
736
|
return merged;
|
|
@@ -796,137 +786,6 @@ export async function executeMiddleware<TEnv>(
|
|
|
796
786
|
return finalResponse;
|
|
797
787
|
}
|
|
798
788
|
|
|
799
|
-
/**
|
|
800
|
-
* Execute middleware for intercepts (simplified execution)
|
|
801
|
-
*
|
|
802
|
-
* Intercepts use a shared stubResponse from the request context. This function:
|
|
803
|
-
* - Runs middleware in sequence with a simple next() chain
|
|
804
|
-
* - Returns Response if any middleware short-circuits (returns OR throws a Response, or redirects, BEFORE next())
|
|
805
|
-
* - Returns null if all middleware calls next() - headers set after next() remain on stubResponse
|
|
806
|
-
*
|
|
807
|
-
* @param middlewares - Array of middleware functions
|
|
808
|
-
* @param request - Original request
|
|
809
|
-
* @param env - Environment bindings
|
|
810
|
-
* @param params - Route params
|
|
811
|
-
* @param variables - Shared variables object
|
|
812
|
-
* @param stubResponse - Response from request context for collecting headers/cookies
|
|
813
|
-
*/
|
|
814
|
-
export async function executeInterceptMiddleware<TEnv>(
|
|
815
|
-
middlewares: MiddlewareFn<TEnv>[],
|
|
816
|
-
request: Request,
|
|
817
|
-
env: TEnv,
|
|
818
|
-
params: Record<string, string>,
|
|
819
|
-
variables: Record<string, any>,
|
|
820
|
-
stubResponse: Response,
|
|
821
|
-
reverse?: (
|
|
822
|
-
name: string,
|
|
823
|
-
params?: Record<string, string>,
|
|
824
|
-
search?: Record<string, unknown>,
|
|
825
|
-
) => string,
|
|
826
|
-
): Promise<Response | null> {
|
|
827
|
-
if (middlewares.length === 0) {
|
|
828
|
-
return null;
|
|
829
|
-
}
|
|
830
|
-
|
|
831
|
-
let index = 0;
|
|
832
|
-
let earlyResponse: Response | null = null;
|
|
833
|
-
|
|
834
|
-
// Use provided stubResponse - headers/cookies set here will be merged by the caller
|
|
835
|
-
const responseHolder: ResponseHolder = { response: stubResponse };
|
|
836
|
-
|
|
837
|
-
const next = async (): Promise<Response> => {
|
|
838
|
-
if (index >= middlewares.length || earlyResponse) {
|
|
839
|
-
return stubResponse;
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
const ordinal = index;
|
|
843
|
-
const middleware = middlewares[index++];
|
|
844
|
-
const ctx = createMiddlewareContext(
|
|
845
|
-
request,
|
|
846
|
-
env,
|
|
847
|
-
params,
|
|
848
|
-
variables,
|
|
849
|
-
responseHolder,
|
|
850
|
-
reverse,
|
|
851
|
-
);
|
|
852
|
-
|
|
853
|
-
let nextCalled = false;
|
|
854
|
-
const guardedNext = (): Promise<Response> => {
|
|
855
|
-
if (nextCalled) {
|
|
856
|
-
throw new Error(
|
|
857
|
-
`[@rangojs/router] Intercept middleware called next() more than once.`,
|
|
858
|
-
);
|
|
859
|
-
}
|
|
860
|
-
nextCalled = true;
|
|
861
|
-
return next();
|
|
862
|
-
};
|
|
863
|
-
|
|
864
|
-
// Span-wrap each intercept middleware as rango.middleware (metric:false —
|
|
865
|
-
// intercept runs inside the render phase already metered by render:total, so
|
|
866
|
-
// it contributes a span but no separate perf metric). Bare MiddlewareFns
|
|
867
|
-
// have no pattern, so the label is scoped to "*" like a pattern-less entry.
|
|
868
|
-
const label = getMiddlewareMetricLabel(
|
|
869
|
-
{ handler: middleware, pattern: null } as MiddlewareEntry<TEnv>,
|
|
870
|
-
ordinal,
|
|
871
|
-
);
|
|
872
|
-
|
|
873
|
-
const result: Response | void = await observePhase(
|
|
874
|
-
PHASES.middleware(label),
|
|
875
|
-
async () => {
|
|
876
|
-
try {
|
|
877
|
-
return await middleware(ctx, guardedNext);
|
|
878
|
-
} catch (error) {
|
|
879
|
-
// Thrown Response is short-circuit control flow, parity with the
|
|
880
|
-
// explicit-return path below. Absorb it INSIDE the span so the tracing
|
|
881
|
-
// runner settles rango.middleware as success, not STATUS_ERROR (same
|
|
882
|
-
// reasoning as executeMiddleware's main chain). Real errors propagate.
|
|
883
|
-
if (error instanceof Response) return error;
|
|
884
|
-
if (isRewrite(error)) throw rewriteNotSupported(error, "intercept");
|
|
885
|
-
throw error;
|
|
886
|
-
}
|
|
887
|
-
},
|
|
888
|
-
);
|
|
889
|
-
if (isRewrite(result)) throw returnedRewriteError(middleware);
|
|
890
|
-
|
|
891
|
-
if (result instanceof Response) {
|
|
892
|
-
earlyResponse = result;
|
|
893
|
-
return result;
|
|
894
|
-
}
|
|
895
|
-
|
|
896
|
-
return stubResponse;
|
|
897
|
-
};
|
|
898
|
-
|
|
899
|
-
await next();
|
|
900
|
-
|
|
901
|
-
// Return early response if middleware short-circuited (returned Response BEFORE next())
|
|
902
|
-
if (earlyResponse) {
|
|
903
|
-
// Capture in const for TypeScript narrowing (earlyResponse is `let` which loses narrowing in callbacks)
|
|
904
|
-
const response: Response = earlyResponse;
|
|
905
|
-
|
|
906
|
-
// Merge any headers/cookies set on stub into the early response
|
|
907
|
-
let hasStubHeaders = false;
|
|
908
|
-
stubResponse.headers.forEach(() => {
|
|
909
|
-
hasStubHeaders = true;
|
|
910
|
-
});
|
|
911
|
-
|
|
912
|
-
if (hasStubHeaders) {
|
|
913
|
-
// Only fill in missing headers — the returned Response's explicit headers
|
|
914
|
-
// take precedence (stubOverridesNonCookie=false), matching executeMiddleware.
|
|
915
|
-
// mergeReqCtx=false: the intercept path deliberately does NOT merge reqCtx
|
|
916
|
-
// stub headers here (pinned by intercept-middleware-headers.test.ts).
|
|
917
|
-
return mergeResponse(response, stubResponse.headers, {
|
|
918
|
-
stubOverridesNonCookie: false,
|
|
919
|
-
mergeReqCtx: false,
|
|
920
|
-
});
|
|
921
|
-
}
|
|
922
|
-
return response;
|
|
923
|
-
}
|
|
924
|
-
|
|
925
|
-
// All middleware completed without short-circuit
|
|
926
|
-
// Headers/cookies set on stubResponse will be merged into the final response by the caller
|
|
927
|
-
return null;
|
|
928
|
-
}
|
|
929
|
-
|
|
930
789
|
/**
|
|
931
790
|
* Execute middleware chain for loaders (simpler signature)
|
|
932
791
|
*
|
|
@@ -4,6 +4,8 @@ import type { RouteMatchResult } from "./pattern-matching.js";
|
|
|
4
4
|
|
|
5
5
|
export interface NavigationSnapshot {
|
|
6
6
|
prevUrl: URL;
|
|
7
|
+
/** The visitor's URL of the page left, when `prevUrl` is its rewrite target. */
|
|
8
|
+
prevVisitorUrl: URL | null;
|
|
7
9
|
prevParams: Record<string, string>;
|
|
8
10
|
prevMatch: RouteMatchResult | null;
|
|
9
11
|
|
|
@@ -95,6 +97,35 @@ export function getInterceptSourceHeader(request: Request): string | null {
|
|
|
95
97
|
return requestHeaders(request).get("X-RSC-Router-Intercept-Source");
|
|
96
98
|
}
|
|
97
99
|
|
|
100
|
+
/**
|
|
101
|
+
* The URL the page left rendered, when middleware rewrote it there (#1144):
|
|
102
|
+
* `_rsc_rt`, the payload's `rewriteTarget` the browser sends back while
|
|
103
|
+
* `X-RSC-Router-Client-Path` names the visitor's URL. Matching the visitor's
|
|
104
|
+
* URL would find the rewriting route. Same origin only. It names the page
|
|
105
|
+
* left, never what renders, so a forged value runs or skips no middleware.
|
|
106
|
+
*/
|
|
107
|
+
function getShownRouteUrl(url: URL): URL | null {
|
|
108
|
+
const shown = url.searchParams.get("_rsc_rt");
|
|
109
|
+
if (!shown) return null;
|
|
110
|
+
try {
|
|
111
|
+
const parsed = new URL(shown, url.origin);
|
|
112
|
+
return parsed.origin === url.origin ? formSearch(parsed) : null;
|
|
113
|
+
} catch {
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* `url` with its query as URLSearchParams writes it, as the request's own URL
|
|
120
|
+
* has it: setting and stripping `_rsc*` params rewrote that one (`?q=a%2Cb`),
|
|
121
|
+
* while a URL the browser names keeps the address bar's (`?q=a,b`). Compared
|
|
122
|
+
* as sent, every navigation from such a page was a search change.
|
|
123
|
+
*/
|
|
124
|
+
function formSearch(url: URL): URL {
|
|
125
|
+
url.search = String(url.searchParams);
|
|
126
|
+
return url;
|
|
127
|
+
}
|
|
128
|
+
|
|
98
129
|
export async function resolveNavigation(
|
|
99
130
|
request: Request,
|
|
100
131
|
url: URL,
|
|
@@ -112,12 +143,15 @@ export async function resolveNavigation(
|
|
|
112
143
|
return null;
|
|
113
144
|
}
|
|
114
145
|
|
|
115
|
-
let
|
|
146
|
+
let visitor: URL;
|
|
116
147
|
try {
|
|
117
|
-
|
|
148
|
+
visitor = formSearch(new URL(previousUrl, url.origin));
|
|
118
149
|
} catch {
|
|
119
150
|
return null;
|
|
120
151
|
}
|
|
152
|
+
// An intercept's request diffs from its source, which _rsc_rt does not name.
|
|
153
|
+
const shown = interceptSourceUrl ? null : getShownRouteUrl(url);
|
|
154
|
+
const prevUrl = shown ?? visitor;
|
|
121
155
|
|
|
122
156
|
let interceptContextUrl: URL;
|
|
123
157
|
try {
|
|
@@ -154,6 +188,7 @@ export async function resolveNavigation(
|
|
|
154
188
|
|
|
155
189
|
return {
|
|
156
190
|
prevUrl,
|
|
191
|
+
prevVisitorUrl: shown && visitor,
|
|
157
192
|
prevParams,
|
|
158
193
|
prevMatch,
|
|
159
194
|
interceptContextUrl,
|
|
@@ -176,6 +211,7 @@ export function createNavigationSnapshot(
|
|
|
176
211
|
const defaultUrl = new URL("http://localhost/");
|
|
177
212
|
return {
|
|
178
213
|
prevUrl: defaultUrl,
|
|
214
|
+
prevVisitorUrl: null,
|
|
179
215
|
prevParams: {},
|
|
180
216
|
prevMatch: null,
|
|
181
217
|
interceptContextUrl: defaultUrl,
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Resolve the prefetch cache TTL once, at router init, into the
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* Cache-Control
|
|
2
|
+
* Resolve the prefetch cache TTL once, at router init, into the values the
|
|
3
|
+
* rest of the router consumes: seconds, and milliseconds for the client-side
|
|
4
|
+
* in-memory prefetch cache (shipped in payload metadata). Prefetch responses
|
|
5
|
+
* carry no Cache-Control: the browser HTTP cache would keep a prefetch whose
|
|
6
|
+
* render failed after its 200 went out (#1170).
|
|
6
7
|
*
|
|
7
8
|
* `false` disables prefetch caching (seconds 0). A non-finite input
|
|
8
9
|
* (NaN/Infinity, e.g. from an env-derived number that failed to parse) is
|
|
9
|
-
* treated as the default rather than
|
|
10
|
-
*
|
|
11
|
-
* which would silently disable caching on EVERY prefetch response. Negative
|
|
12
|
-
* finite values clamp to 0 (disabled).
|
|
10
|
+
* treated as the default rather than disabling the cache. Negative finite
|
|
11
|
+
* values clamp to 0 (disabled).
|
|
13
12
|
*
|
|
14
13
|
* Policy note: this is the prefetch-TTL policy specifically. Other finite-number
|
|
15
14
|
* guards in the codebase deliberately differ — defer.ts treats Infinity as an
|
|
@@ -17,12 +16,10 @@
|
|
|
17
16
|
* at config time. They are NOT the same guard, so don't unify them.
|
|
18
17
|
*/
|
|
19
18
|
export interface ResolvedPrefetchCacheTTL {
|
|
20
|
-
/** TTL in seconds
|
|
19
|
+
/** TTL in seconds. */
|
|
21
20
|
seconds: number;
|
|
22
21
|
/** TTL in milliseconds for the client-side in-memory prefetch cache. */
|
|
23
22
|
ms: number;
|
|
24
|
-
/** Cache-Control header value, or false when caching is disabled. */
|
|
25
|
-
cacheControl: string | false;
|
|
26
23
|
}
|
|
27
24
|
|
|
28
25
|
const DEFAULT_PREFETCH_CACHE_TTL_SECONDS = 300;
|
|
@@ -38,14 +35,9 @@ export function resolvePrefetchCacheTTL(
|
|
|
38
35
|
} else if (typeof rawTTL === "number" && Number.isFinite(rawTTL)) {
|
|
39
36
|
seconds = Math.max(0, Math.floor(rawTTL));
|
|
40
37
|
} else {
|
|
41
|
-
// Non-finite (NaN/Infinity): fall back to the default
|
|
42
|
-
// malformed max-age=NaN/Infinity that CDNs and browsers reject.
|
|
38
|
+
// Non-finite (NaN/Infinity): fall back to the default.
|
|
43
39
|
seconds = DEFAULT_PREFETCH_CACHE_TTL_SECONDS;
|
|
44
40
|
}
|
|
45
41
|
|
|
46
|
-
return {
|
|
47
|
-
seconds,
|
|
48
|
-
ms: seconds * 1000,
|
|
49
|
-
cacheControl: seconds === 0 ? false : `private, max-age=${seconds}`,
|
|
50
|
-
};
|
|
42
|
+
return { seconds, ms: seconds * 1000 };
|
|
51
43
|
}
|
|
@@ -16,6 +16,7 @@ import { detectPrerenderPassthrough } from "../prerender.js";
|
|
|
16
16
|
import { isRouteRootScoped } from "../route-map-builder.js";
|
|
17
17
|
import { setupBuildUse } from "./loader-resolution.js";
|
|
18
18
|
import { findInOrphans } from "./error-handling.js";
|
|
19
|
+
import { interceptSlotId } from "./intercept-resolution.js";
|
|
19
20
|
import { loadManifest } from "./manifest.js";
|
|
20
21
|
import { traverseBack } from "./pattern-matching.js";
|
|
21
22
|
import type { RouterContext } from "./router-context.js";
|
|
@@ -487,7 +488,7 @@ export async function matchForPrerender<TEnv = any>(
|
|
|
487
488
|
// artifact. Re-pinned per iteration so multiple intercepts targeting
|
|
488
489
|
// the same route each get their own id (mirrors fresh.ts parallel-slot
|
|
489
490
|
// pinning).
|
|
490
|
-
const interceptSegmentId =
|
|
491
|
+
const interceptSegmentId = interceptSlotId(parentEntry, intercept);
|
|
491
492
|
(buildCtx as InternalHandlerContext<any, TEnv>)._currentSegmentId =
|
|
492
493
|
interceptSegmentId;
|
|
493
494
|
|
|
@@ -4,7 +4,11 @@
|
|
|
4
4
|
* Evaluates whether segments should revalidate based on params, actions, and custom functions.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import type {
|
|
7
|
+
import type {
|
|
8
|
+
ResolvedSegment,
|
|
9
|
+
HandlerContext,
|
|
10
|
+
ShouldRevalidateFn,
|
|
11
|
+
} from "../types";
|
|
8
12
|
import type { ActionContext } from "./types";
|
|
9
13
|
import {
|
|
10
14
|
debugLog,
|
|
@@ -17,6 +21,22 @@ import { isAutoGeneratedRouteName } from "../route-name.js";
|
|
|
17
21
|
import { paramsEqual } from "./params-util.js";
|
|
18
22
|
import { makeIsAction } from "./is-action.js";
|
|
19
23
|
|
|
24
|
+
const REVALIDATE_ALWAYS: ShouldRevalidateFn<any, any> = () => true;
|
|
25
|
+
const REVALIDATE_NEVER: ShouldRevalidateFn<any, any> = () => false;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The predicate `revalidate(true)` / `revalidate(false)` stands for: the
|
|
29
|
+
* constant hard decision of `revalidate(() => true)` / `() => false`.
|
|
30
|
+
* Booleans are converted where they enter (the urls() helper, clientUrls()
|
|
31
|
+
* materialization, runRevalidate), so evaluation only ever sees functions.
|
|
32
|
+
*/
|
|
33
|
+
export function toShouldRevalidateFn(
|
|
34
|
+
fn: ShouldRevalidateFn<any, any> | boolean,
|
|
35
|
+
): ShouldRevalidateFn<any, any> {
|
|
36
|
+
if (typeof fn !== "boolean") return fn;
|
|
37
|
+
return fn ? REVALIDATE_ALWAYS : REVALIDATE_NEVER;
|
|
38
|
+
}
|
|
39
|
+
|
|
20
40
|
/**
|
|
21
41
|
* Options for revalidation evaluation
|
|
22
42
|
*/
|
|
@@ -85,7 +105,9 @@ export async function evaluateRevalidation<TEnv>(
|
|
|
85
105
|
} = options;
|
|
86
106
|
const nextParams = segment.params || {};
|
|
87
107
|
const paramsChanged = !paramsEqual(nextParams, prevParams);
|
|
88
|
-
const
|
|
108
|
+
const reqCtx = _getRequestContext();
|
|
109
|
+
const searchChanged =
|
|
110
|
+
prevUrl.search !== nextUrl.search || reqCtx?._visitorMoved === true;
|
|
89
111
|
|
|
90
112
|
function pushTrace(
|
|
91
113
|
defaultVal: boolean,
|
|
@@ -197,7 +219,6 @@ export async function evaluateRevalidation<TEnv>(
|
|
|
197
219
|
|
|
198
220
|
const toRouteName =
|
|
199
221
|
routeKey && !isAutoGeneratedRouteName(routeKey) ? routeKey : undefined;
|
|
200
|
-
const reqCtx = _getRequestContext();
|
|
201
222
|
const prevRouteKey = reqCtx?._prevRouteKey;
|
|
202
223
|
const fromRouteName =
|
|
203
224
|
prevRouteKey && !isAutoGeneratedRouteName(prevRouteKey)
|
|
@@ -25,7 +25,7 @@ import type { ResolveSegmentOptions } from "./segment-resolution.js";
|
|
|
25
25
|
* Revalidation context passed to segment resolution
|
|
26
26
|
*/
|
|
27
27
|
export interface RevalidationContext {
|
|
28
|
-
clientSegmentIds:
|
|
28
|
+
clientSegmentIds: ReadonlySet<string>;
|
|
29
29
|
prevParams: Record<string, string>;
|
|
30
30
|
request: Request;
|
|
31
31
|
prevUrl: URL;
|
|
@@ -37,7 +37,7 @@ export interface RevalidationContext {
|
|
|
37
37
|
actionResult?: any;
|
|
38
38
|
formData?: FormData;
|
|
39
39
|
};
|
|
40
|
-
stale
|
|
40
|
+
stale?: boolean;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
/**
|
|
@@ -159,7 +159,7 @@ export interface RouterContext<TEnv = any> {
|
|
|
159
159
|
handlerContext: HandlerContext<any, TEnv>,
|
|
160
160
|
belongsToRoute: boolean,
|
|
161
161
|
revalidationContext?: RevalidationContext,
|
|
162
|
-
options?: {
|
|
162
|
+
options?: { background?: boolean },
|
|
163
163
|
) => Promise<ResolvedSegment[]>;
|
|
164
164
|
|
|
165
165
|
collectWithMarkers?: <T>(
|
|
@@ -250,23 +250,10 @@ export interface RouterContext<TEnv = any> {
|
|
|
250
250
|
resolveInterceptLoadersOnly?: (
|
|
251
251
|
intercept: InterceptEntry,
|
|
252
252
|
entry: EntryData,
|
|
253
|
-
params: Record<string, string>,
|
|
254
253
|
handlerContext: HandlerContext<any, TEnv>,
|
|
255
254
|
belongsToRoute: boolean,
|
|
256
|
-
revalidationContext:
|
|
257
|
-
|
|
258
|
-
prevParams: Record<string, string>;
|
|
259
|
-
request: Request;
|
|
260
|
-
prevUrl: URL;
|
|
261
|
-
nextUrl: URL;
|
|
262
|
-
routeKey: string;
|
|
263
|
-
actionContext?: any;
|
|
264
|
-
stale?: boolean;
|
|
265
|
-
},
|
|
266
|
-
) => Promise<{
|
|
267
|
-
loaderDataPromise: Promise<any[]> | any[];
|
|
268
|
-
loaderIds: string[];
|
|
269
|
-
} | null>;
|
|
255
|
+
revalidationContext: RevalidationContext,
|
|
256
|
+
) => Promise<ResolvedSegment[]>;
|
|
270
257
|
}
|
|
271
258
|
|
|
272
259
|
const routerContext = new AsyncLocalStorage<RouterContext<any>>();
|
|
@@ -382,13 +382,6 @@ export interface RangoInternal<
|
|
|
382
382
|
import("../cache/profile-registry.js").CacheProfile
|
|
383
383
|
>;
|
|
384
384
|
|
|
385
|
-
/**
|
|
386
|
-
* Cache-Control header value for prefetch responses.
|
|
387
|
-
* False means no caching of prefetch responses.
|
|
388
|
-
* Derived from prefetchCacheTTL.
|
|
389
|
-
*/
|
|
390
|
-
readonly prefetchCacheControl: string | false;
|
|
391
|
-
|
|
392
385
|
/**
|
|
393
386
|
* TTL in milliseconds for the client-side in-memory prefetch cache.
|
|
394
387
|
* 0 means caching is disabled.
|
|
@@ -486,12 +479,6 @@ export interface RangoInternal<
|
|
|
486
479
|
*/
|
|
487
480
|
readonly nonce?: NonceProvider<TEnv>;
|
|
488
481
|
|
|
489
|
-
/**
|
|
490
|
-
* The consumer-set version (createRouter({ version })), used for both of the
|
|
491
|
-
* router's versions. Undefined when the router uses its build versions.
|
|
492
|
-
*/
|
|
493
|
-
readonly version?: string;
|
|
494
|
-
|
|
495
482
|
/**
|
|
496
483
|
* URL patterns reference for build-time manifest generation
|
|
497
484
|
*/
|
|
@@ -532,36 +532,12 @@ export interface RangoOptions<TEnv = any> {
|
|
|
532
532
|
nonce?: NonceProvider<TEnv>;
|
|
533
533
|
|
|
534
534
|
/**
|
|
535
|
-
*
|
|
536
|
-
* router's versions: it keys the router's cached data (segments, `"use
|
|
537
|
-
* cache"` values, loader data) and its stored HTML (PPR shells,
|
|
538
|
-
* document-cache responses), is sent in payload metadata, and is what the
|
|
539
|
-
* browser's `_rsc_v` is compared with (a mismatch reloads the tab).
|
|
540
|
-
*
|
|
541
|
-
* Leave it unset for the default: rango computes two versions per router at
|
|
542
|
-
* build time, a data version from the router's server code and a document
|
|
543
|
-
* version that also covers the client assets. The same code builds to the
|
|
544
|
-
* same versions, so a deploy that does not change this router keeps its
|
|
545
|
-
* cache. In dev both are a stamp bumped on every RSC module edit.
|
|
546
|
-
*
|
|
547
|
-
* Set it to clear this router's cache on demand (change the value), or to
|
|
548
|
-
* tie the cache to your own release id (a per-deploy value clears the cache
|
|
549
|
-
* on every deploy).
|
|
550
|
-
*
|
|
551
|
-
* @default the router's build versions
|
|
552
|
-
*/
|
|
553
|
-
version?: string;
|
|
554
|
-
|
|
555
|
-
/**
|
|
556
|
-
* TTL (in seconds) for the in-memory prefetch cache and the
|
|
557
|
-
* Cache-Control header on prefetch responses.
|
|
535
|
+
* TTL (in seconds) for the client-side in-memory prefetch cache.
|
|
558
536
|
*
|
|
559
537
|
* Controls how long prefetch responses are kept in the client-side
|
|
560
|
-
* in-memory cache
|
|
561
|
-
*
|
|
562
|
-
*
|
|
563
|
-
* intercept targets gets no prefetch `Cache-Control`: whether its
|
|
564
|
-
* response is the modal or the full page depends on the source page.
|
|
538
|
+
* in-memory cache. Prefetch responses carry no `Cache-Control`, so the
|
|
539
|
+
* browser's HTTP cache does not keep them: a prefetch whose render failed
|
|
540
|
+
* after its 200 went out would otherwise answer later requests.
|
|
565
541
|
*
|
|
566
542
|
* The cache is automatically invalidated on server actions regardless
|
|
567
543
|
* of TTL, so this is primarily a staleness safety net.
|
|
@@ -38,6 +38,7 @@ import { _getRequestContext } from "../../server/request-context.js";
|
|
|
38
38
|
import { getRouterContext } from "../router-context.js";
|
|
39
39
|
import { trackStreamedHandler } from "./streamed-handler-boundary.js";
|
|
40
40
|
import { observeSegmentHandler } from "../instrument.js";
|
|
41
|
+
import { withReverseScope } from "../handler-context.js";
|
|
41
42
|
import {
|
|
42
43
|
track,
|
|
43
44
|
RangoContext,
|
|
@@ -177,6 +178,7 @@ export async function resolveLoaders<TEnv>(
|
|
|
177
178
|
bakeLane || loaderEntry.bake === true ? segmentId : null,
|
|
178
179
|
),
|
|
179
180
|
loader.$$id,
|
|
181
|
+
loaderEntry.reverseScope,
|
|
180
182
|
),
|
|
181
183
|
entry,
|
|
182
184
|
segmentId,
|
|
@@ -227,6 +229,7 @@ export async function resolveLoaders<TEnv>(
|
|
|
227
229
|
bakeLane || loaderEntry.bake === true ? segmentId : null,
|
|
228
230
|
),
|
|
229
231
|
loader.$$id,
|
|
232
|
+
loaderEntry.reverseScope,
|
|
230
233
|
),
|
|
231
234
|
entry,
|
|
232
235
|
segmentId,
|
|
@@ -646,6 +649,7 @@ export async function resolveParallelEntry<TEnv>(
|
|
|
646
649
|
>;
|
|
647
650
|
|
|
648
651
|
const slotsToResolve = slotNames ?? (Object.keys(slots) as `@${string}`[]);
|
|
652
|
+
const slotContext = withReverseScope(context, parallelEntry.reverseScope);
|
|
649
653
|
|
|
650
654
|
for (const slot of slotsToResolve) {
|
|
651
655
|
// Try static lookup first — in production, handler bodies are evicted
|
|
@@ -683,7 +687,7 @@ export async function resolveParallelEntry<TEnv>(
|
|
|
683
687
|
`${parentShortCode}.${slot}`,
|
|
684
688
|
`${parallelEntry.id}.${slot}`,
|
|
685
689
|
handler,
|
|
686
|
-
|
|
690
|
+
slotContext,
|
|
687
691
|
)
|
|
688
692
|
: handler;
|
|
689
693
|
if (result instanceof Promise) {
|
|
@@ -708,7 +712,7 @@ export async function resolveParallelEntry<TEnv>(
|
|
|
708
712
|
`${parentShortCode}.${slot}`,
|
|
709
713
|
`${parallelEntry.id}.${slot}`,
|
|
710
714
|
handler,
|
|
711
|
-
|
|
715
|
+
slotContext,
|
|
712
716
|
)
|
|
713
717
|
: handler;
|
|
714
718
|
doneParallelHandler();
|
|
@@ -30,6 +30,7 @@ import type { SegmentResolutionDeps } from "../types.js";
|
|
|
30
30
|
import { debugLog } from "../logging.js";
|
|
31
31
|
import { tryStaticLookup } from "./static-store.js";
|
|
32
32
|
import { observeSegmentHandler } from "../instrument.js";
|
|
33
|
+
import { withReverseScope } from "../handler-context.js";
|
|
33
34
|
import type { TelemetrySink } from "../telemetry.js";
|
|
34
35
|
import { resolveSink, safeEmit, getRequestId } from "../telemetry.js";
|
|
35
36
|
|
|
@@ -176,7 +177,12 @@ export async function resolveLayoutComponent<TEnv>(
|
|
|
176
177
|
// the span so a handler that returns a Response (redirect control flow, which it
|
|
177
178
|
// rethrows) is not recorded as a span error — mirrors the route-handler sites.
|
|
178
179
|
return handleHandlerResult(
|
|
179
|
-
await observeSegmentHandler(
|
|
180
|
+
await observeSegmentHandler(
|
|
181
|
+
entry.shortCode,
|
|
182
|
+
entry.id,
|
|
183
|
+
handler,
|
|
184
|
+
withReverseScope(context, entry.reverseScope),
|
|
185
|
+
),
|
|
180
186
|
);
|
|
181
187
|
}
|
|
182
188
|
|
|
@@ -378,9 +378,10 @@ function getLoaderStore(
|
|
|
378
378
|
* push funnel reads the pushReplayed owner, shell-capture.ts), so a replay
|
|
379
379
|
* of that shell that serves this loader from its pin restores them owned
|
|
380
380
|
* (HandleStore.pushRestored), on a document HIT and on a navigation alike.
|
|
381
|
-
* This replay still runs there: the store drops its
|
|
382
|
-
* the record already restored, and keeps the
|
|
383
|
-
* not keep (
|
|
381
|
+
* This replay still runs there: the store drops its values that settle
|
|
382
|
+
* thenable-free, which the record already restored, and keeps the ones
|
|
383
|
+
* holding a promise the record could not keep (the reason the loader's pin
|
|
384
|
+
* carries `runs`).
|
|
384
385
|
*/
|
|
385
386
|
async function replayLoaderHandles(
|
|
386
387
|
encoded: string | undefined,
|
|
@@ -125,9 +125,11 @@ function someNestedThenable(
|
|
|
125
125
|
/**
|
|
126
126
|
* True when `value` is a thenable or holds one where the capture's loader
|
|
127
127
|
* push funnel masks it (maskNestedContainerThenables): through plain data
|
|
128
|
-
* objects, arrays and React element props.
|
|
129
|
-
* record and reaches a HIT only from a run of its
|
|
130
|
-
* keeps it; a push without one is the record's
|
|
128
|
+
* objects, arrays and React element props. A settled push that holds one
|
|
129
|
+
* stays out of a shell's record and reaches a HIT only from a run of its
|
|
130
|
+
* loader there, so a HIT keeps it; a push without one is the record's
|
|
131
|
+
* (HandleStore.pushRestored). A top-level thenable is judged by the value it
|
|
132
|
+
* settles to, on both sides (#1182).
|
|
131
133
|
*/
|
|
132
134
|
export function holdsThenable(value: unknown): boolean {
|
|
133
135
|
return someNestedThenable(value, () => true);
|