@rangojs/router 0.0.0-experimental.9c9afef3 → 0.0.0-experimental.a014d2b7
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 -0
- package/README.md +245 -49
- package/dist/bin/rango.js +440 -133
- package/dist/testing/vitest.js +82 -0
- package/dist/vite/index.js +3373 -1176
- package/dist/vite/index.js.bak +5448 -0
- package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/package.json +68 -14
- package/skills/api-client/SKILL.md +211 -0
- package/skills/breadcrumbs/SKILL.md +64 -2
- package/skills/bundle-analysis/SKILL.md +159 -0
- package/skills/cache-guide/SKILL.md +224 -32
- package/skills/caching/SKILL.md +279 -17
- package/skills/composability/SKILL.md +27 -3
- package/skills/css/SKILL.md +76 -0
- package/skills/debug-manifest/SKILL.md +4 -2
- package/skills/document-cache/SKILL.md +78 -55
- package/skills/handler-use/SKILL.md +364 -0
- package/skills/hooks/SKILL.md +250 -30
- package/skills/host-router/SKILL.md +83 -23
- package/skills/i18n/SKILL.md +276 -0
- package/skills/intercept/SKILL.md +87 -18
- package/skills/layout/SKILL.md +35 -9
- package/skills/links/SKILL.md +249 -17
- package/skills/loader/SKILL.md +235 -9
- 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 +77 -7
- package/skills/prerender/SKILL.md +172 -125
- package/skills/rango/SKILL.md +251 -22
- package/skills/react-compiler/SKILL.md +168 -0
- package/skills/response-routes/SKILL.md +123 -48
- package/skills/route/SKILL.md +70 -5
- package/skills/router-setup/SKILL.md +65 -8
- package/skills/scripts/SKILL.md +179 -0
- package/skills/server-actions/SKILL.md +775 -0
- package/skills/streams-and-websockets/SKILL.md +283 -0
- package/skills/tailwind/SKILL.md +27 -3
- package/skills/testing/SKILL.md +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 +129 -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 +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 +322 -29
- package/skills/use-cache/SKILL.md +57 -14
- 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 +192 -150
- package/src/browser/history-state.ts +21 -0
- package/src/browser/index.ts +3 -3
- package/src/browser/invalidate-client-cache.ts +52 -0
- package/src/browser/navigation-bridge.ts +131 -30
- package/src/browser/navigation-client.ts +186 -100
- package/src/browser/navigation-store-handle.ts +38 -0
- package/src/browser/navigation-store.ts +157 -74
- package/src/browser/navigation-transaction.ts +9 -59
- package/src/browser/network-error-handler.ts +34 -7
- package/src/browser/partial-update.ts +165 -112
- package/src/browser/prefetch/cache.ts +205 -62
- package/src/browser/prefetch/fetch.ts +347 -39
- package/src/browser/prefetch/queue.ts +42 -8
- package/src/browser/rango-state.ts +158 -76
- package/src/browser/react/Link.tsx +102 -15
- package/src/browser/react/NavigationProvider.tsx +295 -119
- package/src/browser/react/ScrollRestoration.tsx +10 -6
- package/src/browser/react/context.ts +7 -2
- package/src/browser/react/deferred-handle-resolution.ts +75 -0
- 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 +114 -24
- package/src/browser/scroll-restoration.ts +37 -22
- package/src/browser/segment-reconciler.ts +36 -14
- package/src/browser/segment-structure-assert.ts +2 -2
- package/src/browser/server-action-bridge.ts +222 -72
- package/src/browser/types.ts +102 -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 +65 -40
- package/src/build/generate-route-types.ts +5 -1
- package/src/build/index.ts +8 -2
- package/src/build/prefix-tree-utils.ts +123 -0
- package/src/build/route-trie.ts +165 -36
- package/src/build/route-types/ast-route-extraction.ts +15 -8
- package/src/build/route-types/codegen.ts +16 -5
- package/src/build/route-types/include-resolution.ts +125 -24
- package/src/build/route-types/param-extraction.ts +6 -3
- package/src/build/route-types/per-module-writer.ts +22 -6
- package/src/build/route-types/router-processing.ts +260 -94
- 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 +224 -41
- package/src/cache/cache-scope.ts +188 -82
- 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 +1989 -378
- 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 +6 -16
- package/src/cache/document-cache.ts +89 -21
- package/src/cache/handle-snapshot.ts +70 -0
- package/src/cache/index.ts +10 -20
- package/src/cache/memory-segment-store.ts +136 -37
- package/src/cache/profile-registry.ts +46 -31
- package/src/cache/read-through-swr.ts +56 -12
- package/src/cache/segment-codec.ts +9 -17
- package/src/cache/tag-invalidation.ts +230 -0
- package/src/cache/types.ts +37 -100
- package/src/client.rsc.tsx +44 -21
- package/src/client.tsx +119 -290
- package/src/cloudflare/index.ts +11 -0
- package/src/cloudflare/tracing.ts +109 -0
- package/src/component-utils.ts +19 -0
- package/src/components/DefaultDocument.tsx +8 -2
- package/src/context-var.ts +18 -6
- 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 +62 -19
- package/src/handles/Scripts.tsx +183 -0
- package/src/handles/breadcrumbs.ts +37 -8
- package/src/handles/is-thenable.ts +19 -0
- package/src/handles/meta.ts +51 -40
- package/src/handles/script.ts +244 -0
- package/src/host/cookie-handler.ts +9 -60
- package/src/host/errors.ts +0 -24
- package/src/host/index.ts +8 -2
- package/src/host/pattern-matcher.ts +23 -52
- package/src/host/router.ts +107 -99
- package/src/host/testing.ts +40 -27
- package/src/host/types.ts +37 -4
- package/src/host/utils.ts +1 -1
- package/src/href-client.ts +137 -22
- package/src/index.rsc.ts +99 -13
- package/src/index.ts +139 -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 +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 +19 -77
- package/src/route-definition/dsl-helpers.ts +461 -304
- package/src/route-definition/helper-factories.ts +28 -140
- package/src/route-definition/helpers-types.ts +143 -69
- package/src/route-definition/index.ts +4 -2
- package/src/route-definition/redirect.ts +51 -10
- 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 +0 -16
- 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 +44 -23
- package/src/router/handler-context.ts +52 -31
- package/src/router/instrument.ts +350 -0
- package/src/router/intercept-resolution.ts +48 -24
- package/src/router/lazy-includes.ts +15 -52
- package/src/router/loader-resolution.ts +268 -56
- package/src/router/logging.ts +0 -6
- package/src/router/manifest.ts +40 -42
- package/src/router/match-api.ts +124 -204
- package/src/router/match-context.ts +0 -22
- package/src/router/match-handlers.ts +58 -58
- package/src/router/match-middleware/background-revalidation.ts +40 -24
- package/src/router/match-middleware/cache-lookup.ts +170 -276
- package/src/router/match-middleware/cache-store.ts +64 -52
- package/src/router/match-middleware/intercept-resolution.ts +0 -22
- package/src/router/match-middleware/segment-resolution.ts +45 -14
- package/src/router/match-pipelines.ts +1 -42
- package/src/router/match-result.ts +87 -39
- package/src/router/metrics.ts +0 -34
- package/src/router/middleware-types.ts +7 -140
- package/src/router/middleware.ts +266 -169
- package/src/router/navigation-snapshot.ts +131 -0
- package/src/router/params-util.ts +23 -0
- package/src/router/pattern-matching.ts +132 -90
- package/src/router/prefetch-cache-ttl.ts +51 -0
- package/src/router/prerender-match.ts +195 -56
- package/src/router/preview-match.ts +32 -102
- package/src/router/request-classification.ts +276 -0
- package/src/router/revalidation.ts +123 -73
- package/src/router/route-snapshot.ts +244 -0
- package/src/router/router-context.ts +3 -28
- package/src/router/router-interfaces.ts +115 -35
- package/src/router/router-options.ts +172 -15
- package/src/router/router-registry.ts +2 -5
- package/src/router/segment-resolution/fresh.ts +162 -84
- package/src/router/segment-resolution/helpers.ts +86 -6
- package/src/router/segment-resolution/loader-cache.ts +76 -39
- package/src/router/segment-resolution/revalidation.ts +351 -321
- 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 +6 -5
- package/src/router/state-cookie-name.ts +33 -0
- package/src/router/substitute-pattern-params.ts +56 -0
- package/src/router/telemetry-otel.ts +161 -199
- package/src/router/telemetry.ts +96 -19
- package/src/router/timeout.ts +0 -20
- package/src/router/tracing.ts +206 -0
- package/src/router/trie-matching.ts +163 -59
- package/src/router/types.ts +9 -63
- package/src/router/url-params.ts +44 -0
- package/src/router.ts +157 -54
- package/src/rsc/handler-context.ts +3 -2
- package/src/rsc/handler.ts +655 -529
- 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 +122 -31
- package/src/rsc/manifest-init.ts +33 -42
- package/src/rsc/origin-guard.ts +39 -25
- package/src/rsc/progressive-enhancement.ts +131 -14
- 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 +109 -74
- package/src/rsc/runtime-warnings.ts +23 -10
- package/src/rsc/server-action.ts +287 -115
- package/src/rsc/ssr-setup.ts +18 -2
- package/src/rsc/transition-gate.ts +89 -0
- package/src/rsc/types.ts +29 -9
- package/src/runtime-env.ts +18 -0
- package/src/search-params.ts +35 -30
- package/src/segment-content-promise.ts +67 -0
- package/src/segment-loader-promise.ts +149 -0
- package/src/segment-system.tsx +236 -202
- package/src/serialize.ts +243 -0
- package/src/server/context.ts +224 -52
- 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 +38 -46
- package/src/server/request-context.ts +401 -173
- package/src/ssr/index.tsx +24 -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 +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 +581 -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 +20 -58
- package/src/theme/ThemeScript.tsx +7 -9
- package/src/theme/constants.ts +52 -13
- package/src/theme/index.ts +0 -7
- package/src/theme/theme-context.ts +1 -5
- package/src/theme/theme-script.ts +22 -21
- package/src/theme/use-theme.ts +0 -3
- package/src/types/boundaries.ts +0 -35
- package/src/types/cache-types.ts +17 -8
- package/src/types/error-types.ts +30 -90
- package/src/types/global-namespace.ts +54 -41
- package/src/types/handler-context.ts +125 -71
- package/src/types/index.ts +3 -10
- package/src/types/loader-types.ts +40 -11
- package/src/types/request-scope.ts +112 -0
- package/src/types/route-config.ts +6 -50
- package/src/types/route-entry.ts +12 -7
- package/src/types/segments.ts +136 -15
- package/src/urls/include-helper.ts +33 -70
- package/src/urls/index.ts +1 -11
- package/src/urls/path-helper-types.ts +68 -18
- 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 -19
- package/src/use-loader.tsx +346 -89
- package/src/vite/debug.ts +185 -0
- package/src/vite/discovery/bundle-postprocess.ts +36 -38
- package/src/vite/discovery/discover-routers.ts +130 -85
- package/src/vite/discovery/discovery-errors.ts +194 -0
- package/src/vite/discovery/gate-state.ts +171 -0
- package/src/vite/discovery/prerender-collection.ts +214 -132
- package/src/vite/discovery/route-types-writer.ts +40 -84
- package/src/vite/discovery/self-gen-tracking.ts +27 -1
- package/src/vite/discovery/state.ts +57 -4
- package/src/vite/discovery/virtual-module-codegen.ts +14 -34
- package/src/vite/index.ts +6 -0
- package/src/vite/inject-client-debug.ts +36 -0
- package/src/vite/plugin-types.ts +178 -5
- 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 +48 -95
- 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 +64 -170
- package/src/vite/plugins/refresh-cmd.ts +89 -27
- package/src/vite/plugins/use-cache-transform.ts +73 -83
- package/src/vite/plugins/version-injector.ts +40 -29
- package/src/vite/plugins/version-plugin.ts +37 -40
- package/src/vite/plugins/virtual-entries.ts +39 -25
- package/src/vite/rango.ts +118 -114
- package/src/vite/router-discovery.ts +941 -142
- package/src/vite/utils/ast-handler-extract.ts +26 -35
- package/src/vite/utils/banner.ts +1 -1
- package/src/vite/utils/bundle-analysis.ts +10 -15
- package/src/vite/utils/client-chunks.ts +184 -0
- package/src/vite/utils/directive-prologue.ts +40 -0
- package/src/vite/utils/forward-user-plugins.ts +171 -0
- package/src/vite/utils/manifest-utils.ts +4 -59
- package/src/vite/utils/package-resolution.ts +20 -52
- package/src/vite/utils/prerender-utils.ts +81 -34
- package/src/vite/utils/shared-utils.ts +92 -42
- package/src/browser/action-response-classifier.ts +0 -99
- package/src/browser/debug-channel.ts +0 -93
- 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
|
@@ -19,61 +19,24 @@ import type {
|
|
|
19
19
|
} from "../../types";
|
|
20
20
|
import type { SegmentResolutionDeps } from "../types.js";
|
|
21
21
|
import { resolveLoaderData } from "./loader-cache.js";
|
|
22
|
-
import { _getRequestContext } from "../../server/request-context.js";
|
|
23
|
-
import { appendMetric } from "../metrics.js";
|
|
24
22
|
import {
|
|
25
23
|
handleHandlerResult,
|
|
26
24
|
tryStaticHandler,
|
|
27
25
|
tryStaticSlot,
|
|
28
26
|
resolveLayoutComponent,
|
|
29
27
|
resolveWithErrorBoundary,
|
|
28
|
+
warnOnStreamedResponse,
|
|
29
|
+
buildLoaderErrorContext,
|
|
30
30
|
} from "./helpers.js";
|
|
31
|
+
import { applyViewTransitionDefault } from "./view-transition-default.js";
|
|
31
32
|
import { getRouterContext } from "../router-context.js";
|
|
32
|
-
import {
|
|
33
|
-
import {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Attach a fire-and-forget rejection observer to a streamed handler promise.
|
|
41
|
-
* React catches the actual error via its error boundary; this only emits
|
|
42
|
-
* the handler.error telemetry event.
|
|
43
|
-
*/
|
|
44
|
-
function observeStreamedHandler(
|
|
45
|
-
promise: Promise<ReactNode>,
|
|
46
|
-
segmentId: string,
|
|
47
|
-
segmentType: string,
|
|
48
|
-
pathname?: string,
|
|
49
|
-
routeKey?: string,
|
|
50
|
-
params?: Record<string, string>,
|
|
51
|
-
): void {
|
|
52
|
-
let routerCtx;
|
|
53
|
-
try {
|
|
54
|
-
routerCtx = getRouterContext();
|
|
55
|
-
} catch {
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
if (!routerCtx?.telemetry) return;
|
|
59
|
-
const sink = resolveSink(routerCtx.telemetry);
|
|
60
|
-
const reqId = routerCtx.requestId;
|
|
61
|
-
promise.catch((err: unknown) => {
|
|
62
|
-
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
63
|
-
safeEmit(sink, {
|
|
64
|
-
type: "handler.error",
|
|
65
|
-
timestamp: performance.now(),
|
|
66
|
-
requestId: reqId,
|
|
67
|
-
segmentId,
|
|
68
|
-
segmentType,
|
|
69
|
-
error: errorObj,
|
|
70
|
-
handledByBoundary: true,
|
|
71
|
-
pathname,
|
|
72
|
-
routeKey,
|
|
73
|
-
params,
|
|
74
|
-
});
|
|
75
|
-
});
|
|
76
|
-
}
|
|
33
|
+
import { observeStreamedHandler } from "./streamed-handler-telemetry.js";
|
|
34
|
+
import { observeHandler } from "../instrument.js";
|
|
35
|
+
import {
|
|
36
|
+
track,
|
|
37
|
+
RangoContext,
|
|
38
|
+
runInsideLoaderScope,
|
|
39
|
+
} from "../../server/context.js";
|
|
77
40
|
|
|
78
41
|
// ---------------------------------------------------------------------------
|
|
79
42
|
// Fresh path (full match, no revalidation)
|
|
@@ -96,7 +59,13 @@ export async function resolveLoaders<TEnv>(
|
|
|
96
59
|
const shortCode = shortCodeOverride ?? entry.shortCode;
|
|
97
60
|
const hasLoading = "loading" in entry && entry.loading !== undefined;
|
|
98
61
|
const loadingDisabled = hasLoading && entry.loading === false;
|
|
99
|
-
|
|
62
|
+
|
|
63
|
+
// Error context for wrapLoaderPromise: without it, a throwing DSL loader never
|
|
64
|
+
// fires createRouter({ onError }) (phase "loader") nor emits the loader.error
|
|
65
|
+
// telemetry event — wrapLoaderPromise only builds the onError/telemetry path
|
|
66
|
+
// when errorContext is supplied. Built from ctx so the live render path reports
|
|
67
|
+
// loader failures the same way handlers/actions/routing/fetchable-loaders do.
|
|
68
|
+
const errorContext = buildLoaderErrorContext(ctx);
|
|
100
69
|
|
|
101
70
|
if (!loadingDisabled) {
|
|
102
71
|
// Streaming loaders: promises kick off now, settle during RSC serialization.
|
|
@@ -112,10 +81,13 @@ export async function resolveLoaders<TEnv>(
|
|
|
112
81
|
params: ctx.params,
|
|
113
82
|
loaderId: loader.$$id,
|
|
114
83
|
loaderData: deps.wrapLoaderPromise(
|
|
115
|
-
|
|
84
|
+
runInsideLoaderScope(() =>
|
|
85
|
+
resolveLoaderData(loaderEntry, ctx, ctx.pathname),
|
|
86
|
+
),
|
|
116
87
|
entry,
|
|
117
88
|
segmentId,
|
|
118
89
|
ctx.pathname,
|
|
90
|
+
errorContext,
|
|
119
91
|
),
|
|
120
92
|
belongsToRoute,
|
|
121
93
|
};
|
|
@@ -126,44 +98,46 @@ export async function resolveLoaders<TEnv>(
|
|
|
126
98
|
|
|
127
99
|
// Loading disabled: still start all loaders in parallel, but only emit
|
|
128
100
|
// settled promises so handlers don't stream loading placeholders.
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
101
|
+
//
|
|
102
|
+
// Wrap each loader promise with wrapLoaderPromise BEFORE awaiting. The wrapped
|
|
103
|
+
// promise resolves to a LoaderDataResult and never rejects, routing a failed
|
|
104
|
+
// loader to its own per-loader error boundary. Awaiting the RAW promises here
|
|
105
|
+
// instead would (1) propagate a rejection to the segment-level boundary,
|
|
106
|
+
// collapsing the whole entry and discarding successful sibling data, and
|
|
107
|
+
// (2) leave the other in-flight raw promises without a .catch, producing
|
|
108
|
+
// unhandled rejections. Mirrors the loading path and intercept-resolution.
|
|
109
|
+
const pendingLoaderData = loaderEntries.map((loaderEntry, i) => {
|
|
110
|
+
const { loader } = loaderEntry;
|
|
111
|
+
const segmentId = `${shortCode}D${i}.${loader.$$id}`;
|
|
112
|
+
const wrapped = deps.wrapLoaderPromise(
|
|
113
|
+
runInsideLoaderScope(() =>
|
|
114
|
+
resolveLoaderData(loaderEntry, ctx, ctx.pathname),
|
|
115
|
+
),
|
|
116
|
+
entry,
|
|
117
|
+
segmentId,
|
|
118
|
+
ctx.pathname,
|
|
119
|
+
errorContext,
|
|
120
|
+
);
|
|
121
|
+
return { wrapped, segmentId };
|
|
133
122
|
});
|
|
134
|
-
await Promise.all(pendingLoaderData.map((p) => p.
|
|
123
|
+
await Promise.all(pendingLoaderData.map((p) => p.wrapped));
|
|
135
124
|
|
|
136
125
|
return loaderEntries.map((loaderEntry, i) => {
|
|
137
126
|
const { loader } = loaderEntry;
|
|
138
|
-
const segmentId = `${shortCode}D${i}.${loader.$$id}`;
|
|
139
127
|
const pending = pendingLoaderData[i]!;
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
const batchEnd = performance.now();
|
|
145
|
-
appendMetric(
|
|
146
|
-
ms,
|
|
147
|
-
`loader:${loader.$$id}`,
|
|
148
|
-
pending.start,
|
|
149
|
-
batchEnd - pending.start,
|
|
150
|
-
2,
|
|
151
|
-
);
|
|
152
|
-
}
|
|
128
|
+
// The "loader:<id>" perf metric is recorded by observePhase at the single
|
|
129
|
+
// loader-metering site (useLoader, reached via ctx.use during
|
|
130
|
+
// resolveLoaderData), with the real per-loader duration rather than a
|
|
131
|
+
// Promise.all batch ceiling.
|
|
153
132
|
return {
|
|
154
|
-
id: segmentId,
|
|
133
|
+
id: pending.segmentId,
|
|
155
134
|
namespace: entry.id,
|
|
156
135
|
type: "loader" as const,
|
|
157
136
|
index: i,
|
|
158
137
|
component: null,
|
|
159
138
|
params: ctx.params,
|
|
160
139
|
loaderId: loader.$$id,
|
|
161
|
-
loaderData:
|
|
162
|
-
pending.promise,
|
|
163
|
-
entry,
|
|
164
|
-
segmentId,
|
|
165
|
-
ctx.pathname,
|
|
166
|
-
),
|
|
140
|
+
loaderData: pending.wrapped,
|
|
167
141
|
belongsToRoute,
|
|
168
142
|
};
|
|
169
143
|
});
|
|
@@ -175,6 +149,15 @@ export async function resolveLoaders<TEnv>(
|
|
|
175
149
|
export interface ResolveSegmentOptions {
|
|
176
150
|
/** When true, skip resolveLoaders() calls (used for pre-rendering) */
|
|
177
151
|
skipLoaders?: boolean;
|
|
152
|
+
/**
|
|
153
|
+
* When true, a thrown render error is re-thrown instead of being converted
|
|
154
|
+
* into an error-boundary segment. Set only by the pre-render path so a
|
|
155
|
+
* build-time render failure (and a `throw new Skip()` inside a render fn)
|
|
156
|
+
* surfaces to the build instead of being silently baked into a frozen error
|
|
157
|
+
* page served as a 200 (issue #587). The live request path leaves this unset,
|
|
158
|
+
* so error boundaries keep catching at request time.
|
|
159
|
+
*/
|
|
160
|
+
throwOnError?: boolean;
|
|
178
161
|
}
|
|
179
162
|
|
|
180
163
|
/**
|
|
@@ -216,7 +199,11 @@ export async function resolveSegment<TEnv>(
|
|
|
216
199
|
index: 0,
|
|
217
200
|
component,
|
|
218
201
|
loading: entry.loading === false ? null : entry.loading,
|
|
219
|
-
transition:
|
|
202
|
+
transition: applyViewTransitionDefault(
|
|
203
|
+
entry.transition,
|
|
204
|
+
deps.viewTransitionDefault,
|
|
205
|
+
entry.shortCode,
|
|
206
|
+
),
|
|
220
207
|
params,
|
|
221
208
|
belongsToRoute: false,
|
|
222
209
|
layoutName: entry.id,
|
|
@@ -276,10 +263,18 @@ export async function resolveSegment<TEnv>(
|
|
|
276
263
|
entry.shortCode,
|
|
277
264
|
);
|
|
278
265
|
if (component === undefined) {
|
|
266
|
+
// For Passthrough routes at runtime, use the live handler instead of
|
|
267
|
+
// the build handler. At build time (context.build === true), always
|
|
268
|
+
// use the build handler from entry.handler.
|
|
269
|
+
const handler =
|
|
270
|
+
!context.build && entry.liveHandler ? entry.liveHandler : entry.handler;
|
|
279
271
|
const doneRouteHandler = track(`handler:${entry.id}`, 2);
|
|
280
272
|
if (entry.loading) {
|
|
281
|
-
const result = handleHandlerResult(
|
|
273
|
+
const result = handleHandlerResult(
|
|
274
|
+
observeHandler(entry.id, handler, context),
|
|
275
|
+
);
|
|
282
276
|
if (result instanceof Promise) {
|
|
277
|
+
warnOnStreamedResponse(result, entry.id);
|
|
283
278
|
result.finally(doneRouteHandler).catch(() => {});
|
|
284
279
|
const tracked = deps.trackHandler(result, {
|
|
285
280
|
segmentId: entry.shortCode,
|
|
@@ -299,7 +294,9 @@ export async function resolveSegment<TEnv>(
|
|
|
299
294
|
component = result;
|
|
300
295
|
}
|
|
301
296
|
} else {
|
|
302
|
-
component = handleHandlerResult(
|
|
297
|
+
component = handleHandlerResult(
|
|
298
|
+
await observeHandler(entry.id, handler, context),
|
|
299
|
+
);
|
|
303
300
|
doneRouteHandler();
|
|
304
301
|
}
|
|
305
302
|
}
|
|
@@ -314,6 +311,7 @@ export async function resolveSegment<TEnv>(
|
|
|
314
311
|
deps,
|
|
315
312
|
options,
|
|
316
313
|
routeKey,
|
|
314
|
+
entry,
|
|
317
315
|
);
|
|
318
316
|
segments.push(...orphanSegments);
|
|
319
317
|
}
|
|
@@ -345,7 +343,11 @@ export async function resolveSegment<TEnv>(
|
|
|
345
343
|
index: 0,
|
|
346
344
|
component: component ?? null,
|
|
347
345
|
loading: entry.loading === false ? null : entry.loading,
|
|
348
|
-
transition:
|
|
346
|
+
transition: applyViewTransitionDefault(
|
|
347
|
+
entry.transition,
|
|
348
|
+
deps.viewTransitionDefault,
|
|
349
|
+
entry.shortCode,
|
|
350
|
+
),
|
|
349
351
|
params,
|
|
350
352
|
belongsToRoute: true,
|
|
351
353
|
...(entry.mountPath ? { mountPath: entry.mountPath } : {}),
|
|
@@ -369,6 +371,9 @@ export async function resolveOrphanLayout<TEnv>(
|
|
|
369
371
|
deps: SegmentResolutionDeps<TEnv>,
|
|
370
372
|
options?: ResolveSegmentOptions,
|
|
371
373
|
routeKey?: string,
|
|
374
|
+
/** Parent route entry — its loaders are inherited by the layout so
|
|
375
|
+
* parallel slots inside this layout can access them via useLoader(). */
|
|
376
|
+
parentRouteEntry?: EntryData,
|
|
372
377
|
): Promise<ResolvedSegment[]> {
|
|
373
378
|
invariant(
|
|
374
379
|
orphan.type === "layout" || orphan.type === "cache",
|
|
@@ -384,6 +389,30 @@ export async function resolveOrphanLayout<TEnv>(
|
|
|
384
389
|
deps,
|
|
385
390
|
);
|
|
386
391
|
segments.push(...loaderSegments);
|
|
392
|
+
|
|
393
|
+
// Inherit parent route's loaders so parallel slots inside this layout
|
|
394
|
+
// can access them via useLoader(). Without this, the route's loaders
|
|
395
|
+
// are only in the route's OutletProvider (rendered as <Outlet /> content),
|
|
396
|
+
// which is a child — not a parent — of the layout's context.
|
|
397
|
+
if (
|
|
398
|
+
parentRouteEntry &&
|
|
399
|
+
parentRouteEntry.loader &&
|
|
400
|
+
parentRouteEntry.loader.length > 0 &&
|
|
401
|
+
Object.keys(orphan.parallel).length > 0
|
|
402
|
+
) {
|
|
403
|
+
const inheritedLoaders = await resolveLoaders(
|
|
404
|
+
parentRouteEntry,
|
|
405
|
+
context,
|
|
406
|
+
belongsToRoute,
|
|
407
|
+
deps,
|
|
408
|
+
orphan.shortCode,
|
|
409
|
+
);
|
|
410
|
+
// Tag as inherited so buildMatchResult can deduplicate when safe
|
|
411
|
+
for (const s of inheritedLoaders) {
|
|
412
|
+
s._inherited = true;
|
|
413
|
+
}
|
|
414
|
+
segments.push(...inheritedLoaders);
|
|
415
|
+
}
|
|
387
416
|
}
|
|
388
417
|
|
|
389
418
|
// Handler-first: orphan layout handler executes before its parallels
|
|
@@ -402,7 +431,11 @@ export async function resolveOrphanLayout<TEnv>(
|
|
|
402
431
|
belongsToRoute,
|
|
403
432
|
layoutName: orphan.id,
|
|
404
433
|
loading: orphan.loading === false ? null : orphan.loading,
|
|
405
|
-
transition:
|
|
434
|
+
transition: applyViewTransitionDefault(
|
|
435
|
+
orphan.transition,
|
|
436
|
+
deps.viewTransitionDefault,
|
|
437
|
+
orphan.shortCode,
|
|
438
|
+
),
|
|
406
439
|
...(orphan.mountPath ? { mountPath: orphan.mountPath } : {}),
|
|
407
440
|
});
|
|
408
441
|
|
|
@@ -474,6 +507,14 @@ export async function resolveParallelEntry<TEnv>(
|
|
|
474
507
|
if (handler === undefined) {
|
|
475
508
|
continue;
|
|
476
509
|
}
|
|
510
|
+
// Pin `_currentSegmentId` to the slot's own id so handle pushes from
|
|
511
|
+
// inside the slot handler get their own bucket in the HandleStore.
|
|
512
|
+
// Parent-keying would collapse them into the parent layout's bucket;
|
|
513
|
+
// the partial-update merge then replaces the parent's bucket on a
|
|
514
|
+
// slot-only revalidation and drops layout-pushed Meta/Breadcrumbs.
|
|
515
|
+
// filterSegmentOrder() retains slot ids so the client preserves them.
|
|
516
|
+
(context as InternalHandlerContext<any, TEnv>)._currentSegmentId =
|
|
517
|
+
`${parentShortCode}.${slot}`;
|
|
477
518
|
const doneParallelHandler = track(
|
|
478
519
|
`handler:${parallelEntry.id}.${slot}`,
|
|
479
520
|
2,
|
|
@@ -482,7 +523,9 @@ export async function resolveParallelEntry<TEnv>(
|
|
|
482
523
|
parallelEntry.loading !== undefined && parallelEntry.loading !== false;
|
|
483
524
|
if (hasLoadingFallback) {
|
|
484
525
|
const result =
|
|
485
|
-
typeof handler === "function"
|
|
526
|
+
typeof handler === "function"
|
|
527
|
+
? observeHandler(`${parallelEntry.id}.${slot}`, handler, context)
|
|
528
|
+
: handler;
|
|
486
529
|
if (result instanceof Promise) {
|
|
487
530
|
result.finally(doneParallelHandler).catch(() => {});
|
|
488
531
|
const tracked = deps.trackHandler(result, {
|
|
@@ -504,7 +547,13 @@ export async function resolveParallelEntry<TEnv>(
|
|
|
504
547
|
}
|
|
505
548
|
} else {
|
|
506
549
|
component =
|
|
507
|
-
typeof handler === "function"
|
|
550
|
+
typeof handler === "function"
|
|
551
|
+
? await observeHandler(
|
|
552
|
+
`${parallelEntry.id}.${slot}`,
|
|
553
|
+
handler,
|
|
554
|
+
context,
|
|
555
|
+
)
|
|
556
|
+
: handler;
|
|
508
557
|
doneParallelHandler();
|
|
509
558
|
}
|
|
510
559
|
}
|
|
@@ -516,7 +565,11 @@ export async function resolveParallelEntry<TEnv>(
|
|
|
516
565
|
index: 0,
|
|
517
566
|
component,
|
|
518
567
|
loading: parallelEntry.loading === false ? null : parallelEntry.loading,
|
|
519
|
-
transition:
|
|
568
|
+
transition: applyViewTransitionDefault(
|
|
569
|
+
parallelEntry.transition,
|
|
570
|
+
deps.viewTransitionDefault,
|
|
571
|
+
`${parentShortCode}.${slot}`,
|
|
572
|
+
),
|
|
520
573
|
params,
|
|
521
574
|
slot,
|
|
522
575
|
belongsToRoute,
|
|
@@ -583,7 +636,7 @@ export async function resolveAllSegments<TEnv>(
|
|
|
583
636
|
// can guard non-cacheable variable reads. Also guards response-level
|
|
584
637
|
// side effects (headers.set). Persists for all descendant entries.
|
|
585
638
|
if (entry.type === "cache") {
|
|
586
|
-
const store =
|
|
639
|
+
const store = RangoContext.getStore();
|
|
587
640
|
if (store) store.insideCacheScope = true;
|
|
588
641
|
}
|
|
589
642
|
const doneEntry = track(`segment:${entry.id}`, 1);
|
|
@@ -605,6 +658,7 @@ export async function resolveAllSegments<TEnv>(
|
|
|
605
658
|
deps,
|
|
606
659
|
{ request: safeRequest, url: context.url, routeKey, telemetry },
|
|
607
660
|
context.pathname,
|
|
661
|
+
options?.throwOnError,
|
|
608
662
|
);
|
|
609
663
|
doneEntry();
|
|
610
664
|
// Deduplicate by segment ID. include() scopes can produce entries that
|
|
@@ -672,6 +726,30 @@ export async function resolveLoadersOnly<TEnv>(
|
|
|
672
726
|
const childBelongsToRoute = belongsToRoute || entry.type === "route";
|
|
673
727
|
for (const layoutEntry of entry.layout) {
|
|
674
728
|
await collectEntryLoaders(layoutEntry, childBelongsToRoute);
|
|
729
|
+
// Inherit route loaders for orphan layouts with parallels.
|
|
730
|
+
// Resolve directly — do NOT re-enter collectEntryLoaders with the
|
|
731
|
+
// route entry, as that would re-iterate route.layout and loop.
|
|
732
|
+
if (
|
|
733
|
+
entry.type === "route" &&
|
|
734
|
+
entry.loader &&
|
|
735
|
+
entry.loader.length > 0 &&
|
|
736
|
+
Object.keys(layoutEntry.parallel).length > 0
|
|
737
|
+
) {
|
|
738
|
+
const inherited = await resolveLoaders(
|
|
739
|
+
entry,
|
|
740
|
+
context,
|
|
741
|
+
childBelongsToRoute,
|
|
742
|
+
deps,
|
|
743
|
+
layoutEntry.shortCode,
|
|
744
|
+
);
|
|
745
|
+
for (const seg of inherited) {
|
|
746
|
+
if (!seenIds.has(seg.id)) {
|
|
747
|
+
seenIds.add(seg.id);
|
|
748
|
+
seg._inherited = true;
|
|
749
|
+
loaderSegments.push(seg);
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
}
|
|
675
753
|
}
|
|
676
754
|
}
|
|
677
755
|
|
|
@@ -19,13 +19,48 @@ import {
|
|
|
19
19
|
import { getRequestContext } from "../../server/request-context.js";
|
|
20
20
|
import { DefaultErrorFallback } from "../../default-error-boundary.js";
|
|
21
21
|
import type { EntryData } from "../../server/context";
|
|
22
|
-
import type {
|
|
22
|
+
import type {
|
|
23
|
+
ResolvedSegment,
|
|
24
|
+
ErrorInfo,
|
|
25
|
+
HandlerContext,
|
|
26
|
+
InternalHandlerContext,
|
|
27
|
+
} from "../../types";
|
|
23
28
|
import type { SegmentResolutionDeps } from "../types.js";
|
|
24
29
|
import { debugLog } from "../logging.js";
|
|
25
30
|
import { tryStaticLookup } from "./static-store.js";
|
|
31
|
+
import { observeHandler } from "../instrument.js";
|
|
26
32
|
import type { TelemetrySink } from "../telemetry.js";
|
|
27
33
|
import { resolveSink, safeEmit, getRequestId } from "../telemetry.js";
|
|
28
34
|
|
|
35
|
+
/** The errorContext shape wrapLoaderPromise expects as its 5th argument. */
|
|
36
|
+
type LoaderErrorContext<TEnv> = NonNullable<
|
|
37
|
+
Parameters<SegmentResolutionDeps<TEnv>["wrapLoaderPromise"]>[4]
|
|
38
|
+
>;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Build the errorContext passed to wrapLoaderPromise so a throwing DSL loader
|
|
42
|
+
* fires createRouter({ onError }) (phase "loader") and emits the loader.error
|
|
43
|
+
* telemetry event. wrapLoaderPromise only wires the onError/telemetry path when
|
|
44
|
+
* this 5th argument is present; every real call site previously omitted it, so
|
|
45
|
+
* loaders were the one phase whose failures were silently dropped (handlers,
|
|
46
|
+
* actions, routing, rendering, and fetchable loaders all reported correctly).
|
|
47
|
+
*
|
|
48
|
+
* The fields come off the handler context, which already carries the request,
|
|
49
|
+
* url, params, env, and (on the internal shape) the matched route name.
|
|
50
|
+
*/
|
|
51
|
+
export function buildLoaderErrorContext<TEnv>(
|
|
52
|
+
ctx: HandlerContext<any, TEnv>,
|
|
53
|
+
): LoaderErrorContext<TEnv> {
|
|
54
|
+
const internal = ctx as InternalHandlerContext<any, TEnv>;
|
|
55
|
+
return {
|
|
56
|
+
request: ctx.request,
|
|
57
|
+
url: ctx.url,
|
|
58
|
+
routeKey: internal._routeName,
|
|
59
|
+
params: ctx.params as Record<string, string>,
|
|
60
|
+
env: ctx.env,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
29
64
|
// ---------------------------------------------------------------------------
|
|
30
65
|
// Handler result processing
|
|
31
66
|
// ---------------------------------------------------------------------------
|
|
@@ -52,6 +87,40 @@ export function handleHandlerResult(
|
|
|
52
87
|
return result;
|
|
53
88
|
}
|
|
54
89
|
|
|
90
|
+
/**
|
|
91
|
+
* Dev-only: warn when a handler on a route that declares loading() resolves or
|
|
92
|
+
* rejects with a Response (e.g. redirect()).
|
|
93
|
+
*
|
|
94
|
+
* On a non-loading route a returned/thrown Response short-circuits to an HTTP
|
|
95
|
+
* redirect. But when the route declares loading(), the handler result is
|
|
96
|
+
* streamed (not awaited at the resolution boundary), so the Response surfaces
|
|
97
|
+
* only during RSC serialization and is rendered into the stream instead of
|
|
98
|
+
* becoming a 302/308 — a silent failure mode. Issue redirects from middleware,
|
|
99
|
+
* a loader, or a synchronous handler return instead. Compiled out in production.
|
|
100
|
+
*/
|
|
101
|
+
export function warnOnStreamedResponse(
|
|
102
|
+
result: Promise<unknown>,
|
|
103
|
+
entryId: string,
|
|
104
|
+
): void {
|
|
105
|
+
if (process.env.NODE_ENV === "production") return;
|
|
106
|
+
// A Response can surface either as a rejection (handleHandlerResult rethrows a
|
|
107
|
+
// resolved Response) or as a resolved value (the raw parallel-slot handler is
|
|
108
|
+
// not run through handleHandlerResult). Check both so every streamed path is
|
|
109
|
+
// covered. Each handler is an independent observer; it does not consume the
|
|
110
|
+
// rejection for the trackHandler/observeStreamedHandler chains.
|
|
111
|
+
const check = (value: unknown) => {
|
|
112
|
+
if (value instanceof Response) {
|
|
113
|
+
console.warn(
|
|
114
|
+
`[rango] Handler for "${entryId}" returned a Response (e.g. ` +
|
|
115
|
+
`redirect()), but it declares loading(): the Response is rendered ` +
|
|
116
|
+
`into the RSC stream, NOT sent as an HTTP redirect. Issue redirects ` +
|
|
117
|
+
`from middleware, a loader, or a synchronous handler return.`,
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
result.then(check, check);
|
|
122
|
+
}
|
|
123
|
+
|
|
55
124
|
// ---------------------------------------------------------------------------
|
|
56
125
|
// Static handler interception
|
|
57
126
|
// ---------------------------------------------------------------------------
|
|
@@ -96,11 +165,16 @@ export async function resolveLayoutComponent<TEnv>(
|
|
|
96
165
|
entry: EntryData,
|
|
97
166
|
context: HandlerContext<any, TEnv>,
|
|
98
167
|
): Promise<ReactNode> {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
168
|
+
// Static/prerender hit: no handler runs, so emit no rango.handler span.
|
|
169
|
+
const staticComponent = await tryStaticHandler(entry, entry.shortCode);
|
|
170
|
+
if (staticComponent !== undefined) return staticComponent;
|
|
171
|
+
const handler = entry.handler;
|
|
172
|
+
if (typeof handler !== "function") return handler as ReactNode;
|
|
173
|
+
// Wrap ONLY the handler call in the rango.handler span (the perf metric is owned
|
|
174
|
+
// by track("handler:<id>") at the call site). handleHandlerResult stays OUTSIDE
|
|
175
|
+
// the span so a handler that returns a Response (redirect control flow, which it
|
|
176
|
+
// rethrows) is not recorded as a span error — mirrors the route-handler sites.
|
|
177
|
+
return handleHandlerResult(await observeHandler(entry.id, handler, context));
|
|
104
178
|
}
|
|
105
179
|
|
|
106
180
|
// ---------------------------------------------------------------------------
|
|
@@ -250,11 +324,17 @@ export async function resolveWithErrorBoundary<TEnv, TResult>(
|
|
|
250
324
|
deps: SegmentResolutionDeps<TEnv>,
|
|
251
325
|
report?: ErrorReportContext,
|
|
252
326
|
pathname?: string,
|
|
327
|
+
throwOnError?: boolean,
|
|
253
328
|
): Promise<TResult> {
|
|
254
329
|
try {
|
|
255
330
|
return await resolveFn();
|
|
256
331
|
} catch (error) {
|
|
257
332
|
if (error instanceof Response) throw error;
|
|
333
|
+
// Pre-render surfaces render failures to the build instead of baking the
|
|
334
|
+
// error boundary as a frozen 200 (issue #587). A `throw new Skip()` in a
|
|
335
|
+
// render fn also propagates here so the build can skip that URL rather than
|
|
336
|
+
// bake its error page. The live request path leaves throwOnError unset.
|
|
337
|
+
if (throwOnError) throw error;
|
|
258
338
|
const segment = catchSegmentError(
|
|
259
339
|
error,
|
|
260
340
|
entry,
|