@rangojs/router 0.0.0-experimental.19 → 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 +17 -0
- package/README.md +291 -61
- package/dist/bin/rango.js +544 -143
- package/dist/testing/vitest.js +82 -0
- package/dist/vite/index.js +3744 -1329
- package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/package.json +67 -13
- package/skills/api-client/SKILL.md +211 -0
- package/skills/breadcrumbs/SKILL.md +312 -0
- package/skills/bundle-analysis/SKILL.md +159 -0
- package/skills/cache-guide/SKILL.md +247 -23
- package/skills/caching/SKILL.md +322 -19
- 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 +282 -60
- package/skills/host-router/SKILL.md +278 -0
- 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 +297 -31
- 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 +155 -111
- package/skills/rango/SKILL.md +251 -23
- package/skills/react-compiler/SKILL.md +168 -0
- package/skills/response-routes/SKILL.md +123 -48
- package/skills/route/SKILL.md +104 -9
- package/skills/router-setup/SKILL.md +124 -11
- 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 +125 -222
- 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 +357 -52
- 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 +67 -40
- package/src/bin/rango.ts +18 -0
- 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/link-interceptor.ts +4 -0
- package/src/browser/navigation-bridge.ts +200 -30
- package/src/browser/navigation-client.ts +217 -58
- 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 +312 -30
- package/src/browser/prefetch/fetch.ts +344 -47
- package/src/browser/prefetch/policy.ts +6 -0
- 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 +125 -18
- 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 +226 -75
- 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 +619 -442
- package/src/browser/types.ts +115 -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 +7 -1
- package/src/build/index.ts +8 -2
- package/src/build/prefix-tree-utils.ts +123 -0
- package/src/build/route-trie.ts +182 -37
- 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 +392 -106
- 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 +45 -21
- package/src/client.tsx +120 -336
- 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 +95 -0
- 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 -5
- 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 +79 -29
- package/src/index.ts +149 -65
- 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 +63 -26
- 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 -3
- 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 +110 -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 +121 -205
- 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 -49
- package/src/router/metrics.ts +217 -26
- package/src/router/middleware-types.ts +24 -110
- package/src/router/middleware.ts +384 -197
- package/src/router/navigation-snapshot.ts +131 -0
- package/src/router/params-util.ts +23 -0
- package/src/router/pattern-matching.ts +148 -91
- package/src/router/prefetch-cache-ttl.ts +51 -0
- package/src/router/prerender-match.ts +199 -56
- package/src/router/preview-match.ts +32 -102
- package/src/router/request-classification.ts +276 -0
- package/src/router/revalidation.ts +144 -74
- package/src/router/route-snapshot.ts +244 -0
- package/src/router/router-context.ts +8 -28
- package/src/router/router-interfaces.ts +129 -36
- package/src/router/router-options.ts +185 -23
- package/src/router/router-registry.ts +2 -5
- package/src/router/segment-resolution/fresh.ts +281 -76
- package/src/router/segment-resolution/helpers.ts +116 -31
- package/src/router/segment-resolution/loader-cache.ts +63 -37
- package/src/router/segment-resolution/revalidation.ts +493 -391
- 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 +180 -58
- package/src/router/types.ts +10 -63
- package/src/router/url-params.ts +44 -0
- package/src/router.ts +182 -54
- package/src/rsc/handler-context.ts +3 -2
- package/src/rsc/handler.ts +702 -460
- package/src/rsc/helpers.ts +168 -46
- package/src/rsc/index.ts +2 -25
- 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 +98 -19
- 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 +126 -106
- package/src/rsc/runtime-warnings.ts +23 -10
- package/src/rsc/server-action.ts +269 -114
- package/src/rsc/ssr-setup.ts +144 -0
- package/src/rsc/types.ts +34 -6
- package/src/runtime-env.ts +18 -0
- package/src/search-params.ts +49 -41
- 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 +317 -63
- 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 +425 -177
- package/src/server.ts +6 -0
- 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 +3 -19
- 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 +236 -88
- 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 +10 -45
- package/src/types/route-entry.ts +19 -7
- package/src/types/segments.ts +37 -19
- 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 +64 -91
- package/src/vite/discovery/discover-routers.ts +147 -88
- 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 +247 -145
- 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 +61 -13
- package/src/vite/discovery/virtual-module-codegen.ts +14 -34
- package/src/vite/index.ts +10 -3
- 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 +120 -0
- 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 +127 -0
- 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 +241 -287
- package/src/vite/router-discovery.ts +956 -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 +141 -34
- package/src/vite/utils/shared-utils.ts +92 -42
- package/CLAUDE.md +0 -5
- 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 -6
- package/src/network-error-thrower.tsx +0 -23
- package/src/route-definition/route-function.ts +0 -119
- 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,53 +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
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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";
|
|
44
54
|
|
|
45
55
|
/**
|
|
46
|
-
*
|
|
47
|
-
*
|
|
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.
|
|
48
59
|
*/
|
|
49
|
-
function
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
segmentType: string,
|
|
53
|
-
pathname?: string,
|
|
54
|
-
routeKey?: string,
|
|
55
|
-
params?: Record<string, string>,
|
|
60
|
+
function traceFullRefetchedParallelSlot(
|
|
61
|
+
parallelId: string,
|
|
62
|
+
belongsToRoute: boolean,
|
|
56
63
|
): void {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
promise.catch((err: unknown) => {
|
|
67
|
-
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
68
|
-
safeEmit(sink, {
|
|
69
|
-
type: "handler.error",
|
|
70
|
-
timestamp: performance.now(),
|
|
71
|
-
requestId: reqId,
|
|
72
|
-
segmentId,
|
|
73
|
-
segmentType,
|
|
74
|
-
error: errorObj,
|
|
75
|
-
handledByBoundary: true,
|
|
76
|
-
pathname,
|
|
77
|
-
routeKey,
|
|
78
|
-
params,
|
|
79
|
-
});
|
|
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",
|
|
80
73
|
});
|
|
81
74
|
}
|
|
82
75
|
|
|
@@ -95,23 +88,14 @@ function emitRevalidationDecision(
|
|
|
95
88
|
routeKey: string,
|
|
96
89
|
shouldRevalidate: boolean,
|
|
97
90
|
): void {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
type: "revalidation.decision",
|
|
107
|
-
timestamp: performance.now(),
|
|
108
|
-
requestId: routerCtx.requestId,
|
|
109
|
-
segmentId,
|
|
110
|
-
pathname,
|
|
111
|
-
routeKey,
|
|
112
|
-
shouldRevalidate,
|
|
113
|
-
});
|
|
114
|
-
}
|
|
91
|
+
observeEvent({
|
|
92
|
+
type: "revalidation.decision",
|
|
93
|
+
timestamp: performance.now(),
|
|
94
|
+
segmentId,
|
|
95
|
+
pathname,
|
|
96
|
+
routeKey,
|
|
97
|
+
shouldRevalidate,
|
|
98
|
+
});
|
|
115
99
|
}
|
|
116
100
|
|
|
117
101
|
// ---------------------------------------------------------------------------
|
|
@@ -216,6 +200,10 @@ export async function resolveLoadersWithRevalidation<TEnv>(
|
|
|
216
200
|
),
|
|
217
201
|
);
|
|
218
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
|
+
|
|
219
207
|
const loadersToRun = revalidationChecks.filter((c) => c.shouldRun);
|
|
220
208
|
const segments: ResolvedSegment[] = loadersToRun.map(
|
|
221
209
|
({ loaderEntry, loader, segmentId, index }) => ({
|
|
@@ -227,10 +215,13 @@ export async function resolveLoadersWithRevalidation<TEnv>(
|
|
|
227
215
|
params: ctx.params,
|
|
228
216
|
loaderId: loader.$$id,
|
|
229
217
|
loaderData: deps.wrapLoaderPromise(
|
|
230
|
-
|
|
218
|
+
runInsideLoaderScope(() =>
|
|
219
|
+
resolveLoaderData(loaderEntry, ctx, ctx.pathname),
|
|
220
|
+
),
|
|
231
221
|
entry,
|
|
232
222
|
segmentId,
|
|
233
223
|
ctx.pathname,
|
|
224
|
+
errorContext,
|
|
234
225
|
),
|
|
235
226
|
belongsToRoute,
|
|
236
227
|
}),
|
|
@@ -257,26 +248,95 @@ export async function resolveLoadersOnlyWithRevalidation<TEnv>(
|
|
|
257
248
|
): Promise<{ segments: ResolvedSegment[]; matchedIds: string[] }> {
|
|
258
249
|
const allLoaderSegments: ResolvedSegment[] = [];
|
|
259
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
|
+
}
|
|
260
337
|
|
|
261
338
|
for (const entry of entries) {
|
|
262
|
-
|
|
263
|
-
const { segments, matchedIds } = await resolveLoadersWithRevalidation(
|
|
264
|
-
entry,
|
|
265
|
-
context,
|
|
266
|
-
belongsToRoute,
|
|
267
|
-
clientSegmentIds,
|
|
268
|
-
prevParams,
|
|
269
|
-
request,
|
|
270
|
-
prevUrl,
|
|
271
|
-
nextUrl,
|
|
272
|
-
routeKey,
|
|
273
|
-
deps,
|
|
274
|
-
actionContext,
|
|
275
|
-
undefined, // shortCodeOverride
|
|
276
|
-
stale,
|
|
277
|
-
);
|
|
278
|
-
allLoaderSegments.push(...segments);
|
|
279
|
-
allMatchedIds.push(...matchedIds);
|
|
339
|
+
await collectEntryLoaders(entry, entry.type === "route");
|
|
280
340
|
}
|
|
281
341
|
|
|
282
342
|
return { segments: allLoaderSegments, matchedIds: allMatchedIds };
|
|
@@ -300,22 +360,20 @@ export function buildEntryRevalidateMap(
|
|
|
300
360
|
map.set(entry.shortCode, { entry, revalidate: entry.revalidate });
|
|
301
361
|
|
|
302
362
|
if (entry.type !== "parallel") {
|
|
303
|
-
for (const parallelEntry of
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
}
|
|
313
|
-
}
|
|
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
|
+
});
|
|
314
372
|
}
|
|
315
373
|
}
|
|
316
374
|
|
|
317
375
|
for (const layoutEntry of entry.layout) {
|
|
318
|
-
processEntry(layoutEntry);
|
|
376
|
+
processEntry(layoutEntry, entry.shortCode);
|
|
319
377
|
}
|
|
320
378
|
}
|
|
321
379
|
|
|
@@ -326,6 +384,97 @@ export function buildEntryRevalidateMap(
|
|
|
326
384
|
return map;
|
|
327
385
|
}
|
|
328
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
|
+
|
|
329
478
|
/**
|
|
330
479
|
* Resolve parallel segments with revalidation.
|
|
331
480
|
*/
|
|
@@ -343,11 +492,35 @@ export async function resolveParallelSegmentsWithRevalidation<TEnv>(
|
|
|
343
492
|
deps: SegmentResolutionDeps<TEnv>,
|
|
344
493
|
actionContext?: ActionContext,
|
|
345
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
|
+
},
|
|
346
514
|
): Promise<SegmentRevalidationResult> {
|
|
347
515
|
const segments: ResolvedSegment[] = [];
|
|
348
516
|
const matchedIds: string[] = [];
|
|
517
|
+
const parentChainDefault = options?.parentChainDefault ?? "type-derived";
|
|
518
|
+
const loaderOrder = options?.loaderOrder ?? "after";
|
|
349
519
|
|
|
350
|
-
|
|
520
|
+
const resolvedParallelEntries = new Set<string>();
|
|
521
|
+
for (const { slot, entry: parallelEntry } of getParallelSlotEntries(
|
|
522
|
+
entry.parallel,
|
|
523
|
+
)) {
|
|
351
524
|
invariant(
|
|
352
525
|
parallelEntry.type === "parallel",
|
|
353
526
|
`Expected parallel entry, got: ${parallelEntry.type}`,
|
|
@@ -358,141 +531,78 @@ export async function resolveParallelSegmentsWithRevalidation<TEnv>(
|
|
|
358
531
|
| ((ctx: HandlerContext<any, TEnv>) => ReactNode | Promise<ReactNode>)
|
|
359
532
|
| ReactNode
|
|
360
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];
|
|
361
538
|
|
|
362
|
-
|
|
363
|
-
const parallelId = `${entry.shortCode}.${slot}`;
|
|
539
|
+
const parallelId = `${entry.shortCode}.${slot}`;
|
|
364
540
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
// all its parallel children must be resolved and tracked.
|
|
368
|
-
// Without this, navigating to a new layout with parallels
|
|
369
|
-
// (e.g., BlogLayout with @sidebar) from a different route
|
|
370
|
-
// would silently drop those parallel segments.
|
|
371
|
-
const isNewParent = !clientSegmentIds.has(entry.shortCode);
|
|
372
|
-
if (
|
|
373
|
-
isFullRefetch ||
|
|
374
|
-
clientSegmentIds.has(parallelId) ||
|
|
375
|
-
belongsToRoute ||
|
|
376
|
-
isNewParent
|
|
377
|
-
) {
|
|
378
|
-
matchedIds.push(parallelId);
|
|
379
|
-
}
|
|
541
|
+
const isFullRefetch = clientSegmentIds.size === 0;
|
|
542
|
+
const isNewParent = !clientSegmentIds.has(entry.shortCode);
|
|
380
543
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
return true;
|
|
395
|
-
}
|
|
396
|
-
if (!clientSegmentIds.has(parallelId)) {
|
|
397
|
-
const result = belongsToRoute || isNewParent;
|
|
398
|
-
if (isTraceActive()) {
|
|
399
|
-
pushRevalidationTraceEntry({
|
|
400
|
-
segmentId: parallelId,
|
|
401
|
-
segmentType: "parallel",
|
|
402
|
-
belongsToRoute,
|
|
403
|
-
source: "parallel",
|
|
404
|
-
defaultShouldRevalidate: result,
|
|
405
|
-
finalShouldRevalidate: result,
|
|
406
|
-
reason: result ? "new-segment" : "skip-parent-chain",
|
|
407
|
-
});
|
|
408
|
-
}
|
|
409
|
-
return result;
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
const dummySegment: ResolvedSegment = {
|
|
413
|
-
id: parallelId,
|
|
414
|
-
namespace: parallelEntry.id,
|
|
415
|
-
type: "parallel",
|
|
416
|
-
index: 0,
|
|
417
|
-
component: null as any,
|
|
418
|
-
params,
|
|
419
|
-
slot,
|
|
420
|
-
belongsToRoute,
|
|
421
|
-
parallelName: `${parallelEntry.id}.${slot}`,
|
|
422
|
-
...(parallelEntry.mountPath
|
|
423
|
-
? { mountPath: parallelEntry.mountPath }
|
|
424
|
-
: {}),
|
|
425
|
-
};
|
|
426
|
-
|
|
427
|
-
return await evaluateRevalidation({
|
|
428
|
-
segment: dummySegment,
|
|
429
|
-
prevParams,
|
|
430
|
-
getPrevSegment: null,
|
|
431
|
-
request,
|
|
432
|
-
prevUrl,
|
|
433
|
-
nextUrl,
|
|
434
|
-
revalidations: parallelEntry.revalidate.map((fn, i) => ({
|
|
435
|
-
name: `revalidate${i}`,
|
|
436
|
-
fn,
|
|
437
|
-
})),
|
|
438
|
-
routeKey,
|
|
439
|
-
context,
|
|
440
|
-
actionContext,
|
|
441
|
-
stale,
|
|
442
|
-
traceSource: "parallel",
|
|
443
|
-
});
|
|
444
|
-
})();
|
|
445
|
-
emitRevalidationDecision(
|
|
446
|
-
parallelId,
|
|
447
|
-
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,
|
|
448
557
|
routeKey,
|
|
449
|
-
|
|
558
|
+
deps,
|
|
559
|
+
actionContext,
|
|
560
|
+
entry.shortCode,
|
|
561
|
+
stale,
|
|
450
562
|
);
|
|
563
|
+
segments.push(...loaderResult.segments);
|
|
564
|
+
matchedIds.push(...loaderResult.matchedIds);
|
|
565
|
+
resolvedParallelEntries.add(parallelEntry.id);
|
|
566
|
+
};
|
|
451
567
|
|
|
452
|
-
|
|
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
|
-
} else {
|
|
483
|
-
component =
|
|
484
|
-
typeof handler === "function" ? await handler(context) : handler;
|
|
485
|
-
}
|
|
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
|
+
};
|
|
486
598
|
}
|
|
487
599
|
|
|
488
|
-
|
|
600
|
+
const dummySegment: ResolvedSegment = {
|
|
489
601
|
id: parallelId,
|
|
490
602
|
namespace: parallelEntry.id,
|
|
491
603
|
type: "parallel",
|
|
492
604
|
index: 0,
|
|
493
|
-
component,
|
|
494
|
-
loading: parallelEntry.loading === false ? null : parallelEntry.loading,
|
|
495
|
-
transition: parallelEntry.transition,
|
|
605
|
+
component: null as any,
|
|
496
606
|
params,
|
|
497
607
|
slot,
|
|
498
608
|
belongsToRoute,
|
|
@@ -500,27 +610,69 @@ export async function resolveParallelSegmentsWithRevalidation<TEnv>(
|
|
|
500
610
|
...(parallelEntry.mountPath
|
|
501
611
|
? { mountPath: parallelEntry.mountPath }
|
|
502
612
|
: {}),
|
|
503
|
-
}
|
|
504
|
-
}
|
|
613
|
+
};
|
|
505
614
|
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
parallelEntry,
|
|
509
|
-
context,
|
|
510
|
-
belongsToRoute,
|
|
511
|
-
clientSegmentIds,
|
|
615
|
+
shouldResolve = await evaluateRevalidation({
|
|
616
|
+
segment: dummySegment,
|
|
512
617
|
prevParams,
|
|
618
|
+
getPrevSegment: null,
|
|
513
619
|
request,
|
|
514
620
|
prevUrl,
|
|
515
621
|
nextUrl,
|
|
622
|
+
revalidations: parallelEntry.revalidate.map((fn, i) => ({
|
|
623
|
+
name: `revalidate${i}`,
|
|
624
|
+
fn,
|
|
625
|
+
})),
|
|
516
626
|
routeKey,
|
|
517
|
-
|
|
627
|
+
context,
|
|
518
628
|
actionContext,
|
|
519
|
-
entry.shortCode,
|
|
520
629
|
stale,
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
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();
|
|
524
676
|
}
|
|
525
677
|
}
|
|
526
678
|
|
|
@@ -547,6 +699,7 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
|
|
|
547
699
|
): Promise<{ segment: ResolvedSegment; matchedId: string }> {
|
|
548
700
|
const matchedId = entry.shortCode;
|
|
549
701
|
|
|
702
|
+
let handlerRan = false;
|
|
550
703
|
const component = await revalidate(
|
|
551
704
|
async () => {
|
|
552
705
|
const hasSegment = clientSegmentIds.has(entry.shortCode);
|
|
@@ -607,6 +760,8 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
|
|
|
607
760
|
context,
|
|
608
761
|
actionContext,
|
|
609
762
|
stale,
|
|
763
|
+
traceSource:
|
|
764
|
+
entry.type === "route" ? "route-handler" : "layout-handler",
|
|
610
765
|
});
|
|
611
766
|
emitRevalidationDecision(
|
|
612
767
|
entry.shortCode,
|
|
@@ -621,20 +776,42 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
|
|
|
621
776
|
return shouldRevalidate;
|
|
622
777
|
},
|
|
623
778
|
async () => {
|
|
779
|
+
handlerRan = true;
|
|
780
|
+
const doneHandler = track(`handler:${entry.id}`, 2);
|
|
624
781
|
(context as InternalHandlerContext<any, TEnv>)._currentSegmentId =
|
|
625
782
|
entry.shortCode;
|
|
626
783
|
if (entry.type === "layout" || entry.type === "cache") {
|
|
627
|
-
|
|
784
|
+
const layoutComponent = await resolveLayoutComponent(entry, context);
|
|
785
|
+
doneHandler();
|
|
786
|
+
return layoutComponent;
|
|
628
787
|
}
|
|
629
788
|
const staticComponent = await tryStaticHandler(entry, entry.shortCode);
|
|
630
|
-
if (staticComponent !== undefined)
|
|
789
|
+
if (staticComponent !== undefined) {
|
|
790
|
+
doneHandler();
|
|
791
|
+
return staticComponent;
|
|
792
|
+
}
|
|
631
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;
|
|
632
801
|
if (!routeEntry.loading) {
|
|
633
|
-
|
|
802
|
+
const result = handleHandlerResult(
|
|
803
|
+
await observeHandler(entry.id, handler, context),
|
|
804
|
+
);
|
|
805
|
+
doneHandler();
|
|
806
|
+
return result;
|
|
634
807
|
}
|
|
635
808
|
if (!actionContext) {
|
|
636
|
-
const result = handleHandlerResult(
|
|
809
|
+
const result = handleHandlerResult(
|
|
810
|
+
observeHandler(entry.id, handler, context),
|
|
811
|
+
);
|
|
637
812
|
if (result instanceof Promise) {
|
|
813
|
+
warnOnStreamedResponse(result, routeEntry.id);
|
|
814
|
+
result.finally(doneHandler).catch(() => {});
|
|
638
815
|
const tracked = deps.trackHandler(result, {
|
|
639
816
|
segmentId: entry.shortCode,
|
|
640
817
|
segmentType: entry.type,
|
|
@@ -649,24 +826,29 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
|
|
|
649
826
|
);
|
|
650
827
|
return { content: tracked };
|
|
651
828
|
}
|
|
829
|
+
doneHandler();
|
|
652
830
|
return { content: result };
|
|
653
831
|
}
|
|
654
832
|
debugLog("segment.action", "resolving action route with awaited value", {
|
|
655
833
|
entryId: entry.id,
|
|
656
834
|
});
|
|
835
|
+
const actionResult = handleHandlerResult(
|
|
836
|
+
await observeHandler(entry.id, handler, context),
|
|
837
|
+
);
|
|
838
|
+
doneHandler();
|
|
657
839
|
return {
|
|
658
|
-
content: Promise.resolve(
|
|
659
|
-
handleHandlerResult(await routeEntry.handler(context)),
|
|
660
|
-
),
|
|
840
|
+
content: Promise.resolve(actionResult),
|
|
661
841
|
};
|
|
662
842
|
},
|
|
663
843
|
() => null,
|
|
664
844
|
);
|
|
665
845
|
|
|
846
|
+
// Normalize void handlers (undefined) to null so the reconciler's
|
|
847
|
+
// component === null checks work consistently for both void and explicit null.
|
|
666
848
|
const resolvedComponent =
|
|
667
849
|
component && typeof component === "object" && "content" in component
|
|
668
|
-
? (component as { content: ReactNode }).content
|
|
669
|
-
: component;
|
|
850
|
+
? ((component as { content: ReactNode }).content ?? null)
|
|
851
|
+
: (component ?? null);
|
|
670
852
|
|
|
671
853
|
const segment: ResolvedSegment = {
|
|
672
854
|
id: entry.shortCode,
|
|
@@ -676,13 +858,17 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
|
|
|
676
858
|
index: 0,
|
|
677
859
|
component: resolvedComponent,
|
|
678
860
|
loading: entry.loading === false ? null : entry.loading,
|
|
679
|
-
transition:
|
|
861
|
+
transition: applyViewTransitionDefault(
|
|
862
|
+
entry.transition,
|
|
863
|
+
deps.viewTransitionDefault,
|
|
864
|
+
),
|
|
680
865
|
params,
|
|
681
866
|
belongsToRoute,
|
|
682
867
|
...(entry.type === "layout" || entry.type === "cache"
|
|
683
868
|
? { layoutName: entry.id }
|
|
684
869
|
: {}),
|
|
685
870
|
...(entry.mountPath ? { mountPath: entry.mountPath } : {}),
|
|
871
|
+
_handlerRan: handlerRan,
|
|
686
872
|
};
|
|
687
873
|
|
|
688
874
|
return { segment, matchedId };
|
|
@@ -701,7 +887,6 @@ export async function resolveSegmentWithRevalidation<TEnv>(
|
|
|
701
887
|
request: Request,
|
|
702
888
|
prevUrl: URL,
|
|
703
889
|
nextUrl: URL,
|
|
704
|
-
loaderPromises: Map<string, Promise<any>>,
|
|
705
890
|
deps: SegmentResolutionDeps<TEnv>,
|
|
706
891
|
actionContext?: ActionContext,
|
|
707
892
|
stale?: boolean,
|
|
@@ -763,11 +948,11 @@ export async function resolveSegmentWithRevalidation<TEnv>(
|
|
|
763
948
|
prevUrl,
|
|
764
949
|
nextUrl,
|
|
765
950
|
routeKey,
|
|
766
|
-
loaderPromises,
|
|
767
951
|
true,
|
|
768
952
|
deps,
|
|
769
953
|
actionContext,
|
|
770
954
|
stale,
|
|
955
|
+
entry,
|
|
771
956
|
);
|
|
772
957
|
segments.push(...orphanResult.segments);
|
|
773
958
|
matchedIds.push(...orphanResult.matchedIds);
|
|
@@ -847,7 +1032,6 @@ export async function resolveSegmentWithRevalidation<TEnv>(
|
|
|
847
1032
|
prevUrl,
|
|
848
1033
|
nextUrl,
|
|
849
1034
|
routeKey,
|
|
850
|
-
loaderPromises,
|
|
851
1035
|
false,
|
|
852
1036
|
deps,
|
|
853
1037
|
actionContext,
|
|
@@ -874,11 +1058,12 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
|
|
|
874
1058
|
prevUrl: URL,
|
|
875
1059
|
nextUrl: URL,
|
|
876
1060
|
routeKey: string,
|
|
877
|
-
loaderPromises: Map<string, Promise<any>>,
|
|
878
1061
|
belongsToRoute: boolean,
|
|
879
1062
|
deps: SegmentResolutionDeps<TEnv>,
|
|
880
1063
|
actionContext?: ActionContext,
|
|
881
1064
|
stale?: boolean,
|
|
1065
|
+
/** Parent route entry — its loaders are inherited so parallel slots can access them. */
|
|
1066
|
+
parentRouteEntry?: EntryData,
|
|
882
1067
|
): Promise<SegmentRevalidationResult> {
|
|
883
1068
|
invariant(
|
|
884
1069
|
orphan.type === "layout" || orphan.type === "cache",
|
|
@@ -906,6 +1091,37 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
|
|
|
906
1091
|
segments.push(...loaderResult.segments);
|
|
907
1092
|
matchedIds.push(...loaderResult.matchedIds);
|
|
908
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
|
+
|
|
909
1125
|
// Handler-first: resolve orphan layout handler before its parallels
|
|
910
1126
|
// so ctx.set() values are visible to parallel children.
|
|
911
1127
|
matchedIds.push(orphan.shortCode);
|
|
@@ -978,157 +1194,40 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
|
|
|
978
1194
|
belongsToRoute,
|
|
979
1195
|
layoutName: orphan.id,
|
|
980
1196
|
loading: orphan.loading === false ? null : orphan.loading,
|
|
981
|
-
transition:
|
|
1197
|
+
transition: applyViewTransitionDefault(
|
|
1198
|
+
orphan.transition,
|
|
1199
|
+
deps.viewTransitionDefault,
|
|
1200
|
+
),
|
|
982
1201
|
...(orphan.mountPath ? { mountPath: orphan.mountPath } : {}),
|
|
983
1202
|
});
|
|
984
1203
|
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
| ReactNode
|
|
1013
|
-
>;
|
|
1014
|
-
|
|
1015
|
-
for (const [slot, handler] of Object.entries(slots)) {
|
|
1016
|
-
// Use orphan.shortCode (the parent layout) to match the SSR path
|
|
1017
|
-
// (resolveParallelEntry receives parentShortCode = orphan.shortCode).
|
|
1018
|
-
// Using parallelEntry.shortCode would generate IDs the client doesn't know about.
|
|
1019
|
-
const parallelId = `${orphan.shortCode}.${slot}`;
|
|
1020
|
-
matchedIds.push(parallelId);
|
|
1021
|
-
|
|
1022
|
-
const shouldResolve = await (async () => {
|
|
1023
|
-
if (!clientSegmentIds.has(parallelId)) {
|
|
1024
|
-
if (isTraceActive()) {
|
|
1025
|
-
pushRevalidationTraceEntry({
|
|
1026
|
-
segmentId: parallelId,
|
|
1027
|
-
segmentType: "parallel",
|
|
1028
|
-
belongsToRoute,
|
|
1029
|
-
source: "parallel",
|
|
1030
|
-
defaultShouldRevalidate: true,
|
|
1031
|
-
finalShouldRevalidate: true,
|
|
1032
|
-
reason: "new-segment",
|
|
1033
|
-
});
|
|
1034
|
-
}
|
|
1035
|
-
return true;
|
|
1036
|
-
}
|
|
1037
|
-
|
|
1038
|
-
const dummySegment: ResolvedSegment = {
|
|
1039
|
-
id: parallelId,
|
|
1040
|
-
namespace: parallelEntry.id,
|
|
1041
|
-
type: "parallel",
|
|
1042
|
-
index: 0,
|
|
1043
|
-
component: null as any,
|
|
1044
|
-
params,
|
|
1045
|
-
slot,
|
|
1046
|
-
belongsToRoute,
|
|
1047
|
-
parallelName: `${parallelEntry.id}.${slot}`,
|
|
1048
|
-
...(parallelEntry.mountPath
|
|
1049
|
-
? { mountPath: parallelEntry.mountPath }
|
|
1050
|
-
: {}),
|
|
1051
|
-
};
|
|
1052
|
-
|
|
1053
|
-
return await evaluateRevalidation({
|
|
1054
|
-
segment: dummySegment,
|
|
1055
|
-
prevParams,
|
|
1056
|
-
getPrevSegment: null,
|
|
1057
|
-
request,
|
|
1058
|
-
prevUrl,
|
|
1059
|
-
nextUrl,
|
|
1060
|
-
revalidations: parallelEntry.revalidate.map((fn, i) => ({
|
|
1061
|
-
name: `revalidate${i}`,
|
|
1062
|
-
fn,
|
|
1063
|
-
})),
|
|
1064
|
-
routeKey,
|
|
1065
|
-
context,
|
|
1066
|
-
actionContext,
|
|
1067
|
-
stale,
|
|
1068
|
-
traceSource: "parallel",
|
|
1069
|
-
});
|
|
1070
|
-
})();
|
|
1071
|
-
emitRevalidationDecision(
|
|
1072
|
-
parallelId,
|
|
1073
|
-
context.pathname,
|
|
1074
|
-
routeKey,
|
|
1075
|
-
shouldResolve,
|
|
1076
|
-
);
|
|
1077
|
-
|
|
1078
|
-
let component: ReactNode | undefined;
|
|
1079
|
-
if (shouldResolve) {
|
|
1080
|
-
component = await tryStaticSlot(parallelEntry, slot, parallelId);
|
|
1081
|
-
}
|
|
1082
|
-
if (component === undefined) {
|
|
1083
|
-
const hasLoadingFallback =
|
|
1084
|
-
parallelEntry.loading !== undefined &&
|
|
1085
|
-
parallelEntry.loading !== false;
|
|
1086
|
-
if (!shouldResolve) {
|
|
1087
|
-
component = null;
|
|
1088
|
-
} else if (hasLoadingFallback) {
|
|
1089
|
-
const result =
|
|
1090
|
-
typeof handler === "function" ? handler(context) : handler;
|
|
1091
|
-
if (result instanceof Promise) {
|
|
1092
|
-
const tracked = deps.trackHandler(result, {
|
|
1093
|
-
segmentId: parallelId,
|
|
1094
|
-
segmentType: "parallel",
|
|
1095
|
-
});
|
|
1096
|
-
observeStreamedHandler(
|
|
1097
|
-
tracked,
|
|
1098
|
-
parallelId,
|
|
1099
|
-
"parallel",
|
|
1100
|
-
context.pathname,
|
|
1101
|
-
routeKey,
|
|
1102
|
-
params,
|
|
1103
|
-
);
|
|
1104
|
-
component = tracked as ReactNode;
|
|
1105
|
-
} else {
|
|
1106
|
-
component = result as ReactNode;
|
|
1107
|
-
}
|
|
1108
|
-
} else {
|
|
1109
|
-
component =
|
|
1110
|
-
typeof handler === "function" ? await handler(context) : handler;
|
|
1111
|
-
}
|
|
1112
|
-
}
|
|
1113
|
-
|
|
1114
|
-
segments.push({
|
|
1115
|
-
id: parallelId,
|
|
1116
|
-
namespace: parallelEntry.id,
|
|
1117
|
-
type: "parallel",
|
|
1118
|
-
index: 0,
|
|
1119
|
-
component,
|
|
1120
|
-
loading: parallelEntry.loading === false ? null : parallelEntry.loading,
|
|
1121
|
-
transition: parallelEntry.transition,
|
|
1122
|
-
params,
|
|
1123
|
-
slot,
|
|
1124
|
-
belongsToRoute,
|
|
1125
|
-
parallelName: `${parallelEntry.id}.${slot}`,
|
|
1126
|
-
...(parallelEntry.mountPath
|
|
1127
|
-
? { mountPath: parallelEntry.mountPath }
|
|
1128
|
-
: {}),
|
|
1129
|
-
});
|
|
1130
|
-
}
|
|
1131
|
-
}
|
|
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);
|
|
1132
1231
|
|
|
1133
1232
|
return { segments, matchedIds };
|
|
1134
1233
|
}
|
|
@@ -1146,12 +1245,12 @@ export async function resolveAllSegmentsWithRevalidation<TEnv>(
|
|
|
1146
1245
|
request: Request,
|
|
1147
1246
|
prevUrl: URL,
|
|
1148
1247
|
nextUrl: URL,
|
|
1149
|
-
loaderPromises: Map<string, Promise<any>>,
|
|
1150
1248
|
actionContext: ActionContext | undefined,
|
|
1151
1249
|
interceptResult: { intercept: any; entry: EntryData } | null,
|
|
1152
1250
|
localRouteName: string,
|
|
1153
1251
|
pathname: string,
|
|
1154
1252
|
deps: SegmentResolutionDeps<TEnv>,
|
|
1253
|
+
stale?: boolean,
|
|
1155
1254
|
): Promise<{ segments: ResolvedSegment[]; matchedIds: string[] }> {
|
|
1156
1255
|
const allSegments: ResolvedSegment[] = [];
|
|
1157
1256
|
const matchedIds: string[] = [];
|
|
@@ -1178,6 +1277,11 @@ export async function resolveAllSegmentsWithRevalidation<TEnv>(
|
|
|
1178
1277
|
}
|
|
1179
1278
|
|
|
1180
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
|
+
}
|
|
1284
|
+
const doneEntry = track(`segment:${entry.id}`, 1);
|
|
1181
1285
|
const resolved = await resolveWithErrorBoundary(
|
|
1182
1286
|
nonParallelEntry,
|
|
1183
1287
|
params,
|
|
@@ -1192,18 +1296,16 @@ export async function resolveAllSegmentsWithRevalidation<TEnv>(
|
|
|
1192
1296
|
request,
|
|
1193
1297
|
prevUrl,
|
|
1194
1298
|
nextUrl,
|
|
1195
|
-
loaderPromises,
|
|
1196
1299
|
deps,
|
|
1197
1300
|
actionContext,
|
|
1198
|
-
|
|
1301
|
+
stale,
|
|
1199
1302
|
),
|
|
1200
1303
|
(seg) => ({ segments: [seg], matchedIds: [seg.id] }),
|
|
1201
1304
|
deps,
|
|
1202
|
-
telemetry
|
|
1203
|
-
? { request, url: context.url, routeKey, isPartial: true, telemetry }
|
|
1204
|
-
: undefined,
|
|
1305
|
+
{ request, url: context.url, routeKey, isPartial: true, telemetry },
|
|
1205
1306
|
pathname,
|
|
1206
1307
|
);
|
|
1308
|
+
doneEntry();
|
|
1207
1309
|
|
|
1208
1310
|
// Deduplicate segments and matchedIds by ID, matching resolveAllSegments.
|
|
1209
1311
|
// include() scopes can produce entries that resolve the same shared
|