@rangojs/router 0.0.0-experimental.49 → 0.0.0-experimental.4fba1c4c
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +196 -43
- package/dist/bin/rango.js +269 -96
- package/dist/testing/vitest.js +48 -0
- package/dist/vite/index.js +2659 -883
- package/dist/vite/index.js.bak +5448 -0
- package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/package.json +57 -11
- package/skills/breadcrumbs/SKILL.md +3 -1
- package/skills/bundle-analysis/SKILL.md +159 -0
- package/skills/cache-guide/SKILL.md +243 -21
- package/skills/caching/SKILL.md +118 -2
- package/skills/composability/SKILL.md +27 -2
- package/skills/document-cache/SKILL.md +78 -55
- package/skills/handler-use/SKILL.md +364 -0
- package/skills/hooks/SKILL.md +229 -20
- package/skills/host-router/SKILL.md +45 -20
- package/skills/i18n/SKILL.md +276 -0
- package/skills/intercept/SKILL.md +46 -4
- package/skills/layout/SKILL.md +28 -7
- package/skills/links/SKILL.md +249 -17
- package/skills/loader/SKILL.md +273 -53
- package/skills/middleware/SKILL.md +49 -12
- package/skills/migrate-nextjs/SKILL.md +562 -0
- package/skills/migrate-react-router/SKILL.md +769 -0
- package/skills/mime-routes/SKILL.md +27 -0
- package/skills/observability/SKILL.md +137 -0
- package/skills/parallel/SKILL.md +71 -6
- package/skills/prerender/SKILL.md +123 -100
- package/skills/rango/SKILL.md +242 -22
- package/skills/react-compiler/SKILL.md +168 -0
- package/skills/response-routes/SKILL.md +66 -9
- package/skills/route/SKILL.md +88 -4
- package/skills/router-setup/SKILL.md +90 -5
- package/skills/server-actions/SKILL.md +751 -0
- package/skills/streams-and-websockets/SKILL.md +283 -0
- package/skills/testing/SKILL.md +734 -0
- package/skills/typesafety/SKILL.md +329 -27
- package/skills/use-cache/SKILL.md +34 -5
- package/skills/view-transitions/SKILL.md +294 -0
- package/src/__augment-tests__/augment.ts +81 -0
- package/src/__augment-tests__/augmented.check.ts +117 -0
- package/src/__internal.ts +1 -1
- package/src/browser/action-coordinator.ts +53 -36
- package/src/browser/app-shell.ts +52 -0
- package/src/browser/app-version.ts +14 -0
- package/src/browser/event-controller.ts +86 -70
- package/src/browser/history-state.ts +21 -0
- package/src/browser/index.ts +3 -3
- package/src/browser/navigation-bridge.ts +101 -13
- package/src/browser/navigation-client.ts +125 -53
- package/src/browser/navigation-store.ts +75 -17
- package/src/browser/navigation-transaction.ts +10 -28
- package/src/browser/partial-update.ts +90 -30
- package/src/browser/prefetch/cache.ts +129 -21
- package/src/browser/prefetch/fetch.ts +156 -18
- package/src/browser/prefetch/queue.ts +92 -29
- package/src/browser/prefetch/resource-ready.ts +77 -0
- package/src/browser/rango-state.ts +53 -13
- package/src/browser/react/Link.tsx +72 -8
- package/src/browser/react/NavigationProvider.tsx +83 -33
- package/src/browser/react/context.ts +7 -2
- package/src/browser/react/filter-segment-order.ts +51 -7
- package/src/browser/react/index.ts +3 -0
- package/src/browser/react/location-state-shared.ts +175 -4
- package/src/browser/react/location-state.ts +39 -13
- package/src/browser/react/use-handle.ts +23 -64
- package/src/browser/react/use-navigation.ts +22 -2
- package/src/browser/react/use-params.ts +20 -8
- package/src/browser/react/use-reverse.ts +106 -0
- package/src/browser/react/use-router.ts +43 -10
- package/src/browser/react/use-segments.ts +11 -8
- package/src/browser/response-adapter.ts +25 -0
- package/src/browser/rsc-router.tsx +87 -22
- package/src/browser/scroll-restoration.ts +29 -19
- package/src/browser/segment-reconciler.ts +36 -14
- package/src/browser/segment-structure-assert.ts +2 -2
- package/src/browser/server-action-bridge.ts +31 -36
- package/src/browser/types.ts +48 -5
- 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 -0
- package/src/build/index.ts +2 -0
- package/src/build/route-trie.ts +52 -25
- package/src/build/route-types/codegen.ts +4 -4
- package/src/build/route-types/include-resolution.ts +9 -2
- package/src/build/route-types/per-module-writer.ts +7 -4
- package/src/build/route-types/router-processing.ts +266 -86
- package/src/build/route-types/scan-filter.ts +9 -2
- package/src/build/route-types/source-scan.ts +118 -0
- package/src/build/runtime-discovery.ts +9 -20
- package/src/cache/cache-scope.ts +74 -47
- package/src/cache/cf/cf-cache-store.ts +54 -13
- package/src/cache/taint.ts +55 -0
- package/src/client.rsc.tsx +3 -0
- package/src/client.tsx +94 -238
- package/src/context-var.ts +72 -2
- package/src/decode-loader-results.ts +36 -0
- package/src/errors.ts +30 -1
- package/src/handle.ts +65 -12
- package/src/host/index.ts +2 -2
- package/src/host/router.ts +129 -57
- package/src/host/types.ts +31 -2
- package/src/host/utils.ts +1 -1
- package/src/href-client.ts +140 -20
- package/src/index.rsc.ts +12 -5
- package/src/index.ts +61 -11
- package/src/loader-store.ts +500 -0
- package/src/loader.rsc.ts +21 -6
- package/src/loader.ts +3 -10
- package/src/missing-id-error.ts +68 -0
- package/src/outlet-context.ts +1 -1
- package/src/prerender/store.ts +5 -4
- package/src/prerender.ts +141 -80
- package/src/response-utils.ts +37 -0
- package/src/reverse.ts +65 -15
- package/src/route-content-wrapper.tsx +6 -28
- package/src/route-definition/dsl-helpers.ts +411 -261
- package/src/route-definition/helper-factories.ts +29 -139
- package/src/route-definition/helpers-types.ts +110 -34
- package/src/route-definition/index.ts +3 -0
- package/src/route-definition/redirect.ts +9 -1
- package/src/route-definition/resolve-handler-use.ts +155 -0
- package/src/route-definition/use-item-types.ts +32 -0
- package/src/route-types.ts +37 -41
- package/src/router/basename.ts +14 -0
- package/src/router/content-negotiation.ts +113 -1
- package/src/router/error-handling.ts +1 -1
- package/src/router/handler-context.ts +77 -38
- package/src/router/intercept-resolution.ts +13 -22
- package/src/router/lazy-includes.ts +8 -8
- package/src/router/loader-resolution.ts +174 -22
- package/src/router/manifest.ts +22 -13
- package/src/router/match-api.ts +128 -192
- package/src/router/match-handlers.ts +63 -20
- package/src/router/match-middleware/cache-lookup.ts +70 -97
- package/src/router/match-middleware/cache-store.ts +8 -2
- package/src/router/match-middleware/segment-resolution.ts +53 -0
- package/src/router/match-result.ts +103 -4
- package/src/router/metrics.ts +1 -1
- package/src/router/middleware-types.ts +21 -34
- package/src/router/middleware.ts +101 -89
- package/src/router/navigation-snapshot.ts +182 -0
- package/src/router/pattern-matching.ts +101 -17
- package/src/router/prerender-match.ts +110 -10
- package/src/router/preview-match.ts +32 -102
- package/src/router/request-classification.ts +286 -0
- package/src/router/revalidation.ts +58 -2
- package/src/router/route-snapshot.ts +245 -0
- package/src/router/router-interfaces.ts +77 -28
- package/src/router/router-options.ts +76 -11
- package/src/router/router-registry.ts +2 -5
- package/src/router/segment-resolution/fresh.ts +105 -13
- package/src/router/segment-resolution/helpers.ts +29 -24
- package/src/router/segment-resolution/revalidation.ts +236 -112
- package/src/router/segment-resolution/view-transition-default.ts +36 -0
- package/src/router/substitute-pattern-params.ts +56 -0
- package/src/router/telemetry.ts +99 -0
- package/src/router/trie-matching.ts +18 -13
- package/src/router/types.ts +9 -0
- package/src/router/url-params.ts +49 -0
- package/src/router.ts +86 -22
- package/src/rsc/handler-context.ts +2 -2
- package/src/rsc/handler.ts +440 -381
- package/src/rsc/helpers.ts +91 -43
- package/src/rsc/index.ts +1 -1
- package/src/rsc/loader-fetch.ts +23 -3
- package/src/rsc/manifest-init.ts +5 -1
- package/src/rsc/origin-guard.ts +28 -10
- package/src/rsc/progressive-enhancement.ts +18 -2
- package/src/rsc/response-route-handler.ts +46 -53
- package/src/rsc/rsc-rendering.ts +41 -48
- package/src/rsc/runtime-warnings.ts +9 -10
- package/src/rsc/server-action.ts +25 -37
- package/src/rsc/ssr-setup.ts +18 -2
- package/src/rsc/types.ts +17 -3
- package/src/search-params.ts +4 -4
- package/src/segment-content-promise.ts +67 -0
- package/src/segment-loader-promise.ts +122 -0
- package/src/segment-system.tsx +132 -116
- package/src/serialize.ts +243 -0
- package/src/server/context.ts +190 -51
- package/src/server/cookie-store.ts +28 -4
- package/src/server/handle-store.ts +19 -0
- package/src/server/loader-registry.ts +9 -8
- package/src/server/request-context.ts +195 -57
- package/src/ssr/index.tsx +8 -1
- package/src/static-handler.ts +19 -7
- package/src/testing/cache-status.ts +166 -0
- package/src/testing/collect-handle.ts +63 -0
- package/src/testing/dispatch.ts +440 -0
- package/src/testing/dom.entry.ts +22 -0
- package/src/testing/e2e/fixture.ts +154 -0
- package/src/testing/e2e/index.ts +149 -0
- package/src/testing/e2e/matchers.ts +51 -0
- package/src/testing/e2e/page-helpers.ts +272 -0
- package/src/testing/e2e/parity.ts +306 -0
- package/src/testing/e2e/server.ts +183 -0
- package/src/testing/flight-matchers.ts +104 -0
- package/src/testing/flight-runtime.d.ts +21 -0
- package/src/testing/flight.entry.ts +22 -0
- package/src/testing/flight.ts +182 -0
- package/src/testing/generated-routes.ts +223 -0
- package/src/testing/index.ts +106 -0
- package/src/testing/internal/context.ts +304 -0
- package/src/testing/render-route.tsx +565 -0
- package/src/testing/run-loader.ts +341 -0
- package/src/testing/run-middleware.ts +179 -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 +183 -0
- package/src/types/cache-types.ts +4 -4
- package/src/types/global-namespace.ts +39 -26
- package/src/types/handler-context.ts +103 -67
- package/src/types/index.ts +1 -0
- package/src/types/loader-types.ts +41 -15
- package/src/types/request-scope.ts +126 -0
- package/src/types/route-entry.ts +12 -1
- package/src/types/segments.ts +36 -2
- package/src/urls/include-helper.ts +34 -67
- package/src/urls/index.ts +0 -3
- package/src/urls/path-helper-types.ts +50 -9
- package/src/urls/path-helper.ts +63 -63
- package/src/urls/pattern-types.ts +48 -19
- package/src/urls/response-types.ts +25 -22
- package/src/urls/type-extraction.ts +26 -116
- package/src/urls/urls-function.ts +1 -5
- package/src/use-loader.tsx +487 -44
- package/src/vite/debug.ts +185 -0
- package/src/vite/discovery/bundle-postprocess.ts +34 -37
- package/src/vite/discovery/discover-routers.ts +105 -51
- 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 +188 -93
- 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 +46 -4
- package/src/vite/discovery/virtual-module-codegen.ts +13 -23
- package/src/vite/index.ts +6 -0
- package/src/vite/plugin-types.ts +126 -4
- package/src/vite/plugins/cjs-to-esm.ts +8 -7
- package/src/vite/plugins/client-ref-dedup.ts +16 -0
- package/src/vite/plugins/client-ref-hashing.ts +28 -5
- 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 +214 -0
- package/src/vite/plugins/expose-action-id.ts +54 -30
- package/src/vite/plugins/expose-id-utils.ts +24 -8
- package/src/vite/plugins/expose-ids/export-analysis.ts +100 -20
- package/src/vite/plugins/expose-ids/handler-transform.ts +12 -35
- package/src/vite/plugins/expose-ids/loader-transform.ts +3 -5
- package/src/vite/plugins/expose-ids/router-transform.ts +20 -3
- package/src/vite/plugins/expose-internal-ids.ts +544 -317
- package/src/vite/plugins/performance-tracks.ts +92 -0
- package/src/vite/plugins/refresh-cmd.ts +88 -26
- package/src/vite/plugins/use-cache-transform.ts +65 -50
- package/src/vite/plugins/version-injector.ts +39 -23
- package/src/vite/plugins/version-plugin.ts +59 -2
- package/src/vite/plugins/virtual-entries.ts +2 -2
- package/src/vite/rango.ts +130 -26
- package/src/vite/router-discovery.ts +920 -129
- package/src/vite/utils/ast-handler-extract.ts +15 -15
- package/src/vite/utils/banner.ts +1 -1
- package/src/vite/utils/bundle-analysis.ts +4 -2
- package/src/vite/utils/client-chunks.ts +190 -0
- package/src/vite/utils/forward-user-plugins.ts +193 -0
- package/src/vite/utils/manifest-utils.ts +21 -5
- package/src/vite/utils/package-resolution.ts +41 -1
- package/src/vite/utils/prerender-utils.ts +38 -5
- package/src/vite/utils/shared-utils.ts +109 -27
- package/src/browser/action-response-classifier.ts +0 -99
|
@@ -8,6 +8,7 @@ import type {
|
|
|
8
8
|
import type { NonceProvider } from "../rsc/types.js";
|
|
9
9
|
import type { ExecutionContext } from "../server/request-context.js";
|
|
10
10
|
import type { UrlPatterns } from "../urls.js";
|
|
11
|
+
import type { UrlBuilder } from "../urls/pattern-types.js";
|
|
11
12
|
import type { NamedRouteEntry } from "./content-negotiation.js";
|
|
12
13
|
import type { TelemetrySink } from "./telemetry.js";
|
|
13
14
|
import type { RouterTimeouts, OnTimeoutCallback } from "./timeout.js";
|
|
@@ -72,7 +73,7 @@ export interface RootLayoutProps {
|
|
|
72
73
|
/**
|
|
73
74
|
* Router configuration options
|
|
74
75
|
*/
|
|
75
|
-
export interface
|
|
76
|
+
export interface RangoOptions<TEnv = any> {
|
|
76
77
|
/**
|
|
77
78
|
* Unique identifier for this router instance.
|
|
78
79
|
* Used to namespace static output files and route maps.
|
|
@@ -95,6 +96,28 @@ export interface RSCRouterOptions<TEnv = any> {
|
|
|
95
96
|
*/
|
|
96
97
|
$$sourceFile?: string;
|
|
97
98
|
|
|
99
|
+
/**
|
|
100
|
+
* URL prefix applied to all routes registered with this router.
|
|
101
|
+
*
|
|
102
|
+
* Useful when the app is served under a sub-path (e.g. `/admin` or `/v2`).
|
|
103
|
+
* All `path()` patterns are automatically prefixed and `reverse()` returns
|
|
104
|
+
* full paths including the basename. Route names are NOT prefixed.
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* ```typescript
|
|
108
|
+
* const router = createRouter({
|
|
109
|
+
* basename: "/admin",
|
|
110
|
+
* }).routes(({ path }) => [
|
|
111
|
+
* path("/", Dashboard, { name: "home" }), // matches /admin
|
|
112
|
+
* path("/users", Users, { name: "users" }), // matches /admin/users
|
|
113
|
+
* ]);
|
|
114
|
+
*
|
|
115
|
+
* router.reverse("home"); // "/admin"
|
|
116
|
+
* router.reverse("users"); // "/admin/users"
|
|
117
|
+
* ```
|
|
118
|
+
*/
|
|
119
|
+
basename?: string;
|
|
120
|
+
|
|
98
121
|
/**
|
|
99
122
|
* Enable performance metrics collection
|
|
100
123
|
* When enabled, metrics are output to console and available via Server-Timing header
|
|
@@ -109,6 +132,21 @@ export interface RSCRouterOptions<TEnv = any> {
|
|
|
109
132
|
*/
|
|
110
133
|
allowDebugManifest?: boolean;
|
|
111
134
|
|
|
135
|
+
/**
|
|
136
|
+
* DEVELOPMENT/TEST ONLY. Emit an `X-Rango-Cache` response header describing
|
|
137
|
+
* the cache status of the matched route, for use by testing primitives such
|
|
138
|
+
* as `assertCacheStatus`.
|
|
139
|
+
*
|
|
140
|
+
* Defaults to `false`. When neither this option nor the
|
|
141
|
+
* `RANGO_TEST_SIGNALS=1` environment flag is set, NO header is emitted and
|
|
142
|
+
* router output is byte-identical to the default.
|
|
143
|
+
*
|
|
144
|
+
* The header encodes per-segment (v1: coarse route-level) status keyed by the
|
|
145
|
+
* route NAME, e.g. `X-Rango-Cache: product.detail=hit`. Do NOT enable in
|
|
146
|
+
* production — it exposes internal cache decisions.
|
|
147
|
+
*/
|
|
148
|
+
debugCacheSignal?: boolean;
|
|
149
|
+
|
|
112
150
|
/**
|
|
113
151
|
* Document component that wraps the entire application.
|
|
114
152
|
*
|
|
@@ -335,27 +373,54 @@ export interface RSCRouterOptions<TEnv = any> {
|
|
|
335
373
|
theme?: import("../theme/types.js").ThemeConfig | true;
|
|
336
374
|
|
|
337
375
|
/**
|
|
338
|
-
*
|
|
376
|
+
* Default for whether the router wraps `transition()` segments in its own
|
|
377
|
+
* React `<ViewTransition>` boundary (experimental React only).
|
|
339
378
|
*
|
|
340
|
-
*
|
|
341
|
-
*
|
|
379
|
+
* - "auto" (default): every route/layout that opts in via `transition()`
|
|
380
|
+
* gets a router-owned cross-fade.
|
|
381
|
+
* - false: the router never places its own boundary. Routes that use
|
|
382
|
+
* `transition()` still drive navigation through startTransition (so loaders
|
|
383
|
+
* hold instead of flashing a skeleton) and still let consumer-placed
|
|
384
|
+
* `<ViewTransition>` elements animate — the router just contributes no
|
|
385
|
+
* cross-fade of its own. This is the "router triggers, you place the
|
|
386
|
+
* transitions" model.
|
|
387
|
+
*
|
|
388
|
+
* A per-segment `transition({ viewTransition })` overrides this default.
|
|
342
389
|
*
|
|
343
390
|
* @example
|
|
344
391
|
* ```typescript
|
|
345
|
-
*
|
|
392
|
+
* // App-wide: drive + hold, but never auto-wrap. Place <ViewTransition>
|
|
393
|
+
* // yourself in components where you want a morph.
|
|
394
|
+
* const router = createRouter<AppEnv>({ viewTransition: false });
|
|
395
|
+
* ```
|
|
396
|
+
*/
|
|
397
|
+
viewTransition?: "auto" | false;
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* URL patterns to register with the router.
|
|
346
401
|
*
|
|
347
|
-
*
|
|
348
|
-
*
|
|
349
|
-
* path("/about", AboutPage, { name: "about" }),
|
|
350
|
-
* ]);
|
|
402
|
+
* Accepts either a `UrlPatterns` object from `urls()` or a builder function
|
|
403
|
+
* directly (urls() is called implicitly).
|
|
351
404
|
*
|
|
352
|
-
*
|
|
405
|
+
* @example
|
|
406
|
+
* ```typescript
|
|
407
|
+
* // With urls()
|
|
408
|
+
* createRouter<AppEnv>({
|
|
353
409
|
* document: Document,
|
|
354
410
|
* urls: urlpatterns,
|
|
355
411
|
* });
|
|
412
|
+
*
|
|
413
|
+
* // With builder function
|
|
414
|
+
* createRouter<AppEnv>({
|
|
415
|
+
* document: Document,
|
|
416
|
+
* urls: ({ path }) => [
|
|
417
|
+
* path("/", HomePage, { name: "home" }),
|
|
418
|
+
* path("/about", AboutPage, { name: "about" }),
|
|
419
|
+
* ],
|
|
420
|
+
* });
|
|
356
421
|
* ```
|
|
357
422
|
*/
|
|
358
|
-
urls?: UrlPatterns<TEnv, any>;
|
|
423
|
+
urls?: UrlPatterns<TEnv, any> | UrlBuilder<TEnv>;
|
|
359
424
|
|
|
360
425
|
/**
|
|
361
426
|
* Injected by the Vite transform at compile time.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { RangoInternal } from "./router-interfaces.js";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Brand marker for identifying router instances at build time.
|
|
@@ -12,10 +12,7 @@ export const RSC_ROUTER_BRAND = "__rsc_router__" as const;
|
|
|
12
12
|
* Used by the Vite plugin at build time to discover routers and extract
|
|
13
13
|
* manifests, prefix trees, and pre-render candidates.
|
|
14
14
|
*/
|
|
15
|
-
export const RouterRegistry: Map<
|
|
16
|
-
string,
|
|
17
|
-
RSCRouterInternal<any, any>
|
|
18
|
-
> = new Map();
|
|
15
|
+
export const RouterRegistry: Map<string, RangoInternal<any, any>> = new Map();
|
|
19
16
|
|
|
20
17
|
export let routerAutoId = 0;
|
|
21
18
|
|
|
@@ -28,9 +28,14 @@ import {
|
|
|
28
28
|
resolveLayoutComponent,
|
|
29
29
|
resolveWithErrorBoundary,
|
|
30
30
|
} from "./helpers.js";
|
|
31
|
+
import { applyViewTransitionDefault } from "./view-transition-default.js";
|
|
31
32
|
import { getRouterContext } from "../router-context.js";
|
|
32
33
|
import { resolveSink, safeEmit } from "../telemetry.js";
|
|
33
|
-
import {
|
|
34
|
+
import {
|
|
35
|
+
track,
|
|
36
|
+
RangoContext,
|
|
37
|
+
runInsideLoaderScope,
|
|
38
|
+
} from "../../server/context.js";
|
|
34
39
|
|
|
35
40
|
// ---------------------------------------------------------------------------
|
|
36
41
|
// Streamed handler telemetry
|
|
@@ -100,9 +105,7 @@ export async function resolveLoaders<TEnv>(
|
|
|
100
105
|
|
|
101
106
|
if (!loadingDisabled) {
|
|
102
107
|
// Streaming loaders: promises kick off now, settle during RSC serialization.
|
|
103
|
-
|
|
104
|
-
// RSC/SSR stream consumption, after the perf timeline is logged.
|
|
105
|
-
return loaderEntries.map((loaderEntry, i) => {
|
|
108
|
+
const segments = loaderEntries.map((loaderEntry, i) => {
|
|
106
109
|
const { loader } = loaderEntry;
|
|
107
110
|
const segmentId = `${shortCode}D${i}.${loader.$$id}`;
|
|
108
111
|
return {
|
|
@@ -114,7 +117,9 @@ export async function resolveLoaders<TEnv>(
|
|
|
114
117
|
params: ctx.params,
|
|
115
118
|
loaderId: loader.$$id,
|
|
116
119
|
loaderData: deps.wrapLoaderPromise(
|
|
117
|
-
|
|
120
|
+
runInsideLoaderScope(() =>
|
|
121
|
+
resolveLoaderData(loaderEntry, ctx, ctx.pathname),
|
|
122
|
+
),
|
|
118
123
|
entry,
|
|
119
124
|
segmentId,
|
|
120
125
|
ctx.pathname,
|
|
@@ -122,14 +127,17 @@ export async function resolveLoaders<TEnv>(
|
|
|
122
127
|
belongsToRoute,
|
|
123
128
|
};
|
|
124
129
|
});
|
|
130
|
+
|
|
131
|
+
return segments;
|
|
125
132
|
}
|
|
126
133
|
|
|
127
134
|
// Loading disabled: still start all loaders in parallel, but only emit
|
|
128
135
|
// settled promises so handlers don't stream loading placeholders.
|
|
129
|
-
// We can measure actual execution time here since we await all loaders.
|
|
130
136
|
const pendingLoaderData = loaderEntries.map((loaderEntry) => {
|
|
131
137
|
const start = performance.now();
|
|
132
|
-
const promise =
|
|
138
|
+
const promise = runInsideLoaderScope(() =>
|
|
139
|
+
resolveLoaderData(loaderEntry, ctx, ctx.pathname),
|
|
140
|
+
);
|
|
133
141
|
return { promise, start, loaderId: loaderEntry.loader.$$id };
|
|
134
142
|
});
|
|
135
143
|
await Promise.all(pendingLoaderData.map((p) => p.promise));
|
|
@@ -217,7 +225,10 @@ export async function resolveSegment<TEnv>(
|
|
|
217
225
|
index: 0,
|
|
218
226
|
component,
|
|
219
227
|
loading: entry.loading === false ? null : entry.loading,
|
|
220
|
-
transition:
|
|
228
|
+
transition: applyViewTransitionDefault(
|
|
229
|
+
entry.transition,
|
|
230
|
+
deps.viewTransitionDefault,
|
|
231
|
+
),
|
|
221
232
|
params,
|
|
222
233
|
belongsToRoute: false,
|
|
223
234
|
layoutName: entry.id,
|
|
@@ -277,9 +288,14 @@ export async function resolveSegment<TEnv>(
|
|
|
277
288
|
entry.shortCode,
|
|
278
289
|
);
|
|
279
290
|
if (component === undefined) {
|
|
291
|
+
// For Passthrough routes at runtime, use the live handler instead of
|
|
292
|
+
// the build handler. At build time (context.build === true), always
|
|
293
|
+
// use the build handler from entry.handler.
|
|
294
|
+
const handler =
|
|
295
|
+
!context.build && entry.liveHandler ? entry.liveHandler : entry.handler;
|
|
280
296
|
const doneRouteHandler = track(`handler:${entry.id}`, 2);
|
|
281
297
|
if (entry.loading) {
|
|
282
|
-
const result = handleHandlerResult(
|
|
298
|
+
const result = handleHandlerResult(handler(context));
|
|
283
299
|
if (result instanceof Promise) {
|
|
284
300
|
result.finally(doneRouteHandler).catch(() => {});
|
|
285
301
|
const tracked = deps.trackHandler(result, {
|
|
@@ -300,7 +316,7 @@ export async function resolveSegment<TEnv>(
|
|
|
300
316
|
component = result;
|
|
301
317
|
}
|
|
302
318
|
} else {
|
|
303
|
-
component = handleHandlerResult(await
|
|
319
|
+
component = handleHandlerResult(await handler(context));
|
|
304
320
|
doneRouteHandler();
|
|
305
321
|
}
|
|
306
322
|
}
|
|
@@ -315,6 +331,7 @@ export async function resolveSegment<TEnv>(
|
|
|
315
331
|
deps,
|
|
316
332
|
options,
|
|
317
333
|
routeKey,
|
|
334
|
+
entry,
|
|
318
335
|
);
|
|
319
336
|
segments.push(...orphanSegments);
|
|
320
337
|
}
|
|
@@ -346,7 +363,10 @@ export async function resolveSegment<TEnv>(
|
|
|
346
363
|
index: 0,
|
|
347
364
|
component: component ?? null,
|
|
348
365
|
loading: entry.loading === false ? null : entry.loading,
|
|
349
|
-
transition:
|
|
366
|
+
transition: applyViewTransitionDefault(
|
|
367
|
+
entry.transition,
|
|
368
|
+
deps.viewTransitionDefault,
|
|
369
|
+
),
|
|
350
370
|
params,
|
|
351
371
|
belongsToRoute: true,
|
|
352
372
|
...(entry.mountPath ? { mountPath: entry.mountPath } : {}),
|
|
@@ -370,6 +390,9 @@ export async function resolveOrphanLayout<TEnv>(
|
|
|
370
390
|
deps: SegmentResolutionDeps<TEnv>,
|
|
371
391
|
options?: ResolveSegmentOptions,
|
|
372
392
|
routeKey?: string,
|
|
393
|
+
/** Parent route entry — its loaders are inherited by the layout so
|
|
394
|
+
* parallel slots inside this layout can access them via useLoader(). */
|
|
395
|
+
parentRouteEntry?: EntryData,
|
|
373
396
|
): Promise<ResolvedSegment[]> {
|
|
374
397
|
invariant(
|
|
375
398
|
orphan.type === "layout" || orphan.type === "cache",
|
|
@@ -385,6 +408,30 @@ export async function resolveOrphanLayout<TEnv>(
|
|
|
385
408
|
deps,
|
|
386
409
|
);
|
|
387
410
|
segments.push(...loaderSegments);
|
|
411
|
+
|
|
412
|
+
// Inherit parent route's loaders so parallel slots inside this layout
|
|
413
|
+
// can access them via useLoader(). Without this, the route's loaders
|
|
414
|
+
// are only in the route's OutletProvider (rendered as <Outlet /> content),
|
|
415
|
+
// which is a child — not a parent — of the layout's context.
|
|
416
|
+
if (
|
|
417
|
+
parentRouteEntry &&
|
|
418
|
+
parentRouteEntry.loader &&
|
|
419
|
+
parentRouteEntry.loader.length > 0 &&
|
|
420
|
+
Object.keys(orphan.parallel).length > 0
|
|
421
|
+
) {
|
|
422
|
+
const inheritedLoaders = await resolveLoaders(
|
|
423
|
+
parentRouteEntry,
|
|
424
|
+
context,
|
|
425
|
+
belongsToRoute,
|
|
426
|
+
deps,
|
|
427
|
+
orphan.shortCode,
|
|
428
|
+
);
|
|
429
|
+
// Tag as inherited so buildMatchResult can deduplicate when safe
|
|
430
|
+
for (const s of inheritedLoaders) {
|
|
431
|
+
s._inherited = true;
|
|
432
|
+
}
|
|
433
|
+
segments.push(...inheritedLoaders);
|
|
434
|
+
}
|
|
388
435
|
}
|
|
389
436
|
|
|
390
437
|
// Handler-first: orphan layout handler executes before its parallels
|
|
@@ -403,7 +450,10 @@ export async function resolveOrphanLayout<TEnv>(
|
|
|
403
450
|
belongsToRoute,
|
|
404
451
|
layoutName: orphan.id,
|
|
405
452
|
loading: orphan.loading === false ? null : orphan.loading,
|
|
406
|
-
transition:
|
|
453
|
+
transition: applyViewTransitionDefault(
|
|
454
|
+
orphan.transition,
|
|
455
|
+
deps.viewTransitionDefault,
|
|
456
|
+
),
|
|
407
457
|
...(orphan.mountPath ? { mountPath: orphan.mountPath } : {}),
|
|
408
458
|
});
|
|
409
459
|
|
|
@@ -475,6 +525,14 @@ export async function resolveParallelEntry<TEnv>(
|
|
|
475
525
|
if (handler === undefined) {
|
|
476
526
|
continue;
|
|
477
527
|
}
|
|
528
|
+
// Pin `_currentSegmentId` to the slot's own id so handle pushes from
|
|
529
|
+
// inside the slot handler get their own bucket in the HandleStore.
|
|
530
|
+
// Parent-keying would collapse them into the parent layout's bucket;
|
|
531
|
+
// the partial-update merge then replaces the parent's bucket on a
|
|
532
|
+
// slot-only revalidation and drops layout-pushed Meta/Breadcrumbs.
|
|
533
|
+
// filterSegmentOrder() retains slot ids so the client preserves them.
|
|
534
|
+
(context as InternalHandlerContext<any, TEnv>)._currentSegmentId =
|
|
535
|
+
`${parentShortCode}.${slot}`;
|
|
478
536
|
const doneParallelHandler = track(
|
|
479
537
|
`handler:${parallelEntry.id}.${slot}`,
|
|
480
538
|
2,
|
|
@@ -517,7 +575,10 @@ export async function resolveParallelEntry<TEnv>(
|
|
|
517
575
|
index: 0,
|
|
518
576
|
component,
|
|
519
577
|
loading: parallelEntry.loading === false ? null : parallelEntry.loading,
|
|
520
|
-
transition:
|
|
578
|
+
transition: applyViewTransitionDefault(
|
|
579
|
+
parallelEntry.transition,
|
|
580
|
+
deps.viewTransitionDefault,
|
|
581
|
+
),
|
|
521
582
|
params,
|
|
522
583
|
slot,
|
|
523
584
|
belongsToRoute,
|
|
@@ -580,6 +641,13 @@ export async function resolveAllSegments<TEnv>(
|
|
|
580
641
|
} catch {}
|
|
581
642
|
|
|
582
643
|
for (const entry of entries) {
|
|
644
|
+
// Set ALS flag when entering a cache() boundary so that ctx.get()
|
|
645
|
+
// can guard non-cacheable variable reads. Also guards response-level
|
|
646
|
+
// side effects (headers.set). Persists for all descendant entries.
|
|
647
|
+
if (entry.type === "cache") {
|
|
648
|
+
const store = RangoContext.getStore();
|
|
649
|
+
if (store) store.insideCacheScope = true;
|
|
650
|
+
}
|
|
583
651
|
const doneEntry = track(`segment:${entry.id}`, 1);
|
|
584
652
|
const resolvedSegments = await resolveWithErrorBoundary(
|
|
585
653
|
entry,
|
|
@@ -666,6 +734,30 @@ export async function resolveLoadersOnly<TEnv>(
|
|
|
666
734
|
const childBelongsToRoute = belongsToRoute || entry.type === "route";
|
|
667
735
|
for (const layoutEntry of entry.layout) {
|
|
668
736
|
await collectEntryLoaders(layoutEntry, childBelongsToRoute);
|
|
737
|
+
// Inherit route loaders for orphan layouts with parallels.
|
|
738
|
+
// Resolve directly — do NOT re-enter collectEntryLoaders with the
|
|
739
|
+
// route entry, as that would re-iterate route.layout and loop.
|
|
740
|
+
if (
|
|
741
|
+
entry.type === "route" &&
|
|
742
|
+
entry.loader &&
|
|
743
|
+
entry.loader.length > 0 &&
|
|
744
|
+
Object.keys(layoutEntry.parallel).length > 0
|
|
745
|
+
) {
|
|
746
|
+
const inherited = await resolveLoaders(
|
|
747
|
+
entry,
|
|
748
|
+
context,
|
|
749
|
+
childBelongsToRoute,
|
|
750
|
+
deps,
|
|
751
|
+
layoutEntry.shortCode,
|
|
752
|
+
);
|
|
753
|
+
for (const seg of inherited) {
|
|
754
|
+
if (!seenIds.has(seg.id)) {
|
|
755
|
+
seenIds.add(seg.id);
|
|
756
|
+
seg._inherited = true;
|
|
757
|
+
loaderSegments.push(seg);
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
}
|
|
669
761
|
}
|
|
670
762
|
}
|
|
671
763
|
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* - Error boundary segment creation
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import type
|
|
11
|
+
import { createElement, type ReactNode } from "react";
|
|
12
12
|
import { DataNotFoundError } from "../../errors";
|
|
13
13
|
import {
|
|
14
14
|
createErrorInfo,
|
|
@@ -180,34 +180,39 @@ export function catchSegmentError<TEnv>(
|
|
|
180
180
|
|
|
181
181
|
if (error instanceof DataNotFoundError) {
|
|
182
182
|
const notFoundFallback = deps.findNearestNotFoundBoundary(entry);
|
|
183
|
+
// Fall back to router's notFound component, then a plain default
|
|
184
|
+
const notFoundOption = deps.notFoundComponent;
|
|
185
|
+
const defaultFallback =
|
|
186
|
+
typeof notFoundOption === "function"
|
|
187
|
+
? notFoundOption({ pathname: pathname ?? "" })
|
|
188
|
+
: (notFoundOption ?? createElement("h1", null, "Not Found"));
|
|
189
|
+
const effectiveNotFoundFallback = notFoundFallback ?? defaultFallback;
|
|
183
190
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
);
|
|
191
|
+
const notFoundInfo = createNotFoundInfo(
|
|
192
|
+
error,
|
|
193
|
+
entry.shortCode,
|
|
194
|
+
entry.type,
|
|
195
|
+
pathname,
|
|
196
|
+
);
|
|
191
197
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
198
|
+
reportError(true, {
|
|
199
|
+
notFound: true,
|
|
200
|
+
message: notFoundInfo.message,
|
|
201
|
+
});
|
|
196
202
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
203
|
+
debugLog("segment", "notFound boundary handled error", {
|
|
204
|
+
segmentId: entry.shortCode,
|
|
205
|
+
message: notFoundInfo.message,
|
|
206
|
+
});
|
|
201
207
|
|
|
202
|
-
|
|
208
|
+
setResponseStatus(404);
|
|
203
209
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
}
|
|
210
|
+
return createNotFoundSegment(
|
|
211
|
+
notFoundInfo,
|
|
212
|
+
effectiveNotFoundFallback,
|
|
213
|
+
entry,
|
|
214
|
+
params,
|
|
215
|
+
);
|
|
211
216
|
}
|
|
212
217
|
|
|
213
218
|
const fallback = deps.findNearestErrorBoundary(entry);
|