@rangojs/router 0.0.0-experimental.0f44aca1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +5 -0
- package/README.md +899 -0
- package/dist/bin/rango.js +1601 -0
- package/dist/vite/index.js +5214 -0
- package/package.json +176 -0
- package/skills/breadcrumbs/SKILL.md +250 -0
- package/skills/cache-guide/SKILL.md +262 -0
- package/skills/caching/SKILL.md +220 -0
- package/skills/composability/SKILL.md +172 -0
- package/skills/debug-manifest/SKILL.md +112 -0
- package/skills/document-cache/SKILL.md +182 -0
- package/skills/fonts/SKILL.md +167 -0
- package/skills/hooks/SKILL.md +704 -0
- package/skills/host-router/SKILL.md +218 -0
- package/skills/intercept/SKILL.md +313 -0
- package/skills/layout/SKILL.md +310 -0
- package/skills/links/SKILL.md +239 -0
- package/skills/loader/SKILL.md +596 -0
- package/skills/middleware/SKILL.md +339 -0
- package/skills/mime-routes/SKILL.md +128 -0
- package/skills/parallel/SKILL.md +305 -0
- package/skills/prerender/SKILL.md +643 -0
- package/skills/rango/SKILL.md +118 -0
- package/skills/response-routes/SKILL.md +411 -0
- package/skills/route/SKILL.md +385 -0
- package/skills/router-setup/SKILL.md +439 -0
- package/skills/tailwind/SKILL.md +129 -0
- package/skills/theme/SKILL.md +79 -0
- package/skills/typesafety/SKILL.md +623 -0
- package/skills/use-cache/SKILL.md +324 -0
- package/src/__internal.ts +273 -0
- package/src/bin/rango.ts +321 -0
- package/src/browser/action-coordinator.ts +97 -0
- package/src/browser/action-response-classifier.ts +99 -0
- package/src/browser/event-controller.ts +899 -0
- package/src/browser/history-state.ts +80 -0
- package/src/browser/index.ts +18 -0
- package/src/browser/intercept-utils.ts +52 -0
- package/src/browser/link-interceptor.ts +141 -0
- package/src/browser/logging.ts +55 -0
- package/src/browser/merge-segment-loaders.ts +134 -0
- package/src/browser/navigation-bridge.ts +645 -0
- package/src/browser/navigation-client.ts +215 -0
- package/src/browser/navigation-store.ts +806 -0
- package/src/browser/navigation-transaction.ts +295 -0
- package/src/browser/network-error-handler.ts +61 -0
- package/src/browser/partial-update.ts +550 -0
- package/src/browser/prefetch/cache.ts +146 -0
- package/src/browser/prefetch/fetch.ts +135 -0
- package/src/browser/prefetch/observer.ts +65 -0
- package/src/browser/prefetch/policy.ts +42 -0
- package/src/browser/prefetch/queue.ts +88 -0
- package/src/browser/rango-state.ts +112 -0
- package/src/browser/react/Link.tsx +360 -0
- package/src/browser/react/NavigationProvider.tsx +386 -0
- package/src/browser/react/ScrollRestoration.tsx +94 -0
- package/src/browser/react/context.ts +59 -0
- package/src/browser/react/filter-segment-order.ts +11 -0
- package/src/browser/react/index.ts +52 -0
- package/src/browser/react/location-state-shared.ts +162 -0
- package/src/browser/react/location-state.ts +107 -0
- package/src/browser/react/mount-context.ts +37 -0
- package/src/browser/react/nonce-context.ts +23 -0
- package/src/browser/react/shallow-equal.ts +27 -0
- package/src/browser/react/use-action.ts +218 -0
- package/src/browser/react/use-client-cache.ts +58 -0
- package/src/browser/react/use-handle.ts +162 -0
- package/src/browser/react/use-href.tsx +40 -0
- package/src/browser/react/use-link-status.ts +135 -0
- package/src/browser/react/use-mount.ts +31 -0
- package/src/browser/react/use-navigation.ts +99 -0
- package/src/browser/react/use-params.ts +65 -0
- package/src/browser/react/use-pathname.ts +47 -0
- package/src/browser/react/use-router.ts +63 -0
- package/src/browser/react/use-search-params.ts +56 -0
- package/src/browser/react/use-segments.ts +171 -0
- package/src/browser/response-adapter.ts +73 -0
- package/src/browser/rsc-router.tsx +431 -0
- package/src/browser/scroll-restoration.ts +400 -0
- package/src/browser/segment-reconciler.ts +216 -0
- package/src/browser/segment-structure-assert.ts +83 -0
- package/src/browser/server-action-bridge.ts +667 -0
- package/src/browser/shallow.ts +40 -0
- package/src/browser/types.ts +538 -0
- package/src/browser/validate-redirect-origin.ts +29 -0
- package/src/build/generate-manifest.ts +438 -0
- package/src/build/generate-route-types.ts +36 -0
- package/src/build/index.ts +35 -0
- package/src/build/route-trie.ts +265 -0
- package/src/build/route-types/ast-helpers.ts +25 -0
- package/src/build/route-types/ast-route-extraction.ts +98 -0
- package/src/build/route-types/codegen.ts +102 -0
- package/src/build/route-types/include-resolution.ts +411 -0
- package/src/build/route-types/param-extraction.ts +48 -0
- package/src/build/route-types/per-module-writer.ts +128 -0
- package/src/build/route-types/router-processing.ts +469 -0
- package/src/build/route-types/scan-filter.ts +78 -0
- package/src/build/runtime-discovery.ts +231 -0
- package/src/cache/background-task.ts +34 -0
- package/src/cache/cache-key-utils.ts +44 -0
- package/src/cache/cache-policy.ts +125 -0
- package/src/cache/cache-runtime.ts +338 -0
- package/src/cache/cache-scope.ts +382 -0
- package/src/cache/cf/cf-cache-store.ts +540 -0
- package/src/cache/cf/index.ts +25 -0
- package/src/cache/document-cache.ts +369 -0
- package/src/cache/handle-capture.ts +81 -0
- package/src/cache/handle-snapshot.ts +41 -0
- package/src/cache/index.ts +43 -0
- package/src/cache/memory-segment-store.ts +328 -0
- package/src/cache/profile-registry.ts +73 -0
- package/src/cache/read-through-swr.ts +134 -0
- package/src/cache/segment-codec.ts +256 -0
- package/src/cache/taint.ts +98 -0
- package/src/cache/types.ts +342 -0
- package/src/client.rsc.tsx +85 -0
- package/src/client.tsx +601 -0
- package/src/component-utils.ts +76 -0
- package/src/components/DefaultDocument.tsx +27 -0
- package/src/context-var.ts +86 -0
- package/src/debug.ts +243 -0
- package/src/default-error-boundary.tsx +88 -0
- package/src/deps/browser.ts +8 -0
- package/src/deps/html-stream-client.ts +2 -0
- package/src/deps/html-stream-server.ts +2 -0
- package/src/deps/rsc.ts +10 -0
- package/src/deps/ssr.ts +2 -0
- package/src/errors.ts +365 -0
- package/src/handle.ts +135 -0
- package/src/handles/MetaTags.tsx +246 -0
- package/src/handles/breadcrumbs.ts +66 -0
- package/src/handles/index.ts +7 -0
- package/src/handles/meta.ts +264 -0
- package/src/host/cookie-handler.ts +165 -0
- package/src/host/errors.ts +97 -0
- package/src/host/index.ts +53 -0
- package/src/host/pattern-matcher.ts +214 -0
- package/src/host/router.ts +352 -0
- package/src/host/testing.ts +79 -0
- package/src/host/types.ts +146 -0
- package/src/host/utils.ts +25 -0
- package/src/href-client.ts +222 -0
- package/src/index.rsc.ts +233 -0
- package/src/index.ts +277 -0
- package/src/internal-debug.ts +11 -0
- package/src/loader.rsc.ts +89 -0
- package/src/loader.ts +64 -0
- package/src/network-error-thrower.tsx +23 -0
- package/src/outlet-context.ts +15 -0
- package/src/outlet-provider.tsx +45 -0
- package/src/prerender/param-hash.ts +37 -0
- package/src/prerender/store.ts +185 -0
- package/src/prerender.ts +463 -0
- package/src/reverse.ts +330 -0
- package/src/root-error-boundary.tsx +289 -0
- package/src/route-content-wrapper.tsx +196 -0
- package/src/route-definition/dsl-helpers.ts +934 -0
- package/src/route-definition/helper-factories.ts +200 -0
- package/src/route-definition/helpers-types.ts +430 -0
- package/src/route-definition/index.ts +52 -0
- package/src/route-definition/redirect.ts +93 -0
- package/src/route-definition.ts +1 -0
- package/src/route-map-builder.ts +275 -0
- package/src/route-name.ts +53 -0
- package/src/route-types.ts +259 -0
- package/src/router/content-negotiation.ts +116 -0
- package/src/router/debug-manifest.ts +72 -0
- package/src/router/error-handling.ts +287 -0
- package/src/router/find-match.ts +158 -0
- package/src/router/handler-context.ts +451 -0
- package/src/router/intercept-resolution.ts +395 -0
- package/src/router/lazy-includes.ts +234 -0
- package/src/router/loader-resolution.ts +420 -0
- package/src/router/logging.ts +248 -0
- package/src/router/manifest.ts +267 -0
- package/src/router/match-api.ts +620 -0
- package/src/router/match-context.ts +266 -0
- package/src/router/match-handlers.ts +440 -0
- package/src/router/match-middleware/background-revalidation.ts +223 -0
- package/src/router/match-middleware/cache-lookup.ts +634 -0
- package/src/router/match-middleware/cache-store.ts +295 -0
- package/src/router/match-middleware/index.ts +81 -0
- package/src/router/match-middleware/intercept-resolution.ts +306 -0
- package/src/router/match-middleware/segment-resolution.ts +192 -0
- package/src/router/match-pipelines.ts +179 -0
- package/src/router/match-result.ts +219 -0
- package/src/router/metrics.ts +282 -0
- package/src/router/middleware-cookies.ts +55 -0
- package/src/router/middleware-types.ts +222 -0
- package/src/router/middleware.ts +748 -0
- package/src/router/pattern-matching.ts +563 -0
- package/src/router/prerender-match.ts +402 -0
- package/src/router/preview-match.ts +170 -0
- package/src/router/revalidation.ts +289 -0
- package/src/router/router-context.ts +316 -0
- package/src/router/router-interfaces.ts +452 -0
- package/src/router/router-options.ts +592 -0
- package/src/router/router-registry.ts +24 -0
- package/src/router/segment-resolution/fresh.ts +570 -0
- package/src/router/segment-resolution/helpers.ts +263 -0
- package/src/router/segment-resolution/loader-cache.ts +198 -0
- package/src/router/segment-resolution/revalidation.ts +1239 -0
- package/src/router/segment-resolution/static-store.ts +67 -0
- package/src/router/segment-resolution.ts +21 -0
- package/src/router/segment-wrappers.ts +289 -0
- package/src/router/telemetry-otel.ts +299 -0
- package/src/router/telemetry.ts +300 -0
- package/src/router/timeout.ts +148 -0
- package/src/router/trie-matching.ts +239 -0
- package/src/router/types.ts +170 -0
- package/src/router.ts +1002 -0
- package/src/rsc/handler-context.ts +45 -0
- package/src/rsc/handler.ts +1089 -0
- package/src/rsc/helpers.ts +198 -0
- package/src/rsc/index.ts +36 -0
- package/src/rsc/loader-fetch.ts +209 -0
- package/src/rsc/manifest-init.ts +86 -0
- package/src/rsc/nonce.ts +32 -0
- package/src/rsc/origin-guard.ts +141 -0
- package/src/rsc/progressive-enhancement.ts +379 -0
- package/src/rsc/response-error.ts +37 -0
- package/src/rsc/response-route-handler.ts +347 -0
- package/src/rsc/rsc-rendering.ts +235 -0
- package/src/rsc/runtime-warnings.ts +42 -0
- package/src/rsc/server-action.ts +348 -0
- package/src/rsc/ssr-setup.ts +128 -0
- package/src/rsc/types.ts +263 -0
- package/src/search-params.ts +230 -0
- package/src/segment-system.tsx +454 -0
- package/src/server/context.ts +591 -0
- package/src/server/cookie-store.ts +190 -0
- package/src/server/fetchable-loader-store.ts +37 -0
- package/src/server/handle-store.ts +308 -0
- package/src/server/loader-registry.ts +133 -0
- package/src/server/request-context.ts +914 -0
- package/src/server/root-layout.tsx +10 -0
- package/src/server/tsconfig.json +14 -0
- package/src/server.ts +51 -0
- package/src/ssr/index.tsx +365 -0
- package/src/static-handler.ts +114 -0
- package/src/theme/ThemeProvider.tsx +297 -0
- package/src/theme/ThemeScript.tsx +61 -0
- package/src/theme/constants.ts +62 -0
- package/src/theme/index.ts +48 -0
- package/src/theme/theme-context.ts +44 -0
- package/src/theme/theme-script.ts +155 -0
- package/src/theme/types.ts +182 -0
- package/src/theme/use-theme.ts +44 -0
- package/src/types/boundaries.ts +158 -0
- package/src/types/cache-types.ts +198 -0
- package/src/types/error-types.ts +192 -0
- package/src/types/global-namespace.ts +100 -0
- package/src/types/handler-context.ts +687 -0
- package/src/types/index.ts +88 -0
- package/src/types/loader-types.ts +183 -0
- package/src/types/route-config.ts +170 -0
- package/src/types/route-entry.ts +102 -0
- package/src/types/segments.ts +148 -0
- package/src/types.ts +1 -0
- package/src/urls/include-helper.ts +197 -0
- package/src/urls/index.ts +53 -0
- package/src/urls/path-helper-types.ts +339 -0
- package/src/urls/path-helper.ts +329 -0
- package/src/urls/pattern-types.ts +95 -0
- package/src/urls/response-types.ts +106 -0
- package/src/urls/type-extraction.ts +372 -0
- package/src/urls/urls-function.ts +98 -0
- package/src/urls.ts +1 -0
- package/src/use-loader.tsx +354 -0
- package/src/vite/discovery/bundle-postprocess.ts +184 -0
- package/src/vite/discovery/discover-routers.ts +344 -0
- package/src/vite/discovery/prerender-collection.ts +385 -0
- package/src/vite/discovery/route-types-writer.ts +258 -0
- package/src/vite/discovery/self-gen-tracking.ts +47 -0
- package/src/vite/discovery/state.ts +110 -0
- package/src/vite/discovery/virtual-module-codegen.ts +203 -0
- package/src/vite/index.ts +16 -0
- package/src/vite/plugin-types.ts +131 -0
- package/src/vite/plugins/cjs-to-esm.ts +93 -0
- package/src/vite/plugins/client-ref-dedup.ts +115 -0
- package/src/vite/plugins/client-ref-hashing.ts +105 -0
- package/src/vite/plugins/expose-action-id.ts +365 -0
- package/src/vite/plugins/expose-id-utils.ts +287 -0
- package/src/vite/plugins/expose-ids/export-analysis.ts +296 -0
- package/src/vite/plugins/expose-ids/handler-transform.ts +179 -0
- package/src/vite/plugins/expose-ids/loader-transform.ts +74 -0
- package/src/vite/plugins/expose-ids/router-transform.ts +110 -0
- package/src/vite/plugins/expose-ids/types.ts +45 -0
- package/src/vite/plugins/expose-internal-ids.ts +569 -0
- package/src/vite/plugins/refresh-cmd.ts +65 -0
- package/src/vite/plugins/use-cache-transform.ts +323 -0
- package/src/vite/plugins/version-injector.ts +83 -0
- package/src/vite/plugins/version-plugin.ts +254 -0
- package/src/vite/plugins/version.d.ts +12 -0
- package/src/vite/plugins/virtual-entries.ts +123 -0
- package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
- package/src/vite/rango.ts +510 -0
- package/src/vite/router-discovery.ts +785 -0
- package/src/vite/utils/ast-handler-extract.ts +517 -0
- package/src/vite/utils/banner.ts +36 -0
- package/src/vite/utils/bundle-analysis.ts +137 -0
- package/src/vite/utils/manifest-utils.ts +70 -0
- package/src/vite/utils/package-resolution.ts +121 -0
- package/src/vite/utils/prerender-utils.ts +189 -0
- package/src/vite/utils/shared-utils.ts +169 -0
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: router-setup
|
|
3
|
+
description: Create and configure the RSC router with createRouter
|
|
4
|
+
argument-hint: [option]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Router Setup with createRouter
|
|
8
|
+
|
|
9
|
+
## Basic Router Creation
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
// src/router.tsx
|
|
13
|
+
import { createRouter } from "@rangojs/router";
|
|
14
|
+
import { Document } from "./document";
|
|
15
|
+
import { urlpatterns } from "./urls";
|
|
16
|
+
|
|
17
|
+
const router = createRouter({
|
|
18
|
+
document: Document,
|
|
19
|
+
urls: urlpatterns,
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
export default router;
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## URL Patterns (Django-style)
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
// src/urls.tsx
|
|
29
|
+
import { urls } from "@rangojs/router";
|
|
30
|
+
import { HomePage } from "./pages/home";
|
|
31
|
+
import { AboutPage } from "./pages/about";
|
|
32
|
+
import { ProductPage } from "./pages/product";
|
|
33
|
+
import { RootLayout } from "./layouts/RootLayout";
|
|
34
|
+
|
|
35
|
+
export const urlpatterns = urls(({ path, layout, loader, loading }) => [
|
|
36
|
+
path("/", HomePage, { name: "home" }),
|
|
37
|
+
path("/about", AboutPage, { name: "about" }),
|
|
38
|
+
|
|
39
|
+
layout(<RootLayout />, () => [
|
|
40
|
+
path("/product/:slug", ProductPage, { name: "product" }, () => [
|
|
41
|
+
loader(ProductLoader),
|
|
42
|
+
loading(<ProductSkeleton />),
|
|
43
|
+
]),
|
|
44
|
+
]),
|
|
45
|
+
]);
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## The urls() DSL
|
|
49
|
+
|
|
50
|
+
The `urls()` function provides a callback with all available DSL functions:
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
urls(
|
|
54
|
+
({
|
|
55
|
+
path, // Define a route
|
|
56
|
+
layout, // Wrap routes in a layout
|
|
57
|
+
parallel, // Define parallel routes (slots)
|
|
58
|
+
loader, // Add data loader
|
|
59
|
+
loading, // Add loading skeleton
|
|
60
|
+
cache, // Configure caching
|
|
61
|
+
middleware, // Add middleware
|
|
62
|
+
revalidate, // Control revalidation
|
|
63
|
+
intercept, // Intercept routes for modals
|
|
64
|
+
when, // Conditional rendering
|
|
65
|
+
}) => [
|
|
66
|
+
// Route definitions here
|
|
67
|
+
],
|
|
68
|
+
);
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Router Options
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
interface RSCRouterOptions<TEnv> {
|
|
75
|
+
// URL patterns from urls() function
|
|
76
|
+
urls: UrlPatterns;
|
|
77
|
+
|
|
78
|
+
// Document component wrapping entire app
|
|
79
|
+
document?: ComponentType<{ children: ReactNode }>;
|
|
80
|
+
|
|
81
|
+
// Enable per-request performance timeline (console waterfall + Server-Timing header)
|
|
82
|
+
debugPerformance?: boolean;
|
|
83
|
+
|
|
84
|
+
// Default error boundary
|
|
85
|
+
defaultErrorBoundary?: ReactNode | ErrorBoundaryHandler;
|
|
86
|
+
|
|
87
|
+
// Default not-found boundary
|
|
88
|
+
defaultNotFoundBoundary?: ReactNode | NotFoundBoundaryHandler;
|
|
89
|
+
|
|
90
|
+
// Component for 404 routes
|
|
91
|
+
notFound?: ReactNode | ((props: { pathname: string }) => ReactNode);
|
|
92
|
+
|
|
93
|
+
// Error logging callback
|
|
94
|
+
onError?: OnErrorCallback<TEnv>;
|
|
95
|
+
|
|
96
|
+
// Global cache configuration
|
|
97
|
+
cache?: CacheConfig<TEnv>;
|
|
98
|
+
|
|
99
|
+
// Theme configuration
|
|
100
|
+
theme?: ThemeConfig | true;
|
|
101
|
+
|
|
102
|
+
// SSR options (streaming policy)
|
|
103
|
+
ssr?: SSROptions<TEnv>;
|
|
104
|
+
|
|
105
|
+
// Telemetry sink for structured lifecycle events
|
|
106
|
+
telemetry?: TelemetrySink;
|
|
107
|
+
|
|
108
|
+
// Connection warmup (default: true)
|
|
109
|
+
warmup?: boolean;
|
|
110
|
+
|
|
111
|
+
// Prefetch cache TTL in seconds (default: 300)
|
|
112
|
+
// Controls in-memory cache duration and Cache-Control max-age for prefetch responses.
|
|
113
|
+
// Set to false to disable prefetch caching.
|
|
114
|
+
prefetchCacheTTL?: number | false;
|
|
115
|
+
|
|
116
|
+
// CSP nonce provider (for router.fetch)
|
|
117
|
+
nonce?: (
|
|
118
|
+
request: Request,
|
|
119
|
+
env: TEnv,
|
|
120
|
+
) => string | true | Promise<string | true>;
|
|
121
|
+
|
|
122
|
+
// RSC version string (for router.fetch)
|
|
123
|
+
version?: string;
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Using the Request Handler
|
|
128
|
+
|
|
129
|
+
The router provides a `fetch` method to handle RSC requests:
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
// src/router.tsx
|
|
133
|
+
import { createRouter } from "@rangojs/router";
|
|
134
|
+
import { Document } from "./document";
|
|
135
|
+
import { urlpatterns } from "./urls";
|
|
136
|
+
|
|
137
|
+
export const router = createRouter({
|
|
138
|
+
document: Document,
|
|
139
|
+
urls: urlpatterns,
|
|
140
|
+
nonce: () => true, // Auto-generate nonce for CSP
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// src/worker.tsx (Cloudflare Workers)
|
|
144
|
+
import { router } from "./router";
|
|
145
|
+
|
|
146
|
+
export default { fetch: router.fetch };
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Document Component
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
// src/document.tsx
|
|
153
|
+
import type { ReactNode } from "react";
|
|
154
|
+
|
|
155
|
+
export function Document({ children }: { children: ReactNode }) {
|
|
156
|
+
return (
|
|
157
|
+
<html lang="en">
|
|
158
|
+
<head>
|
|
159
|
+
<meta charSet="utf-8" />
|
|
160
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
161
|
+
<title>My App</title>
|
|
162
|
+
</head>
|
|
163
|
+
<body>
|
|
164
|
+
<div id="root">{children}</div>
|
|
165
|
+
</body>
|
|
166
|
+
</html>
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Using with Cloudflare Workers
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
// src/router.tsx
|
|
175
|
+
import { createRouter } from "@rangojs/router";
|
|
176
|
+
import { Document } from "./document";
|
|
177
|
+
import { urlpatterns } from "./urls";
|
|
178
|
+
|
|
179
|
+
export const router = createRouter<AppBindings>({
|
|
180
|
+
document: Document,
|
|
181
|
+
urls: urlpatterns,
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
// src/worker.tsx
|
|
185
|
+
import { router } from "./router";
|
|
186
|
+
|
|
187
|
+
export default {
|
|
188
|
+
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
|
|
189
|
+
return router.fetch(request, { env, ctx });
|
|
190
|
+
},
|
|
191
|
+
};
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### With Dynamic Cache Configuration
|
|
195
|
+
|
|
196
|
+
For per-request cache configuration (e.g., Cloudflare Workers with ExecutionContext):
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
// src/router.tsx
|
|
200
|
+
import { createRouter } from "@rangojs/router";
|
|
201
|
+
import { CFCacheStore } from "@rangojs/router/cache";
|
|
202
|
+
|
|
203
|
+
export const router = createRouter<AppBindings>({
|
|
204
|
+
document: Document,
|
|
205
|
+
urls: urlpatterns,
|
|
206
|
+
// Cache config receives (env, ctx) separately
|
|
207
|
+
cache: (_env, ctx) => ({
|
|
208
|
+
store: new CFCacheStore({ ctx: ctx!, defaults: { ttl: 60 } }),
|
|
209
|
+
}),
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
// src/worker.tsx
|
|
213
|
+
import { router } from "./router";
|
|
214
|
+
|
|
215
|
+
export default {
|
|
216
|
+
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
|
|
217
|
+
return router.fetch(request, { env, ctx });
|
|
218
|
+
},
|
|
219
|
+
};
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## Complete Example
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
// src/urls.tsx
|
|
226
|
+
import { urls } from "@rangojs/router";
|
|
227
|
+
import { Outlet } from "@rangojs/router/client";
|
|
228
|
+
|
|
229
|
+
// Pages
|
|
230
|
+
import { HomePage } from "./pages/home";
|
|
231
|
+
import { AboutPage } from "./pages/about";
|
|
232
|
+
import { BlogIndexPage, BlogPostPage } from "./pages/blog";
|
|
233
|
+
|
|
234
|
+
// Layouts
|
|
235
|
+
import { RootLayout } from "./layouts/RootLayout";
|
|
236
|
+
import { BlogLayout } from "./layouts/BlogLayout";
|
|
237
|
+
|
|
238
|
+
// Loaders
|
|
239
|
+
import { BlogPostLoader, BlogSidebarLoader } from "./loaders/blog";
|
|
240
|
+
|
|
241
|
+
export const urlpatterns = urls(({ path, layout, parallel, loader, loading, cache }) => [
|
|
242
|
+
// Simple routes
|
|
243
|
+
path("/", HomePage, { name: "home" }),
|
|
244
|
+
path("/about", AboutPage, { name: "about" }),
|
|
245
|
+
|
|
246
|
+
// Blog with layout and loaders
|
|
247
|
+
layout(<BlogLayout />, () => [
|
|
248
|
+
// Sidebar as parallel route
|
|
249
|
+
parallel({ "@sidebar": () => <BlogSidebar /> }, () => [
|
|
250
|
+
loader(BlogSidebarLoader),
|
|
251
|
+
]),
|
|
252
|
+
|
|
253
|
+
// Cached blog routes
|
|
254
|
+
cache({ ttl: 60 }, () => [
|
|
255
|
+
path("/blog", BlogIndexPage, { name: "blog" }),
|
|
256
|
+
path("/blog/:slug", BlogPostPage, { name: "blogPost" }, () => [
|
|
257
|
+
loader(BlogPostLoader),
|
|
258
|
+
loading(<BlogPostSkeleton />),
|
|
259
|
+
]),
|
|
260
|
+
]),
|
|
261
|
+
]),
|
|
262
|
+
]);
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
// src/router.tsx
|
|
267
|
+
import { createRouter } from "@rangojs/router";
|
|
268
|
+
import { Document } from "./document";
|
|
269
|
+
import { urlpatterns } from "./urls";
|
|
270
|
+
|
|
271
|
+
const router = createRouter({
|
|
272
|
+
document: Document,
|
|
273
|
+
urls: urlpatterns,
|
|
274
|
+
|
|
275
|
+
defaultErrorBoundary: ({ error, reset }) => (
|
|
276
|
+
<div>
|
|
277
|
+
<h1>Something went wrong</h1>
|
|
278
|
+
<button onClick={reset}>Try again</button>
|
|
279
|
+
</div>
|
|
280
|
+
),
|
|
281
|
+
|
|
282
|
+
notFound: ({ pathname }) => (
|
|
283
|
+
<div>
|
|
284
|
+
<h1>404</h1>
|
|
285
|
+
<p>Page not found: {pathname}</p>
|
|
286
|
+
</div>
|
|
287
|
+
),
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
export default router;
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
## Including Sub-patterns
|
|
294
|
+
|
|
295
|
+
```typescript
|
|
296
|
+
// src/urls/shop.tsx
|
|
297
|
+
import { urls } from "@rangojs/router";
|
|
298
|
+
|
|
299
|
+
export const shopPatterns = urls(({ path, layout }) => [
|
|
300
|
+
path("/", ShopIndex, { name: "index" }),
|
|
301
|
+
path("/product/:slug", ProductPage, { name: "product" }),
|
|
302
|
+
]);
|
|
303
|
+
|
|
304
|
+
// src/urls.tsx
|
|
305
|
+
import { urls } from "@rangojs/router";
|
|
306
|
+
import { shopPatterns } from "./urls/shop";
|
|
307
|
+
|
|
308
|
+
export const urlpatterns = urls(({ path, include }) => [
|
|
309
|
+
path("/", HomePage, { name: "home" }),
|
|
310
|
+
include("/shop", shopPatterns, { name: "shop" }),
|
|
311
|
+
]);
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
## Environment Types
|
|
315
|
+
|
|
316
|
+
```typescript
|
|
317
|
+
// Bindings passed as TEnv to createRouter<TEnv>()
|
|
318
|
+
interface AppBindings {
|
|
319
|
+
DB: D1Database;
|
|
320
|
+
KV: KVNamespace;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Variables declared via module augmentation
|
|
324
|
+
interface AppVariables {
|
|
325
|
+
user?: { id: string; name: string };
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const router = createRouter<AppBindings>({
|
|
329
|
+
document: Document,
|
|
330
|
+
urls: urlpatterns,
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
// Register types globally for implicit typing
|
|
334
|
+
declare global {
|
|
335
|
+
namespace RSCRouter {
|
|
336
|
+
interface Env extends AppBindings {}
|
|
337
|
+
interface Vars extends AppVariables {}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
## Connection Warmup
|
|
343
|
+
|
|
344
|
+
Enabled by default. Keeps TCP+TLS connections alive so navigations after idle periods
|
|
345
|
+
don't pay handshake costs.
|
|
346
|
+
|
|
347
|
+
After 60s of no user interaction, the connection is marked cold. When the user returns
|
|
348
|
+
(tab becomes visible or first mouse/touch), a `HEAD ?_rsc_warmup` request re-establishes
|
|
349
|
+
the TLS connection before the next navigation. The server responds with 204 No Content
|
|
350
|
+
before any middleware or routing runs.
|
|
351
|
+
|
|
352
|
+
```typescript
|
|
353
|
+
// Enabled by default
|
|
354
|
+
const router = createRouter({
|
|
355
|
+
document: Document,
|
|
356
|
+
urls: urlpatterns,
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
// Disable warmup
|
|
360
|
+
const router = createRouter({
|
|
361
|
+
document: Document,
|
|
362
|
+
urls: urlpatterns,
|
|
363
|
+
warmup: false,
|
|
364
|
+
});
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
The warmup request is relative to the current page path, so it works correctly
|
|
368
|
+
with subpath deployments (reverse proxy, base path).
|
|
369
|
+
|
|
370
|
+
## Telemetry
|
|
371
|
+
|
|
372
|
+
The router emits structured lifecycle events through a pluggable telemetry sink.
|
|
373
|
+
Zero overhead when not configured.
|
|
374
|
+
|
|
375
|
+
```typescript
|
|
376
|
+
// Console sink for development
|
|
377
|
+
import { createRouter, createConsoleSink } from "@rangojs/router";
|
|
378
|
+
|
|
379
|
+
const router = createRouter({
|
|
380
|
+
document: Document,
|
|
381
|
+
urls: urlpatterns,
|
|
382
|
+
telemetry: createConsoleSink(),
|
|
383
|
+
});
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
```typescript
|
|
387
|
+
// OpenTelemetry for production
|
|
388
|
+
import { createRouter, createOTelSink } from "@rangojs/router";
|
|
389
|
+
import { trace } from "@opentelemetry/api";
|
|
390
|
+
|
|
391
|
+
const router = createRouter({
|
|
392
|
+
document: Document,
|
|
393
|
+
urls: urlpatterns,
|
|
394
|
+
telemetry: createOTelSink(trace.getTracer("my-app")),
|
|
395
|
+
});
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
```typescript
|
|
399
|
+
// Custom sink
|
|
400
|
+
const router = createRouter({
|
|
401
|
+
telemetry: {
|
|
402
|
+
emit(event) {
|
|
403
|
+
// Send to any observability backend
|
|
404
|
+
myTracer.record(event);
|
|
405
|
+
},
|
|
406
|
+
},
|
|
407
|
+
});
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
Events emitted: `request.start/end/error`, `loader.start/end/error`,
|
|
411
|
+
`handler.error`, `cache.decision`, `revalidation.decision`.
|
|
412
|
+
|
|
413
|
+
## SSR Streaming Policy
|
|
414
|
+
|
|
415
|
+
Control whether HTML SSR responses stream progressively or wait for all content:
|
|
416
|
+
|
|
417
|
+
```typescript
|
|
418
|
+
import { createRouter, type SSRStreamMode } from "@rangojs/router";
|
|
419
|
+
|
|
420
|
+
const router = createRouter({
|
|
421
|
+
ssr: {
|
|
422
|
+
resolveStreaming: ({ request }) => {
|
|
423
|
+
const ua = request.headers.get("user-agent") ?? "";
|
|
424
|
+
// Bots that can't process streamed HTML get a fully resolved page
|
|
425
|
+
if (/Googlebot|bingbot/i.test(ua)) return "allReady";
|
|
426
|
+
return "stream";
|
|
427
|
+
},
|
|
428
|
+
},
|
|
429
|
+
});
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
`SSRStreamMode` is `"stream" | "allReady"`:
|
|
433
|
+
|
|
434
|
+
- `"stream"` (default) — flush HTML as React renders. Suspense fallbacks appear first, then resolved content streams in. Best for real users (fastest TTFB).
|
|
435
|
+
- `"allReady"` — await `stream.allReady` before flushing. The full page arrives in one shot. Use for bots that cannot execute JavaScript or process chunked HTML.
|
|
436
|
+
|
|
437
|
+
The resolver receives `{ request, env, url }` and may be sync or async. It only runs on HTML SSR paths — RSC partials, `__rsc` requests, and response routes are unaffected.
|
|
438
|
+
|
|
439
|
+
When `resolveStreaming` is not configured, the default is `"stream"`.
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: tailwind
|
|
3
|
+
description: Set up Tailwind CSS v4 with the Document component and CSS imports
|
|
4
|
+
argument-hint: [setup]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Tailwind CSS
|
|
8
|
+
|
|
9
|
+
Set up Tailwind CSS v4 with the Rango router. Styles are loaded through the Document component using Vite's `?url` CSS import.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pnpm add -D tailwindcss @tailwindcss/vite
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Vite Plugin
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
// vite.config.ts
|
|
21
|
+
import tailwindcss from "@tailwindcss/vite";
|
|
22
|
+
|
|
23
|
+
export default defineConfig({
|
|
24
|
+
plugins: [
|
|
25
|
+
tailwindcss(),
|
|
26
|
+
// ... other plugins
|
|
27
|
+
],
|
|
28
|
+
});
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## CSS Entry Point
|
|
32
|
+
|
|
33
|
+
```css
|
|
34
|
+
/* src/index.css */
|
|
35
|
+
@import "tailwindcss";
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Document Component
|
|
39
|
+
|
|
40
|
+
Import the CSS file with `?url` to get a hashed URL, then preload and link it in `<head>`:
|
|
41
|
+
|
|
42
|
+
```tsx
|
|
43
|
+
// src/document.tsx
|
|
44
|
+
"use client";
|
|
45
|
+
|
|
46
|
+
import type { ReactNode } from "react";
|
|
47
|
+
import { MetaTags } from "@rangojs/router/client";
|
|
48
|
+
import styles from "./index.css?url";
|
|
49
|
+
|
|
50
|
+
export function Document({ children }: { children: ReactNode }) {
|
|
51
|
+
return (
|
|
52
|
+
<html lang="en">
|
|
53
|
+
<head>
|
|
54
|
+
<link rel="preload" href={styles} as="style" />
|
|
55
|
+
<link rel="stylesheet" href={styles} />
|
|
56
|
+
<MetaTags />
|
|
57
|
+
</head>
|
|
58
|
+
<body className="font-sans antialiased text-slate-900 bg-slate-50">
|
|
59
|
+
{children}
|
|
60
|
+
</body>
|
|
61
|
+
</html>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The `?url` suffix tells Vite to return the processed CSS file's URL instead of injecting it as a side effect. This gives you a stable, hashed asset path that works in both development and production.
|
|
67
|
+
|
|
68
|
+
## Customizing the Theme
|
|
69
|
+
|
|
70
|
+
Tailwind v4 uses CSS `@theme` for customization:
|
|
71
|
+
|
|
72
|
+
```css
|
|
73
|
+
/* src/index.css */
|
|
74
|
+
@import "tailwindcss";
|
|
75
|
+
|
|
76
|
+
@theme {
|
|
77
|
+
--font-sans: "Inter", system-ui, sans-serif;
|
|
78
|
+
--color-primary: #3b82f6;
|
|
79
|
+
--color-secondary: #64748b;
|
|
80
|
+
--breakpoint-3xl: 1920px;
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Dark Mode
|
|
85
|
+
|
|
86
|
+
Combine with the Rango theme system (see `/theme`):
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
const router = createRouter({
|
|
90
|
+
document: Document,
|
|
91
|
+
urls: urlpatterns,
|
|
92
|
+
theme: { attribute: "class" },
|
|
93
|
+
});
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Then use Tailwind's `dark:` variant which reads the `class` attribute:
|
|
97
|
+
|
|
98
|
+
```tsx
|
|
99
|
+
<div className="bg-white dark:bg-slate-900 text-slate-900 dark:text-white">
|
|
100
|
+
Content
|
|
101
|
+
</div>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## With Custom Fonts
|
|
105
|
+
|
|
106
|
+
Use `@fontsource-variable` for self-hosted fonts bundled by Vite (see `/fonts` for all options):
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
pnpm add @fontsource-variable/inter
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
```css
|
|
113
|
+
/* src/index.css */
|
|
114
|
+
@import "@fontsource-variable/inter";
|
|
115
|
+
@import "tailwindcss";
|
|
116
|
+
|
|
117
|
+
@theme {
|
|
118
|
+
--font-sans: "Inter Variable", system-ui, sans-serif;
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
No extra `<link>` tags needed in the Document -- Vite bundles the font files from `node_modules` automatically.
|
|
123
|
+
|
|
124
|
+
## Notes
|
|
125
|
+
|
|
126
|
+
- `?url` import is required -- bare CSS imports inject styles as a side effect and do not work with SSR streaming
|
|
127
|
+
- `<link rel="preload" as="style">` eliminates render-blocking by starting the download early
|
|
128
|
+
- Tailwind v4 does not need a `tailwind.config.js` -- use `@theme` in CSS instead
|
|
129
|
+
- The `@tailwindcss/vite` plugin handles content detection automatically
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: theme
|
|
3
|
+
description: Opt-in theme system with FOUC prevention for light/dark mode
|
|
4
|
+
argument-hint: [setup]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Theme Support
|
|
8
|
+
|
|
9
|
+
Opt-in theme system with FOUC prevention.
|
|
10
|
+
|
|
11
|
+
## Enable
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { createRouter } from "@rangojs/router";
|
|
15
|
+
|
|
16
|
+
// Simple - all defaults
|
|
17
|
+
const router = createRouter<Env>({
|
|
18
|
+
document: Document,
|
|
19
|
+
urls: urlpatterns,
|
|
20
|
+
theme: true,
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// Custom config
|
|
24
|
+
const router = createRouter<Env>({
|
|
25
|
+
document: Document,
|
|
26
|
+
urls: urlpatterns,
|
|
27
|
+
theme: {
|
|
28
|
+
defaultTheme: "system", // "light" | "dark" | "system"
|
|
29
|
+
themes: ["light", "dark"],
|
|
30
|
+
attribute: "class", // or "data-theme"
|
|
31
|
+
storageKey: "theme",
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Server (in loaders/middleware)
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
import { createLoader } from "@rangojs/router";
|
|
40
|
+
import type { Middleware } from "@rangojs/router";
|
|
41
|
+
|
|
42
|
+
// In a loader
|
|
43
|
+
export const SettingsLoader = createLoader(async (ctx) => {
|
|
44
|
+
const currentTheme = ctx.theme; // read from cookie
|
|
45
|
+
return { theme: currentTheme };
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// In middleware
|
|
49
|
+
export const themeMiddleware: Middleware = async (ctx, next) => {
|
|
50
|
+
// Set theme based on user preference
|
|
51
|
+
ctx.setTheme("dark");
|
|
52
|
+
await next();
|
|
53
|
+
};
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Client
|
|
57
|
+
|
|
58
|
+
```tsx
|
|
59
|
+
"use client";
|
|
60
|
+
import { useTheme } from "@rangojs/router/theme";
|
|
61
|
+
|
|
62
|
+
function ThemeToggle() {
|
|
63
|
+
const { theme, setTheme, resolvedTheme, systemTheme, themes } = useTheme();
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<select value={theme} onChange={(e) => setTheme(e.target.value)}>
|
|
67
|
+
<option value="system">System</option>
|
|
68
|
+
<option value="light">Light</option>
|
|
69
|
+
<option value="dark">Dark</option>
|
|
70
|
+
</select>
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Notes
|
|
76
|
+
|
|
77
|
+
- `<MetaTags />` auto-renders inline script for FOUC prevention
|
|
78
|
+
- Add `suppressHydrationWarning` to `<html>`
|
|
79
|
+
- Theme persists in localStorage + cookie (for SSR)
|