@rangojs/router 0.0.0-experimental.31 → 0.0.0-experimental.3232cd17
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 +4 -0
- package/README.md +198 -44
- package/dist/bin/rango.js +287 -105
- package/dist/testing/vitest.js +82 -0
- package/dist/vite/index.js +3248 -1117
- package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/package.json +73 -21
- package/skills/api-client/SKILL.md +211 -0
- package/skills/breadcrumbs/SKILL.md +107 -1
- package/skills/bundle-analysis/SKILL.md +159 -0
- package/skills/cache-guide/SKILL.md +245 -21
- package/skills/caching/SKILL.md +302 -6
- package/skills/composability/SKILL.md +27 -2
- package/skills/css/SKILL.md +76 -0
- package/skills/document-cache/SKILL.md +78 -55
- package/skills/handler-use/SKILL.md +364 -0
- package/skills/hooks/SKILL.md +270 -30
- package/skills/host-router/SKILL.md +82 -22
- package/skills/i18n/SKILL.md +276 -0
- package/skills/intercept/SKILL.md +49 -5
- package/skills/layout/SKILL.md +35 -9
- package/skills/links/SKILL.md +249 -17
- package/skills/loader/SKILL.md +294 -30
- package/skills/middleware/SKILL.md +52 -13
- package/skills/migrate-nextjs/SKILL.md +584 -0
- package/skills/migrate-react-router/SKILL.md +769 -0
- package/skills/mime-routes/SKILL.md +27 -0
- package/skills/observability/SKILL.md +137 -0
- package/skills/parallel/SKILL.md +203 -7
- package/skills/prerender/SKILL.md +123 -100
- package/skills/rango/SKILL.md +250 -22
- package/skills/react-compiler/SKILL.md +168 -0
- package/skills/response-routes/SKILL.md +122 -47
- package/skills/route/SKILL.md +97 -5
- package/skills/router-setup/SKILL.md +90 -5
- package/skills/server-actions/SKILL.md +775 -0
- package/skills/streams-and-websockets/SKILL.md +283 -0
- package/skills/tailwind/SKILL.md +27 -3
- package/skills/testing/SKILL.md +129 -0
- package/skills/testing/bindings.md +89 -0
- package/skills/testing/cache-prerender.md +124 -0
- package/skills/testing/client-components.md +122 -0
- package/skills/testing/e2e-parity.md +125 -0
- package/skills/testing/flight.md +92 -0
- package/skills/testing/handles.md +129 -0
- package/skills/testing/loader.md +128 -0
- package/skills/testing/middleware.md +99 -0
- package/skills/testing/render-handler.md +121 -0
- package/skills/testing/response-routes.md +95 -0
- package/skills/testing/reverse-and-types.md +84 -0
- package/skills/testing/server-actions.md +107 -0
- package/skills/testing/server-tree.md +128 -0
- package/skills/testing/setup.md +120 -0
- package/skills/typesafety/SKILL.md +329 -27
- package/skills/use-cache/SKILL.md +36 -5
- package/skills/view-transitions/SKILL.md +294 -0
- package/src/__augment-tests__/augment.ts +81 -0
- package/src/__augment-tests__/augmented.check.ts +116 -0
- package/src/__internal.ts +67 -40
- package/src/browser/action-coordinator.ts +53 -36
- package/src/browser/action-fence.ts +47 -0
- package/src/browser/app-shell.ts +39 -0
- package/src/browser/app-version.ts +14 -0
- package/src/browser/cookie-name.ts +140 -0
- package/src/browser/event-controller.ts +86 -147
- package/src/browser/history-state.ts +21 -0
- package/src/browser/index.ts +3 -3
- package/src/browser/invalidate-client-cache.ts +52 -0
- package/src/browser/link-interceptor.ts +4 -0
- package/src/browser/navigation-bridge.ts +148 -19
- package/src/browser/navigation-client.ts +187 -67
- package/src/browser/navigation-store-handle.ts +38 -0
- package/src/browser/navigation-store.ts +76 -67
- package/src/browser/navigation-transaction.ts +18 -66
- package/src/browser/partial-update.ts +123 -94
- package/src/browser/prefetch/cache.ts +214 -36
- package/src/browser/prefetch/fetch.ts +260 -38
- package/src/browser/prefetch/policy.ts +6 -0
- package/src/browser/prefetch/queue.ts +126 -20
- package/src/browser/prefetch/resource-ready.ts +77 -0
- package/src/browser/rango-state.ts +158 -76
- package/src/browser/react/Link.tsx +93 -11
- package/src/browser/react/NavigationProvider.tsx +115 -34
- package/src/browser/react/ScrollRestoration.tsx +10 -6
- package/src/browser/react/context.ts +7 -2
- package/src/browser/react/filter-segment-order.ts +49 -7
- package/src/browser/react/index.ts +0 -48
- package/src/browser/react/location-state-shared.ts +166 -8
- package/src/browser/react/location-state.ts +39 -14
- package/src/browser/react/use-action.ts +6 -15
- package/src/browser/react/use-handle.ts +23 -69
- package/src/browser/react/use-link-status.ts +0 -4
- package/src/browser/react/use-navigation.ts +22 -5
- package/src/browser/react/use-params.ts +20 -10
- package/src/browser/react/use-reverse.ts +106 -0
- package/src/browser/react/use-router.ts +46 -11
- package/src/browser/react/use-search-params.ts +0 -5
- package/src/browser/react/use-segments.ts +11 -21
- package/src/browser/response-adapter.ts +52 -1
- package/src/browser/rsc-router.tsx +215 -76
- package/src/browser/scroll-restoration.ts +46 -39
- package/src/browser/segment-reconciler.ts +36 -9
- package/src/browser/segment-structure-assert.ts +2 -2
- package/src/browser/server-action-bridge.ts +176 -50
- package/src/browser/types.ts +95 -11
- package/src/browser/validate-redirect-origin.ts +43 -16
- package/src/build/collect-fallback-refs.ts +107 -0
- package/src/build/generate-manifest.ts +65 -40
- package/src/build/generate-route-types.ts +5 -0
- package/src/build/index.ts +8 -2
- package/src/build/prefix-tree-utils.ts +123 -0
- package/src/build/route-trie.ts +137 -32
- package/src/build/route-types/codegen.ts +4 -4
- package/src/build/route-types/include-resolution.ts +9 -2
- package/src/build/route-types/param-extraction.ts +6 -3
- package/src/build/route-types/per-module-writer.ts +7 -4
- package/src/build/route-types/router-processing.ts +278 -96
- package/src/build/route-types/scan-filter.ts +9 -2
- package/src/build/route-types/source-scan.ts +118 -0
- package/src/build/runtime-discovery.ts +9 -20
- package/src/cache/cache-error.ts +104 -0
- package/src/cache/cache-policy.ts +68 -28
- package/src/cache/cache-runtime.ts +149 -43
- package/src/cache/cache-scope.ts +148 -81
- package/src/cache/cache-tag.ts +98 -0
- package/src/cache/cf/cf-cache-store.ts +2550 -93
- package/src/cache/cf/index.ts +11 -17
- package/src/cache/document-cache.ts +78 -27
- package/src/cache/handle-snapshot.ts +63 -0
- package/src/cache/index.ts +23 -20
- package/src/cache/memory-segment-store.ts +136 -37
- 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/tag-invalidation.ts +230 -0
- package/src/cache/taint.ts +55 -0
- package/src/cache/types.ts +33 -100
- package/src/cache/vercel/index.ts +11 -0
- package/src/cache/vercel/vercel-cache-store.ts +799 -0
- package/src/client.rsc.tsx +6 -21
- package/src/client.tsx +108 -290
- package/src/component-utils.ts +19 -0
- package/src/context-var.ts +84 -2
- package/src/debug.ts +2 -2
- package/src/decode-loader-results.ts +36 -0
- package/src/defer.ts +196 -0
- package/src/deps/ssr.ts +0 -1
- package/src/errors.ts +30 -4
- package/src/handle.ts +70 -22
- 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 +8 -2
- package/src/host/pattern-matcher.ts +7 -50
- package/src/host/router.ts +107 -99
- package/src/host/testing.ts +40 -27
- package/src/host/types.ts +37 -4
- package/src/host/utils.ts +1 -1
- package/src/href-client.ts +137 -22
- package/src/index.rsc.ts +52 -26
- package/src/index.ts +100 -38
- package/src/internal-debug.ts +2 -4
- package/src/loader-store.ts +500 -0
- package/src/loader.rsc.ts +20 -13
- package/src/loader.ts +12 -11
- package/src/missing-id-error.ts +68 -0
- package/src/network-error-thrower.tsx +1 -6
- package/src/outlet-context.ts +1 -1
- package/src/outlet-provider.tsx +1 -5
- package/src/prerender/param-hash.ts +10 -11
- package/src/prerender/store.ts +37 -41
- package/src/prerender.ts +198 -82
- package/src/redirect-origin.ts +100 -0
- package/src/response-utils.ts +37 -0
- package/src/reverse.ts +65 -15
- package/src/root-error-boundary.tsx +1 -19
- package/src/route-content-wrapper.tsx +7 -72
- package/src/route-definition/dsl-helpers.ts +437 -274
- package/src/route-definition/helper-factories.ts +29 -139
- package/src/route-definition/helpers-types.ts +113 -37
- package/src/route-definition/index.ts +3 -0
- package/src/route-definition/redirect.ts +52 -10
- package/src/route-definition/resolve-handler-use.ts +161 -0
- package/src/route-definition/use-item-types.ts +32 -0
- package/src/route-map-builder.ts +7 -17
- package/src/route-types.ts +37 -41
- package/src/router/basename.ts +14 -0
- package/src/router/content-negotiation.ts +108 -9
- package/src/router/error-handling.ts +13 -17
- package/src/router/find-match.ts +45 -22
- package/src/router/handler-context.ts +83 -41
- package/src/router/intercept-resolution.ts +25 -23
- package/src/router/lazy-includes.ts +19 -53
- package/src/router/loader-resolution.ts +213 -30
- package/src/router/logging.ts +5 -8
- package/src/router/manifest.ts +49 -45
- package/src/router/match-api.ts +121 -205
- package/src/router/match-context.ts +0 -22
- package/src/router/match-handlers.ts +58 -58
- package/src/router/match-middleware/background-revalidation.ts +27 -6
- package/src/router/match-middleware/cache-lookup.ts +205 -249
- package/src/router/match-middleware/cache-store.ts +45 -32
- package/src/router/match-middleware/intercept-resolution.ts +8 -28
- package/src/router/match-middleware/segment-resolution.ts +52 -18
- package/src/router/match-pipelines.ts +1 -42
- package/src/router/match-result.ts +104 -40
- package/src/router/metrics.ts +5 -34
- package/src/router/middleware-types.ts +13 -142
- package/src/router/middleware.ts +173 -143
- package/src/router/navigation-snapshot.ts +131 -0
- package/src/router/params-util.ts +23 -0
- package/src/router/pattern-matching.ts +109 -63
- package/src/router/prerender-match.ts +192 -54
- package/src/router/preview-match.ts +32 -102
- package/src/router/request-classification.ts +276 -0
- package/src/router/revalidation.ts +63 -55
- package/src/router/route-snapshot.ts +244 -0
- package/src/router/router-context.ts +6 -28
- package/src/router/router-interfaces.ts +100 -35
- package/src/router/router-options.ts +91 -11
- package/src/router/router-registry.ts +2 -5
- package/src/router/segment-resolution/fresh.ts +242 -75
- package/src/router/segment-resolution/helpers.ts +64 -25
- package/src/router/segment-resolution/loader-cache.ts +41 -37
- package/src/router/segment-resolution/revalidation.ts +456 -372
- package/src/router/segment-resolution/static-store.ts +19 -5
- package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
- package/src/router/segment-resolution/view-transition-default.ts +36 -0
- package/src/router/segment-resolution.ts +4 -1
- package/src/router/segment-wrappers.ts +2 -3
- package/src/router/state-cookie-name.ts +33 -0
- package/src/router/substitute-pattern-params.ts +56 -0
- package/src/router/telemetry-otel.ts +0 -20
- package/src/router/telemetry.ts +96 -19
- package/src/router/timeout.ts +0 -20
- package/src/router/trie-matching.ts +91 -46
- package/src/router/types.ts +10 -63
- package/src/router/url-params.ts +44 -0
- package/src/router.ts +134 -43
- package/src/rsc/handler-context.ts +3 -2
- package/src/rsc/handler.ts +492 -383
- package/src/rsc/helpers.ts +162 -46
- package/src/rsc/index.ts +1 -1
- package/src/rsc/json-route-result.ts +38 -0
- package/src/rsc/loader-fetch.ts +23 -3
- package/src/rsc/manifest-init.ts +33 -42
- package/src/rsc/origin-guard.ts +39 -25
- package/src/rsc/progressive-enhancement.ts +30 -3
- package/src/rsc/redirect-guard.ts +99 -0
- package/src/rsc/response-error.ts +79 -12
- package/src/rsc/response-route-handler.ts +90 -63
- package/src/rsc/rsc-rendering.ts +56 -54
- package/src/rsc/runtime-warnings.ts +23 -10
- package/src/rsc/server-action.ts +74 -67
- package/src/rsc/ssr-setup.ts +18 -2
- package/src/rsc/types.ts +25 -6
- package/src/runtime-env.ts +18 -0
- package/src/search-params.ts +4 -20
- package/src/segment-content-promise.ts +67 -0
- package/src/segment-loader-promise.ts +134 -0
- package/src/segment-system.tsx +272 -129
- package/src/serialize.ts +243 -0
- package/src/server/context.ts +309 -61
- package/src/server/cookie-store.ts +80 -5
- package/src/server/handle-store.ts +26 -24
- package/src/server/loader-registry.ts +10 -28
- package/src/server/request-context.ts +348 -128
- package/src/ssr/index.tsx +23 -15
- package/src/static-handler.ts +27 -18
- package/src/testing/cache-status.ts +162 -0
- package/src/testing/collect-handle.ts +40 -0
- package/src/testing/dispatch.ts +618 -0
- package/src/testing/dom.entry.ts +22 -0
- package/src/testing/e2e/fixture.ts +188 -0
- package/src/testing/e2e/index.ts +128 -0
- package/src/testing/e2e/matchers.ts +35 -0
- package/src/testing/e2e/page-helpers.ts +272 -0
- package/src/testing/e2e/parity.ts +387 -0
- package/src/testing/e2e/server.ts +195 -0
- package/src/testing/flight-matchers.ts +97 -0
- package/src/testing/flight-normalize.ts +11 -0
- package/src/testing/flight-runtime.d.ts +57 -0
- package/src/testing/flight-tree.ts +682 -0
- package/src/testing/flight.entry.ts +52 -0
- package/src/testing/flight.ts +232 -0
- package/src/testing/generated-routes.ts +183 -0
- package/src/testing/index.ts +99 -0
- package/src/testing/internal/context.ts +348 -0
- package/src/testing/internal/flight-client-globals.ts +30 -0
- package/src/testing/internal/seed-vars.ts +54 -0
- package/src/testing/render-handler.ts +330 -0
- package/src/testing/render-route.tsx +566 -0
- package/src/testing/run-loader.ts +378 -0
- package/src/testing/run-middleware.ts +205 -0
- package/src/testing/vitest-stubs/cloudflare-email.ts +9 -0
- package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
- package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
- package/src/testing/vitest-stubs/version.ts +5 -0
- package/src/testing/vitest.ts +305 -0
- 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/cache-types.ts +17 -8
- package/src/types/error-types.ts +30 -90
- package/src/types/global-namespace.ts +54 -41
- package/src/types/handler-context.ts +233 -81
- package/src/types/index.ts +1 -10
- package/src/types/loader-types.ts +44 -15
- package/src/types/request-scope.ts +107 -0
- package/src/types/route-config.ts +6 -50
- package/src/types/route-entry.ts +19 -7
- package/src/types/segments.ts +37 -14
- package/src/urls/include-helper.ts +33 -70
- package/src/urls/index.ts +1 -11
- package/src/urls/path-helper-types.ts +58 -11
- package/src/urls/path-helper.ts +57 -111
- package/src/urls/pattern-types.ts +48 -19
- package/src/urls/response-types.ts +25 -22
- package/src/urls/type-extraction.ts +58 -139
- package/src/urls/urls-function.ts +1 -18
- package/src/use-loader.tsx +346 -89
- package/src/vite/debug.ts +185 -0
- package/src/vite/discovery/bundle-postprocess.ts +36 -38
- package/src/vite/discovery/discover-routers.ts +130 -85
- package/src/vite/discovery/discovery-errors.ts +194 -0
- package/src/vite/discovery/gate-state.ts +171 -0
- package/src/vite/discovery/prerender-collection.ts +192 -99
- package/src/vite/discovery/route-types-writer.ts +40 -84
- package/src/vite/discovery/self-gen-tracking.ts +27 -1
- package/src/vite/discovery/state.ts +51 -6
- package/src/vite/discovery/virtual-module-codegen.ts +14 -34
- package/src/vite/index.ts +8 -0
- package/src/vite/plugin-types.ts +187 -69
- package/src/vite/plugins/cjs-to-esm.ts +8 -18
- package/src/vite/plugins/client-ref-dedup.ts +16 -11
- package/src/vite/plugins/client-ref-hashing.ts +28 -15
- package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
- package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/src/vite/plugins/cloudflare-protocol-stub.ts +194 -0
- package/src/vite/plugins/expose-action-id.ts +49 -98
- package/src/vite/plugins/expose-id-utils.ts +11 -50
- package/src/vite/plugins/expose-ids/export-analysis.ts +76 -34
- package/src/vite/plugins/expose-ids/handler-transform.ts +10 -48
- package/src/vite/plugins/expose-ids/loader-transform.ts +3 -20
- package/src/vite/plugins/expose-ids/router-transform.ts +20 -16
- package/src/vite/plugins/expose-internal-ids.ts +554 -317
- package/src/vite/plugins/performance-tracks.ts +89 -0
- package/src/vite/plugins/refresh-cmd.ts +89 -27
- package/src/vite/plugins/use-cache-transform.ts +73 -83
- package/src/vite/plugins/vercel-output.ts +258 -0
- package/src/vite/plugins/version-injector.ts +21 -25
- package/src/vite/plugins/version-plugin.ts +41 -20
- package/src/vite/plugins/virtual-entries.ts +2 -17
- package/src/vite/rango.ts +257 -289
- package/src/vite/router-discovery.ts +930 -140
- package/src/vite/utils/ast-handler-extract.ts +15 -31
- package/src/vite/utils/banner.ts +4 -4
- package/src/vite/utils/bundle-analysis.ts +10 -15
- package/src/vite/utils/client-chunks.ts +184 -0
- package/src/vite/utils/forward-user-plugins.ts +171 -0
- package/src/vite/utils/manifest-utils.ts +4 -59
- package/src/vite/utils/package-resolution.ts +20 -52
- package/src/vite/utils/prerender-utils.ts +27 -29
- package/src/vite/utils/shared-utils.ts +92 -42
- package/src/browser/action-response-classifier.ts +0 -99
- package/src/browser/react/use-client-cache.ts +0 -58
- package/src/browser/shallow.ts +0 -40
- package/src/handles/index.ts +0 -7
- package/src/router/middleware-cookies.ts +0 -55
package/AGENTS.md
CHANGED
|
@@ -3,3 +3,7 @@
|
|
|
3
3
|
A file-system based React Server Components router.
|
|
4
4
|
|
|
5
5
|
Run `/rango` to understand the API. Detailed guides for each feature are in the `skills/` directory (e.g. `node_modules/@rangojs/router/skills/loader`, `skills/caching`, `skills/middleware`, etc.).
|
|
6
|
+
|
|
7
|
+
## Development rules
|
|
8
|
+
|
|
9
|
+
- Always commit generated files (e.g. `*.gen.ts`) alongside the source changes that produced them.
|
package/README.md
CHANGED
|
@@ -10,6 +10,7 @@ Named-route RSC router with structural composability and type-safe partial rende
|
|
|
10
10
|
- **Structural composability** — Attach routes, loaders, middleware, handles, caching, prerendering, and static generation without hiding the route tree
|
|
11
11
|
- **Composable URL patterns** — Django-style `urls()` DSL with `path`, `layout`, `include`
|
|
12
12
|
- **Data loaders** — `createLoader()` with automatic streaming and Suspense integration
|
|
13
|
+
- **Server actions** — `"use server"` mutations with `useActionState`, `useOptimistic`, and per-segment + per-loader `revalidate()` rules
|
|
13
14
|
- **Live data layer** — Pre-render or cache the UI shell while loaders stay live by default at request time
|
|
14
15
|
- **Layouts & nesting** — Nested layouts with `<Outlet />` and parallel routes
|
|
15
16
|
- **Segment-level caching** — `cache()` DSL with TTL/SWR and pluggable cache stores
|
|
@@ -91,24 +92,29 @@ This file is a server/RSC module and should import router construction APIs from
|
|
|
91
92
|
|
|
92
93
|
```tsx
|
|
93
94
|
// src/router.tsx
|
|
94
|
-
import { createRouter
|
|
95
|
-
import { Document } from "./document";
|
|
95
|
+
import { createRouter } from "@rangojs/router";
|
|
96
96
|
|
|
97
|
-
const
|
|
98
|
-
path("/",
|
|
99
|
-
path("
|
|
97
|
+
export const router = createRouter().routes(({ path }) => [
|
|
98
|
+
path("/", HomePage, { name: "home" }),
|
|
99
|
+
path("/about", AboutPage, { name: "about" }),
|
|
100
100
|
]);
|
|
101
101
|
|
|
102
|
+
export const reverse = router.reverse;
|
|
103
|
+
// reverse("home") -> "/"
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
For larger apps, extract route modules with `urls()` and compose with `include()`:
|
|
107
|
+
|
|
108
|
+
```tsx
|
|
109
|
+
import { createRouter, urls } from "@rangojs/router";
|
|
110
|
+
import { blogPatterns } from "./urls/blog";
|
|
111
|
+
|
|
102
112
|
const urlpatterns = urls(({ path, include }) => [
|
|
103
113
|
path("/", HomePage, { name: "home" }),
|
|
104
114
|
include("/blog", blogPatterns, { name: "blog" }),
|
|
105
115
|
]);
|
|
106
116
|
|
|
107
|
-
export const router = createRouter(
|
|
108
|
-
|
|
109
|
-
// Export typed reverse function for URL generation by route name
|
|
110
|
-
export const reverse = router.reverse;
|
|
111
|
-
|
|
117
|
+
export const router = createRouter().routes(urlpatterns);
|
|
112
118
|
// reverse("blog.post", { slug: "hello-world" }) -> "/blog/hello-world"
|
|
113
119
|
```
|
|
114
120
|
|
|
@@ -156,13 +162,18 @@ const urlpatterns = urls(({ path }) => [
|
|
|
156
162
|
]);
|
|
157
163
|
```
|
|
158
164
|
|
|
159
|
-
Use `reverse()` as the default way to link to routes:
|
|
165
|
+
Use `ctx.reverse()` from handler context as the default way to link to routes from server code:
|
|
160
166
|
|
|
161
167
|
```tsx
|
|
162
|
-
|
|
163
|
-
|
|
168
|
+
const ProductPage: Handler<"product"> = (ctx) => {
|
|
169
|
+
const url = ctx.reverse("product", { slug: "widget" }); // "/product/widget"
|
|
170
|
+
const searchUrl = ctx.reverse("search", undefined, { q: "rsc" }); // "/search?q=rsc"
|
|
171
|
+
return <Link to={url}>Widget</Link>;
|
|
172
|
+
};
|
|
164
173
|
```
|
|
165
174
|
|
|
175
|
+
`router.reverse()` (exported from the router module) is the same function without a handler context, useful in scripts or tests. In request code, prefer `ctx.reverse()` — it auto-fills mount params from the current match.
|
|
176
|
+
|
|
166
177
|
### Composable URL Modules
|
|
167
178
|
|
|
168
179
|
Local route names compose cleanly with `include(..., { name })`:
|
|
@@ -275,7 +286,8 @@ All handler typing styles are supported, but they solve different problems:
|
|
|
275
286
|
Example of a scoped local name inside a mounted module:
|
|
276
287
|
|
|
277
288
|
```tsx
|
|
278
|
-
import type { Handler
|
|
289
|
+
import type { Handler } from "@rangojs/router";
|
|
290
|
+
import type { ScopedRouteMap } from "@rangojs/router/__internal";
|
|
279
291
|
|
|
280
292
|
type BlogRoutes = ScopedRouteMap<"blog">;
|
|
281
293
|
|
|
@@ -471,41 +483,130 @@ const urlpatterns = urls(({ path, loader }) => [
|
|
|
471
483
|
]);
|
|
472
484
|
```
|
|
473
485
|
|
|
474
|
-
##
|
|
486
|
+
## Server Actions
|
|
475
487
|
|
|
476
|
-
|
|
488
|
+
Server actions are React's RSC mutation primitive. Define them with the
|
|
489
|
+
`"use server"` directive — Rango uses standard React 19 hooks
|
|
490
|
+
(`useActionState`, `useFormStatus`, `useOptimistic`) with no framework wrapper.
|
|
477
491
|
|
|
478
|
-
|
|
492
|
+
```tsx
|
|
493
|
+
// app/actions/cart.ts
|
|
494
|
+
"use server";
|
|
495
|
+
|
|
496
|
+
import { getRequestContext } from "@rangojs/router";
|
|
497
|
+
|
|
498
|
+
export async function addToCart(productId: string): Promise<void> {
|
|
499
|
+
const ctx = getRequestContext();
|
|
500
|
+
const userId = ctx.get("user").id;
|
|
501
|
+
await db.cart.insert({ userId, productId });
|
|
502
|
+
}
|
|
503
|
+
```
|
|
479
504
|
|
|
480
505
|
```tsx
|
|
481
|
-
|
|
482
|
-
|
|
506
|
+
// Client form with progressive enhancement + pending state
|
|
507
|
+
"use client";
|
|
508
|
+
import { useActionState } from "react";
|
|
509
|
+
import { saveProfile } from "../actions/profile";
|
|
483
510
|
|
|
484
|
-
function
|
|
511
|
+
export function ProfileForm() {
|
|
512
|
+
const [state, action, pending] = useActionState(saveProfile, null);
|
|
485
513
|
return (
|
|
486
|
-
<
|
|
487
|
-
<
|
|
488
|
-
<
|
|
489
|
-
<
|
|
490
|
-
</
|
|
514
|
+
<form action={action}>
|
|
515
|
+
<input name="name" defaultValue={state?.values?.name} />
|
|
516
|
+
{state?.errors?.name && <p role="alert">{state.errors.name}</p>}
|
|
517
|
+
<button disabled={pending}>{pending ? "Saving…" : "Save"}</button>
|
|
518
|
+
</form>
|
|
491
519
|
);
|
|
492
520
|
}
|
|
493
521
|
```
|
|
494
522
|
|
|
495
|
-
|
|
523
|
+
After an action runs, matched route segments (path/layout/parallel/intercept)
|
|
524
|
+
and loaders can re-render/re-resolve so the UI reflects the new state.
|
|
525
|
+
Attach a `revalidate(({ actionId }) => ...)` rule on any segment or loader
|
|
526
|
+
that owns data the action touched:
|
|
527
|
+
|
|
528
|
+
```tsx
|
|
529
|
+
urls(({ path, loader, revalidate }) => [
|
|
530
|
+
// Segment-level: re-render the cart page handler after cart actions.
|
|
531
|
+
// Nest loaders that belong to this route inside the same path() so the
|
|
532
|
+
// segment owns its data dependencies.
|
|
533
|
+
path("/cart", CartPage, { name: "cart" }, () => [
|
|
534
|
+
revalidate(
|
|
535
|
+
({ actionId }) => actionId?.startsWith("src/actions/cart.ts#") ?? false,
|
|
536
|
+
),
|
|
537
|
+
loader(CartLoader, () => [
|
|
538
|
+
revalidate(
|
|
539
|
+
({ actionId }) => actionId?.startsWith("src/actions/cart.ts#") ?? false,
|
|
540
|
+
),
|
|
541
|
+
]),
|
|
542
|
+
]),
|
|
543
|
+
]);
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
For the full guide — validation with Zod, error handling, file uploads,
|
|
547
|
+
`useOptimistic`, redirects, and progressive enhancement — see the
|
|
548
|
+
`/server-actions` skill.
|
|
549
|
+
|
|
550
|
+
## Navigation & Links
|
|
496
551
|
|
|
497
|
-
|
|
552
|
+
### Named Routes with `ctx.reverse()` (Server)
|
|
553
|
+
|
|
554
|
+
In server components and handlers, use `ctx.reverse()` to generate URLs by route name. This is the default — it is typed, auto-fills mount params from the current match, and resolves both local (`.name`) and absolute (`name.sub`) names:
|
|
498
555
|
|
|
499
556
|
```tsx
|
|
557
|
+
import { Link } from "@rangojs/router/client";
|
|
558
|
+
import type { Handler } from "@rangojs/router";
|
|
559
|
+
|
|
500
560
|
const BlogPostPage: Handler<"blogPost"> = (ctx) => {
|
|
501
561
|
const backUrl = ctx.reverse("blog");
|
|
502
562
|
return <Link to={backUrl}>Back to blog</Link>;
|
|
503
563
|
};
|
|
504
564
|
```
|
|
505
565
|
|
|
566
|
+
`reverse()` is type-safe — route names and required params are checked at compile time. Included routes use dotted names: `ctx.reverse("api.health")`.
|
|
567
|
+
|
|
568
|
+
For scripts, tests, or other code without a handler context, import the router-level `reverse`:
|
|
569
|
+
|
|
570
|
+
```tsx
|
|
571
|
+
import { reverse } from "./router";
|
|
572
|
+
reverse("blogPost", { slug: "my-post" });
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
### Client Components
|
|
576
|
+
|
|
577
|
+
**`reverse()` is server-only.** It depends on the route manifest and handler context — neither is available in the browser bundle. Client components receive URLs as props, loader data, or server-action return values:
|
|
578
|
+
|
|
579
|
+
```tsx
|
|
580
|
+
// server
|
|
581
|
+
function BlogIndex(ctx: HandlerContext) {
|
|
582
|
+
return (
|
|
583
|
+
<Nav
|
|
584
|
+
home={ctx.reverse("home")}
|
|
585
|
+
post={ctx.reverse("blogPost", { slug: "my-post" })}
|
|
586
|
+
/>
|
|
587
|
+
);
|
|
588
|
+
}
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
```tsx
|
|
592
|
+
"use client";
|
|
593
|
+
import { Link } from "@rangojs/router/client";
|
|
594
|
+
|
|
595
|
+
export function Nav({ home, post }: { home: string; post: string }) {
|
|
596
|
+
return (
|
|
597
|
+
<nav>
|
|
598
|
+
<Link to={home}>Home</Link>
|
|
599
|
+
<Link to={post}>My Post</Link>
|
|
600
|
+
</nav>
|
|
601
|
+
);
|
|
602
|
+
}
|
|
603
|
+
```
|
|
604
|
+
|
|
605
|
+
For client-side navigation to static paths (no named-route lookup), use `href()` — see below. For URLs tied to named routes, you have two options: import the per-module generated `routes` map and use `useReverse(routes)` for in-module names (see [`/links` skill](./skills/links/SKILL.md)), or generate the URL on the server and pass the string in for cross-module URLs.
|
|
606
|
+
|
|
506
607
|
### `href()` for Path Validation (Client Components)
|
|
507
608
|
|
|
508
|
-
In client components, use `href()` for compile-time path validation:
|
|
609
|
+
In client components, use `href()` for compile-time path validation on static path strings:
|
|
509
610
|
|
|
510
611
|
```tsx
|
|
511
612
|
"use client";
|
|
@@ -710,10 +811,12 @@ export const BlogPost = Prerender(
|
|
|
710
811
|
|
|
711
812
|
### Passthrough for Unknown Params
|
|
712
813
|
|
|
814
|
+
Wrap a `Prerender` definition with `Passthrough()` to add a live handler for unknown params at runtime. The build handler runs at build time, the live handler runs at request time for params not in the prerender cache.
|
|
815
|
+
|
|
713
816
|
```tsx
|
|
714
|
-
import { Prerender } from "@rangojs/router";
|
|
817
|
+
import { Prerender, Passthrough } from "@rangojs/router";
|
|
715
818
|
|
|
716
|
-
export const
|
|
819
|
+
export const ProductPageDef = Prerender(
|
|
717
820
|
async () => {
|
|
718
821
|
const featured = await db.getFeaturedProducts();
|
|
719
822
|
return featured.map((p) => ({ id: p.id }));
|
|
@@ -722,16 +825,22 @@ export const ProductPage = Prerender(
|
|
|
722
825
|
const product = await db.getProduct(ctx.params.id);
|
|
723
826
|
return <Product data={product} />;
|
|
724
827
|
},
|
|
725
|
-
{ passthrough: true },
|
|
726
828
|
);
|
|
727
|
-
```
|
|
728
829
|
|
|
729
|
-
|
|
830
|
+
// In route definition:
|
|
831
|
+
path(
|
|
832
|
+
"/products/:id",
|
|
833
|
+
Passthrough(ProductPageDef, async (ctx) => {
|
|
834
|
+
const product = await ctx.env.DB.getProduct(ctx.params.id);
|
|
835
|
+
return <Product data={product} />;
|
|
836
|
+
}),
|
|
837
|
+
);
|
|
838
|
+
```
|
|
730
839
|
|
|
731
|
-
|
|
840
|
+
Build handlers can also skip individual param sets with `ctx.passthrough()`, deferring them to the live handler:
|
|
732
841
|
|
|
733
842
|
```tsx
|
|
734
|
-
export const
|
|
843
|
+
export const ProductPageDef = Prerender(
|
|
735
844
|
async () => {
|
|
736
845
|
const all = await db.getAllProducts();
|
|
737
846
|
return all.map((p) => ({ id: p.id }));
|
|
@@ -741,10 +850,55 @@ export const ProductPage = Prerender(
|
|
|
741
850
|
if (!product.published) return ctx.passthrough();
|
|
742
851
|
return <Product data={product} />;
|
|
743
852
|
},
|
|
744
|
-
{ passthrough: true },
|
|
745
853
|
);
|
|
746
854
|
```
|
|
747
855
|
|
|
856
|
+
### Build-Time Environment Bindings
|
|
857
|
+
|
|
858
|
+
Prerender handlers can access platform bindings (KV, D1, R2) at build time when `buildEnv` is configured in the Vite plugin:
|
|
859
|
+
|
|
860
|
+
```ts
|
|
861
|
+
// vite.config.ts
|
|
862
|
+
import { rango } from "@rangojs/router/vite";
|
|
863
|
+
|
|
864
|
+
rango({ preset: "cloudflare", buildEnv: "auto" });
|
|
865
|
+
```
|
|
866
|
+
|
|
867
|
+
With `buildEnv: "auto"`, the plugin calls `wrangler.getPlatformProxy()` to provide local bindings. Handlers then access `ctx.env` during build:
|
|
868
|
+
|
|
869
|
+
```tsx
|
|
870
|
+
export const BlogPosts = Prerender<{ slug: string }>(
|
|
871
|
+
async (ctx) => {
|
|
872
|
+
const rows = await ctx.env.DB.prepare("SELECT slug FROM posts").all();
|
|
873
|
+
return rows.map((r) => ({ slug: r.slug }));
|
|
874
|
+
},
|
|
875
|
+
async (ctx) => {
|
|
876
|
+
const post = await ctx.env.DB.prepare("SELECT * FROM posts WHERE slug = ?")
|
|
877
|
+
.bind(ctx.params.slug)
|
|
878
|
+
.first();
|
|
879
|
+
return <BlogPost post={post} />;
|
|
880
|
+
},
|
|
881
|
+
);
|
|
882
|
+
```
|
|
883
|
+
|
|
884
|
+
`buildEnv` also accepts a factory function or plain object:
|
|
885
|
+
|
|
886
|
+
```ts
|
|
887
|
+
// Custom factory
|
|
888
|
+
rango({
|
|
889
|
+
buildEnv: async (ctx) => {
|
|
890
|
+
const { getPlatformProxy } = await import("wrangler");
|
|
891
|
+
const proxy = await getPlatformProxy();
|
|
892
|
+
return { env: proxy.env, dispose: proxy.dispose };
|
|
893
|
+
},
|
|
894
|
+
});
|
|
895
|
+
|
|
896
|
+
// Plain object (Node.js)
|
|
897
|
+
rango({ buildEnv: { DATABASE_URL: process.env.DATABASE_URL } });
|
|
898
|
+
```
|
|
899
|
+
|
|
900
|
+
Build-time env applies to both production builds and dev on-demand prerender. Without `buildEnv`, accessing `ctx.env` in a Prerender handler throws with a clear error.
|
|
901
|
+
|
|
748
902
|
## Theme
|
|
749
903
|
|
|
750
904
|
### Router Configuration
|
|
@@ -789,9 +943,9 @@ import { createHostRouter } from "@rangojs/router/host";
|
|
|
789
943
|
|
|
790
944
|
const hostRouter = createHostRouter();
|
|
791
945
|
|
|
792
|
-
hostRouter.host(["*.localhost"]).
|
|
793
|
-
hostRouter.host(["localhost"]).
|
|
794
|
-
hostRouter.fallback().
|
|
946
|
+
hostRouter.host(["*.localhost"]).lazy(() => import("./apps/admin/handler.js"));
|
|
947
|
+
hostRouter.host(["localhost"]).lazy(() => import("./apps/site/handler.js"));
|
|
948
|
+
hostRouter.fallback().lazy(() => import("./apps/site/handler.js"));
|
|
795
949
|
|
|
796
950
|
export default {
|
|
797
951
|
async fetch(request, env, ctx) {
|
|
@@ -800,7 +954,7 @@ export default {
|
|
|
800
954
|
};
|
|
801
955
|
```
|
|
802
956
|
|
|
803
|
-
|
|
957
|
+
Use `.lazy(() => import("./sub-app"))` to mount a lazily-imported sub-app (a module whose `default` export is a handler or nested host router), and `.map((request) => Response)` for an inline request handler. Only `.lazy()` mounts are imported during build-time discovery; `.map(() => import(...))` is a type error. Each sub-app has its own `createRouter()` and `urls()`. Patterns are matched in registration order — register more specific patterns (subdomains) before catch-alls.
|
|
804
958
|
|
|
805
959
|
## Meta Tags
|
|
806
960
|
|
|
@@ -839,16 +993,16 @@ Auto-detects file type:
|
|
|
839
993
|
|
|
840
994
|
## Type Safety
|
|
841
995
|
|
|
842
|
-
The Vite plugin automatically generates a `router.named-routes.gen.ts` file that globally registers route names, patterns, and search schemas via `
|
|
996
|
+
The Vite plugin automatically generates a `router.named-routes.gen.ts` file that globally registers route names, patterns, and search schemas via `Rango.GeneratedRouteMap`. This powers server-side named-route typing such as `Handler<"name">`, `ctx.reverse()`, `getRequestContext().reverse()`, and `RouteParams<"name">` without any manual route registration. The gen file is updated on dev server startup, HMR, and production builds.
|
|
843
997
|
|
|
844
|
-
Use the generated map by default. Augment `
|
|
998
|
+
Use the generated map by default. Augment `Rango.RegisteredRoutes` only when you need the richer `typeof router.routeMap` shape globally, especially for response-aware and path-based utilities.
|
|
845
999
|
|
|
846
1000
|
```typescript
|
|
847
1001
|
// router.tsx
|
|
848
1002
|
const router = createRouter<AppBindings>({}).routes(urlpatterns);
|
|
849
1003
|
|
|
850
1004
|
declare global {
|
|
851
|
-
namespace
|
|
1005
|
+
namespace Rango {
|
|
852
1006
|
interface Env extends AppEnv {}
|
|
853
1007
|
interface Vars extends AppVars {}
|
|
854
1008
|
interface RegisteredRoutes extends typeof router.routeMap {}
|
|
@@ -860,7 +1014,7 @@ Quick rule of thumb:
|
|
|
860
1014
|
|
|
861
1015
|
- `GeneratedRouteMap` (auto-generated) — use for server-side named-route typing: `Handler<"name">`, `ctx.reverse()`, `Prerender<"name">`
|
|
862
1016
|
- `typeof router.routeMap` — use when you need route entries with response metadata
|
|
863
|
-
- `RegisteredRoutes` (manual augmentation) — use to expose `typeof router.routeMap` globally for `href()`, `
|
|
1017
|
+
- `RegisteredRoutes` (manual augmentation) — use to expose `typeof router.routeMap` globally for `href()`, `Rango.Path`, `Rango.PathResponse`, and other path/response-aware utilities
|
|
864
1018
|
|
|
865
1019
|
For extracted reusable loaders or middleware, prefer global dotted names on
|
|
866
1020
|
`ctx.reverse()` by default. If you want type-safe local names for a specific
|