@rangojs/router 0.0.0-experimental.2

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.
Files changed (155) hide show
  1. package/CLAUDE.md +7 -0
  2. package/README.md +19 -0
  3. package/dist/vite/index.js +1298 -0
  4. package/package.json +140 -0
  5. package/skills/caching/SKILL.md +319 -0
  6. package/skills/document-cache/SKILL.md +152 -0
  7. package/skills/hooks/SKILL.md +359 -0
  8. package/skills/intercept/SKILL.md +292 -0
  9. package/skills/layout/SKILL.md +216 -0
  10. package/skills/loader/SKILL.md +365 -0
  11. package/skills/middleware/SKILL.md +442 -0
  12. package/skills/parallel/SKILL.md +255 -0
  13. package/skills/route/SKILL.md +141 -0
  14. package/skills/router-setup/SKILL.md +403 -0
  15. package/skills/theme/SKILL.md +54 -0
  16. package/skills/typesafety/SKILL.md +352 -0
  17. package/src/__mocks__/version.ts +6 -0
  18. package/src/__tests__/component-utils.test.ts +76 -0
  19. package/src/__tests__/route-definition.test.ts +63 -0
  20. package/src/__tests__/urls.test.tsx +436 -0
  21. package/src/browser/event-controller.ts +876 -0
  22. package/src/browser/index.ts +18 -0
  23. package/src/browser/link-interceptor.ts +121 -0
  24. package/src/browser/lru-cache.ts +69 -0
  25. package/src/browser/merge-segment-loaders.ts +126 -0
  26. package/src/browser/navigation-bridge.ts +893 -0
  27. package/src/browser/navigation-client.ts +162 -0
  28. package/src/browser/navigation-store.ts +823 -0
  29. package/src/browser/partial-update.ts +559 -0
  30. package/src/browser/react/Link.tsx +248 -0
  31. package/src/browser/react/NavigationProvider.tsx +275 -0
  32. package/src/browser/react/ScrollRestoration.tsx +94 -0
  33. package/src/browser/react/context.ts +53 -0
  34. package/src/browser/react/index.ts +52 -0
  35. package/src/browser/react/location-state-shared.ts +120 -0
  36. package/src/browser/react/location-state.ts +62 -0
  37. package/src/browser/react/use-action.ts +240 -0
  38. package/src/browser/react/use-client-cache.ts +56 -0
  39. package/src/browser/react/use-handle.ts +178 -0
  40. package/src/browser/react/use-href.tsx +208 -0
  41. package/src/browser/react/use-link-status.ts +134 -0
  42. package/src/browser/react/use-navigation.ts +150 -0
  43. package/src/browser/react/use-segments.ts +188 -0
  44. package/src/browser/request-controller.ts +164 -0
  45. package/src/browser/rsc-router.tsx +353 -0
  46. package/src/browser/scroll-restoration.ts +324 -0
  47. package/src/browser/server-action-bridge.ts +747 -0
  48. package/src/browser/shallow.ts +35 -0
  49. package/src/browser/types.ts +464 -0
  50. package/src/cache/__tests__/document-cache.test.ts +522 -0
  51. package/src/cache/__tests__/memory-segment-store.test.ts +487 -0
  52. package/src/cache/__tests__/memory-store.test.ts +484 -0
  53. package/src/cache/cache-scope.ts +565 -0
  54. package/src/cache/cf/__tests__/cf-cache-store.test.ts +428 -0
  55. package/src/cache/cf/cf-cache-store.ts +428 -0
  56. package/src/cache/cf/index.ts +19 -0
  57. package/src/cache/document-cache.ts +340 -0
  58. package/src/cache/index.ts +58 -0
  59. package/src/cache/memory-segment-store.ts +150 -0
  60. package/src/cache/memory-store.ts +253 -0
  61. package/src/cache/types.ts +387 -0
  62. package/src/client.rsc.tsx +88 -0
  63. package/src/client.tsx +621 -0
  64. package/src/component-utils.ts +76 -0
  65. package/src/components/DefaultDocument.tsx +23 -0
  66. package/src/default-error-boundary.tsx +88 -0
  67. package/src/deps/browser.ts +8 -0
  68. package/src/deps/html-stream-client.ts +2 -0
  69. package/src/deps/html-stream-server.ts +2 -0
  70. package/src/deps/rsc.ts +10 -0
  71. package/src/deps/ssr.ts +2 -0
  72. package/src/errors.ts +259 -0
  73. package/src/handle.ts +120 -0
  74. package/src/handles/MetaTags.tsx +193 -0
  75. package/src/handles/index.ts +6 -0
  76. package/src/handles/meta.ts +247 -0
  77. package/src/href-client.ts +128 -0
  78. package/src/href-context.ts +33 -0
  79. package/src/href.ts +177 -0
  80. package/src/index.rsc.ts +79 -0
  81. package/src/index.ts +87 -0
  82. package/src/loader.rsc.ts +204 -0
  83. package/src/loader.ts +47 -0
  84. package/src/network-error-thrower.tsx +21 -0
  85. package/src/outlet-context.ts +15 -0
  86. package/src/root-error-boundary.tsx +277 -0
  87. package/src/route-content-wrapper.tsx +198 -0
  88. package/src/route-definition.ts +1371 -0
  89. package/src/route-map-builder.ts +146 -0
  90. package/src/route-types.ts +198 -0
  91. package/src/route-utils.ts +89 -0
  92. package/src/router/__tests__/match-context.test.ts +104 -0
  93. package/src/router/__tests__/match-pipelines.test.ts +537 -0
  94. package/src/router/__tests__/match-result.test.ts +566 -0
  95. package/src/router/__tests__/on-error.test.ts +935 -0
  96. package/src/router/__tests__/pattern-matching.test.ts +577 -0
  97. package/src/router/error-handling.ts +287 -0
  98. package/src/router/handler-context.ts +158 -0
  99. package/src/router/loader-resolution.ts +326 -0
  100. package/src/router/manifest.ts +138 -0
  101. package/src/router/match-context.ts +264 -0
  102. package/src/router/match-middleware/background-revalidation.ts +236 -0
  103. package/src/router/match-middleware/cache-lookup.ts +261 -0
  104. package/src/router/match-middleware/cache-store.ts +266 -0
  105. package/src/router/match-middleware/index.ts +81 -0
  106. package/src/router/match-middleware/intercept-resolution.ts +268 -0
  107. package/src/router/match-middleware/segment-resolution.ts +174 -0
  108. package/src/router/match-pipelines.ts +214 -0
  109. package/src/router/match-result.ts +214 -0
  110. package/src/router/metrics.ts +62 -0
  111. package/src/router/middleware.test.ts +1355 -0
  112. package/src/router/middleware.ts +748 -0
  113. package/src/router/pattern-matching.ts +272 -0
  114. package/src/router/revalidation.ts +190 -0
  115. package/src/router/router-context.ts +299 -0
  116. package/src/router/types.ts +96 -0
  117. package/src/router.ts +3876 -0
  118. package/src/rsc/__tests__/helpers.test.ts +175 -0
  119. package/src/rsc/handler.ts +1060 -0
  120. package/src/rsc/helpers.ts +64 -0
  121. package/src/rsc/index.ts +56 -0
  122. package/src/rsc/nonce.ts +18 -0
  123. package/src/rsc/types.ts +237 -0
  124. package/src/segment-system.tsx +456 -0
  125. package/src/server/__tests__/request-context.test.ts +171 -0
  126. package/src/server/context.ts +417 -0
  127. package/src/server/handle-store.ts +230 -0
  128. package/src/server/loader-registry.ts +174 -0
  129. package/src/server/request-context.ts +554 -0
  130. package/src/server/root-layout.tsx +10 -0
  131. package/src/server/tsconfig.json +14 -0
  132. package/src/server.ts +146 -0
  133. package/src/ssr/__tests__/ssr-handler.test.tsx +188 -0
  134. package/src/ssr/index.tsx +234 -0
  135. package/src/theme/ThemeProvider.tsx +291 -0
  136. package/src/theme/ThemeScript.tsx +61 -0
  137. package/src/theme/__tests__/theme.test.ts +120 -0
  138. package/src/theme/constants.ts +55 -0
  139. package/src/theme/index.ts +58 -0
  140. package/src/theme/theme-context.ts +70 -0
  141. package/src/theme/theme-script.ts +152 -0
  142. package/src/theme/types.ts +182 -0
  143. package/src/theme/use-theme.ts +44 -0
  144. package/src/types.ts +1561 -0
  145. package/src/urls.ts +726 -0
  146. package/src/use-loader.tsx +346 -0
  147. package/src/vite/__tests__/expose-loader-id.test.ts +117 -0
  148. package/src/vite/expose-action-id.ts +344 -0
  149. package/src/vite/expose-handle-id.ts +209 -0
  150. package/src/vite/expose-loader-id.ts +357 -0
  151. package/src/vite/expose-location-state-id.ts +177 -0
  152. package/src/vite/index.ts +787 -0
  153. package/src/vite/package-resolution.ts +125 -0
  154. package/src/vite/version.d.ts +12 -0
  155. package/src/vite/virtual-entries.ts +109 -0
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Client-safe route map builder
3
+ *
4
+ * Provides a fluent API for building route maps with prefixes.
5
+ * Can be imported in client code without pulling in server dependencies.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * import { createRouteMap, registerRouteMap } from "rsc-router/browser";
10
+ *
11
+ * const routeMap = createRouteMap()
12
+ * .add(homeRoutes)
13
+ * .add(blogRoutes, "blog")
14
+ * .add(shopRoutes, "shop");
15
+ *
16
+ * registerRouteMap(routeMap.routes);
17
+ *
18
+ * declare global {
19
+ * namespace RSCRouter {
20
+ * interface RegisteredRoutes extends typeof routeMap.routes {}
21
+ * }
22
+ * }
23
+ * ```
24
+ */
25
+
26
+ import type { PrefixRoutePatterns } from "./href.js";
27
+
28
+ /**
29
+ * Route map builder interface
30
+ *
31
+ * Accumulates route types through the builder chain for type-safe href.
32
+ */
33
+ export interface RouteMapBuilder<TRoutes extends Record<string, string> = {}> {
34
+ /**
35
+ * Add routes without prefix
36
+ */
37
+ add<T extends Record<string, string>>(routes: T): RouteMapBuilder<TRoutes & T>;
38
+
39
+ /**
40
+ * Add routes with prefix (only URL patterns are prefixed, keys stay unchanged)
41
+ * @param routes - Route definitions to add
42
+ * @param prefix - URL prefix WITHOUT leading slash (e.g., "blog" not "/blog")
43
+ */
44
+ add<T extends Record<string, string>, P extends string>(
45
+ routes: T,
46
+ prefix: P
47
+ ): RouteMapBuilder<TRoutes & PrefixRoutePatterns<T, `/${P}`>>;
48
+
49
+ /**
50
+ * The accumulated route map (for typeof extraction in module augmentation)
51
+ */
52
+ readonly routes: TRoutes;
53
+ }
54
+
55
+ /**
56
+ * Add routes to a map with optional prefix
57
+ * Keys stay unchanged for composability - only URL patterns get prefixed.
58
+ *
59
+ * @param routeMap - The map to add routes to
60
+ * @param routes - Routes to add
61
+ * @param prefix - Optional prefix for URL paths WITHOUT leading slash (keys stay unchanged)
62
+ */
63
+ function addRoutes(
64
+ routeMap: Record<string, string>,
65
+ routes: Record<string, string>,
66
+ prefix: string = ""
67
+ ): void {
68
+ // Normalize prefix: remove leading slash if accidentally provided
69
+ const normalizedPrefix = prefix.startsWith("/") ? prefix.slice(1) : prefix;
70
+
71
+ for (const [key, pattern] of Object.entries(routes)) {
72
+ const prefixedPattern =
73
+ normalizedPrefix && pattern !== "/"
74
+ ? `/${normalizedPrefix}${pattern}`
75
+ : normalizedPrefix && pattern === "/"
76
+ ? `/${normalizedPrefix}`
77
+ : pattern;
78
+ // Use original key - enables reusable route modules
79
+ routeMap[key] = prefixedPattern;
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Create a new route map builder
85
+ *
86
+ * @returns A builder for accumulating routes with type-safe prefixes
87
+ *
88
+ * @example
89
+ * ```typescript
90
+ * const routeMap = createRouteMap()
91
+ * .add(homeRoutes)
92
+ * .add(blogRoutes, "blog");
93
+ *
94
+ * // Types are accumulated through the chain
95
+ * type AppRoutes = typeof routeMap.routes;
96
+ * ```
97
+ */
98
+ export function createRouteMap(): RouteMapBuilder<{}> {
99
+ const routeMap: Record<string, string> = {};
100
+
101
+ const builder: RouteMapBuilder<any> = {
102
+ add(routes: Record<string, string>, prefix?: string) {
103
+ addRoutes(routeMap, routes, prefix);
104
+ return builder;
105
+ },
106
+ get routes() {
107
+ return routeMap;
108
+ },
109
+ };
110
+
111
+ return builder;
112
+ }
113
+
114
+ // Singleton route map instance - populated when routes.ts is imported
115
+ let globalRouteMap: Record<string, string> = {};
116
+
117
+ /**
118
+ * Register the route map globally for href to use at runtime
119
+ *
120
+ * Call this after building your route map to make it available to href.
121
+ *
122
+ * @param map - The route map to register
123
+ *
124
+ * @example
125
+ * ```typescript
126
+ * const routeMap = createRouteMap()
127
+ * .add(homeRoutes)
128
+ * .add(blogRoutes, "blog");
129
+ *
130
+ * registerRouteMap(routeMap.routes);
131
+ * ```
132
+ */
133
+ export function registerRouteMap(map: Record<string, string>): void {
134
+ globalRouteMap = map;
135
+ }
136
+
137
+ /**
138
+ * Get the globally registered route map
139
+ *
140
+ * Used internally by href to resolve route names to URLs at runtime.
141
+ *
142
+ * @returns The registered route map
143
+ */
144
+ export function getGlobalRouteMap(): Record<string, string> {
145
+ return globalRouteMap;
146
+ }
@@ -0,0 +1,198 @@
1
+ /**
2
+ * Type definitions for route system items
3
+ * These are extracted separately to avoid circular dependencies
4
+ * and to prevent bundling server-only code in client bundles
5
+ */
6
+
7
+ /**
8
+ * Branded return types for route helpers
9
+ */
10
+ export declare const LayoutBrand: unique symbol;
11
+ export declare const RouteBrand: unique symbol;
12
+ export declare const ParallelBrand: unique symbol;
13
+ export declare const InterceptBrand: unique symbol;
14
+ export declare const MiddlewareBrand: unique symbol;
15
+ export declare const RevalidateBrand: unique symbol;
16
+ export declare const LoaderBrand: unique symbol;
17
+ export declare const LoadingBrand: unique symbol;
18
+ export declare const ErrorBoundaryBrand: unique symbol;
19
+ export declare const NotFoundBoundaryBrand: unique symbol;
20
+ export declare const WhenBrand: unique symbol;
21
+ export declare const CacheBrand: unique symbol;
22
+ export declare const IncludeBrand: unique symbol;
23
+ export declare const UrlPatternsBrand: unique symbol;
24
+
25
+ export type LayoutItem = {
26
+ name: string;
27
+ type: "layout";
28
+ uses?: AllUseItems[];
29
+ [LayoutBrand]: void;
30
+ };
31
+
32
+ /**
33
+ * Typed layout item that carries child routes as phantom type
34
+ * Used for type inference in urls() API
35
+ */
36
+ export type TypedLayoutItem<
37
+ TChildRoutes extends Record<string, string> = Record<string, string>
38
+ > = LayoutItem & {
39
+ readonly __childRoutes?: TChildRoutes;
40
+ };
41
+ export type RouteItem = {
42
+ name: string;
43
+ type: "route";
44
+ uses?: AllUseItems[];
45
+ [RouteBrand]: void;
46
+ };
47
+
48
+ /**
49
+ * Typed route item that carries route name and pattern as phantom types
50
+ * Used for type inference in urls() API
51
+ */
52
+ export type TypedRouteItem<
53
+ TName extends string = string,
54
+ TPattern extends string = string
55
+ > = RouteItem & {
56
+ readonly __name?: TName;
57
+ readonly __pattern?: TPattern;
58
+ };
59
+ export type ParallelItem = {
60
+ name: string;
61
+ type: "parallel";
62
+ uses?: ParallelUseItem[];
63
+ [ParallelBrand]: void;
64
+ };
65
+ export type InterceptItem = {
66
+ name: string;
67
+ type: "intercept";
68
+ uses?: InterceptUseItem[];
69
+ [InterceptBrand]: void;
70
+ };
71
+ export type LoaderItem = {
72
+ name: string;
73
+ type: "loader";
74
+ uses?: LoaderUseItem[];
75
+ [LoaderBrand]: void;
76
+ };
77
+ export type MiddlewareItem = {
78
+ name: string;
79
+ type: "middleware";
80
+ uses?: AllUseItems[];
81
+ [MiddlewareBrand]: void;
82
+ };
83
+ export type RevalidateItem = {
84
+ name: string;
85
+ type: "revalidate";
86
+ uses?: AllUseItems[];
87
+ [RevalidateBrand]: void;
88
+ };
89
+ export type LoadingItem = {
90
+ name: string;
91
+ type: "loading";
92
+ [LoadingBrand]: void;
93
+ };
94
+ export type ErrorBoundaryItem = {
95
+ name: string;
96
+ type: "errorBoundary";
97
+ uses?: AllUseItems[];
98
+ [ErrorBoundaryBrand]: void;
99
+ };
100
+ export type NotFoundBoundaryItem = {
101
+ name: string;
102
+ type: "notFoundBoundary";
103
+ uses?: AllUseItems[];
104
+ [NotFoundBoundaryBrand]: void;
105
+ };
106
+ export type WhenItem = {
107
+ name: string;
108
+ type: "when";
109
+ [WhenBrand]: void;
110
+ };
111
+ export type CacheItem = {
112
+ name: string;
113
+ type: "cache";
114
+ uses?: AllUseItems[];
115
+ [CacheBrand]: void;
116
+ };
117
+
118
+ /**
119
+ * Typed cache item that carries child routes as phantom type
120
+ * Used for type inference in urls() API
121
+ */
122
+ export type TypedCacheItem<
123
+ TChildRoutes extends Record<string, string> = Record<string, string>
124
+ > = CacheItem & {
125
+ readonly __childRoutes?: TChildRoutes;
126
+ };
127
+
128
+ /**
129
+ * Include item for URL pattern composition (used by urls() API)
130
+ */
131
+ export type IncludeItem = {
132
+ type: "include";
133
+ name: string;
134
+ prefix: string;
135
+ patterns: unknown; // UrlPatterns - avoid circular ref
136
+ options?: { name?: string };
137
+ [IncludeBrand]: void;
138
+ };
139
+
140
+ /**
141
+ * Typed include item that carries nested routes as phantom type
142
+ * Used for type inference in urls() API
143
+ */
144
+ export type TypedIncludeItem<
145
+ TRoutes extends Record<string, string> = Record<string, string>,
146
+ TNamePrefix extends string = string
147
+ > = IncludeItem & {
148
+ readonly __routes?: TRoutes;
149
+ readonly __namePrefix?: TNamePrefix;
150
+ };
151
+
152
+ /**
153
+ * Union types for use() callbacks
154
+ */
155
+ export type AllUseItems =
156
+ | LayoutItem
157
+ | RouteItem
158
+ | MiddlewareItem
159
+ | RevalidateItem
160
+ | ParallelItem
161
+ | InterceptItem
162
+ | LoaderItem
163
+ | LoadingItem
164
+ | ErrorBoundaryItem
165
+ | NotFoundBoundaryItem
166
+ | CacheItem
167
+ | IncludeItem;
168
+
169
+ /** Items that can be used inside a layout callback */
170
+ export type LayoutUseItem = AllUseItems;
171
+ export type RouteUseItem =
172
+ | LayoutItem
173
+ | ParallelItem
174
+ | InterceptItem
175
+ | MiddlewareItem
176
+ | RevalidateItem
177
+ | LoaderItem
178
+ | LoadingItem
179
+ | ErrorBoundaryItem
180
+ | NotFoundBoundaryItem
181
+ | CacheItem;
182
+ export type ParallelUseItem =
183
+ | RevalidateItem
184
+ | LoaderItem
185
+ | LoadingItem
186
+ | ErrorBoundaryItem
187
+ | NotFoundBoundaryItem;
188
+ export type InterceptUseItem =
189
+ | MiddlewareItem
190
+ | RevalidateItem
191
+ | LoaderItem
192
+ | LoadingItem
193
+ | ErrorBoundaryItem
194
+ | NotFoundBoundaryItem
195
+ | LayoutItem
196
+ | RouteItem
197
+ | WhenItem;
198
+ export type LoaderUseItem = RevalidateItem | CacheItem;
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Client-safe route utilities
3
+ *
4
+ * These utilities can be imported in client code without pulling in server dependencies.
5
+ */
6
+
7
+ import type { RouteDefinition, RouteConfig, ResolvedRouteMap, TrailingSlashMode } from "./types.js";
8
+
9
+ /**
10
+ * Check if a value is a RouteConfig object
11
+ */
12
+ function isRouteConfig(value: unknown): value is RouteConfig {
13
+ return (
14
+ typeof value === "object" &&
15
+ value !== null &&
16
+ "path" in value &&
17
+ typeof (value as RouteConfig).path === "string"
18
+ );
19
+ }
20
+
21
+ /**
22
+ * Flatten nested route definitions
23
+ */
24
+ function flattenRoutes(
25
+ routes: RouteDefinition,
26
+ prefix: string,
27
+ trailingSlashConfig: Record<string, TrailingSlashMode>
28
+ ): Record<string, string> {
29
+ const flattened: Record<string, string> = {};
30
+
31
+ for (const [key, value] of Object.entries(routes)) {
32
+ const fullKey = prefix + key;
33
+ if (typeof value === "string") {
34
+ // Direct route pattern - include prefix
35
+ flattened[fullKey] = value;
36
+ } else if (isRouteConfig(value)) {
37
+ // Route config object - extract path and trailing slash config
38
+ flattened[fullKey] = value.path;
39
+ if (value.trailingSlash) {
40
+ trailingSlashConfig[fullKey] = value.trailingSlash;
41
+ }
42
+ } else {
43
+ // Nested routes - flatten recursively
44
+ const nested = flattenRoutes(value, `${fullKey}.`, trailingSlashConfig);
45
+ Object.assign(flattened, nested);
46
+ }
47
+ }
48
+
49
+ return flattened;
50
+ }
51
+
52
+ /**
53
+ * Define routes with type safety (client-safe version)
54
+ *
55
+ * This is a client-safe version of the route function that can be imported
56
+ * in client code without pulling in server dependencies.
57
+ *
58
+ * @param input - Route definition object
59
+ * @returns Flattened route map with full type information
60
+ *
61
+ * @example
62
+ * ```typescript
63
+ * const shopRoutes = route({
64
+ * index: "/",
65
+ * cart: "/cart",
66
+ * products: {
67
+ * detail: "/product/:slug",
68
+ * category: "/products/:category",
69
+ * },
70
+ * });
71
+ * // Result: { index: "/", cart: "/cart", "products.detail": "/product/:slug", "products.category": "/products/:category" }
72
+ * ```
73
+ */
74
+ export function route<const T extends RouteDefinition>(
75
+ input: T
76
+ ): ResolvedRouteMap<T> {
77
+ const trailingSlash: Record<string, TrailingSlashMode> = {};
78
+ const routes = flattenRoutes(input as RouteDefinition, "", trailingSlash);
79
+
80
+ const result = routes as ResolvedRouteMap<T> & { __trailingSlash?: Record<string, TrailingSlashMode> };
81
+ if (Object.keys(trailingSlash).length > 0) {
82
+ Object.defineProperty(result, "__trailingSlash", {
83
+ value: trailingSlash,
84
+ enumerable: false,
85
+ writable: false,
86
+ });
87
+ }
88
+ return result;
89
+ }
@@ -0,0 +1,104 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { createPipelineState } from "../match-context";
3
+
4
+ describe("match-context", () => {
5
+ describe("createPipelineState()", () => {
6
+ it("should create initial state with default values", () => {
7
+ const state = createPipelineState();
8
+
9
+ expect(state.cacheHit).toBe(false);
10
+ expect(state.segments).toEqual([]);
11
+ expect(state.matchedIds).toEqual([]);
12
+ expect(state.interceptSegments).toEqual([]);
13
+ expect(state.slots).toEqual({});
14
+ });
15
+
16
+ it("should create state without cached data by default", () => {
17
+ const state = createPipelineState();
18
+
19
+ expect(state.cachedSegments).toBeUndefined();
20
+ expect(state.cachedMatchedIds).toBeUndefined();
21
+ expect(state.shouldRevalidate).toBeUndefined();
22
+ });
23
+
24
+ it("should allow mutation of state properties", () => {
25
+ const state = createPipelineState();
26
+
27
+ state.cacheHit = true;
28
+ state.segments.push({
29
+ id: "seg1",
30
+ type: "route",
31
+ component: null,
32
+ params: {},
33
+ });
34
+ state.matchedIds.push("seg1");
35
+ state.slots["@modal"] = { active: true, segments: [] };
36
+
37
+ expect(state.cacheHit).toBe(true);
38
+ expect(state.segments).toHaveLength(1);
39
+ expect(state.matchedIds).toContain("seg1");
40
+ expect(state.slots["@modal"]).toBeDefined();
41
+ });
42
+
43
+ it("should create independent state instances", () => {
44
+ const state1 = createPipelineState();
45
+ const state2 = createPipelineState();
46
+
47
+ state1.cacheHit = true;
48
+ state1.segments.push({
49
+ id: "seg1",
50
+ type: "route",
51
+ component: null,
52
+ params: {},
53
+ });
54
+
55
+ expect(state2.cacheHit).toBe(false);
56
+ expect(state2.segments).toHaveLength(0);
57
+ });
58
+
59
+ it("should support setting cache-related properties", () => {
60
+ const state = createPipelineState();
61
+
62
+ state.cacheHit = true;
63
+ state.shouldRevalidate = true;
64
+ state.cachedSegments = [
65
+ { id: "cached1", type: "route", component: "CachedComponent", params: {} },
66
+ ];
67
+ state.cachedMatchedIds = ["cached1"];
68
+
69
+ expect(state.cacheHit).toBe(true);
70
+ expect(state.shouldRevalidate).toBe(true);
71
+ expect(state.cachedSegments).toHaveLength(1);
72
+ expect(state.cachedMatchedIds).toContain("cached1");
73
+ });
74
+
75
+ it("should support intercept segments", () => {
76
+ const state = createPipelineState();
77
+
78
+ state.interceptSegments.push({
79
+ id: "modal-seg",
80
+ type: "route",
81
+ component: "ModalComponent",
82
+ params: {},
83
+ });
84
+
85
+ expect(state.interceptSegments).toHaveLength(1);
86
+ expect(state.interceptSegments[0].id).toBe("modal-seg");
87
+ });
88
+
89
+ it("should support slots with nested segments", () => {
90
+ const state = createPipelineState();
91
+
92
+ state.slots["@modal"] = {
93
+ active: true,
94
+ segments: [
95
+ { id: "modal1", type: "route", component: "Modal1", params: {} },
96
+ { id: "modal2", type: "route", component: "Modal2", params: {} },
97
+ ],
98
+ };
99
+
100
+ expect(state.slots["@modal"].active).toBe(true);
101
+ expect(state.slots["@modal"].segments).toHaveLength(2);
102
+ });
103
+ });
104
+ });