@rangojs/router 0.0.0-experimental.0f44aca1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +5 -0
- package/README.md +899 -0
- package/dist/bin/rango.js +1601 -0
- package/dist/vite/index.js +5214 -0
- package/package.json +176 -0
- package/skills/breadcrumbs/SKILL.md +250 -0
- package/skills/cache-guide/SKILL.md +262 -0
- package/skills/caching/SKILL.md +220 -0
- package/skills/composability/SKILL.md +172 -0
- package/skills/debug-manifest/SKILL.md +112 -0
- package/skills/document-cache/SKILL.md +182 -0
- package/skills/fonts/SKILL.md +167 -0
- package/skills/hooks/SKILL.md +704 -0
- package/skills/host-router/SKILL.md +218 -0
- package/skills/intercept/SKILL.md +313 -0
- package/skills/layout/SKILL.md +310 -0
- package/skills/links/SKILL.md +239 -0
- package/skills/loader/SKILL.md +596 -0
- package/skills/middleware/SKILL.md +339 -0
- package/skills/mime-routes/SKILL.md +128 -0
- package/skills/parallel/SKILL.md +305 -0
- package/skills/prerender/SKILL.md +643 -0
- package/skills/rango/SKILL.md +118 -0
- package/skills/response-routes/SKILL.md +411 -0
- package/skills/route/SKILL.md +385 -0
- package/skills/router-setup/SKILL.md +439 -0
- package/skills/tailwind/SKILL.md +129 -0
- package/skills/theme/SKILL.md +79 -0
- package/skills/typesafety/SKILL.md +623 -0
- package/skills/use-cache/SKILL.md +324 -0
- package/src/__internal.ts +273 -0
- package/src/bin/rango.ts +321 -0
- package/src/browser/action-coordinator.ts +97 -0
- package/src/browser/action-response-classifier.ts +99 -0
- package/src/browser/event-controller.ts +899 -0
- package/src/browser/history-state.ts +80 -0
- package/src/browser/index.ts +18 -0
- package/src/browser/intercept-utils.ts +52 -0
- package/src/browser/link-interceptor.ts +141 -0
- package/src/browser/logging.ts +55 -0
- package/src/browser/merge-segment-loaders.ts +134 -0
- package/src/browser/navigation-bridge.ts +645 -0
- package/src/browser/navigation-client.ts +215 -0
- package/src/browser/navigation-store.ts +806 -0
- package/src/browser/navigation-transaction.ts +295 -0
- package/src/browser/network-error-handler.ts +61 -0
- package/src/browser/partial-update.ts +550 -0
- package/src/browser/prefetch/cache.ts +146 -0
- package/src/browser/prefetch/fetch.ts +135 -0
- package/src/browser/prefetch/observer.ts +65 -0
- package/src/browser/prefetch/policy.ts +42 -0
- package/src/browser/prefetch/queue.ts +88 -0
- package/src/browser/rango-state.ts +112 -0
- package/src/browser/react/Link.tsx +360 -0
- package/src/browser/react/NavigationProvider.tsx +386 -0
- package/src/browser/react/ScrollRestoration.tsx +94 -0
- package/src/browser/react/context.ts +59 -0
- package/src/browser/react/filter-segment-order.ts +11 -0
- package/src/browser/react/index.ts +52 -0
- package/src/browser/react/location-state-shared.ts +162 -0
- package/src/browser/react/location-state.ts +107 -0
- package/src/browser/react/mount-context.ts +37 -0
- package/src/browser/react/nonce-context.ts +23 -0
- package/src/browser/react/shallow-equal.ts +27 -0
- package/src/browser/react/use-action.ts +218 -0
- package/src/browser/react/use-client-cache.ts +58 -0
- package/src/browser/react/use-handle.ts +162 -0
- package/src/browser/react/use-href.tsx +40 -0
- package/src/browser/react/use-link-status.ts +135 -0
- package/src/browser/react/use-mount.ts +31 -0
- package/src/browser/react/use-navigation.ts +99 -0
- package/src/browser/react/use-params.ts +65 -0
- package/src/browser/react/use-pathname.ts +47 -0
- package/src/browser/react/use-router.ts +63 -0
- package/src/browser/react/use-search-params.ts +56 -0
- package/src/browser/react/use-segments.ts +171 -0
- package/src/browser/response-adapter.ts +73 -0
- package/src/browser/rsc-router.tsx +431 -0
- package/src/browser/scroll-restoration.ts +400 -0
- package/src/browser/segment-reconciler.ts +216 -0
- package/src/browser/segment-structure-assert.ts +83 -0
- package/src/browser/server-action-bridge.ts +667 -0
- package/src/browser/shallow.ts +40 -0
- package/src/browser/types.ts +538 -0
- package/src/browser/validate-redirect-origin.ts +29 -0
- package/src/build/generate-manifest.ts +438 -0
- package/src/build/generate-route-types.ts +36 -0
- package/src/build/index.ts +35 -0
- package/src/build/route-trie.ts +265 -0
- package/src/build/route-types/ast-helpers.ts +25 -0
- package/src/build/route-types/ast-route-extraction.ts +98 -0
- package/src/build/route-types/codegen.ts +102 -0
- package/src/build/route-types/include-resolution.ts +411 -0
- package/src/build/route-types/param-extraction.ts +48 -0
- package/src/build/route-types/per-module-writer.ts +128 -0
- package/src/build/route-types/router-processing.ts +469 -0
- package/src/build/route-types/scan-filter.ts +78 -0
- package/src/build/runtime-discovery.ts +231 -0
- package/src/cache/background-task.ts +34 -0
- package/src/cache/cache-key-utils.ts +44 -0
- package/src/cache/cache-policy.ts +125 -0
- package/src/cache/cache-runtime.ts +338 -0
- package/src/cache/cache-scope.ts +382 -0
- package/src/cache/cf/cf-cache-store.ts +540 -0
- package/src/cache/cf/index.ts +25 -0
- package/src/cache/document-cache.ts +369 -0
- package/src/cache/handle-capture.ts +81 -0
- package/src/cache/handle-snapshot.ts +41 -0
- package/src/cache/index.ts +43 -0
- package/src/cache/memory-segment-store.ts +328 -0
- package/src/cache/profile-registry.ts +73 -0
- package/src/cache/read-through-swr.ts +134 -0
- package/src/cache/segment-codec.ts +256 -0
- package/src/cache/taint.ts +98 -0
- package/src/cache/types.ts +342 -0
- package/src/client.rsc.tsx +85 -0
- package/src/client.tsx +601 -0
- package/src/component-utils.ts +76 -0
- package/src/components/DefaultDocument.tsx +27 -0
- package/src/context-var.ts +86 -0
- package/src/debug.ts +243 -0
- package/src/default-error-boundary.tsx +88 -0
- package/src/deps/browser.ts +8 -0
- package/src/deps/html-stream-client.ts +2 -0
- package/src/deps/html-stream-server.ts +2 -0
- package/src/deps/rsc.ts +10 -0
- package/src/deps/ssr.ts +2 -0
- package/src/errors.ts +365 -0
- package/src/handle.ts +135 -0
- package/src/handles/MetaTags.tsx +246 -0
- package/src/handles/breadcrumbs.ts +66 -0
- package/src/handles/index.ts +7 -0
- package/src/handles/meta.ts +264 -0
- package/src/host/cookie-handler.ts +165 -0
- package/src/host/errors.ts +97 -0
- package/src/host/index.ts +53 -0
- package/src/host/pattern-matcher.ts +214 -0
- package/src/host/router.ts +352 -0
- package/src/host/testing.ts +79 -0
- package/src/host/types.ts +146 -0
- package/src/host/utils.ts +25 -0
- package/src/href-client.ts +222 -0
- package/src/index.rsc.ts +233 -0
- package/src/index.ts +277 -0
- package/src/internal-debug.ts +11 -0
- package/src/loader.rsc.ts +89 -0
- package/src/loader.ts +64 -0
- package/src/network-error-thrower.tsx +23 -0
- package/src/outlet-context.ts +15 -0
- package/src/outlet-provider.tsx +45 -0
- package/src/prerender/param-hash.ts +37 -0
- package/src/prerender/store.ts +185 -0
- package/src/prerender.ts +463 -0
- package/src/reverse.ts +330 -0
- package/src/root-error-boundary.tsx +289 -0
- package/src/route-content-wrapper.tsx +196 -0
- package/src/route-definition/dsl-helpers.ts +934 -0
- package/src/route-definition/helper-factories.ts +200 -0
- package/src/route-definition/helpers-types.ts +430 -0
- package/src/route-definition/index.ts +52 -0
- package/src/route-definition/redirect.ts +93 -0
- package/src/route-definition.ts +1 -0
- package/src/route-map-builder.ts +275 -0
- package/src/route-name.ts +53 -0
- package/src/route-types.ts +259 -0
- package/src/router/content-negotiation.ts +116 -0
- package/src/router/debug-manifest.ts +72 -0
- package/src/router/error-handling.ts +287 -0
- package/src/router/find-match.ts +158 -0
- package/src/router/handler-context.ts +451 -0
- package/src/router/intercept-resolution.ts +395 -0
- package/src/router/lazy-includes.ts +234 -0
- package/src/router/loader-resolution.ts +420 -0
- package/src/router/logging.ts +248 -0
- package/src/router/manifest.ts +267 -0
- package/src/router/match-api.ts +620 -0
- package/src/router/match-context.ts +266 -0
- package/src/router/match-handlers.ts +440 -0
- package/src/router/match-middleware/background-revalidation.ts +223 -0
- package/src/router/match-middleware/cache-lookup.ts +634 -0
- package/src/router/match-middleware/cache-store.ts +295 -0
- package/src/router/match-middleware/index.ts +81 -0
- package/src/router/match-middleware/intercept-resolution.ts +306 -0
- package/src/router/match-middleware/segment-resolution.ts +192 -0
- package/src/router/match-pipelines.ts +179 -0
- package/src/router/match-result.ts +219 -0
- package/src/router/metrics.ts +282 -0
- package/src/router/middleware-cookies.ts +55 -0
- package/src/router/middleware-types.ts +222 -0
- package/src/router/middleware.ts +748 -0
- package/src/router/pattern-matching.ts +563 -0
- package/src/router/prerender-match.ts +402 -0
- package/src/router/preview-match.ts +170 -0
- package/src/router/revalidation.ts +289 -0
- package/src/router/router-context.ts +316 -0
- package/src/router/router-interfaces.ts +452 -0
- package/src/router/router-options.ts +592 -0
- package/src/router/router-registry.ts +24 -0
- package/src/router/segment-resolution/fresh.ts +570 -0
- package/src/router/segment-resolution/helpers.ts +263 -0
- package/src/router/segment-resolution/loader-cache.ts +198 -0
- package/src/router/segment-resolution/revalidation.ts +1239 -0
- package/src/router/segment-resolution/static-store.ts +67 -0
- package/src/router/segment-resolution.ts +21 -0
- package/src/router/segment-wrappers.ts +289 -0
- package/src/router/telemetry-otel.ts +299 -0
- package/src/router/telemetry.ts +300 -0
- package/src/router/timeout.ts +148 -0
- package/src/router/trie-matching.ts +239 -0
- package/src/router/types.ts +170 -0
- package/src/router.ts +1002 -0
- package/src/rsc/handler-context.ts +45 -0
- package/src/rsc/handler.ts +1089 -0
- package/src/rsc/helpers.ts +198 -0
- package/src/rsc/index.ts +36 -0
- package/src/rsc/loader-fetch.ts +209 -0
- package/src/rsc/manifest-init.ts +86 -0
- package/src/rsc/nonce.ts +32 -0
- package/src/rsc/origin-guard.ts +141 -0
- package/src/rsc/progressive-enhancement.ts +379 -0
- package/src/rsc/response-error.ts +37 -0
- package/src/rsc/response-route-handler.ts +347 -0
- package/src/rsc/rsc-rendering.ts +235 -0
- package/src/rsc/runtime-warnings.ts +42 -0
- package/src/rsc/server-action.ts +348 -0
- package/src/rsc/ssr-setup.ts +128 -0
- package/src/rsc/types.ts +263 -0
- package/src/search-params.ts +230 -0
- package/src/segment-system.tsx +454 -0
- package/src/server/context.ts +591 -0
- package/src/server/cookie-store.ts +190 -0
- package/src/server/fetchable-loader-store.ts +37 -0
- package/src/server/handle-store.ts +308 -0
- package/src/server/loader-registry.ts +133 -0
- package/src/server/request-context.ts +914 -0
- package/src/server/root-layout.tsx +10 -0
- package/src/server/tsconfig.json +14 -0
- package/src/server.ts +51 -0
- package/src/ssr/index.tsx +365 -0
- package/src/static-handler.ts +114 -0
- package/src/theme/ThemeProvider.tsx +297 -0
- package/src/theme/ThemeScript.tsx +61 -0
- package/src/theme/constants.ts +62 -0
- package/src/theme/index.ts +48 -0
- package/src/theme/theme-context.ts +44 -0
- package/src/theme/theme-script.ts +155 -0
- package/src/theme/types.ts +182 -0
- package/src/theme/use-theme.ts +44 -0
- package/src/types/boundaries.ts +158 -0
- package/src/types/cache-types.ts +198 -0
- package/src/types/error-types.ts +192 -0
- package/src/types/global-namespace.ts +100 -0
- package/src/types/handler-context.ts +687 -0
- package/src/types/index.ts +88 -0
- package/src/types/loader-types.ts +183 -0
- package/src/types/route-config.ts +170 -0
- package/src/types/route-entry.ts +102 -0
- package/src/types/segments.ts +148 -0
- package/src/types.ts +1 -0
- package/src/urls/include-helper.ts +197 -0
- package/src/urls/index.ts +53 -0
- package/src/urls/path-helper-types.ts +339 -0
- package/src/urls/path-helper.ts +329 -0
- package/src/urls/pattern-types.ts +95 -0
- package/src/urls/response-types.ts +106 -0
- package/src/urls/type-extraction.ts +372 -0
- package/src/urls/urls-function.ts +98 -0
- package/src/urls.ts +1 -0
- package/src/use-loader.tsx +354 -0
- package/src/vite/discovery/bundle-postprocess.ts +184 -0
- package/src/vite/discovery/discover-routers.ts +344 -0
- package/src/vite/discovery/prerender-collection.ts +385 -0
- package/src/vite/discovery/route-types-writer.ts +258 -0
- package/src/vite/discovery/self-gen-tracking.ts +47 -0
- package/src/vite/discovery/state.ts +110 -0
- package/src/vite/discovery/virtual-module-codegen.ts +203 -0
- package/src/vite/index.ts +16 -0
- package/src/vite/plugin-types.ts +131 -0
- package/src/vite/plugins/cjs-to-esm.ts +93 -0
- package/src/vite/plugins/client-ref-dedup.ts +115 -0
- package/src/vite/plugins/client-ref-hashing.ts +105 -0
- package/src/vite/plugins/expose-action-id.ts +365 -0
- package/src/vite/plugins/expose-id-utils.ts +287 -0
- package/src/vite/plugins/expose-ids/export-analysis.ts +296 -0
- package/src/vite/plugins/expose-ids/handler-transform.ts +179 -0
- package/src/vite/plugins/expose-ids/loader-transform.ts +74 -0
- package/src/vite/plugins/expose-ids/router-transform.ts +110 -0
- package/src/vite/plugins/expose-ids/types.ts +45 -0
- package/src/vite/plugins/expose-internal-ids.ts +569 -0
- package/src/vite/plugins/refresh-cmd.ts +65 -0
- package/src/vite/plugins/use-cache-transform.ts +323 -0
- package/src/vite/plugins/version-injector.ts +83 -0
- package/src/vite/plugins/version-plugin.ts +254 -0
- package/src/vite/plugins/version.d.ts +12 -0
- package/src/vite/plugins/virtual-entries.ts +123 -0
- package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
- package/src/vite/rango.ts +510 -0
- package/src/vite/router-discovery.ts +785 -0
- package/src/vite/utils/ast-handler-extract.ts +517 -0
- package/src/vite/utils/banner.ts +36 -0
- package/src/vite/utils/bundle-analysis.ts +137 -0
- package/src/vite/utils/manifest-utils.ts +70 -0
- package/src/vite/utils/package-resolution.ts +121 -0
- package/src/vite/utils/prerender-utils.ts +189 -0
- package/src/vite/utils/shared-utils.ts +169 -0
|
@@ -0,0 +1,687 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import type { Handle } from "../handle.js";
|
|
3
|
+
import type { ContextVar } from "../context-var.js";
|
|
4
|
+
import type { MiddlewareFn } from "../router/middleware.js";
|
|
5
|
+
import type { Theme } from "../theme/types.js";
|
|
6
|
+
import type { ScopedReverseFunction } from "../reverse.js";
|
|
7
|
+
import type { SearchSchema, ResolveSearchSchema } from "../search-params.js";
|
|
8
|
+
import type { LocationStateEntry } from "../browser/react/location-state-shared.js";
|
|
9
|
+
import type {
|
|
10
|
+
DefaultEnv,
|
|
11
|
+
DefaultHandlerRouteMap,
|
|
12
|
+
DefaultReverseRouteMap,
|
|
13
|
+
DefaultRouteName,
|
|
14
|
+
DefaultVars,
|
|
15
|
+
} from "./global-namespace.js";
|
|
16
|
+
import type {
|
|
17
|
+
ExtractParams,
|
|
18
|
+
RouteDefinition,
|
|
19
|
+
ResolvedRouteMap,
|
|
20
|
+
} from "./route-config.js";
|
|
21
|
+
import type { LoaderDefinition } from "./loader-types.js";
|
|
22
|
+
|
|
23
|
+
// Re-export MiddlewareFn for internal/advanced use
|
|
24
|
+
export type { MiddlewareFn } from "../router/middleware.js";
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Create a scoped view of a route map by stripping a name prefix.
|
|
28
|
+
* Useful for handlers in modules mounted via include() -- use the local
|
|
29
|
+
* route name instead of the fully qualified global name.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```typescript
|
|
33
|
+
* // Given GeneratedRouteMap: { "blog.index": "/blog"; "blog.post": "/blog/:postId"; ... }
|
|
34
|
+
* type BlogRoutes = ScopedRouteMap<"blog">;
|
|
35
|
+
* // Resolves to: { "index": "/blog"; "post": "/blog/:postId" }
|
|
36
|
+
*
|
|
37
|
+
* const handler: Handler<"post", BlogRoutes> = (ctx) => {
|
|
38
|
+
* ctx.params.postId // string
|
|
39
|
+
* };
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export type ScopedRouteMap<
|
|
43
|
+
TPrefix extends string,
|
|
44
|
+
TMap = RSCRouter.GeneratedRouteMap,
|
|
45
|
+
> = {
|
|
46
|
+
[K in keyof TMap as K extends `${TPrefix}.${infer Rest}`
|
|
47
|
+
? Rest
|
|
48
|
+
: never]: TMap[K];
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Extract the search schema from a route map entry.
|
|
53
|
+
* - string value (old format): no search schema -> {}
|
|
54
|
+
* - { path, search } value: extract search
|
|
55
|
+
* - { path, response } value (no search): -> {}
|
|
56
|
+
*/
|
|
57
|
+
/** Extract params from a route map entry (string pattern or { path } object). */
|
|
58
|
+
type ExtractParamsFromEntry<TEntry, TFallback> = TEntry extends string
|
|
59
|
+
? ExtractParams<TEntry>
|
|
60
|
+
: TEntry extends { readonly path: infer P extends string }
|
|
61
|
+
? ExtractParams<P>
|
|
62
|
+
: TFallback;
|
|
63
|
+
|
|
64
|
+
/** Extract search schema from a route map entry. */
|
|
65
|
+
type ExtractSearchFromEntry<TMap, TKey> = TKey extends keyof TMap
|
|
66
|
+
? TMap[TKey] extends { readonly search: infer S extends SearchSchema }
|
|
67
|
+
? S
|
|
68
|
+
: {}
|
|
69
|
+
: {};
|
|
70
|
+
|
|
71
|
+
type IsEmptyObject<T> = keyof T extends never ? true : false;
|
|
72
|
+
|
|
73
|
+
type AutofillParamsFromEntry<TEntry> = TEntry extends string
|
|
74
|
+
? string extends TEntry
|
|
75
|
+
? Record<string, string>
|
|
76
|
+
: Partial<ExtractParams<TEntry>>
|
|
77
|
+
: TEntry extends { readonly path: infer P extends string }
|
|
78
|
+
? string extends P
|
|
79
|
+
? Record<string, string>
|
|
80
|
+
: Partial<ExtractParams<P>>
|
|
81
|
+
: Record<string, string>;
|
|
82
|
+
|
|
83
|
+
type AutofillSearchFromEntry<TMap, TKey> = TKey extends keyof TMap
|
|
84
|
+
? TMap[TKey] extends { readonly search: infer S extends SearchSchema }
|
|
85
|
+
? ResolveSearchSchema<S>
|
|
86
|
+
: Record<string, unknown>
|
|
87
|
+
: Record<string, unknown>;
|
|
88
|
+
|
|
89
|
+
type AutofillAwareReverseFunction<TLocalRoutes, TGlobalRoutes> =
|
|
90
|
+
ScopedReverseFunction<TLocalRoutes, TGlobalRoutes> & {
|
|
91
|
+
<TName extends keyof TGlobalRoutes & string>(
|
|
92
|
+
name: TName,
|
|
93
|
+
params?: AutofillParamsFromEntry<TGlobalRoutes[TName]>,
|
|
94
|
+
search?: AutofillSearchFromEntry<TGlobalRoutes, TName>,
|
|
95
|
+
): string;
|
|
96
|
+
<TName extends keyof TLocalRoutes & string>(
|
|
97
|
+
name: `.${TName}`,
|
|
98
|
+
params?: AutofillParamsFromEntry<TLocalRoutes[TName]>,
|
|
99
|
+
search?: AutofillSearchFromEntry<TLocalRoutes, TName>,
|
|
100
|
+
): string;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
type StrictLocalParamsWithExtras<TEntry> =
|
|
104
|
+
IsEmptyObject<ExtractParamsFromEntry<TEntry, {}>> extends true
|
|
105
|
+
? Record<string, string>
|
|
106
|
+
: ExtractParamsFromEntry<TEntry, {}> & Record<string, string>;
|
|
107
|
+
|
|
108
|
+
// HandlerContext.reverse is the only reverse surface with runtime param autofill
|
|
109
|
+
// from the current matched request. Middleware/loaders/request context do not
|
|
110
|
+
// have the same local-route guarantees, so they keep plain ScopedReverseFunction.
|
|
111
|
+
//
|
|
112
|
+
// When a handler has an explicit local route map, enforce that local route
|
|
113
|
+
// params declared by that map are present while still allowing extra mount
|
|
114
|
+
// params to be passed through. Global names remain autofill-friendly because
|
|
115
|
+
// parent include() params are often unknown at the module definition site.
|
|
116
|
+
type StrictLocalAutofillGlobalReverseFunction<TLocalRoutes, TGlobalRoutes> =
|
|
117
|
+
ScopedReverseFunction<TLocalRoutes, TGlobalRoutes> & {
|
|
118
|
+
<TName extends keyof TGlobalRoutes & string>(
|
|
119
|
+
name: TName,
|
|
120
|
+
params?: AutofillParamsFromEntry<TGlobalRoutes[TName]>,
|
|
121
|
+
search?: AutofillSearchFromEntry<TGlobalRoutes, TName>,
|
|
122
|
+
): string;
|
|
123
|
+
<TName extends keyof TLocalRoutes & string>(
|
|
124
|
+
name: `.${TName}`,
|
|
125
|
+
params: StrictLocalParamsWithExtras<TLocalRoutes[TName]>,
|
|
126
|
+
search?: AutofillSearchFromEntry<TLocalRoutes, TName>,
|
|
127
|
+
): string;
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
export type Handler<
|
|
131
|
+
T extends
|
|
132
|
+
| keyof DefaultHandlerRouteMap
|
|
133
|
+
| `.${keyof TRouteMap & string}`
|
|
134
|
+
| `/${string}`
|
|
135
|
+
| Record<string, any> = {},
|
|
136
|
+
TRouteMap extends {} = DefaultHandlerRouteMap,
|
|
137
|
+
TEnv = DefaultEnv,
|
|
138
|
+
> = (
|
|
139
|
+
ctx: HandlerContext<
|
|
140
|
+
T extends `.${infer Local}`
|
|
141
|
+
? Local extends keyof TRouteMap
|
|
142
|
+
? ExtractParamsFromEntry<
|
|
143
|
+
TRouteMap[Local],
|
|
144
|
+
T extends string ? ExtractParams<T> : T
|
|
145
|
+
>
|
|
146
|
+
: T extends string
|
|
147
|
+
? ExtractParams<T>
|
|
148
|
+
: T
|
|
149
|
+
: T extends keyof DefaultHandlerRouteMap
|
|
150
|
+
? ExtractParamsFromEntry<
|
|
151
|
+
DefaultHandlerRouteMap[T],
|
|
152
|
+
T extends string ? ExtractParams<T> : T
|
|
153
|
+
>
|
|
154
|
+
: T extends string
|
|
155
|
+
? ExtractParams<T>
|
|
156
|
+
: T,
|
|
157
|
+
TEnv,
|
|
158
|
+
T extends `.${infer Local}`
|
|
159
|
+
? ExtractSearchFromEntry<TRouteMap, Local>
|
|
160
|
+
: ExtractSearchFromEntry<DefaultHandlerRouteMap, T>,
|
|
161
|
+
TRouteMap extends DefaultHandlerRouteMap ? never : TRouteMap
|
|
162
|
+
>,
|
|
163
|
+
) => ReactNode | Promise<ReactNode> | Response | Promise<Response>;
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Context passed to handlers (Hono-inspired type-safe context)
|
|
167
|
+
*
|
|
168
|
+
* Provides type-safe access to:
|
|
169
|
+
* - Route params (from URL pattern)
|
|
170
|
+
* - Cleaned route URL (`url`, `searchParams`, `pathname` — no `_rsc*` params)
|
|
171
|
+
* - Original request (`request` — raw transport URL, headers, method, body)
|
|
172
|
+
* - Platform bindings (env.DB, env.KV, env.SECRETS)
|
|
173
|
+
* - Middleware variables (var.user, var.permissions)
|
|
174
|
+
* - Getter/setter for variables (get('user'), set('user', ...))
|
|
175
|
+
*
|
|
176
|
+
* @example
|
|
177
|
+
* ```typescript
|
|
178
|
+
* const handler = (ctx: HandlerContext<{ slug: string }, AppEnv>) => {
|
|
179
|
+
* ctx.params.slug // Route param (string)
|
|
180
|
+
* ctx.env.DB // Binding (D1Database)
|
|
181
|
+
* ctx.var.user // Variable (User | undefined)
|
|
182
|
+
* ctx.get('user') // Alternative getter
|
|
183
|
+
* ctx.set('user', {...}) // Setter
|
|
184
|
+
* ctx.url // Clean URL (no _rsc* params)
|
|
185
|
+
* ctx.searchParams // Clean params (no _rsc* params)
|
|
186
|
+
* ctx.request // Raw transport request (original URL intact)
|
|
187
|
+
* }
|
|
188
|
+
* ```
|
|
189
|
+
*/
|
|
190
|
+
export type HandlerContext<
|
|
191
|
+
TParams = {},
|
|
192
|
+
TEnv = DefaultEnv,
|
|
193
|
+
TSearch extends SearchSchema = {},
|
|
194
|
+
TRouteMap = never,
|
|
195
|
+
> = {
|
|
196
|
+
/**
|
|
197
|
+
* Route parameters extracted from the URL pattern.
|
|
198
|
+
* Type-safe when using Handler<"/path/:param"> or Handler<{ param: string }>.
|
|
199
|
+
*/
|
|
200
|
+
params: TParams;
|
|
201
|
+
/** @internal Phantom property for params type invariance. Prevents mounting handlers on wrong routes. */
|
|
202
|
+
readonly _paramCheck?: (params: TParams) => TParams;
|
|
203
|
+
/**
|
|
204
|
+
* True during build-time pre-rendering, false at runtime.
|
|
205
|
+
* Build-time collection and dev on-demand prerender use `true`.
|
|
206
|
+
* Live request rendering, including passthrough fallback, uses `false`.
|
|
207
|
+
*/
|
|
208
|
+
build: boolean;
|
|
209
|
+
/**
|
|
210
|
+
* The original incoming Request object (transport URL intact).
|
|
211
|
+
* Use `ctx.url` / `ctx.searchParams` for application logic — those have
|
|
212
|
+
* internal `_rsc*` params stripped. `ctx.request` preserves the raw URL
|
|
213
|
+
* for cases where you need original headers, method, or body.
|
|
214
|
+
*/
|
|
215
|
+
request: Request;
|
|
216
|
+
/**
|
|
217
|
+
* Query parameters from the URL (system params like `_rsc*` are filtered).
|
|
218
|
+
* Always a standard URLSearchParams instance.
|
|
219
|
+
*/
|
|
220
|
+
searchParams: URLSearchParams;
|
|
221
|
+
/**
|
|
222
|
+
* Typed search parameters parsed from URL query string via the route's
|
|
223
|
+
* search schema. Empty object when no schema is defined.
|
|
224
|
+
*/
|
|
225
|
+
search: {} extends TSearch ? {} : ResolveSearchSchema<TSearch>;
|
|
226
|
+
/**
|
|
227
|
+
* The pathname portion of the request URL.
|
|
228
|
+
*/
|
|
229
|
+
pathname: string;
|
|
230
|
+
/**
|
|
231
|
+
* The full URL object (with internal `_rsc*` params stripped).
|
|
232
|
+
* Use this for application logic — routing, link generation, display.
|
|
233
|
+
*/
|
|
234
|
+
url: URL;
|
|
235
|
+
/**
|
|
236
|
+
* The original request URL with all parameters intact, including
|
|
237
|
+
* internal `_rsc*` transport params. Use `ctx.url` for application
|
|
238
|
+
* logic — this is only needed for advanced cases like debugging
|
|
239
|
+
* or custom cache keying.
|
|
240
|
+
*/
|
|
241
|
+
originalUrl: URL;
|
|
242
|
+
/**
|
|
243
|
+
* Platform bindings (DB, KV, secrets, etc.).
|
|
244
|
+
* Access resources like `ctx.env.DB`, `ctx.env.KV`.
|
|
245
|
+
*/
|
|
246
|
+
env: TEnv;
|
|
247
|
+
/**
|
|
248
|
+
* Middleware-injected variables.
|
|
249
|
+
* Access values like `ctx.var.user`, `ctx.var.permissions`.
|
|
250
|
+
*/
|
|
251
|
+
var: DefaultVars;
|
|
252
|
+
/**
|
|
253
|
+
* Type-safe getter for middleware variables.
|
|
254
|
+
* Alternative to `ctx.var.key` with better autocomplete.
|
|
255
|
+
*
|
|
256
|
+
* @example
|
|
257
|
+
* ```typescript
|
|
258
|
+
* const user = ctx.get("user"); // Type-safe!
|
|
259
|
+
* ```
|
|
260
|
+
*/
|
|
261
|
+
get: {
|
|
262
|
+
<T>(contextVar: ContextVar<T>): T | undefined;
|
|
263
|
+
} & (<K extends keyof DefaultVars>(key: K) => DefaultVars[K]);
|
|
264
|
+
/**
|
|
265
|
+
* Type-safe setter for middleware variables.
|
|
266
|
+
* Use in middleware to pass data to handlers.
|
|
267
|
+
*
|
|
268
|
+
* @example
|
|
269
|
+
* ```typescript
|
|
270
|
+
* ctx.set("user", { id: "123", name: "John" }); // Type-safe!
|
|
271
|
+
* ctx.set(MyVar, { ... }); // Type-safe via ContextVar token!
|
|
272
|
+
* ```
|
|
273
|
+
*/
|
|
274
|
+
set: {
|
|
275
|
+
<T>(contextVar: ContextVar<T>, value: T): void;
|
|
276
|
+
} & (<K extends keyof DefaultVars>(key: K, value: DefaultVars[K]) => void);
|
|
277
|
+
/**
|
|
278
|
+
* Response headers. Headers set here are merged into the final response.
|
|
279
|
+
*
|
|
280
|
+
* @example
|
|
281
|
+
* ```typescript
|
|
282
|
+
* route("product", (ctx) => {
|
|
283
|
+
* ctx.headers.set("Cache-Control", "s-maxage=60");
|
|
284
|
+
* return <ProductPage />;
|
|
285
|
+
* });
|
|
286
|
+
* ```
|
|
287
|
+
*/
|
|
288
|
+
headers: Headers;
|
|
289
|
+
/**
|
|
290
|
+
* Access loader data or push handle data.
|
|
291
|
+
*
|
|
292
|
+
* For loaders: Returns a promise that resolves to the loader data.
|
|
293
|
+
* Loaders are executed in parallel and memoized per request.
|
|
294
|
+
*
|
|
295
|
+
* For handles: Returns a push function to add data for this segment.
|
|
296
|
+
* Handle data accumulates across all matched route segments.
|
|
297
|
+
* Push accepts: direct value, Promise, or async callback (executed immediately).
|
|
298
|
+
*
|
|
299
|
+
* @example
|
|
300
|
+
* ```typescript
|
|
301
|
+
* // Loader usage
|
|
302
|
+
* route("cart", async (ctx) => {
|
|
303
|
+
* const cart = await ctx.use(CartLoader);
|
|
304
|
+
* return <CartPage cart={cart} />;
|
|
305
|
+
* });
|
|
306
|
+
*
|
|
307
|
+
* // Handle usage - direct value
|
|
308
|
+
* route("shop", (ctx) => {
|
|
309
|
+
* const push = ctx.use(Breadcrumbs);
|
|
310
|
+
* push({ label: "Shop", href: "/shop" });
|
|
311
|
+
* return <ShopPage />;
|
|
312
|
+
* });
|
|
313
|
+
*
|
|
314
|
+
* // Handle usage - Promise
|
|
315
|
+
* route("product", (ctx) => {
|
|
316
|
+
* const push = ctx.use(Breadcrumbs);
|
|
317
|
+
* push(fetchProductBreadcrumb(ctx.params.id));
|
|
318
|
+
* return <ProductPage />;
|
|
319
|
+
* });
|
|
320
|
+
*
|
|
321
|
+
* // Handle usage - async callback (executed immediately)
|
|
322
|
+
* route("product", (ctx) => {
|
|
323
|
+
* const push = ctx.use(Breadcrumbs);
|
|
324
|
+
* push(async () => {
|
|
325
|
+
* const product = await db.getProduct(ctx.params.id);
|
|
326
|
+
* return { label: product.name, href: `/product/${product.id}` };
|
|
327
|
+
* });
|
|
328
|
+
* return <ProductPage />;
|
|
329
|
+
* });
|
|
330
|
+
* ```
|
|
331
|
+
*/
|
|
332
|
+
use: {
|
|
333
|
+
<T, TLoaderParams = any>(
|
|
334
|
+
loader: LoaderDefinition<T, TLoaderParams>,
|
|
335
|
+
): Promise<T>;
|
|
336
|
+
<TData, TAccumulated = TData[]>(
|
|
337
|
+
handle: Handle<TData, TAccumulated>,
|
|
338
|
+
): (data: TData | Promise<TData> | (() => Promise<TData>)) => void;
|
|
339
|
+
};
|
|
340
|
+
/**
|
|
341
|
+
* Current theme (from cookie or default).
|
|
342
|
+
* Only available when theme is enabled in router config.
|
|
343
|
+
*
|
|
344
|
+
* @example
|
|
345
|
+
* ```typescript
|
|
346
|
+
* route("settings", (ctx) => {
|
|
347
|
+
* const currentTheme = ctx.theme; // "light" | "dark" | "system" | undefined
|
|
348
|
+
* return <SettingsPage theme={currentTheme} />;
|
|
349
|
+
* });
|
|
350
|
+
* ```
|
|
351
|
+
*/
|
|
352
|
+
theme?: Theme;
|
|
353
|
+
/**
|
|
354
|
+
* Set the theme (only available when theme is enabled in router config).
|
|
355
|
+
* Sets a cookie with the new theme value.
|
|
356
|
+
*
|
|
357
|
+
* @example
|
|
358
|
+
* ```typescript
|
|
359
|
+
* route("settings", async (ctx) => {
|
|
360
|
+
* if (ctx.request.method === "POST") {
|
|
361
|
+
* const formData = await ctx.request.formData();
|
|
362
|
+
* const newTheme = formData.get("theme") as Theme;
|
|
363
|
+
* ctx.setTheme?.(newTheme);
|
|
364
|
+
* }
|
|
365
|
+
* return <SettingsPage />;
|
|
366
|
+
* });
|
|
367
|
+
* ```
|
|
368
|
+
*/
|
|
369
|
+
setTheme?: (theme: Theme) => void;
|
|
370
|
+
/**
|
|
371
|
+
* Attach location state entries to this response.
|
|
372
|
+
* State is delivered to the client via history.pushState and accessible
|
|
373
|
+
* through the useLocationState() hook.
|
|
374
|
+
*
|
|
375
|
+
* @example
|
|
376
|
+
* ```typescript
|
|
377
|
+
* route("product", (ctx) => {
|
|
378
|
+
* ctx.setLocationState(ServerInfo({ data: "value" }));
|
|
379
|
+
* return <ProductPage />;
|
|
380
|
+
* });
|
|
381
|
+
* ```
|
|
382
|
+
*/
|
|
383
|
+
setLocationState(entries: LocationStateEntry | LocationStateEntry[]): void;
|
|
384
|
+
/**
|
|
385
|
+
* The matched route name, if the route has an explicit name.
|
|
386
|
+
* Undefined for unnamed routes (those without a `name` option in path()).
|
|
387
|
+
* Includes the namespace prefix from include() (e.g., "blog.post").
|
|
388
|
+
*
|
|
389
|
+
* @example
|
|
390
|
+
* ```typescript
|
|
391
|
+
* route("product", (ctx) => {
|
|
392
|
+
* ctx.routeName // "product"
|
|
393
|
+
* return <ProductPage />;
|
|
394
|
+
* });
|
|
395
|
+
* ```
|
|
396
|
+
*/
|
|
397
|
+
routeName?: DefaultRouteName;
|
|
398
|
+
/**
|
|
399
|
+
* Generate URLs from route names.
|
|
400
|
+
*
|
|
401
|
+
* - `.name` -- local route, resolved within current include() scope
|
|
402
|
+
* - `name` -- global route, from the named-routes definition
|
|
403
|
+
*
|
|
404
|
+
* @example
|
|
405
|
+
* ```typescript
|
|
406
|
+
* ctx.reverse(".article", { slug: "hello" }) // Local: magazine.article
|
|
407
|
+
* ctx.reverse(".index") // Local: magazine.index
|
|
408
|
+
* ctx.reverse("magazine.index") // Global: magazine.index
|
|
409
|
+
* ctx.reverse("blog.post", { slug: "hello" }) // Global: blog.post
|
|
410
|
+
* ```
|
|
411
|
+
*/
|
|
412
|
+
reverse: [TRouteMap] extends [never]
|
|
413
|
+
? AutofillAwareReverseFunction<
|
|
414
|
+
Record<string, string>,
|
|
415
|
+
DefaultReverseRouteMap
|
|
416
|
+
>
|
|
417
|
+
: StrictLocalAutofillGlobalReverseFunction<
|
|
418
|
+
TRouteMap,
|
|
419
|
+
DefaultReverseRouteMap
|
|
420
|
+
>;
|
|
421
|
+
};
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Internal handler context with additional props for router internals.
|
|
425
|
+
* Use `HandlerContext` for user-facing code.
|
|
426
|
+
* @internal
|
|
427
|
+
*/
|
|
428
|
+
export type InternalHandlerContext<
|
|
429
|
+
TParams = {},
|
|
430
|
+
TEnv = DefaultEnv,
|
|
431
|
+
TSearch extends SearchSchema = {},
|
|
432
|
+
> = HandlerContext<TParams, TEnv, TSearch> & {
|
|
433
|
+
/** @internal Stub response for collecting headers/cookies. */
|
|
434
|
+
res: Response;
|
|
435
|
+
/** Prerender-only control flow helper, attached when the runtime context supports it. */
|
|
436
|
+
passthrough?: () => unknown;
|
|
437
|
+
/** Current segment ID for handle data attribution. */
|
|
438
|
+
_currentSegmentId?: string;
|
|
439
|
+
/** Response type tag (json, text, html, etc.) for cache key differentiation. */
|
|
440
|
+
_responseType?: string;
|
|
441
|
+
/** Route name for cache key scoping (prevents cross-route collisions). */
|
|
442
|
+
_routeName?: string;
|
|
443
|
+
};
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Generic params type - flexible object with string keys
|
|
447
|
+
* Users can narrow this by explicitly typing their params:
|
|
448
|
+
*
|
|
449
|
+
* @example
|
|
450
|
+
* ```typescript
|
|
451
|
+
* [revalidate('post')]: (({ currentParams, nextParams }: RevalidateParams<{ slug: string }>) => {
|
|
452
|
+
* currentParams.slug // typed as string
|
|
453
|
+
* return currentParams.slug !== nextParams.slug;
|
|
454
|
+
* })
|
|
455
|
+
* ```
|
|
456
|
+
*/
|
|
457
|
+
export type GenericParams = { [key: string]: string | undefined };
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* Helper type for revalidation handler params
|
|
461
|
+
* Allows inline type annotation for stricter param typing
|
|
462
|
+
*
|
|
463
|
+
* @example
|
|
464
|
+
* ```typescript
|
|
465
|
+
* [revalidate('post')]: (params: RevalidateParams<{ slug: string }>) => {
|
|
466
|
+
* params.currentParams.slug // typed as string
|
|
467
|
+
* return params.defaultShouldRevalidate;
|
|
468
|
+
* }
|
|
469
|
+
* ```
|
|
470
|
+
*/
|
|
471
|
+
export type RevalidateParams<TParams = GenericParams, TEnv = any> = Parameters<
|
|
472
|
+
ShouldRevalidateFn<TParams, TEnv>
|
|
473
|
+
>[0];
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Should revalidate function signature (inspired by React Router)
|
|
477
|
+
*
|
|
478
|
+
* Determines whether a route segment should re-render during partial navigation.
|
|
479
|
+
* Multiple revalidation functions can be defined per route - they execute in order.
|
|
480
|
+
*
|
|
481
|
+
* **Return Types:**
|
|
482
|
+
* - `boolean` - Hard decision: immediately returns this value (short-circuits)
|
|
483
|
+
* - `{ defaultShouldRevalidate: boolean }` - Soft decision: updates suggestion for next revalidator
|
|
484
|
+
*
|
|
485
|
+
* **Execution Flow:**
|
|
486
|
+
* 1. Start with built-in `defaultShouldRevalidate` (true if params changed)
|
|
487
|
+
* 2. Execute global revalidators first, then route-specific
|
|
488
|
+
* 3. Hard decision (boolean): stop immediately and use that value
|
|
489
|
+
* 4. Soft decision (object): update suggestion and continue to next revalidator
|
|
490
|
+
* 5. If all return soft decisions: use the final suggestion
|
|
491
|
+
*
|
|
492
|
+
* @param args.currentParams - Previous route params (generic by default, can be narrowed)
|
|
493
|
+
* @param args.currentUrl - Previous URL
|
|
494
|
+
* @param args.nextParams - Next route params (generic by default, can be narrowed)
|
|
495
|
+
* @param args.nextUrl - Next URL
|
|
496
|
+
* @param args.defaultShouldRevalidate - Current suggestion (updated by soft decisions)
|
|
497
|
+
* @param args.context - App context (db, user, etc.)
|
|
498
|
+
* @param args.actionResult - Result from action (future support)
|
|
499
|
+
* @param args.formData - Form data from action (future support)
|
|
500
|
+
* @param args.formMethod - HTTP method from action (future support)
|
|
501
|
+
*
|
|
502
|
+
* @returns Hard decision (boolean) or soft suggestion (object)
|
|
503
|
+
*
|
|
504
|
+
* @example
|
|
505
|
+
* ```typescript
|
|
506
|
+
* // Hard decision - definitive answer
|
|
507
|
+
* [revalidate('post')]: ({ currentParams, nextParams }) => {
|
|
508
|
+
* return currentParams.slug !== nextParams.slug; // boolean - short-circuits
|
|
509
|
+
* }
|
|
510
|
+
*
|
|
511
|
+
* // Soft decision - allows downstream revalidators to override
|
|
512
|
+
* [revalidate('*', 'global')]: ({ defaultShouldRevalidate }) => {
|
|
513
|
+
* return { defaultShouldRevalidate: true }; // object - continues to next
|
|
514
|
+
* }
|
|
515
|
+
*
|
|
516
|
+
* // Explicit typing for stricter params
|
|
517
|
+
* [revalidate('post')]: ((params: RevalidateParams<{ slug: string }>) => {
|
|
518
|
+
* return params.currentParams.slug !== params.nextParams.slug;
|
|
519
|
+
* })
|
|
520
|
+
* ```
|
|
521
|
+
*/
|
|
522
|
+
export type ShouldRevalidateFn<TParams = GenericParams, TEnv = any> = (args: {
|
|
523
|
+
currentParams: TParams;
|
|
524
|
+
currentUrl: URL;
|
|
525
|
+
nextParams: TParams;
|
|
526
|
+
nextUrl: URL;
|
|
527
|
+
defaultShouldRevalidate: boolean;
|
|
528
|
+
context: HandlerContext<TParams, TEnv>;
|
|
529
|
+
// Segment metadata (which segment is being evaluated):
|
|
530
|
+
segmentType: "layout" | "route" | "parallel";
|
|
531
|
+
layoutName?: string; // Layout name (e.g., "root", "shop", "auth") - only for layouts
|
|
532
|
+
slotName?: string; // Slot name (e.g., "@sidebar", "@modal") - only for parallels
|
|
533
|
+
// Action context (populated when revalidation triggered by server action):
|
|
534
|
+
actionId?: string; // Action identifier (e.g., "src/actions.ts#addToCart")
|
|
535
|
+
actionUrl?: URL; // URL where action was executed
|
|
536
|
+
actionResult?: any; // Return value from action execution
|
|
537
|
+
formData?: FormData; // FormData from action request
|
|
538
|
+
method?: string; // Request method: 'GET' for navigation, 'POST' for actions
|
|
539
|
+
routeName?: DefaultRouteName; // Route name of the navigation target (alias for toRouteName)
|
|
540
|
+
// Named-route identity for both ends of a navigation transition.
|
|
541
|
+
// Undefined for unnamed internal routes (those without a `name` option).
|
|
542
|
+
fromRouteName?: DefaultRouteName; // Route name being navigated away from
|
|
543
|
+
toRouteName?: DefaultRouteName; // Route name being navigated to
|
|
544
|
+
// Stale cache revalidation (SWR pattern):
|
|
545
|
+
stale?: boolean; // True if this is a stale cache revalidation request
|
|
546
|
+
}) => boolean | { defaultShouldRevalidate: boolean };
|
|
547
|
+
|
|
548
|
+
// MiddlewareFn is imported from "../router/middleware.js" and re-exported
|
|
549
|
+
|
|
550
|
+
/**
|
|
551
|
+
* Extract all route keys from a route definition (includes flattened nested routes)
|
|
552
|
+
*/
|
|
553
|
+
export type RouteKeys<T extends RouteDefinition> = keyof ResolvedRouteMap<T> &
|
|
554
|
+
string;
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* Valid layout value - component or handler function
|
|
558
|
+
* Note: Arrays are not supported. Use separate layout() declarations with unique names instead.
|
|
559
|
+
*/
|
|
560
|
+
type LayoutValue<TEnv = any> = ReactNode | Handler<any, any, TEnv>;
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* Helper to extract params from a route key using the resolved (flattened) route map
|
|
564
|
+
*/
|
|
565
|
+
export type ExtractRouteParams<
|
|
566
|
+
T extends RouteDefinition,
|
|
567
|
+
K extends string,
|
|
568
|
+
> = K extends keyof ResolvedRouteMap<T>
|
|
569
|
+
? ResolvedRouteMap<T>[K] extends string
|
|
570
|
+
? ExtractParams<ResolvedRouteMap<T>[K]>
|
|
571
|
+
: GenericParams
|
|
572
|
+
: GenericParams;
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* Handlers object that maps route names to handler functions with type-safe string patterns
|
|
576
|
+
*/
|
|
577
|
+
export type HandlersForRouteMap<T extends RouteDefinition, TEnv = any> = {
|
|
578
|
+
// Route handlers - type-safe params extracted from route patterns
|
|
579
|
+
[K in RouteKeys<T>]?: Handler<ExtractRouteParams<T, K & string>, any, TEnv>;
|
|
580
|
+
} & {
|
|
581
|
+
// Layout patterns: $layout.{routeName}.{layoutName}
|
|
582
|
+
[K in `$layout.${RouteKeys<T> | "*"}.${string}`]?: LayoutValue<TEnv>;
|
|
583
|
+
} & {
|
|
584
|
+
// Parallel route patterns: $parallel.{routeName}.{parallelName}
|
|
585
|
+
[K in `$parallel.${RouteKeys<T>}.${string}`]?: Record<
|
|
586
|
+
`@${string}`,
|
|
587
|
+
Handler<
|
|
588
|
+
K extends `$parallel.${infer RouteKey}.${string}`
|
|
589
|
+
? RouteKey extends RouteKeys<T>
|
|
590
|
+
? ExtractRouteParams<T, RouteKey & string>
|
|
591
|
+
: GenericParams
|
|
592
|
+
: GenericParams,
|
|
593
|
+
any,
|
|
594
|
+
TEnv
|
|
595
|
+
>
|
|
596
|
+
>;
|
|
597
|
+
} & {
|
|
598
|
+
// Global parallel routes (with '*') use GenericParams
|
|
599
|
+
[K in `$parallel.${"*"}.${string}`]?: Record<
|
|
600
|
+
`@${string}`,
|
|
601
|
+
Handler<GenericParams, any, TEnv>
|
|
602
|
+
>;
|
|
603
|
+
} & {
|
|
604
|
+
// Middleware patterns: $middleware.{routeName}.{middlewareName}
|
|
605
|
+
[K in `$middleware.${RouteKeys<T> | "*"}.${string}`]?: MiddlewareFn<
|
|
606
|
+
TEnv,
|
|
607
|
+
GenericParams
|
|
608
|
+
>[];
|
|
609
|
+
} & {
|
|
610
|
+
// Route revalidate patterns: $revalidate.route.{routeName}.{revalidateName}
|
|
611
|
+
[K in `$revalidate.route.${RouteKeys<T> | "*"}.${string}`]?: ShouldRevalidateFn<
|
|
612
|
+
GenericParams,
|
|
613
|
+
TEnv
|
|
614
|
+
>;
|
|
615
|
+
} & {
|
|
616
|
+
// Layout revalidate patterns: $revalidate.layout.{routeName}.{layoutName}.{revalidateName}
|
|
617
|
+
[K in `$revalidate.layout.${RouteKeys<T> | "*"}.${string}.${string}`]?: ShouldRevalidateFn<
|
|
618
|
+
GenericParams,
|
|
619
|
+
TEnv
|
|
620
|
+
>;
|
|
621
|
+
} & {
|
|
622
|
+
// Parallel revalidate patterns: $revalidate.parallel.{routeName}.{parallelName}.{slotName}.{revalidateName}
|
|
623
|
+
[K in `$revalidate.parallel.${RouteKeys<T> | "*"}.${string}.${string}.${string}`]?: ShouldRevalidateFn<
|
|
624
|
+
GenericParams,
|
|
625
|
+
TEnv
|
|
626
|
+
>;
|
|
627
|
+
};
|
|
628
|
+
|
|
629
|
+
/**
|
|
630
|
+
* Revalidation function with typed params
|
|
631
|
+
*
|
|
632
|
+
* @template T - Params object
|
|
633
|
+
* @template TEnv - Environment type
|
|
634
|
+
*
|
|
635
|
+
* @example
|
|
636
|
+
* ```typescript
|
|
637
|
+
* const revalidate: Revalidate<{ slug: string }> = ({ currentParams, nextParams }) => {
|
|
638
|
+
* return currentParams.slug !== nextParams.slug;
|
|
639
|
+
* }
|
|
640
|
+
* ```
|
|
641
|
+
*/
|
|
642
|
+
export type Revalidate<
|
|
643
|
+
T = GenericParams,
|
|
644
|
+
TEnv = DefaultEnv,
|
|
645
|
+
> = ShouldRevalidateFn<T, TEnv>;
|
|
646
|
+
|
|
647
|
+
/**
|
|
648
|
+
* Middleware function with typed params and environment
|
|
649
|
+
*
|
|
650
|
+
* @template TParams - Params object (defaults to generic)
|
|
651
|
+
* @template TEnv - Environment type (defaults to global RSCRouter.Env)
|
|
652
|
+
*
|
|
653
|
+
* Note: Middleware cannot directly use route names for params typing because
|
|
654
|
+
* middleware is defined during router setup, before RegisteredRoutes is populated.
|
|
655
|
+
* Use ExtractParams<"/path/:id"> for typed params from a path pattern.
|
|
656
|
+
*
|
|
657
|
+
* @example
|
|
658
|
+
* ```typescript
|
|
659
|
+
* // Basic middleware (uses global RSCRouter.Env via module augmentation)
|
|
660
|
+
* const middleware: Middleware = async (ctx, next) => {
|
|
661
|
+
* ctx.set("user", { id: "123" }); // Type-safe!
|
|
662
|
+
* await next();
|
|
663
|
+
* }
|
|
664
|
+
*
|
|
665
|
+
* // With explicit params (most common)
|
|
666
|
+
* const middleware: Middleware<{ id: string }> = async (ctx, next) => {
|
|
667
|
+
* console.log(ctx.params.id);
|
|
668
|
+
* await next();
|
|
669
|
+
* }
|
|
670
|
+
*
|
|
671
|
+
* // With params from path pattern
|
|
672
|
+
* const middleware: Middleware<ExtractParams<"/products/:id">> = async (ctx, next) => {
|
|
673
|
+
* console.log(ctx.params.id);
|
|
674
|
+
* await next();
|
|
675
|
+
* }
|
|
676
|
+
*
|
|
677
|
+
* // With both params and explicit env
|
|
678
|
+
* const middleware: Middleware<{ id: string }, AppEnv> = async (ctx, next) => {
|
|
679
|
+
* ctx.set("user", { id: ctx.params.id });
|
|
680
|
+
* await next();
|
|
681
|
+
* }
|
|
682
|
+
* ```
|
|
683
|
+
*/
|
|
684
|
+
export type Middleware<
|
|
685
|
+
TParams = GenericParams,
|
|
686
|
+
TEnv = DefaultEnv,
|
|
687
|
+
> = MiddlewareFn<TEnv, TParams>;
|