@rangojs/router 0.0.0-experimental.10 → 0.0.0-experimental.100
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 +1037 -4
- package/dist/bin/rango.js +1619 -157
- package/dist/vite/index.js +5762 -2301
- package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/package.json +71 -63
- package/skills/breadcrumbs/SKILL.md +252 -0
- package/skills/cache-guide/SKILL.md +294 -0
- package/skills/caching/SKILL.md +93 -23
- package/skills/composability/SKILL.md +172 -0
- package/skills/debug-manifest/SKILL.md +12 -8
- package/skills/document-cache/SKILL.md +18 -16
- package/skills/fonts/SKILL.md +6 -4
- package/skills/handler-use/SKILL.md +364 -0
- package/skills/hooks/SKILL.md +367 -71
- package/skills/host-router/SKILL.md +218 -0
- package/skills/i18n/SKILL.md +276 -0
- package/skills/intercept/SKILL.md +176 -8
- package/skills/layout/SKILL.md +124 -3
- package/skills/links/SKILL.md +304 -25
- package/skills/loader/SKILL.md +474 -47
- package/skills/middleware/SKILL.md +207 -37
- package/skills/migrate-nextjs/SKILL.md +562 -0
- package/skills/migrate-react-router/SKILL.md +769 -0
- package/skills/mime-routes/SKILL.md +15 -11
- package/skills/parallel/SKILL.md +272 -1
- package/skills/prerender/SKILL.md +467 -65
- package/skills/rango/SKILL.md +89 -21
- package/skills/response-routes/SKILL.md +152 -91
- package/skills/route/SKILL.md +305 -14
- package/skills/router-setup/SKILL.md +210 -32
- package/skills/server-actions/SKILL.md +739 -0
- package/skills/streams-and-websockets/SKILL.md +283 -0
- package/skills/theme/SKILL.md +9 -8
- package/skills/typesafety/SKILL.md +333 -86
- package/skills/use-cache/SKILL.md +324 -0
- package/skills/view-transitions/SKILL.md +212 -0
- package/src/__internal.ts +102 -4
- package/src/bin/rango.ts +312 -15
- package/src/browser/action-coordinator.ts +97 -0
- package/src/browser/action-response-classifier.ts +99 -0
- package/src/browser/app-shell.ts +52 -0
- package/src/browser/app-version.ts +14 -0
- package/src/browser/event-controller.ts +136 -68
- package/src/browser/history-state.ts +80 -0
- package/src/browser/intercept-utils.ts +52 -0
- package/src/browser/link-interceptor.ts +24 -4
- package/src/browser/logging.ts +55 -0
- package/src/browser/merge-segment-loaders.ts +20 -12
- package/src/browser/navigation-bridge.ts +374 -561
- package/src/browser/navigation-client.ts +228 -70
- package/src/browser/navigation-store.ts +97 -55
- package/src/browser/navigation-transaction.ts +297 -0
- package/src/browser/network-error-handler.ts +61 -0
- package/src/browser/partial-update.ts +376 -315
- package/src/browser/prefetch/cache.ts +314 -0
- package/src/browser/prefetch/fetch.ts +282 -0
- package/src/browser/prefetch/observer.ts +65 -0
- package/src/browser/prefetch/policy.ts +48 -0
- package/src/browser/prefetch/queue.ts +191 -0
- package/src/browser/prefetch/resource-ready.ts +77 -0
- package/src/browser/rango-state.ts +152 -0
- package/src/browser/react/Link.tsx +255 -71
- package/src/browser/react/NavigationProvider.tsx +152 -24
- package/src/browser/react/context.ts +11 -0
- package/src/browser/react/filter-segment-order.ts +55 -0
- package/src/browser/react/index.ts +15 -12
- package/src/browser/react/location-state-shared.ts +95 -53
- package/src/browser/react/location-state.ts +60 -15
- package/src/browser/react/mount-context.ts +6 -1
- 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 +29 -51
- package/src/browser/react/use-client-cache.ts +5 -3
- package/src/browser/react/use-handle.ts +30 -120
- package/src/browser/react/use-link-status.ts +6 -5
- package/src/browser/react/use-navigation.ts +44 -65
- package/src/browser/react/use-params.ts +78 -0
- package/src/browser/react/use-pathname.ts +47 -0
- package/src/browser/react/use-reverse.ts +99 -0
- package/src/browser/react/use-router.ts +83 -0
- package/src/browser/react/use-search-params.ts +56 -0
- package/src/browser/react/use-segments.ts +85 -99
- package/src/browser/response-adapter.ts +73 -0
- package/src/browser/rsc-router.tsx +246 -64
- package/src/browser/scroll-restoration.ts +127 -52
- package/src/browser/segment-reconciler.ts +243 -0
- package/src/browser/segment-structure-assert.ts +16 -0
- package/src/browser/server-action-bridge.ts +510 -603
- package/src/browser/shallow.ts +6 -1
- package/src/browser/types.ts +158 -48
- package/src/browser/validate-redirect-origin.ts +29 -0
- package/src/build/generate-manifest.ts +84 -23
- package/src/build/generate-route-types.ts +39 -828
- package/src/build/index.ts +4 -5
- package/src/build/route-trie.ts +85 -32
- 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 +418 -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 +618 -0
- package/src/build/route-types/scan-filter.ts +85 -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 +342 -0
- package/src/cache/cache-scope.ts +167 -307
- package/src/cache/cf/cf-cache-store.ts +573 -21
- package/src/cache/cf/index.ts +13 -3
- package/src/cache/document-cache.ts +116 -77
- package/src/cache/handle-capture.ts +81 -0
- package/src/cache/handle-snapshot.ts +41 -0
- package/src/cache/index.ts +1 -15
- package/src/cache/memory-segment-store.ts +191 -13
- 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 +153 -0
- package/src/cache/types.ts +72 -122
- package/src/client.rsc.tsx +6 -1
- package/src/client.tsx +118 -302
- package/src/component-utils.ts +4 -4
- package/src/components/DefaultDocument.tsx +5 -1
- package/src/context-var.ts +156 -0
- package/src/debug.ts +19 -9
- package/src/errors.ts +77 -7
- package/src/handle.ts +55 -10
- package/src/handles/MetaTags.tsx +73 -20
- package/src/handles/breadcrumbs.ts +66 -0
- package/src/handles/index.ts +1 -0
- package/src/handles/meta.ts +30 -13
- package/src/host/cookie-handler.ts +21 -15
- package/src/host/errors.ts +8 -8
- package/src/host/index.ts +4 -7
- package/src/host/pattern-matcher.ts +27 -27
- package/src/host/router.ts +61 -39
- package/src/host/testing.ts +8 -8
- package/src/host/types.ts +15 -7
- package/src/host/utils.ts +1 -1
- package/src/href-client.ts +65 -45
- package/src/index.rsc.ts +138 -21
- package/src/index.ts +206 -51
- package/src/internal-debug.ts +11 -0
- package/src/loader.rsc.ts +25 -143
- package/src/loader.ts +27 -10
- package/src/network-error-thrower.tsx +3 -1
- package/src/outlet-context.ts +1 -1
- package/src/outlet-provider.tsx +45 -0
- package/src/prerender/param-hash.ts +4 -2
- package/src/prerender/store.ts +159 -13
- package/src/prerender.ts +397 -29
- package/src/response-utils.ts +28 -0
- package/src/reverse.ts +231 -121
- package/src/root-error-boundary.tsx +41 -29
- package/src/route-content-wrapper.tsx +7 -4
- package/src/route-definition/dsl-helpers.ts +1134 -0
- package/src/route-definition/helper-factories.ts +200 -0
- package/src/route-definition/helpers-types.ts +483 -0
- package/src/route-definition/index.ts +55 -0
- package/src/route-definition/redirect.ts +101 -0
- package/src/route-definition/resolve-handler-use.ts +155 -0
- package/src/route-definition.ts +1 -1431
- package/src/route-map-builder.ts +162 -123
- package/src/route-name.ts +53 -0
- package/src/route-types.ts +66 -9
- package/src/router/content-negotiation.ts +215 -0
- package/src/router/debug-manifest.ts +72 -0
- package/src/router/error-handling.ts +9 -9
- package/src/router/find-match.ts +160 -0
- package/src/router/handler-context.ts +418 -86
- package/src/router/intercept-resolution.ts +35 -20
- package/src/router/lazy-includes.ts +237 -0
- package/src/router/loader-resolution.ts +359 -128
- package/src/router/logging.ts +251 -0
- package/src/router/manifest.ts +98 -32
- package/src/router/match-api.ts +196 -261
- package/src/router/match-context.ts +4 -2
- package/src/router/match-handlers.ts +441 -0
- package/src/router/match-middleware/background-revalidation.ts +108 -93
- package/src/router/match-middleware/cache-lookup.ts +415 -86
- package/src/router/match-middleware/cache-store.ts +91 -29
- package/src/router/match-middleware/intercept-resolution.ts +48 -21
- package/src/router/match-middleware/segment-resolution.ts +73 -9
- package/src/router/match-pipelines.ts +10 -45
- package/src/router/match-result.ts +154 -35
- package/src/router/metrics.ts +240 -15
- package/src/router/middleware-cookies.ts +55 -0
- package/src/router/middleware-types.ts +209 -0
- package/src/router/middleware.ts +373 -371
- package/src/router/navigation-snapshot.ts +182 -0
- package/src/router/pattern-matching.ts +292 -52
- package/src/router/prerender-match.ts +502 -0
- package/src/router/preview-match.ts +98 -0
- package/src/router/request-classification.ts +310 -0
- package/src/router/revalidation.ts +152 -39
- package/src/router/route-snapshot.ts +245 -0
- package/src/router/router-context.ts +41 -21
- package/src/router/router-interfaces.ts +484 -0
- package/src/router/router-options.ts +618 -0
- package/src/router/router-registry.ts +24 -0
- package/src/router/segment-resolution/fresh.ts +756 -0
- package/src/router/segment-resolution/helpers.ts +268 -0
- package/src/router/segment-resolution/loader-cache.ts +199 -0
- package/src/router/segment-resolution/revalidation.ts +1407 -0
- package/src/router/segment-resolution/static-store.ts +67 -0
- package/src/router/segment-resolution.ts +21 -1315
- package/src/router/segment-wrappers.ts +291 -0
- package/src/router/substitute-pattern-params.ts +56 -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 +111 -39
- package/src/router/types.ts +17 -9
- package/src/router/url-params.ts +49 -0
- package/src/router.ts +642 -2011
- package/src/rsc/handler-context.ts +45 -0
- package/src/rsc/handler.ts +864 -1114
- package/src/rsc/helpers.ts +181 -19
- package/src/rsc/index.ts +0 -20
- package/src/rsc/loader-fetch.ts +229 -0
- package/src/rsc/manifest-init.ts +90 -0
- package/src/rsc/nonce.ts +14 -0
- package/src/rsc/origin-guard.ts +141 -0
- package/src/rsc/progressive-enhancement.ts +395 -0
- package/src/rsc/response-error.ts +37 -0
- package/src/rsc/response-route-handler.ts +360 -0
- package/src/rsc/rsc-rendering.ts +256 -0
- package/src/rsc/runtime-warnings.ts +42 -0
- package/src/rsc/server-action.ts +360 -0
- package/src/rsc/ssr-setup.ts +128 -0
- package/src/rsc/types.ts +52 -11
- package/src/search-params.ts +230 -0
- package/src/segment-content-promise.ts +67 -0
- package/src/segment-loader-promise.ts +122 -0
- package/src/segment-system.tsx +187 -38
- package/src/server/context.ts +333 -59
- package/src/server/cookie-store.ts +190 -0
- package/src/server/fetchable-loader-store.ts +37 -0
- package/src/server/handle-store.ts +113 -15
- package/src/server/loader-registry.ts +24 -64
- package/src/server/request-context.ts +603 -109
- package/src/server.ts +35 -155
- package/src/ssr/index.tsx +107 -30
- package/src/static-handler.ts +126 -0
- package/src/theme/ThemeProvider.tsx +21 -15
- package/src/theme/ThemeScript.tsx +5 -5
- package/src/theme/constants.ts +5 -2
- package/src/theme/index.ts +4 -14
- package/src/theme/theme-context.ts +4 -30
- package/src/theme/theme-script.ts +21 -18
- 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 +764 -0
- package/src/types/index.ts +88 -0
- package/src/types/loader-types.ts +209 -0
- package/src/types/request-scope.ts +126 -0
- package/src/types/route-config.ts +170 -0
- package/src/types/route-entry.ts +120 -0
- package/src/types/segments.ts +167 -0
- package/src/types.ts +1 -1757
- package/src/urls/include-helper.ts +207 -0
- package/src/urls/index.ts +53 -0
- package/src/urls/path-helper-types.ts +372 -0
- package/src/urls/path-helper.ts +364 -0
- package/src/urls/pattern-types.ts +107 -0
- package/src/urls/response-types.ts +108 -0
- package/src/urls/type-extraction.ts +372 -0
- package/src/urls/urls-function.ts +98 -0
- package/src/urls.ts +1 -1282
- package/src/use-loader.tsx +161 -81
- package/src/vite/debug.ts +184 -0
- package/src/vite/discovery/bundle-postprocess.ts +181 -0
- package/src/vite/discovery/discover-routers.ts +376 -0
- package/src/vite/discovery/gate-state.ts +171 -0
- package/src/vite/discovery/prerender-collection.ts +486 -0
- package/src/vite/discovery/route-types-writer.ts +258 -0
- package/src/vite/discovery/self-gen-tracking.ts +73 -0
- package/src/vite/discovery/state.ts +117 -0
- package/src/vite/discovery/virtual-module-codegen.ts +203 -0
- package/src/vite/index.ts +15 -2063
- package/src/vite/plugin-types.ts +103 -0
- package/src/vite/plugins/cjs-to-esm.ts +98 -0
- package/src/vite/plugins/client-ref-dedup.ts +131 -0
- package/src/vite/plugins/client-ref-hashing.ts +117 -0
- package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
- package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/src/vite/plugins/cloudflare-protocol-stub.ts +214 -0
- package/src/vite/{expose-action-id.ts → plugins/expose-action-id.ts} +107 -64
- package/src/vite/plugins/expose-id-utils.ts +299 -0
- package/src/vite/plugins/expose-ids/export-analysis.ts +296 -0
- package/src/vite/plugins/expose-ids/handler-transform.ts +209 -0
- package/src/vite/plugins/expose-ids/loader-transform.ts +74 -0
- package/src/vite/plugins/expose-ids/router-transform.ts +127 -0
- package/src/vite/plugins/expose-ids/types.ts +45 -0
- package/src/vite/plugins/expose-internal-ids.ts +816 -0
- package/src/vite/plugins/performance-tracks.ts +96 -0
- package/src/vite/plugins/refresh-cmd.ts +127 -0
- package/src/vite/plugins/use-cache-transform.ts +336 -0
- package/src/vite/plugins/version-injector.ts +109 -0
- package/src/vite/plugins/version-plugin.ts +266 -0
- package/src/vite/{virtual-entries.ts → plugins/virtual-entries.ts} +23 -14
- package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
- package/src/vite/rango.ts +497 -0
- package/src/vite/router-discovery.ts +1423 -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 +161 -0
- package/src/vite/utils/prerender-utils.ts +222 -0
- package/src/vite/utils/shared-utils.ts +170 -0
- package/CLAUDE.md +0 -43
- package/src/browser/lru-cache.ts +0 -69
- package/src/browser/request-controller.ts +0 -164
- package/src/cache/memory-store.ts +0 -253
- package/src/href-context.ts +0 -33
- package/src/router.gen.ts +0 -6
- package/src/urls.gen.ts +0 -8
- package/src/vite/expose-handle-id.ts +0 -209
- package/src/vite/expose-loader-id.ts +0 -426
- package/src/vite/expose-location-state-id.ts +0 -177
- package/src/vite/expose-prerender-handler-id.ts +0 -429
- package/src/vite/package-resolution.ts +0 -125
- /package/src/vite/{version.d.ts → plugins/version.d.ts} +0 -0
package/skills/rango/SKILL.md
CHANGED
|
@@ -10,26 +10,32 @@ Django-inspired RSC router with composable URL patterns, type-safe href, and ser
|
|
|
10
10
|
|
|
11
11
|
## Skills
|
|
12
12
|
|
|
13
|
-
| Skill
|
|
14
|
-
|
|
15
|
-
| `/router-setup`
|
|
16
|
-
| `/route`
|
|
17
|
-
| `/layout`
|
|
18
|
-
| `/loader`
|
|
19
|
-
| `/
|
|
20
|
-
| `/
|
|
21
|
-
| `/
|
|
22
|
-
| `/
|
|
23
|
-
| `/
|
|
24
|
-
| `/
|
|
25
|
-
| `/
|
|
26
|
-
| `/
|
|
27
|
-
| `/
|
|
28
|
-
| `/
|
|
29
|
-
| `/
|
|
30
|
-
| `/
|
|
31
|
-
| `/
|
|
32
|
-
| `/
|
|
13
|
+
| Skill | Description |
|
|
14
|
+
| ----------------------- | -------------------------------------------------------------------------- |
|
|
15
|
+
| `/router-setup` | Create and configure the RSC router |
|
|
16
|
+
| `/route` | Define routes with `urls()` and `path()` |
|
|
17
|
+
| `/layout` | Layouts that wrap child routes |
|
|
18
|
+
| `/loader` | Data loaders with `createLoader()` |
|
|
19
|
+
| `/server-actions` | Mutations with `"use server"`, useActionState, validation, revalidation |
|
|
20
|
+
| `/i18n` | Locale routing with `:locale?`, resolution chains, react-intl integration |
|
|
21
|
+
| `/middleware` | Request processing and authentication |
|
|
22
|
+
| `/intercept` | Modal/slide-over patterns for soft navigation |
|
|
23
|
+
| `/parallel` | Multi-column layouts and sidebars |
|
|
24
|
+
| `/caching` | Segment caching with memory or KV stores |
|
|
25
|
+
| `/use-cache` | Function-level caching with `"use cache"` directive |
|
|
26
|
+
| `/cache-guide` | When to use `cache()` vs `"use cache"` — differences and decision guide |
|
|
27
|
+
| `/document-cache` | Edge caching with Cache-Control headers |
|
|
28
|
+
| `/theme` | Light/dark mode with FOUC prevention |
|
|
29
|
+
| `/links` | URL generation: ctx.reverse, href, useHref, useMount, scopedReverse |
|
|
30
|
+
| `/hooks` | Client-side React hooks |
|
|
31
|
+
| `/typesafety` | Type-safe routes, params, href, and environment |
|
|
32
|
+
| `/host-router` | Multi-app host routing with domain/subdomain patterns |
|
|
33
|
+
| `/tailwind` | Set up Tailwind CSS v4 with `?url` imports |
|
|
34
|
+
| `/response-routes` | JSON/text/HTML/XML/stream endpoints with `path.json()`, `path.text()` |
|
|
35
|
+
| `/mime-routes` | Content negotiation — same URL, different response types via Accept header |
|
|
36
|
+
| `/fonts` | Load web fonts with preload hints |
|
|
37
|
+
| `/migrate-nextjs` | Migrate a Next.js App Router project to Rango |
|
|
38
|
+
| `/migrate-react-router` | Migrate a React Router / Remix project to Rango |
|
|
33
39
|
|
|
34
40
|
## Quick Start
|
|
35
41
|
|
|
@@ -48,7 +54,69 @@ export const urlpatterns = urls(({ path, layout }) => [
|
|
|
48
54
|
import { createRouter } from "@rangojs/router";
|
|
49
55
|
import { urlpatterns } from "./urls";
|
|
50
56
|
|
|
51
|
-
export default createRouter({ document: Document }).
|
|
57
|
+
export default createRouter({ document: Document }).routes(urlpatterns);
|
|
52
58
|
```
|
|
53
59
|
|
|
54
60
|
Use `/typesafety` for type-safe href and environment setup.
|
|
61
|
+
|
|
62
|
+
## CLI: `npx rango generate`
|
|
63
|
+
|
|
64
|
+
Single command to generate `.gen.ts` route type files. Auto-detects file type and
|
|
65
|
+
generates the appropriate output.
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
# Single file
|
|
69
|
+
npx rango generate src/urls.tsx
|
|
70
|
+
|
|
71
|
+
# Multiple files
|
|
72
|
+
npx rango generate src/router.tsx src/urls.tsx
|
|
73
|
+
|
|
74
|
+
# Directory (recursive scan)
|
|
75
|
+
npx rango generate src/
|
|
76
|
+
|
|
77
|
+
# Mix of files and directories
|
|
78
|
+
npx rango generate src/urls.tsx src/api/
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Auto-detection
|
|
82
|
+
|
|
83
|
+
Each file is classified by its contents:
|
|
84
|
+
|
|
85
|
+
| Contains | Generated output |
|
|
86
|
+
| -------------- | ---------------------------------------------------------------- |
|
|
87
|
+
| `urls(` | Per-module `*.gen.ts` with route names, patterns, params, search |
|
|
88
|
+
| `createRouter` | Per-router `*.named-routes.gen.ts` with global route map |
|
|
89
|
+
| Both | Both files |
|
|
90
|
+
|
|
91
|
+
Directories are scanned recursively for `.ts`/`.tsx` files, skipping `node_modules`,
|
|
92
|
+
dotfiles, and existing `.gen.` files.
|
|
93
|
+
|
|
94
|
+
### Recursive includes
|
|
95
|
+
|
|
96
|
+
The generator follows `include()` calls across files, resolving imports to build
|
|
97
|
+
the full route tree. Circular includes are detected and warned about.
|
|
98
|
+
|
|
99
|
+
### First-wins deduplication
|
|
100
|
+
|
|
101
|
+
When a route name appears more than once, the first definition wins and duplicates
|
|
102
|
+
are dropped with a warning. This applies only to the generated `.gen.ts` type files.
|
|
103
|
+
Define the primary route before any fallback variant that reuses the same name.
|
|
104
|
+
|
|
105
|
+
Content negotiation (see `/mime-routes`) is unaffected — negotiated routes use
|
|
106
|
+
distinct names (e.g. `"product"` and `"productJson"`) and the Accept header
|
|
107
|
+
dispatching happens at runtime in the trie, not in the type generator.
|
|
108
|
+
|
|
109
|
+
### Limitations
|
|
110
|
+
|
|
111
|
+
The CLI uses static source analysis (AST walking), not runtime execution. It cannot
|
|
112
|
+
extract routes defined dynamically:
|
|
113
|
+
|
|
114
|
+
- `Array.from()` or `.map()` generating path() calls
|
|
115
|
+
- Conditional routes behind `import.meta.env` or feature flags
|
|
116
|
+
- Routes computed from external data (databases, config files)
|
|
117
|
+
- Template literal patterns with interpolated variables
|
|
118
|
+
|
|
119
|
+
These routes are only discovered by the Vite plugin's runtime discovery during
|
|
120
|
+
`pnpm dev` or `pnpm build`. The CLI-generated `.gen.ts` may have fewer routes
|
|
121
|
+
than the runtime-generated version. During dev, the `preserveIfLarger` guard
|
|
122
|
+
prevents the static parser from overwriting a larger runtime-discovered file.
|
|
@@ -14,7 +14,7 @@ XML feeds, image proxies, and any route that returns a `Response` instead of Rea
|
|
|
14
14
|
Inside any `urls()` callback, use `path.json()`, `path.text()`, or other tags alongside regular RSC routes:
|
|
15
15
|
|
|
16
16
|
```typescript
|
|
17
|
-
import { urls, RouterError } from "@rangojs/router
|
|
17
|
+
import { urls, RouterError } from "@rangojs/router";
|
|
18
18
|
|
|
19
19
|
export const urlpatterns = urls(({ path, layout, include }) => [
|
|
20
20
|
// RSC routes (normal)
|
|
@@ -22,43 +22,62 @@ export const urlpatterns = urls(({ path, layout, include }) => [
|
|
|
22
22
|
path("/about", AboutPage, { name: "about" }),
|
|
23
23
|
|
|
24
24
|
// JSON API route (inline, alongside RSC routes)
|
|
25
|
-
path.json(
|
|
26
|
-
status
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
path.json(
|
|
26
|
+
"/api/status",
|
|
27
|
+
(ctx) => ({
|
|
28
|
+
status: "ok",
|
|
29
|
+
timestamp: Date.now(),
|
|
30
|
+
}),
|
|
31
|
+
{ name: "status" },
|
|
32
|
+
),
|
|
29
33
|
|
|
30
34
|
// Text route
|
|
31
|
-
path.text(
|
|
32
|
-
|
|
33
|
-
|
|
35
|
+
path.text(
|
|
36
|
+
"/robots.txt",
|
|
37
|
+
(ctx) => {
|
|
38
|
+
return "User-agent: *\nAllow: /\nDisallow: /api/\n";
|
|
39
|
+
},
|
|
40
|
+
{ name: "robots" },
|
|
41
|
+
),
|
|
34
42
|
|
|
35
43
|
// Markdown route
|
|
36
|
-
path.md(
|
|
37
|
-
|
|
38
|
-
|
|
44
|
+
path.md(
|
|
45
|
+
"/docs/:slug.md",
|
|
46
|
+
(ctx) => {
|
|
47
|
+
return `# ${ctx.params.slug}\n\nDocumentation content here.`;
|
|
48
|
+
},
|
|
49
|
+
{ name: "docs" },
|
|
50
|
+
),
|
|
39
51
|
|
|
40
52
|
// Response route (full control, returns Response directly)
|
|
41
|
-
path.image(
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
53
|
+
path.image(
|
|
54
|
+
"/og/:slug.png",
|
|
55
|
+
async (ctx) => {
|
|
56
|
+
const image = await generateOgImage(ctx.params.slug);
|
|
57
|
+
return new Response(image, {
|
|
58
|
+
headers: {
|
|
59
|
+
"Content-Type": "image/png",
|
|
60
|
+
"Cache-Control": "public, max-age=86400",
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
},
|
|
64
|
+
{ name: "ogImage" },
|
|
65
|
+
),
|
|
47
66
|
]);
|
|
48
67
|
```
|
|
49
68
|
|
|
50
69
|
## Available Tags
|
|
51
70
|
|
|
52
|
-
| Tag
|
|
53
|
-
|
|
54
|
-
| `json`
|
|
55
|
-
| `text`
|
|
56
|
-
| `html`
|
|
57
|
-
| `xml`
|
|
58
|
-
| `md`
|
|
59
|
-
| `image`
|
|
60
|
-
| `stream` | `path.stream()` | Response
|
|
61
|
-
| `any`
|
|
71
|
+
| Tag | Usage | Handler returns | Auto-wrap |
|
|
72
|
+
| -------- | --------------- | ------------------ | ------------------------ |
|
|
73
|
+
| `json` | `path.json()` | plain object/array | `{ data: T }` envelope |
|
|
74
|
+
| `text` | `path.text()` | string | text/plain Response |
|
|
75
|
+
| `html` | `path.html()` | string | text/html Response |
|
|
76
|
+
| `xml` | `path.xml()` | string | application/xml Response |
|
|
77
|
+
| `md` | `path.md()` | string | text/markdown Response |
|
|
78
|
+
| `image` | `path.image()` | Response | pass-through |
|
|
79
|
+
| `stream` | `path.stream()` | Response | pass-through |
|
|
80
|
+
| `any` | `path.any()` | Response | pass-through |
|
|
62
81
|
|
|
63
82
|
## ResponseHandlerContext
|
|
64
83
|
|
|
@@ -67,14 +86,15 @@ Response route handlers receive a lighter context (no `ctx.use()`, no `ctx.res`)
|
|
|
67
86
|
```typescript
|
|
68
87
|
interface ResponseHandlerContext<TParams, TEnv> {
|
|
69
88
|
request: Request;
|
|
70
|
-
params: TParams;
|
|
71
|
-
env:
|
|
89
|
+
params: TParams; // Typed from URL pattern
|
|
90
|
+
env: TEnv; // Plain bindings (DB, KV, etc.)
|
|
72
91
|
searchParams: URLSearchParams;
|
|
73
92
|
url: URL;
|
|
74
93
|
pathname: string;
|
|
75
|
-
|
|
94
|
+
reverse: (name: string, params?: Record<string, string>) => string;
|
|
95
|
+
get: GetVariableFn; // Read middleware variables
|
|
76
96
|
header: (name: string, value: string) => void;
|
|
77
|
-
|
|
97
|
+
// Use cookies().set(name, value, opts) for cookie mutations (standalone API)
|
|
78
98
|
}
|
|
79
99
|
```
|
|
80
100
|
|
|
@@ -84,31 +104,39 @@ String-returning handlers (json, text, html, xml, md) can set custom headers and
|
|
|
84
104
|
without constructing a full Response:
|
|
85
105
|
|
|
86
106
|
```typescript
|
|
87
|
-
path.md(
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
107
|
+
path.md(
|
|
108
|
+
"/docs/:slug.md",
|
|
109
|
+
(ctx) => {
|
|
110
|
+
ctx.header("Cache-Control", "public, max-age=3600");
|
|
111
|
+
cookies().set("last-doc", ctx.params.slug, { path: "/" });
|
|
112
|
+
return `# ${ctx.params.slug}\n\nContent here.`;
|
|
113
|
+
},
|
|
114
|
+
{ name: "docs" },
|
|
115
|
+
);
|
|
92
116
|
```
|
|
93
117
|
|
|
94
|
-
Headers
|
|
118
|
+
Headers set via `ctx.header()` and cookies set via `cookies().set()` are merged into the
|
|
95
119
|
auto-wrapped Response. If the handler returns a `Response` directly, these are ignored
|
|
96
120
|
(use the Response headers instead).
|
|
97
121
|
|
|
98
|
-
### Environment
|
|
122
|
+
### Environment Access
|
|
99
123
|
|
|
100
|
-
`env`
|
|
124
|
+
`ctx.env` is always the plain bindings passed as TEnv to `createRouter<TEnv>()`:
|
|
101
125
|
|
|
102
126
|
```typescript
|
|
103
|
-
|
|
127
|
+
// createRouter<{ DB: D1Database; KV: KVNamespace }>({ ... })
|
|
104
128
|
|
|
105
129
|
// In a response handler:
|
|
106
|
-
path.json(
|
|
107
|
-
|
|
108
|
-
ctx
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
130
|
+
path.json(
|
|
131
|
+
"/api/data",
|
|
132
|
+
(ctx) => {
|
|
133
|
+
ctx.env.DB; // D1Database (plain bindings)
|
|
134
|
+
ctx.env.KV; // KVNamespace
|
|
135
|
+
// Variables are accessed via ctx.get("key") or ctx.get(ContextVar)
|
|
136
|
+
return { data: "ok" };
|
|
137
|
+
},
|
|
138
|
+
{ name: "data" },
|
|
139
|
+
);
|
|
112
140
|
```
|
|
113
141
|
|
|
114
142
|
## JSON Envelope
|
|
@@ -129,18 +157,24 @@ in a `ResponseEnvelope<T>` discriminated union:
|
|
|
129
157
|
Throw `RouterError` to return structured error envelopes:
|
|
130
158
|
|
|
131
159
|
```typescript
|
|
132
|
-
import { RouterError } from "@rangojs/router
|
|
133
|
-
|
|
134
|
-
path.json(
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
160
|
+
import { RouterError } from "@rangojs/router";
|
|
161
|
+
|
|
162
|
+
path.json(
|
|
163
|
+
"/api/users/:id",
|
|
164
|
+
(ctx) => {
|
|
165
|
+
const user = users.get(ctx.params.id);
|
|
166
|
+
if (!user) {
|
|
167
|
+
throw new RouterError("NOT_FOUND", `User ${ctx.params.id} not found`, {
|
|
168
|
+
status: 404,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
if (!hasPermission(ctx)) {
|
|
172
|
+
throw new RouterError("FORBIDDEN", "Access denied", { status: 403 });
|
|
173
|
+
}
|
|
174
|
+
return user;
|
|
175
|
+
},
|
|
176
|
+
{ name: "user" },
|
|
177
|
+
);
|
|
144
178
|
```
|
|
145
179
|
|
|
146
180
|
### Returning Response Directly
|
|
@@ -148,15 +182,19 @@ path.json("/api/users/:id", (ctx) => {
|
|
|
148
182
|
JSON handlers can return `Response` to bypass auto-wrap (custom status, headers, streaming):
|
|
149
183
|
|
|
150
184
|
```typescript
|
|
151
|
-
path.json(
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
},
|
|
185
|
+
path.json(
|
|
186
|
+
"/api/export",
|
|
187
|
+
(ctx) => {
|
|
188
|
+
const csv = generateCsv();
|
|
189
|
+
return new Response(csv, {
|
|
190
|
+
headers: {
|
|
191
|
+
"Content-Type": "text/csv",
|
|
192
|
+
"Content-Disposition": "attachment; filename=export.csv",
|
|
193
|
+
},
|
|
194
|
+
});
|
|
195
|
+
},
|
|
196
|
+
{ name: "export" },
|
|
197
|
+
);
|
|
160
198
|
```
|
|
161
199
|
|
|
162
200
|
## Client-Side Type Safety
|
|
@@ -188,7 +226,7 @@ if (isResponseError(result)) {
|
|
|
188
226
|
Look up response type from a `path.json()` or `path.text()` module by route name:
|
|
189
227
|
|
|
190
228
|
```typescript
|
|
191
|
-
import type { RouteResponse } from "@rangojs/router
|
|
229
|
+
import type { RouteResponse } from "@rangojs/router";
|
|
192
230
|
|
|
193
231
|
// From the apiPatterns module (before include)
|
|
194
232
|
type HealthData = RouteResponse<typeof apiPatterns, "health">;
|
|
@@ -269,25 +307,36 @@ A self-contained module with RSC pages + JSON APIs, mountable via `include()`:
|
|
|
269
307
|
|
|
270
308
|
```typescript
|
|
271
309
|
// blog/api/urls.tsx
|
|
272
|
-
import { urls, RouterError } from "@rangojs/router
|
|
310
|
+
import { urls, RouterError } from "@rangojs/router";
|
|
273
311
|
|
|
274
312
|
export const blogApiPatterns = urls(({ path }) => [
|
|
275
|
-
path.json(
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
path.json(
|
|
285
|
-
|
|
286
|
-
|
|
313
|
+
path.json(
|
|
314
|
+
"/stats",
|
|
315
|
+
(ctx) => ({
|
|
316
|
+
views: 1200,
|
|
317
|
+
visitors: 450,
|
|
318
|
+
}),
|
|
319
|
+
{ name: "stats" },
|
|
320
|
+
),
|
|
321
|
+
|
|
322
|
+
path.json(
|
|
323
|
+
"/:slug/likes",
|
|
324
|
+
(ctx) => ({
|
|
325
|
+
slug: ctx.params.slug,
|
|
326
|
+
count: 42,
|
|
327
|
+
}),
|
|
328
|
+
{ name: "likes" },
|
|
329
|
+
),
|
|
330
|
+
|
|
331
|
+
path.json(
|
|
332
|
+
"/:slug/comments",
|
|
333
|
+
(ctx) => [{ id: "c1", body: "Great post", author: "alice" }],
|
|
334
|
+
{ name: "comments" },
|
|
335
|
+
),
|
|
287
336
|
]);
|
|
288
337
|
|
|
289
338
|
// blog/urls.tsx
|
|
290
|
-
import { urls } from "@rangojs/router
|
|
339
|
+
import { urls } from "@rangojs/router";
|
|
291
340
|
import { blogApiPatterns } from "./api/urls";
|
|
292
341
|
|
|
293
342
|
export const blogPatterns = urls(({ path, include }) => [
|
|
@@ -299,7 +348,7 @@ export const blogPatterns = urls(({ path, include }) => [
|
|
|
299
348
|
]);
|
|
300
349
|
|
|
301
350
|
// app/urls.tsx
|
|
302
|
-
import { urls } from "@rangojs/router
|
|
351
|
+
import { urls } from "@rangojs/router";
|
|
303
352
|
import { blogPatterns } from "./blog/urls";
|
|
304
353
|
|
|
305
354
|
export const urlpatterns = urls(({ path, include }) => [
|
|
@@ -311,7 +360,7 @@ export const urlpatterns = urls(({ path, include }) => [
|
|
|
311
360
|
### Type safety after mounting
|
|
312
361
|
|
|
313
362
|
```typescript
|
|
314
|
-
import type { RouteResponse } from "@rangojs/router
|
|
363
|
+
import type { RouteResponse } from "@rangojs/router";
|
|
315
364
|
import type { PathResponse, ParamsFor } from "@rangojs/router/client";
|
|
316
365
|
|
|
317
366
|
// Scoped (before mount) -- use the module directly
|
|
@@ -333,13 +382,17 @@ Response route handlers inside a mounted module can reference local names:
|
|
|
333
382
|
|
|
334
383
|
```typescript
|
|
335
384
|
// Inside blogApiPatterns handler
|
|
336
|
-
path(
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
385
|
+
path(
|
|
386
|
+
"/:slug/likes",
|
|
387
|
+
(ctx) => {
|
|
388
|
+
// ctx.reverse resolves names relative to the mount point
|
|
389
|
+
const commentsUrl = ctx.reverse("comments", { slug: ctx.params.slug });
|
|
390
|
+
// -> "/blog/api/my-post/comments"
|
|
391
|
+
|
|
392
|
+
return { slug: ctx.params.slug, count: 42, commentsUrl };
|
|
393
|
+
},
|
|
394
|
+
{ name: "likes" },
|
|
395
|
+
);
|
|
343
396
|
```
|
|
344
397
|
|
|
345
398
|
## Content Negotiation
|
|
@@ -347,6 +400,14 @@ path("/:slug/likes", (ctx) => {
|
|
|
347
400
|
Multiple response types can share the same URL pattern. See `/mime-routes` for the
|
|
348
401
|
full content negotiation API (Accept header matching, Vary: Accept, multi-variant routes).
|
|
349
402
|
|
|
403
|
+
## Long-Lived Responses (SSE / WebSocket)
|
|
404
|
+
|
|
405
|
+
For Server-Sent Events (`path.stream`) and WebSocket upgrades (`path.any`
|
|
406
|
+
returning a 101 / `webSocket` Response), see `/streams-and-websockets`.
|
|
407
|
+
Upgrade responses flow through without reconstruction; `Vary` and
|
|
408
|
+
`Server-Timing` are skipped, and stub headers are applied in place on a
|
|
409
|
+
best-effort basis.
|
|
410
|
+
|
|
350
411
|
## How It Works
|
|
351
412
|
|
|
352
413
|
1. `path.json()` tags the route at the trie level with a MIME type
|