@rangojs/router 0.0.0-experimental.124 → 0.0.0-experimental.126
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 +6 -4
- package/dist/bin/rango.js +3 -4
- package/dist/vite/index.js +315 -68
- package/package.json +19 -18
- package/skills/breadcrumbs/SKILL.md +60 -0
- package/skills/hooks/SKILL.md +2 -2
- package/skills/route/SKILL.md +6 -0
- package/skills/server-actions/SKILL.md +25 -1
- package/skills/testing/SKILL.md +17 -17
- package/skills/testing/cache-prerender.md +29 -3
- package/skills/testing/flight.md +13 -10
- package/skills/testing/render-handler.md +3 -0
- package/skills/testing/server-tree.md +1 -1
- package/skills/testing/setup.md +1 -1
- package/src/__internal.ts +0 -65
- package/src/browser/action-coordinator.ts +1 -1
- package/src/browser/action-fence.ts +10 -0
- package/src/browser/event-controller.ts +1 -83
- package/src/browser/navigation-store-handle.ts +3 -4
- package/src/browser/navigation-store.ts +0 -39
- package/src/browser/navigation-transaction.ts +0 -32
- package/src/browser/partial-update.ts +23 -84
- package/src/browser/prefetch/cache.ts +6 -45
- package/src/browser/prefetch/queue.ts +6 -3
- package/src/browser/rango-state.ts +2 -23
- package/src/browser/react/Link.tsx +0 -2
- package/src/browser/react/NavigationProvider.tsx +2 -1
- package/src/browser/react/ScrollRestoration.tsx +10 -6
- package/src/browser/react/filter-segment-order.ts +0 -2
- package/src/browser/react/index.ts +0 -45
- package/src/browser/react/location-state-shared.ts +0 -13
- package/src/browser/react/location-state.ts +0 -1
- package/src/browser/react/use-action.ts +6 -15
- package/src/browser/react/use-handle.ts +0 -5
- package/src/browser/react/use-link-status.ts +0 -4
- package/src/browser/react/use-navigation.ts +0 -3
- package/src/browser/react/use-params.ts +0 -2
- package/src/browser/react/use-router.ts +2 -1
- package/src/browser/react/use-search-params.ts +0 -5
- package/src/browser/react/use-segments.ts +0 -13
- package/src/browser/rsc-router.tsx +10 -3
- package/src/browser/server-action-bridge.ts +51 -3
- package/src/browser/types.ts +23 -5
- package/src/browser/validate-redirect-origin.ts +43 -16
- package/src/build/index.ts +8 -9
- package/src/build/route-trie.ts +46 -11
- package/src/build/route-types/param-extraction.ts +6 -3
- package/src/build/route-types/router-processing.ts +0 -8
- package/src/cache/cache-policy.ts +0 -54
- package/src/cache/cache-runtime.ts +48 -24
- package/src/cache/cache-scope.ts +0 -27
- package/src/cache/cache-tag.ts +0 -37
- package/src/cache/cf/cf-cache-store.ts +72 -45
- package/src/cache/cf/index.ts +0 -24
- package/src/cache/document-cache.ts +10 -36
- package/src/cache/handle-snapshot.ts +0 -40
- package/src/cache/index.ts +0 -27
- package/src/cache/memory-segment-store.ts +0 -52
- package/src/cache/profile-registry.ts +6 -30
- package/src/cache/read-through-swr.ts +41 -11
- package/src/cache/segment-codec.ts +0 -16
- package/src/cache/types.ts +0 -98
- package/src/client.rsc.tsx +4 -22
- package/src/client.tsx +19 -32
- package/src/context-var.ts +12 -0
- package/src/defer.ts +196 -0
- package/src/deps/ssr.ts +0 -1
- package/src/handle.ts +2 -12
- package/src/handles/MetaTags.tsx +0 -14
- package/src/handles/breadcrumbs.ts +16 -5
- package/src/handles/meta.ts +0 -39
- package/src/host/cookie-handler.ts +0 -36
- package/src/host/errors.ts +0 -24
- package/src/host/index.ts +6 -0
- package/src/host/pattern-matcher.ts +7 -50
- package/src/host/router.ts +1 -65
- package/src/host/testing.ts +0 -16
- package/src/host/types.ts +6 -2
- package/src/href-client.ts +0 -4
- package/src/index.rsc.ts +27 -2
- package/src/index.ts +7 -0
- package/src/internal-debug.ts +2 -4
- package/src/loader.rsc.ts +4 -15
- package/src/loader.ts +3 -9
- package/src/network-error-thrower.tsx +1 -6
- package/src/outlet-provider.tsx +1 -5
- package/src/prerender/param-hash.ts +10 -11
- package/src/prerender/store.ts +23 -30
- package/src/prerender.ts +34 -0
- package/src/redirect-origin.ts +100 -0
- package/src/root-error-boundary.tsx +1 -19
- package/src/route-content-wrapper.tsx +1 -44
- package/src/route-definition/dsl-helpers.ts +7 -19
- package/src/route-definition/helpers-types.ts +3 -3
- package/src/route-definition/redirect.ts +43 -9
- package/src/route-definition/resolve-handler-use.ts +6 -0
- package/src/route-map-builder.ts +0 -16
- package/src/router/content-negotiation.ts +0 -13
- package/src/router/error-handling.ts +12 -16
- package/src/router/find-match.ts +4 -31
- package/src/router/intercept-resolution.ts +10 -1
- package/src/router/lazy-includes.ts +1 -57
- package/src/router/loader-resolution.ts +25 -23
- package/src/router/logging.ts +0 -6
- package/src/router/manifest.ts +1 -25
- package/src/router/match-api.ts +0 -20
- package/src/router/match-context.ts +0 -22
- package/src/router/match-handlers.ts +0 -43
- package/src/router/match-middleware/background-revalidation.ts +0 -7
- package/src/router/match-middleware/cache-lookup.ts +96 -179
- package/src/router/match-middleware/cache-store.ts +0 -31
- package/src/router/match-middleware/intercept-resolution.ts +0 -22
- package/src/router/match-middleware/segment-resolution.ts +0 -22
- package/src/router/match-pipelines.ts +1 -42
- package/src/router/match-result.ts +1 -52
- package/src/router/metrics.ts +0 -34
- package/src/router/middleware-types.ts +0 -116
- package/src/router/middleware.ts +77 -60
- package/src/router/navigation-snapshot.ts +0 -51
- package/src/router/params-util.ts +23 -0
- package/src/router/pattern-matching.ts +5 -56
- package/src/router/prerender-match.ts +56 -51
- package/src/router/request-classification.ts +1 -38
- package/src/router/revalidation.ts +14 -62
- package/src/router/route-snapshot.ts +0 -1
- package/src/router/router-context.ts +0 -27
- package/src/router/router-interfaces.ts +10 -0
- package/src/router/segment-resolution/fresh.ts +25 -57
- package/src/router/segment-resolution/helpers.ts +34 -0
- package/src/router/segment-resolution/loader-cache.ts +35 -23
- package/src/router/segment-resolution/revalidation.ts +188 -283
- package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
- package/src/router/segment-resolution.ts +4 -1
- package/src/router/segment-wrappers.ts +0 -3
- package/src/router/telemetry-otel.ts +0 -20
- package/src/router/telemetry.ts +0 -22
- package/src/router/timeout.ts +0 -20
- package/src/router/trie-matching.ts +66 -45
- package/src/router/types.ts +1 -63
- package/src/router/url-params.ts +0 -5
- package/src/router.ts +8 -11
- package/src/rsc/handler-context.ts +1 -0
- package/src/rsc/handler.ts +20 -4
- package/src/rsc/helpers.ts +71 -3
- package/src/rsc/json-route-result.ts +38 -0
- package/src/rsc/origin-guard.ts +9 -15
- package/src/rsc/progressive-enhancement.ts +10 -1
- package/src/rsc/redirect-guard.ts +99 -0
- package/src/rsc/response-route-handler.ts +23 -18
- package/src/rsc/rsc-rendering.ts +2 -7
- package/src/rsc/runtime-warnings.ts +14 -0
- package/src/rsc/server-action.ts +34 -29
- package/src/rsc/types.ts +6 -3
- package/src/search-params.ts +0 -16
- package/src/segment-loader-promise.ts +14 -2
- package/src/segment-system.tsx +79 -88
- package/src/server/handle-store.ts +7 -24
- package/src/server/loader-registry.ts +5 -24
- package/src/server/request-context.ts +29 -92
- package/src/ssr/index.tsx +14 -14
- package/src/static-handler.ts +2 -27
- package/src/testing/cache-status.ts +44 -48
- package/src/testing/collect-handle.ts +1 -24
- package/src/testing/dispatch.ts +43 -6
- package/src/testing/e2e/index.ts +1 -22
- package/src/testing/e2e/matchers.ts +0 -16
- package/src/testing/flight-matchers.ts +0 -13
- package/src/testing/flight-normalize.ts +3 -30
- package/src/testing/flight.ts +46 -48
- package/src/testing/generated-routes.ts +1 -41
- package/src/testing/index.ts +1 -21
- package/src/testing/internal/context.ts +3 -45
- package/src/testing/internal/seed-vars.ts +0 -26
- package/src/testing/render-handler.ts +31 -61
- package/src/testing/render-route.tsx +75 -103
- package/src/testing/run-loader.ts +0 -96
- package/src/testing/run-middleware.ts +0 -26
- package/src/theme/ThemeProvider.tsx +0 -52
- package/src/theme/ThemeScript.tsx +0 -6
- package/src/theme/constants.ts +0 -12
- package/src/theme/index.ts +0 -7
- package/src/theme/theme-context.ts +1 -5
- package/src/theme/theme-script.ts +0 -14
- package/src/theme/use-theme.ts +0 -3
- package/src/types/boundaries.ts +0 -35
- package/src/types/error-types.ts +25 -89
- package/src/types/global-namespace.ts +4 -14
- package/src/types/handler-context.ts +28 -9
- package/src/types/index.ts +0 -10
- package/src/types/request-scope.ts +0 -19
- package/src/types/route-config.ts +6 -50
- package/src/types/route-entry.ts +0 -6
- package/src/types/segments.ts +0 -13
- package/src/urls/include-helper.ts +0 -4
- package/src/urls/index.ts +0 -6
- package/src/urls/path-helper-types.ts +2 -2
- package/src/urls/path-helper.ts +0 -54
- package/src/urls/urls-function.ts +0 -13
- package/src/use-loader.tsx +0 -186
- package/src/vite/discovery/bundle-postprocess.ts +2 -1
- package/src/vite/discovery/discover-routers.ts +28 -18
- package/src/vite/discovery/prerender-collection.ts +2 -4
- package/src/vite/discovery/state.ts +5 -0
- package/src/vite/discovery/virtual-module-codegen.ts +1 -11
- package/src/vite/plugin-types.ts +35 -9
- package/src/vite/plugins/cjs-to-esm.ts +0 -11
- package/src/vite/plugins/client-ref-dedup.ts +0 -11
- package/src/vite/plugins/client-ref-hashing.ts +0 -10
- package/src/vite/plugins/cloudflare-protocol-stub.ts +0 -20
- package/src/vite/plugins/expose-action-id.ts +2 -73
- package/src/vite/plugins/expose-id-utils.ts +0 -55
- package/src/vite/plugins/expose-ids/export-analysis.ts +0 -38
- package/src/vite/plugins/expose-ids/handler-transform.ts +0 -15
- package/src/vite/plugins/expose-ids/loader-transform.ts +0 -15
- package/src/vite/plugins/expose-ids/router-transform.ts +0 -13
- package/src/vite/plugins/expose-internal-ids.ts +10 -0
- package/src/vite/plugins/performance-tracks.ts +0 -3
- package/src/vite/plugins/refresh-cmd.ts +1 -1
- package/src/vite/plugins/use-cache-transform.ts +21 -46
- package/src/vite/plugins/version-injector.ts +0 -20
- package/src/vite/plugins/version-plugin.ts +1 -49
- package/src/vite/plugins/virtual-entries.ts +0 -15
- package/src/vite/rango.ts +2 -108
- package/src/vite/router-discovery.ts +9 -1
- package/src/vite/utils/ast-handler-extract.ts +0 -16
- package/src/vite/utils/bundle-analysis.ts +6 -13
- package/src/vite/utils/client-chunks.ts +0 -6
- package/src/vite/utils/forward-user-plugins.ts +0 -22
- package/src/vite/utils/manifest-utils.ts +0 -4
- package/src/vite/utils/package-resolution.ts +1 -73
- package/src/vite/utils/prerender-utils.ts +0 -35
- package/src/vite/utils/shared-utils.ts +3 -35
- package/src/browser/shallow.ts +0 -40
- package/src/handles/index.ts +0 -7
- package/src/router/middleware-cookies.ts +0 -55
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
2
|
import type { Handle } from "../handle.js";
|
|
3
|
+
import type { HandlePush } from "../defer.js";
|
|
3
4
|
import type { ContextVar } from "../context-var.js";
|
|
4
5
|
import type { MiddlewareFn } from "../router/middleware.js";
|
|
5
6
|
import type { Theme } from "../theme/types.js";
|
|
@@ -107,14 +108,6 @@ type StrictLocalParamsWithExtras<TEntry> =
|
|
|
107
108
|
? Record<string, string>
|
|
108
109
|
: ExtractParamsFromEntry<TEntry, {}> & Record<string, string>;
|
|
109
110
|
|
|
110
|
-
// HandlerContext.reverse is the only reverse surface with runtime param autofill
|
|
111
|
-
// from the current matched request. Middleware/loaders/request context do not
|
|
112
|
-
// have the same local-route guarantees, so they keep plain ScopedReverseFunction.
|
|
113
|
-
//
|
|
114
|
-
// When a handler has an explicit local route map, enforce that local route
|
|
115
|
-
// params declared by that map are present while still allowing extra mount
|
|
116
|
-
// params to be passed through. Global names remain autofill-friendly because
|
|
117
|
-
// parent include() params are often unknown at the module definition site.
|
|
118
111
|
type StrictLocalAutofillGlobalReverseFunction<TLocalRoutes, TGlobalRoutes> =
|
|
119
112
|
ScopedReverseFunction<TLocalRoutes, TGlobalRoutes> & {
|
|
120
113
|
<TName extends keyof TGlobalRoutes & string>(
|
|
@@ -282,6 +275,8 @@ export type HandlerContext<
|
|
|
282
275
|
* For handles: Returns a push function to add data for this segment.
|
|
283
276
|
* Handle data accumulates across all matched route segments.
|
|
284
277
|
* Push accepts: direct value, Promise, or async callback (executed immediately).
|
|
278
|
+
* Or call `.defer()` to reserve the slot now and resolve it later (e.g. from a
|
|
279
|
+
* deep async component), with a timeout safety net — see {@link HandlePush}.
|
|
285
280
|
*
|
|
286
281
|
* @example
|
|
287
282
|
* ```typescript
|
|
@@ -315,6 +310,13 @@ export type HandlerContext<
|
|
|
315
310
|
* });
|
|
316
311
|
* return <ProductPage />;
|
|
317
312
|
* });
|
|
313
|
+
*
|
|
314
|
+
* // Handle usage - deferred (reserve the slot now, resolve from a deep component)
|
|
315
|
+
* route("product", (ctx) => {
|
|
316
|
+
* const resolve = ctx.use(Breadcrumbs).defer({ timeoutMs: 5000, else: null });
|
|
317
|
+
* loadCrumb(ctx.params.id).then(resolve); // resolver is push-equal
|
|
318
|
+
* return <ProductPage />; // auto-resolves to `else` on timeout
|
|
319
|
+
* });
|
|
318
320
|
* ```
|
|
319
321
|
*/
|
|
320
322
|
use: {
|
|
@@ -323,7 +325,7 @@ export type HandlerContext<
|
|
|
323
325
|
): Promise<T>;
|
|
324
326
|
<TData, TAccumulated = TData[]>(
|
|
325
327
|
handle: Handle<TData, TAccumulated>,
|
|
326
|
-
):
|
|
328
|
+
): HandlePush<TData>;
|
|
327
329
|
};
|
|
328
330
|
/**
|
|
329
331
|
* Current theme (from cookie or default).
|
|
@@ -430,6 +432,18 @@ export type InternalHandlerContext<
|
|
|
430
432
|
_responseType?: string;
|
|
431
433
|
/** Route name for cache key scoping (prevents cross-route collisions). */
|
|
432
434
|
_routeName?: string;
|
|
435
|
+
/**
|
|
436
|
+
* @internal Loader-cache override table: loaderId -> memoized data promise.
|
|
437
|
+
* A single stable ctx.use interceptor consults this instead of chaining one
|
|
438
|
+
* wrapper per cached loader (avoids O(N) dispatch). See loader-cache.ts.
|
|
439
|
+
*/
|
|
440
|
+
_loaderCacheOverrides?: Map<string, Promise<any>>;
|
|
441
|
+
/**
|
|
442
|
+
* @internal ctx.use captured before the loader-cache interceptor was installed.
|
|
443
|
+
* The cache-miss execute runs the loader through this, bypassing the override
|
|
444
|
+
* table (so a loader cannot await its own in-flight memoized promise).
|
|
445
|
+
*/
|
|
446
|
+
_loaderCacheOriginalUse?: (item: any) => any;
|
|
433
447
|
};
|
|
434
448
|
|
|
435
449
|
/**
|
|
@@ -621,6 +635,10 @@ export type ShouldRevalidateFn<TParams = GenericParams, TEnv = any> = (args: {
|
|
|
621
635
|
* namespace import (`import * as CartActions`), any export of that module —
|
|
622
636
|
* and `false` otherwise (including plain navigation with no action).
|
|
623
637
|
*
|
|
638
|
+
* Called with NO arguments it answers "is this request an action at all?":
|
|
639
|
+
* `true` for any action, `false` on plain navigation. Use the bare form when
|
|
640
|
+
* you want to revalidate on every action regardless of which one fired.
|
|
641
|
+
*
|
|
624
642
|
* Prefer this over hand-written `actionId` substring matches: it resolves the
|
|
625
643
|
* action's stable `path#export` id from the imported reference, so a rename is
|
|
626
644
|
* a type error in one place instead of silent drift across consumers. It
|
|
@@ -635,6 +653,7 @@ export type ShouldRevalidateFn<TParams = GenericParams, TEnv = any> = (args: {
|
|
|
635
653
|
* import { addToCart, removeFromCart } from "./actions/cart";
|
|
636
654
|
* import * as CartActions from "./actions/cart";
|
|
637
655
|
*
|
|
656
|
+
* revalidate((ctx) => ctx.isAction() || undefined); // any action
|
|
638
657
|
* revalidate((ctx) => ctx.isAction(addToCart) || undefined); // one action
|
|
639
658
|
* revalidate((ctx) => ctx.isAction(addToCart, removeFromCart) || undefined); // several
|
|
640
659
|
* revalidate((ctx) => ctx.isAction(CartActions) || undefined); // any in the module
|
package/src/types/index.ts
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
|
-
// Global namespace (must be imported for side effects: `declare global`)
|
|
2
1
|
export type {
|
|
3
2
|
GetRegisteredRoutes,
|
|
4
3
|
DefaultHandlerRouteMap,
|
|
5
4
|
DefaultReverseRouteMap,
|
|
6
5
|
DefaultEnv,
|
|
7
6
|
} from "./global-namespace.js";
|
|
8
|
-
// Ensure the global namespace declaration is evaluated
|
|
9
7
|
import "./global-namespace.js";
|
|
10
8
|
|
|
11
|
-
// Route configuration
|
|
12
9
|
export type {
|
|
13
10
|
DocumentProps,
|
|
14
11
|
ExtractParams,
|
|
@@ -19,7 +16,6 @@ export type {
|
|
|
19
16
|
ResolvedRouteMap,
|
|
20
17
|
} from "./route-config.js";
|
|
21
18
|
|
|
22
|
-
// Boundaries (error/notFound)
|
|
23
19
|
export type {
|
|
24
20
|
ErrorInfo,
|
|
25
21
|
ErrorBoundaryFallbackProps,
|
|
@@ -32,7 +28,6 @@ export type {
|
|
|
32
28
|
} from "./boundaries.js";
|
|
33
29
|
export { isLoaderDataResult } from "./boundaries.js";
|
|
34
30
|
|
|
35
|
-
// Handler context and related types
|
|
36
31
|
export type {
|
|
37
32
|
MiddlewareFn,
|
|
38
33
|
ScopedRouteMap,
|
|
@@ -50,7 +45,6 @@ export type {
|
|
|
50
45
|
Middleware,
|
|
51
46
|
} from "./handler-context.js";
|
|
52
47
|
|
|
53
|
-
// Segments
|
|
54
48
|
export type {
|
|
55
49
|
ViewTransitionClass,
|
|
56
50
|
TransitionConfig,
|
|
@@ -61,10 +55,8 @@ export type {
|
|
|
61
55
|
MatchResult,
|
|
62
56
|
} from "./segments.js";
|
|
63
57
|
|
|
64
|
-
// Route entries
|
|
65
58
|
export type { LazyIncludeContext, RouteEntry } from "./route-entry.js";
|
|
66
59
|
|
|
67
|
-
// Loader types
|
|
68
60
|
export type {
|
|
69
61
|
LoaderContext,
|
|
70
62
|
LoaderFn,
|
|
@@ -73,7 +65,6 @@ export type {
|
|
|
73
65
|
LoaderDefinition,
|
|
74
66
|
} from "./loader-types.js";
|
|
75
67
|
|
|
76
|
-
// Cache types
|
|
77
68
|
export type {
|
|
78
69
|
CacheContext,
|
|
79
70
|
CacheOptions,
|
|
@@ -81,7 +72,6 @@ export type {
|
|
|
81
72
|
EntryCacheConfig,
|
|
82
73
|
} from "./cache-types.js";
|
|
83
74
|
|
|
84
|
-
// Error handling types
|
|
85
75
|
export type {
|
|
86
76
|
ErrorPhase,
|
|
87
77
|
OnErrorContext,
|
|
@@ -1,22 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* RequestScope: the fields every user-facing context shares.
|
|
3
|
-
*
|
|
4
|
-
* A handler, middleware, loader, response handler, and the ALS-bound
|
|
5
|
-
* RequestContext are all different phases of the same request, and they
|
|
6
|
-
* all carry the same set of request-scoped capabilities: the raw Request,
|
|
7
|
-
* the parsed URL pair (`url` is cleaned of internal `_rsc*` params,
|
|
8
|
-
* `originalUrl` retains them), pathname/searchParams, platform bindings
|
|
9
|
-
* (`env`), and two escape hatches for work that outlives the response
|
|
10
|
-
* (`waitUntil`) or needs the raw Cloudflare runtime object
|
|
11
|
-
* (`executionContext`).
|
|
12
|
-
*
|
|
13
|
-
* Each public context type intersects `RequestScope<TEnv>` with its own
|
|
14
|
-
* phase-specific fields (e.g. `params`/`reverse` on HandlerContext,
|
|
15
|
-
* `headers`/`header()` on MiddlewareContext). That keeps platform surface
|
|
16
|
-
* in one place and lets the next runtime escape hatch we need land in
|
|
17
|
-
* one file instead of four.
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
1
|
import type { DefaultEnv } from "./global-namespace.js";
|
|
21
2
|
|
|
22
3
|
/**
|
|
@@ -7,47 +7,24 @@ export type DocumentProps = {
|
|
|
7
7
|
children: ReactNode;
|
|
8
8
|
};
|
|
9
9
|
|
|
10
|
-
/**
|
|
11
|
-
* Parse constraint values into a union type
|
|
12
|
-
* "a|b|c" -> "a" | "b" | "c"
|
|
13
|
-
*/
|
|
14
10
|
type ParseConstraint<T extends string> =
|
|
15
11
|
T extends `${infer First}|${infer Rest}` ? First | ParseConstraint<Rest> : T;
|
|
16
12
|
|
|
17
|
-
/**
|
|
18
|
-
* Extract param info from a param segment
|
|
19
|
-
*
|
|
20
|
-
* Handles:
|
|
21
|
-
* - :param -> { name: "param", optional: false, type: string }
|
|
22
|
-
* - :param? -> { name: "param", optional: true, type: string }
|
|
23
|
-
* - :param(a|b) -> { name: "param", optional: false, type: "a" | "b" }
|
|
24
|
-
* - :param(a|b)? -> { name: "param", optional: true, type: "a" | "b" }
|
|
25
|
-
*/
|
|
26
13
|
type ExtractParamInfo<T extends string> =
|
|
27
|
-
// Optional + constrained (with optional suffix): :param(a|b)?suffix
|
|
28
14
|
T extends `${infer Name}(${infer Constraint})?${string}`
|
|
29
15
|
? { name: Name; optional: true; type: ParseConstraint<Constraint> }
|
|
30
|
-
:
|
|
31
|
-
T extends `${infer Name}(${infer Constraint})${string}`
|
|
16
|
+
: T extends `${infer Name}(${infer Constraint})${string}`
|
|
32
17
|
? { name: Name; optional: false; type: ParseConstraint<Constraint> }
|
|
33
|
-
:
|
|
34
|
-
T extends `${infer Name}?${string}`
|
|
18
|
+
: T extends `${infer Name}?${string}`
|
|
35
19
|
? { name: Name; optional: true; type: string }
|
|
36
|
-
:
|
|
37
|
-
T extends `${infer Name}.${string}`
|
|
20
|
+
: T extends `${infer Name}.${string}`
|
|
38
21
|
? { name: Name; optional: false; type: string }
|
|
39
|
-
:
|
|
40
|
-
T extends `${infer Name}-${string}`
|
|
22
|
+
: T extends `${infer Name}-${string}`
|
|
41
23
|
? { name: Name; optional: false; type: string }
|
|
42
|
-
:
|
|
43
|
-
T extends `${infer Name}~${string}`
|
|
24
|
+
: T extends `${infer Name}~${string}`
|
|
44
25
|
? { name: Name; optional: false; type: string }
|
|
45
|
-
:
|
|
46
|
-
{ name: T; optional: false; type: string };
|
|
26
|
+
: { name: T; optional: false; type: string };
|
|
47
27
|
|
|
48
|
-
/**
|
|
49
|
-
* Build param object from info
|
|
50
|
-
*/
|
|
51
28
|
type ParamFromInfo<Info> = Info extends {
|
|
52
29
|
name: infer N extends string;
|
|
53
30
|
optional: true;
|
|
@@ -62,10 +39,6 @@ type ParamFromInfo<Info> = Info extends {
|
|
|
62
39
|
? { [K in N]: V }
|
|
63
40
|
: never;
|
|
64
41
|
|
|
65
|
-
/**
|
|
66
|
-
* Merge two param objects preserving optionality
|
|
67
|
-
* Uses Pick to preserve the modifiers from source types
|
|
68
|
-
*/
|
|
69
42
|
type MergeParams<A, B> = Pick<A, keyof A> & Pick<B, keyof B> extends infer O
|
|
70
43
|
? { [K in keyof O]: O[K] }
|
|
71
44
|
: never;
|
|
@@ -109,17 +82,11 @@ export type ExtractParams<
|
|
|
109
82
|
*/
|
|
110
83
|
export type TrailingSlashMode = "never" | "always" | "ignore";
|
|
111
84
|
|
|
112
|
-
/**
|
|
113
|
-
* Route configuration object (alternative to string path)
|
|
114
|
-
*/
|
|
115
85
|
export type RouteConfig = {
|
|
116
86
|
path: string;
|
|
117
87
|
trailingSlash?: TrailingSlashMode;
|
|
118
88
|
};
|
|
119
89
|
|
|
120
|
-
/**
|
|
121
|
-
* Route definition options (global defaults)
|
|
122
|
-
*/
|
|
123
90
|
export type RouteDefinitionOptions = {
|
|
124
91
|
trailingSlash?: TrailingSlashMode;
|
|
125
92
|
};
|
|
@@ -128,11 +95,6 @@ export type RouteDefinition = {
|
|
|
128
95
|
[key: string]: string | RouteConfig | RouteDefinition;
|
|
129
96
|
};
|
|
130
97
|
|
|
131
|
-
/**
|
|
132
|
-
* Recursively flatten nested routes with depth limit to prevent infinite recursion
|
|
133
|
-
* Transforms: { products: { detail: "/product/:slug" } } => { "products.detail": "/product/:slug" }
|
|
134
|
-
* Also handles RouteConfig objects: { api: { path: "/api" } } => { "api": "/api" }
|
|
135
|
-
*/
|
|
136
98
|
type FlattenRoutes<
|
|
137
99
|
T extends RouteDefinition,
|
|
138
100
|
Prefix extends string = "",
|
|
@@ -153,18 +115,12 @@ type FlattenRoutes<
|
|
|
153
115
|
: never;
|
|
154
116
|
}[keyof T];
|
|
155
117
|
|
|
156
|
-
/**
|
|
157
|
-
* Union to intersection helper
|
|
158
|
-
*/
|
|
159
118
|
type UnionToIntersection<U> = (
|
|
160
119
|
U extends unknown ? (k: U) => void : never
|
|
161
120
|
) extends (k: infer I) => void
|
|
162
121
|
? I
|
|
163
122
|
: never;
|
|
164
123
|
|
|
165
|
-
/**
|
|
166
|
-
* Resolved route map - flattened route definitions with full paths
|
|
167
|
-
*/
|
|
168
124
|
export type ResolvedRouteMap<T extends RouteDefinition> = UnionToIntersection<
|
|
169
125
|
FlattenRoutes<T>
|
|
170
126
|
>;
|
package/src/types/route-entry.ts
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import type { AllUseItems } from "../route-types.js";
|
|
2
2
|
import type { TrailingSlashMode, ResolvedRouteMap } from "./route-config.js";
|
|
3
3
|
|
|
4
|
-
/**
|
|
5
|
-
* Context captured for lazy include evaluation
|
|
6
|
-
*/
|
|
7
4
|
export interface LazyIncludeContext {
|
|
8
5
|
urlPrefix: string;
|
|
9
6
|
namePrefix: string | undefined;
|
|
@@ -25,9 +22,6 @@ export interface LazyIncludeContext {
|
|
|
25
22
|
includeScope?: string;
|
|
26
23
|
}
|
|
27
24
|
|
|
28
|
-
/**
|
|
29
|
-
* Internal route entry stored in router
|
|
30
|
-
*/
|
|
31
25
|
export interface RouteEntry<TEnv = any> {
|
|
32
26
|
prefix: string;
|
|
33
27
|
/**
|
package/src/types/segments.ts
CHANGED
|
@@ -41,14 +41,6 @@ export interface TransitionConfig {
|
|
|
41
41
|
/**
|
|
42
42
|
* Resolved segment with component
|
|
43
43
|
*
|
|
44
|
-
* Segment types:
|
|
45
|
-
* - layout: Wraps child content via <Outlet />
|
|
46
|
-
* - route: The leaf content for a URL
|
|
47
|
-
* - parallel: Named slots rendered via <ParallelOutlet name="@slot" />
|
|
48
|
-
* - loader: Data segment (no visual rendering, carries loaderData)
|
|
49
|
-
* - error: Error fallback segment (replaces failed segment with error UI)
|
|
50
|
-
* - notFound: Not found fallback segment (replaces segment when data not found)
|
|
51
|
-
*
|
|
52
44
|
* @internal This type is an implementation detail and may change without notice.
|
|
53
45
|
*/
|
|
54
46
|
export interface ResolvedSegment {
|
|
@@ -89,11 +81,6 @@ export interface ResolvedSegment {
|
|
|
89
81
|
_handlerRan?: boolean;
|
|
90
82
|
}
|
|
91
83
|
|
|
92
|
-
/**
|
|
93
|
-
* Segment metadata (without component)
|
|
94
|
-
*
|
|
95
|
-
* @internal This type is an implementation detail and may change without notice.
|
|
96
|
-
*/
|
|
97
84
|
export interface SegmentMetadata {
|
|
98
85
|
id: string;
|
|
99
86
|
type: "layout" | "route" | "parallel" | "loader" | "error" | "notFound";
|
|
@@ -37,10 +37,8 @@ export function processItems(items: readonly AllUseItems[]): AllUseItems[] {
|
|
|
37
37
|
if (!item) continue;
|
|
38
38
|
|
|
39
39
|
if (item.type === "include") {
|
|
40
|
-
// All includes are lazy; the router expands them on first matching request.
|
|
41
40
|
result.push(item);
|
|
42
41
|
} else if (item.type === "layout" && (item as any).uses) {
|
|
43
|
-
// Process nested items in layout
|
|
44
42
|
const layoutItem = item as any;
|
|
45
43
|
layoutItem.uses = processItems(layoutItem.uses);
|
|
46
44
|
result.push(layoutItem);
|
|
@@ -141,8 +139,6 @@ export function createIncludeHelper<TEnv>(): IncludeFn<TEnv> {
|
|
|
141
139
|
? (parentRootScoped ?? false)
|
|
142
140
|
: parentRootScoped;
|
|
143
141
|
|
|
144
|
-
// All includes are lazy - patterns are evaluated on first matching request
|
|
145
|
-
// This improves cold start time significantly for large route sets
|
|
146
142
|
return {
|
|
147
143
|
type: "include",
|
|
148
144
|
name,
|
package/src/urls/index.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// Response types and symbols
|
|
2
1
|
export {
|
|
3
2
|
RESPONSE_TYPE,
|
|
4
3
|
type ResponseHandler,
|
|
@@ -8,7 +7,6 @@ export {
|
|
|
8
7
|
type ResponseHandlerContext,
|
|
9
8
|
} from "./response-types.js";
|
|
10
9
|
|
|
11
|
-
// Pattern types
|
|
12
10
|
export type {
|
|
13
11
|
UnnamedRoute,
|
|
14
12
|
LocalOnlyInclude,
|
|
@@ -17,7 +15,6 @@ export type {
|
|
|
17
15
|
IncludeOptions,
|
|
18
16
|
} from "./pattern-types.js";
|
|
19
17
|
|
|
20
|
-
// Type extraction utilities
|
|
21
18
|
export type {
|
|
22
19
|
ExtractRoutes,
|
|
23
20
|
ExtractResponses,
|
|
@@ -25,7 +22,6 @@ export type {
|
|
|
25
22
|
RouteResponse,
|
|
26
23
|
} from "./type-extraction.js";
|
|
27
24
|
|
|
28
|
-
// Path helper types
|
|
29
25
|
export type {
|
|
30
26
|
PathFn,
|
|
31
27
|
ResponsePathFn,
|
|
@@ -35,10 +31,8 @@ export type {
|
|
|
35
31
|
PathHelpers,
|
|
36
32
|
} from "./path-helper-types.js";
|
|
37
33
|
|
|
38
|
-
// Main entry point
|
|
39
34
|
export { urls } from "./urls-function.js";
|
|
40
35
|
|
|
41
|
-
// Re-exports from route-types
|
|
42
36
|
export type {
|
|
43
37
|
AllUseItems,
|
|
44
38
|
IncludeItem,
|
|
@@ -348,9 +348,9 @@ export type PathHelpers<TEnv> = {
|
|
|
348
348
|
<const TChildren extends readonly (AllUseItems | readonly AllUseItems[])[]>(
|
|
349
349
|
children: () => TChildren,
|
|
350
350
|
): TypedCacheItem<ExtractRoutes<TChildren>, ExtractResponses<TChildren>>;
|
|
351
|
-
(options: PartialCacheOptions | false): TypedCacheItem<{}, {}>;
|
|
351
|
+
(options: PartialCacheOptions<TEnv> | false): TypedCacheItem<{}, {}>;
|
|
352
352
|
<const TChildren extends readonly (AllUseItems | readonly AllUseItems[])[]>(
|
|
353
|
-
options: PartialCacheOptions | false,
|
|
353
|
+
options: PartialCacheOptions<TEnv> | false,
|
|
354
354
|
use: () => TChildren,
|
|
355
355
|
): TypedCacheItem<ExtractRoutes<TChildren>, ExtractResponses<TChildren>>;
|
|
356
356
|
};
|
package/src/urls/path-helper.ts
CHANGED
|
@@ -39,10 +39,6 @@ import {
|
|
|
39
39
|
runAndValidateUseItems,
|
|
40
40
|
} from "../route-definition/dsl-helpers.js";
|
|
41
41
|
|
|
42
|
-
/**
|
|
43
|
-
* Apply URL prefix to a pattern
|
|
44
|
-
* Handles edge cases like "/" patterns and double slashes
|
|
45
|
-
*/
|
|
46
42
|
function applyUrlPrefix(prefix: string, pattern: string): string {
|
|
47
43
|
if (!prefix) return pattern;
|
|
48
44
|
if (pattern === "/") return prefix;
|
|
@@ -52,29 +48,17 @@ function applyUrlPrefix(prefix: string, pattern: string): string {
|
|
|
52
48
|
return prefix + pattern;
|
|
53
49
|
}
|
|
54
50
|
|
|
55
|
-
/**
|
|
56
|
-
* Apply name prefix to a route name
|
|
57
|
-
*/
|
|
58
51
|
function applyNamePrefix(prefix: string | undefined, name: string): string {
|
|
59
52
|
if (!prefix) return name;
|
|
60
53
|
return `${prefix}.${name}`;
|
|
61
54
|
}
|
|
62
55
|
|
|
63
|
-
/**
|
|
64
|
-
* Resolve response type from path options (set by path.json(), path.text(), etc.)
|
|
65
|
-
*/
|
|
66
56
|
function resolveResponseType(
|
|
67
57
|
options: PathOptions | undefined,
|
|
68
58
|
): string | undefined {
|
|
69
59
|
return options?.[RESPONSE_TYPE];
|
|
70
60
|
}
|
|
71
61
|
|
|
72
|
-
/**
|
|
73
|
-
* Create path() helper
|
|
74
|
-
*
|
|
75
|
-
* The path() function is the key new feature - it combines URL pattern
|
|
76
|
-
* with handler at the definition site.
|
|
77
|
-
*/
|
|
78
62
|
export function createPathHelper<TEnv>(): PathFn<TEnv> {
|
|
79
63
|
return ((
|
|
80
64
|
pattern: string,
|
|
@@ -91,8 +75,6 @@ export function createPathHelper<TEnv>(): PathFn<TEnv> {
|
|
|
91
75
|
"path() cannot be used inside parallel()",
|
|
92
76
|
);
|
|
93
77
|
|
|
94
|
-
// Walk the parent chain to prevent path() nested under another path(),
|
|
95
|
-
// even when separated by intermediate layouts (e.g. path(layout(path())))
|
|
96
78
|
{
|
|
97
79
|
let ancestor = ctx.parent;
|
|
98
80
|
while (ancestor) {
|
|
@@ -104,7 +86,6 @@ export function createPathHelper<TEnv>(): PathFn<TEnv> {
|
|
|
104
86
|
}
|
|
105
87
|
}
|
|
106
88
|
|
|
107
|
-
// Determine options and use based on argument types
|
|
108
89
|
let options: PathOptions | undefined;
|
|
109
90
|
let use: (() => UseItems<RouteUseItem>) | undefined;
|
|
110
91
|
|
|
@@ -117,49 +98,29 @@ export function createPathHelper<TEnv>(): PathFn<TEnv> {
|
|
|
117
98
|
use = maybeUse;
|
|
118
99
|
}
|
|
119
100
|
|
|
120
|
-
// Merge handler.use() defaults with explicit use()
|
|
121
|
-
// Response routes (path.json, path.text, etc.) only allow middleware + cache
|
|
122
101
|
const handlerUseFn = resolveHandlerUse(handler);
|
|
123
102
|
const mountSite = resolveResponseType(options) ? "response" : "path";
|
|
124
103
|
const mergedUse = mergeHandlerUse(handlerUseFn, use, mountSite);
|
|
125
104
|
|
|
126
|
-
// Get prefixes from context (set by include())
|
|
127
105
|
const urlPrefix = getUrlPrefix();
|
|
128
106
|
const namePrefix = getNamePrefix();
|
|
129
107
|
|
|
130
|
-
// Apply URL prefix to pattern
|
|
131
108
|
const prefixedPattern = applyUrlPrefix(urlPrefix, pattern);
|
|
132
109
|
|
|
133
|
-
// Generate route name - use provided name or generate from pattern
|
|
134
110
|
const localName =
|
|
135
111
|
options?.name || `$path_${pattern.replace(/[/:*?]/g, "_")}`;
|
|
136
112
|
if (options?.name) {
|
|
137
113
|
validateUserRouteName(options.name);
|
|
138
114
|
}
|
|
139
|
-
// Apply name prefix if set (from include())
|
|
140
115
|
const routeName = applyNamePrefix(namePrefix, localName);
|
|
141
116
|
|
|
142
117
|
const namespace = `${ctx.namespace}.${store.getNextIndex("route")}.${routeName}`;
|
|
143
118
|
|
|
144
|
-
// Per-request pruning: skip registration for routes that won't be rendered.
|
|
145
|
-
// forRoute is set by loadManifest() to the matched route name. During
|
|
146
|
-
// evaluateLazyEntry() (route matching), forRoute is unset so all routes
|
|
147
|
-
// register normally. We still increment counters to keep shortCodes stable
|
|
148
|
-
// across different routes (needed for segment reconciliation on navigation).
|
|
149
|
-
//
|
|
150
|
-
// include() does not need its own forRoute pruning. include() creates lazy
|
|
151
|
-
// entries that defer handler execution until route matching. When the lazy
|
|
152
|
-
// handler eventually runs inside loadManifest(), this path() check already
|
|
153
|
-
// covers all routes defined inside the include.
|
|
154
119
|
if (ctx.forRoute && routeName !== ctx.forRoute) {
|
|
155
120
|
store.getShortCode("route");
|
|
156
121
|
return { type: "route" } as RouteItem;
|
|
157
122
|
}
|
|
158
123
|
|
|
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".
|
|
163
124
|
const wrappedHandler: Handler<any, any, TEnv> =
|
|
164
125
|
typeof handler === "function"
|
|
165
126
|
? (handler as Handler<any, any, TEnv>)
|
|
@@ -190,7 +151,6 @@ export function createPathHelper<TEnv>(): PathFn<TEnv> {
|
|
|
190
151
|
type: "route" as const,
|
|
191
152
|
parent: ctx.parent,
|
|
192
153
|
handler: wrappedHandler,
|
|
193
|
-
// Store the PREFIXED pattern for route matching
|
|
194
154
|
pattern: prefixedPattern,
|
|
195
155
|
...(urlPrefix ? { mountPath: urlPrefix } : {}),
|
|
196
156
|
...(isPassthroughHandler(handler)
|
|
@@ -217,29 +177,23 @@ export function createPathHelper<TEnv>(): PathFn<TEnv> {
|
|
|
217
177
|
: {}),
|
|
218
178
|
};
|
|
219
179
|
|
|
220
|
-
// Capture namespace prefix on static handler for build-time reverse() resolution
|
|
221
180
|
if (isStaticHandler(handler) && handler.$$id && ctx.namePrefix) {
|
|
222
181
|
(handler as any).$$routePrefix = ctx.namePrefix;
|
|
223
182
|
}
|
|
224
183
|
|
|
225
|
-
// Check for duplicate route names (TypeScript should catch this, but runtime check too)
|
|
226
184
|
invariant(
|
|
227
185
|
ctx.manifest.get(routeName) === undefined,
|
|
228
186
|
`Duplicate route name: ${routeName} at ${namespace}`,
|
|
229
187
|
);
|
|
230
188
|
|
|
231
|
-
// Register route entry with prefixed name
|
|
232
189
|
ctx.manifest.set(routeName, entry);
|
|
233
190
|
|
|
234
|
-
// Register root-scope flag for dot-local reverse resolution
|
|
235
191
|
registerRouteRootScope(routeName, getRootScoped());
|
|
236
192
|
|
|
237
|
-
// Also store pattern in a separate map for URL generation
|
|
238
193
|
if (ctx.patterns) {
|
|
239
194
|
ctx.patterns.set(routeName, prefixedPattern);
|
|
240
195
|
}
|
|
241
196
|
|
|
242
|
-
// Store pattern grouped by URL prefix for separate entry creation
|
|
243
197
|
if (ctx.patternsByPrefix) {
|
|
244
198
|
const urlPrefix = getUrlPrefix() || "";
|
|
245
199
|
if (!ctx.patternsByPrefix.has(urlPrefix)) {
|
|
@@ -248,12 +202,10 @@ export function createPathHelper<TEnv>(): PathFn<TEnv> {
|
|
|
248
202
|
ctx.patternsByPrefix.get(urlPrefix)!.set(routeName, prefixedPattern);
|
|
249
203
|
}
|
|
250
204
|
|
|
251
|
-
// Store trailing slash config if specified
|
|
252
205
|
if (options?.trailingSlash && ctx.trailingSlash) {
|
|
253
206
|
ctx.trailingSlash.set(routeName, options.trailingSlash);
|
|
254
207
|
}
|
|
255
208
|
|
|
256
|
-
// Store search schema if specified
|
|
257
209
|
if (options?.search) {
|
|
258
210
|
if (ctx.searchSchemas) {
|
|
259
211
|
ctx.searchSchemas.set(routeName, options.search);
|
|
@@ -261,7 +213,6 @@ export function createPathHelper<TEnv>(): PathFn<TEnv> {
|
|
|
261
213
|
registerSearchSchema(routeName, options.search);
|
|
262
214
|
}
|
|
263
215
|
|
|
264
|
-
// Run merged use callback (handler.use defaults + explicit use) if present
|
|
265
216
|
if (mergedUse) {
|
|
266
217
|
const result = runAndValidateUseItems(
|
|
267
218
|
store,
|
|
@@ -278,10 +229,6 @@ export function createPathHelper<TEnv>(): PathFn<TEnv> {
|
|
|
278
229
|
}) as PathFn<TEnv>;
|
|
279
230
|
}
|
|
280
231
|
|
|
281
|
-
/**
|
|
282
|
-
* Attach response type tag methods (.json, .text, .html, .xml, .md, .image, .stream, .any) to a path helper.
|
|
283
|
-
* Each tag wraps the original path() call with the RESPONSE_TYPE option set.
|
|
284
|
-
*/
|
|
285
232
|
export function attachPathResponseTags<TEnv>(
|
|
286
233
|
pathFn: PathFn<TEnv>,
|
|
287
234
|
): PathFn<TEnv> & {
|
|
@@ -303,7 +250,6 @@ export function attachPathResponseTags<TEnv>(
|
|
|
303
250
|
) => {
|
|
304
251
|
let options: PathOptions;
|
|
305
252
|
let use: (() => any[]) | undefined;
|
|
306
|
-
|
|
307
253
|
if (typeof optionsOrUse === "function") {
|
|
308
254
|
options = { [RESPONSE_TYPE]: responseType };
|
|
309
255
|
use = optionsOrUse;
|
|
@@ -34,24 +34,18 @@ export function urls<
|
|
|
34
34
|
>(
|
|
35
35
|
builder: (helpers: PathHelpers<TEnv>) => TItems,
|
|
36
36
|
): UrlPatterns<TEnv, ExtractRoutes<TItems>, ExtractResponses<TItems>> {
|
|
37
|
-
// Create the handler function that will be called by the router
|
|
38
37
|
const handler = () => {
|
|
39
38
|
invariant(
|
|
40
39
|
typeof builder === "function",
|
|
41
40
|
"urls() expects a builder function as its argument",
|
|
42
41
|
);
|
|
43
42
|
|
|
44
|
-
// Get base helpers from the existing route-definition module
|
|
45
43
|
const baseHelpers = createRouteHelpers<any, TEnv>();
|
|
46
44
|
|
|
47
|
-
// Create the path helper (with .json, .text, .html, .xml, .image, .stream, .any tags)
|
|
48
45
|
const pathHelper = attachPathResponseTags(createPathHelper<TEnv>());
|
|
49
46
|
|
|
50
|
-
// Create the include helper
|
|
51
47
|
const includeHelper = createIncludeHelper<TEnv>();
|
|
52
48
|
|
|
53
|
-
// Combine all helpers
|
|
54
|
-
// Note: layout and cache are cast to their typed versions - phantom types don't affect runtime
|
|
55
49
|
const helpers: PathHelpers<TEnv> = {
|
|
56
50
|
path: pathHelper as any,
|
|
57
51
|
include: includeHelper as any,
|
|
@@ -69,20 +63,13 @@ export function urls<
|
|
|
69
63
|
transition: baseHelpers.transition as PathHelpers<TEnv>["transition"],
|
|
70
64
|
};
|
|
71
65
|
|
|
72
|
-
// Execute builder directly - manifest.ts handles RootLayout wrapping
|
|
73
|
-
// for inline handlers (non-Promise results).
|
|
74
|
-
// For nested include() calls, routes inherit the outer RootLayout.
|
|
75
66
|
const builderResult = builder(helpers).flat(3) as AllUseItems[];
|
|
76
67
|
return processItems(builderResult);
|
|
77
68
|
};
|
|
78
69
|
|
|
79
|
-
// trailingSlash config is populated when handler() runs
|
|
80
|
-
// We expose it via a getter that reads from the context after handler execution
|
|
81
70
|
return {
|
|
82
71
|
handler,
|
|
83
72
|
get trailingSlash() {
|
|
84
|
-
// Get the trailingSlash map from the current context
|
|
85
|
-
// This will be populated after handler() is called
|
|
86
73
|
const store = getContext();
|
|
87
74
|
const ctx = store.context.getStore();
|
|
88
75
|
if (!ctx?.trailingSlash) {
|