@rangojs/router 0.0.0-experimental.b9cb8739 → 0.0.0-experimental.bdaf10aa
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 +8 -4
- package/README.md +303 -741
- package/dist/bin/rango.js +730 -184
- package/dist/testing/vitest.js +82 -0
- package/dist/vite/index.js +4344 -1335
- package/dist/vite/index.js.bak +5448 -0
- package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/package.json +86 -15
- package/skills/api-client/SKILL.md +211 -0
- package/skills/breadcrumbs/SKILL.md +85 -6
- package/skills/bundle-analysis/SKILL.md +159 -0
- package/skills/cache-guide/SKILL.md +251 -24
- package/skills/caching/SKILL.md +375 -17
- package/skills/catalog.json +271 -0
- package/skills/comparison/SKILL.md +50 -0
- package/skills/comparison/agents/openai.yaml +4 -0
- package/skills/comparison/references/framework-comparison.md +837 -0
- package/skills/composability/SKILL.md +110 -4
- package/skills/css/SKILL.md +76 -0
- package/skills/debug-manifest/SKILL.md +5 -3
- package/skills/defer-hydration/SKILL.md +235 -0
- package/skills/document-cache/SKILL.md +87 -56
- package/skills/fonts/SKILL.md +1 -1
- package/skills/handler-use/SKILL.md +364 -0
- package/skills/hooks/SKILL.md +73 -691
- package/skills/hooks/data.md +273 -0
- package/skills/hooks/handle-and-actions.md +103 -0
- package/skills/hooks/navigation.md +110 -0
- package/skills/hooks/outlets.md +41 -0
- package/skills/hooks/state.md +228 -0
- package/skills/hooks/urls.md +135 -0
- package/skills/host-router/SKILL.md +129 -27
- package/skills/i18n/SKILL.md +276 -0
- package/skills/intercept/SKILL.md +94 -18
- package/skills/layout/SKILL.md +62 -19
- package/skills/links/SKILL.md +249 -17
- package/skills/loader/SKILL.md +302 -54
- package/skills/middleware/SKILL.md +59 -16
- package/skills/migrate-nextjs/SKILL.md +745 -0
- package/skills/migrate-react-router/SKILL.md +153 -0
- package/skills/migrate-react-router/cloudflare-workers.md +129 -0
- package/skills/migrate-react-router/component-migration.md +196 -0
- package/skills/migrate-react-router/data-and-actions.md +225 -0
- package/skills/migrate-react-router/route-mapping.md +271 -0
- package/skills/mime-routes/SKILL.md +29 -2
- package/skills/observability/SKILL.md +202 -0
- package/skills/parallel/SKILL.md +225 -10
- package/skills/ppr/SKILL.md +622 -0
- package/skills/prerender/SKILL.md +178 -124
- package/skills/rango/SKILL.md +318 -24
- package/skills/react-compiler/SKILL.md +168 -0
- package/skills/response-routes/SKILL.md +138 -49
- package/skills/route/SKILL.md +172 -9
- package/skills/router-setup/SKILL.md +131 -11
- package/skills/scripts/SKILL.md +179 -0
- package/skills/server-actions/SKILL.md +776 -0
- package/skills/shell-manifest/SKILL.md +185 -0
- package/skills/streams-and-websockets/SKILL.md +283 -0
- package/skills/tailwind/SKILL.md +28 -4
- package/skills/testing/SKILL.md +130 -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 +131 -0
- package/skills/testing/loader.md +128 -0
- package/skills/testing/middleware.md +99 -0
- package/skills/testing/render-handler.md +122 -0
- package/skills/testing/response-routes.md +95 -0
- package/skills/testing/reverse-and-types.md +85 -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/theme/SKILL.md +1 -1
- package/skills/typesafety/SKILL.md +45 -616
- package/skills/typesafety/env-and-bindings.md +254 -0
- package/skills/typesafety/generated-files-and-cli.md +335 -0
- package/skills/typesafety/params-and-search.md +153 -0
- package/skills/typesafety/route-types.md +209 -0
- package/skills/use-cache/SKILL.md +74 -15
- package/skills/vercel/SKILL.md +128 -0
- package/skills/view-transitions/SKILL.md +337 -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 +257 -158
- 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/logging.ts +28 -0
- package/src/browser/merge-segment-loaders.ts +6 -4
- package/src/browser/navigation-bridge.ts +132 -33
- package/src/browser/navigation-client.ts +218 -68
- package/src/browser/navigation-store-handle.ts +38 -0
- package/src/browser/navigation-store.ts +203 -80
- package/src/browser/navigation-transaction.ts +18 -66
- package/src/browser/network-error-handler.ts +34 -7
- package/src/browser/partial-update.ts +241 -127
- package/src/browser/prefetch/cache.ts +271 -44
- package/src/browser/prefetch/fetch.ts +367 -40
- package/src/browser/prefetch/queue.ts +144 -23
- package/src/browser/prefetch/resource-ready.ts +77 -0
- package/src/browser/rango-state.ts +158 -76
- package/src/browser/react/Link.tsx +121 -16
- package/src/browser/react/NavigationProvider.tsx +240 -122
- 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 +99 -8
- package/src/browser/rsc-router.tsx +272 -80
- package/src/browser/scroll-restoration.ts +56 -22
- package/src/browser/segment-reconciler.ts +44 -7
- package/src/browser/segment-structure-assert.ts +2 -2
- package/src/browser/server-action-bridge.ts +244 -71
- package/src/browser/types.ts +136 -12
- package/src/browser/validate-redirect-origin.ts +43 -16
- package/src/build/collect-fallback-refs.ts +107 -0
- package/src/build/generate-manifest.ts +207 -158
- package/src/build/generate-route-types.ts +6 -1
- package/src/build/index.ts +11 -3
- package/src/build/prefix-tree-utils.ts +123 -0
- package/src/build/route-trie.ts +198 -41
- 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 +464 -63
- 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 +336 -110
- 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 +13 -21
- package/src/cache/cache-error.ts +104 -0
- package/src/cache/cache-key-utils.ts +58 -13
- package/src/cache/cache-policy.ts +108 -34
- package/src/cache/cache-runtime.ts +454 -97
- package/src/cache/cache-scope.ts +235 -103
- package/src/cache/cache-tag.ts +149 -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 +2446 -170
- 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 +144 -49
- package/src/cache/handle-snapshot.ts +70 -0
- package/src/cache/index.ts +24 -20
- package/src/cache/memory-segment-store.ts +243 -37
- package/src/cache/profile-registry.ts +46 -31
- package/src/cache/read-through-swr.ts +56 -12
- package/src/cache/segment-codec.ts +13 -21
- package/src/cache/shell-snapshot.ts +417 -0
- package/src/cache/tag-invalidation.ts +230 -0
- package/src/cache/taint.ts +55 -0
- package/src/cache/types.ts +194 -99
- package/src/cache/vercel/index.ts +11 -0
- package/src/cache/vercel/vercel-cache-store.ts +1132 -0
- package/src/client.rsc.tsx +41 -21
- package/src/client.tsx +116 -290
- package/src/cloudflare/index.ts +11 -0
- package/src/cloudflare/tracing.ts +108 -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 +185 -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 +104 -34
- package/src/handles/MetaTags.tsx +24 -53
- package/src/handles/Scripts.tsx +183 -0
- package/src/handles/breadcrumbs.ts +35 -8
- package/src/handles/deferred-resolution.ts +127 -0
- package/src/handles/is-thenable.ts +18 -0
- package/src/handles/meta.ts +14 -40
- package/src/handles/script.ts +244 -0
- package/src/host/cookie-handler.ts +9 -60
- package/src/host/errors.ts +13 -22
- package/src/host/index.ts +9 -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 +100 -13
- package/src/index.ts +143 -19
- 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 +215 -86
- package/src/redirect-origin.ts +114 -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 +19 -77
- package/src/route-definition/dsl-helpers.ts +485 -303
- package/src/route-definition/helper-factories.ts +28 -140
- package/src/route-definition/helpers-types.ts +153 -77
- package/src/route-definition/index.ts +4 -2
- package/src/route-definition/redirect.ts +53 -12
- package/src/route-definition/resolve-handler-use.ts +160 -0
- package/src/route-definition/use-item-types.ts +29 -0
- package/src/route-map-builder.ts +48 -21
- package/src/route-types.ts +37 -46
- 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 +130 -29
- package/src/router/handler-context.ts +83 -39
- package/src/router/instrument.ts +355 -0
- package/src/router/intercept-resolution.ts +50 -24
- package/src/router/lazy-includes.ts +89 -63
- package/src/router/loader-resolution.ts +286 -56
- package/src/router/logging.ts +5 -8
- package/src/router/manifest.ts +105 -56
- package/src/router/match-api.ts +178 -218
- package/src/router/match-context.ts +0 -22
- package/src/router/match-handlers.ts +211 -165
- package/src/router/match-middleware/background-revalidation.ts +66 -22
- package/src/router/match-middleware/cache-lookup.ts +214 -263
- package/src/router/match-middleware/cache-store.ts +105 -50
- 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 +128 -44
- package/src/router/metrics.ts +5 -34
- package/src/router/middleware-types.ts +13 -142
- package/src/router/middleware.ts +301 -177
- package/src/router/navigation-snapshot.ts +133 -0
- package/src/router/params-util.ts +23 -0
- package/src/router/parse-pattern.ts +115 -0
- package/src/router/pattern-matching.ts +181 -150
- package/src/router/prefetch-cache-ttl.ts +51 -0
- package/src/router/prefetch-limits.ts +37 -0
- package/src/router/prerender-match.ts +203 -58
- package/src/router/preview-match.ts +35 -103
- package/src/router/request-classification.ts +291 -0
- package/src/router/revalidation.ts +123 -73
- package/src/router/route-snapshot.ts +256 -0
- package/src/router/router-context.ts +11 -29
- package/src/router/router-interfaces.ts +146 -35
- package/src/router/router-options.ts +202 -15
- package/src/router/router-registry.ts +2 -5
- package/src/router/segment-resolution/fresh.ts +301 -78
- package/src/router/segment-resolution/helpers.ts +115 -30
- package/src/router/segment-resolution/loader-cache.ts +156 -39
- package/src/router/segment-resolution/loader-mask.ts +60 -0
- package/src/router/segment-resolution/loader-snapshot.ts +259 -0
- package/src/router/segment-resolution/mask-nested.ts +83 -0
- package/src/router/segment-resolution/revalidation.ts +477 -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 +56 -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 +75 -0
- package/src/router/telemetry-otel.ts +160 -200
- package/src/router/telemetry.ts +105 -20
- package/src/router/timeout.ts +0 -20
- package/src/router/tracing.ts +215 -0
- package/src/router/trie-matching.ts +171 -59
- package/src/router/types.ts +10 -63
- package/src/router/url-params.ts +57 -0
- package/src/router.ts +210 -71
- package/src/rsc/full-payload.ts +70 -0
- package/src/rsc/handler-context.ts +3 -2
- package/src/rsc/handler.ts +682 -508
- 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/nonce.ts +10 -1
- package/src/rsc/origin-guard.ts +39 -25
- package/src/rsc/progressive-enhancement.ts +138 -15
- package/src/rsc/redirect-guard.ts +100 -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 +509 -73
- package/src/rsc/runtime-warnings.ts +23 -10
- package/src/rsc/server-action.ts +287 -113
- package/src/rsc/shell-capture.ts +1190 -0
- package/src/rsc/shell-serve.ts +181 -0
- package/src/rsc/ssr-setup.ts +18 -2
- package/src/rsc/transition-gate.ts +89 -0
- package/src/rsc/types.ts +62 -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 +167 -0
- package/src/segment-system.tsx +449 -132
- package/src/serialize.ts +243 -0
- package/src/server/context.ts +367 -61
- package/src/server/cookie-parse.ts +32 -0
- package/src/server/cookie-store.ts +152 -5
- package/src/server/handle-store.ts +40 -38
- package/src/server/loader-registry.ts +38 -46
- package/src/server/request-context.ts +558 -173
- package/src/ssr/index.tsx +491 -174
- package/src/ssr/inject-rsc-eager.ts +167 -0
- package/src/ssr/ssr-root.tsx +228 -0
- package/src/static-handler.ts +27 -18
- package/src/testing/cache-status.ts +162 -0
- package/src/testing/collect-handle.ts +46 -0
- package/src/testing/dispatch.ts +813 -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 +199 -0
- package/src/testing/index.ts +105 -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 +357 -0
- package/src/testing/render-route.tsx +584 -0
- package/src/testing/run-loader.ts +385 -0
- package/src/testing/run-middleware.ts +205 -0
- package/src/testing/run-transition-when.ts +164 -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 +56 -84
- 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 +234 -82
- package/src/types/index.ts +3 -10
- package/src/types/loader-types.ts +44 -15
- package/src/types/request-scope.ts +112 -0
- package/src/types/route-config.ts +20 -52
- package/src/types/route-entry.ts +19 -7
- package/src/types/segments.ts +137 -14
- package/src/urls/include-helper.ts +40 -75
- package/src/urls/include-provider.ts +71 -0
- package/src/urls/index.ts +2 -11
- package/src/urls/path-helper-types.ts +102 -23
- package/src/urls/path-helper.ts +62 -111
- package/src/urls/pattern-types.ts +84 -19
- package/src/urls/response-types.ts +25 -22
- package/src/urls/type-extraction.ts +98 -154
- package/src/urls/urls-function.ts +1 -19
- package/src/use-loader.tsx +346 -89
- package/src/vercel/index.ts +11 -0
- package/src/vercel/tracing.ts +88 -0
- package/src/vite/debug.ts +185 -0
- package/src/vite/discovery/bundle-postprocess.ts +36 -38
- package/src/vite/discovery/dev-prerender-cache.ts +117 -0
- package/src/vite/discovery/discover-routers.ts +130 -85
- package/src/vite/discovery/discovery-errors.ts +255 -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 +15 -0
- package/src/vite/inject-client-debug.ts +88 -0
- package/src/vite/plugin-types.ts +234 -62
- 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/vercel-output.ts +384 -0
- package/src/vite/plugins/version-injector.ts +40 -29
- package/src/vite/plugins/version-plugin.ts +46 -37
- package/src/vite/plugins/virtual-entries.ts +138 -27
- package/src/vite/rango.ts +353 -303
- package/src/vite/router-discovery.ts +1090 -166
- 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 +98 -38
- package/src/vite/utils/shared-utils.ts +144 -44
- 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}`;
|
|
539
|
+
const parallelId = `${entry.shortCode}.${slot}`;
|
|
365
540
|
|
|
366
|
-
|
|
367
|
-
|
|
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
|
-
}
|
|
541
|
+
const isFullRefetch = clientSegmentIds.size === 0;
|
|
542
|
+
const isNewParent = !clientSegmentIds.has(entry.shortCode);
|
|
381
543
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
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
|
-
}
|
|
412
|
-
|
|
413
|
-
const dummySegment: ResolvedSegment = {
|
|
414
|
-
id: parallelId,
|
|
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
|
-
};
|
|
427
|
-
|
|
428
|
-
return await evaluateRevalidation({
|
|
429
|
-
segment: dummySegment,
|
|
430
|
-
prevParams,
|
|
431
|
-
getPrevSegment: null,
|
|
432
|
-
request,
|
|
433
|
-
prevUrl,
|
|
434
|
-
nextUrl,
|
|
435
|
-
revalidations: parallelEntry.revalidate.map((fn, i) => ({
|
|
436
|
-
name: `revalidate${i}`,
|
|
437
|
-
fn,
|
|
438
|
-
})),
|
|
439
|
-
routeKey,
|
|
440
|
-
context,
|
|
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,70 @@ 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
|
+
parallelId,
|
|
664
|
+
),
|
|
665
|
+
params,
|
|
666
|
+
slot,
|
|
667
|
+
_handlerRan: handlerRan,
|
|
668
|
+
belongsToRoute,
|
|
669
|
+
parallelName: `${parallelEntry.id}.${slot}`,
|
|
670
|
+
...(parallelEntry.mountPath
|
|
671
|
+
? { mountPath: parallelEntry.mountPath }
|
|
672
|
+
: {}),
|
|
673
|
+
});
|
|
674
|
+
|
|
675
|
+
if (loaderOrder === "after") {
|
|
676
|
+
await resolveSlotLoaders();
|
|
525
677
|
}
|
|
526
678
|
}
|
|
527
679
|
|
|
@@ -548,6 +700,7 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
|
|
|
548
700
|
): Promise<{ segment: ResolvedSegment; matchedId: string }> {
|
|
549
701
|
const matchedId = entry.shortCode;
|
|
550
702
|
|
|
703
|
+
let handlerRan = false;
|
|
551
704
|
const component = await revalidate(
|
|
552
705
|
async () => {
|
|
553
706
|
const hasSegment = clientSegmentIds.has(entry.shortCode);
|
|
@@ -608,6 +761,8 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
|
|
|
608
761
|
context,
|
|
609
762
|
actionContext,
|
|
610
763
|
stale,
|
|
764
|
+
traceSource:
|
|
765
|
+
entry.type === "route" ? "route-handler" : "layout-handler",
|
|
611
766
|
});
|
|
612
767
|
emitRevalidationDecision(
|
|
613
768
|
entry.shortCode,
|
|
@@ -622,6 +777,7 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
|
|
|
622
777
|
return shouldRevalidate;
|
|
623
778
|
},
|
|
624
779
|
async () => {
|
|
780
|
+
handlerRan = true;
|
|
625
781
|
const doneHandler = track(`handler:${entry.id}`, 2);
|
|
626
782
|
(context as InternalHandlerContext<any, TEnv>)._currentSegmentId =
|
|
627
783
|
entry.shortCode;
|
|
@@ -636,14 +792,26 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
|
|
|
636
792
|
return staticComponent;
|
|
637
793
|
}
|
|
638
794
|
const routeEntry = entry as Extract<EntryData, { type: "route" }>;
|
|
795
|
+
// For Passthrough routes at runtime, use the live handler instead of
|
|
796
|
+
// the build handler. At build time (context.build === true), always
|
|
797
|
+
// use the build handler from routeEntry.handler.
|
|
798
|
+
const handler =
|
|
799
|
+
!context.build && routeEntry.liveHandler
|
|
800
|
+
? routeEntry.liveHandler
|
|
801
|
+
: routeEntry.handler;
|
|
639
802
|
if (!routeEntry.loading) {
|
|
640
|
-
const result = handleHandlerResult(
|
|
803
|
+
const result = handleHandlerResult(
|
|
804
|
+
await observeHandler(entry.id, handler, context),
|
|
805
|
+
);
|
|
641
806
|
doneHandler();
|
|
642
807
|
return result;
|
|
643
808
|
}
|
|
644
809
|
if (!actionContext) {
|
|
645
|
-
const result = handleHandlerResult(
|
|
810
|
+
const result = handleHandlerResult(
|
|
811
|
+
observeHandler(entry.id, handler, context),
|
|
812
|
+
);
|
|
646
813
|
if (result instanceof Promise) {
|
|
814
|
+
warnOnStreamedResponse(result, routeEntry.id);
|
|
647
815
|
result.finally(doneHandler).catch(() => {});
|
|
648
816
|
const tracked = deps.trackHandler(result, {
|
|
649
817
|
segmentId: entry.shortCode,
|
|
@@ -666,7 +834,7 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
|
|
|
666
834
|
entryId: entry.id,
|
|
667
835
|
});
|
|
668
836
|
const actionResult = handleHandlerResult(
|
|
669
|
-
await
|
|
837
|
+
await observeHandler(entry.id, handler, context),
|
|
670
838
|
);
|
|
671
839
|
doneHandler();
|
|
672
840
|
return {
|
|
@@ -676,10 +844,12 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
|
|
|
676
844
|
() => null,
|
|
677
845
|
);
|
|
678
846
|
|
|
847
|
+
// Normalize void handlers (undefined) to null so the reconciler's
|
|
848
|
+
// component === null checks work consistently for both void and explicit null.
|
|
679
849
|
const resolvedComponent =
|
|
680
850
|
component && typeof component === "object" && "content" in component
|
|
681
|
-
? (component as { content: ReactNode }).content
|
|
682
|
-
: component;
|
|
851
|
+
? ((component as { content: ReactNode }).content ?? null)
|
|
852
|
+
: (component ?? null);
|
|
683
853
|
|
|
684
854
|
const segment: ResolvedSegment = {
|
|
685
855
|
id: entry.shortCode,
|
|
@@ -689,13 +859,18 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
|
|
|
689
859
|
index: 0,
|
|
690
860
|
component: resolvedComponent,
|
|
691
861
|
loading: entry.loading === false ? null : entry.loading,
|
|
692
|
-
transition:
|
|
862
|
+
transition: applyViewTransitionDefault(
|
|
863
|
+
entry.transition,
|
|
864
|
+
deps.viewTransitionDefault,
|
|
865
|
+
entry.shortCode,
|
|
866
|
+
),
|
|
693
867
|
params,
|
|
694
868
|
belongsToRoute,
|
|
695
869
|
...(entry.type === "layout" || entry.type === "cache"
|
|
696
870
|
? { layoutName: entry.id }
|
|
697
871
|
: {}),
|
|
698
872
|
...(entry.mountPath ? { mountPath: entry.mountPath } : {}),
|
|
873
|
+
_handlerRan: handlerRan,
|
|
699
874
|
};
|
|
700
875
|
|
|
701
876
|
return { segment, matchedId };
|
|
@@ -714,7 +889,6 @@ export async function resolveSegmentWithRevalidation<TEnv>(
|
|
|
714
889
|
request: Request,
|
|
715
890
|
prevUrl: URL,
|
|
716
891
|
nextUrl: URL,
|
|
717
|
-
loaderPromises: Map<string, Promise<any>>,
|
|
718
892
|
deps: SegmentResolutionDeps<TEnv>,
|
|
719
893
|
actionContext?: ActionContext,
|
|
720
894
|
stale?: boolean,
|
|
@@ -776,11 +950,11 @@ export async function resolveSegmentWithRevalidation<TEnv>(
|
|
|
776
950
|
prevUrl,
|
|
777
951
|
nextUrl,
|
|
778
952
|
routeKey,
|
|
779
|
-
loaderPromises,
|
|
780
953
|
true,
|
|
781
954
|
deps,
|
|
782
955
|
actionContext,
|
|
783
956
|
stale,
|
|
957
|
+
entry,
|
|
784
958
|
);
|
|
785
959
|
segments.push(...orphanResult.segments);
|
|
786
960
|
matchedIds.push(...orphanResult.matchedIds);
|
|
@@ -860,7 +1034,6 @@ export async function resolveSegmentWithRevalidation<TEnv>(
|
|
|
860
1034
|
prevUrl,
|
|
861
1035
|
nextUrl,
|
|
862
1036
|
routeKey,
|
|
863
|
-
loaderPromises,
|
|
864
1037
|
false,
|
|
865
1038
|
deps,
|
|
866
1039
|
actionContext,
|
|
@@ -887,11 +1060,12 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
|
|
|
887
1060
|
prevUrl: URL,
|
|
888
1061
|
nextUrl: URL,
|
|
889
1062
|
routeKey: string,
|
|
890
|
-
loaderPromises: Map<string, Promise<any>>,
|
|
891
1063
|
belongsToRoute: boolean,
|
|
892
1064
|
deps: SegmentResolutionDeps<TEnv>,
|
|
893
1065
|
actionContext?: ActionContext,
|
|
894
1066
|
stale?: boolean,
|
|
1067
|
+
/** Parent route entry — its loaders are inherited so parallel slots can access them. */
|
|
1068
|
+
parentRouteEntry?: EntryData,
|
|
895
1069
|
): Promise<SegmentRevalidationResult> {
|
|
896
1070
|
invariant(
|
|
897
1071
|
orphan.type === "layout" || orphan.type === "cache",
|
|
@@ -919,6 +1093,37 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
|
|
|
919
1093
|
segments.push(...loaderResult.segments);
|
|
920
1094
|
matchedIds.push(...loaderResult.matchedIds);
|
|
921
1095
|
|
|
1096
|
+
// Inherit parent route's loaders so parallel slots inside this layout
|
|
1097
|
+
// can access them via useLoader(). See resolveOrphanLayout in fresh.ts.
|
|
1098
|
+
if (
|
|
1099
|
+
parentRouteEntry &&
|
|
1100
|
+
parentRouteEntry.loader &&
|
|
1101
|
+
parentRouteEntry.loader.length > 0 &&
|
|
1102
|
+
Object.keys(orphan.parallel).length > 0
|
|
1103
|
+
) {
|
|
1104
|
+
const inheritedResult = await resolveLoadersWithRevalidation(
|
|
1105
|
+
parentRouteEntry,
|
|
1106
|
+
context,
|
|
1107
|
+
belongsToRoute,
|
|
1108
|
+
clientSegmentIds,
|
|
1109
|
+
prevParams,
|
|
1110
|
+
request,
|
|
1111
|
+
prevUrl,
|
|
1112
|
+
nextUrl,
|
|
1113
|
+
routeKey,
|
|
1114
|
+
deps,
|
|
1115
|
+
actionContext,
|
|
1116
|
+
orphan.shortCode,
|
|
1117
|
+
stale,
|
|
1118
|
+
);
|
|
1119
|
+
// Tag as inherited so buildMatchResult can deduplicate when safe
|
|
1120
|
+
for (const s of inheritedResult.segments) {
|
|
1121
|
+
s._inherited = true;
|
|
1122
|
+
}
|
|
1123
|
+
segments.push(...inheritedResult.segments);
|
|
1124
|
+
matchedIds.push(...inheritedResult.matchedIds);
|
|
1125
|
+
}
|
|
1126
|
+
|
|
922
1127
|
// Handler-first: resolve orphan layout handler before its parallels
|
|
923
1128
|
// so ctx.set() values are visible to parallel children.
|
|
924
1129
|
matchedIds.push(orphan.shortCode);
|
|
@@ -991,157 +1196,41 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
|
|
|
991
1196
|
belongsToRoute,
|
|
992
1197
|
layoutName: orphan.id,
|
|
993
1198
|
loading: orphan.loading === false ? null : orphan.loading,
|
|
994
|
-
transition:
|
|
1199
|
+
transition: applyViewTransitionDefault(
|
|
1200
|
+
orphan.transition,
|
|
1201
|
+
deps.viewTransitionDefault,
|
|
1202
|
+
orphan.shortCode,
|
|
1203
|
+
),
|
|
995
1204
|
...(orphan.mountPath ? { mountPath: orphan.mountPath } : {}),
|
|
996
1205
|
});
|
|
997
1206
|
|
|
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
|
-
}
|
|
1207
|
+
// Resolve the orphan layout's parallel slots through the shared main-path
|
|
1208
|
+
// helper. The orphan policy is carried by explicit args, byte-for-byte:
|
|
1209
|
+
// - parentChainDefault "force-render": an unknown parent-chain slot seeds
|
|
1210
|
+
// `true` (orphan parallels always belong to the route — the #482 guard),
|
|
1211
|
+
// where the main path would seed `belongsToRoute || isNewParent`.
|
|
1212
|
+
// - loaderOrder "before": a slot's loaders are emitted before the slot
|
|
1213
|
+
// segment, matching the prior inlined order.
|
|
1214
|
+
// `entry.shortCode` inside the helper is `orphan.shortCode` (orphan is passed
|
|
1215
|
+
// as `entry`), so the parallel ids + loader shortCodeOverride are unchanged.
|
|
1216
|
+
const parallelResult = await resolveParallelSegmentsWithRevalidation(
|
|
1217
|
+
orphan,
|
|
1218
|
+
params,
|
|
1219
|
+
context,
|
|
1220
|
+
belongsToRoute,
|
|
1221
|
+
clientSegmentIds,
|
|
1222
|
+
prevParams,
|
|
1223
|
+
request,
|
|
1224
|
+
prevUrl,
|
|
1225
|
+
nextUrl,
|
|
1226
|
+
routeKey,
|
|
1227
|
+
deps,
|
|
1228
|
+
actionContext,
|
|
1229
|
+
stale,
|
|
1230
|
+
{ parentChainDefault: "force-render", loaderOrder: "before" },
|
|
1231
|
+
);
|
|
1232
|
+
segments.push(...parallelResult.segments);
|
|
1233
|
+
matchedIds.push(...parallelResult.matchedIds);
|
|
1145
1234
|
|
|
1146
1235
|
return { segments, matchedIds };
|
|
1147
1236
|
}
|
|
@@ -1159,12 +1248,12 @@ export async function resolveAllSegmentsWithRevalidation<TEnv>(
|
|
|
1159
1248
|
request: Request,
|
|
1160
1249
|
prevUrl: URL,
|
|
1161
1250
|
nextUrl: URL,
|
|
1162
|
-
loaderPromises: Map<string, Promise<any>>,
|
|
1163
1251
|
actionContext: ActionContext | undefined,
|
|
1164
1252
|
interceptResult: { intercept: any; entry: EntryData } | null,
|
|
1165
1253
|
localRouteName: string,
|
|
1166
1254
|
pathname: string,
|
|
1167
1255
|
deps: SegmentResolutionDeps<TEnv>,
|
|
1256
|
+
stale?: boolean,
|
|
1168
1257
|
): Promise<{ segments: ResolvedSegment[]; matchedIds: string[] }> {
|
|
1169
1258
|
const allSegments: ResolvedSegment[] = [];
|
|
1170
1259
|
const matchedIds: string[] = [];
|
|
@@ -1191,6 +1280,10 @@ export async function resolveAllSegmentsWithRevalidation<TEnv>(
|
|
|
1191
1280
|
}
|
|
1192
1281
|
|
|
1193
1282
|
const nonParallelEntry = entry as Exclude<EntryData, { type: "parallel" }>;
|
|
1283
|
+
if (entry.type === "cache") {
|
|
1284
|
+
const store = RangoContext.getStore();
|
|
1285
|
+
if (store) store.insideCacheScope = true;
|
|
1286
|
+
}
|
|
1194
1287
|
const doneEntry = track(`segment:${entry.id}`, 1);
|
|
1195
1288
|
const resolved = await resolveWithErrorBoundary(
|
|
1196
1289
|
nonParallelEntry,
|
|
@@ -1206,10 +1299,9 @@ export async function resolveAllSegmentsWithRevalidation<TEnv>(
|
|
|
1206
1299
|
request,
|
|
1207
1300
|
prevUrl,
|
|
1208
1301
|
nextUrl,
|
|
1209
|
-
loaderPromises,
|
|
1210
1302
|
deps,
|
|
1211
1303
|
actionContext,
|
|
1212
|
-
|
|
1304
|
+
stale,
|
|
1213
1305
|
),
|
|
1214
1306
|
(seg) => ({ segments: [seg], matchedIds: [seg.id] }),
|
|
1215
1307
|
deps,
|