@rangojs/router 0.0.0-experimental.b9cb8739 → 0.0.0-experimental.bd6e11bc
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 +277 -99
- package/dist/testing/vitest.js +48 -0
- package/dist/vite/index.js +2779 -1064
- 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 +155 -6
- 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 +197 -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 +716 -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 +91 -70
- package/src/browser/history-state.ts +21 -0
- package/src/browser/index.ts +3 -3
- package/src/browser/navigation-bridge.ts +102 -16
- package/src/browser/navigation-client.ts +164 -59
- package/src/browser/navigation-store.ts +75 -17
- package/src/browser/navigation-transaction.ts +21 -37
- package/src/browser/partial-update.ts +139 -38
- package/src/browser/prefetch/cache.ts +175 -15
- package/src/browser/prefetch/fetch.ts +180 -33
- package/src/browser/prefetch/queue.ts +123 -20
- package/src/browser/prefetch/resource-ready.ts +77 -0
- package/src/browser/rango-state.ts +53 -13
- package/src/browser/react/Link.tsx +81 -9
- package/src/browser/react/NavigationProvider.tsx +110 -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 +191 -74
- package/src/browser/scroll-restoration.ts +41 -14
- package/src/browser/segment-reconciler.ts +36 -9
- package/src/browser/segment-structure-assert.ts +2 -2
- package/src/browser/server-action-bridge.ts +31 -36
- package/src/browser/types.ts +57 -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 +278 -88
- 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-runtime.ts +15 -11
- package/src/cache/cache-scope.ts +76 -49
- package/src/cache/cf/cf-cache-store.ts +501 -18
- package/src/cache/cf/index.ts +5 -1
- package/src/cache/document-cache.ts +17 -7
- package/src/cache/index.ts +1 -0
- 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/debug.ts +2 -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 +2 -5
- 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 +435 -260
- 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 +11 -3
- package/src/route-definition/resolve-handler-use.ts +155 -0
- package/src/route-definition/use-item-types.ts +32 -0
- package/src/route-map-builder.ts +7 -1
- 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/find-match.ts +4 -2
- package/src/router/handler-context.ts +77 -38
- package/src/router/intercept-resolution.ts +15 -22
- package/src/router/lazy-includes.ts +12 -9
- package/src/router/loader-resolution.ts +174 -22
- package/src/router/logging.ts +5 -2
- package/src/router/manifest.ts +31 -16
- package/src/router/match-api.ts +128 -192
- package/src/router/match-handlers.ts +63 -20
- package/src/router/match-middleware/background-revalidation.ts +30 -2
- package/src/router/match-middleware/cache-lookup.ts +136 -106
- package/src/router/match-middleware/cache-store.ts +54 -10
- package/src/router/match-middleware/intercept-resolution.ts +9 -7
- package/src/router/match-middleware/segment-resolution.ts +61 -5
- package/src/router/match-result.ts +125 -10
- package/src/router/metrics.ts +7 -2
- package/src/router/middleware-types.ts +21 -34
- package/src/router/middleware.ts +103 -90
- 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-context.ts +6 -1
- 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 +223 -24
- package/src/router/segment-resolution/helpers.ts +29 -24
- package/src/router/segment-resolution/loader-cache.ts +1 -0
- package/src/router/segment-resolution/revalidation.ts +466 -285
- package/src/router/segment-resolution/view-transition-default.ts +36 -0
- package/src/router/segment-wrappers.ts +2 -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 +91 -23
- 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 +219 -67
- package/src/serialize.ts +243 -0
- package/src/server/context.ts +277 -61
- 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 +204 -60
- package/src/ssr/index.tsx +9 -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 +255 -0
- package/src/testing/render-route.tsx +565 -0
- package/src/testing/run-loader.ts +296 -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 +194 -72
- 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 +19 -1
- package/src/types/segments.ts +37 -1
- 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 -6
- package/src/vite/discovery/virtual-module-codegen.ts +13 -23
- package/src/vite/index.ts +6 -0
- package/src/vite/plugin-types.ts +111 -72
- 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 +55 -33
- 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 +72 -3
- package/src/vite/plugins/virtual-entries.ts +2 -2
- package/src/vite/rango.ts +265 -226
- package/src/vite/router-discovery.ts +920 -137
- package/src/vite/utils/ast-handler-extract.ts +15 -15
- package/src/vite/utils/banner.ts +4 -4
- 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
package/src/types/route-entry.ts
CHANGED
|
@@ -8,10 +8,21 @@ export interface LazyIncludeContext {
|
|
|
8
8
|
urlPrefix: string;
|
|
9
9
|
namePrefix: string | undefined;
|
|
10
10
|
parent: unknown; // EntryData - avoid circular import
|
|
11
|
+
/** Counter snapshot from pattern extraction for consistent shortCode indices */
|
|
12
|
+
counters?: Record<string, number>;
|
|
11
13
|
cacheProfiles?: Record<
|
|
12
14
|
string,
|
|
13
15
|
import("../cache/profile-registry.js").CacheProfile
|
|
14
16
|
>;
|
|
17
|
+
/** Root scope flag for dot-local reverse resolution */
|
|
18
|
+
rootScoped?: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Positional include scope token composed from the parent scope plus this
|
|
21
|
+
* include's sibling index (`${parentScope}I${idx}`). Applied to direct-
|
|
22
|
+
* descendant shortCodes during lazy evaluation so routes inside the
|
|
23
|
+
* include cannot collide with siblings declared outside it.
|
|
24
|
+
*/
|
|
25
|
+
includeScope?: string;
|
|
15
26
|
}
|
|
16
27
|
|
|
17
28
|
/**
|
|
@@ -55,6 +66,13 @@ export interface RouteEntry<TEnv = any> {
|
|
|
55
66
|
| Promise<() => Array<AllUseItems>>;
|
|
56
67
|
mountIndex: number;
|
|
57
68
|
|
|
69
|
+
/**
|
|
70
|
+
* Router ID that owns this entry. Used to namespace the manifest cache
|
|
71
|
+
* so multi-router setups (host routing) don't share cached EntryData
|
|
72
|
+
* across routers with overlapping mountIndex + routeKey combinations.
|
|
73
|
+
*/
|
|
74
|
+
routerId?: string;
|
|
75
|
+
|
|
58
76
|
/**
|
|
59
77
|
* Route keys in this entry that have pre-render handlers.
|
|
60
78
|
* Used by the non-trie match path to set the `pr` flag.
|
|
@@ -62,7 +80,7 @@ export interface RouteEntry<TEnv = any> {
|
|
|
62
80
|
prerenderRouteKeys?: Set<string>;
|
|
63
81
|
|
|
64
82
|
/**
|
|
65
|
-
* Route keys in this entry that
|
|
83
|
+
* Route keys in this entry that are wrapped with `Passthrough()`.
|
|
66
84
|
* Used by the non-trie match path to set the `pt` flag.
|
|
67
85
|
*/
|
|
68
86
|
passthroughRouteKeys?: Set<string>;
|
package/src/types/segments.ts
CHANGED
|
@@ -10,7 +10,10 @@ export type ViewTransitionClass = Record<string, string> | string;
|
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Configuration for React's <ViewTransition> component.
|
|
13
|
-
*
|
|
13
|
+
*
|
|
14
|
+
* The phase fields (enter/exit/update/share/default/name) map directly to
|
|
15
|
+
* ViewTransitionProps (minus children/ref/callbacks). The `viewTransition`
|
|
16
|
+
* field is router-specific and is stripped before the config reaches React.
|
|
14
17
|
*/
|
|
15
18
|
export interface TransitionConfig {
|
|
16
19
|
enter?: ViewTransitionClass;
|
|
@@ -19,6 +22,20 @@ export interface TransitionConfig {
|
|
|
19
22
|
share?: ViewTransitionClass;
|
|
20
23
|
default?: ViewTransitionClass;
|
|
21
24
|
name?: string;
|
|
25
|
+
/**
|
|
26
|
+
* Whether the router wraps this segment's content in its own
|
|
27
|
+
* <ViewTransition> boundary.
|
|
28
|
+
*
|
|
29
|
+
* - "auto" (default): the router places the boundary, producing the
|
|
30
|
+
* router-owned cross-fade described by the phase fields above.
|
|
31
|
+
* - false: the router places no boundary. The navigation commit is still
|
|
32
|
+
* driven through startTransition (so loaders hold instead of flashing a
|
|
33
|
+
* skeleton, and consumer-placed <ViewTransition> elements still animate),
|
|
34
|
+
* but the router contributes no cross-fade of its own.
|
|
35
|
+
*
|
|
36
|
+
* When unset, inherits the createRouter({ viewTransition }) default.
|
|
37
|
+
*/
|
|
38
|
+
viewTransition?: "auto" | false;
|
|
22
39
|
}
|
|
23
40
|
|
|
24
41
|
/**
|
|
@@ -50,7 +67,9 @@ export interface ResolvedSegment {
|
|
|
50
67
|
parallelName?: string; // For parallels: the parallel group name (used to match with revalidations)
|
|
51
68
|
// Loader-specific fields
|
|
52
69
|
loaderId?: string; // For loaders: the loader $$id identifier
|
|
70
|
+
_inherited?: boolean; // For inherited loaders: dedup marker for buildMatchResult
|
|
53
71
|
loaderData?: any; // For loaders: the resolved data from loader execution
|
|
72
|
+
parallelLoading?: ReactNode; // For parallel-owned loaders: the parallel's loading fallback
|
|
54
73
|
// Intercept loader fields (for streaming loader data in parallel segments)
|
|
55
74
|
loaderDataPromise?: Promise<any[]> | any[]; // Loader data promise or resolved array
|
|
56
75
|
loaderIds?: string[]; // IDs ($$id) of loaders for this segment
|
|
@@ -60,6 +79,14 @@ export interface ResolvedSegment {
|
|
|
60
79
|
notFoundInfo?: NotFoundInfo; // For notFound segments: the not found information
|
|
61
80
|
// Mount path from include() scope, used for MountContext.Provider wrapping
|
|
62
81
|
mountPath?: string;
|
|
82
|
+
/**
|
|
83
|
+
* @internal Server-side marker: true when the segment's handler actually ran
|
|
84
|
+
* this request (not skipped via the revalidate cache path). Used by
|
|
85
|
+
* match-result.ts to populate `MatchResult.resolvedIds` for client-side
|
|
86
|
+
* handle-bucket cleanup. Stripped from the wire payload before serialization
|
|
87
|
+
* — never reaches the client.
|
|
88
|
+
*/
|
|
89
|
+
_handlerRan?: boolean;
|
|
63
90
|
}
|
|
64
91
|
|
|
65
92
|
/**
|
|
@@ -114,6 +141,15 @@ export interface MatchResult {
|
|
|
114
141
|
segments: ResolvedSegment[];
|
|
115
142
|
matched: string[];
|
|
116
143
|
diff: string[];
|
|
144
|
+
/**
|
|
145
|
+
* Every segment id whose handler actually ran on the server this request,
|
|
146
|
+
* including ones with `component === null` that get filtered out of
|
|
147
|
+
* `segments`/`diff` to avoid wasted bytes. Drives the client's handle-
|
|
148
|
+
* cleanup pass — a slot that re-resolves and pushes nothing must clear
|
|
149
|
+
* its previous handle bucket, but `diff` doesn't carry it because the
|
|
150
|
+
* segment payload doesn't either. A superset of `diff`.
|
|
151
|
+
*/
|
|
152
|
+
resolvedIds: string[];
|
|
117
153
|
/**
|
|
118
154
|
* Merged route params from all matched segments
|
|
119
155
|
* Available for use by the handler after route matching
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import type { AllUseItems, IncludeItem } from "../route-types.js";
|
|
2
2
|
import {
|
|
3
|
-
getContext,
|
|
4
|
-
runWithPrefixes,
|
|
5
3
|
getUrlPrefix,
|
|
6
4
|
getNamePrefix,
|
|
7
|
-
|
|
5
|
+
requireDslContext,
|
|
8
6
|
} from "../server/context";
|
|
9
7
|
import {
|
|
10
8
|
INTERNAL_INCLUDE_SCOPE_PREFIX,
|
|
@@ -27,28 +25,10 @@ function allocateInternalIncludeScopeId(
|
|
|
27
25
|
}
|
|
28
26
|
|
|
29
27
|
/**
|
|
30
|
-
*
|
|
31
|
-
* This expands the include into actual route registrations
|
|
32
|
-
*/
|
|
33
|
-
function processIncludeItem(item: IncludeItem): AllUseItems[] {
|
|
34
|
-
const { prefix, patterns } = item;
|
|
35
|
-
const namePrefix =
|
|
36
|
-
(item as IncludeItem & { _lazyContext?: { namePrefix?: string } })
|
|
37
|
-
._lazyContext?.namePrefix ?? item.options?.name;
|
|
38
|
-
|
|
39
|
-
// Execute the nested patterns' handler with URL and name prefixes
|
|
40
|
-
// The urlPrefix being set tells nested urls() to skip RootLayout wrapping
|
|
41
|
-
return runWithPrefixes(prefix, namePrefix, () => {
|
|
42
|
-
// Call the nested patterns' handler - this registers routes with prefixed patterns/names
|
|
43
|
-
return (patterns as UrlPatterns).handler();
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Recursively process items, expanding any IncludeItems
|
|
49
|
-
* Returns items with IncludeItems expanded into actual route items
|
|
28
|
+
* Recursively walk items, recursing into layout children.
|
|
50
29
|
*
|
|
51
|
-
*
|
|
30
|
+
* All includes are lazy and kept as-is; the router expands them on the first
|
|
31
|
+
* matching request.
|
|
52
32
|
*/
|
|
53
33
|
export function processItems(items: readonly AllUseItems[]): AllUseItems[] {
|
|
54
34
|
const result: AllUseItems[] = [];
|
|
@@ -57,26 +37,8 @@ export function processItems(items: readonly AllUseItems[]): AllUseItems[] {
|
|
|
57
37
|
if (!item) continue;
|
|
58
38
|
|
|
59
39
|
if (item.type === "include") {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
lazy?: boolean;
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
// Lazy includes are NOT expanded here - kept for router to handle
|
|
66
|
-
if (includeItem.lazy) {
|
|
67
|
-
result.push(item);
|
|
68
|
-
continue;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Eager includes are already expanded during include() call
|
|
72
|
-
if (includeItem._expanded) {
|
|
73
|
-
// Items were expanded immediately - just process them recursively
|
|
74
|
-
result.push(...processItems(includeItem._expanded));
|
|
75
|
-
} else {
|
|
76
|
-
// Fallback for legacy include items without _expanded
|
|
77
|
-
const expanded = processIncludeItem(item as IncludeItem);
|
|
78
|
-
result.push(...processItems(expanded));
|
|
79
|
-
}
|
|
40
|
+
// All includes are lazy; the router expands them on first matching request.
|
|
41
|
+
result.push(item);
|
|
80
42
|
} else if (item.type === "layout" && (item as any).uses) {
|
|
81
43
|
// Process nested items in layout
|
|
82
44
|
const layoutItem = item as any;
|
|
@@ -93,13 +55,9 @@ export function processItems(items: readonly AllUseItems[]): AllUseItems[] {
|
|
|
93
55
|
/**
|
|
94
56
|
* Create include() helper for composing URL patterns
|
|
95
57
|
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
* With `lazy: true`, patterns are NOT expanded at definition time. Instead,
|
|
101
|
-
* they're evaluated on first request that matches the prefix. This improves
|
|
102
|
-
* cold start time for apps with many routes.
|
|
58
|
+
* All includes are lazy: the nested patterns are NOT expanded at definition
|
|
59
|
+
* time. Instead they are evaluated on the first request that matches the
|
|
60
|
+
* prefix, which improves cold start time for apps with many routes.
|
|
103
61
|
*/
|
|
104
62
|
export function createIncludeHelper<TEnv>(): IncludeFn<TEnv> {
|
|
105
63
|
return (
|
|
@@ -107,9 +65,7 @@ export function createIncludeHelper<TEnv>(): IncludeFn<TEnv> {
|
|
|
107
65
|
patterns: UrlPatterns<TEnv>,
|
|
108
66
|
options?: IncludeOptions,
|
|
109
67
|
): IncludeItem => {
|
|
110
|
-
const
|
|
111
|
-
const ctx = store.getStore();
|
|
112
|
-
if (!ctx) throw new Error("include() must be called inside urls()");
|
|
68
|
+
const { ctx } = requireDslContext("include() must be called inside urls()");
|
|
113
69
|
|
|
114
70
|
const explicitName = options?.name;
|
|
115
71
|
const hasExplicitName = hasExplicitNameOption(options);
|
|
@@ -149,22 +105,32 @@ export function createIncludeHelper<TEnv>(): IncludeFn<TEnv> {
|
|
|
149
105
|
});
|
|
150
106
|
}
|
|
151
107
|
|
|
152
|
-
//
|
|
153
|
-
//
|
|
154
|
-
//
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
//
|
|
158
|
-
//
|
|
159
|
-
//
|
|
160
|
-
// and
|
|
161
|
-
|
|
108
|
+
// Allocate an include-scope token for this include() call. The token is
|
|
109
|
+
// appended to the parent's shortCode prefix whenever the include's
|
|
110
|
+
// direct-descendant shortCodes are generated (see getShortCode in
|
|
111
|
+
// context.ts), partitioning the parent's counter namespace so routes
|
|
112
|
+
// inside an include cannot collide with siblings declared outside it.
|
|
113
|
+
//
|
|
114
|
+
// Scopes compose: a nested include inside an outer include with scope
|
|
115
|
+
// "I0" allocates against the `${parent.shortCode}I0_include` counter
|
|
116
|
+
// and produces scope "I0I0", "I0I1", etc.
|
|
117
|
+
const parentScope = ctx.includeScope ?? "";
|
|
118
|
+
let includeScope = parentScope;
|
|
162
119
|
if (capturedParent?.shortCode) {
|
|
163
|
-
const
|
|
164
|
-
ctx.counters[
|
|
165
|
-
ctx.counters[
|
|
120
|
+
const includeCounterKey = `${capturedParent.shortCode}${parentScope}_include`;
|
|
121
|
+
ctx.counters[includeCounterKey] ??= 0;
|
|
122
|
+
const includeIdx = ctx.counters[includeCounterKey];
|
|
123
|
+
ctx.counters[includeCounterKey] = includeIdx + 1;
|
|
124
|
+
includeScope = `${parentScope}I${includeIdx}`;
|
|
166
125
|
}
|
|
167
126
|
|
|
127
|
+
// Snapshot parent's counters AFTER allocating the include scope so lazy
|
|
128
|
+
// manifest generation starts with the same counter state this include
|
|
129
|
+
// observed — its descendants still get fresh per-scope counters because
|
|
130
|
+
// they key off `${parent.shortCode}${includeScope}_*` (not shared with
|
|
131
|
+
// siblings outside the include).
|
|
132
|
+
const capturedCounters = { ...ctx.counters };
|
|
133
|
+
|
|
168
134
|
// Compute rootScoped at capture time, mirroring the logic in runWithPrefixes.
|
|
169
135
|
// This ensures lazy evaluation restores the correct scope state.
|
|
170
136
|
const parentRootScoped = ctx.rootScoped;
|
|
@@ -191,6 +157,7 @@ export function createIncludeHelper<TEnv>(): IncludeFn<TEnv> {
|
|
|
191
157
|
counters: capturedCounters,
|
|
192
158
|
cacheProfiles: ctx.cacheProfiles,
|
|
193
159
|
rootScoped: capturedRootScoped,
|
|
160
|
+
includeScope,
|
|
194
161
|
},
|
|
195
162
|
} as IncludeItem;
|
|
196
163
|
};
|
package/src/urls/index.ts
CHANGED
|
@@ -13,7 +13,6 @@ export type {
|
|
|
13
13
|
UnnamedRoute,
|
|
14
14
|
LocalOnlyInclude,
|
|
15
15
|
PathOptions,
|
|
16
|
-
PathDefinition,
|
|
17
16
|
UrlPatterns,
|
|
18
17
|
IncludeOptions,
|
|
19
18
|
} from "./pattern-types.js";
|
|
@@ -22,8 +21,6 @@ export type {
|
|
|
22
21
|
export type {
|
|
23
22
|
ExtractRoutes,
|
|
24
23
|
ExtractResponses,
|
|
25
|
-
ExtractRouteNames,
|
|
26
|
-
ExtractPathParams,
|
|
27
24
|
ResponseError,
|
|
28
25
|
ResponseEnvelope,
|
|
29
26
|
RouteResponse,
|
|
@@ -37,7 +37,10 @@ import type {
|
|
|
37
37
|
UseItems,
|
|
38
38
|
} from "../route-types.js";
|
|
39
39
|
import type { SearchSchema } from "../search-params.js";
|
|
40
|
-
import type {
|
|
40
|
+
import type {
|
|
41
|
+
PrerenderHandlerDefinition,
|
|
42
|
+
PassthroughHandlerDefinition,
|
|
43
|
+
} from "../prerender.js";
|
|
41
44
|
import type { StaticHandlerDefinition } from "../static-handler.js";
|
|
42
45
|
import type { InterceptWhenFn } from "../server/context";
|
|
43
46
|
import type {
|
|
@@ -70,6 +73,7 @@ export type PathFn<TEnv> = <
|
|
|
70
73
|
ctx: HandlerContext<TParams, TEnv, TSearch>,
|
|
71
74
|
) => ReactNode | Promise<ReactNode> | Response | Promise<Response>)
|
|
72
75
|
| PrerenderHandlerDefinition<TParams>
|
|
76
|
+
| PassthroughHandlerDefinition<TParams, TEnv>
|
|
73
77
|
| StaticHandlerDefinition<TParams>,
|
|
74
78
|
optionsOrUse?: PathOptions<TName, TSearch> | (() => UseItems<RouteUseItem>),
|
|
75
79
|
use?: () => UseItems<RouteUseItem>,
|
|
@@ -229,12 +233,27 @@ export type PathHelpers<TEnv> = {
|
|
|
229
233
|
include: IncludeFn<TEnv>;
|
|
230
234
|
|
|
231
235
|
/**
|
|
232
|
-
* Define parallel routes that render simultaneously in named slots
|
|
236
|
+
* Define parallel routes that render simultaneously in named slots.
|
|
237
|
+
*
|
|
238
|
+
* A slot value can be a Handler / ReactNode / StaticHandlerDefinition
|
|
239
|
+
* (legacy form, broadcast use applies to every slot) or a slot descriptor
|
|
240
|
+
* `{ handler, use? }` whose `use` is scoped to that slot only. Per-slot
|
|
241
|
+
* merge order is `handler.use` → shared `use` → slot-local `use`, with
|
|
242
|
+
* narrowest scope winning for last-write-wins items like `loading()`.
|
|
233
243
|
*/
|
|
234
244
|
parallel: <
|
|
235
245
|
TSlots extends Record<
|
|
236
246
|
`@${string}`,
|
|
237
|
-
Handler<any, any, TEnv>
|
|
247
|
+
| Handler<any, any, TEnv>
|
|
248
|
+
| ReactNode
|
|
249
|
+
| StaticHandlerDefinition
|
|
250
|
+
| {
|
|
251
|
+
handler:
|
|
252
|
+
| Handler<any, any, TEnv>
|
|
253
|
+
| ReactNode
|
|
254
|
+
| StaticHandlerDefinition;
|
|
255
|
+
use?: () => ParallelUseItem[];
|
|
256
|
+
}
|
|
238
257
|
>,
|
|
239
258
|
>(
|
|
240
259
|
slots: TSlots,
|
|
@@ -245,7 +264,7 @@ export type PathHelpers<TEnv> = {
|
|
|
245
264
|
* Define an intercepting route for soft navigation
|
|
246
265
|
* Note: routeName must match a named path() in this urlpatterns
|
|
247
266
|
*/
|
|
248
|
-
intercept: keyof
|
|
267
|
+
intercept: keyof Rango.GeneratedRouteMap extends never
|
|
249
268
|
? (
|
|
250
269
|
slotName: `@${string}`,
|
|
251
270
|
routeName: string,
|
|
@@ -254,15 +273,26 @@ export type PathHelpers<TEnv> = {
|
|
|
254
273
|
) => InterceptItem
|
|
255
274
|
: (
|
|
256
275
|
slotName: `@${string}`,
|
|
257
|
-
routeName: (keyof
|
|
276
|
+
routeName: (keyof Rango.GeneratedRouteMap & string) | `.${string}`,
|
|
258
277
|
handler: ReactNode | Handler<any, any, TEnv>,
|
|
259
278
|
use?: () => InterceptUseItem[],
|
|
260
279
|
) => InterceptItem;
|
|
261
280
|
|
|
262
281
|
/**
|
|
263
|
-
* Attach middleware to the current route/layout
|
|
282
|
+
* Attach middleware to the current route/layout, or wrap child segments
|
|
264
283
|
*/
|
|
265
|
-
middleware:
|
|
284
|
+
middleware: {
|
|
285
|
+
(fn: MiddlewareFn<TEnv>): MiddlewareItem;
|
|
286
|
+
(
|
|
287
|
+
fn: MiddlewareFn<TEnv>,
|
|
288
|
+
children: () => UseItems<LayoutUseItem>,
|
|
289
|
+
): MiddlewareItem;
|
|
290
|
+
(fns: MiddlewareFn<TEnv>[]): MiddlewareItem;
|
|
291
|
+
(
|
|
292
|
+
fns: MiddlewareFn<TEnv>[],
|
|
293
|
+
children: () => UseItems<LayoutUseItem>,
|
|
294
|
+
): MiddlewareItem;
|
|
295
|
+
};
|
|
266
296
|
|
|
267
297
|
/**
|
|
268
298
|
* Control when a segment should revalidate during navigation
|
|
@@ -280,7 +310,10 @@ export type PathHelpers<TEnv> = {
|
|
|
280
310
|
/**
|
|
281
311
|
* Attach a loading component to the current route/layout
|
|
282
312
|
*/
|
|
283
|
-
loading: (
|
|
313
|
+
loading: (
|
|
314
|
+
component: ReactNode | (() => ReactNode),
|
|
315
|
+
options?: { ssr?: boolean },
|
|
316
|
+
) => LoadingItem;
|
|
284
317
|
|
|
285
318
|
/**
|
|
286
319
|
* Attach an error boundary to catch errors in this segment
|
|
@@ -317,7 +350,15 @@ export type PathHelpers<TEnv> = {
|
|
|
317
350
|
};
|
|
318
351
|
|
|
319
352
|
/**
|
|
320
|
-
*
|
|
353
|
+
* Opt a route (or group of routes) into transition-driven navigation.
|
|
354
|
+
*
|
|
355
|
+
* Two independent layers: (1) startTransition, on all React versions, holds
|
|
356
|
+
* the previous content across a same-route nav (no skeleton flash) and is the
|
|
357
|
+
* precondition for any view transition; (2) on experimental React, an
|
|
358
|
+
* additional `<ViewTransition>` boundary cross-fades/morphs the swap. Pass
|
|
359
|
+
* `{ viewTransition: false }` to keep #1 without the router boundary. A view
|
|
360
|
+
* transition cannot fire without a startTransition. See
|
|
361
|
+
* skills/view-transitions for the startTransition x ViewTransition matrix.
|
|
321
362
|
*/
|
|
322
363
|
transition: {
|
|
323
364
|
(): TransitionItem;
|
package/src/urls/path-helper.ts
CHANGED
|
@@ -1,21 +1,17 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
2
|
import type { Handler } from "../types.js";
|
|
3
|
-
import type {
|
|
4
|
-
AllUseItems,
|
|
5
|
-
RouteItem,
|
|
6
|
-
RouteUseItem,
|
|
7
|
-
UseItems,
|
|
8
|
-
} from "../route-types.js";
|
|
3
|
+
import type { RouteItem, RouteUseItem, UseItems } from "../route-types.js";
|
|
9
4
|
import {
|
|
10
|
-
getContext,
|
|
11
5
|
getUrlPrefix,
|
|
12
6
|
getNamePrefix,
|
|
13
7
|
getRootScoped,
|
|
8
|
+
requireDslContext,
|
|
14
9
|
} from "../server/context";
|
|
15
|
-
import { invariant } from "../errors";
|
|
10
|
+
import { invariant, DataNotFoundError } from "../errors";
|
|
16
11
|
import { validateUserRouteName } from "../route-name.js";
|
|
17
12
|
import {
|
|
18
13
|
isPrerenderHandler,
|
|
14
|
+
isPassthroughHandler,
|
|
19
15
|
type PrerenderHandlerDefinition,
|
|
20
16
|
} from "../prerender.js";
|
|
21
17
|
import {
|
|
@@ -34,35 +30,14 @@ import type {
|
|
|
34
30
|
JsonResponsePathFn,
|
|
35
31
|
TextResponsePathFn,
|
|
36
32
|
} from "./path-helper-types.js";
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
(item &&
|
|
46
|
-
typeof item === "object" &&
|
|
47
|
-
"type" in item &&
|
|
48
|
-
[
|
|
49
|
-
"layout",
|
|
50
|
-
"route",
|
|
51
|
-
"middleware",
|
|
52
|
-
"revalidate",
|
|
53
|
-
"parallel",
|
|
54
|
-
"intercept",
|
|
55
|
-
"loader",
|
|
56
|
-
"loading",
|
|
57
|
-
"errorBoundary",
|
|
58
|
-
"notFoundBoundary",
|
|
59
|
-
"when",
|
|
60
|
-
"cache",
|
|
61
|
-
"transition",
|
|
62
|
-
"include",
|
|
63
|
-
].includes(item.type))
|
|
64
|
-
);
|
|
65
|
-
};
|
|
33
|
+
import {
|
|
34
|
+
resolveHandlerUse,
|
|
35
|
+
mergeHandlerUse,
|
|
36
|
+
} from "../route-definition/resolve-handler-use.js";
|
|
37
|
+
import {
|
|
38
|
+
emptySegmentBase,
|
|
39
|
+
runAndValidateUseItems,
|
|
40
|
+
} from "../route-definition/dsl-helpers.js";
|
|
66
41
|
|
|
67
42
|
/**
|
|
68
43
|
* Apply URL prefix to a pattern
|
|
@@ -107,9 +82,9 @@ export function createPathHelper<TEnv>(): PathFn<TEnv> {
|
|
|
107
82
|
optionsOrUse?: PathOptions | (() => UseItems<RouteUseItem>),
|
|
108
83
|
maybeUse?: () => UseItems<RouteUseItem>,
|
|
109
84
|
): RouteItem => {
|
|
110
|
-
const store =
|
|
111
|
-
|
|
112
|
-
|
|
85
|
+
const { store, ctx } = requireDslContext(
|
|
86
|
+
"path() must be called inside urls()",
|
|
87
|
+
);
|
|
113
88
|
|
|
114
89
|
invariant(
|
|
115
90
|
!ctx.parent || ctx.parent.type !== "parallel",
|
|
@@ -142,6 +117,12 @@ export function createPathHelper<TEnv>(): PathFn<TEnv> {
|
|
|
142
117
|
use = maybeUse;
|
|
143
118
|
}
|
|
144
119
|
|
|
120
|
+
// Merge handler.use() defaults with explicit use()
|
|
121
|
+
// Response routes (path.json, path.text, etc.) only allow middleware + cache
|
|
122
|
+
const handlerUseFn = resolveHandlerUse(handler);
|
|
123
|
+
const mountSite = resolveResponseType(options) ? "response" : "path";
|
|
124
|
+
const mergedUse = mergeHandlerUse(handlerUseFn, use, mountSite);
|
|
125
|
+
|
|
145
126
|
// Get prefixes from context (set by include())
|
|
146
127
|
const urlPrefix = getUrlPrefix();
|
|
147
128
|
const namePrefix = getNamePrefix();
|
|
@@ -176,16 +157,34 @@ export function createPathHelper<TEnv>(): PathFn<TEnv> {
|
|
|
176
157
|
}
|
|
177
158
|
|
|
178
159
|
// Ensure handler is always a function (wrap ReactNode or extract from prerender/static def)
|
|
160
|
+
// For prerender stubs (production builds where handler code is evicted),
|
|
161
|
+
// handler.handler is undefined — provide a notFound fallback so requests
|
|
162
|
+
// for non-prerendered params get 404 instead of "handler is not a function".
|
|
179
163
|
const wrappedHandler: Handler<any, any, TEnv> =
|
|
180
164
|
typeof handler === "function"
|
|
181
165
|
? (handler as Handler<any, any, TEnv>)
|
|
182
|
-
:
|
|
183
|
-
?
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
166
|
+
: isPassthroughHandler(handler)
|
|
167
|
+
? typeof handler.prerenderDef.handler === "function"
|
|
168
|
+
? (handler.prerenderDef.handler as Handler<any, any, TEnv>)
|
|
169
|
+
: () => {
|
|
170
|
+
throw new DataNotFoundError(
|
|
171
|
+
"No prerender data found for this route",
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
: isPrerenderHandler(handler)
|
|
175
|
+
? typeof handler.handler === "function"
|
|
176
|
+
? (handler.handler as Handler<any, any, TEnv>)
|
|
177
|
+
: () => {
|
|
178
|
+
throw new DataNotFoundError(
|
|
179
|
+
"No prerender data found for this route",
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
: isStaticHandler(handler)
|
|
183
|
+
? (handler.handler as Handler<any, any, TEnv>)
|
|
184
|
+
: () => handler;
|
|
187
185
|
|
|
188
186
|
const entry = {
|
|
187
|
+
...emptySegmentBase(),
|
|
189
188
|
id: namespace,
|
|
190
189
|
shortCode: store.getShortCode("route"),
|
|
191
190
|
type: "route" as const,
|
|
@@ -193,22 +192,20 @@ export function createPathHelper<TEnv>(): PathFn<TEnv> {
|
|
|
193
192
|
handler: wrappedHandler,
|
|
194
193
|
// Store the PREFIXED pattern for route matching
|
|
195
194
|
pattern: prefixedPattern,
|
|
196
|
-
loading: undefined,
|
|
197
|
-
middleware: [],
|
|
198
|
-
revalidate: [],
|
|
199
|
-
errorBoundary: [],
|
|
200
|
-
notFoundBoundary: [],
|
|
201
|
-
layout: [],
|
|
202
|
-
parallel: [],
|
|
203
|
-
intercept: [],
|
|
204
|
-
loader: [],
|
|
205
195
|
...(urlPrefix ? { mountPath: urlPrefix } : {}),
|
|
206
|
-
...(
|
|
196
|
+
...(isPassthroughHandler(handler)
|
|
207
197
|
? {
|
|
208
198
|
isPrerender: true as const,
|
|
209
|
-
prerenderDef: handler as PrerenderHandlerDefinition,
|
|
199
|
+
prerenderDef: handler.prerenderDef as PrerenderHandlerDefinition,
|
|
200
|
+
isPassthrough: true as const,
|
|
201
|
+
liveHandler: handler.liveHandler as Handler<any, any, TEnv>,
|
|
210
202
|
}
|
|
211
|
-
:
|
|
203
|
+
: isPrerenderHandler(handler)
|
|
204
|
+
? {
|
|
205
|
+
isPrerender: true as const,
|
|
206
|
+
prerenderDef: handler as PrerenderHandlerDefinition,
|
|
207
|
+
}
|
|
208
|
+
: {}),
|
|
212
209
|
...(isStaticHandler(handler)
|
|
213
210
|
? {
|
|
214
211
|
isStaticPrerender: true as const,
|
|
@@ -264,12 +261,15 @@ export function createPathHelper<TEnv>(): PathFn<TEnv> {
|
|
|
264
261
|
registerSearchSchema(routeName, options.search);
|
|
265
262
|
}
|
|
266
263
|
|
|
267
|
-
// Run use callback if
|
|
268
|
-
if (
|
|
269
|
-
const result =
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
264
|
+
// Run merged use callback (handler.use defaults + explicit use) if present
|
|
265
|
+
if (mergedUse) {
|
|
266
|
+
const result = runAndValidateUseItems(
|
|
267
|
+
store,
|
|
268
|
+
namespace,
|
|
269
|
+
entry,
|
|
270
|
+
mergedUse,
|
|
271
|
+
"path",
|
|
272
|
+
"use",
|
|
273
273
|
);
|
|
274
274
|
return { name: namespace, type: "route", uses: result } as RouteItem;
|
|
275
275
|
}
|