@pithyjs/router 0.1.0-beta.0

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 (163) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +621 -0
  3. package/dist/index.d.ts +29 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +49 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/router/core/config.d.ts +22 -0
  8. package/dist/router/core/config.d.ts.map +1 -0
  9. package/dist/router/core/config.js +54 -0
  10. package/dist/router/core/config.js.map +1 -0
  11. package/dist/router/core/constants.d.ts +89 -0
  12. package/dist/router/core/constants.d.ts.map +1 -0
  13. package/dist/router/core/constants.js +95 -0
  14. package/dist/router/core/constants.js.map +1 -0
  15. package/dist/router/core/history.d.ts +54 -0
  16. package/dist/router/core/history.d.ts.map +1 -0
  17. package/dist/router/core/history.js +89 -0
  18. package/dist/router/core/history.js.map +1 -0
  19. package/dist/router/core/matcher.d.ts +43 -0
  20. package/dist/router/core/matcher.d.ts.map +1 -0
  21. package/dist/router/core/matcher.js +242 -0
  22. package/dist/router/core/matcher.js.map +1 -0
  23. package/dist/router/core/params.d.ts +88 -0
  24. package/dist/router/core/params.d.ts.map +1 -0
  25. package/dist/router/core/params.js +150 -0
  26. package/dist/router/core/params.js.map +1 -0
  27. package/dist/router/core/pipeline.d.ts +90 -0
  28. package/dist/router/core/pipeline.d.ts.map +1 -0
  29. package/dist/router/core/pipeline.js +323 -0
  30. package/dist/router/core/pipeline.js.map +1 -0
  31. package/dist/router/core/route-tree.d.ts +76 -0
  32. package/dist/router/core/route-tree.d.ts.map +1 -0
  33. package/dist/router/core/route-tree.js +143 -0
  34. package/dist/router/core/route-tree.js.map +1 -0
  35. package/dist/router/core/router.d.ts +229 -0
  36. package/dist/router/core/router.d.ts.map +1 -0
  37. package/dist/router/core/router.js +762 -0
  38. package/dist/router/core/router.js.map +1 -0
  39. package/dist/router/core/types.d.ts +387 -0
  40. package/dist/router/core/types.d.ts.map +1 -0
  41. package/dist/router/core/types.js +22 -0
  42. package/dist/router/core/types.js.map +1 -0
  43. package/dist/router/core/utils.d.ts +66 -0
  44. package/dist/router/core/utils.d.ts.map +1 -0
  45. package/dist/router/core/utils.js +152 -0
  46. package/dist/router/core/utils.js.map +1 -0
  47. package/dist/router/features/coordinator.d.ts +75 -0
  48. package/dist/router/features/coordinator.d.ts.map +1 -0
  49. package/dist/router/features/coordinator.js +203 -0
  50. package/dist/router/features/coordinator.js.map +1 -0
  51. package/dist/router/features/errors/error-codes.d.ts +107 -0
  52. package/dist/router/features/errors/error-codes.d.ts.map +1 -0
  53. package/dist/router/features/errors/error-codes.js +247 -0
  54. package/dist/router/features/errors/error-codes.js.map +1 -0
  55. package/dist/router/features/errors/error-resolution.d.ts +31 -0
  56. package/dist/router/features/errors/error-resolution.d.ts.map +1 -0
  57. package/dist/router/features/errors/error-resolution.js +47 -0
  58. package/dist/router/features/errors/error-resolution.js.map +1 -0
  59. package/dist/router/features/errors/error-types.d.ts +60 -0
  60. package/dist/router/features/errors/error-types.d.ts.map +1 -0
  61. package/dist/router/features/errors/error-types.js +57 -0
  62. package/dist/router/features/errors/error-types.js.map +1 -0
  63. package/dist/router/features/errors/global-error-fallback.d.ts +33 -0
  64. package/dist/router/features/errors/global-error-fallback.d.ts.map +1 -0
  65. package/dist/router/features/errors/global-error-fallback.js +182 -0
  66. package/dist/router/features/errors/global-error-fallback.js.map +1 -0
  67. package/dist/router/features/errors/index.d.ts +15 -0
  68. package/dist/router/features/errors/index.d.ts.map +1 -0
  69. package/dist/router/features/errors/index.js +18 -0
  70. package/dist/router/features/errors/index.js.map +1 -0
  71. package/dist/router/features/errors/router-errors.d.ts +44 -0
  72. package/dist/router/features/errors/router-errors.d.ts.map +1 -0
  73. package/dist/router/features/errors/router-errors.js +53 -0
  74. package/dist/router/features/errors/router-errors.js.map +1 -0
  75. package/dist/router/features/guards/engine.d.ts +40 -0
  76. package/dist/router/features/guards/engine.d.ts.map +1 -0
  77. package/dist/router/features/guards/engine.js +67 -0
  78. package/dist/router/features/guards/engine.js.map +1 -0
  79. package/dist/router/features/guards/index.d.ts +52 -0
  80. package/dist/router/features/guards/index.d.ts.map +1 -0
  81. package/dist/router/features/guards/index.js +95 -0
  82. package/dist/router/features/guards/index.js.map +1 -0
  83. package/dist/router/features/loaders/cache.d.ts +60 -0
  84. package/dist/router/features/loaders/cache.d.ts.map +1 -0
  85. package/dist/router/features/loaders/cache.js +80 -0
  86. package/dist/router/features/loaders/cache.js.map +1 -0
  87. package/dist/router/features/loaders/engine.d.ts +17 -0
  88. package/dist/router/features/loaders/engine.d.ts.map +1 -0
  89. package/dist/router/features/loaders/engine.js +27 -0
  90. package/dist/router/features/loaders/engine.js.map +1 -0
  91. package/dist/router/features/loaders/index.d.ts +49 -0
  92. package/dist/router/features/loaders/index.d.ts.map +1 -0
  93. package/dist/router/features/loaders/index.js +112 -0
  94. package/dist/router/features/loaders/index.js.map +1 -0
  95. package/dist/router/features/loading-ui-manager.d.ts +69 -0
  96. package/dist/router/features/loading-ui-manager.d.ts.map +1 -0
  97. package/dist/router/features/loading-ui-manager.js +209 -0
  98. package/dist/router/features/loading-ui-manager.js.map +1 -0
  99. package/dist/router/features/module-loader.d.ts +50 -0
  100. package/dist/router/features/module-loader.d.ts.map +1 -0
  101. package/dist/router/features/module-loader.js +121 -0
  102. package/dist/router/features/module-loader.js.map +1 -0
  103. package/dist/router/features/outlet/errors.d.ts +24 -0
  104. package/dist/router/features/outlet/errors.d.ts.map +1 -0
  105. package/dist/router/features/outlet/errors.js +96 -0
  106. package/dist/router/features/outlet/errors.js.map +1 -0
  107. package/dist/router/features/outlet/index.d.ts +45 -0
  108. package/dist/router/features/outlet/index.d.ts.map +1 -0
  109. package/dist/router/features/outlet/index.js +95 -0
  110. package/dist/router/features/outlet/index.js.map +1 -0
  111. package/dist/router/features/outlet/mount.d.ts +38 -0
  112. package/dist/router/features/outlet/mount.d.ts.map +1 -0
  113. package/dist/router/features/outlet/mount.js +301 -0
  114. package/dist/router/features/outlet/mount.js.map +1 -0
  115. package/dist/router/features/outlet/segment-chain.d.ts +16 -0
  116. package/dist/router/features/outlet/segment-chain.d.ts.map +1 -0
  117. package/dist/router/features/outlet/segment-chain.js +62 -0
  118. package/dist/router/features/outlet/segment-chain.js.map +1 -0
  119. package/dist/router/features/prefetch/index.d.ts +97 -0
  120. package/dist/router/features/prefetch/index.d.ts.map +1 -0
  121. package/dist/router/features/prefetch/index.js +345 -0
  122. package/dist/router/features/prefetch/index.js.map +1 -0
  123. package/dist/router/features/ux/focus.d.ts +62 -0
  124. package/dist/router/features/ux/focus.d.ts.map +1 -0
  125. package/dist/router/features/ux/focus.js +136 -0
  126. package/dist/router/features/ux/focus.js.map +1 -0
  127. package/dist/router/features/ux/live-region.d.ts +81 -0
  128. package/dist/router/features/ux/live-region.d.ts.map +1 -0
  129. package/dist/router/features/ux/live-region.js +166 -0
  130. package/dist/router/features/ux/live-region.js.map +1 -0
  131. package/dist/router/features/ux/scroll-focus.d.ts +105 -0
  132. package/dist/router/features/ux/scroll-focus.d.ts.map +1 -0
  133. package/dist/router/features/ux/scroll-focus.js +111 -0
  134. package/dist/router/features/ux/scroll-focus.js.map +1 -0
  135. package/dist/router/features/ux/scroll.d.ts +71 -0
  136. package/dist/router/features/ux/scroll.d.ts.map +1 -0
  137. package/dist/router/features/ux/scroll.js +137 -0
  138. package/dist/router/features/ux/scroll.js.map +1 -0
  139. package/dist/router/features/ux/transitions.d.ts +120 -0
  140. package/dist/router/features/ux/transitions.d.ts.map +1 -0
  141. package/dist/router/features/ux/transitions.js +235 -0
  142. package/dist/router/features/ux/transitions.js.map +1 -0
  143. package/dist/router/integration/directive-binder.d.ts +20 -0
  144. package/dist/router/integration/directive-binder.d.ts.map +1 -0
  145. package/dist/router/integration/directive-binder.js +139 -0
  146. package/dist/router/integration/directive-binder.js.map +1 -0
  147. package/dist/router/integration/directives.d.ts +42 -0
  148. package/dist/router/integration/directives.d.ts.map +1 -0
  149. package/dist/router/integration/directives.js +109 -0
  150. package/dist/router/integration/directives.js.map +1 -0
  151. package/dist/router/integration/history-adapter.d.ts +76 -0
  152. package/dist/router/integration/history-adapter.d.ts.map +1 -0
  153. package/dist/router/integration/history-adapter.js +157 -0
  154. package/dist/router/integration/history-adapter.js.map +1 -0
  155. package/dist/router/integration/hmr.d.ts +83 -0
  156. package/dist/router/integration/hmr.d.ts.map +1 -0
  157. package/dist/router/integration/hmr.js +160 -0
  158. package/dist/router/integration/hmr.js.map +1 -0
  159. package/dist/router/integration/link-interceptor.d.ts +49 -0
  160. package/dist/router/integration/link-interceptor.d.ts.map +1 -0
  161. package/dist/router/integration/link-interceptor.js +94 -0
  162. package/dist/router/integration/link-interceptor.js.map +1 -0
  163. package/package.json +47 -0
@@ -0,0 +1,242 @@
1
+ /** @codex
2
+ {
3
+ "id": "pithy.router.matcher",
4
+ "title": "Route Path Matching Engine",
5
+ "category": "runtime"
6
+ }
7
+ */
8
+ import { SegmentType, } from './types';
9
+ import { MATCHER_SCORES } from './constants';
10
+ /** @codexApi {"parent":"pithy.router.matcher","name":"parseRoutePattern","stability":"stable","signature":"(pattern: string) => RouteSegment[]","description":"Parse a route pattern into typed segments (static, dynamic, catch-all, group)"} */
11
+ /**
12
+ * Parse a route pattern into segments with type information
13
+ * Handles static, dynamic [id], catch-all [...slug], and groups (group)
14
+ */
15
+ export function parseRoutePattern(pattern) {
16
+ // Normalize pattern - remove leading/trailing slashes, handle root
17
+ const normalizedPattern = pattern === '/' ? '' : pattern.replace(/^\/+|\/+$/g, '');
18
+ if (!normalizedPattern) {
19
+ return []; // Root route
20
+ }
21
+ const segments = normalizedPattern.split('/');
22
+ return segments.map((segment) => {
23
+ // Route group: (group) - excluded from URL but affects nesting
24
+ if (segment.startsWith('(') && segment.endsWith(')')) {
25
+ return {
26
+ raw: segment,
27
+ type: SegmentType.Static, // Groups don't affect URL matching precedence
28
+ isGroup: true,
29
+ };
30
+ }
31
+ // Catch-all: * (new syntax)
32
+ if (segment === '*') {
33
+ return {
34
+ raw: segment,
35
+ type: SegmentType.CatchAll,
36
+ paramName: 'slug',
37
+ isCatchAll: true,
38
+ };
39
+ }
40
+ // Dynamic: :id (new syntax)
41
+ if (segment.startsWith(':')) {
42
+ const paramName = segment.slice(1);
43
+ return {
44
+ raw: segment,
45
+ type: SegmentType.Dynamic,
46
+ paramName,
47
+ };
48
+ }
49
+ // Catch-all: [...slug] (existing syntax)
50
+ if (segment.startsWith('[...') && segment.endsWith(']')) {
51
+ const paramName = segment.slice(4, -1); // Remove [...] wrapper
52
+ return {
53
+ raw: segment,
54
+ type: SegmentType.CatchAll,
55
+ paramName,
56
+ isCatchAll: true,
57
+ };
58
+ }
59
+ // Dynamic: [id] (existing syntax)
60
+ if (segment.startsWith('[') && segment.endsWith(']')) {
61
+ const paramName = segment.slice(1, -1); // Remove [] wrapper
62
+ return {
63
+ raw: segment,
64
+ type: SegmentType.Dynamic,
65
+ paramName,
66
+ };
67
+ }
68
+ // Static segment
69
+ return {
70
+ raw: segment,
71
+ type: SegmentType.Static,
72
+ };
73
+ });
74
+ }
75
+ /** @codexApi {"parent":"pithy.router.matcher","name":"calculateRouteScore","stability":"stable","signature":"(segments: RouteSegment[]) => number","description":"Calculate route precedence score for conflict resolution"} */
76
+ /**
77
+ * Calculate route precedence score for conflict resolution
78
+ * Higher score = higher precedence
79
+ * Rules: Static > Dynamic > CatchAll, Longer > Shorter
80
+ */
81
+ export function calculateRouteScore(segments) {
82
+ let score = 0;
83
+ let segmentCount = 0;
84
+ for (const segment of segments) {
85
+ // Skip groups for URL matching but count for nesting depth
86
+ if (segment.isGroup) {
87
+ score += 0.1; // Small bonus for nesting depth
88
+ continue;
89
+ }
90
+ segmentCount++;
91
+ // Base score by segment type
92
+ score += segment.type * MATCHER_SCORES.SCORE_MULTIPLIER;
93
+ // Length bonus - longer routes are more specific
94
+ score += segmentCount * 10;
95
+ }
96
+ return score;
97
+ }
98
+ /** @codexApi {"parent":"pithy.router.matcher","name":"normalizePath","stability":"stable","signature":"(path: string, policy?: TrailingSlashPolicy) => string","description":"Normalize path according to trailing slash policy"} */
99
+ /**
100
+ * Normalize path according to trailing slash policy
101
+ */
102
+ export function normalizePath(path, policy = 'never') {
103
+ // Always preserve root
104
+ if (path === '/' || path === '') {
105
+ return '/';
106
+ }
107
+ switch (policy) {
108
+ case 'never':
109
+ return path.replace(/\/+$/, '') || '/';
110
+ case 'always':
111
+ return path.replace(/\/+$/, '') + '/';
112
+ case 'preserve':
113
+ default:
114
+ return path;
115
+ }
116
+ }
117
+ /** @codexApi {"parent":"pithy.router.matcher","name":"extractParams","stability":"stable","signature":"(pathSegments: string[], routeSegments: RouteSegment[], decodeParams?: boolean) => RouteParams","description":"Extract parameters from a matched path using route segments"} */
118
+ /**
119
+ * Extract parameters from a matched path using route segments
120
+ */
121
+ export function extractParams(pathSegments, routeSegments, decodeParams = true) {
122
+ const params = {};
123
+ let pathIndex = 0;
124
+ for (let routeIndex = 0; routeIndex < routeSegments.length; routeIndex++) {
125
+ const routeSegment = routeSegments[routeIndex];
126
+ if (!routeSegment)
127
+ continue;
128
+ // Skip groups - they don't consume path segments
129
+ if (routeSegment.isGroup) {
130
+ continue;
131
+ }
132
+ // Ensure we have path segments to match
133
+ if (pathIndex >= pathSegments.length) {
134
+ break;
135
+ }
136
+ const pathValue = pathSegments[pathIndex];
137
+ if (!pathValue)
138
+ break;
139
+ if (routeSegment.type === SegmentType.Dynamic && routeSegment.paramName) {
140
+ // Single dynamic parameter
141
+ const value = decodeParams ? decodeURIComponent(pathValue) : pathValue;
142
+ params[routeSegment.paramName] = value;
143
+ pathIndex++;
144
+ }
145
+ else if (routeSegment.type === SegmentType.CatchAll &&
146
+ routeSegment.paramName) {
147
+ // Catch-all parameter - consume remaining segments
148
+ const remainingSegments = pathSegments.slice(pathIndex);
149
+ const values = decodeParams
150
+ ? remainingSegments.map(s => decodeURIComponent(s))
151
+ : remainingSegments;
152
+ params[routeSegment.paramName] = values;
153
+ break; // Catch-all consumes everything
154
+ }
155
+ else if (routeSegment.type === SegmentType.Static) {
156
+ // Static segment - just advance
157
+ pathIndex++;
158
+ }
159
+ }
160
+ return params;
161
+ }
162
+ /** @codexApi {"parent":"pithy.router.matcher","name":"matchRoute","stability":"stable","signature":"(path: string, pattern: string, config?: RouterConfig) => RouteMatch | null","description":"Check if a path matches a route pattern and return match details"} */
163
+ /**
164
+ * Check if a path matches a route pattern
165
+ */
166
+ export function matchRoute(path, pattern, config = {}) {
167
+ // Normalize paths
168
+ const normalizedPath = normalizePath(path, config.trailingSlash);
169
+ const pathSegments = normalizedPath === '/' ? [] : normalizedPath.split('/').filter(Boolean);
170
+ // Parse route pattern
171
+ const routeSegments = parseRoutePattern(pattern);
172
+ const urlSegments = routeSegments.filter(s => !s.isGroup); // Only non-group segments affect URL matching
173
+ // Check if path can match this route
174
+ if (!canPathMatchRoute(pathSegments, urlSegments)) {
175
+ return null;
176
+ }
177
+ // Extract parameters
178
+ const params = extractParams(pathSegments, routeSegments, config.decodeParams);
179
+ // Calculate precedence score
180
+ const score = calculateRouteScore(routeSegments);
181
+ return {
182
+ pattern,
183
+ path: normalizedPath,
184
+ params,
185
+ segments: pathSegments,
186
+ score,
187
+ };
188
+ }
189
+ /**
190
+ * Check if a path can potentially match a route pattern
191
+ * Used for early filtering before expensive parameter extraction
192
+ */
193
+ function canPathMatchRoute(pathSegments, routeSegments) {
194
+ let pathIndex = 0;
195
+ for (let i = 0; i < routeSegments.length; i++) {
196
+ const seg = routeSegments[i];
197
+ if (!seg)
198
+ continue;
199
+ // A catch-all matches the rest (including zero segments) *but only after*
200
+ // all previous segments have matched.
201
+ if (seg.type === SegmentType.CatchAll) {
202
+ return true;
203
+ }
204
+ // If we still have a non-catch-all to match but no path left → no match.
205
+ if (pathIndex >= pathSegments.length) {
206
+ return false;
207
+ }
208
+ const currentPathSegment = pathSegments[pathIndex];
209
+ if (seg.type === SegmentType.Static) {
210
+ if (currentPathSegment !== seg.raw) {
211
+ return false;
212
+ }
213
+ }
214
+ // Dynamic always matches any single segment.
215
+ pathIndex++;
216
+ }
217
+ // All route segments consumed; path must also be fully consumed.
218
+ return pathIndex === pathSegments.length;
219
+ }
220
+ /** @codexApi {"parent":"pithy.router.matcher","name":"findBestMatch","stability":"stable","signature":"(path: string, patterns: string[], config?: RouterConfig) => RouteMatch | null","description":"Find the best matching route by highest precedence score"} */
221
+ /**
222
+ * Find the best matching route from a list of patterns
223
+ * Returns the route with the highest precedence score
224
+ */
225
+ export function findBestMatch(path, patterns, config = {}) {
226
+ const matches = [];
227
+ // Find all matching routes
228
+ for (const pattern of patterns) {
229
+ const match = matchRoute(path, pattern, config);
230
+ if (match) {
231
+ matches.push(match);
232
+ }
233
+ }
234
+ if (matches.length === 0) {
235
+ return null;
236
+ }
237
+ // Sort by precedence score (highest first)
238
+ matches.sort((a, b) => b.score - a.score);
239
+ // Return the best match
240
+ return matches[0] || null;
241
+ }
242
+ //# sourceMappingURL=matcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"matcher.js","sourceRoot":"","sources":["../../../src/router/core/matcher.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,88 @@
1
+ /** @codex
2
+ {
3
+ "id": "pithy.router.core.params",
4
+ "title": "Parameter Parsing and Query String Utilities",
5
+ "category": "runtime"
6
+ }
7
+ */
8
+ import type { RouteParams, QueryParams } from './types';
9
+ /** @codexApi {"parent":"pithy.router.core.params","name":"parseQueryParams","stability":"stable","signature":"(search: string) => QueryParams"} */
10
+ /**
11
+ * Parse query parameters from URL search string
12
+ * Handles multi-value parameters (e.g., ?tag=foo&tag=bar becomes tag: ['foo', 'bar'])
13
+ *
14
+ * @param search - URL search string (e.g., "?foo=bar&baz=qux")
15
+ * @returns Parsed query parameters object
16
+ *
17
+ * @example
18
+ * parseQueryParams("?name=John&age=30") // { name: "John", age: "30" }
19
+ * parseQueryParams("?tag=a&tag=b") // { tag: ["a", "b"] }
20
+ */
21
+ export declare function parseQueryParams(search: string): QueryParams;
22
+ /** @codexApi {"parent":"pithy.router.core.params","name":"stringifyQueryParams","stability":"stable","signature":"(params: QueryParams) => string"} */
23
+ /**
24
+ * Convert query parameters object to URL search string
25
+ * Handles arrays as multiple parameters with the same key
26
+ *
27
+ * @param params - Query parameters object
28
+ * @returns URL search string (without leading ?)
29
+ *
30
+ * @example
31
+ * stringifyQueryParams({ name: "John", age: "30" }) // "name=John&age=30"
32
+ * stringifyQueryParams({ tag: ["a", "b"] }) // "tag=a&tag=b"
33
+ */
34
+ export declare function stringifyQueryParams(params: QueryParams): string;
35
+ /** @codexApi {"parent":"pithy.router.core.params","name":"decodeParam","stability":"stable","signature":"(value: string) => string"} */
36
+ /**
37
+ * Decode a single route parameter value
38
+ * Handles percent-encoded values and special characters
39
+ *
40
+ * @param value - Encoded parameter value
41
+ * @returns Decoded parameter value
42
+ *
43
+ * @example
44
+ * decodeParam("hello%20world") // "hello world"
45
+ * decodeParam("user%2F123") // "user/123"
46
+ */
47
+ export declare function decodeParam(value: string): string;
48
+ /** @codexApi {"parent":"pithy.router.core.params","name":"encodeParam","stability":"stable","signature":"(value: string | number | boolean) => string"} */
49
+ /**
50
+ * Encode a route parameter value for use in URLs
51
+ *
52
+ * @param value - Parameter value to encode
53
+ * @returns Encoded parameter value
54
+ *
55
+ * @example
56
+ * encodeParam("hello world") // "hello%20world"
57
+ * encodeParam("user/123") // "user%2F123"
58
+ */
59
+ export declare function encodeParam(value: string | number | boolean): string;
60
+ /** @codexApi {"parent":"pithy.router.core.params","name":"mergeParams","stability":"stable","signature":"(...paramSets: RouteParams[]) => RouteParams"} */
61
+ /**
62
+ * Merge multiple parameter objects with proper precedence
63
+ * Later parameters override earlier ones
64
+ *
65
+ * @param paramSets - Parameter objects to merge
66
+ * @returns Merged parameters object
67
+ *
68
+ * @example
69
+ * mergeParams({ a: "1" }, { b: "2" }) // { a: "1", b: "2" }
70
+ * mergeParams({ a: "1" }, { a: "2" }) // { a: "2" } (last wins)
71
+ */
72
+ export declare function mergeParams(...paramSets: RouteParams[]): RouteParams;
73
+ /** @codexApi {"parent":"pithy.router.core.params","name":"coerceParam","stability":"stable","signature":"(value: string | string[], type: 'string' | 'number' | 'boolean') => string | number | boolean | (string | number | boolean)[]"} */
74
+ /**
75
+ * Coerce a parameter value to a specific type
76
+ * Useful for converting string parameters to numbers, booleans, etc.
77
+ *
78
+ * @param value - Parameter value (string or array of strings)
79
+ * @param type - Target type ("string" | "number" | "boolean")
80
+ * @returns Coerced value
81
+ *
82
+ * @example
83
+ * coerceParam("123", "number") // 123
84
+ * coerceParam("true", "boolean") // true
85
+ * coerceParam(["1", "2"], "number") // [1, 2]
86
+ */
87
+ export declare function coerceParam(value: string | string[], type: 'string' | 'number' | 'boolean'): string | number | boolean | (string | number | boolean)[];
88
+ //# sourceMappingURL=params.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"params.d.ts","sourceRoot":"","sources":["../../../src/router/core/params.ts"],"names":[],"mappings":"AAAA;;;;;;EAME;AAEF,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAExD,mJAAmJ;AACnJ;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,CAkB5D;AAED,uJAAuJ;AACvJ;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAehE;AAED,wIAAwI;AACxI;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAajD;AAED,2JAA2J;AAC3J;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAEpE;AAED,2JAA2J;AAC3J;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,GAAG,SAAS,EAAE,WAAW,EAAE,GAAG,WAAW,CAEpE;AAED,6OAA6O;AAC7O;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,EACxB,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,GACpC,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,CAAC,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,EAAE,CAoB3D"}
@@ -0,0 +1,150 @@
1
+ /** @codex
2
+ {
3
+ "id": "pithy.router.core.params",
4
+ "title": "Parameter Parsing and Query String Utilities",
5
+ "category": "runtime"
6
+ }
7
+ */
8
+ /** @codexApi {"parent":"pithy.router.core.params","name":"parseQueryParams","stability":"stable","signature":"(search: string) => QueryParams"} */
9
+ /**
10
+ * Parse query parameters from URL search string
11
+ * Handles multi-value parameters (e.g., ?tag=foo&tag=bar becomes tag: ['foo', 'bar'])
12
+ *
13
+ * @param search - URL search string (e.g., "?foo=bar&baz=qux")
14
+ * @returns Parsed query parameters object
15
+ *
16
+ * @example
17
+ * parseQueryParams("?name=John&age=30") // { name: "John", age: "30" }
18
+ * parseQueryParams("?tag=a&tag=b") // { tag: ["a", "b"] }
19
+ */
20
+ export function parseQueryParams(search) {
21
+ const params = {};
22
+ const urlParams = new URLSearchParams(search);
23
+ for (const [key, value] of urlParams.entries()) {
24
+ if (params[key]) {
25
+ // Handle multiple values for same key
26
+ if (Array.isArray(params[key])) {
27
+ params[key].push(value);
28
+ }
29
+ else {
30
+ params[key] = [params[key], value];
31
+ }
32
+ }
33
+ else {
34
+ params[key] = value;
35
+ }
36
+ }
37
+ return params;
38
+ }
39
+ /** @codexApi {"parent":"pithy.router.core.params","name":"stringifyQueryParams","stability":"stable","signature":"(params: QueryParams) => string"} */
40
+ /**
41
+ * Convert query parameters object to URL search string
42
+ * Handles arrays as multiple parameters with the same key
43
+ *
44
+ * @param params - Query parameters object
45
+ * @returns URL search string (without leading ?)
46
+ *
47
+ * @example
48
+ * stringifyQueryParams({ name: "John", age: "30" }) // "name=John&age=30"
49
+ * stringifyQueryParams({ tag: ["a", "b"] }) // "tag=a&tag=b"
50
+ */
51
+ export function stringifyQueryParams(params) {
52
+ const urlParams = new URLSearchParams();
53
+ for (const [key, value] of Object.entries(params)) {
54
+ if (Array.isArray(value)) {
55
+ // Add each array value as separate parameter
56
+ for (const v of value) {
57
+ urlParams.append(key, v);
58
+ }
59
+ }
60
+ else if (value !== undefined && value !== null) {
61
+ urlParams.append(key, String(value));
62
+ }
63
+ }
64
+ return urlParams.toString();
65
+ }
66
+ /** @codexApi {"parent":"pithy.router.core.params","name":"decodeParam","stability":"stable","signature":"(value: string) => string"} */
67
+ /**
68
+ * Decode a single route parameter value
69
+ * Handles percent-encoded values and special characters
70
+ *
71
+ * @param value - Encoded parameter value
72
+ * @returns Decoded parameter value
73
+ *
74
+ * @example
75
+ * decodeParam("hello%20world") // "hello world"
76
+ * decodeParam("user%2F123") // "user/123"
77
+ */
78
+ export function decodeParam(value) {
79
+ try {
80
+ return decodeURIComponent(value);
81
+ }
82
+ catch (err) {
83
+ // If decoding fails, return original value
84
+ if (typeof window !== 'undefined' &&
85
+ import.meta.hot) {
86
+ console.warn(`Failed to decode parameter: ${value}`, err);
87
+ }
88
+ return value;
89
+ }
90
+ }
91
+ /** @codexApi {"parent":"pithy.router.core.params","name":"encodeParam","stability":"stable","signature":"(value: string | number | boolean) => string"} */
92
+ /**
93
+ * Encode a route parameter value for use in URLs
94
+ *
95
+ * @param value - Parameter value to encode
96
+ * @returns Encoded parameter value
97
+ *
98
+ * @example
99
+ * encodeParam("hello world") // "hello%20world"
100
+ * encodeParam("user/123") // "user%2F123"
101
+ */
102
+ export function encodeParam(value) {
103
+ return encodeURIComponent(String(value));
104
+ }
105
+ /** @codexApi {"parent":"pithy.router.core.params","name":"mergeParams","stability":"stable","signature":"(...paramSets: RouteParams[]) => RouteParams"} */
106
+ /**
107
+ * Merge multiple parameter objects with proper precedence
108
+ * Later parameters override earlier ones
109
+ *
110
+ * @param paramSets - Parameter objects to merge
111
+ * @returns Merged parameters object
112
+ *
113
+ * @example
114
+ * mergeParams({ a: "1" }, { b: "2" }) // { a: "1", b: "2" }
115
+ * mergeParams({ a: "1" }, { a: "2" }) // { a: "2" } (last wins)
116
+ */
117
+ export function mergeParams(...paramSets) {
118
+ return Object.assign({}, ...paramSets);
119
+ }
120
+ /** @codexApi {"parent":"pithy.router.core.params","name":"coerceParam","stability":"stable","signature":"(value: string | string[], type: 'string' | 'number' | 'boolean') => string | number | boolean | (string | number | boolean)[]"} */
121
+ /**
122
+ * Coerce a parameter value to a specific type
123
+ * Useful for converting string parameters to numbers, booleans, etc.
124
+ *
125
+ * @param value - Parameter value (string or array of strings)
126
+ * @param type - Target type ("string" | "number" | "boolean")
127
+ * @returns Coerced value
128
+ *
129
+ * @example
130
+ * coerceParam("123", "number") // 123
131
+ * coerceParam("true", "boolean") // true
132
+ * coerceParam(["1", "2"], "number") // [1, 2]
133
+ */
134
+ export function coerceParam(value, type) {
135
+ if (Array.isArray(value)) {
136
+ return value.map(v => coerceParam(v, type));
137
+ }
138
+ switch (type) {
139
+ case 'number': {
140
+ const num = Number(value);
141
+ return isNaN(num) ? value : num;
142
+ }
143
+ case 'boolean':
144
+ return value === 'true' || value === '1' || value === 'yes';
145
+ case 'string':
146
+ default:
147
+ return value;
148
+ }
149
+ }
150
+ //# sourceMappingURL=params.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"params.js","sourceRoot":"","sources":["../../../src/router/core/params.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,90 @@
1
+ /** @codex
2
+ {
3
+ "id": "pithy.router.core.pipeline",
4
+ "title": "Navigation Pipeline - Multi-Phase Navigation Orchestration",
5
+ "category": "runtime"
6
+ }
7
+ */
8
+ import type { NavigationId, RouteMatch, RouteNode, RouterConfig, QueryParams, RouterState, NavigationOptions, GuardContext, GuardFunction, LoaderContext, GuardExecutionResult } from './types';
9
+ /**
10
+ * Navigation source type
11
+ */
12
+ export type NavSource = 'programmatic' | 'intercept' | 'pop' | 'hmr';
13
+ /**
14
+ * Navigation context
15
+ * Provides router state access and callbacks needed during navigation
16
+ */
17
+ export interface NavigationContext {
18
+ getCurrentPath: () => string;
19
+ getCurrentMatch: () => RouteMatch | null;
20
+ updateState: (updates: Partial<RouterState>) => Promise<void>;
21
+ config: RouterConfig;
22
+ routes: RouteNode[];
23
+ routePatterns: string[];
24
+ onLoadGuards: (pattern: string) => Promise<GuardFunction[]>;
25
+ onExecuteGuards: (guards: GuardFunction[], context: GuardContext) => Promise<GuardExecutionResult>;
26
+ onExecuteLoaders: (pattern: string, context: LoaderContext) => Promise<boolean>;
27
+ onShowLoadingUI: (route: RouteNode | undefined, match: RouteMatch, navId: NavigationId, source: string) => Promise<void>;
28
+ onClearLoadingUI: () => void;
29
+ onSaveScrollToHistory: () => void;
30
+ onRestoreSnapshotIfEmpty: () => void;
31
+ onHistoryPush: (state: unknown, url: string) => void;
32
+ onHistoryReplace: (state: unknown, url: string) => void;
33
+ onHistorySnapBack: () => void;
34
+ normalizePath: (path: string) => string;
35
+ parseQueryParams: (search: string) => QueryParams;
36
+ findMatch: (pathname: string) => RouteMatch | null;
37
+ }
38
+ /**
39
+ * Navigation Pipeline
40
+ * Orchestrates multi-phase navigation flow: GUARD → HISTORY → LOADER → MOUNT
41
+ *
42
+ * @codexApi {"parent":"pithy.router.core.pipeline","name":"NavigationPipeline","stability":"stable","signature":"class NavigationPipeline"}
43
+ */
44
+ export declare class NavigationPipeline {
45
+ private nextNavId;
46
+ private activeNavId;
47
+ private activeTransaction;
48
+ /**
49
+ * Execute a navigation transaction
50
+ * Returns true if navigation succeeded, false if aborted/failed
51
+ *
52
+ * @codexApi {"parent":"pithy.router.core.pipeline","name":"execute","stability":"stable","signature":"async execute(path: string, options: NavigationOptions, context: NavigationContext): Promise<boolean>"}
53
+ */
54
+ execute(path: string, options: NavigationOptions, context: NavigationContext): Promise<boolean>;
55
+ /**
56
+ * Create a new navigation transaction
57
+ *
58
+ * @codexApi {"parent":"pithy.router.core.pipeline","name":"createTransaction","stability":"internal","signature":"private createTransaction(id: NavigationId, path: string): NavigationTransaction"}
59
+ */
60
+ private createTransaction;
61
+ /**
62
+ * Cancel active navigation
63
+ * Useful for cleanup when disposing the router
64
+ *
65
+ * @codexApi {"parent":"pithy.router.core.pipeline","name":"cancelActiveNavigation","stability":"stable","signature":"public cancelActiveNavigation(): void"}
66
+ */
67
+ cancelActiveNavigation(): void;
68
+ /**
69
+ * Check if navigation is active
70
+ *
71
+ * @codexApi {"parent":"pithy.router.core.pipeline","name":"isActive","stability":"stable","signature":"isActive(navId: NavigationId): boolean"}
72
+ */
73
+ isActive(navId: NavigationId): boolean;
74
+ /**
75
+ * Get current active navigation ID
76
+ *
77
+ * @codexApi {"parent":"pithy.router.core.pipeline","name":"getActiveNavId","stability":"stable","signature":"getActiveNavId(): NavigationId"}
78
+ */
79
+ getActiveNavId(): NavigationId;
80
+ /**
81
+ * Initialize next nav ID (for HMR or testing)
82
+ */
83
+ initializeNavId(startId: NavigationId): void;
84
+ /**
85
+ * COMMIT phase: Execute loaders, show loading UI, commit route change
86
+ * This is the core navigation commit logic extracted from router.ts
87
+ */
88
+ private commitPhase;
89
+ }
90
+ //# sourceMappingURL=pipeline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../../src/router/core/pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;EAME;AAEF,OAAO,KAAK,EACV,YAAY,EAEZ,UAAU,EACV,SAAS,EACT,YAAY,EACZ,WAAW,EACX,WAAW,EACX,iBAAiB,EACjB,YAAY,EACZ,aAAa,EACb,aAAa,EACb,oBAAoB,EACrB,MAAM,SAAS,CAAC;AAKjB;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,cAAc,GAAG,WAAW,GAAG,KAAK,GAAG,KAAK,CAAC;AAErE;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAEhC,cAAc,EAAE,MAAM,MAAM,CAAC;IAC7B,eAAe,EAAE,MAAM,UAAU,GAAG,IAAI,CAAC;IAGzC,WAAW,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,WAAW,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAG9D,MAAM,EAAE,YAAY,CAAC;IACrB,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,aAAa,EAAE,MAAM,EAAE,CAAC;IAGxB,YAAY,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;IAC5D,eAAe,EAAE,CACf,MAAM,EAAE,aAAa,EAAE,EACvB,OAAO,EAAE,YAAY,KAClB,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACnC,gBAAgB,EAAE,CAChB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,aAAa,KACnB,OAAO,CAAC,OAAO,CAAC,CAAC;IACtB,eAAe,EAAE,CACf,KAAK,EAAE,SAAS,GAAG,SAAS,EAC5B,KAAK,EAAE,UAAU,EACjB,KAAK,EAAE,YAAY,EACnB,MAAM,EAAE,MAAM,KACX,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,qBAAqB,EAAE,MAAM,IAAI,CAAC;IAClC,wBAAwB,EAAE,MAAM,IAAI,CAAC;IAGrC,aAAa,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACrD,gBAAgB,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACxD,iBAAiB,EAAE,MAAM,IAAI,CAAC;IAG9B,aAAa,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACxC,gBAAgB,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,WAAW,CAAC;IAClD,SAAS,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,UAAU,GAAG,IAAI,CAAC;CACpD;AAED;;;;;GAKG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,SAAS,CAAmB;IACpC,OAAO,CAAC,WAAW,CAAmB;IACtC,OAAO,CAAC,iBAAiB,CAAsC;IAE/D;;;;;OAKG;IACG,OAAO,CACX,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,iBAAiB,EAC1B,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,OAAO,CAAC;IA0LnB;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;IAazB;;;;;OAKG;IACI,sBAAsB,IAAI,IAAI;IAOrC;;;;OAIG;IACH,QAAQ,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO;IAatC;;;;OAIG;IACH,cAAc,IAAI,YAAY;IAI9B;;OAEG;IACH,eAAe,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI;IAI5C;;;OAGG;YACW,WAAW;CAqG1B"}