@rangojs/router 0.0.0-experimental.002d056c
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 +9 -0
- package/README.md +899 -0
- package/dist/bin/rango.js +1606 -0
- package/dist/vite/index.js +5153 -0
- package/package.json +177 -0
- package/skills/breadcrumbs/SKILL.md +250 -0
- package/skills/cache-guide/SKILL.md +262 -0
- package/skills/caching/SKILL.md +253 -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 +638 -0
- package/src/browser/navigation-client.ts +261 -0
- package/src/browser/navigation-store.ts +806 -0
- package/src/browser/navigation-transaction.ts +297 -0
- package/src/browser/network-error-handler.ts +61 -0
- package/src/browser/partial-update.ts +582 -0
- package/src/browser/prefetch/cache.ts +206 -0
- package/src/browser/prefetch/fetch.ts +145 -0
- package/src/browser/prefetch/observer.ts +65 -0
- package/src/browser/prefetch/policy.ts +48 -0
- package/src/browser/prefetch/queue.ts +128 -0
- package/src/browser/rango-state.ts +112 -0
- package/src/browser/react/Link.tsx +368 -0
- package/src/browser/react/NavigationProvider.tsx +413 -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 +464 -0
- package/src/browser/scroll-restoration.ts +397 -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 +547 -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 +479 -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 +982 -0
- package/src/cache/cf/index.ts +29 -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 +44 -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 +281 -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 +160 -0
- package/src/router/handler-context.ts +451 -0
- package/src/router/intercept-resolution.ts +397 -0
- package/src/router/lazy-includes.ts +236 -0
- package/src/router/loader-resolution.ts +420 -0
- package/src/router/logging.ts +251 -0
- package/src/router/manifest.ts +269 -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 +193 -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 +749 -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 +320 -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 +1242 -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 +291 -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 +1006 -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 +237 -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 +920 -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 +109 -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 +108 -0
- package/src/vite/discovery/virtual-module-codegen.ts +203 -0
- package/src/vite/index.ts +16 -0
- package/src/vite/plugin-types.ts +48 -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 +363 -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 +266 -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 +445 -0
- package/src/vite/router-discovery.ts +777 -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
package/src/rsc/types.ts
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RSC Handler Types
|
|
3
|
+
*
|
|
4
|
+
* Type definitions for the RSC request handler, payload structures,
|
|
5
|
+
* and SSR integration.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { ResolvedSegment, SlotState } from "../types.js";
|
|
9
|
+
import type { HandleData } from "../server/handle-store.js";
|
|
10
|
+
import type { RSCRouterInternal } from "../router/router-interfaces.js";
|
|
11
|
+
import type { ResolvedThemeConfig, Theme } from "../theme/types.js";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* RSC payload sent to the client.
|
|
15
|
+
* The tree is always reconstructed from metadata.segments by consumers
|
|
16
|
+
* (SSR via renderSegments, browser via renderSegments in bridges).
|
|
17
|
+
*/
|
|
18
|
+
export interface RscPayload {
|
|
19
|
+
metadata?: {
|
|
20
|
+
pathname: string;
|
|
21
|
+
segments: ResolvedSegment[];
|
|
22
|
+
isPartial?: boolean;
|
|
23
|
+
isError?: boolean;
|
|
24
|
+
matched?: string[];
|
|
25
|
+
diff?: string[];
|
|
26
|
+
/** Merged route params from the matched route */
|
|
27
|
+
params?: Record<string, string>;
|
|
28
|
+
slots?: Record<string, SlotState>;
|
|
29
|
+
/** Root layout component for browser-side re-renders (client component reference) */
|
|
30
|
+
rootLayout?: React.ComponentType<{ children: React.ReactNode }>;
|
|
31
|
+
/** Handle data accumulated across route segments (async generator that yields on each push) */
|
|
32
|
+
handles?: AsyncGenerator<HandleData, void, unknown>;
|
|
33
|
+
/** RSC version string for cache invalidation */
|
|
34
|
+
version?: string;
|
|
35
|
+
/** TTL in milliseconds for the client-side in-memory prefetch cache */
|
|
36
|
+
prefetchCacheTTL?: number;
|
|
37
|
+
/** Theme configuration for FOUC prevention */
|
|
38
|
+
themeConfig?: ResolvedThemeConfig | null;
|
|
39
|
+
/** Initial theme from cookie (for SSR hydration) */
|
|
40
|
+
initialTheme?: Theme;
|
|
41
|
+
/** Whether connection warmup is enabled */
|
|
42
|
+
warmupEnabled?: boolean;
|
|
43
|
+
/** Server-side redirect with optional state (for partial requests) */
|
|
44
|
+
redirect?: { url: string };
|
|
45
|
+
/** Server-set location state to include in history.pushState */
|
|
46
|
+
locationState?: Record<string, unknown>;
|
|
47
|
+
};
|
|
48
|
+
returnValue?: { ok: boolean; data: unknown };
|
|
49
|
+
formState?: unknown;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* React form state type for useActionState progressive enhancement
|
|
54
|
+
*/
|
|
55
|
+
export type ReactFormState = unknown;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* RSC dependencies from @vitejs/plugin-rsc/rsc
|
|
59
|
+
*/
|
|
60
|
+
export interface RSCDependencies {
|
|
61
|
+
/**
|
|
62
|
+
* renderToReadableStream from @vitejs/plugin-rsc/rsc
|
|
63
|
+
*/
|
|
64
|
+
renderToReadableStream: <T>(
|
|
65
|
+
payload: T,
|
|
66
|
+
options?: { temporaryReferences?: unknown },
|
|
67
|
+
) => ReadableStream<Uint8Array>;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* decodeReply from @vitejs/plugin-rsc/rsc
|
|
71
|
+
*/
|
|
72
|
+
decodeReply: (
|
|
73
|
+
body: FormData | string,
|
|
74
|
+
options?: { temporaryReferences?: unknown },
|
|
75
|
+
) => Promise<unknown[]>;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* createTemporaryReferenceSet from @vitejs/plugin-rsc/rsc
|
|
79
|
+
*/
|
|
80
|
+
createTemporaryReferenceSet: () => unknown;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* loadServerAction from @vitejs/plugin-rsc/rsc
|
|
84
|
+
*/
|
|
85
|
+
loadServerAction: (actionId: string) => Promise<Function>;
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* decodeAction from @vitejs/plugin-rsc/rsc
|
|
89
|
+
* Decodes a FormData into a bound action function (for useActionState forms)
|
|
90
|
+
*/
|
|
91
|
+
decodeAction: (body: FormData) => Promise<() => Promise<unknown>>;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* decodeFormState from @vitejs/plugin-rsc/rsc
|
|
95
|
+
* Decodes the action result into a ReactFormState for useActionState progressive enhancement
|
|
96
|
+
*/
|
|
97
|
+
decodeFormState: (
|
|
98
|
+
actionResult: unknown,
|
|
99
|
+
body: FormData,
|
|
100
|
+
) => Promise<ReactFormState | null>;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Options for SSR HTML rendering
|
|
105
|
+
*/
|
|
106
|
+
export interface SSRRenderOptions {
|
|
107
|
+
/**
|
|
108
|
+
* Form state for useActionState progressive enhancement.
|
|
109
|
+
* This is the result of decodeFormState() and should be passed to
|
|
110
|
+
* react-dom's renderToReadableStream to enable useActionState to
|
|
111
|
+
* receive the action result during SSR.
|
|
112
|
+
*/
|
|
113
|
+
formState?: ReactFormState | null;
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Nonce for Content Security Policy (CSP)
|
|
117
|
+
*/
|
|
118
|
+
nonce?: string;
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* SSR stream mode.
|
|
122
|
+
*
|
|
123
|
+
* - `"stream"` (default) — start flushing HTML immediately.
|
|
124
|
+
* - `"allReady"` — await `stream.allReady` before returning.
|
|
125
|
+
*/
|
|
126
|
+
streamMode?: import("../router/router-options.js").SSRStreamMode;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* SSR module interface for HTML rendering
|
|
131
|
+
*/
|
|
132
|
+
export interface SSRModule {
|
|
133
|
+
renderHTML: (
|
|
134
|
+
rscStream: ReadableStream<Uint8Array>,
|
|
135
|
+
options?: SSRRenderOptions,
|
|
136
|
+
) => Promise<ReadableStream<Uint8Array>>;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Function to load SSR module dynamically
|
|
141
|
+
*/
|
|
142
|
+
export type LoadSSRModule = () => Promise<SSRModule>;
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Cache configuration for handler.
|
|
146
|
+
* TTL is configured via store.defaults or cache() boundaries.
|
|
147
|
+
*/
|
|
148
|
+
export interface HandlerCacheConfig {
|
|
149
|
+
/** Cache store implementation */
|
|
150
|
+
store: import("../cache/types.js").SegmentCacheStore;
|
|
151
|
+
/** Enable/disable caching (default: true) */
|
|
152
|
+
enabled?: boolean;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Nonce provider function type.
|
|
157
|
+
* Can return a nonce string, or true to auto-generate one.
|
|
158
|
+
*/
|
|
159
|
+
export type NonceProvider<TEnv = unknown> = (
|
|
160
|
+
request: Request,
|
|
161
|
+
env: TEnv,
|
|
162
|
+
) => string | true | Promise<string | true>;
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Options for creating an RSC handler
|
|
166
|
+
*/
|
|
167
|
+
export interface CreateRSCHandlerOptions<
|
|
168
|
+
TEnv = unknown,
|
|
169
|
+
TRoutes extends Record<string, string> = Record<string, string>,
|
|
170
|
+
> {
|
|
171
|
+
/**
|
|
172
|
+
* The RSC router instance
|
|
173
|
+
*/
|
|
174
|
+
router: RSCRouterInternal<TEnv, TRoutes>;
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* RSC dependencies from @vitejs/plugin-rsc/rsc.
|
|
178
|
+
* Defaults to the exports from @vitejs/plugin-rsc/rsc.
|
|
179
|
+
*/
|
|
180
|
+
deps?: RSCDependencies;
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Function to load the SSR module for HTML rendering.
|
|
184
|
+
* Defaults to: () => import.meta.viteRsc.loadModule("ssr", "index")
|
|
185
|
+
*/
|
|
186
|
+
loadSSRModule?: LoadSSRModule;
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Cache configuration for segment caching.
|
|
190
|
+
*
|
|
191
|
+
* Can be a static config object or a function that receives the env
|
|
192
|
+
* (useful for accessing Cloudflare bindings).
|
|
193
|
+
*
|
|
194
|
+
* If not provided, caching is disabled. TTL is configured via store.defaults
|
|
195
|
+
* or cache() boundaries in the route definition.
|
|
196
|
+
*
|
|
197
|
+
* @example Static config
|
|
198
|
+
* ```typescript
|
|
199
|
+
* cache: {
|
|
200
|
+
* store: new MemorySegmentCacheStore({ defaults: { ttl: 60 } }),
|
|
201
|
+
* }
|
|
202
|
+
* ```
|
|
203
|
+
*
|
|
204
|
+
* @example Dynamic config with env
|
|
205
|
+
* ```typescript
|
|
206
|
+
* cache: (env) => ({
|
|
207
|
+
* store: new KVSegmentCacheStore(env.Bindings.MY_CACHE, { defaults: { ttl: 60 } }),
|
|
208
|
+
* })
|
|
209
|
+
* ```
|
|
210
|
+
*/
|
|
211
|
+
cache?: HandlerCacheConfig | ((env: TEnv) => HandlerCacheConfig);
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* RSC version string included in metadata.
|
|
215
|
+
* The browser sends this back on partial requests to detect version mismatches.
|
|
216
|
+
*
|
|
217
|
+
* Defaults to the auto-generated VERSION from `rsc-router:version` virtual module.
|
|
218
|
+
* Only set this if you need a custom versioning strategy.
|
|
219
|
+
*
|
|
220
|
+
* @default VERSION from rsc-router:version
|
|
221
|
+
*/
|
|
222
|
+
version?: string;
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Nonce provider for Content Security Policy (CSP).
|
|
226
|
+
*
|
|
227
|
+
* Can be:
|
|
228
|
+
* - A function that returns a nonce string
|
|
229
|
+
* - A function that returns `true` to auto-generate a nonce
|
|
230
|
+
* - Undefined to disable nonce (default)
|
|
231
|
+
*
|
|
232
|
+
* The nonce will be applied to inline scripts injected by the RSC payload.
|
|
233
|
+
* It's also available to middleware via the typed `nonce` token:
|
|
234
|
+
* `import { nonce } from "@rangojs/router"; ctx.get(nonce)`
|
|
235
|
+
*
|
|
236
|
+
* @example Auto-generate nonce
|
|
237
|
+
* ```tsx
|
|
238
|
+
* createRSCHandler({
|
|
239
|
+
* router,
|
|
240
|
+
* nonce: () => true,
|
|
241
|
+
* });
|
|
242
|
+
* ```
|
|
243
|
+
*
|
|
244
|
+
* @example Custom nonce from request context
|
|
245
|
+
* ```tsx
|
|
246
|
+
* createRSCHandler({
|
|
247
|
+
* router,
|
|
248
|
+
* nonce: (request, env) => env.nonce,
|
|
249
|
+
* });
|
|
250
|
+
* ```
|
|
251
|
+
*
|
|
252
|
+
* @example Access nonce in middleware
|
|
253
|
+
* ```tsx
|
|
254
|
+
* import { nonce } from "@rangojs/router";
|
|
255
|
+
*
|
|
256
|
+
* const cspMiddleware: Middleware = async (ctx, next) => {
|
|
257
|
+
* const value = ctx.get(nonce); // string | undefined
|
|
258
|
+
* await next();
|
|
259
|
+
* };
|
|
260
|
+
* ```
|
|
261
|
+
*/
|
|
262
|
+
nonce?: NonceProvider<TEnv>;
|
|
263
|
+
}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Search parameter schema types and runtime utilities.
|
|
3
|
+
*
|
|
4
|
+
* Provides a lightweight schema system for typed query parameters.
|
|
5
|
+
* When a route defines a `search` schema, ctx.search provides a typed
|
|
6
|
+
* object with parsed values. ctx.searchParams always remains a standard
|
|
7
|
+
* URLSearchParams instance.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
// ============================================================================
|
|
11
|
+
// Schema Types
|
|
12
|
+
// ============================================================================
|
|
13
|
+
|
|
14
|
+
/** Supported scalar types for search params (append ? for optional). */
|
|
15
|
+
export type SearchSchemaValue =
|
|
16
|
+
| "string"
|
|
17
|
+
| "number"
|
|
18
|
+
| "boolean"
|
|
19
|
+
| "string?"
|
|
20
|
+
| "number?"
|
|
21
|
+
| "boolean?";
|
|
22
|
+
|
|
23
|
+
/** A search schema maps param names to their type descriptors. */
|
|
24
|
+
export type SearchSchema = Record<string, SearchSchemaValue>;
|
|
25
|
+
|
|
26
|
+
// ============================================================================
|
|
27
|
+
// Type-Level Schema Resolution
|
|
28
|
+
// ============================================================================
|
|
29
|
+
|
|
30
|
+
/** Strip trailing `?` from a schema value to get the base type. */
|
|
31
|
+
type BaseType<T extends string> = T extends `${infer B}?` ? B : T;
|
|
32
|
+
|
|
33
|
+
/** Map a base type string to its TypeScript type. */
|
|
34
|
+
type ResolveBaseType<T extends string> = T extends "string"
|
|
35
|
+
? string
|
|
36
|
+
: T extends "number"
|
|
37
|
+
? number
|
|
38
|
+
: T extends "boolean"
|
|
39
|
+
? boolean
|
|
40
|
+
: never;
|
|
41
|
+
|
|
42
|
+
/** Keys whose schema value does NOT end with `?`. */
|
|
43
|
+
type RequiredKeys<T extends SearchSchema> = {
|
|
44
|
+
[K in keyof T]: T[K] extends `${string}?` ? never : K;
|
|
45
|
+
}[keyof T];
|
|
46
|
+
|
|
47
|
+
/** Keys whose schema value ends with `?`. */
|
|
48
|
+
type OptionalKeys<T extends SearchSchema> = {
|
|
49
|
+
[K in keyof T]: T[K] extends `${string}?` ? K : never;
|
|
50
|
+
}[keyof T];
|
|
51
|
+
|
|
52
|
+
/** Flatten an intersection type into a single object type. */
|
|
53
|
+
type Simplify<T> = { [K in keyof T]: T[K] };
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Resolve a SearchSchema to its typed object.
|
|
57
|
+
*
|
|
58
|
+
* Both required and optional params resolve to `T | undefined` at the handler
|
|
59
|
+
* level. The required/optional distinction is a consumer-facing contract
|
|
60
|
+
* (e.g., for href() and reverse() autocomplete) — it tells callers which
|
|
61
|
+
* params the route expects, but the handler must still check for undefined
|
|
62
|
+
* since the framework cannot trust the client to send all required params.
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* type S = { q: "string"; page: "number?"; sort: "string?" };
|
|
66
|
+
* type R = ResolveSearchSchema<S>;
|
|
67
|
+
* // { q: string | undefined; page?: number; sort?: string }
|
|
68
|
+
*/
|
|
69
|
+
export type ResolveSearchSchema<T extends SearchSchema> = Simplify<
|
|
70
|
+
{
|
|
71
|
+
[K in RequiredKeys<T> & string]:
|
|
72
|
+
| ResolveBaseType<BaseType<T[K]>>
|
|
73
|
+
| undefined;
|
|
74
|
+
} & {
|
|
75
|
+
[K in OptionalKeys<T> & string]?: ResolveBaseType<BaseType<T[K]>>;
|
|
76
|
+
}
|
|
77
|
+
>;
|
|
78
|
+
|
|
79
|
+
// ============================================================================
|
|
80
|
+
// Route-Level Type Extraction
|
|
81
|
+
// ============================================================================
|
|
82
|
+
|
|
83
|
+
/** Resolve the global route map from RegisteredRoutes or GeneratedRouteMap. */
|
|
84
|
+
type GlobalRouteMap = keyof RSCRouter.RegisteredRoutes extends never
|
|
85
|
+
? keyof RSCRouter.GeneratedRouteMap extends never
|
|
86
|
+
? Record<string, string>
|
|
87
|
+
: RSCRouter.GeneratedRouteMap
|
|
88
|
+
: RSCRouter.RegisteredRoutes;
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Extract the resolved search params type for a named route.
|
|
92
|
+
* Looks up the search schema from the route map and resolves it.
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* ```typescript
|
|
96
|
+
* // Given: path("/search", handler, { name: "search", search: { q: "string", page: "number?" } })
|
|
97
|
+
* type Params = RouteSearchParams<"search">;
|
|
98
|
+
* // { q: string; page?: number }
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
export type RouteSearchParams<TName extends string, TRouteMap = never> = [
|
|
102
|
+
TRouteMap,
|
|
103
|
+
] extends [never]
|
|
104
|
+
? ExtractAndResolveSearch<GlobalRouteMap, TName>
|
|
105
|
+
: ExtractAndResolveSearch<TRouteMap, TName>;
|
|
106
|
+
|
|
107
|
+
type ExtractAndResolveSearch<TRouteMap, TName> = TName extends keyof TRouteMap
|
|
108
|
+
? TRouteMap[TName] extends { readonly search: infer S extends SearchSchema }
|
|
109
|
+
? ResolveSearchSchema<S>
|
|
110
|
+
: {}
|
|
111
|
+
: {};
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Extract the route params type for a named route.
|
|
115
|
+
* Looks up the path pattern from the route map and extracts params.
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* ```typescript
|
|
119
|
+
* // Given: path("/blog/:slug", handler, { name: "blogPost" })
|
|
120
|
+
* type Params = RouteParams<"blogPost">;
|
|
121
|
+
* // { slug: string }
|
|
122
|
+
* ```
|
|
123
|
+
*/
|
|
124
|
+
export type RouteParams<TName extends string, TRouteMap = never> = [
|
|
125
|
+
TRouteMap,
|
|
126
|
+
] extends [never]
|
|
127
|
+
? ExtractRouteParamsFromMap<GlobalRouteMap, TName>
|
|
128
|
+
: ExtractRouteParamsFromMap<TRouteMap, TName>;
|
|
129
|
+
|
|
130
|
+
type ExtractRouteParamsFromMap<TRouteMap, TName> = TName extends keyof TRouteMap
|
|
131
|
+
? TRouteMap[TName] extends string
|
|
132
|
+
? ExtractParamsFromPattern<TRouteMap[TName]>
|
|
133
|
+
: TRouteMap[TName] extends { readonly path: infer P extends string }
|
|
134
|
+
? ExtractParamsFromPattern<P>
|
|
135
|
+
: {}
|
|
136
|
+
: {};
|
|
137
|
+
|
|
138
|
+
/** Parse "a|b|c" into "a" | "b" | "c" */
|
|
139
|
+
type ParseConstraint<T extends string> =
|
|
140
|
+
T extends `${infer First}|${infer Rest}` ? First | ParseConstraint<Rest> : T;
|
|
141
|
+
|
|
142
|
+
/** Minimal inline param extraction (avoids importing from types.ts to prevent circular deps). */
|
|
143
|
+
type ExtractParamsFromPattern<T extends string> =
|
|
144
|
+
T extends `${string}:${infer Param}/${infer Rest}`
|
|
145
|
+
? Param extends `${infer Name}(${infer C})?`
|
|
146
|
+
? {
|
|
147
|
+
[K in Name]?: ParseConstraint<C>;
|
|
148
|
+
} & ExtractParamsFromPattern<`/${Rest}`>
|
|
149
|
+
: Param extends `${infer Name}(${infer C})`
|
|
150
|
+
? {
|
|
151
|
+
[K in Name]: ParseConstraint<C>;
|
|
152
|
+
} & ExtractParamsFromPattern<`/${Rest}`>
|
|
153
|
+
: Param extends `${infer Name}?`
|
|
154
|
+
? { [K in Name]?: string } & ExtractParamsFromPattern<`/${Rest}`>
|
|
155
|
+
: { [K in Param]: string } & ExtractParamsFromPattern<`/${Rest}`>
|
|
156
|
+
: T extends `${string}:${infer Param}`
|
|
157
|
+
? Param extends `${infer Name}(${infer C})?`
|
|
158
|
+
? { [K in Name]?: ParseConstraint<C> }
|
|
159
|
+
: Param extends `${infer Name}(${infer C})`
|
|
160
|
+
? { [K in Name]: ParseConstraint<C> }
|
|
161
|
+
: Param extends `${infer Name}?`
|
|
162
|
+
? { [K in Name]?: string }
|
|
163
|
+
: { [K in Param]: string }
|
|
164
|
+
: {};
|
|
165
|
+
|
|
166
|
+
// ============================================================================
|
|
167
|
+
// Runtime Parser
|
|
168
|
+
// ============================================================================
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Parse URLSearchParams into a typed object using the given schema.
|
|
172
|
+
*
|
|
173
|
+
* - `"string"` / `"string?"` - kept as-is
|
|
174
|
+
* - `"number"` / `"number?"` - coerced via `Number()`; NaN treated as missing
|
|
175
|
+
* - `"boolean"` / `"boolean?"` - `"true"` / `"1"` -> true, `"false"` / `"0"` / `""` -> false
|
|
176
|
+
*
|
|
177
|
+
* Missing params (both required and optional) are omitted from the result
|
|
178
|
+
* (undefined). The required/optional distinction is a consumer-facing contract
|
|
179
|
+
* only — the handler must check for undefined.
|
|
180
|
+
*/
|
|
181
|
+
export function parseSearchParams<T extends SearchSchema>(
|
|
182
|
+
searchParams: URLSearchParams,
|
|
183
|
+
schema: T,
|
|
184
|
+
): ResolveSearchSchema<T> {
|
|
185
|
+
const result: Record<string, unknown> = {};
|
|
186
|
+
|
|
187
|
+
for (const [key, descriptor] of Object.entries(schema)) {
|
|
188
|
+
const isOptional = descriptor.endsWith("?");
|
|
189
|
+
const baseType = isOptional ? descriptor.slice(0, -1) : descriptor;
|
|
190
|
+
const raw = searchParams.get(key);
|
|
191
|
+
|
|
192
|
+
if (raw === null) {
|
|
193
|
+
// Missing params are omitted (undefined) regardless of required/optional
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (baseType === "string") {
|
|
198
|
+
result[key] = raw;
|
|
199
|
+
} else if (baseType === "number") {
|
|
200
|
+
const num = Number(raw);
|
|
201
|
+
if (!Number.isNaN(num)) {
|
|
202
|
+
result[key] = num;
|
|
203
|
+
}
|
|
204
|
+
// NaN treated as missing (undefined)
|
|
205
|
+
} else if (baseType === "boolean") {
|
|
206
|
+
result[key] = raw === "true" || raw === "1";
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return result as ResolveSearchSchema<T>;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// ============================================================================
|
|
214
|
+
// Runtime Serializer
|
|
215
|
+
// ============================================================================
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Serialize a typed search params object to a query string (without leading `?`).
|
|
219
|
+
* Skips `undefined` and `null` values.
|
|
220
|
+
*/
|
|
221
|
+
export function serializeSearchParams(params: Record<string, unknown>): string {
|
|
222
|
+
const parts: string[] = [];
|
|
223
|
+
for (const [key, value] of Object.entries(params)) {
|
|
224
|
+
if (value === undefined || value === null) continue;
|
|
225
|
+
parts.push(
|
|
226
|
+
`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`,
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
return parts.join("&");
|
|
230
|
+
}
|