@rangojs/router 0.0.0-experimental.124 → 0.0.0-experimental.126
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/README.md +6 -4
- package/dist/bin/rango.js +3 -4
- package/dist/vite/index.js +315 -68
- package/package.json +19 -18
- package/skills/breadcrumbs/SKILL.md +60 -0
- package/skills/hooks/SKILL.md +2 -2
- package/skills/route/SKILL.md +6 -0
- package/skills/server-actions/SKILL.md +25 -1
- package/skills/testing/SKILL.md +17 -17
- package/skills/testing/cache-prerender.md +29 -3
- package/skills/testing/flight.md +13 -10
- package/skills/testing/render-handler.md +3 -0
- package/skills/testing/server-tree.md +1 -1
- package/skills/testing/setup.md +1 -1
- package/src/__internal.ts +0 -65
- package/src/browser/action-coordinator.ts +1 -1
- package/src/browser/action-fence.ts +10 -0
- package/src/browser/event-controller.ts +1 -83
- package/src/browser/navigation-store-handle.ts +3 -4
- package/src/browser/navigation-store.ts +0 -39
- package/src/browser/navigation-transaction.ts +0 -32
- package/src/browser/partial-update.ts +23 -84
- package/src/browser/prefetch/cache.ts +6 -45
- package/src/browser/prefetch/queue.ts +6 -3
- package/src/browser/rango-state.ts +2 -23
- package/src/browser/react/Link.tsx +0 -2
- package/src/browser/react/NavigationProvider.tsx +2 -1
- package/src/browser/react/ScrollRestoration.tsx +10 -6
- package/src/browser/react/filter-segment-order.ts +0 -2
- package/src/browser/react/index.ts +0 -45
- package/src/browser/react/location-state-shared.ts +0 -13
- package/src/browser/react/location-state.ts +0 -1
- package/src/browser/react/use-action.ts +6 -15
- package/src/browser/react/use-handle.ts +0 -5
- package/src/browser/react/use-link-status.ts +0 -4
- package/src/browser/react/use-navigation.ts +0 -3
- package/src/browser/react/use-params.ts +0 -2
- package/src/browser/react/use-router.ts +2 -1
- package/src/browser/react/use-search-params.ts +0 -5
- package/src/browser/react/use-segments.ts +0 -13
- package/src/browser/rsc-router.tsx +10 -3
- package/src/browser/server-action-bridge.ts +51 -3
- package/src/browser/types.ts +23 -5
- package/src/browser/validate-redirect-origin.ts +43 -16
- package/src/build/index.ts +8 -9
- package/src/build/route-trie.ts +46 -11
- package/src/build/route-types/param-extraction.ts +6 -3
- package/src/build/route-types/router-processing.ts +0 -8
- package/src/cache/cache-policy.ts +0 -54
- package/src/cache/cache-runtime.ts +48 -24
- package/src/cache/cache-scope.ts +0 -27
- package/src/cache/cache-tag.ts +0 -37
- package/src/cache/cf/cf-cache-store.ts +72 -45
- package/src/cache/cf/index.ts +0 -24
- package/src/cache/document-cache.ts +10 -36
- package/src/cache/handle-snapshot.ts +0 -40
- package/src/cache/index.ts +0 -27
- package/src/cache/memory-segment-store.ts +0 -52
- 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/types.ts +0 -98
- package/src/client.rsc.tsx +4 -22
- package/src/client.tsx +19 -32
- package/src/context-var.ts +12 -0
- package/src/defer.ts +196 -0
- package/src/deps/ssr.ts +0 -1
- package/src/handle.ts +2 -12
- 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 +6 -0
- package/src/host/pattern-matcher.ts +7 -50
- package/src/host/router.ts +1 -65
- package/src/host/testing.ts +0 -16
- package/src/host/types.ts +6 -2
- package/src/href-client.ts +0 -4
- package/src/index.rsc.ts +27 -2
- package/src/index.ts +7 -0
- package/src/internal-debug.ts +2 -4
- package/src/loader.rsc.ts +4 -15
- package/src/loader.ts +3 -9
- package/src/network-error-thrower.tsx +1 -6
- package/src/outlet-provider.tsx +1 -5
- package/src/prerender/param-hash.ts +10 -11
- package/src/prerender/store.ts +23 -30
- package/src/prerender.ts +34 -0
- package/src/redirect-origin.ts +100 -0
- package/src/root-error-boundary.tsx +1 -19
- package/src/route-content-wrapper.tsx +1 -44
- package/src/route-definition/dsl-helpers.ts +7 -19
- package/src/route-definition/helpers-types.ts +3 -3
- package/src/route-definition/redirect.ts +43 -9
- package/src/route-definition/resolve-handler-use.ts +6 -0
- package/src/route-map-builder.ts +0 -16
- package/src/router/content-negotiation.ts +0 -13
- package/src/router/error-handling.ts +12 -16
- package/src/router/find-match.ts +4 -31
- package/src/router/intercept-resolution.ts +10 -1
- package/src/router/lazy-includes.ts +1 -57
- package/src/router/loader-resolution.ts +25 -23
- package/src/router/logging.ts +0 -6
- package/src/router/manifest.ts +1 -25
- package/src/router/match-api.ts +0 -20
- package/src/router/match-context.ts +0 -22
- package/src/router/match-handlers.ts +0 -43
- package/src/router/match-middleware/background-revalidation.ts +0 -7
- package/src/router/match-middleware/cache-lookup.ts +96 -179
- package/src/router/match-middleware/cache-store.ts +0 -31
- package/src/router/match-middleware/intercept-resolution.ts +0 -22
- package/src/router/match-middleware/segment-resolution.ts +0 -22
- package/src/router/match-pipelines.ts +1 -42
- package/src/router/match-result.ts +1 -52
- package/src/router/metrics.ts +0 -34
- package/src/router/middleware-types.ts +0 -116
- package/src/router/middleware.ts +77 -60
- package/src/router/navigation-snapshot.ts +0 -51
- package/src/router/params-util.ts +23 -0
- package/src/router/pattern-matching.ts +5 -56
- package/src/router/prerender-match.ts +56 -51
- package/src/router/request-classification.ts +1 -38
- package/src/router/revalidation.ts +14 -62
- package/src/router/route-snapshot.ts +0 -1
- package/src/router/router-context.ts +0 -27
- package/src/router/router-interfaces.ts +10 -0
- package/src/router/segment-resolution/fresh.ts +25 -57
- package/src/router/segment-resolution/helpers.ts +34 -0
- package/src/router/segment-resolution/loader-cache.ts +35 -23
- package/src/router/segment-resolution/revalidation.ts +188 -283
- package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
- package/src/router/segment-resolution.ts +4 -1
- package/src/router/segment-wrappers.ts +0 -3
- package/src/router/telemetry-otel.ts +0 -20
- package/src/router/telemetry.ts +0 -22
- package/src/router/timeout.ts +0 -20
- package/src/router/trie-matching.ts +66 -45
- package/src/router/types.ts +1 -63
- package/src/router/url-params.ts +0 -5
- package/src/router.ts +8 -11
- package/src/rsc/handler-context.ts +1 -0
- package/src/rsc/handler.ts +20 -4
- package/src/rsc/helpers.ts +71 -3
- package/src/rsc/json-route-result.ts +38 -0
- package/src/rsc/origin-guard.ts +9 -15
- package/src/rsc/progressive-enhancement.ts +10 -1
- package/src/rsc/redirect-guard.ts +99 -0
- package/src/rsc/response-route-handler.ts +23 -18
- package/src/rsc/rsc-rendering.ts +2 -7
- package/src/rsc/runtime-warnings.ts +14 -0
- package/src/rsc/server-action.ts +34 -29
- package/src/rsc/types.ts +6 -3
- package/src/search-params.ts +0 -16
- package/src/segment-loader-promise.ts +14 -2
- package/src/segment-system.tsx +79 -88
- package/src/server/handle-store.ts +7 -24
- package/src/server/loader-registry.ts +5 -24
- package/src/server/request-context.ts +29 -92
- package/src/ssr/index.tsx +14 -14
- package/src/static-handler.ts +2 -27
- package/src/testing/cache-status.ts +44 -48
- package/src/testing/collect-handle.ts +1 -24
- package/src/testing/dispatch.ts +43 -6
- package/src/testing/e2e/index.ts +1 -22
- package/src/testing/e2e/matchers.ts +0 -16
- package/src/testing/flight-matchers.ts +0 -13
- package/src/testing/flight-normalize.ts +3 -30
- package/src/testing/flight.ts +46 -48
- package/src/testing/generated-routes.ts +1 -41
- package/src/testing/index.ts +1 -21
- package/src/testing/internal/context.ts +3 -45
- package/src/testing/internal/seed-vars.ts +0 -26
- package/src/testing/render-handler.ts +31 -61
- package/src/testing/render-route.tsx +75 -103
- package/src/testing/run-loader.ts +0 -96
- package/src/testing/run-middleware.ts +0 -26
- 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/error-types.ts +25 -89
- package/src/types/global-namespace.ts +4 -14
- package/src/types/handler-context.ts +28 -9
- package/src/types/index.ts +0 -10
- package/src/types/request-scope.ts +0 -19
- package/src/types/route-config.ts +6 -50
- package/src/types/route-entry.ts +0 -6
- package/src/types/segments.ts +0 -13
- package/src/urls/include-helper.ts +0 -4
- package/src/urls/index.ts +0 -6
- package/src/urls/path-helper-types.ts +2 -2
- package/src/urls/path-helper.ts +0 -54
- package/src/urls/urls-function.ts +0 -13
- package/src/use-loader.tsx +0 -186
- package/src/vite/discovery/bundle-postprocess.ts +2 -1
- package/src/vite/discovery/discover-routers.ts +28 -18
- package/src/vite/discovery/prerender-collection.ts +2 -4
- package/src/vite/discovery/state.ts +5 -0
- package/src/vite/discovery/virtual-module-codegen.ts +1 -11
- package/src/vite/plugin-types.ts +35 -9
- package/src/vite/plugins/cjs-to-esm.ts +0 -11
- package/src/vite/plugins/client-ref-dedup.ts +0 -11
- package/src/vite/plugins/client-ref-hashing.ts +0 -10
- package/src/vite/plugins/cloudflare-protocol-stub.ts +0 -20
- package/src/vite/plugins/expose-action-id.ts +2 -73
- package/src/vite/plugins/expose-id-utils.ts +0 -55
- package/src/vite/plugins/expose-ids/export-analysis.ts +0 -38
- package/src/vite/plugins/expose-ids/handler-transform.ts +0 -15
- package/src/vite/plugins/expose-ids/loader-transform.ts +0 -15
- package/src/vite/plugins/expose-ids/router-transform.ts +0 -13
- package/src/vite/plugins/expose-internal-ids.ts +10 -0
- package/src/vite/plugins/performance-tracks.ts +0 -3
- package/src/vite/plugins/refresh-cmd.ts +1 -1
- package/src/vite/plugins/use-cache-transform.ts +21 -46
- package/src/vite/plugins/version-injector.ts +0 -20
- package/src/vite/plugins/version-plugin.ts +1 -49
- package/src/vite/plugins/virtual-entries.ts +0 -15
- package/src/vite/rango.ts +2 -108
- package/src/vite/router-discovery.ts +9 -1
- package/src/vite/utils/ast-handler-extract.ts +0 -16
- package/src/vite/utils/bundle-analysis.ts +6 -13
- package/src/vite/utils/client-chunks.ts +0 -6
- package/src/vite/utils/forward-user-plugins.ts +0 -22
- package/src/vite/utils/manifest-utils.ts +0 -4
- package/src/vite/utils/package-resolution.ts +1 -73
- package/src/vite/utils/prerender-utils.ts +0 -35
- package/src/vite/utils/shared-utils.ts +3 -35
- package/src/browser/shallow.ts +0 -40
- package/src/handles/index.ts +0 -7
- package/src/router/middleware-cookies.ts +0 -55
|
@@ -28,9 +28,6 @@ import type {
|
|
|
28
28
|
} from "./types.js";
|
|
29
29
|
import { THEME_COOKIE } from "./constants.js";
|
|
30
30
|
|
|
31
|
-
/**
|
|
32
|
-
* Get system preference for color scheme
|
|
33
|
-
*/
|
|
34
31
|
function getSystemTheme(): ResolvedTheme {
|
|
35
32
|
if (typeof window !== "undefined" && window.matchMedia) {
|
|
36
33
|
return window.matchMedia("(prefers-color-scheme: dark)").matches
|
|
@@ -40,9 +37,6 @@ function getSystemTheme(): ResolvedTheme {
|
|
|
40
37
|
return "light";
|
|
41
38
|
}
|
|
42
39
|
|
|
43
|
-
/**
|
|
44
|
-
* Read theme from cookie
|
|
45
|
-
*/
|
|
46
40
|
function readThemeFromCookie(storageKey: string): string | null {
|
|
47
41
|
if (typeof document === "undefined") return null;
|
|
48
42
|
|
|
@@ -61,9 +55,6 @@ function readThemeFromCookie(storageKey: string): string | null {
|
|
|
61
55
|
return null;
|
|
62
56
|
}
|
|
63
57
|
|
|
64
|
-
/**
|
|
65
|
-
* Read theme from localStorage
|
|
66
|
-
*/
|
|
67
58
|
function readThemeFromStorage(storageKey: string): string | null {
|
|
68
59
|
if (typeof localStorage === "undefined") return null;
|
|
69
60
|
|
|
@@ -74,9 +65,6 @@ function readThemeFromStorage(storageKey: string): string | null {
|
|
|
74
65
|
}
|
|
75
66
|
}
|
|
76
67
|
|
|
77
|
-
/**
|
|
78
|
-
* Write theme to cookie
|
|
79
|
-
*/
|
|
80
68
|
function writeThemeToCookie(storageKey: string, theme: Theme): void {
|
|
81
69
|
if (typeof document === "undefined") return;
|
|
82
70
|
|
|
@@ -85,9 +73,6 @@ function writeThemeToCookie(storageKey: string, theme: Theme): void {
|
|
|
85
73
|
document.cookie = cookie;
|
|
86
74
|
}
|
|
87
75
|
|
|
88
|
-
/**
|
|
89
|
-
* Write theme to localStorage
|
|
90
|
-
*/
|
|
91
76
|
function writeThemeToStorage(storageKey: string, theme: Theme): void {
|
|
92
77
|
if (typeof localStorage === "undefined") return;
|
|
93
78
|
|
|
@@ -98,9 +83,6 @@ function writeThemeToStorage(storageKey: string, theme: Theme): void {
|
|
|
98
83
|
}
|
|
99
84
|
}
|
|
100
85
|
|
|
101
|
-
/**
|
|
102
|
-
* Apply theme to HTML element
|
|
103
|
-
*/
|
|
104
86
|
function applyThemeToDocument(theme: Theme, config: ResolvedThemeConfig): void {
|
|
105
87
|
if (typeof document === "undefined") return;
|
|
106
88
|
|
|
@@ -112,40 +94,30 @@ function applyThemeToDocument(theme: Theme, config: ResolvedThemeConfig): void {
|
|
|
112
94
|
const value = config.value[resolved] || resolved;
|
|
113
95
|
const el = document.documentElement;
|
|
114
96
|
|
|
115
|
-
// Apply attribute
|
|
116
97
|
if (config.attribute === "class") {
|
|
117
|
-
// Remove all theme classes
|
|
118
98
|
for (const t of config.themes) {
|
|
119
99
|
const v = config.value[t] || t;
|
|
120
100
|
el.classList.remove(v);
|
|
121
101
|
}
|
|
122
|
-
// Add current theme class
|
|
123
102
|
el.classList.add(value);
|
|
124
103
|
} else {
|
|
125
104
|
el.setAttribute(config.attribute, value);
|
|
126
105
|
}
|
|
127
106
|
|
|
128
|
-
// Set color-scheme for native dark mode support
|
|
129
107
|
if (config.enableColorScheme) {
|
|
130
108
|
el.style.colorScheme = resolved;
|
|
131
109
|
}
|
|
132
110
|
}
|
|
133
111
|
|
|
134
|
-
/**
|
|
135
|
-
* Get the resolved stored theme (validated against available themes)
|
|
136
|
-
*/
|
|
137
112
|
function getStoredTheme(config: ResolvedThemeConfig): Theme {
|
|
138
113
|
const { storageKey, themes, defaultTheme, enableSystem } = config;
|
|
139
114
|
|
|
140
|
-
// Try cookie first (for SSR consistency)
|
|
141
115
|
let stored = readThemeFromCookie(storageKey);
|
|
142
116
|
|
|
143
|
-
// Fall back to localStorage
|
|
144
117
|
if (!stored) {
|
|
145
118
|
stored = readThemeFromStorage(storageKey);
|
|
146
119
|
}
|
|
147
120
|
|
|
148
|
-
// Validate stored value
|
|
149
121
|
if (stored) {
|
|
150
122
|
if (stored === "system" && enableSystem) {
|
|
151
123
|
return "system";
|
|
@@ -158,38 +130,26 @@ function getStoredTheme(config: ResolvedThemeConfig): Theme {
|
|
|
158
130
|
return defaultTheme;
|
|
159
131
|
}
|
|
160
132
|
|
|
161
|
-
/**
|
|
162
|
-
* ThemeProvider component
|
|
163
|
-
*
|
|
164
|
-
* Provides theme state to the component tree via context.
|
|
165
|
-
* Handles theme persistence, system preference detection, and cross-tab sync.
|
|
166
|
-
*/
|
|
167
133
|
export function ThemeProvider({
|
|
168
134
|
config,
|
|
169
135
|
initialTheme,
|
|
170
136
|
children,
|
|
171
137
|
}: ThemeProviderProps): React.ReactNode {
|
|
172
|
-
// Track mount state to avoid hydration mismatches
|
|
173
|
-
// During SSR and initial hydration, mounted is false
|
|
174
138
|
const [mounted, setMounted] = useState(false);
|
|
175
139
|
|
|
176
|
-
// Initialize theme from prop, storage, or default
|
|
177
140
|
const [theme, setThemeState] = useState<Theme>(() => {
|
|
178
141
|
if (initialTheme) return initialTheme;
|
|
179
142
|
if (typeof window === "undefined") return config.defaultTheme;
|
|
180
143
|
return getStoredTheme(config);
|
|
181
144
|
});
|
|
182
145
|
|
|
183
|
-
// Track system preference - use stable default during SSR
|
|
184
146
|
const [systemTheme, setSystemTheme] = useState<ResolvedTheme>("light");
|
|
185
147
|
|
|
186
|
-
// Set mounted after hydration and detect actual system theme
|
|
187
148
|
useEffect(() => {
|
|
188
149
|
setMounted(true);
|
|
189
150
|
setSystemTheme(getSystemTheme());
|
|
190
151
|
}, []);
|
|
191
152
|
|
|
192
|
-
// Set theme and persist to storage
|
|
193
153
|
const setTheme = useCallback(
|
|
194
154
|
(newTheme: Theme) => {
|
|
195
155
|
setThemeState(newTheme);
|
|
@@ -200,7 +160,6 @@ export function ThemeProvider({
|
|
|
200
160
|
[config],
|
|
201
161
|
);
|
|
202
162
|
|
|
203
|
-
// Listen for system preference changes
|
|
204
163
|
useEffect(() => {
|
|
205
164
|
if (!config.enableSystem) return;
|
|
206
165
|
if (typeof window === "undefined" || !window.matchMedia) return;
|
|
@@ -211,13 +170,11 @@ export function ThemeProvider({
|
|
|
211
170
|
const newSystemTheme = e.matches ? "dark" : "light";
|
|
212
171
|
setSystemTheme(newSystemTheme);
|
|
213
172
|
|
|
214
|
-
// If current theme is "system", re-apply to update document
|
|
215
173
|
if (theme === "system") {
|
|
216
174
|
applyThemeToDocument("system", config);
|
|
217
175
|
}
|
|
218
176
|
};
|
|
219
177
|
|
|
220
|
-
// Modern browsers
|
|
221
178
|
mediaQuery.addEventListener("change", handleChange);
|
|
222
179
|
|
|
223
180
|
return () => {
|
|
@@ -225,7 +182,6 @@ export function ThemeProvider({
|
|
|
225
182
|
};
|
|
226
183
|
}, [config, theme]);
|
|
227
184
|
|
|
228
|
-
// Cross-tab synchronization via localStorage storage event
|
|
229
185
|
useEffect(() => {
|
|
230
186
|
if (typeof window === "undefined") return;
|
|
231
187
|
|
|
@@ -249,12 +205,8 @@ export function ThemeProvider({
|
|
|
249
205
|
};
|
|
250
206
|
}, [config]);
|
|
251
207
|
|
|
252
|
-
// Compute resolved theme
|
|
253
|
-
// During SSR (not mounted), use the initial theme or default to avoid hydration mismatch
|
|
254
208
|
const resolvedTheme: ResolvedTheme = useMemo(() => {
|
|
255
209
|
if (!mounted) {
|
|
256
|
-
// During SSR, return the initial theme if it's not "system", otherwise "light"
|
|
257
|
-
// The inline script will apply the correct class before hydration
|
|
258
210
|
if (initialTheme && initialTheme !== "system") {
|
|
259
211
|
return initialTheme as ResolvedTheme;
|
|
260
212
|
}
|
|
@@ -266,7 +218,6 @@ export function ThemeProvider({
|
|
|
266
218
|
return theme as ResolvedTheme;
|
|
267
219
|
}, [theme, systemTheme, config.enableSystem, mounted, initialTheme]);
|
|
268
220
|
|
|
269
|
-
// Build themes list (include "system" if enabled)
|
|
270
221
|
const themes = useMemo(() => {
|
|
271
222
|
if (config.enableSystem) {
|
|
272
223
|
return ["system", ...config.themes.filter((t) => t !== "system")];
|
|
@@ -274,14 +225,11 @@ export function ThemeProvider({
|
|
|
274
225
|
return config.themes;
|
|
275
226
|
}, [config.themes, config.enableSystem]);
|
|
276
227
|
|
|
277
|
-
// Context value
|
|
278
|
-
// During SSR (not mounted), return stable values to avoid hydration mismatch
|
|
279
228
|
const contextValue: ThemeContextValue = useMemo(
|
|
280
229
|
() => ({
|
|
281
230
|
theme,
|
|
282
231
|
setTheme,
|
|
283
232
|
resolvedTheme,
|
|
284
|
-
// Return stable "light" for systemTheme during SSR - actual value updates after mount
|
|
285
233
|
systemTheme: mounted ? systemTheme : "light",
|
|
286
234
|
themes,
|
|
287
235
|
config,
|
|
@@ -43,12 +43,6 @@ export interface ThemeScriptProps {
|
|
|
43
43
|
nonce?: string;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
/**
|
|
47
|
-
* Server component that renders the theme initialization script.
|
|
48
|
-
*
|
|
49
|
-
* This renders a synchronous inline script that applies the theme
|
|
50
|
-
* to the HTML element before React hydration, preventing FOUC.
|
|
51
|
-
*/
|
|
52
46
|
export function ThemeScript({
|
|
53
47
|
config,
|
|
54
48
|
nonce,
|
package/src/theme/constants.ts
CHANGED
|
@@ -4,9 +4,6 @@
|
|
|
4
4
|
|
|
5
5
|
import type { ResolvedThemeConfig, ThemeConfig } from "./types.js";
|
|
6
6
|
|
|
7
|
-
/**
|
|
8
|
-
* Default theme configuration values
|
|
9
|
-
*/
|
|
10
7
|
export const THEME_DEFAULTS = {
|
|
11
8
|
defaultTheme: "system",
|
|
12
9
|
themes: ["light", "dark"],
|
|
@@ -16,9 +13,6 @@ export const THEME_DEFAULTS = {
|
|
|
16
13
|
enableColorScheme: true,
|
|
17
14
|
} as const;
|
|
18
15
|
|
|
19
|
-
/**
|
|
20
|
-
* Cookie configuration for theme persistence
|
|
21
|
-
*/
|
|
22
16
|
export const THEME_COOKIE: {
|
|
23
17
|
readonly maxAge: number;
|
|
24
18
|
readonly path: string;
|
|
@@ -29,21 +23,15 @@ export const THEME_COOKIE: {
|
|
|
29
23
|
sameSite: "lax",
|
|
30
24
|
};
|
|
31
25
|
|
|
32
|
-
/**
|
|
33
|
-
* Resolve theme config by applying defaults.
|
|
34
|
-
* Accepts `true` to enable with all defaults, or a config object.
|
|
35
|
-
*/
|
|
36
26
|
export function resolveThemeConfig(
|
|
37
27
|
config: ThemeConfig | true,
|
|
38
28
|
): ResolvedThemeConfig {
|
|
39
|
-
// Handle `theme: true` shorthand
|
|
40
29
|
if (config === true) {
|
|
41
30
|
config = {};
|
|
42
31
|
}
|
|
43
32
|
|
|
44
33
|
const themes = config.themes ?? [...THEME_DEFAULTS.themes];
|
|
45
34
|
|
|
46
|
-
// Build value mapping - default to identity mapping
|
|
47
35
|
const value: Record<string, string> = {};
|
|
48
36
|
for (const theme of themes) {
|
|
49
37
|
value[theme] = config.value?.[theme] ?? theme;
|
package/src/theme/index.ts
CHANGED
|
@@ -23,16 +23,10 @@
|
|
|
23
23
|
* ```
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
|
-
// Main hook for accessing theme
|
|
27
26
|
export { useTheme } from "./use-theme.js";
|
|
28
|
-
|
|
29
|
-
// Provider (typically auto-included via NavigationProvider when theme is enabled)
|
|
30
27
|
export { ThemeProvider } from "./ThemeProvider.js";
|
|
31
|
-
|
|
32
|
-
// Script component for FOUC prevention (use in document head)
|
|
33
28
|
export { ThemeScript, type ThemeScriptProps } from "./ThemeScript.js";
|
|
34
29
|
|
|
35
|
-
// Types
|
|
36
30
|
export type {
|
|
37
31
|
Theme,
|
|
38
32
|
ResolvedTheme,
|
|
@@ -44,5 +38,4 @@ export type {
|
|
|
44
38
|
ThemeContextValue,
|
|
45
39
|
} from "./types.js";
|
|
46
40
|
|
|
47
|
-
// Constants
|
|
48
41
|
export { THEME_DEFAULTS, THEME_COOKIE } from "./constants.js";
|
|
@@ -19,17 +19,13 @@ import type { ThemeContextValue } from "./types.js";
|
|
|
19
19
|
export const ThemeContext: Context<ThemeContextValue | null> =
|
|
20
20
|
createContext<ThemeContextValue | null>(null);
|
|
21
21
|
|
|
22
|
-
/**
|
|
23
|
-
* Get theme context (internal use)
|
|
24
|
-
* Returns null if theme is not enabled
|
|
25
|
-
*/
|
|
26
22
|
export function useThemeContext(): ThemeContextValue | null {
|
|
27
23
|
return useContext(ThemeContext);
|
|
28
24
|
}
|
|
29
25
|
|
|
30
26
|
/**
|
|
31
27
|
* Get theme context, throwing if not available
|
|
32
|
-
*
|
|
28
|
+
* Used by useTheme hook
|
|
33
29
|
*/
|
|
34
30
|
export function requireThemeContext(): ThemeContextValue {
|
|
35
31
|
const ctx = useContext(ThemeContext);
|
|
@@ -22,7 +22,6 @@ import type { ResolvedThemeConfig } from "./types.js";
|
|
|
22
22
|
* - Handle all edge cases (no localStorage, no cookie, etc.)
|
|
23
23
|
*/
|
|
24
24
|
export function generateThemeScript(config: ResolvedThemeConfig): string {
|
|
25
|
-
// Build the script as a string, then minify
|
|
26
25
|
const script = `
|
|
27
26
|
(function() {
|
|
28
27
|
var storageKey = ${JSON.stringify(config.storageKey)};
|
|
@@ -33,9 +32,7 @@ export function generateThemeScript(config: ResolvedThemeConfig): string {
|
|
|
33
32
|
var valueMap = ${JSON.stringify(config.value)};
|
|
34
33
|
var themes = ${JSON.stringify(config.themes)};
|
|
35
34
|
|
|
36
|
-
// Read theme from cookie or localStorage
|
|
37
35
|
function getStoredTheme() {
|
|
38
|
-
// Try cookie first (for SSR consistency)
|
|
39
36
|
var cookies = document.cookie.split(';');
|
|
40
37
|
for (var i = 0; i < cookies.length; i++) {
|
|
41
38
|
var cookie = cookies[i].trim();
|
|
@@ -44,7 +41,6 @@ export function generateThemeScript(config: ResolvedThemeConfig): string {
|
|
|
44
41
|
catch (e) { return cookie.substring(storageKey.length + 1); }
|
|
45
42
|
}
|
|
46
43
|
}
|
|
47
|
-
// Fall back to localStorage
|
|
48
44
|
try {
|
|
49
45
|
return localStorage.getItem(storageKey);
|
|
50
46
|
} catch (e) {
|
|
@@ -52,7 +48,6 @@ export function generateThemeScript(config: ResolvedThemeConfig): string {
|
|
|
52
48
|
}
|
|
53
49
|
}
|
|
54
50
|
|
|
55
|
-
// Get system preference
|
|
56
51
|
function getSystemTheme() {
|
|
57
52
|
if (typeof window !== 'undefined' && window.matchMedia) {
|
|
58
53
|
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
@@ -60,7 +55,6 @@ export function generateThemeScript(config: ResolvedThemeConfig): string {
|
|
|
60
55
|
return 'light';
|
|
61
56
|
}
|
|
62
57
|
|
|
63
|
-
// Resolve "system" to actual theme
|
|
64
58
|
function resolveTheme(theme) {
|
|
65
59
|
if (theme === 'system' && enableSystem) {
|
|
66
60
|
return getSystemTheme();
|
|
@@ -68,15 +62,12 @@ export function generateThemeScript(config: ResolvedThemeConfig): string {
|
|
|
68
62
|
return theme;
|
|
69
63
|
}
|
|
70
64
|
|
|
71
|
-
// Apply theme to HTML element
|
|
72
65
|
function applyTheme(theme) {
|
|
73
66
|
var resolved = resolveTheme(theme);
|
|
74
67
|
var value = valueMap[resolved] || resolved;
|
|
75
68
|
var el = document.documentElement;
|
|
76
69
|
|
|
77
|
-
// Apply attribute
|
|
78
70
|
if (attribute === 'class') {
|
|
79
|
-
// Remove all theme classes, then add current
|
|
80
71
|
for (var i = 0; i < themes.length; i++) {
|
|
81
72
|
var v = valueMap[themes[i]] || themes[i];
|
|
82
73
|
el.classList.remove(v);
|
|
@@ -86,22 +77,18 @@ export function generateThemeScript(config: ResolvedThemeConfig): string {
|
|
|
86
77
|
el.setAttribute(attribute, value);
|
|
87
78
|
}
|
|
88
79
|
|
|
89
|
-
// Set color-scheme for native dark mode support
|
|
90
80
|
if (enableColorScheme) {
|
|
91
81
|
el.style.colorScheme = resolved;
|
|
92
82
|
}
|
|
93
83
|
}
|
|
94
84
|
|
|
95
|
-
// Get stored theme or use default
|
|
96
85
|
var stored = getStoredTheme();
|
|
97
86
|
var theme = stored && (stored === 'system' || themes.indexOf(stored) !== -1)
|
|
98
87
|
? stored
|
|
99
88
|
: defaultTheme;
|
|
100
89
|
|
|
101
|
-
// Apply immediately
|
|
102
90
|
applyTheme(theme);
|
|
103
91
|
|
|
104
|
-
// Listen for system preference changes (for "system" theme)
|
|
105
92
|
if (enableSystem && typeof window !== 'undefined' && window.matchMedia) {
|
|
106
93
|
try {
|
|
107
94
|
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', function() {
|
|
@@ -117,7 +104,6 @@ export function generateThemeScript(config: ResolvedThemeConfig): string {
|
|
|
117
104
|
})();
|
|
118
105
|
`;
|
|
119
106
|
|
|
120
|
-
// Minify by removing comments, extra whitespace, and newlines
|
|
121
107
|
return minifyScript(script);
|
|
122
108
|
}
|
|
123
109
|
|
package/src/theme/use-theme.ts
CHANGED
|
@@ -27,9 +27,6 @@ import type { UseThemeReturn } from "./types.js";
|
|
|
27
27
|
*
|
|
28
28
|
* Must be used within a ThemeProvider (which is automatically included
|
|
29
29
|
* in NavigationProvider when theme is enabled in router config).
|
|
30
|
-
*
|
|
31
|
-
* @returns Theme state and methods
|
|
32
|
-
* @throws Error if used outside ThemeProvider
|
|
33
30
|
*/
|
|
34
31
|
export function useTheme(): UseThemeReturn {
|
|
35
32
|
const ctx = requireThemeContext();
|
package/src/types/boundaries.ts
CHANGED
|
@@ -1,22 +1,12 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
2
|
|
|
3
|
-
/**
|
|
4
|
-
* Error information passed to error boundary fallback components
|
|
5
|
-
*/
|
|
6
3
|
export interface ErrorInfo {
|
|
7
|
-
/** Error message (always available) */
|
|
8
4
|
message: string;
|
|
9
|
-
/** Error name/type (e.g., "RouteNotFoundError", "MiddlewareError") */
|
|
10
5
|
name: string;
|
|
11
|
-
/** Optional error code for programmatic handling */
|
|
12
6
|
code?: string;
|
|
13
|
-
/** Stack trace (only in development) */
|
|
14
7
|
stack?: string;
|
|
15
|
-
/** Original error cause if available */
|
|
16
8
|
cause?: unknown;
|
|
17
|
-
/** Segment ID where the error occurred */
|
|
18
9
|
segmentId: string;
|
|
19
|
-
/** Segment type where the error occurred */
|
|
20
10
|
segmentType:
|
|
21
11
|
| "layout"
|
|
22
12
|
| "route"
|
|
@@ -46,13 +36,9 @@ export interface ErrorInfo {
|
|
|
46
36
|
* ```
|
|
47
37
|
*/
|
|
48
38
|
export interface ErrorBoundaryFallbackProps {
|
|
49
|
-
/** Error information */
|
|
50
39
|
error: ErrorInfo;
|
|
51
40
|
}
|
|
52
41
|
|
|
53
|
-
/**
|
|
54
|
-
* Error boundary handler - receives error info and returns fallback UI
|
|
55
|
-
*/
|
|
56
42
|
export type ErrorBoundaryHandler = (
|
|
57
43
|
props: ErrorBoundaryFallbackProps,
|
|
58
44
|
) => ReactNode;
|
|
@@ -77,17 +63,10 @@ export type ErrorBoundaryHandler = (
|
|
|
77
63
|
* ```
|
|
78
64
|
*/
|
|
79
65
|
export interface ClientErrorBoundaryFallbackProps {
|
|
80
|
-
/** Error information */
|
|
81
66
|
error: ErrorInfo;
|
|
82
|
-
/** Function to reset error state and retry rendering */
|
|
83
67
|
reset: () => void;
|
|
84
68
|
}
|
|
85
69
|
|
|
86
|
-
/**
|
|
87
|
-
* Wrapped loader data result for deferred resolution with error handling.
|
|
88
|
-
* When loaders are deferred to client-side resolution, errors need to be
|
|
89
|
-
* wrapped so the client can handle them appropriately.
|
|
90
|
-
*/
|
|
91
70
|
export type LoaderDataResult<T = unknown> =
|
|
92
71
|
| { __loaderResult: true; ok: true; data: T }
|
|
93
72
|
| {
|
|
@@ -97,9 +76,6 @@ export type LoaderDataResult<T = unknown> =
|
|
|
97
76
|
fallback: ReactNode | null;
|
|
98
77
|
};
|
|
99
78
|
|
|
100
|
-
/**
|
|
101
|
-
* Type guard to check if a value is a wrapped loader result
|
|
102
|
-
*/
|
|
103
79
|
export function isLoaderDataResult(value: unknown): value is LoaderDataResult {
|
|
104
80
|
return (
|
|
105
81
|
typeof value === "object" &&
|
|
@@ -109,15 +85,9 @@ export function isLoaderDataResult(value: unknown): value is LoaderDataResult {
|
|
|
109
85
|
);
|
|
110
86
|
}
|
|
111
87
|
|
|
112
|
-
/**
|
|
113
|
-
* Not found information passed to notFound boundary fallback components
|
|
114
|
-
*/
|
|
115
88
|
export interface NotFoundInfo {
|
|
116
|
-
/** Not found message */
|
|
117
89
|
message: string;
|
|
118
|
-
/** Segment ID where notFound was thrown */
|
|
119
90
|
segmentId: string;
|
|
120
|
-
/** Segment type where notFound was thrown */
|
|
121
91
|
segmentType:
|
|
122
92
|
| "layout"
|
|
123
93
|
| "route"
|
|
@@ -125,7 +95,6 @@ export interface NotFoundInfo {
|
|
|
125
95
|
| "loader"
|
|
126
96
|
| "middleware"
|
|
127
97
|
| "cache";
|
|
128
|
-
/** The pathname that triggered the not found */
|
|
129
98
|
pathname?: string;
|
|
130
99
|
}
|
|
131
100
|
|
|
@@ -146,13 +115,9 @@ export interface NotFoundInfo {
|
|
|
146
115
|
* ```
|
|
147
116
|
*/
|
|
148
117
|
export interface NotFoundBoundaryFallbackProps {
|
|
149
|
-
/** Not found information */
|
|
150
118
|
notFound: NotFoundInfo;
|
|
151
119
|
}
|
|
152
120
|
|
|
153
|
-
/**
|
|
154
|
-
* NotFound boundary handler - receives not found info and returns fallback UI
|
|
155
|
-
*/
|
|
156
121
|
export type NotFoundBoundaryHandler = (
|
|
157
122
|
props: NotFoundBoundaryFallbackProps,
|
|
158
123
|
) => ReactNode;
|
package/src/types/error-types.ts
CHANGED
|
@@ -14,19 +14,19 @@
|
|
|
14
14
|
* - "unknown": Fallback for unclassified errors (not currently invoked)
|
|
15
15
|
*/
|
|
16
16
|
export type ErrorPhase =
|
|
17
|
-
| "routing"
|
|
18
|
-
| "manifest"
|
|
19
|
-
| "middleware"
|
|
20
|
-
| "loader"
|
|
21
|
-
| "handler"
|
|
22
|
-
| "rendering"
|
|
23
|
-
| "action"
|
|
24
|
-
| "revalidation"
|
|
25
|
-
| "cache"
|
|
26
|
-
| "prerender"
|
|
27
|
-
| "static"
|
|
28
|
-
| "origin"
|
|
29
|
-
| "unknown";
|
|
17
|
+
| "routing"
|
|
18
|
+
| "manifest"
|
|
19
|
+
| "middleware"
|
|
20
|
+
| "loader"
|
|
21
|
+
| "handler"
|
|
22
|
+
| "rendering"
|
|
23
|
+
| "action"
|
|
24
|
+
| "revalidation"
|
|
25
|
+
| "cache"
|
|
26
|
+
| "prerender"
|
|
27
|
+
| "static"
|
|
28
|
+
| "origin"
|
|
29
|
+
| "unknown";
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
32
|
* Comprehensive context passed to onError callback
|
|
@@ -59,99 +59,35 @@ export type ErrorPhase =
|
|
|
59
59
|
* ```
|
|
60
60
|
*/
|
|
61
61
|
export interface OnErrorContext<TEnv = any> {
|
|
62
|
-
/**
|
|
63
|
-
* The error that occurred
|
|
64
|
-
*/
|
|
65
62
|
error: Error;
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Phase where the error occurred
|
|
69
|
-
*/
|
|
70
63
|
phase: ErrorPhase;
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* The original request
|
|
74
|
-
*/
|
|
75
64
|
request: Request;
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Parsed URL from the request
|
|
79
|
-
*/
|
|
80
65
|
url: URL;
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Request pathname
|
|
84
|
-
*/
|
|
85
66
|
pathname: string;
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* HTTP method
|
|
89
|
-
*/
|
|
90
67
|
method: string;
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Matched route key (if available)
|
|
94
|
-
* e.g., "shop.products.detail"
|
|
95
|
-
*/
|
|
68
|
+
/** Matched route key (if available) e.g., "shop.products.detail" */
|
|
96
69
|
routeKey?: string;
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Route params (if available)
|
|
100
|
-
* e.g., { slug: "headphones" }
|
|
101
|
-
*/
|
|
70
|
+
/** Route params (if available) e.g., { slug: "headphones" } */
|
|
102
71
|
params?: Record<string, string>;
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Segment ID where error occurred (if available)
|
|
106
|
-
* e.g., "M1L0" for a layout, "M1R0" for a route
|
|
107
|
-
*/
|
|
72
|
+
/** Segment ID where error occurred (if available) e.g., "M1L0" for a layout, "M1R0" for a route */
|
|
108
73
|
segmentId?: string;
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Segment type where error occurred (if available)
|
|
112
|
-
*/
|
|
74
|
+
/** Segment type where error occurred (if available) */
|
|
113
75
|
segmentType?: "layout" | "route" | "parallel" | "loader" | "middleware";
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Loader name (if error occurred in a loader)
|
|
117
|
-
*/
|
|
76
|
+
/** Loader name (if error occurred in a loader) */
|
|
118
77
|
loaderName?: string;
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Middleware name/id (if error occurred in middleware)
|
|
122
|
-
*/
|
|
78
|
+
/** Middleware name/id (if error occurred in middleware) */
|
|
123
79
|
middlewareId?: string;
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Action ID (if error occurred during server action)
|
|
127
|
-
* e.g., "src/actions.ts#addToCart"
|
|
128
|
-
*/
|
|
80
|
+
/** Action ID (if error occurred during server action) e.g., "src/actions.ts#addToCart" */
|
|
129
81
|
actionId?: string;
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Environment/bindings (platform context)
|
|
133
|
-
*/
|
|
82
|
+
/** Environment/bindings (platform context) */
|
|
134
83
|
env?: TEnv;
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Duration from request start to error (milliseconds)
|
|
138
|
-
*/
|
|
84
|
+
/** Duration from request start to error (milliseconds) */
|
|
139
85
|
duration?: number;
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Whether this is a partial/navigation request
|
|
143
|
-
*/
|
|
86
|
+
/** Whether this is a partial/navigation request */
|
|
144
87
|
isPartial?: boolean;
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Whether an error boundary caught the error
|
|
148
|
-
* If true, the error was handled and a fallback UI was rendered
|
|
149
|
-
*/
|
|
88
|
+
/** Whether an error boundary caught the error */
|
|
150
89
|
handledByBoundary?: boolean;
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Stack trace (if available)
|
|
154
|
-
*/
|
|
90
|
+
/** Stack trace (if available) */
|
|
155
91
|
stack?: string;
|
|
156
92
|
|
|
157
93
|
/**
|
|
@@ -20,26 +20,16 @@
|
|
|
20
20
|
declare global {
|
|
21
21
|
namespace Rango {
|
|
22
22
|
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
|
23
|
-
interface Env {
|
|
24
|
-
// Empty by default - users augment with their bindings (e.g., { DB: D1Database })
|
|
25
|
-
}
|
|
23
|
+
interface Env {}
|
|
26
24
|
|
|
27
25
|
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
|
28
|
-
interface Vars {
|
|
29
|
-
// Empty by default - users augment with their variables (e.g., { user?: User })
|
|
30
|
-
}
|
|
26
|
+
interface Vars {}
|
|
31
27
|
|
|
32
28
|
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
|
33
|
-
interface RegisteredRoutes {
|
|
34
|
-
// Empty by default - users augment with their merged route maps for type-safe href()
|
|
35
|
-
// Values are string (pattern) for RSC routes, or { path: string; response: T } for response routes
|
|
36
|
-
}
|
|
29
|
+
interface RegisteredRoutes {}
|
|
37
30
|
|
|
38
31
|
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
|
39
|
-
interface GeneratedRouteMap {
|
|
40
|
-
// Empty by default - populated by generated named-routes.gen.ts
|
|
41
|
-
// Maps route names to URL pattern strings for Handler<"routeName"> support
|
|
42
|
-
}
|
|
32
|
+
interface GeneratedRouteMap {}
|
|
43
33
|
}
|
|
44
34
|
}
|
|
45
35
|
|