@rangojs/router 0.0.0-experimental.1b930379 → 0.0.0-experimental.1c0bdfad
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/AGENTS.md +12 -0
- package/README.md +245 -49
- package/dist/bin/rango.js +441 -134
- package/dist/testing/vitest.js +82 -0
- package/dist/vite/index.js +3453 -1240
- package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/package.json +76 -21
- package/skills/api-client/SKILL.md +211 -0
- package/skills/breadcrumbs/SKILL.md +64 -2
- package/skills/bundle-analysis/SKILL.md +159 -0
- package/skills/cache-guide/SKILL.md +247 -23
- package/skills/caching/SKILL.md +318 -15
- package/skills/composability/SKILL.md +27 -2
- package/skills/css/SKILL.md +76 -0
- package/skills/debug-manifest/SKILL.md +4 -2
- package/skills/document-cache/SKILL.md +78 -55
- package/skills/handler-use/SKILL.md +364 -0
- package/skills/hooks/SKILL.md +250 -30
- package/skills/host-router/SKILL.md +83 -23
- package/skills/i18n/SKILL.md +276 -0
- package/skills/intercept/SKILL.md +50 -6
- package/skills/layout/SKILL.md +35 -9
- package/skills/links/SKILL.md +249 -17
- package/skills/loader/SKILL.md +279 -53
- package/skills/middleware/SKILL.md +52 -13
- package/skills/migrate-nextjs/SKILL.md +584 -0
- package/skills/migrate-react-router/SKILL.md +771 -0
- package/skills/mime-routes/SKILL.md +28 -1
- package/skills/observability/SKILL.md +172 -0
- package/skills/parallel/SKILL.md +203 -7
- package/skills/prerender/SKILL.md +153 -109
- package/skills/rango/SKILL.md +251 -22
- package/skills/react-compiler/SKILL.md +168 -0
- package/skills/response-routes/SKILL.md +123 -48
- package/skills/route/SKILL.md +101 -5
- package/skills/router-setup/SKILL.md +116 -8
- package/skills/scripts/SKILL.md +179 -0
- package/skills/server-actions/SKILL.md +775 -0
- package/skills/streams-and-websockets/SKILL.md +283 -0
- package/skills/tailwind/SKILL.md +27 -3
- package/skills/testing/SKILL.md +129 -0
- package/skills/testing/bindings.md +103 -0
- package/skills/testing/cache-prerender.md +127 -0
- package/skills/testing/client-components.md +124 -0
- package/skills/testing/e2e-parity.md +125 -0
- package/skills/testing/flight.md +91 -0
- package/skills/testing/handles.md +129 -0
- package/skills/testing/loader.md +128 -0
- package/skills/testing/middleware.md +99 -0
- package/skills/testing/render-handler.md +121 -0
- package/skills/testing/response-routes.md +95 -0
- package/skills/testing/reverse-and-types.md +84 -0
- package/skills/testing/server-actions.md +107 -0
- package/skills/testing/server-tree.md +128 -0
- package/skills/testing/setup.md +123 -0
- package/skills/typesafety/SKILL.md +332 -29
- package/skills/use-cache/SKILL.md +46 -14
- package/skills/view-transitions/SKILL.md +294 -0
- package/src/__augment-tests__/augment.ts +81 -0
- package/src/__augment-tests__/augmented.check.ts +116 -0
- package/src/__internal.ts +1 -66
- package/src/browser/action-coordinator.ts +53 -36
- package/src/browser/action-fence.ts +47 -0
- package/src/browser/app-shell.ts +39 -0
- package/src/browser/app-version.ts +14 -0
- package/src/browser/connection-warmup.ts +134 -0
- package/src/browser/cookie-name.ts +140 -0
- package/src/browser/event-controller.ts +197 -150
- package/src/browser/history-state.ts +21 -0
- package/src/browser/index.ts +3 -3
- package/src/browser/invalidate-client-cache.ts +52 -0
- package/src/browser/navigation-bridge.ts +111 -31
- package/src/browser/navigation-client.ts +201 -67
- package/src/browser/navigation-store-handle.ts +38 -0
- package/src/browser/navigation-store.ts +76 -67
- package/src/browser/navigation-transaction.ts +18 -66
- package/src/browser/network-error-handler.ts +34 -7
- package/src/browser/partial-update.ts +187 -112
- package/src/browser/prefetch/cache.ts +230 -35
- package/src/browser/prefetch/fetch.ts +338 -39
- package/src/browser/prefetch/queue.ts +126 -20
- package/src/browser/prefetch/resource-ready.ts +77 -0
- package/src/browser/rango-state.ts +158 -76
- package/src/browser/react/Link.tsx +111 -16
- package/src/browser/react/NavigationProvider.tsx +135 -120
- package/src/browser/react/ScrollRestoration.tsx +10 -6
- package/src/browser/react/context.ts +7 -2
- package/src/browser/react/filter-segment-order.ts +66 -7
- package/src/browser/react/index.ts +0 -48
- package/src/browser/react/location-state-shared.ts +178 -8
- package/src/browser/react/location-state.ts +39 -14
- package/src/browser/react/use-action.ts +6 -15
- package/src/browser/react/use-handle.ts +23 -69
- package/src/browser/react/use-href.tsx +8 -1
- package/src/browser/react/use-link-status.ts +33 -8
- package/src/browser/react/use-navigation.ts +32 -7
- package/src/browser/react/use-params.ts +20 -10
- package/src/browser/react/use-reverse.ts +106 -0
- package/src/browser/react/use-router.ts +46 -11
- package/src/browser/react/use-search-params.ts +0 -5
- package/src/browser/react/use-segments.ts +11 -21
- package/src/browser/response-adapter.ts +80 -5
- package/src/browser/rsc-router.tsx +218 -76
- package/src/browser/scroll-restoration.ts +54 -42
- package/src/browser/segment-reconciler.ts +36 -9
- package/src/browser/segment-structure-assert.ts +2 -2
- package/src/browser/server-action-bridge.ts +222 -61
- package/src/browser/types.ts +91 -11
- package/src/browser/validate-redirect-origin.ts +43 -16
- package/src/build/collect-fallback-refs.ts +107 -0
- package/src/build/generate-manifest.ts +65 -40
- package/src/build/generate-route-types.ts +5 -1
- package/src/build/index.ts +8 -2
- package/src/build/prefix-tree-utils.ts +123 -0
- package/src/build/route-trie.ts +165 -36
- package/src/build/route-types/ast-route-extraction.ts +15 -8
- package/src/build/route-types/codegen.ts +16 -5
- package/src/build/route-types/include-resolution.ts +125 -24
- package/src/build/route-types/param-extraction.ts +6 -3
- package/src/build/route-types/per-module-writer.ts +22 -6
- package/src/build/route-types/router-processing.ts +272 -96
- package/src/build/route-types/scan-filter.ts +9 -2
- package/src/build/route-types/source-scan.ts +216 -0
- package/src/build/runtime-discovery.ts +9 -20
- package/src/cache/cache-error.ts +104 -0
- package/src/cache/cache-key-utils.ts +29 -13
- package/src/cache/cache-policy.ts +108 -34
- package/src/cache/cache-runtime.ts +214 -48
- package/src/cache/cache-scope.ts +236 -89
- package/src/cache/cache-tag.ts +103 -0
- package/src/cache/cf/cf-base64.ts +33 -0
- package/src/cache/cf/cf-cache-constants.ts +127 -0
- package/src/cache/cf/cf-cache-store.ts +2224 -171
- package/src/cache/cf/cf-cache-types.ts +349 -0
- package/src/cache/cf/cf-kv-utils.ts +46 -0
- package/src/cache/cf/cf-tag-marker-memo.ts +105 -0
- package/src/cache/cf/index.ts +11 -17
- package/src/cache/document-cache.ts +89 -27
- package/src/cache/handle-snapshot.ts +70 -0
- package/src/cache/index.ts +11 -20
- package/src/cache/memory-segment-store.ts +136 -37
- package/src/cache/profile-registry.ts +31 -31
- package/src/cache/read-through-swr.ts +41 -11
- package/src/cache/segment-codec.ts +9 -17
- package/src/cache/tag-invalidation.ts +230 -0
- package/src/cache/taint.ts +55 -0
- package/src/cache/types.ts +37 -100
- package/src/client.rsc.tsx +44 -21
- package/src/client.tsx +119 -290
- package/src/cloudflare/index.ts +11 -0
- package/src/cloudflare/tracing.ts +109 -0
- package/src/component-utils.ts +19 -0
- package/src/components/DefaultDocument.tsx +8 -2
- package/src/context-var.ts +84 -2
- package/src/debug.ts +2 -2
- package/src/decode-loader-results.ts +52 -0
- package/src/defer.ts +196 -0
- package/src/deps/ssr.ts +0 -1
- package/src/encode-kv.ts +49 -0
- package/src/errors.ts +30 -4
- package/src/escape-script.ts +52 -0
- package/src/handle.ts +70 -22
- package/src/handles/MetaTags.tsx +56 -19
- package/src/handles/Scripts.tsx +183 -0
- package/src/handles/breadcrumbs.ts +37 -8
- package/src/handles/is-thenable.ts +19 -0
- package/src/handles/meta.ts +51 -40
- package/src/handles/script.ts +244 -0
- package/src/host/cookie-handler.ts +9 -60
- package/src/host/errors.ts +0 -24
- package/src/host/index.ts +8 -2
- package/src/host/pattern-matcher.ts +23 -52
- package/src/host/router.ts +107 -99
- package/src/host/testing.ts +40 -27
- package/src/host/types.ts +37 -4
- package/src/host/utils.ts +1 -1
- package/src/href-client.ts +137 -22
- package/src/index.rsc.ts +93 -12
- package/src/index.ts +133 -15
- package/src/internal-debug.ts +11 -10
- package/src/loader-store.ts +500 -0
- package/src/loader.rsc.ts +20 -13
- package/src/loader.ts +12 -11
- package/src/missing-id-error.ts +68 -0
- package/src/outlet-context.ts +1 -1
- package/src/outlet-provider.tsx +1 -5
- package/src/prerender/param-hash.ts +16 -16
- package/src/prerender/store.ts +37 -41
- package/src/prerender.ts +198 -82
- package/src/redirect-origin.ts +100 -0
- package/src/regex-escape.ts +8 -0
- package/src/render-error-thrower.tsx +20 -0
- package/src/response-utils.ts +62 -0
- package/src/reverse.ts +65 -15
- package/src/root-error-boundary.tsx +1 -19
- package/src/route-content-wrapper.tsx +7 -72
- package/src/route-definition/dsl-helpers.ts +469 -276
- package/src/route-definition/helper-factories.ts +29 -139
- package/src/route-definition/helpers-types.ts +113 -37
- package/src/route-definition/index.ts +3 -0
- package/src/route-definition/redirect.ts +53 -12
- package/src/route-definition/resolve-handler-use.ts +161 -0
- package/src/route-definition/use-item-types.ts +32 -0
- package/src/route-map-builder.ts +7 -17
- package/src/route-types.ts +37 -41
- package/src/router/basename.ts +14 -0
- package/src/router/content-negotiation.ts +164 -17
- package/src/router/error-handling.ts +45 -18
- package/src/router/find-match.ts +45 -22
- package/src/router/handler-context.ts +83 -39
- package/src/router/instrument.ts +350 -0
- package/src/router/intercept-resolution.ts +50 -24
- package/src/router/lazy-includes.ts +19 -53
- package/src/router/loader-resolution.ts +274 -56
- package/src/router/logging.ts +5 -8
- package/src/router/manifest.ts +49 -45
- package/src/router/match-api.ts +120 -204
- package/src/router/match-context.ts +0 -22
- package/src/router/match-handlers.ts +58 -58
- package/src/router/match-middleware/background-revalidation.ts +33 -6
- package/src/router/match-middleware/cache-lookup.ts +214 -263
- package/src/router/match-middleware/cache-store.ts +73 -33
- package/src/router/match-middleware/intercept-resolution.ts +8 -28
- package/src/router/match-middleware/segment-resolution.ts +52 -18
- package/src/router/match-pipelines.ts +1 -42
- package/src/router/match-result.ts +104 -40
- package/src/router/metrics.ts +5 -34
- package/src/router/middleware-types.ts +13 -142
- package/src/router/middleware.ts +270 -172
- package/src/router/navigation-snapshot.ts +131 -0
- package/src/router/params-util.ts +23 -0
- package/src/router/pattern-matching.ts +132 -90
- package/src/router/prefetch-cache-ttl.ts +51 -0
- package/src/router/prerender-match.ts +195 -56
- package/src/router/preview-match.ts +32 -102
- package/src/router/request-classification.ts +276 -0
- package/src/router/revalidation.ts +123 -73
- package/src/router/route-snapshot.ts +244 -0
- package/src/router/router-context.ts +8 -28
- package/src/router/router-interfaces.ts +115 -35
- package/src/router/router-options.ts +172 -15
- package/src/router/router-registry.ts +2 -5
- package/src/router/segment-resolution/fresh.ts +264 -77
- package/src/router/segment-resolution/helpers.ts +115 -30
- package/src/router/segment-resolution/loader-cache.ts +63 -37
- package/src/router/segment-resolution/revalidation.ts +474 -385
- package/src/router/segment-resolution/static-store.ts +19 -5
- package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
- package/src/router/segment-resolution/view-transition-default.ts +36 -0
- package/src/router/segment-resolution.ts +5 -1
- package/src/router/segment-wrappers.ts +8 -5
- package/src/router/state-cookie-name.ts +33 -0
- package/src/router/substitute-pattern-params.ts +56 -0
- package/src/router/telemetry-otel.ts +161 -199
- package/src/router/telemetry.ts +96 -19
- package/src/router/timeout.ts +0 -20
- package/src/router/tracing.ts +206 -0
- package/src/router/trie-matching.ts +163 -59
- package/src/router/types.ts +10 -63
- package/src/router/url-params.ts +44 -0
- package/src/router.ts +163 -55
- package/src/rsc/handler-context.ts +3 -2
- package/src/rsc/handler.ts +658 -511
- package/src/rsc/helpers.ts +168 -46
- package/src/rsc/index.ts +2 -5
- package/src/rsc/json-route-result.ts +38 -0
- package/src/rsc/loader-fetch.ts +127 -31
- package/src/rsc/manifest-init.ts +33 -42
- package/src/rsc/origin-guard.ts +39 -25
- package/src/rsc/progressive-enhancement.ts +77 -11
- package/src/rsc/redirect-guard.ts +99 -0
- package/src/rsc/response-cache-serve.ts +238 -0
- package/src/rsc/response-error.ts +79 -12
- package/src/rsc/response-route-handler.ts +99 -189
- package/src/rsc/rsc-rendering.ts +105 -72
- package/src/rsc/runtime-warnings.ts +23 -10
- package/src/rsc/server-action.ts +263 -112
- package/src/rsc/ssr-setup.ts +18 -2
- package/src/rsc/types.ts +32 -6
- package/src/runtime-env.ts +18 -0
- package/src/search-params.ts +35 -30
- package/src/segment-content-promise.ts +67 -0
- package/src/segment-loader-promise.ts +149 -0
- package/src/segment-system.tsx +281 -129
- package/src/serialize.ts +243 -0
- package/src/server/context.ts +309 -61
- package/src/server/cookie-parse.ts +32 -0
- package/src/server/cookie-store.ts +80 -5
- package/src/server/handle-store.ts +40 -38
- package/src/server/loader-registry.ts +26 -46
- package/src/server/request-context.ts +398 -172
- package/src/ssr/index.tsx +25 -16
- package/src/static-handler.ts +27 -18
- package/src/testing/cache-status.ts +162 -0
- package/src/testing/collect-handle.ts +40 -0
- package/src/testing/dispatch.ts +701 -0
- package/src/testing/dom.entry.ts +22 -0
- package/src/testing/e2e/fixture.ts +188 -0
- package/src/testing/e2e/index.ts +128 -0
- package/src/testing/e2e/matchers.ts +35 -0
- package/src/testing/e2e/page-helpers.ts +272 -0
- package/src/testing/e2e/parity.ts +387 -0
- package/src/testing/e2e/server.ts +195 -0
- package/src/testing/flight-matchers.ts +97 -0
- package/src/testing/flight-normalize.ts +11 -0
- package/src/testing/flight-runtime.d.ts +57 -0
- package/src/testing/flight-tree.ts +682 -0
- package/src/testing/flight.entry.ts +52 -0
- package/src/testing/flight.ts +257 -0
- package/src/testing/generated-routes.ts +183 -0
- package/src/testing/index.ts +99 -0
- package/src/testing/internal/context.ts +371 -0
- package/src/testing/internal/flight-client-globals.ts +30 -0
- package/src/testing/internal/seed-vars.ts +54 -0
- package/src/testing/render-handler.ts +343 -0
- package/src/testing/render-route.tsx +581 -0
- package/src/testing/run-loader.ts +385 -0
- package/src/testing/run-middleware.ts +205 -0
- package/src/testing/vitest-stubs/cloudflare-email.ts +9 -0
- package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
- package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
- package/src/testing/vitest-stubs/version.ts +5 -0
- package/src/testing/vitest.ts +305 -0
- package/src/theme/ThemeProvider.tsx +20 -58
- package/src/theme/ThemeScript.tsx +7 -9
- package/src/theme/constants.ts +52 -13
- package/src/theme/index.ts +0 -7
- package/src/theme/theme-context.ts +1 -5
- package/src/theme/theme-script.ts +22 -21
- package/src/theme/use-theme.ts +0 -3
- package/src/types/boundaries.ts +0 -35
- package/src/types/cache-types.ts +17 -8
- package/src/types/error-types.ts +30 -90
- package/src/types/global-namespace.ts +54 -41
- package/src/types/handler-context.ts +233 -81
- package/src/types/index.ts +1 -10
- package/src/types/loader-types.ts +44 -15
- package/src/types/request-scope.ts +112 -0
- package/src/types/route-config.ts +6 -50
- package/src/types/route-entry.ts +19 -7
- package/src/types/segments.ts +37 -14
- package/src/urls/include-helper.ts +33 -70
- package/src/urls/index.ts +1 -11
- package/src/urls/path-helper-types.ts +58 -11
- package/src/urls/path-helper.ts +57 -111
- package/src/urls/pattern-types.ts +48 -19
- package/src/urls/response-types.ts +25 -22
- package/src/urls/type-extraction.ts +58 -139
- package/src/urls/urls-function.ts +1 -18
- package/src/use-loader.tsx +346 -89
- package/src/vite/debug.ts +185 -0
- package/src/vite/discovery/bundle-postprocess.ts +36 -38
- package/src/vite/discovery/discover-routers.ts +130 -85
- package/src/vite/discovery/discovery-errors.ts +194 -0
- package/src/vite/discovery/gate-state.ts +171 -0
- package/src/vite/discovery/prerender-collection.ts +214 -132
- package/src/vite/discovery/route-types-writer.ts +40 -84
- package/src/vite/discovery/self-gen-tracking.ts +27 -1
- package/src/vite/discovery/state.ts +57 -6
- package/src/vite/discovery/virtual-module-codegen.ts +14 -34
- package/src/vite/index.ts +6 -0
- package/src/vite/inject-client-debug.ts +36 -0
- package/src/vite/plugin-types.ts +155 -65
- package/src/vite/plugins/cjs-to-esm.ts +16 -19
- package/src/vite/plugins/client-ref-dedup.ts +16 -11
- package/src/vite/plugins/client-ref-hashing.ts +28 -15
- package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
- package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/src/vite/plugins/cloudflare-protocol-stub.ts +194 -0
- package/src/vite/plugins/expose-action-id.ts +49 -98
- package/src/vite/plugins/expose-id-utils.ts +96 -51
- package/src/vite/plugins/expose-ids/export-analysis.ts +101 -34
- package/src/vite/plugins/expose-ids/handler-transform.ts +15 -64
- package/src/vite/plugins/expose-ids/loader-transform.ts +14 -24
- package/src/vite/plugins/expose-ids/router-transform.ts +118 -29
- package/src/vite/plugins/expose-internal-ids.ts +553 -317
- package/src/vite/plugins/performance-tracks.ts +89 -0
- package/src/vite/plugins/refresh-cmd.ts +89 -27
- package/src/vite/plugins/use-cache-transform.ts +73 -83
- package/src/vite/plugins/version-injector.ts +21 -25
- package/src/vite/plugins/version-plugin.ts +46 -37
- package/src/vite/plugins/virtual-entries.ts +13 -18
- package/src/vite/rango.ts +238 -295
- package/src/vite/router-discovery.ts +940 -149
- package/src/vite/utils/ast-handler-extract.ts +26 -35
- package/src/vite/utils/banner.ts +4 -4
- package/src/vite/utils/bundle-analysis.ts +10 -15
- package/src/vite/utils/client-chunks.ts +184 -0
- package/src/vite/utils/directive-prologue.ts +40 -0
- package/src/vite/utils/forward-user-plugins.ts +171 -0
- package/src/vite/utils/manifest-utils.ts +4 -59
- package/src/vite/utils/package-resolution.ts +20 -52
- package/src/vite/utils/prerender-utils.ts +81 -34
- package/src/vite/utils/shared-utils.ts +92 -42
- package/src/browser/action-response-classifier.ts +0 -99
- package/src/browser/react/use-client-cache.ts +0 -58
- package/src/browser/shallow.ts +0 -40
- package/src/handles/index.ts +0 -7
- package/src/network-error-thrower.tsx +0 -23
- package/src/router/middleware-cookies.ts +0 -55
|
@@ -10,7 +10,12 @@ import type { ReactNode } from "react";
|
|
|
10
10
|
import { invariant } from "../../errors";
|
|
11
11
|
import { revalidate } from "../loader-resolution.js";
|
|
12
12
|
import { evaluateRevalidation } from "../revalidation.js";
|
|
13
|
-
import
|
|
13
|
+
import {
|
|
14
|
+
getParallelEntries,
|
|
15
|
+
getParallelSlotEntries,
|
|
16
|
+
type EntryData,
|
|
17
|
+
type ParallelEntryData,
|
|
18
|
+
} from "../../server/context";
|
|
14
19
|
import type {
|
|
15
20
|
HandlerContext,
|
|
16
21
|
InternalHandlerContext,
|
|
@@ -30,54 +35,41 @@ import {
|
|
|
30
35
|
import { resolveLoaderData } from "./loader-cache.js";
|
|
31
36
|
import {
|
|
32
37
|
handleHandlerResult,
|
|
38
|
+
warnOnStreamedResponse,
|
|
33
39
|
tryStaticHandler,
|
|
34
40
|
tryStaticSlot,
|
|
35
41
|
resolveLayoutComponent,
|
|
36
42
|
resolveWithErrorBoundary,
|
|
43
|
+
buildLoaderErrorContext,
|
|
37
44
|
} from "./helpers.js";
|
|
45
|
+
import { applyViewTransitionDefault } from "./view-transition-default.js";
|
|
38
46
|
import { getRouterContext } from "../router-context.js";
|
|
39
|
-
import {
|
|
40
|
-
import {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
47
|
+
import { observeEvent, observeHandler } from "../instrument.js";
|
|
48
|
+
import { observeStreamedHandler } from "./streamed-handler-telemetry.js";
|
|
49
|
+
import {
|
|
50
|
+
track,
|
|
51
|
+
RangoContext,
|
|
52
|
+
runInsideLoaderScope,
|
|
53
|
+
} from "../../server/context.js";
|
|
45
54
|
|
|
46
55
|
/**
|
|
47
|
-
*
|
|
48
|
-
*
|
|
56
|
+
* Trace a parallel slot that's being force-rendered on a full refetch (client
|
|
57
|
+
* has no cached state). User revalidate fns are bypassed in this case — see
|
|
58
|
+
* the call sites for the load-bearing rationale.
|
|
49
59
|
*/
|
|
50
|
-
function
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
segmentType: string,
|
|
54
|
-
pathname?: string,
|
|
55
|
-
routeKey?: string,
|
|
56
|
-
params?: Record<string, string>,
|
|
60
|
+
function traceFullRefetchedParallelSlot(
|
|
61
|
+
parallelId: string,
|
|
62
|
+
belongsToRoute: boolean,
|
|
57
63
|
): void {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
promise.catch((err: unknown) => {
|
|
68
|
-
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
69
|
-
safeEmit(sink, {
|
|
70
|
-
type: "handler.error",
|
|
71
|
-
timestamp: performance.now(),
|
|
72
|
-
requestId: reqId,
|
|
73
|
-
segmentId,
|
|
74
|
-
segmentType,
|
|
75
|
-
error: errorObj,
|
|
76
|
-
handledByBoundary: true,
|
|
77
|
-
pathname,
|
|
78
|
-
routeKey,
|
|
79
|
-
params,
|
|
80
|
-
});
|
|
64
|
+
if (!isTraceActive()) return;
|
|
65
|
+
pushRevalidationTraceEntry({
|
|
66
|
+
segmentId: parallelId,
|
|
67
|
+
segmentType: "parallel",
|
|
68
|
+
belongsToRoute,
|
|
69
|
+
source: "parallel",
|
|
70
|
+
defaultShouldRevalidate: true,
|
|
71
|
+
finalShouldRevalidate: true,
|
|
72
|
+
reason: "full-refetch",
|
|
81
73
|
});
|
|
82
74
|
}
|
|
83
75
|
|
|
@@ -96,23 +88,14 @@ function emitRevalidationDecision(
|
|
|
96
88
|
routeKey: string,
|
|
97
89
|
shouldRevalidate: boolean,
|
|
98
90
|
): void {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
type: "revalidation.decision",
|
|
108
|
-
timestamp: performance.now(),
|
|
109
|
-
requestId: routerCtx.requestId,
|
|
110
|
-
segmentId,
|
|
111
|
-
pathname,
|
|
112
|
-
routeKey,
|
|
113
|
-
shouldRevalidate,
|
|
114
|
-
});
|
|
115
|
-
}
|
|
91
|
+
observeEvent({
|
|
92
|
+
type: "revalidation.decision",
|
|
93
|
+
timestamp: performance.now(),
|
|
94
|
+
segmentId,
|
|
95
|
+
pathname,
|
|
96
|
+
routeKey,
|
|
97
|
+
shouldRevalidate,
|
|
98
|
+
});
|
|
116
99
|
}
|
|
117
100
|
|
|
118
101
|
// ---------------------------------------------------------------------------
|
|
@@ -217,6 +200,10 @@ export async function resolveLoadersWithRevalidation<TEnv>(
|
|
|
217
200
|
),
|
|
218
201
|
);
|
|
219
202
|
|
|
203
|
+
// Partial (revalidation) render path: a throwing DSL loader must still fire
|
|
204
|
+
// onError/loader.error. isPartial flags the reporting phase accordingly.
|
|
205
|
+
const errorContext = { ...buildLoaderErrorContext(ctx), isPartial: true };
|
|
206
|
+
|
|
220
207
|
const loadersToRun = revalidationChecks.filter((c) => c.shouldRun);
|
|
221
208
|
const segments: ResolvedSegment[] = loadersToRun.map(
|
|
222
209
|
({ loaderEntry, loader, segmentId, index }) => ({
|
|
@@ -228,10 +215,13 @@ export async function resolveLoadersWithRevalidation<TEnv>(
|
|
|
228
215
|
params: ctx.params,
|
|
229
216
|
loaderId: loader.$$id,
|
|
230
217
|
loaderData: deps.wrapLoaderPromise(
|
|
231
|
-
|
|
218
|
+
runInsideLoaderScope(() =>
|
|
219
|
+
resolveLoaderData(loaderEntry, ctx, ctx.pathname),
|
|
220
|
+
),
|
|
232
221
|
entry,
|
|
233
222
|
segmentId,
|
|
234
223
|
ctx.pathname,
|
|
224
|
+
errorContext,
|
|
235
225
|
),
|
|
236
226
|
belongsToRoute,
|
|
237
227
|
}),
|
|
@@ -258,26 +248,95 @@ export async function resolveLoadersOnlyWithRevalidation<TEnv>(
|
|
|
258
248
|
): Promise<{ segments: ResolvedSegment[]; matchedIds: string[] }> {
|
|
259
249
|
const allLoaderSegments: ResolvedSegment[] = [];
|
|
260
250
|
const allMatchedIds: string[] = [];
|
|
251
|
+
const seenIds = new Set<string>();
|
|
252
|
+
|
|
253
|
+
async function collectEntryLoaders(
|
|
254
|
+
entry: EntryData,
|
|
255
|
+
belongsToRoute: boolean,
|
|
256
|
+
shortCodeOverride?: string,
|
|
257
|
+
): Promise<void> {
|
|
258
|
+
// Skip if all loaders from this entry have already been resolved
|
|
259
|
+
// via a parent (e.g., cache boundary wrapping a layout with shared loaders).
|
|
260
|
+
const loaderEntries = entry.loader ?? [];
|
|
261
|
+
const sc = shortCodeOverride ?? entry.shortCode;
|
|
262
|
+
const allAlreadySeen =
|
|
263
|
+
loaderEntries.length > 0 &&
|
|
264
|
+
loaderEntries.every((le, i) =>
|
|
265
|
+
seenIds.has(`${sc}D${i}.${le.loader.$$id}`),
|
|
266
|
+
);
|
|
267
|
+
if (!allAlreadySeen) {
|
|
268
|
+
const { segments, matchedIds } = await resolveLoadersWithRevalidation(
|
|
269
|
+
entry,
|
|
270
|
+
context,
|
|
271
|
+
belongsToRoute,
|
|
272
|
+
clientSegmentIds,
|
|
273
|
+
prevParams,
|
|
274
|
+
request,
|
|
275
|
+
prevUrl,
|
|
276
|
+
nextUrl,
|
|
277
|
+
routeKey,
|
|
278
|
+
deps,
|
|
279
|
+
actionContext,
|
|
280
|
+
shortCodeOverride,
|
|
281
|
+
stale,
|
|
282
|
+
);
|
|
283
|
+
for (const seg of segments) {
|
|
284
|
+
if (!seenIds.has(seg.id)) {
|
|
285
|
+
seenIds.add(seg.id);
|
|
286
|
+
allLoaderSegments.push(seg);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
allMatchedIds.push(...matchedIds);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const seenParallelEntryIds = new Set<string>();
|
|
293
|
+
for (const parallelEntry of getParallelEntries(entry.parallel)) {
|
|
294
|
+
if (seenParallelEntryIds.has(parallelEntry.id)) continue;
|
|
295
|
+
seenParallelEntryIds.add(parallelEntry.id);
|
|
296
|
+
await collectEntryLoaders(parallelEntry, belongsToRoute, entry.shortCode);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const childBelongsToRoute = belongsToRoute || entry.type === "route";
|
|
300
|
+
for (const layoutEntry of entry.layout) {
|
|
301
|
+
await collectEntryLoaders(layoutEntry, childBelongsToRoute);
|
|
302
|
+
// Inherit route loaders for orphan layouts with parallels.
|
|
303
|
+
// Resolve directly — do NOT re-enter collectEntryLoaders with the
|
|
304
|
+
// route entry, as that would re-iterate route.layout and loop.
|
|
305
|
+
if (
|
|
306
|
+
entry.type === "route" &&
|
|
307
|
+
entry.loader &&
|
|
308
|
+
entry.loader.length > 0 &&
|
|
309
|
+
Object.keys(layoutEntry.parallel).length > 0
|
|
310
|
+
) {
|
|
311
|
+
const inherited = await resolveLoadersWithRevalidation(
|
|
312
|
+
entry,
|
|
313
|
+
context,
|
|
314
|
+
childBelongsToRoute,
|
|
315
|
+
clientSegmentIds,
|
|
316
|
+
prevParams,
|
|
317
|
+
request,
|
|
318
|
+
prevUrl,
|
|
319
|
+
nextUrl,
|
|
320
|
+
routeKey,
|
|
321
|
+
deps,
|
|
322
|
+
actionContext,
|
|
323
|
+
layoutEntry.shortCode,
|
|
324
|
+
stale,
|
|
325
|
+
);
|
|
326
|
+
for (const seg of inherited.segments) {
|
|
327
|
+
if (!seenIds.has(seg.id)) {
|
|
328
|
+
seenIds.add(seg.id);
|
|
329
|
+
seg._inherited = true;
|
|
330
|
+
allLoaderSegments.push(seg);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
allMatchedIds.push(...inherited.matchedIds);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
261
337
|
|
|
262
338
|
for (const entry of entries) {
|
|
263
|
-
|
|
264
|
-
const { segments, matchedIds } = await resolveLoadersWithRevalidation(
|
|
265
|
-
entry,
|
|
266
|
-
context,
|
|
267
|
-
belongsToRoute,
|
|
268
|
-
clientSegmentIds,
|
|
269
|
-
prevParams,
|
|
270
|
-
request,
|
|
271
|
-
prevUrl,
|
|
272
|
-
nextUrl,
|
|
273
|
-
routeKey,
|
|
274
|
-
deps,
|
|
275
|
-
actionContext,
|
|
276
|
-
undefined, // shortCodeOverride
|
|
277
|
-
stale,
|
|
278
|
-
);
|
|
279
|
-
allLoaderSegments.push(...segments);
|
|
280
|
-
allMatchedIds.push(...matchedIds);
|
|
339
|
+
await collectEntryLoaders(entry, entry.type === "route");
|
|
281
340
|
}
|
|
282
341
|
|
|
283
342
|
return { segments: allLoaderSegments, matchedIds: allMatchedIds };
|
|
@@ -301,22 +360,20 @@ export function buildEntryRevalidateMap(
|
|
|
301
360
|
map.set(entry.shortCode, { entry, revalidate: entry.revalidate });
|
|
302
361
|
|
|
303
362
|
if (entry.type !== "parallel") {
|
|
304
|
-
for (const parallelEntry of
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
}
|
|
314
|
-
}
|
|
363
|
+
for (const { slot, entry: parallelEntry } of getParallelSlotEntries(
|
|
364
|
+
entry.parallel,
|
|
365
|
+
)) {
|
|
366
|
+
const parallelParentShortCode = parentShortCode ?? entry.shortCode;
|
|
367
|
+
const parallelId = `${parallelParentShortCode}.${slot}`;
|
|
368
|
+
map.set(parallelId, {
|
|
369
|
+
entry: parallelEntry,
|
|
370
|
+
revalidate: parallelEntry.revalidate,
|
|
371
|
+
});
|
|
315
372
|
}
|
|
316
373
|
}
|
|
317
374
|
|
|
318
375
|
for (const layoutEntry of entry.layout) {
|
|
319
|
-
processEntry(layoutEntry);
|
|
376
|
+
processEntry(layoutEntry, entry.shortCode);
|
|
320
377
|
}
|
|
321
378
|
}
|
|
322
379
|
|
|
@@ -327,6 +384,97 @@ export function buildEntryRevalidateMap(
|
|
|
327
384
|
return map;
|
|
328
385
|
}
|
|
329
386
|
|
|
387
|
+
/**
|
|
388
|
+
* Resolve the component for a single parallel slot on the revalidation path.
|
|
389
|
+
* Pure component resolution shared verbatim by
|
|
390
|
+
* resolveParallelSegmentsWithRevalidation and the orphan-inlined loop in
|
|
391
|
+
* resolveOrphanLayoutWithRevalidation: try the static slot cache, else run the
|
|
392
|
+
* slot handler (pinning _currentSegmentId to the slot id so handle pushes land
|
|
393
|
+
* in the slot's own bucket, and wrapping a streamed handler). Returns the
|
|
394
|
+
* resolved component and whether the handler actually ran. Does NOT touch the
|
|
395
|
+
* revalidate-default policy (the caller decides shouldResolve, including the
|
|
396
|
+
* orphan-vs-main defaultOverride divergence) or loader-resolution ordering.
|
|
397
|
+
*/
|
|
398
|
+
async function resolveParallelSlotComponent<TEnv>(args: {
|
|
399
|
+
shouldResolve: boolean;
|
|
400
|
+
parallelEntry: ParallelEntryData;
|
|
401
|
+
slot: string;
|
|
402
|
+
parallelId: string;
|
|
403
|
+
handler:
|
|
404
|
+
| ((ctx: HandlerContext<any, TEnv>) => ReactNode | Promise<ReactNode>)
|
|
405
|
+
| ReactNode
|
|
406
|
+
| undefined;
|
|
407
|
+
context: HandlerContext<any, TEnv>;
|
|
408
|
+
deps: SegmentResolutionDeps<TEnv>;
|
|
409
|
+
routeKey: string;
|
|
410
|
+
params: Record<string, string>;
|
|
411
|
+
}): Promise<{ component: ReactNode | undefined; handlerRan: boolean }> {
|
|
412
|
+
const {
|
|
413
|
+
shouldResolve,
|
|
414
|
+
parallelEntry,
|
|
415
|
+
slot,
|
|
416
|
+
parallelId,
|
|
417
|
+
handler,
|
|
418
|
+
context,
|
|
419
|
+
deps,
|
|
420
|
+
routeKey,
|
|
421
|
+
params,
|
|
422
|
+
} = args;
|
|
423
|
+
|
|
424
|
+
let component: ReactNode | undefined;
|
|
425
|
+
let handlerRan = false;
|
|
426
|
+
if (shouldResolve) {
|
|
427
|
+
component = await tryStaticSlot(parallelEntry, slot, parallelId);
|
|
428
|
+
// tryStaticSlot returning a value means the static cache supplied the
|
|
429
|
+
// component — handler did NOT run. handlerRan stays false.
|
|
430
|
+
}
|
|
431
|
+
if (component === undefined) {
|
|
432
|
+
const hasLoadingFallback =
|
|
433
|
+
parallelEntry.loading !== undefined && parallelEntry.loading !== false;
|
|
434
|
+
if (!shouldResolve) {
|
|
435
|
+
component = null;
|
|
436
|
+
} else if (handler === undefined) {
|
|
437
|
+
// Handler evicted (production static slot) but static lookup missed.
|
|
438
|
+
// Nothing to render — use null so the client keeps its cached version.
|
|
439
|
+
component = null;
|
|
440
|
+
} else {
|
|
441
|
+
// Slot-keyed pushes — slot owns its own bucket, parent layout owns its
|
|
442
|
+
// own. On slot-only revalidations the partial merge updates only the
|
|
443
|
+
// slot's bucket; the parent's bucket stays intact.
|
|
444
|
+
(context as InternalHandlerContext<any, TEnv>)._currentSegmentId =
|
|
445
|
+
parallelId;
|
|
446
|
+
handlerRan = true;
|
|
447
|
+
if (hasLoadingFallback) {
|
|
448
|
+
const result =
|
|
449
|
+
typeof handler === "function" ? handler(context) : handler;
|
|
450
|
+
if (result instanceof Promise) {
|
|
451
|
+
warnOnStreamedResponse(result, parallelId);
|
|
452
|
+
const tracked = deps.trackHandler(result, {
|
|
453
|
+
segmentId: parallelId,
|
|
454
|
+
segmentType: "parallel",
|
|
455
|
+
});
|
|
456
|
+
observeStreamedHandler(
|
|
457
|
+
tracked,
|
|
458
|
+
parallelId,
|
|
459
|
+
"parallel",
|
|
460
|
+
context.pathname,
|
|
461
|
+
routeKey,
|
|
462
|
+
params,
|
|
463
|
+
);
|
|
464
|
+
component = tracked as ReactNode;
|
|
465
|
+
} else {
|
|
466
|
+
component = result as ReactNode;
|
|
467
|
+
}
|
|
468
|
+
} else {
|
|
469
|
+
component =
|
|
470
|
+
typeof handler === "function" ? await handler(context) : handler;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
return { component, handlerRan };
|
|
476
|
+
}
|
|
477
|
+
|
|
330
478
|
/**
|
|
331
479
|
* Resolve parallel segments with revalidation.
|
|
332
480
|
*/
|
|
@@ -344,11 +492,35 @@ export async function resolveParallelSegmentsWithRevalidation<TEnv>(
|
|
|
344
492
|
deps: SegmentResolutionDeps<TEnv>,
|
|
345
493
|
actionContext?: ActionContext,
|
|
346
494
|
stale?: boolean,
|
|
495
|
+
options?: {
|
|
496
|
+
/**
|
|
497
|
+
* Seed for an unknown parent-chain slot (slot not in clientSegmentIds) when
|
|
498
|
+
* there are no deciding revalidate fns. "type-derived" (default, main path):
|
|
499
|
+
* `belongsToRoute || isNewParent`. "force-render" (orphan path): always
|
|
500
|
+
* `true` — orphan parallels always belong to the route and must render
|
|
501
|
+
* unless the user opts out via revalidate(); the #482 blank-parent-chain-
|
|
502
|
+
* slot guard.
|
|
503
|
+
*/
|
|
504
|
+
parentChainDefault?: "type-derived" | "force-render";
|
|
505
|
+
/**
|
|
506
|
+
* When a slot's loaders are resolved relative to the slot segment push.
|
|
507
|
+
* "after" (default, main path) pushes the slot segment first; "before"
|
|
508
|
+
* (orphan path) resolves loaders first. This only changes the
|
|
509
|
+
* segments/matchedIds emission ORDER (the client reconciler is insensitive
|
|
510
|
+
* to it: loader sub-ids are filtered out and slots are re-grouped by parent).
|
|
511
|
+
*/
|
|
512
|
+
loaderOrder?: "after" | "before";
|
|
513
|
+
},
|
|
347
514
|
): Promise<SegmentRevalidationResult> {
|
|
348
515
|
const segments: ResolvedSegment[] = [];
|
|
349
516
|
const matchedIds: string[] = [];
|
|
517
|
+
const parentChainDefault = options?.parentChainDefault ?? "type-derived";
|
|
518
|
+
const loaderOrder = options?.loaderOrder ?? "after";
|
|
350
519
|
|
|
351
|
-
|
|
520
|
+
const resolvedParallelEntries = new Set<string>();
|
|
521
|
+
for (const { slot, entry: parallelEntry } of getParallelSlotEntries(
|
|
522
|
+
entry.parallel,
|
|
523
|
+
)) {
|
|
352
524
|
invariant(
|
|
353
525
|
parallelEntry.type === "parallel",
|
|
354
526
|
`Expected parallel entry, got: ${parallelEntry.type}`,
|
|
@@ -359,141 +531,78 @@ export async function resolveParallelSegmentsWithRevalidation<TEnv>(
|
|
|
359
531
|
| ((ctx: HandlerContext<any, TEnv>) => ReactNode | Promise<ReactNode>)
|
|
360
532
|
| ReactNode
|
|
361
533
|
>;
|
|
534
|
+
// In production, static handler bodies are evicted and the slot value
|
|
535
|
+
// may be undefined. The static store holds the pre-rendered component.
|
|
536
|
+
// We defer the handler check until after tryStaticSlot.
|
|
537
|
+
const handler = slots[slot];
|
|
362
538
|
|
|
363
|
-
|
|
364
|
-
const parallelId = `${entry.shortCode}.${slot}`;
|
|
365
|
-
|
|
366
|
-
const isFullRefetch = clientSegmentIds.size === 0;
|
|
367
|
-
// When the parent layout is new (not in client's segment set),
|
|
368
|
-
// all its parallel children must be resolved and tracked.
|
|
369
|
-
// Without this, navigating to a new layout with parallels
|
|
370
|
-
// (e.g., BlogLayout with @sidebar) from a different route
|
|
371
|
-
// would silently drop those parallel segments.
|
|
372
|
-
const isNewParent = !clientSegmentIds.has(entry.shortCode);
|
|
373
|
-
if (
|
|
374
|
-
isFullRefetch ||
|
|
375
|
-
clientSegmentIds.has(parallelId) ||
|
|
376
|
-
belongsToRoute ||
|
|
377
|
-
isNewParent
|
|
378
|
-
) {
|
|
379
|
-
matchedIds.push(parallelId);
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
const shouldResolve = await (async () => {
|
|
383
|
-
if (isFullRefetch) {
|
|
384
|
-
if (isTraceActive()) {
|
|
385
|
-
pushRevalidationTraceEntry({
|
|
386
|
-
segmentId: parallelId,
|
|
387
|
-
segmentType: "parallel",
|
|
388
|
-
belongsToRoute,
|
|
389
|
-
source: "parallel",
|
|
390
|
-
defaultShouldRevalidate: true,
|
|
391
|
-
finalShouldRevalidate: true,
|
|
392
|
-
reason: "full-refetch",
|
|
393
|
-
});
|
|
394
|
-
}
|
|
395
|
-
return true;
|
|
396
|
-
}
|
|
397
|
-
if (!clientSegmentIds.has(parallelId)) {
|
|
398
|
-
const result = belongsToRoute || isNewParent;
|
|
399
|
-
if (isTraceActive()) {
|
|
400
|
-
pushRevalidationTraceEntry({
|
|
401
|
-
segmentId: parallelId,
|
|
402
|
-
segmentType: "parallel",
|
|
403
|
-
belongsToRoute,
|
|
404
|
-
source: "parallel",
|
|
405
|
-
defaultShouldRevalidate: result,
|
|
406
|
-
finalShouldRevalidate: result,
|
|
407
|
-
reason: result ? "new-segment" : "skip-parent-chain",
|
|
408
|
-
});
|
|
409
|
-
}
|
|
410
|
-
return result;
|
|
411
|
-
}
|
|
539
|
+
const parallelId = `${entry.shortCode}.${slot}`;
|
|
412
540
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
namespace: parallelEntry.id,
|
|
416
|
-
type: "parallel",
|
|
417
|
-
index: 0,
|
|
418
|
-
component: null as any,
|
|
419
|
-
params,
|
|
420
|
-
slot,
|
|
421
|
-
belongsToRoute,
|
|
422
|
-
parallelName: `${parallelEntry.id}.${slot}`,
|
|
423
|
-
...(parallelEntry.mountPath
|
|
424
|
-
? { mountPath: parallelEntry.mountPath }
|
|
425
|
-
: {}),
|
|
426
|
-
};
|
|
541
|
+
const isFullRefetch = clientSegmentIds.size === 0;
|
|
542
|
+
const isNewParent = !clientSegmentIds.has(entry.shortCode);
|
|
427
543
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
actionContext,
|
|
442
|
-
stale,
|
|
443
|
-
traceSource: "parallel",
|
|
444
|
-
});
|
|
445
|
-
})();
|
|
446
|
-
emitRevalidationDecision(
|
|
447
|
-
parallelId,
|
|
448
|
-
context.pathname,
|
|
544
|
+
// A slot's loaders (never cached) are deduped per parallel entry and
|
|
545
|
+
// emitted either before or after the slot segment per loaderOrder.
|
|
546
|
+
const resolveSlotLoaders = async () => {
|
|
547
|
+
if (resolvedParallelEntries.has(parallelEntry.id)) return;
|
|
548
|
+
const loaderResult = await resolveLoadersWithRevalidation(
|
|
549
|
+
parallelEntry,
|
|
550
|
+
context,
|
|
551
|
+
belongsToRoute,
|
|
552
|
+
clientSegmentIds,
|
|
553
|
+
prevParams,
|
|
554
|
+
request,
|
|
555
|
+
prevUrl,
|
|
556
|
+
nextUrl,
|
|
449
557
|
routeKey,
|
|
450
|
-
|
|
558
|
+
deps,
|
|
559
|
+
actionContext,
|
|
560
|
+
entry.shortCode,
|
|
561
|
+
stale,
|
|
451
562
|
);
|
|
563
|
+
segments.push(...loaderResult.segments);
|
|
564
|
+
matchedIds.push(...loaderResult.matchedIds);
|
|
565
|
+
resolvedParallelEntries.add(parallelEntry.id);
|
|
566
|
+
};
|
|
452
567
|
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
} else {
|
|
484
|
-
component =
|
|
485
|
-
typeof handler === "function" ? await handler(context) : handler;
|
|
486
|
-
}
|
|
568
|
+
if (loaderOrder === "before") {
|
|
569
|
+
await resolveSlotLoaders();
|
|
570
|
+
}
|
|
571
|
+
// Always announce the slot in matchedIds — it's unconditionally appended
|
|
572
|
+
// to `segments` below, and a segment present in segments but missing from
|
|
573
|
+
// matched lets the client prune it (then it's missing from clientSegmentIds
|
|
574
|
+
// on the next request, perpetuating the staleness).
|
|
575
|
+
matchedIds.push(parallelId);
|
|
576
|
+
|
|
577
|
+
let shouldResolve: boolean;
|
|
578
|
+
if (isFullRefetch) {
|
|
579
|
+
// Client has nothing cached — slot MUST render. User revalidate fns are
|
|
580
|
+
// bypassed here because returning false would leave the segment blank
|
|
581
|
+
// with no client-side fallback.
|
|
582
|
+
traceFullRefetchedParallelSlot(parallelId, belongsToRoute);
|
|
583
|
+
shouldResolve = true;
|
|
584
|
+
} else {
|
|
585
|
+
// For non-empty client sets, consult user revalidate fns. When the slot
|
|
586
|
+
// is unknown to the client, override the type-derived default so the
|
|
587
|
+
// soft chain seeds with the right "new segment" / "parent-chain" value.
|
|
588
|
+
let defaultOverride: { value: boolean; reason: string } | undefined;
|
|
589
|
+
if (!clientSegmentIds.has(parallelId)) {
|
|
590
|
+
const value =
|
|
591
|
+
parentChainDefault === "force-render"
|
|
592
|
+
? true
|
|
593
|
+
: belongsToRoute || isNewParent;
|
|
594
|
+
defaultOverride = {
|
|
595
|
+
value,
|
|
596
|
+
reason: value ? "new-segment" : "skip-parent-chain",
|
|
597
|
+
};
|
|
487
598
|
}
|
|
488
599
|
|
|
489
|
-
|
|
600
|
+
const dummySegment: ResolvedSegment = {
|
|
490
601
|
id: parallelId,
|
|
491
602
|
namespace: parallelEntry.id,
|
|
492
603
|
type: "parallel",
|
|
493
604
|
index: 0,
|
|
494
|
-
component,
|
|
495
|
-
loading: parallelEntry.loading === false ? null : parallelEntry.loading,
|
|
496
|
-
transition: parallelEntry.transition,
|
|
605
|
+
component: null as any,
|
|
497
606
|
params,
|
|
498
607
|
slot,
|
|
499
608
|
belongsToRoute,
|
|
@@ -501,27 +610,69 @@ export async function resolveParallelSegmentsWithRevalidation<TEnv>(
|
|
|
501
610
|
...(parallelEntry.mountPath
|
|
502
611
|
? { mountPath: parallelEntry.mountPath }
|
|
503
612
|
: {}),
|
|
504
|
-
}
|
|
505
|
-
}
|
|
613
|
+
};
|
|
506
614
|
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
parallelEntry,
|
|
510
|
-
context,
|
|
511
|
-
belongsToRoute,
|
|
512
|
-
clientSegmentIds,
|
|
615
|
+
shouldResolve = await evaluateRevalidation({
|
|
616
|
+
segment: dummySegment,
|
|
513
617
|
prevParams,
|
|
618
|
+
getPrevSegment: null,
|
|
514
619
|
request,
|
|
515
620
|
prevUrl,
|
|
516
621
|
nextUrl,
|
|
622
|
+
revalidations: parallelEntry.revalidate.map((fn, i) => ({
|
|
623
|
+
name: `revalidate${i}`,
|
|
624
|
+
fn,
|
|
625
|
+
})),
|
|
517
626
|
routeKey,
|
|
518
|
-
|
|
627
|
+
context,
|
|
519
628
|
actionContext,
|
|
520
|
-
entry.shortCode,
|
|
521
629
|
stale,
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
630
|
+
traceSource: "parallel",
|
|
631
|
+
defaultOverride,
|
|
632
|
+
});
|
|
633
|
+
}
|
|
634
|
+
emitRevalidationDecision(
|
|
635
|
+
parallelId,
|
|
636
|
+
context.pathname,
|
|
637
|
+
routeKey,
|
|
638
|
+
shouldResolve,
|
|
639
|
+
);
|
|
640
|
+
|
|
641
|
+
const { component, handlerRan } = await resolveParallelSlotComponent({
|
|
642
|
+
shouldResolve,
|
|
643
|
+
parallelEntry,
|
|
644
|
+
slot,
|
|
645
|
+
parallelId,
|
|
646
|
+
handler,
|
|
647
|
+
context,
|
|
648
|
+
deps,
|
|
649
|
+
routeKey,
|
|
650
|
+
params,
|
|
651
|
+
});
|
|
652
|
+
|
|
653
|
+
segments.push({
|
|
654
|
+
id: parallelId,
|
|
655
|
+
namespace: parallelEntry.id,
|
|
656
|
+
type: "parallel",
|
|
657
|
+
index: 0,
|
|
658
|
+
component,
|
|
659
|
+
loading: parallelEntry.loading === false ? null : parallelEntry.loading,
|
|
660
|
+
transition: applyViewTransitionDefault(
|
|
661
|
+
parallelEntry.transition,
|
|
662
|
+
deps.viewTransitionDefault,
|
|
663
|
+
),
|
|
664
|
+
params,
|
|
665
|
+
slot,
|
|
666
|
+
_handlerRan: handlerRan,
|
|
667
|
+
belongsToRoute,
|
|
668
|
+
parallelName: `${parallelEntry.id}.${slot}`,
|
|
669
|
+
...(parallelEntry.mountPath
|
|
670
|
+
? { mountPath: parallelEntry.mountPath }
|
|
671
|
+
: {}),
|
|
672
|
+
});
|
|
673
|
+
|
|
674
|
+
if (loaderOrder === "after") {
|
|
675
|
+
await resolveSlotLoaders();
|
|
525
676
|
}
|
|
526
677
|
}
|
|
527
678
|
|
|
@@ -548,6 +699,7 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
|
|
|
548
699
|
): Promise<{ segment: ResolvedSegment; matchedId: string }> {
|
|
549
700
|
const matchedId = entry.shortCode;
|
|
550
701
|
|
|
702
|
+
let handlerRan = false;
|
|
551
703
|
const component = await revalidate(
|
|
552
704
|
async () => {
|
|
553
705
|
const hasSegment = clientSegmentIds.has(entry.shortCode);
|
|
@@ -608,6 +760,8 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
|
|
|
608
760
|
context,
|
|
609
761
|
actionContext,
|
|
610
762
|
stale,
|
|
763
|
+
traceSource:
|
|
764
|
+
entry.type === "route" ? "route-handler" : "layout-handler",
|
|
611
765
|
});
|
|
612
766
|
emitRevalidationDecision(
|
|
613
767
|
entry.shortCode,
|
|
@@ -622,6 +776,7 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
|
|
|
622
776
|
return shouldRevalidate;
|
|
623
777
|
},
|
|
624
778
|
async () => {
|
|
779
|
+
handlerRan = true;
|
|
625
780
|
const doneHandler = track(`handler:${entry.id}`, 2);
|
|
626
781
|
(context as InternalHandlerContext<any, TEnv>)._currentSegmentId =
|
|
627
782
|
entry.shortCode;
|
|
@@ -636,14 +791,26 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
|
|
|
636
791
|
return staticComponent;
|
|
637
792
|
}
|
|
638
793
|
const routeEntry = entry as Extract<EntryData, { type: "route" }>;
|
|
794
|
+
// For Passthrough routes at runtime, use the live handler instead of
|
|
795
|
+
// the build handler. At build time (context.build === true), always
|
|
796
|
+
// use the build handler from routeEntry.handler.
|
|
797
|
+
const handler =
|
|
798
|
+
!context.build && routeEntry.liveHandler
|
|
799
|
+
? routeEntry.liveHandler
|
|
800
|
+
: routeEntry.handler;
|
|
639
801
|
if (!routeEntry.loading) {
|
|
640
|
-
const result = handleHandlerResult(
|
|
802
|
+
const result = handleHandlerResult(
|
|
803
|
+
await observeHandler(entry.id, handler, context),
|
|
804
|
+
);
|
|
641
805
|
doneHandler();
|
|
642
806
|
return result;
|
|
643
807
|
}
|
|
644
808
|
if (!actionContext) {
|
|
645
|
-
const result = handleHandlerResult(
|
|
809
|
+
const result = handleHandlerResult(
|
|
810
|
+
observeHandler(entry.id, handler, context),
|
|
811
|
+
);
|
|
646
812
|
if (result instanceof Promise) {
|
|
813
|
+
warnOnStreamedResponse(result, routeEntry.id);
|
|
647
814
|
result.finally(doneHandler).catch(() => {});
|
|
648
815
|
const tracked = deps.trackHandler(result, {
|
|
649
816
|
segmentId: entry.shortCode,
|
|
@@ -666,7 +833,7 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
|
|
|
666
833
|
entryId: entry.id,
|
|
667
834
|
});
|
|
668
835
|
const actionResult = handleHandlerResult(
|
|
669
|
-
await
|
|
836
|
+
await observeHandler(entry.id, handler, context),
|
|
670
837
|
);
|
|
671
838
|
doneHandler();
|
|
672
839
|
return {
|
|
@@ -676,10 +843,12 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
|
|
|
676
843
|
() => null,
|
|
677
844
|
);
|
|
678
845
|
|
|
846
|
+
// Normalize void handlers (undefined) to null so the reconciler's
|
|
847
|
+
// component === null checks work consistently for both void and explicit null.
|
|
679
848
|
const resolvedComponent =
|
|
680
849
|
component && typeof component === "object" && "content" in component
|
|
681
|
-
? (component as { content: ReactNode }).content
|
|
682
|
-
: component;
|
|
850
|
+
? ((component as { content: ReactNode }).content ?? null)
|
|
851
|
+
: (component ?? null);
|
|
683
852
|
|
|
684
853
|
const segment: ResolvedSegment = {
|
|
685
854
|
id: entry.shortCode,
|
|
@@ -689,13 +858,17 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
|
|
|
689
858
|
index: 0,
|
|
690
859
|
component: resolvedComponent,
|
|
691
860
|
loading: entry.loading === false ? null : entry.loading,
|
|
692
|
-
transition:
|
|
861
|
+
transition: applyViewTransitionDefault(
|
|
862
|
+
entry.transition,
|
|
863
|
+
deps.viewTransitionDefault,
|
|
864
|
+
),
|
|
693
865
|
params,
|
|
694
866
|
belongsToRoute,
|
|
695
867
|
...(entry.type === "layout" || entry.type === "cache"
|
|
696
868
|
? { layoutName: entry.id }
|
|
697
869
|
: {}),
|
|
698
870
|
...(entry.mountPath ? { mountPath: entry.mountPath } : {}),
|
|
871
|
+
_handlerRan: handlerRan,
|
|
699
872
|
};
|
|
700
873
|
|
|
701
874
|
return { segment, matchedId };
|
|
@@ -714,7 +887,6 @@ export async function resolveSegmentWithRevalidation<TEnv>(
|
|
|
714
887
|
request: Request,
|
|
715
888
|
prevUrl: URL,
|
|
716
889
|
nextUrl: URL,
|
|
717
|
-
loaderPromises: Map<string, Promise<any>>,
|
|
718
890
|
deps: SegmentResolutionDeps<TEnv>,
|
|
719
891
|
actionContext?: ActionContext,
|
|
720
892
|
stale?: boolean,
|
|
@@ -776,11 +948,11 @@ export async function resolveSegmentWithRevalidation<TEnv>(
|
|
|
776
948
|
prevUrl,
|
|
777
949
|
nextUrl,
|
|
778
950
|
routeKey,
|
|
779
|
-
loaderPromises,
|
|
780
951
|
true,
|
|
781
952
|
deps,
|
|
782
953
|
actionContext,
|
|
783
954
|
stale,
|
|
955
|
+
entry,
|
|
784
956
|
);
|
|
785
957
|
segments.push(...orphanResult.segments);
|
|
786
958
|
matchedIds.push(...orphanResult.matchedIds);
|
|
@@ -860,7 +1032,6 @@ export async function resolveSegmentWithRevalidation<TEnv>(
|
|
|
860
1032
|
prevUrl,
|
|
861
1033
|
nextUrl,
|
|
862
1034
|
routeKey,
|
|
863
|
-
loaderPromises,
|
|
864
1035
|
false,
|
|
865
1036
|
deps,
|
|
866
1037
|
actionContext,
|
|
@@ -887,11 +1058,12 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
|
|
|
887
1058
|
prevUrl: URL,
|
|
888
1059
|
nextUrl: URL,
|
|
889
1060
|
routeKey: string,
|
|
890
|
-
loaderPromises: Map<string, Promise<any>>,
|
|
891
1061
|
belongsToRoute: boolean,
|
|
892
1062
|
deps: SegmentResolutionDeps<TEnv>,
|
|
893
1063
|
actionContext?: ActionContext,
|
|
894
1064
|
stale?: boolean,
|
|
1065
|
+
/** Parent route entry — its loaders are inherited so parallel slots can access them. */
|
|
1066
|
+
parentRouteEntry?: EntryData,
|
|
895
1067
|
): Promise<SegmentRevalidationResult> {
|
|
896
1068
|
invariant(
|
|
897
1069
|
orphan.type === "layout" || orphan.type === "cache",
|
|
@@ -919,6 +1091,37 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
|
|
|
919
1091
|
segments.push(...loaderResult.segments);
|
|
920
1092
|
matchedIds.push(...loaderResult.matchedIds);
|
|
921
1093
|
|
|
1094
|
+
// Inherit parent route's loaders so parallel slots inside this layout
|
|
1095
|
+
// can access them via useLoader(). See resolveOrphanLayout in fresh.ts.
|
|
1096
|
+
if (
|
|
1097
|
+
parentRouteEntry &&
|
|
1098
|
+
parentRouteEntry.loader &&
|
|
1099
|
+
parentRouteEntry.loader.length > 0 &&
|
|
1100
|
+
Object.keys(orphan.parallel).length > 0
|
|
1101
|
+
) {
|
|
1102
|
+
const inheritedResult = await resolveLoadersWithRevalidation(
|
|
1103
|
+
parentRouteEntry,
|
|
1104
|
+
context,
|
|
1105
|
+
belongsToRoute,
|
|
1106
|
+
clientSegmentIds,
|
|
1107
|
+
prevParams,
|
|
1108
|
+
request,
|
|
1109
|
+
prevUrl,
|
|
1110
|
+
nextUrl,
|
|
1111
|
+
routeKey,
|
|
1112
|
+
deps,
|
|
1113
|
+
actionContext,
|
|
1114
|
+
orphan.shortCode,
|
|
1115
|
+
stale,
|
|
1116
|
+
);
|
|
1117
|
+
// Tag as inherited so buildMatchResult can deduplicate when safe
|
|
1118
|
+
for (const s of inheritedResult.segments) {
|
|
1119
|
+
s._inherited = true;
|
|
1120
|
+
}
|
|
1121
|
+
segments.push(...inheritedResult.segments);
|
|
1122
|
+
matchedIds.push(...inheritedResult.matchedIds);
|
|
1123
|
+
}
|
|
1124
|
+
|
|
922
1125
|
// Handler-first: resolve orphan layout handler before its parallels
|
|
923
1126
|
// so ctx.set() values are visible to parallel children.
|
|
924
1127
|
matchedIds.push(orphan.shortCode);
|
|
@@ -991,157 +1194,40 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
|
|
|
991
1194
|
belongsToRoute,
|
|
992
1195
|
layoutName: orphan.id,
|
|
993
1196
|
loading: orphan.loading === false ? null : orphan.loading,
|
|
994
|
-
transition:
|
|
1197
|
+
transition: applyViewTransitionDefault(
|
|
1198
|
+
orphan.transition,
|
|
1199
|
+
deps.viewTransitionDefault,
|
|
1200
|
+
),
|
|
995
1201
|
...(orphan.mountPath ? { mountPath: orphan.mountPath } : {}),
|
|
996
1202
|
});
|
|
997
1203
|
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
| ReactNode
|
|
1026
|
-
>;
|
|
1027
|
-
|
|
1028
|
-
for (const [slot, handler] of Object.entries(slots)) {
|
|
1029
|
-
// Use orphan.shortCode (the parent layout) to match the SSR path
|
|
1030
|
-
// (resolveParallelEntry receives parentShortCode = orphan.shortCode).
|
|
1031
|
-
// Using parallelEntry.shortCode would generate IDs the client doesn't know about.
|
|
1032
|
-
const parallelId = `${orphan.shortCode}.${slot}`;
|
|
1033
|
-
matchedIds.push(parallelId);
|
|
1034
|
-
|
|
1035
|
-
const shouldResolve = await (async () => {
|
|
1036
|
-
if (!clientSegmentIds.has(parallelId)) {
|
|
1037
|
-
if (isTraceActive()) {
|
|
1038
|
-
pushRevalidationTraceEntry({
|
|
1039
|
-
segmentId: parallelId,
|
|
1040
|
-
segmentType: "parallel",
|
|
1041
|
-
belongsToRoute,
|
|
1042
|
-
source: "parallel",
|
|
1043
|
-
defaultShouldRevalidate: true,
|
|
1044
|
-
finalShouldRevalidate: true,
|
|
1045
|
-
reason: "new-segment",
|
|
1046
|
-
});
|
|
1047
|
-
}
|
|
1048
|
-
return true;
|
|
1049
|
-
}
|
|
1050
|
-
|
|
1051
|
-
const dummySegment: ResolvedSegment = {
|
|
1052
|
-
id: parallelId,
|
|
1053
|
-
namespace: parallelEntry.id,
|
|
1054
|
-
type: "parallel",
|
|
1055
|
-
index: 0,
|
|
1056
|
-
component: null as any,
|
|
1057
|
-
params,
|
|
1058
|
-
slot,
|
|
1059
|
-
belongsToRoute,
|
|
1060
|
-
parallelName: `${parallelEntry.id}.${slot}`,
|
|
1061
|
-
...(parallelEntry.mountPath
|
|
1062
|
-
? { mountPath: parallelEntry.mountPath }
|
|
1063
|
-
: {}),
|
|
1064
|
-
};
|
|
1065
|
-
|
|
1066
|
-
return await evaluateRevalidation({
|
|
1067
|
-
segment: dummySegment,
|
|
1068
|
-
prevParams,
|
|
1069
|
-
getPrevSegment: null,
|
|
1070
|
-
request,
|
|
1071
|
-
prevUrl,
|
|
1072
|
-
nextUrl,
|
|
1073
|
-
revalidations: parallelEntry.revalidate.map((fn, i) => ({
|
|
1074
|
-
name: `revalidate${i}`,
|
|
1075
|
-
fn,
|
|
1076
|
-
})),
|
|
1077
|
-
routeKey,
|
|
1078
|
-
context,
|
|
1079
|
-
actionContext,
|
|
1080
|
-
stale,
|
|
1081
|
-
traceSource: "parallel",
|
|
1082
|
-
});
|
|
1083
|
-
})();
|
|
1084
|
-
emitRevalidationDecision(
|
|
1085
|
-
parallelId,
|
|
1086
|
-
context.pathname,
|
|
1087
|
-
routeKey,
|
|
1088
|
-
shouldResolve,
|
|
1089
|
-
);
|
|
1090
|
-
|
|
1091
|
-
let component: ReactNode | undefined;
|
|
1092
|
-
if (shouldResolve) {
|
|
1093
|
-
component = await tryStaticSlot(parallelEntry, slot, parallelId);
|
|
1094
|
-
}
|
|
1095
|
-
if (component === undefined) {
|
|
1096
|
-
const hasLoadingFallback =
|
|
1097
|
-
parallelEntry.loading !== undefined &&
|
|
1098
|
-
parallelEntry.loading !== false;
|
|
1099
|
-
if (!shouldResolve) {
|
|
1100
|
-
component = null;
|
|
1101
|
-
} else if (hasLoadingFallback) {
|
|
1102
|
-
const result =
|
|
1103
|
-
typeof handler === "function" ? handler(context) : handler;
|
|
1104
|
-
if (result instanceof Promise) {
|
|
1105
|
-
const tracked = deps.trackHandler(result, {
|
|
1106
|
-
segmentId: parallelId,
|
|
1107
|
-
segmentType: "parallel",
|
|
1108
|
-
});
|
|
1109
|
-
observeStreamedHandler(
|
|
1110
|
-
tracked,
|
|
1111
|
-
parallelId,
|
|
1112
|
-
"parallel",
|
|
1113
|
-
context.pathname,
|
|
1114
|
-
routeKey,
|
|
1115
|
-
params,
|
|
1116
|
-
);
|
|
1117
|
-
component = tracked as ReactNode;
|
|
1118
|
-
} else {
|
|
1119
|
-
component = result as ReactNode;
|
|
1120
|
-
}
|
|
1121
|
-
} else {
|
|
1122
|
-
component =
|
|
1123
|
-
typeof handler === "function" ? await handler(context) : handler;
|
|
1124
|
-
}
|
|
1125
|
-
}
|
|
1126
|
-
|
|
1127
|
-
segments.push({
|
|
1128
|
-
id: parallelId,
|
|
1129
|
-
namespace: parallelEntry.id,
|
|
1130
|
-
type: "parallel",
|
|
1131
|
-
index: 0,
|
|
1132
|
-
component,
|
|
1133
|
-
loading: parallelEntry.loading === false ? null : parallelEntry.loading,
|
|
1134
|
-
transition: parallelEntry.transition,
|
|
1135
|
-
params,
|
|
1136
|
-
slot,
|
|
1137
|
-
belongsToRoute,
|
|
1138
|
-
parallelName: `${parallelEntry.id}.${slot}`,
|
|
1139
|
-
...(parallelEntry.mountPath
|
|
1140
|
-
? { mountPath: parallelEntry.mountPath }
|
|
1141
|
-
: {}),
|
|
1142
|
-
});
|
|
1143
|
-
}
|
|
1144
|
-
}
|
|
1204
|
+
// Resolve the orphan layout's parallel slots through the shared main-path
|
|
1205
|
+
// helper. The orphan policy is carried by explicit args, byte-for-byte:
|
|
1206
|
+
// - parentChainDefault "force-render": an unknown parent-chain slot seeds
|
|
1207
|
+
// `true` (orphan parallels always belong to the route — the #482 guard),
|
|
1208
|
+
// where the main path would seed `belongsToRoute || isNewParent`.
|
|
1209
|
+
// - loaderOrder "before": a slot's loaders are emitted before the slot
|
|
1210
|
+
// segment, matching the prior inlined order.
|
|
1211
|
+
// `entry.shortCode` inside the helper is `orphan.shortCode` (orphan is passed
|
|
1212
|
+
// as `entry`), so the parallel ids + loader shortCodeOverride are unchanged.
|
|
1213
|
+
const parallelResult = await resolveParallelSegmentsWithRevalidation(
|
|
1214
|
+
orphan,
|
|
1215
|
+
params,
|
|
1216
|
+
context,
|
|
1217
|
+
belongsToRoute,
|
|
1218
|
+
clientSegmentIds,
|
|
1219
|
+
prevParams,
|
|
1220
|
+
request,
|
|
1221
|
+
prevUrl,
|
|
1222
|
+
nextUrl,
|
|
1223
|
+
routeKey,
|
|
1224
|
+
deps,
|
|
1225
|
+
actionContext,
|
|
1226
|
+
stale,
|
|
1227
|
+
{ parentChainDefault: "force-render", loaderOrder: "before" },
|
|
1228
|
+
);
|
|
1229
|
+
segments.push(...parallelResult.segments);
|
|
1230
|
+
matchedIds.push(...parallelResult.matchedIds);
|
|
1145
1231
|
|
|
1146
1232
|
return { segments, matchedIds };
|
|
1147
1233
|
}
|
|
@@ -1159,12 +1245,12 @@ export async function resolveAllSegmentsWithRevalidation<TEnv>(
|
|
|
1159
1245
|
request: Request,
|
|
1160
1246
|
prevUrl: URL,
|
|
1161
1247
|
nextUrl: URL,
|
|
1162
|
-
loaderPromises: Map<string, Promise<any>>,
|
|
1163
1248
|
actionContext: ActionContext | undefined,
|
|
1164
1249
|
interceptResult: { intercept: any; entry: EntryData } | null,
|
|
1165
1250
|
localRouteName: string,
|
|
1166
1251
|
pathname: string,
|
|
1167
1252
|
deps: SegmentResolutionDeps<TEnv>,
|
|
1253
|
+
stale?: boolean,
|
|
1168
1254
|
): Promise<{ segments: ResolvedSegment[]; matchedIds: string[] }> {
|
|
1169
1255
|
const allSegments: ResolvedSegment[] = [];
|
|
1170
1256
|
const matchedIds: string[] = [];
|
|
@@ -1191,6 +1277,10 @@ export async function resolveAllSegmentsWithRevalidation<TEnv>(
|
|
|
1191
1277
|
}
|
|
1192
1278
|
|
|
1193
1279
|
const nonParallelEntry = entry as Exclude<EntryData, { type: "parallel" }>;
|
|
1280
|
+
if (entry.type === "cache") {
|
|
1281
|
+
const store = RangoContext.getStore();
|
|
1282
|
+
if (store) store.insideCacheScope = true;
|
|
1283
|
+
}
|
|
1194
1284
|
const doneEntry = track(`segment:${entry.id}`, 1);
|
|
1195
1285
|
const resolved = await resolveWithErrorBoundary(
|
|
1196
1286
|
nonParallelEntry,
|
|
@@ -1206,10 +1296,9 @@ export async function resolveAllSegmentsWithRevalidation<TEnv>(
|
|
|
1206
1296
|
request,
|
|
1207
1297
|
prevUrl,
|
|
1208
1298
|
nextUrl,
|
|
1209
|
-
loaderPromises,
|
|
1210
1299
|
deps,
|
|
1211
1300
|
actionContext,
|
|
1212
|
-
|
|
1301
|
+
stale,
|
|
1213
1302
|
),
|
|
1214
1303
|
(seg) => ({ segments: [seg], matchedIds: [seg.id] }),
|
|
1215
1304
|
deps,
|