@real-router/core 0.79.0 → 0.79.1

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 (363) hide show
  1. package/dist/cjs/Router-DGUed2F4.js +2 -0
  2. package/dist/cjs/{Router-BRuC-daj.js.map → Router-DGUed2F4.js.map} +1 -1
  3. package/dist/cjs/{Router-BG5yNYUG.d.ts → Router.d.ts} +5 -4
  4. package/dist/cjs/Router.d.ts.map +1 -0
  5. package/dist/cjs/Router.js +2 -0
  6. package/dist/cjs/Router.js.map +1 -0
  7. package/dist/cjs/RouterError.d.ts +182 -0
  8. package/dist/cjs/RouterError.d.ts.map +1 -0
  9. package/dist/cjs/RouterError.js +2 -0
  10. package/dist/cjs/RouterError.js.map +1 -0
  11. package/dist/cjs/api/cloneRouter.d.ts +86 -0
  12. package/dist/cjs/api/cloneRouter.d.ts.map +1 -0
  13. package/dist/cjs/api/cloneRouter.js +2 -0
  14. package/dist/cjs/api/cloneRouter.js.map +1 -0
  15. package/dist/cjs/api/getDependenciesApi.d.ts +7 -0
  16. package/dist/cjs/api/getDependenciesApi.d.ts.map +1 -0
  17. package/dist/cjs/api/getDependenciesApi.js +2 -0
  18. package/dist/cjs/api/getDependenciesApi.js.map +1 -0
  19. package/dist/cjs/api/getLifecycleApi.d.ts +7 -0
  20. package/dist/cjs/api/getLifecycleApi.d.ts.map +1 -0
  21. package/dist/cjs/api/getLifecycleApi.js +2 -0
  22. package/dist/cjs/api/getLifecycleApi.js.map +1 -0
  23. package/dist/cjs/api/getPluginApi.d.ts +7 -0
  24. package/dist/cjs/api/getPluginApi.d.ts.map +1 -0
  25. package/dist/cjs/api/getPluginApi.js +2 -0
  26. package/dist/cjs/api/getPluginApi.js.map +1 -0
  27. package/dist/cjs/api/getRoutesApi.d.ts +7 -0
  28. package/dist/cjs/api/getRoutesApi.d.ts.map +1 -0
  29. package/dist/cjs/api/getRoutesApi.js +2 -0
  30. package/dist/cjs/api/getRoutesApi.js.map +1 -0
  31. package/dist/cjs/api/helpers.js +2 -0
  32. package/dist/cjs/api/helpers.js.map +1 -0
  33. package/dist/cjs/api/types.d.ts +9 -0
  34. package/dist/cjs/api/types.d.ts.map +1 -0
  35. package/dist/cjs/api.d.ts +8 -105
  36. package/dist/cjs/api.js +1 -1
  37. package/dist/cjs/api.js.map +1 -1
  38. package/dist/cjs/{cloneRouter-3AOUeQ8M.js → cloneRouter-Ch25z3Z2.js} +2 -2
  39. package/dist/cjs/{cloneRouter-3AOUeQ8M.js.map → cloneRouter-Ch25z3Z2.js.map} +1 -1
  40. package/dist/cjs/constants.d.ts +25 -0
  41. package/dist/cjs/constants.d.ts.map +1 -0
  42. package/dist/cjs/constants.js +2 -0
  43. package/dist/cjs/constants.js.map +1 -0
  44. package/dist/cjs/createRouter.d.ts +23 -0
  45. package/dist/cjs/createRouter.d.ts.map +1 -0
  46. package/dist/cjs/createRouter.js +2 -0
  47. package/dist/cjs/createRouter.js.map +1 -0
  48. package/dist/cjs/engine/builder/buildTree.js +2 -0
  49. package/dist/cjs/engine/builder/buildTree.js.map +1 -0
  50. package/dist/cjs/engine/builder/computeCaches.js +2 -0
  51. package/dist/cjs/engine/builder/computeCaches.js.map +1 -0
  52. package/dist/cjs/engine/builder/createRouteTree.js +2 -0
  53. package/dist/cjs/engine/builder/createRouteTree.js.map +1 -0
  54. package/dist/cjs/engine/builder/types.d.ts +48 -0
  55. package/dist/cjs/engine/builder/types.d.ts.map +1 -0
  56. package/dist/cjs/engine/createMatcher.d.ts +29 -0
  57. package/dist/cjs/engine/createMatcher.d.ts.map +1 -0
  58. package/dist/cjs/engine/createMatcher.js +2 -0
  59. package/dist/cjs/engine/createMatcher.js.map +1 -0
  60. package/dist/cjs/engine/operations/routeTreeToDefinitions.js +2 -0
  61. package/dist/cjs/engine/operations/routeTreeToDefinitions.js.map +1 -0
  62. package/dist/cjs/engine/path-matcher/SegmentMatcher.d.ts +16 -0
  63. package/dist/cjs/engine/path-matcher/SegmentMatcher.d.ts.map +1 -0
  64. package/dist/cjs/engine/path-matcher/SegmentMatcher.js +2 -0
  65. package/dist/cjs/engine/path-matcher/SegmentMatcher.js.map +1 -0
  66. package/dist/cjs/engine/path-matcher/buildParamMeta.js +2 -0
  67. package/dist/cjs/engine/path-matcher/buildParamMeta.js.map +1 -0
  68. package/dist/cjs/engine/path-matcher/encoding.js +2 -0
  69. package/dist/cjs/engine/path-matcher/encoding.js.map +1 -0
  70. package/dist/cjs/engine/path-matcher/parseSegment.js +2 -0
  71. package/dist/cjs/engine/path-matcher/parseSegment.js.map +1 -0
  72. package/dist/cjs/engine/path-matcher/pathUtils.js +2 -0
  73. package/dist/cjs/engine/path-matcher/pathUtils.js.map +1 -0
  74. package/dist/cjs/engine/path-matcher/percentEncoding.js +2 -0
  75. package/dist/cjs/engine/path-matcher/percentEncoding.js.map +1 -0
  76. package/dist/cjs/engine/path-matcher/registration/buildParts.js +2 -0
  77. package/dist/cjs/engine/path-matcher/registration/buildParts.js.map +1 -0
  78. package/dist/cjs/engine/path-matcher/registration/context.js +2 -0
  79. package/dist/cjs/engine/path-matcher/registration/context.js.map +1 -0
  80. package/dist/cjs/engine/path-matcher/registration/errors.js +2 -0
  81. package/dist/cjs/engine/path-matcher/registration/errors.js.map +1 -0
  82. package/dist/cjs/engine/path-matcher/registration/index.js +2 -0
  83. package/dist/cjs/engine/path-matcher/registration/index.js.map +1 -0
  84. package/dist/cjs/engine/path-matcher/registration/trie.js +2 -0
  85. package/dist/cjs/engine/path-matcher/registration/trie.js.map +1 -0
  86. package/dist/cjs/engine/path-matcher/registration/trieNodes.js +2 -0
  87. package/dist/cjs/engine/path-matcher/registration/trieNodes.js.map +1 -0
  88. package/dist/cjs/{types-DaXCTxi1.d.ts → engine/path-matcher/types.d.ts} +2 -47
  89. package/dist/cjs/engine/path-matcher/types.d.ts.map +1 -0
  90. package/dist/cjs/engine/search-params/decode.js +2 -0
  91. package/dist/cjs/engine/search-params/decode.js.map +1 -0
  92. package/dist/cjs/engine/search-params/encode.js +2 -0
  93. package/dist/cjs/engine/search-params/encode.js.map +1 -0
  94. package/dist/cjs/engine/search-params/searchParams.js +2 -0
  95. package/dist/cjs/engine/search-params/searchParams.js.map +1 -0
  96. package/dist/cjs/engine/search-params/strategies/array.js +2 -0
  97. package/dist/cjs/engine/search-params/strategies/array.js.map +1 -0
  98. package/dist/cjs/engine/search-params/strategies/boolean.js +2 -0
  99. package/dist/cjs/engine/search-params/strategies/boolean.js.map +1 -0
  100. package/dist/cjs/engine/search-params/strategies/index.js +2 -0
  101. package/dist/cjs/engine/search-params/strategies/index.js.map +1 -0
  102. package/dist/cjs/engine/search-params/strategies/null.js +2 -0
  103. package/dist/cjs/engine/search-params/strategies/null.js.map +1 -0
  104. package/dist/cjs/engine/search-params/strategies/number.js +2 -0
  105. package/dist/cjs/engine/search-params/strategies/number.js.map +1 -0
  106. package/dist/cjs/engine/search-params/types.d.ts +72 -0
  107. package/dist/cjs/engine/search-params/types.d.ts.map +1 -0
  108. package/dist/cjs/engine/search-params/utils.js +2 -0
  109. package/dist/cjs/engine/search-params/utils.js.map +1 -0
  110. package/dist/cjs/engine/validation/route-batch.d.ts +38 -0
  111. package/dist/cjs/engine/validation/route-batch.d.ts.map +1 -0
  112. package/dist/cjs/engine/validation/route-batch.js +2 -0
  113. package/dist/cjs/engine/validation/route-batch.js.map +1 -0
  114. package/dist/cjs/engine/validation/routes.js +2 -0
  115. package/dist/cjs/engine/validation/routes.js.map +1 -0
  116. package/dist/cjs/foundation/event-emitter/EventEmitter.js +2 -0
  117. package/dist/cjs/foundation/event-emitter/EventEmitter.js.map +1 -0
  118. package/dist/cjs/foundation/fsm/fsm.js +2 -0
  119. package/dist/cjs/foundation/fsm/fsm.js.map +1 -0
  120. package/dist/cjs/foundation/logger/RouterLogger.js +2 -0
  121. package/dist/cjs/foundation/logger/RouterLogger.js.map +1 -0
  122. package/dist/cjs/foundation/logger/constants.js +2 -0
  123. package/dist/cjs/foundation/logger/constants.js.map +1 -0
  124. package/dist/cjs/getNavigator.d.ts +6 -0
  125. package/dist/cjs/getNavigator.d.ts.map +1 -0
  126. package/dist/cjs/getNavigator.js +2 -0
  127. package/dist/cjs/getNavigator.js.map +1 -0
  128. package/dist/cjs/guards.js +2 -0
  129. package/dist/cjs/guards.js.map +1 -0
  130. package/dist/cjs/helpers.js +2 -0
  131. package/dist/cjs/helpers.js.map +1 -0
  132. package/dist/cjs/index.d.ts +17 -239
  133. package/dist/cjs/index.js +1 -1
  134. package/dist/cjs/internals.d.ts +99 -0
  135. package/dist/cjs/internals.d.ts.map +1 -0
  136. package/dist/cjs/internals.js +2 -0
  137. package/dist/cjs/internals.js.map +1 -0
  138. package/dist/cjs/namespaces/DependenciesNamespace/dependenciesStore.d.ts +10 -0
  139. package/dist/cjs/namespaces/DependenciesNamespace/dependenciesStore.d.ts.map +1 -0
  140. package/dist/cjs/namespaces/DependenciesNamespace/dependenciesStore.js +2 -0
  141. package/dist/cjs/namespaces/DependenciesNamespace/dependenciesStore.js.map +1 -0
  142. package/dist/cjs/namespaces/EventBusNamespace/EventBusNamespace.js +2 -0
  143. package/dist/cjs/namespaces/EventBusNamespace/EventBusNamespace.js.map +1 -0
  144. package/dist/cjs/namespaces/NavigationNamespace/NavigationNamespace.js +2 -0
  145. package/dist/cjs/namespaces/NavigationNamespace/NavigationNamespace.js.map +1 -0
  146. package/dist/cjs/namespaces/NavigationNamespace/constants.js +2 -0
  147. package/dist/cjs/namespaces/NavigationNamespace/constants.js.map +1 -0
  148. package/dist/cjs/namespaces/NavigationNamespace/transition/completeTransition.js +2 -0
  149. package/dist/cjs/namespaces/NavigationNamespace/transition/completeTransition.js.map +1 -0
  150. package/dist/cjs/namespaces/NavigationNamespace/transition/errorHandling.js +2 -0
  151. package/dist/cjs/namespaces/NavigationNamespace/transition/errorHandling.js.map +1 -0
  152. package/dist/cjs/namespaces/NavigationNamespace/transition/guardPhase.js +2 -0
  153. package/dist/cjs/namespaces/NavigationNamespace/transition/guardPhase.js.map +1 -0
  154. package/dist/cjs/namespaces/OptionsNamespace/OptionsNamespace.js +2 -0
  155. package/dist/cjs/namespaces/OptionsNamespace/OptionsNamespace.js.map +1 -0
  156. package/dist/cjs/namespaces/OptionsNamespace/constants.js +2 -0
  157. package/dist/cjs/namespaces/OptionsNamespace/constants.js.map +1 -0
  158. package/dist/cjs/namespaces/OptionsNamespace/helpers.js +2 -0
  159. package/dist/cjs/namespaces/OptionsNamespace/helpers.js.map +1 -0
  160. package/dist/cjs/namespaces/OptionsNamespace/validators.js +2 -0
  161. package/dist/cjs/namespaces/OptionsNamespace/validators.js.map +1 -0
  162. package/dist/cjs/namespaces/PluginsNamespace/PluginsNamespace.js +2 -0
  163. package/dist/cjs/namespaces/PluginsNamespace/PluginsNamespace.js.map +1 -0
  164. package/dist/cjs/namespaces/PluginsNamespace/constants.js +2 -0
  165. package/dist/cjs/namespaces/PluginsNamespace/constants.js.map +1 -0
  166. package/dist/cjs/namespaces/PluginsNamespace/validators.js +2 -0
  167. package/dist/cjs/namespaces/PluginsNamespace/validators.js.map +1 -0
  168. package/dist/cjs/namespaces/RouteLifecycleNamespace/RouteLifecycleNamespace.d.ts +174 -0
  169. package/dist/cjs/namespaces/RouteLifecycleNamespace/RouteLifecycleNamespace.d.ts.map +1 -0
  170. package/dist/cjs/namespaces/RouteLifecycleNamespace/RouteLifecycleNamespace.js +2 -0
  171. package/dist/cjs/namespaces/RouteLifecycleNamespace/RouteLifecycleNamespace.js.map +1 -0
  172. package/dist/cjs/namespaces/RouteLifecycleNamespace/types.d.ts +17 -0
  173. package/dist/cjs/namespaces/RouteLifecycleNamespace/types.d.ts.map +1 -0
  174. package/dist/cjs/namespaces/RouterLifecycleNamespace/RouterLifecycleNamespace.js +2 -0
  175. package/dist/cjs/namespaces/RouterLifecycleNamespace/RouterLifecycleNamespace.js.map +1 -0
  176. package/dist/cjs/namespaces/RouterLifecycleNamespace/constants.js +2 -0
  177. package/dist/cjs/namespaces/RouterLifecycleNamespace/constants.js.map +1 -0
  178. package/dist/cjs/namespaces/RoutesNamespace/RoutesNamespace.js +2 -0
  179. package/dist/cjs/namespaces/RoutesNamespace/RoutesNamespace.js.map +1 -0
  180. package/dist/cjs/namespaces/RoutesNamespace/constants.js +2 -0
  181. package/dist/cjs/namespaces/RoutesNamespace/constants.js.map +1 -0
  182. package/dist/cjs/namespaces/RoutesNamespace/forwardChain.d.ts +5 -0
  183. package/dist/cjs/namespaces/RoutesNamespace/forwardChain.d.ts.map +1 -0
  184. package/dist/cjs/namespaces/RoutesNamespace/forwardChain.js +2 -0
  185. package/dist/cjs/namespaces/RoutesNamespace/forwardChain.js.map +1 -0
  186. package/dist/cjs/namespaces/RoutesNamespace/helpers.js +2 -0
  187. package/dist/cjs/namespaces/RoutesNamespace/helpers.js.map +1 -0
  188. package/dist/cjs/namespaces/RoutesNamespace/routeGuards.js +2 -0
  189. package/dist/cjs/namespaces/RoutesNamespace/routeGuards.js.map +1 -0
  190. package/dist/cjs/namespaces/RoutesNamespace/routesStore.d.ts +42 -0
  191. package/dist/cjs/namespaces/RoutesNamespace/routesStore.d.ts.map +1 -0
  192. package/dist/cjs/namespaces/RoutesNamespace/routesStore.js +2 -0
  193. package/dist/cjs/namespaces/RoutesNamespace/routesStore.js.map +1 -0
  194. package/dist/cjs/namespaces/RoutesNamespace/types.d.ts +57 -0
  195. package/dist/cjs/namespaces/RoutesNamespace/types.d.ts.map +1 -0
  196. package/dist/cjs/namespaces/StateNamespace/StateNamespace.js +2 -0
  197. package/dist/cjs/namespaces/StateNamespace/StateNamespace.js.map +1 -0
  198. package/dist/cjs/namespaces/StateNamespace/helpers.js +2 -0
  199. package/dist/cjs/namespaces/StateNamespace/helpers.js.map +1 -0
  200. package/dist/cjs/routerFSM.js +2 -0
  201. package/dist/cjs/routerFSM.js.map +1 -0
  202. package/dist/cjs/stateMetaStore.js +2 -0
  203. package/dist/cjs/stateMetaStore.js.map +1 -0
  204. package/dist/cjs/transitionPath.js +2 -0
  205. package/dist/cjs/transitionPath.js.map +1 -0
  206. package/dist/cjs/{RouterValidator-CZ8kPkrI.d.ts → types/RouterValidator.d.ts} +2 -2
  207. package/dist/cjs/types/RouterValidator.d.ts.map +1 -0
  208. package/dist/cjs/types/api.d.ts +184 -0
  209. package/dist/cjs/types/api.d.ts.map +1 -0
  210. package/dist/cjs/types/base.d.ts +86 -0
  211. package/dist/cjs/types/base.d.ts.map +1 -0
  212. package/dist/cjs/types/constants.d.ts +72 -0
  213. package/dist/cjs/types/constants.d.ts.map +1 -0
  214. package/dist/cjs/types/internal.d.ts +9 -0
  215. package/dist/cjs/types/internal.d.ts.map +1 -0
  216. package/dist/cjs/types/limits.d.ts +45 -0
  217. package/dist/cjs/types/limits.d.ts.map +1 -0
  218. package/dist/cjs/types/route-node-types.d.ts +46 -0
  219. package/dist/cjs/types/route-node-types.d.ts.map +1 -0
  220. package/dist/cjs/types/router.d.ts +313 -0
  221. package/dist/cjs/types/router.d.ts.map +1 -0
  222. package/dist/cjs/types/tree-changed.d.ts +55 -0
  223. package/dist/cjs/types/tree-changed.d.ts.map +1 -0
  224. package/dist/cjs/types.d.ts +208 -2
  225. package/dist/cjs/types.d.ts.map +1 -0
  226. package/dist/cjs/utils/createRequestScope.d.ts +111 -0
  227. package/dist/cjs/utils/createRequestScope.d.ts.map +1 -0
  228. package/dist/cjs/utils/createRequestScope.js +2 -0
  229. package/dist/cjs/utils/createRequestScope.js.map +1 -0
  230. package/dist/cjs/utils/getStaticPaths.d.ts +7 -0
  231. package/dist/cjs/utils/getStaticPaths.d.ts.map +1 -0
  232. package/dist/cjs/utils/getStaticPaths.js +2 -0
  233. package/dist/cjs/utils/getStaticPaths.js.map +1 -0
  234. package/dist/cjs/utils/hydrateRouter.d.ts +69 -0
  235. package/dist/cjs/utils/hydrateRouter.d.ts.map +1 -0
  236. package/dist/cjs/utils/hydrateRouter.js +2 -0
  237. package/dist/cjs/utils/hydrateRouter.js.map +1 -0
  238. package/dist/cjs/utils/serializeRouterState.d.ts +84 -0
  239. package/dist/cjs/utils/serializeRouterState.d.ts.map +1 -0
  240. package/dist/cjs/utils/serializeRouterState.js +2 -0
  241. package/dist/cjs/utils/serializeRouterState.js.map +1 -0
  242. package/dist/cjs/utils/serializeState.d.ts +50 -0
  243. package/dist/cjs/utils/serializeState.d.ts.map +1 -0
  244. package/dist/cjs/utils/serializeState.js +2 -0
  245. package/dist/cjs/utils/serializeState.js.map +1 -0
  246. package/dist/cjs/utils.d.ts +5 -1
  247. package/dist/cjs/utils.js +1 -1
  248. package/dist/cjs/validation.d.ts +6 -518
  249. package/dist/cjs/wiring/wireNamespaces.js +2 -0
  250. package/dist/cjs/wiring/wireNamespaces.js.map +1 -0
  251. package/dist/esm/Router-Bk6PfSE8.mjs +2 -0
  252. package/dist/esm/{Router-DikzugaD.mjs.map → Router-Bk6PfSE8.mjs.map} +1 -1
  253. package/dist/esm/{Router-Cy75qbgU.d.mts → Router.d.mts} +5 -4
  254. package/dist/esm/Router.d.mts.map +1 -0
  255. package/dist/esm/RouterError.d.mts +182 -0
  256. package/dist/esm/RouterError.d.mts.map +1 -0
  257. package/dist/esm/api/cloneRouter.d.mts +86 -0
  258. package/dist/esm/api/cloneRouter.d.mts.map +1 -0
  259. package/dist/esm/api/getDependenciesApi.d.mts +7 -0
  260. package/dist/esm/api/getDependenciesApi.d.mts.map +1 -0
  261. package/dist/esm/api/getLifecycleApi.d.mts +7 -0
  262. package/dist/esm/api/getLifecycleApi.d.mts.map +1 -0
  263. package/dist/esm/api/getPluginApi.d.mts +7 -0
  264. package/dist/esm/api/getPluginApi.d.mts.map +1 -0
  265. package/dist/esm/api/getRoutesApi.d.mts +7 -0
  266. package/dist/esm/api/getRoutesApi.d.mts.map +1 -0
  267. package/dist/esm/api/types.d.mts +9 -0
  268. package/dist/esm/api/types.d.mts.map +1 -0
  269. package/dist/esm/api.d.mts +8 -105
  270. package/dist/esm/api.mjs +1 -1
  271. package/dist/esm/api.mjs.map +1 -1
  272. package/dist/esm/{cloneRouter-CAh5vA3l.mjs → cloneRouter-D4rnqmi9.mjs} +2 -2
  273. package/dist/esm/{cloneRouter-CAh5vA3l.mjs.map → cloneRouter-D4rnqmi9.mjs.map} +1 -1
  274. package/dist/esm/constants.d.mts +25 -0
  275. package/dist/esm/constants.d.mts.map +1 -0
  276. package/dist/esm/createRouter.d.mts +23 -0
  277. package/dist/esm/createRouter.d.mts.map +1 -0
  278. package/dist/esm/engine/builder/types.d.mts +48 -0
  279. package/dist/esm/engine/builder/types.d.mts.map +1 -0
  280. package/dist/esm/engine/createMatcher.d.mts +29 -0
  281. package/dist/esm/engine/createMatcher.d.mts.map +1 -0
  282. package/dist/esm/engine/path-matcher/SegmentMatcher.d.mts +16 -0
  283. package/dist/esm/engine/path-matcher/SegmentMatcher.d.mts.map +1 -0
  284. package/dist/esm/{types-DaXCTxi1.d.mts → engine/path-matcher/types.d.mts} +2 -47
  285. package/dist/esm/engine/path-matcher/types.d.mts.map +1 -0
  286. package/dist/esm/engine/search-params/types.d.mts +72 -0
  287. package/dist/esm/engine/search-params/types.d.mts.map +1 -0
  288. package/dist/esm/engine/validation/route-batch.d.mts +38 -0
  289. package/dist/esm/engine/validation/route-batch.d.mts.map +1 -0
  290. package/dist/esm/getNavigator.d.mts +6 -0
  291. package/dist/esm/getNavigator.d.mts.map +1 -0
  292. package/dist/esm/index.d.mts +17 -239
  293. package/dist/esm/index.mjs +1 -1
  294. package/dist/esm/internals.d.mts +99 -0
  295. package/dist/esm/internals.d.mts.map +1 -0
  296. package/dist/esm/namespaces/DependenciesNamespace/dependenciesStore.d.mts +10 -0
  297. package/dist/esm/namespaces/DependenciesNamespace/dependenciesStore.d.mts.map +1 -0
  298. package/dist/esm/namespaces/RouteLifecycleNamespace/RouteLifecycleNamespace.d.mts +174 -0
  299. package/dist/esm/namespaces/RouteLifecycleNamespace/RouteLifecycleNamespace.d.mts.map +1 -0
  300. package/dist/esm/namespaces/RouteLifecycleNamespace/types.d.mts +17 -0
  301. package/dist/esm/namespaces/RouteLifecycleNamespace/types.d.mts.map +1 -0
  302. package/dist/esm/namespaces/RoutesNamespace/forwardChain.d.mts +5 -0
  303. package/dist/esm/namespaces/RoutesNamespace/forwardChain.d.mts.map +1 -0
  304. package/dist/esm/namespaces/RoutesNamespace/routesStore.d.mts +42 -0
  305. package/dist/esm/namespaces/RoutesNamespace/routesStore.d.mts.map +1 -0
  306. package/dist/esm/namespaces/RoutesNamespace/types.d.mts +57 -0
  307. package/dist/esm/namespaces/RoutesNamespace/types.d.mts.map +1 -0
  308. package/dist/esm/{RouterValidator-CZ8kPkrI.d.mts → types/RouterValidator.d.mts} +2 -2
  309. package/dist/esm/types/RouterValidator.d.mts.map +1 -0
  310. package/dist/esm/types/api.d.mts +184 -0
  311. package/dist/esm/types/api.d.mts.map +1 -0
  312. package/dist/esm/types/base.d.mts +86 -0
  313. package/dist/esm/types/base.d.mts.map +1 -0
  314. package/dist/esm/types/constants.d.mts +72 -0
  315. package/dist/esm/types/constants.d.mts.map +1 -0
  316. package/dist/esm/types/internal.d.mts +9 -0
  317. package/dist/esm/types/internal.d.mts.map +1 -0
  318. package/dist/esm/types/limits.d.mts +45 -0
  319. package/dist/esm/types/limits.d.mts.map +1 -0
  320. package/dist/esm/types/route-node-types.d.mts +46 -0
  321. package/dist/esm/types/route-node-types.d.mts.map +1 -0
  322. package/dist/esm/types/router.d.mts +313 -0
  323. package/dist/esm/types/router.d.mts.map +1 -0
  324. package/dist/esm/types/tree-changed.d.mts +55 -0
  325. package/dist/esm/types/tree-changed.d.mts.map +1 -0
  326. package/dist/esm/types.d.mts +208 -2
  327. package/dist/esm/types.d.mts.map +1 -0
  328. package/dist/esm/utils/createRequestScope.d.mts +111 -0
  329. package/dist/esm/utils/createRequestScope.d.mts.map +1 -0
  330. package/dist/esm/utils/getStaticPaths.d.mts +7 -0
  331. package/dist/esm/utils/getStaticPaths.d.mts.map +1 -0
  332. package/dist/esm/utils/hydrateRouter.d.mts +69 -0
  333. package/dist/esm/utils/hydrateRouter.d.mts.map +1 -0
  334. package/dist/esm/utils/serializeRouterState.d.mts +84 -0
  335. package/dist/esm/utils/serializeRouterState.d.mts.map +1 -0
  336. package/dist/esm/utils/serializeState.d.mts +50 -0
  337. package/dist/esm/utils/serializeState.d.mts.map +1 -0
  338. package/dist/esm/utils.d.mts +5 -1
  339. package/dist/esm/utils.mjs +1 -1
  340. package/dist/esm/validation.d.mts +6 -518
  341. package/package.json +2 -2
  342. package/dist/cjs/Router-BG5yNYUG.d.ts.map +0 -1
  343. package/dist/cjs/Router-BRuC-daj.js +0 -2
  344. package/dist/cjs/RouterValidator-CZ8kPkrI.d.ts.map +0 -1
  345. package/dist/cjs/api.d.ts.map +0 -1
  346. package/dist/cjs/index-CAnGQpnz.d.ts +0 -974
  347. package/dist/cjs/index-CAnGQpnz.d.ts.map +0 -1
  348. package/dist/cjs/index-DDrryY2U.d.ts +0 -309
  349. package/dist/cjs/index-DDrryY2U.d.ts.map +0 -1
  350. package/dist/cjs/index.d.ts.map +0 -1
  351. package/dist/cjs/types-DaXCTxi1.d.ts.map +0 -1
  352. package/dist/cjs/validation.d.ts.map +0 -1
  353. package/dist/esm/Router-Cy75qbgU.d.mts.map +0 -1
  354. package/dist/esm/Router-DikzugaD.mjs +0 -2
  355. package/dist/esm/RouterValidator-CZ8kPkrI.d.mts.map +0 -1
  356. package/dist/esm/api.d.mts.map +0 -1
  357. package/dist/esm/index-CAnGQpnz.d.mts +0 -974
  358. package/dist/esm/index-CAnGQpnz.d.mts.map +0 -1
  359. package/dist/esm/index-DnSU6huX.d.mts +0 -309
  360. package/dist/esm/index-DnSU6huX.d.mts.map +0 -1
  361. package/dist/esm/index.d.mts.map +0 -1
  362. package/dist/esm/types-DaXCTxi1.d.mts.map +0 -1
  363. package/dist/esm/validation.d.mts.map +0 -1
@@ -1,2 +1,2 @@
1
- const e=require("./Router-BRuC-daj.js"),t=require("./buildParamMeta-DniluAIV.js");function n(t){if(t())throw new e.n(e.T.ROUTER_DISPOSED)}function r(t){if(t())throw new e.n(e.T.REENTRANT_TREE_MUTATION)}const i=new WeakMap;function a(r){let a=i.get(r);if(a)return a;let o=t.u(r),s={makeState:(e,t,n,r)=>(o.validator?.state.validateMakeStateArgs(e,t,n),o.makeState(e,t,n,r?.params)),buildState:(e,t)=>{o.validator?.routes.validateStateBuilderArgs(e,t,`buildState`);let{name:n,params:r}=o.forwardState(e,t);return o.buildStateResolved(n,r)},forwardState:(e,t)=>(o.validator?.routes.validateStateBuilderArgs(e,t,`forwardState`),o.forwardState(e,t)),matchPath:e=>(o.validator?.routes.validateMatchPathArgs(e),o.matchPath(e,o.getOptions())),navigateToState:(e,t)=>(n(o.isDisposed),o.validator?.navigation.validateNavigateToStateArgs(e),t!==void 0&&o.validator?.navigation.validateNavigationOptions(t,`navigateToState`),o.navigateToState(e,t)),setRootPath:e=>{n(o.isDisposed),o.validator?.routes.validateSetRootPathArgs(e),o.setRootPath(e)},getRootPath:o.getRootPath,addEventListener:(e,t)=>(n(o.isDisposed),o.validator?.eventBus.validateListenerArgs(e,t),o.addEventListener(e,t)),buildNavigationState:(e,t={})=>{o.validator?.routes.validateStateBuilderArgs(e,t,`buildNavigationState`);let{name:n,params:r}=o.forwardState(e,t),i=o.buildStateResolved(n,r);if(i)return o.makeState(i.name,i.params,o.buildPath(i.name,i.params),i.meta)},getOptions:o.getOptions,getTree:o.getTree,addInterceptor:(e,t)=>{n(o.isDisposed),o.validator?.plugins.validateAddInterceptorArgs(e,t);let r=o.interceptors.get(e);r||(r=[],o.interceptors.set(e,r)),r.push(t);let i=!1;return()=>{i||(i=!0,r.splice(r.indexOf(t),1))}},getRouteConfig:e=>{let t=o.routeGetStore();if(t.matcher.hasRoute(e))return t.routeCustomFields[e]},extendRouter:t=>{n(o.isDisposed);let i=Object.keys(t);for(let t of i)if(t in r)throw new e.n(e.T.PLUGIN_CONFLICT,{message:`Cannot extend router: property "${t}" already exists`});for(let e of i)r[e]=t[e];let a={keys:i};o.routerExtensions.push(a);let s=!1;return()=>{if(s)return;s=!0;for(let e of a.keys)delete r[e];let e=o.routerExtensions.indexOf(a);e!==-1&&o.routerExtensions.splice(e,1)}},emitTransitionError:e=>{n(o.isDisposed),o.emitTransitionError(e)},claimContextNamespace:t=>{if(n(o.isDisposed),typeof t!=`string`||t===``)throw TypeError(`[claimContextNamespace] namespace must be a non-empty string, got ${typeof t==`string`?`an empty string`:typeof t}`);if(o.contextClaimRecords.has(t))throw new e.n(e.T.CONTEXT_NAMESPACE_ALREADY_CLAIMED,{message:`Cannot claim context namespace: "${t}" is already claimed by another plugin`});return o.contextClaimRecords.add(t),{write(e,n){t===`__proto__`?Object.defineProperty(e.context,t,{value:n,writable:!0,enumerable:!0,configurable:!0}):e.context[t]=n},release(){o.contextClaimRecords.delete(t)}}}};return i.set(r,s),s}function o(e){let r=t.u(e),i=r.routeGetStore().lifecycleNamespace;return{addActivateGuard(e,t){n(r.isDisposed),r.validator?.routes.validateRouteName(e,`addActivateGuard`),r.validator?.lifecycle.validateHandler(t,`addActivateGuard`),i.addCanActivate(e,t)},addDeactivateGuard(e,t){n(r.isDisposed),r.validator?.routes.validateRouteName(e,`addDeactivateGuard`),r.validator?.lifecycle.validateHandler(t,`addDeactivateGuard`),i.addCanDeactivate(e,t)},removeActivateGuard(e){n(r.isDisposed),r.validator?.routes.validateRouteName(e,`removeActivateGuard`),i.clearCanActivate(e,`external`)},removeDeactivateGuard(e){n(r.isDisposed),r.validator?.routes.validateRouteName(e,`removeDeactivateGuard`),i.clearCanDeactivate(e,`external`)}}}function s(n,r,i){let a=t.u(n);if(a.isDisposed())throw new e.n(e.T.ROUTER_DISPOSED);a.validator?.dependencies.validateCloneArgs(r);let s=a.routeGetStore(),c=e.x(s.tree),l=s.config,u=s.resolvedForwardMap,d=s.routeCustomFields,{options:f,dependencies:p,pluginFactories:m,loggerConfig:h}=a.getCloneState(),{definition:g,external:_}=s.lifecycleNamespace.getFactoriesByOrigin(),v={...p,...r},y=i?.logger?{...h,...i.logger}:h,b=new e.t(c,{...f,logger:y},v),x=t.u(b),S=x.routeGetStore(),C=S.lifecycleNamespace;e._(S.config,l),Object.assign(S.resolvedForwardMap,u),Object.assign(S.routeCustomFields,d),s.rootPath!==``&&x.setRootPath(s.rootPath);let[w,T]=g,[E,D]=_;for(let[e,t]of Object.entries(w))C.addCanDeactivate(e,t,!0);for(let[e,t]of Object.entries(T))C.addCanActivate(e,t,!0);let O=o(b);for(let[e,t]of Object.entries(E))O.addDeactivateGuard(e,t);for(let[e,t]of Object.entries(D))O.addActivateGuard(e,t);let k=new Set(x.getCloneState().pluginFactories),A=m.filter(e=>!k.has(e));return A.length>0&&b.usePlugin(...A),b}Object.defineProperty(exports,"a",{enumerable:!0,get:function(){return r}}),Object.defineProperty(exports,"i",{enumerable:!0,get:function(){return n}}),Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return o}}),Object.defineProperty(exports,"r",{enumerable:!0,get:function(){return a}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return s}});
2
- //# sourceMappingURL=cloneRouter-3AOUeQ8M.js.map
1
+ const e=require("./Router-DGUed2F4.js"),t=require("./buildParamMeta-DniluAIV.js");function n(t){if(t())throw new e.n(e.T.ROUTER_DISPOSED)}function r(t){if(t())throw new e.n(e.T.REENTRANT_TREE_MUTATION)}const i=new WeakMap;function a(r){let a=i.get(r);if(a)return a;let o=t.u(r),s={makeState:(e,t,n,r)=>(o.validator?.state.validateMakeStateArgs(e,t,n),o.makeState(e,t,n,r?.params)),buildState:(e,t)=>{o.validator?.routes.validateStateBuilderArgs(e,t,`buildState`);let{name:n,params:r}=o.forwardState(e,t);return o.buildStateResolved(n,r)},forwardState:(e,t)=>(o.validator?.routes.validateStateBuilderArgs(e,t,`forwardState`),o.forwardState(e,t)),matchPath:e=>(o.validator?.routes.validateMatchPathArgs(e),o.matchPath(e,o.getOptions())),navigateToState:(e,t)=>(n(o.isDisposed),o.validator?.navigation.validateNavigateToStateArgs(e),t!==void 0&&o.validator?.navigation.validateNavigationOptions(t,`navigateToState`),o.navigateToState(e,t)),setRootPath:e=>{n(o.isDisposed),o.validator?.routes.validateSetRootPathArgs(e),o.setRootPath(e)},getRootPath:o.getRootPath,addEventListener:(e,t)=>(n(o.isDisposed),o.validator?.eventBus.validateListenerArgs(e,t),o.addEventListener(e,t)),buildNavigationState:(e,t={})=>{o.validator?.routes.validateStateBuilderArgs(e,t,`buildNavigationState`);let{name:n,params:r}=o.forwardState(e,t),i=o.buildStateResolved(n,r);if(i)return o.makeState(i.name,i.params,o.buildPath(i.name,i.params),i.meta)},getOptions:o.getOptions,getTree:o.getTree,addInterceptor:(e,t)=>{n(o.isDisposed),o.validator?.plugins.validateAddInterceptorArgs(e,t);let r=o.interceptors.get(e);r||(r=[],o.interceptors.set(e,r)),r.push(t);let i=!1;return()=>{i||(i=!0,r.splice(r.indexOf(t),1))}},getRouteConfig:e=>{let t=o.routeGetStore();if(t.matcher.hasRoute(e))return t.routeCustomFields[e]},extendRouter:t=>{n(o.isDisposed);let i=Object.keys(t);for(let t of i)if(t in r)throw new e.n(e.T.PLUGIN_CONFLICT,{message:`Cannot extend router: property "${t}" already exists`});for(let e of i)r[e]=t[e];let a={keys:i};o.routerExtensions.push(a);let s=!1;return()=>{if(s)return;s=!0;for(let e of a.keys)delete r[e];let e=o.routerExtensions.indexOf(a);e!==-1&&o.routerExtensions.splice(e,1)}},emitTransitionError:e=>{n(o.isDisposed),o.emitTransitionError(e)},claimContextNamespace:t=>{if(n(o.isDisposed),typeof t!=`string`||t===``)throw TypeError(`[claimContextNamespace] namespace must be a non-empty string, got ${typeof t==`string`?`an empty string`:typeof t}`);if(o.contextClaimRecords.has(t))throw new e.n(e.T.CONTEXT_NAMESPACE_ALREADY_CLAIMED,{message:`Cannot claim context namespace: "${t}" is already claimed by another plugin`});return o.contextClaimRecords.add(t),{write(e,n){t===`__proto__`?Object.defineProperty(e.context,t,{value:n,writable:!0,enumerable:!0,configurable:!0}):e.context[t]=n},release(){o.contextClaimRecords.delete(t)}}}};return i.set(r,s),s}function o(e){let r=t.u(e),i=r.routeGetStore().lifecycleNamespace;return{addActivateGuard(e,t){n(r.isDisposed),r.validator?.routes.validateRouteName(e,`addActivateGuard`),r.validator?.lifecycle.validateHandler(t,`addActivateGuard`),i.addCanActivate(e,t)},addDeactivateGuard(e,t){n(r.isDisposed),r.validator?.routes.validateRouteName(e,`addDeactivateGuard`),r.validator?.lifecycle.validateHandler(t,`addDeactivateGuard`),i.addCanDeactivate(e,t)},removeActivateGuard(e){n(r.isDisposed),r.validator?.routes.validateRouteName(e,`removeActivateGuard`),i.clearCanActivate(e,`external`)},removeDeactivateGuard(e){n(r.isDisposed),r.validator?.routes.validateRouteName(e,`removeDeactivateGuard`),i.clearCanDeactivate(e,`external`)}}}function s(n,r,i){let a=t.u(n);if(a.isDisposed())throw new e.n(e.T.ROUTER_DISPOSED);a.validator?.dependencies.validateCloneArgs(r);let s=a.routeGetStore(),c=e.x(s.tree),l=s.config,u=s.resolvedForwardMap,d=s.routeCustomFields,{options:f,dependencies:p,pluginFactories:m,loggerConfig:h}=a.getCloneState(),{definition:g,external:_}=s.lifecycleNamespace.getFactoriesByOrigin(),v={...p,...r},y=i?.logger?{...h,...i.logger}:h,b=new e.t(c,{...f,logger:y},v),x=t.u(b),S=x.routeGetStore(),C=S.lifecycleNamespace;e._(S.config,l),Object.assign(S.resolvedForwardMap,u),Object.assign(S.routeCustomFields,d),s.rootPath!==``&&x.setRootPath(s.rootPath);let[w,T]=g,[E,D]=_;for(let[e,t]of Object.entries(w))C.addCanDeactivate(e,t,!0);for(let[e,t]of Object.entries(T))C.addCanActivate(e,t,!0);let O=o(b);for(let[e,t]of Object.entries(E))O.addDeactivateGuard(e,t);for(let[e,t]of Object.entries(D))O.addActivateGuard(e,t);let k=new Set(x.getCloneState().pluginFactories),A=m.filter(e=>!k.has(e));return A.length>0&&b.usePlugin(...A),b}Object.defineProperty(exports,"a",{enumerable:!0,get:function(){return r}}),Object.defineProperty(exports,"i",{enumerable:!0,get:function(){return n}}),Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return o}}),Object.defineProperty(exports,"r",{enumerable:!0,get:function(){return a}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return s}});
2
+ //# sourceMappingURL=cloneRouter-Ch25z3Z2.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cloneRouter-3AOUeQ8M.js","names":["RouterError","errorCodes","getInternals","RouterError","errorCodes","getInternals","getInternals","RouterError","errorCodes","routeTreeToDefinitions","RouterClass"],"sources":["../../src/api/helpers.ts","../../src/api/getPluginApi.ts","../../src/api/getLifecycleApi.ts","../../src/api/cloneRouter.ts"],"sourcesContent":["// packages/core/src/api/helpers.ts\n\nimport { errorCodes } from \"../constants\";\nimport { RouterError } from \"../RouterError\";\n\nexport function throwIfDisposed(isDisposed: () => boolean): void {\n if (isDisposed()) {\n throw new RouterError(errorCodes.ROUTER_DISPOSED);\n }\n}\n\n/**\n * Bans synchronous reentrant route-CRUD: a CRUD op called while a `TREE_CHANGED`\n * emit is on the stack (i.e. from inside a `subscribeChanges` handler) throws\n * `REENTRANT_TREE_MUTATION` BEFORE mutating — the tree stays atomic (#1032).\n * Deferred CRUD (`queueMicrotask` / `await`) runs after the dispatch settles and\n * is unaffected; CRUD from a transition listener is not a TREE_CHANGED dispatch.\n */\nexport function throwIfReentrantTreeMutation(isEmitting: () => boolean): void {\n if (isEmitting()) {\n throw new RouterError(errorCodes.REENTRANT_TREE_MUTATION);\n }\n}\n","import { throwIfDisposed } from \"./helpers\";\nimport { errorCodes } from \"../constants\";\nimport { getInternals } from \"../internals\";\nimport { RouterError } from \"../RouterError\";\n\nimport type { PluginApi } from \"./types\";\nimport type {\n ContextNamespaceClaim,\n DefaultDependencies,\n Params,\n Router,\n State,\n} from \"../types\";\n\n// Cache the assembled PluginApi per router — mirrors getNavigator() (#525):\n// avoids re-allocating the closure-bag on each call (plugins call this once\n// at init, but tests + nested plugins poll it), and gives spy/stub helpers\n// a stable object identity to attach to (e.g. spying on\n// `getPluginApi(router).navigateToState` to inject errors in popstate\n// recovery tests).\nconst cache = new WeakMap<object, PluginApi>();\n\nexport function getPluginApi<\n Dependencies extends DefaultDependencies = DefaultDependencies,\n>(router: Router<Dependencies>): PluginApi {\n const cached = cache.get(router);\n\n if (cached) {\n return cached;\n }\n\n const ctx = getInternals(router);\n const api: PluginApi = {\n makeState: (name, params, path, meta) => {\n ctx.validator?.state.validateMakeStateArgs(name, params, path);\n\n return ctx.makeState(\n name,\n params,\n path,\n meta?.params as\n Record<string, Record<string, \"url\" | \"query\">> | undefined,\n );\n },\n buildState: (routeName, routeParams) => {\n ctx.validator?.routes.validateStateBuilderArgs(\n routeName,\n routeParams,\n \"buildState\",\n );\n\n const { name, params } = ctx.forwardState(routeName, routeParams);\n\n return ctx.buildStateResolved(name, params);\n },\n forwardState: <P extends Params = Params>(\n routeName: string,\n routeParams: P,\n ) => {\n ctx.validator?.routes.validateStateBuilderArgs(\n routeName,\n routeParams,\n \"forwardState\",\n );\n\n return ctx.forwardState(routeName, routeParams);\n },\n matchPath: (path) => {\n ctx.validator?.routes.validateMatchPathArgs(path);\n\n return ctx.matchPath(path, ctx.getOptions());\n },\n navigateToState: (state, options) => {\n throwIfDisposed(ctx.isDisposed);\n\n ctx.validator?.navigation.validateNavigateToStateArgs(state);\n\n if (options !== undefined) {\n ctx.validator?.navigation.validateNavigationOptions(\n options,\n \"navigateToState\",\n );\n }\n\n return ctx.navigateToState(state, options);\n },\n setRootPath: (rootPath) => {\n throwIfDisposed(ctx.isDisposed);\n\n ctx.validator?.routes.validateSetRootPathArgs(rootPath);\n\n ctx.setRootPath(rootPath);\n },\n getRootPath: ctx.getRootPath,\n addEventListener: (eventName, cb) => {\n throwIfDisposed(ctx.isDisposed);\n\n ctx.validator?.eventBus.validateListenerArgs(eventName, cb);\n\n return ctx.addEventListener(eventName, cb);\n },\n buildNavigationState: (name, params = {}) => {\n ctx.validator?.routes.validateStateBuilderArgs(\n name,\n params,\n \"buildNavigationState\",\n );\n\n const { name: resolvedName, params: resolvedParams } = ctx.forwardState(\n name,\n params,\n );\n const routeInfo = ctx.buildStateResolved(resolvedName, resolvedParams);\n\n if (!routeInfo) {\n return;\n }\n\n return ctx.makeState(\n routeInfo.name,\n routeInfo.params,\n ctx.buildPath(routeInfo.name, routeInfo.params),\n routeInfo.meta,\n );\n },\n getOptions: ctx.getOptions,\n getTree: ctx.getTree,\n addInterceptor: (method, fn) => {\n throwIfDisposed(ctx.isDisposed);\n ctx.validator?.plugins.validateAddInterceptorArgs(method, fn);\n let list = ctx.interceptors.get(method);\n\n if (!list) {\n list = [];\n ctx.interceptors.set(method, list);\n }\n\n list.push(fn);\n\n // Idempotency flag (#1198). Without it, a double call would `indexOf(fn)`\n // again and splice a DUPLICATE registration of the same fn — silently\n // deactivating another plugin's interceptor whose own unsubscribe was never\n // called. The `Unsubscribe` contract is documented idempotent. The flag\n // guarantees exactly one splice of a still-present `fn`, so no `index !== -1`\n // guard is needed (it would be dead — the second call returns above).\n let removed = false;\n\n return () => {\n if (removed) {\n return;\n }\n\n removed = true;\n list.splice(list.indexOf(fn), 1);\n };\n },\n getRouteConfig: (name) => {\n const store = ctx.routeGetStore();\n\n // Stryker disable next-line ConditionalExpression,BlockStatement: equivalent — a missing route yields routeCustomFields[name] === undefined, identical to this early return\n if (!store.matcher.hasRoute(name)) {\n return;\n }\n\n return store.routeCustomFields[name];\n },\n extendRouter: (extensions: Record<string, unknown>) => {\n throwIfDisposed(ctx.isDisposed);\n\n const keys = Object.keys(extensions);\n\n for (const key of keys) {\n if (key in router) {\n throw new RouterError(errorCodes.PLUGIN_CONFLICT, {\n message: `Cannot extend router: property \"${key}\" already exists`,\n });\n }\n }\n\n for (const key of keys) {\n (router as Record<string, unknown>)[key] = extensions[key];\n }\n\n const extensionRecord = { keys };\n\n ctx.routerExtensions.push(extensionRecord);\n\n let removed = false;\n\n return () => {\n if (removed) {\n return;\n }\n\n removed = true;\n\n for (const key of extensionRecord.keys) {\n delete (router as Record<string, unknown>)[key];\n }\n\n const idx = ctx.routerExtensions.indexOf(extensionRecord);\n\n // Stryker disable next-line ConditionalExpression,EqualityOperator,UnaryOperator,BlockStatement: equivalent — this splice only tidies the `routerExtensions` TRACKING array; the router INSTANCE is cleaned by the `delete router[key]` loop above, and dispose()'s safety-net re-deletes any leaked key harmlessly. So no mutation of this guard/splice is behaviourally observable (full suite green with `===`, `+1`, and an empty body). Contrast the addInterceptor splice, which IS observable through buildPath and is killed behaviourally by invariantGuardMutants.test.ts.\n if (idx !== -1) {\n ctx.routerExtensions.splice(idx, 1);\n }\n };\n },\n emitTransitionError: (error) => {\n throwIfDisposed(ctx.isDisposed);\n ctx.emitTransitionError(error);\n },\n claimContextNamespace: (namespace: string) => {\n throwIfDisposed(ctx.isDisposed);\n\n // Input-shape guard, symmetric with the other always-on invariant guards\n // (subscribe / start / navigateToNotFound each typeof-check their input).\n // A non-string namespace coerces to an inconsistent key (\"42\"); an empty\n // string is a meaningless namespace (#1191 N4).\n if (typeof namespace !== \"string\" || namespace === \"\") {\n throw new TypeError(\n `[claimContextNamespace] namespace must be a non-empty string, got ${\n typeof namespace === \"string\" ? \"an empty string\" : typeof namespace\n }`,\n );\n }\n\n if (ctx.contextClaimRecords.has(namespace)) {\n throw new RouterError(errorCodes.CONTEXT_NAMESPACE_ALREADY_CLAIMED, {\n message: `Cannot claim context namespace: \"${namespace}\" is already claimed by another plugin`,\n });\n }\n\n ctx.contextClaimRecords.add(namespace);\n\n return {\n write(state: State, value: unknown) {\n // `state.context[namespace] = value` dispatches into the inherited\n // Object.prototype.__proto__ setter for the literal key \"__proto__\",\n // swapping the prototype instead of creating an own entry — the data\n // then vanishes from Object.keys / serializeRouterState (#1191 N3).\n // Mirror search-params' assignParam: defineProperty writes a genuine\n // own property; normal names keep the plain-assignment fast path.\n if (namespace === \"__proto__\") {\n Object.defineProperty(state.context, namespace, {\n value,\n writable: true,\n enumerable: true,\n configurable: true,\n });\n } else {\n state.context[namespace] = value;\n }\n },\n release() {\n ctx.contextClaimRecords.delete(namespace);\n },\n } satisfies ContextNamespaceClaim;\n },\n };\n\n cache.set(router, api);\n\n return api;\n}\n","import { throwIfDisposed } from \"./helpers\";\nimport { getInternals } from \"../internals\";\n\nimport type { LifecycleApi } from \"./types\";\nimport type { DefaultDependencies, Router } from \"../types\";\n\nexport function getLifecycleApi<\n Dependencies extends DefaultDependencies = DefaultDependencies,\n>(router: Router<Dependencies>): LifecycleApi<Dependencies> {\n const ctx = getInternals(router);\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- guaranteed set after wiring\n const lifecycleNamespace = ctx.routeGetStore().lifecycleNamespace!;\n\n return {\n addActivateGuard(name, handler) {\n throwIfDisposed(ctx.isDisposed);\n\n ctx.validator?.routes.validateRouteName(name, \"addActivateGuard\");\n ctx.validator?.lifecycle.validateHandler(handler, \"addActivateGuard\");\n\n // Handler-limit enforcement lives at the namespace registration choke point\n // (RouteLifecycleNamespace.#registerHandler) so all paths are bounded\n // uniformly — see #961.\n lifecycleNamespace.addCanActivate(name, handler);\n },\n\n addDeactivateGuard(name, handler) {\n throwIfDisposed(ctx.isDisposed);\n\n ctx.validator?.routes.validateRouteName(name, \"addDeactivateGuard\");\n ctx.validator?.lifecycle.validateHandler(handler, \"addDeactivateGuard\");\n\n lifecycleNamespace.addCanDeactivate(name, handler);\n },\n\n removeActivateGuard(name) {\n throwIfDisposed(ctx.isDisposed);\n\n ctx.validator?.routes.validateRouteName(name, \"removeActivateGuard\");\n\n // Inverse of addActivateGuard (external): clears only the external guard;\n // a route-config (definition) canActivate survives (#1171).\n lifecycleNamespace.clearCanActivate(name, \"external\");\n },\n\n removeDeactivateGuard(name) {\n throwIfDisposed(ctx.isDisposed);\n\n ctx.validator?.routes.validateRouteName(name, \"removeDeactivateGuard\");\n\n // Inverse of addDeactivateGuard (external): clears only the external guard;\n // a route-config (definition) canDeactivate survives (#1171).\n lifecycleNamespace.clearCanDeactivate(name, \"external\");\n },\n };\n}\n","import { errorCodes } from \"../constants\";\nimport { routeTreeToDefinitions } from \"../engine\";\nimport { getInternals } from \"../internals\";\nimport { getLifecycleApi } from \"./getLifecycleApi\";\nimport { assignConfigEntries } from \"../namespaces/RoutesNamespace/helpers\";\nimport { Router as RouterClass } from \"../Router\";\nimport { RouterError } from \"../RouterError\";\n\nimport type {\n DefaultDependencies,\n LoggerConfig,\n Router,\n Route,\n} from \"../types\";\n\n/**\n * Per-clone overrides beyond dependencies.\n */\nexport interface CloneOptions {\n /**\n * Per-clone logger config override, merged **over** the base router's resolved\n * logger config. Primary use: per-request `traceId` in SSR — a fresh\n * `callback` closed over the request id, while `level` inherits the base.\n * Omitted keys inherit the base (level / callback / callbackIgnoresLevel).\n *\n * Override is by **config**, not a logger instance: `RouterLogger` is\n * core-internal (only its `{ log, warn, error }` interface is public), so\n * nothing outside core constructs one — configuration is the whole surface.\n */\n logger?: Partial<LoggerConfig>;\n}\n\n/**\n * Build an independent router instance that shares the route tree, options,\n * lifecycle guards, and plugin factories of `router`. The primary use case\n * is **SSR multi-tenancy** — one base router per process, one clone per\n * request.\n *\n * @param router - Source router (must not be disposed).\n * @param dependencies - Optional per-clone overrides merged on top of the\n * base router's dependencies. Always **fresh per call** in the documented\n * SSR pattern: pass per-request state here, never store it in the base.\n *\n * @remarks\n *\n * **Dependency merge — shallow by design.** `base.dependencies` are spread\n * into the clone via `{ ...sourceDeps, ...dependencies }`. Top-level keys\n * are new objects, but **values are shared by reference**: a `Map`, `Set`,\n * class instance, function, or nested plain object stored in\n * `base.dependencies` is the **same instance** in every clone. Mutations\n * in one clone are visible in the base and in every sibling clone.\n *\n * This is intentional. `structuredClone` of dep values is **not** applied\n * because it would:\n * - strip class prototypes (`new DbClient()` → plain object, methods lost)\n * - reject functions and symbols (`DataCloneError`)\n * - fragment singleton pools (one connection pool per request — pool\n * semantics destroyed)\n * - reject circular references\n *\n * **SSR rule of thumb.** Place values in `base.dependencies` according to\n * their lifecycle:\n *\n * - **Singletons / shared services** → `base.dependencies`. Examples: DB\n * client, connection pool, logger, config, feature-flag client. Process-\n * wide pooling depends on sharing these by reference.\n * - **Per-request state** → the `dependencies` override parameter (or\n * `createRequestScope`'s `deps` argument). Examples: `currentUser`,\n * `traceId`, `sessionId`, `abortSignal`. The override is applied last,\n * so it wins over base keys; pass a fresh object per call.\n *\n * Cross-request data leaks are **only possible** when per-request mutable\n * state is incorrectly placed in `base.dependencies`. The override slot is\n * the safe channel.\n *\n * @example\n * ```typescript\n * // Server boot — singletons only\n * const base = createRouter(routes, options, {\n * db: new DbClient(dbUrl),\n * logger,\n * });\n *\n * // Per request — fresh override per call\n * const clone = cloneRouter(base, {\n * currentUser,\n * traceId,\n * });\n * // clone.deps.db === base.deps.db ✓ shared pool (intentional)\n * // clone.deps.currentUser ✓ unique per request\n * ```\n *\n * @see createRequestScope — `@real-router/core/utils` SSR helper that\n * wraps this function and injects `abortSignal` automatically.\n */\nexport function cloneRouter<\n Dependencies extends DefaultDependencies = DefaultDependencies,\n>(\n router: Router<Dependencies>,\n dependencies?: Dependencies,\n opts?: CloneOptions,\n): RouterClass<Dependencies> {\n const ctx = getInternals(router);\n\n if (ctx.isDisposed()) {\n throw new RouterError(errorCodes.ROUTER_DISPOSED);\n }\n\n ctx.validator?.dependencies.validateCloneArgs(dependencies);\n\n // Get source store directly\n const sourceStore = ctx.routeGetStore();\n const routes = routeTreeToDefinitions(sourceStore.tree);\n const routeConfig = sourceStore.config;\n const resolvedForwardMap = sourceStore.resolvedForwardMap;\n const routeCustomFields = sourceStore.routeCustomFields;\n\n const {\n options,\n dependencies: sourceDeps,\n pluginFactories,\n loggerConfig,\n } = ctx.getCloneState();\n // Origin-aware factory snapshot — definition guards are re-registered with\n // `isFromDefinition=true` on the clone so `replace()` can still strip them\n // via `clearDefinitionGuards()`. External guards take the public lifecycle\n // API path so they survive `replace()` symmetric with the base.\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- guaranteed set after wiring\n const sourceLifecycleNamespace = sourceStore.lifecycleNamespace!;\n const { definition: definitionFactories, external: externalFactories } =\n sourceLifecycleNamespace.getFactoriesByOrigin();\n\n const mergedDeps = {\n ...sourceDeps,\n ...dependencies,\n } as Dependencies;\n\n // The clone builds its OWN logger (isolation, #724) but INHERITS the base's\n // resolved config — frozen options don't carry `logger`, so without this the\n // clone would fall back to the default logger and lose the base's\n // callback/level (an M1 regression the singleton used to mask). A per-request\n // `opts.logger` override (e.g. a traceId-bound callback) merges on top.\n const clonedLoggerConfig: Partial<LoggerConfig> = opts?.logger\n ? { ...loggerConfig, ...opts.logger }\n : loggerConfig;\n\n const newRouter = new RouterClass<Dependencies>(\n routes as Route<Dependencies>[],\n { ...options, logger: clonedLoggerConfig },\n mergedDeps,\n );\n\n const newCtx = getInternals(newRouter);\n const newStore = newCtx.routeGetStore();\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- guaranteed set after wiring\n const newLifecycleNamespace = newStore.lifecycleNamespace!;\n\n // Copy the source config + store-level maps BEFORE re-registering guards\n // (#1331 review): the definition-guard factories re-executed below must\n // observe the fully-built clone (encoders/decoders/defaultParams/custom\n // fields), mirroring the constructor where flushPendingGuards runs after the\n // store is complete. The five RouteConfig sub-maps go through a single\n // enumeration so a newly added config field is carried over automatically\n // (#965); resolvedForwardMap and routeCustomFields are store-level (not part\n // of RouteConfig) and stay explicit.\n assignConfigEntries(newStore.config, routeConfig);\n Object.assign(newStore.resolvedForwardMap, resolvedForwardMap);\n Object.assign(newStore.routeCustomFields, routeCustomFields);\n\n // #1175: carry the source rootPath. It lives in the store (not options/config),\n // and neither routeTreeToDefinitions nor getCloneState include it — so a clone\n // of a base configured with `setRootPath(\"/app\")` would otherwise build/match\n // under \"\" and 404 every request of a sub-path SSR deployment. setRootPath\n // rebuilds the tree in place with the just-copied config; the rebuild is only\n // paid when a rootPath is actually set, and it runs before the definition-guard\n // factories below so they observe the fully-built clone (rootPath included).\n if (sourceStore.rootPath !== \"\") {\n newCtx.setRootPath(sourceStore.rootPath);\n }\n\n const [definitionDeactivate, definitionActivate] = definitionFactories;\n const [externalDeactivate, externalActivate] = externalFactories;\n\n for (const [name, handler] of Object.entries(definitionDeactivate)) {\n newLifecycleNamespace.addCanDeactivate(name, handler, true);\n }\n\n for (const [name, handler] of Object.entries(definitionActivate)) {\n newLifecycleNamespace.addCanActivate(name, handler, true);\n }\n\n const lifecycle = getLifecycleApi(newRouter);\n\n for (const [name, handler] of Object.entries(externalDeactivate)) {\n lifecycle.addDeactivateGuard(name, handler);\n }\n\n for (const [name, handler] of Object.entries(externalActivate)) {\n lifecycle.addActivateGuard(name, handler);\n }\n\n // Plugin replay runs last and skips factories that a (contract-violating)\n // definition-guard factory already registered on the clone during the\n // re-compilation above — without the filter every clone would double-apply\n // such a plugin: once via the factory, once via this replay (#1331 review).\n const alreadyRegistered = new Set(newCtx.getCloneState().pluginFactories);\n const pluginsToReplay = pluginFactories.filter(\n (factory) => !alreadyRegistered.has(factory),\n );\n\n // Stryker disable next-line EqualityOperator: equivalent — `>= 0` is always true, but `usePlugin(...[])` with an empty spread is a no-op, so entering the block on an empty list behaves identically to skipping it. (ConditionalExpression stays live: `→false` skips a real plugin list and is killable.)\n if (pluginsToReplay.length > 0) {\n newRouter.usePlugin(...pluginsToReplay);\n }\n\n return newRouter;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"cloneRouter-Ch25z3Z2.js","names":["RouterError","errorCodes","getInternals","RouterError","errorCodes","getInternals","getInternals","RouterError","errorCodes","routeTreeToDefinitions","RouterClass"],"sources":["../../src/api/helpers.ts","../../src/api/getPluginApi.ts","../../src/api/getLifecycleApi.ts","../../src/api/cloneRouter.ts"],"sourcesContent":["// packages/core/src/api/helpers.ts\n\nimport { errorCodes } from \"../constants\";\nimport { RouterError } from \"../RouterError\";\n\nexport function throwIfDisposed(isDisposed: () => boolean): void {\n if (isDisposed()) {\n throw new RouterError(errorCodes.ROUTER_DISPOSED);\n }\n}\n\n/**\n * Bans synchronous reentrant route-CRUD: a CRUD op called while a `TREE_CHANGED`\n * emit is on the stack (i.e. from inside a `subscribeChanges` handler) throws\n * `REENTRANT_TREE_MUTATION` BEFORE mutating — the tree stays atomic (#1032).\n * Deferred CRUD (`queueMicrotask` / `await`) runs after the dispatch settles and\n * is unaffected; CRUD from a transition listener is not a TREE_CHANGED dispatch.\n */\nexport function throwIfReentrantTreeMutation(isEmitting: () => boolean): void {\n if (isEmitting()) {\n throw new RouterError(errorCodes.REENTRANT_TREE_MUTATION);\n }\n}\n","import { throwIfDisposed } from \"./helpers\";\nimport { errorCodes } from \"../constants\";\nimport { getInternals } from \"../internals\";\nimport { RouterError } from \"../RouterError\";\n\nimport type { PluginApi } from \"./types\";\nimport type {\n ContextNamespaceClaim,\n DefaultDependencies,\n Params,\n Router,\n State,\n} from \"../types\";\n\n// Cache the assembled PluginApi per router — mirrors getNavigator() (#525):\n// avoids re-allocating the closure-bag on each call (plugins call this once\n// at init, but tests + nested plugins poll it), and gives spy/stub helpers\n// a stable object identity to attach to (e.g. spying on\n// `getPluginApi(router).navigateToState` to inject errors in popstate\n// recovery tests).\nconst cache = new WeakMap<object, PluginApi>();\n\nexport function getPluginApi<\n Dependencies extends DefaultDependencies = DefaultDependencies,\n>(router: Router<Dependencies>): PluginApi {\n const cached = cache.get(router);\n\n if (cached) {\n return cached;\n }\n\n const ctx = getInternals(router);\n const api: PluginApi = {\n makeState: (name, params, path, meta) => {\n ctx.validator?.state.validateMakeStateArgs(name, params, path);\n\n return ctx.makeState(\n name,\n params,\n path,\n meta?.params as\n Record<string, Record<string, \"url\" | \"query\">> | undefined,\n );\n },\n buildState: (routeName, routeParams) => {\n ctx.validator?.routes.validateStateBuilderArgs(\n routeName,\n routeParams,\n \"buildState\",\n );\n\n const { name, params } = ctx.forwardState(routeName, routeParams);\n\n return ctx.buildStateResolved(name, params);\n },\n forwardState: <P extends Params = Params>(\n routeName: string,\n routeParams: P,\n ) => {\n ctx.validator?.routes.validateStateBuilderArgs(\n routeName,\n routeParams,\n \"forwardState\",\n );\n\n return ctx.forwardState(routeName, routeParams);\n },\n matchPath: (path) => {\n ctx.validator?.routes.validateMatchPathArgs(path);\n\n return ctx.matchPath(path, ctx.getOptions());\n },\n navigateToState: (state, options) => {\n throwIfDisposed(ctx.isDisposed);\n\n ctx.validator?.navigation.validateNavigateToStateArgs(state);\n\n if (options !== undefined) {\n ctx.validator?.navigation.validateNavigationOptions(\n options,\n \"navigateToState\",\n );\n }\n\n return ctx.navigateToState(state, options);\n },\n setRootPath: (rootPath) => {\n throwIfDisposed(ctx.isDisposed);\n\n ctx.validator?.routes.validateSetRootPathArgs(rootPath);\n\n ctx.setRootPath(rootPath);\n },\n getRootPath: ctx.getRootPath,\n addEventListener: (eventName, cb) => {\n throwIfDisposed(ctx.isDisposed);\n\n ctx.validator?.eventBus.validateListenerArgs(eventName, cb);\n\n return ctx.addEventListener(eventName, cb);\n },\n buildNavigationState: (name, params = {}) => {\n ctx.validator?.routes.validateStateBuilderArgs(\n name,\n params,\n \"buildNavigationState\",\n );\n\n const { name: resolvedName, params: resolvedParams } = ctx.forwardState(\n name,\n params,\n );\n const routeInfo = ctx.buildStateResolved(resolvedName, resolvedParams);\n\n if (!routeInfo) {\n return;\n }\n\n return ctx.makeState(\n routeInfo.name,\n routeInfo.params,\n ctx.buildPath(routeInfo.name, routeInfo.params),\n routeInfo.meta,\n );\n },\n getOptions: ctx.getOptions,\n getTree: ctx.getTree,\n addInterceptor: (method, fn) => {\n throwIfDisposed(ctx.isDisposed);\n ctx.validator?.plugins.validateAddInterceptorArgs(method, fn);\n let list = ctx.interceptors.get(method);\n\n if (!list) {\n list = [];\n ctx.interceptors.set(method, list);\n }\n\n list.push(fn);\n\n // Idempotency flag (#1198). Without it, a double call would `indexOf(fn)`\n // again and splice a DUPLICATE registration of the same fn — silently\n // deactivating another plugin's interceptor whose own unsubscribe was never\n // called. The `Unsubscribe` contract is documented idempotent. The flag\n // guarantees exactly one splice of a still-present `fn`, so no `index !== -1`\n // guard is needed (it would be dead — the second call returns above).\n let removed = false;\n\n return () => {\n if (removed) {\n return;\n }\n\n removed = true;\n list.splice(list.indexOf(fn), 1);\n };\n },\n getRouteConfig: (name) => {\n const store = ctx.routeGetStore();\n\n // Stryker disable next-line ConditionalExpression,BlockStatement: equivalent — a missing route yields routeCustomFields[name] === undefined, identical to this early return\n if (!store.matcher.hasRoute(name)) {\n return;\n }\n\n return store.routeCustomFields[name];\n },\n extendRouter: (extensions: Record<string, unknown>) => {\n throwIfDisposed(ctx.isDisposed);\n\n const keys = Object.keys(extensions);\n\n for (const key of keys) {\n if (key in router) {\n throw new RouterError(errorCodes.PLUGIN_CONFLICT, {\n message: `Cannot extend router: property \"${key}\" already exists`,\n });\n }\n }\n\n for (const key of keys) {\n (router as Record<string, unknown>)[key] = extensions[key];\n }\n\n const extensionRecord = { keys };\n\n ctx.routerExtensions.push(extensionRecord);\n\n let removed = false;\n\n return () => {\n if (removed) {\n return;\n }\n\n removed = true;\n\n for (const key of extensionRecord.keys) {\n delete (router as Record<string, unknown>)[key];\n }\n\n const idx = ctx.routerExtensions.indexOf(extensionRecord);\n\n // Stryker disable next-line ConditionalExpression,EqualityOperator,UnaryOperator,BlockStatement: equivalent — this splice only tidies the `routerExtensions` TRACKING array; the router INSTANCE is cleaned by the `delete router[key]` loop above, and dispose()'s safety-net re-deletes any leaked key harmlessly. So no mutation of this guard/splice is behaviourally observable (full suite green with `===`, `+1`, and an empty body). Contrast the addInterceptor splice, which IS observable through buildPath and is killed behaviourally by invariantGuardMutants.test.ts.\n if (idx !== -1) {\n ctx.routerExtensions.splice(idx, 1);\n }\n };\n },\n emitTransitionError: (error) => {\n throwIfDisposed(ctx.isDisposed);\n ctx.emitTransitionError(error);\n },\n claimContextNamespace: (namespace: string) => {\n throwIfDisposed(ctx.isDisposed);\n\n // Input-shape guard, symmetric with the other always-on invariant guards\n // (subscribe / start / navigateToNotFound each typeof-check their input).\n // A non-string namespace coerces to an inconsistent key (\"42\"); an empty\n // string is a meaningless namespace (#1191 N4).\n if (typeof namespace !== \"string\" || namespace === \"\") {\n throw new TypeError(\n `[claimContextNamespace] namespace must be a non-empty string, got ${\n typeof namespace === \"string\" ? \"an empty string\" : typeof namespace\n }`,\n );\n }\n\n if (ctx.contextClaimRecords.has(namespace)) {\n throw new RouterError(errorCodes.CONTEXT_NAMESPACE_ALREADY_CLAIMED, {\n message: `Cannot claim context namespace: \"${namespace}\" is already claimed by another plugin`,\n });\n }\n\n ctx.contextClaimRecords.add(namespace);\n\n return {\n write(state: State, value: unknown) {\n // `state.context[namespace] = value` dispatches into the inherited\n // Object.prototype.__proto__ setter for the literal key \"__proto__\",\n // swapping the prototype instead of creating an own entry — the data\n // then vanishes from Object.keys / serializeRouterState (#1191 N3).\n // Mirror search-params' assignParam: defineProperty writes a genuine\n // own property; normal names keep the plain-assignment fast path.\n if (namespace === \"__proto__\") {\n Object.defineProperty(state.context, namespace, {\n value,\n writable: true,\n enumerable: true,\n configurable: true,\n });\n } else {\n state.context[namespace] = value;\n }\n },\n release() {\n ctx.contextClaimRecords.delete(namespace);\n },\n } satisfies ContextNamespaceClaim;\n },\n };\n\n cache.set(router, api);\n\n return api;\n}\n","import { throwIfDisposed } from \"./helpers\";\nimport { getInternals } from \"../internals\";\n\nimport type { LifecycleApi } from \"./types\";\nimport type { DefaultDependencies, Router } from \"../types\";\n\nexport function getLifecycleApi<\n Dependencies extends DefaultDependencies = DefaultDependencies,\n>(router: Router<Dependencies>): LifecycleApi<Dependencies> {\n const ctx = getInternals(router);\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- guaranteed set after wiring\n const lifecycleNamespace = ctx.routeGetStore().lifecycleNamespace!;\n\n return {\n addActivateGuard(name, handler) {\n throwIfDisposed(ctx.isDisposed);\n\n ctx.validator?.routes.validateRouteName(name, \"addActivateGuard\");\n ctx.validator?.lifecycle.validateHandler(handler, \"addActivateGuard\");\n\n // Handler-limit enforcement lives at the namespace registration choke point\n // (RouteLifecycleNamespace.#registerHandler) so all paths are bounded\n // uniformly — see #961.\n lifecycleNamespace.addCanActivate(name, handler);\n },\n\n addDeactivateGuard(name, handler) {\n throwIfDisposed(ctx.isDisposed);\n\n ctx.validator?.routes.validateRouteName(name, \"addDeactivateGuard\");\n ctx.validator?.lifecycle.validateHandler(handler, \"addDeactivateGuard\");\n\n lifecycleNamespace.addCanDeactivate(name, handler);\n },\n\n removeActivateGuard(name) {\n throwIfDisposed(ctx.isDisposed);\n\n ctx.validator?.routes.validateRouteName(name, \"removeActivateGuard\");\n\n // Inverse of addActivateGuard (external): clears only the external guard;\n // a route-config (definition) canActivate survives (#1171).\n lifecycleNamespace.clearCanActivate(name, \"external\");\n },\n\n removeDeactivateGuard(name) {\n throwIfDisposed(ctx.isDisposed);\n\n ctx.validator?.routes.validateRouteName(name, \"removeDeactivateGuard\");\n\n // Inverse of addDeactivateGuard (external): clears only the external guard;\n // a route-config (definition) canDeactivate survives (#1171).\n lifecycleNamespace.clearCanDeactivate(name, \"external\");\n },\n };\n}\n","import { errorCodes } from \"../constants\";\nimport { routeTreeToDefinitions } from \"../engine\";\nimport { getInternals } from \"../internals\";\nimport { getLifecycleApi } from \"./getLifecycleApi\";\nimport { assignConfigEntries } from \"../namespaces/RoutesNamespace/helpers\";\nimport { Router as RouterClass } from \"../Router\";\nimport { RouterError } from \"../RouterError\";\n\nimport type {\n DefaultDependencies,\n LoggerConfig,\n Router,\n Route,\n} from \"../types\";\n\n/**\n * Per-clone overrides beyond dependencies.\n */\nexport interface CloneOptions {\n /**\n * Per-clone logger config override, merged **over** the base router's resolved\n * logger config. Primary use: per-request `traceId` in SSR — a fresh\n * `callback` closed over the request id, while `level` inherits the base.\n * Omitted keys inherit the base (level / callback / callbackIgnoresLevel).\n *\n * Override is by **config**, not a logger instance: `RouterLogger` is\n * core-internal (only its `{ log, warn, error }` interface is public), so\n * nothing outside core constructs one — configuration is the whole surface.\n */\n logger?: Partial<LoggerConfig>;\n}\n\n/**\n * Build an independent router instance that shares the route tree, options,\n * lifecycle guards, and plugin factories of `router`. The primary use case\n * is **SSR multi-tenancy** — one base router per process, one clone per\n * request.\n *\n * @param router - Source router (must not be disposed).\n * @param dependencies - Optional per-clone overrides merged on top of the\n * base router's dependencies. Always **fresh per call** in the documented\n * SSR pattern: pass per-request state here, never store it in the base.\n *\n * @remarks\n *\n * **Dependency merge — shallow by design.** `base.dependencies` are spread\n * into the clone via `{ ...sourceDeps, ...dependencies }`. Top-level keys\n * are new objects, but **values are shared by reference**: a `Map`, `Set`,\n * class instance, function, or nested plain object stored in\n * `base.dependencies` is the **same instance** in every clone. Mutations\n * in one clone are visible in the base and in every sibling clone.\n *\n * This is intentional. `structuredClone` of dep values is **not** applied\n * because it would:\n * - strip class prototypes (`new DbClient()` → plain object, methods lost)\n * - reject functions and symbols (`DataCloneError`)\n * - fragment singleton pools (one connection pool per request — pool\n * semantics destroyed)\n * - reject circular references\n *\n * **SSR rule of thumb.** Place values in `base.dependencies` according to\n * their lifecycle:\n *\n * - **Singletons / shared services** → `base.dependencies`. Examples: DB\n * client, connection pool, logger, config, feature-flag client. Process-\n * wide pooling depends on sharing these by reference.\n * - **Per-request state** → the `dependencies` override parameter (or\n * `createRequestScope`'s `deps` argument). Examples: `currentUser`,\n * `traceId`, `sessionId`, `abortSignal`. The override is applied last,\n * so it wins over base keys; pass a fresh object per call.\n *\n * Cross-request data leaks are **only possible** when per-request mutable\n * state is incorrectly placed in `base.dependencies`. The override slot is\n * the safe channel.\n *\n * @example\n * ```typescript\n * // Server boot — singletons only\n * const base = createRouter(routes, options, {\n * db: new DbClient(dbUrl),\n * logger,\n * });\n *\n * // Per request — fresh override per call\n * const clone = cloneRouter(base, {\n * currentUser,\n * traceId,\n * });\n * // clone.deps.db === base.deps.db ✓ shared pool (intentional)\n * // clone.deps.currentUser ✓ unique per request\n * ```\n *\n * @see createRequestScope — `@real-router/core/utils` SSR helper that\n * wraps this function and injects `abortSignal` automatically.\n */\nexport function cloneRouter<\n Dependencies extends DefaultDependencies = DefaultDependencies,\n>(\n router: Router<Dependencies>,\n dependencies?: Dependencies,\n opts?: CloneOptions,\n): RouterClass<Dependencies> {\n const ctx = getInternals(router);\n\n if (ctx.isDisposed()) {\n throw new RouterError(errorCodes.ROUTER_DISPOSED);\n }\n\n ctx.validator?.dependencies.validateCloneArgs(dependencies);\n\n // Get source store directly\n const sourceStore = ctx.routeGetStore();\n const routes = routeTreeToDefinitions(sourceStore.tree);\n const routeConfig = sourceStore.config;\n const resolvedForwardMap = sourceStore.resolvedForwardMap;\n const routeCustomFields = sourceStore.routeCustomFields;\n\n const {\n options,\n dependencies: sourceDeps,\n pluginFactories,\n loggerConfig,\n } = ctx.getCloneState();\n // Origin-aware factory snapshot — definition guards are re-registered with\n // `isFromDefinition=true` on the clone so `replace()` can still strip them\n // via `clearDefinitionGuards()`. External guards take the public lifecycle\n // API path so they survive `replace()` symmetric with the base.\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- guaranteed set after wiring\n const sourceLifecycleNamespace = sourceStore.lifecycleNamespace!;\n const { definition: definitionFactories, external: externalFactories } =\n sourceLifecycleNamespace.getFactoriesByOrigin();\n\n const mergedDeps = {\n ...sourceDeps,\n ...dependencies,\n } as Dependencies;\n\n // The clone builds its OWN logger (isolation, #724) but INHERITS the base's\n // resolved config — frozen options don't carry `logger`, so without this the\n // clone would fall back to the default logger and lose the base's\n // callback/level (an M1 regression the singleton used to mask). A per-request\n // `opts.logger` override (e.g. a traceId-bound callback) merges on top.\n const clonedLoggerConfig: Partial<LoggerConfig> = opts?.logger\n ? { ...loggerConfig, ...opts.logger }\n : loggerConfig;\n\n const newRouter = new RouterClass<Dependencies>(\n routes as Route<Dependencies>[],\n { ...options, logger: clonedLoggerConfig },\n mergedDeps,\n );\n\n const newCtx = getInternals(newRouter);\n const newStore = newCtx.routeGetStore();\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- guaranteed set after wiring\n const newLifecycleNamespace = newStore.lifecycleNamespace!;\n\n // Copy the source config + store-level maps BEFORE re-registering guards\n // (#1331 review): the definition-guard factories re-executed below must\n // observe the fully-built clone (encoders/decoders/defaultParams/custom\n // fields), mirroring the constructor where flushPendingGuards runs after the\n // store is complete. The five RouteConfig sub-maps go through a single\n // enumeration so a newly added config field is carried over automatically\n // (#965); resolvedForwardMap and routeCustomFields are store-level (not part\n // of RouteConfig) and stay explicit.\n assignConfigEntries(newStore.config, routeConfig);\n Object.assign(newStore.resolvedForwardMap, resolvedForwardMap);\n Object.assign(newStore.routeCustomFields, routeCustomFields);\n\n // #1175: carry the source rootPath. It lives in the store (not options/config),\n // and neither routeTreeToDefinitions nor getCloneState include it — so a clone\n // of a base configured with `setRootPath(\"/app\")` would otherwise build/match\n // under \"\" and 404 every request of a sub-path SSR deployment. setRootPath\n // rebuilds the tree in place with the just-copied config; the rebuild is only\n // paid when a rootPath is actually set, and it runs before the definition-guard\n // factories below so they observe the fully-built clone (rootPath included).\n if (sourceStore.rootPath !== \"\") {\n newCtx.setRootPath(sourceStore.rootPath);\n }\n\n const [definitionDeactivate, definitionActivate] = definitionFactories;\n const [externalDeactivate, externalActivate] = externalFactories;\n\n for (const [name, handler] of Object.entries(definitionDeactivate)) {\n newLifecycleNamespace.addCanDeactivate(name, handler, true);\n }\n\n for (const [name, handler] of Object.entries(definitionActivate)) {\n newLifecycleNamespace.addCanActivate(name, handler, true);\n }\n\n const lifecycle = getLifecycleApi(newRouter);\n\n for (const [name, handler] of Object.entries(externalDeactivate)) {\n lifecycle.addDeactivateGuard(name, handler);\n }\n\n for (const [name, handler] of Object.entries(externalActivate)) {\n lifecycle.addActivateGuard(name, handler);\n }\n\n // Plugin replay runs last and skips factories that a (contract-violating)\n // definition-guard factory already registered on the clone during the\n // re-compilation above — without the filter every clone would double-apply\n // such a plugin: once via the factory, once via this replay (#1331 review).\n const alreadyRegistered = new Set(newCtx.getCloneState().pluginFactories);\n const pluginsToReplay = pluginFactories.filter(\n (factory) => !alreadyRegistered.has(factory),\n );\n\n // Stryker disable next-line EqualityOperator: equivalent — `>= 0` is always true, but `usePlugin(...[])` with an empty spread is a no-op, so entering the block on an empty list behaves identically to skipping it. (ConditionalExpression stays live: `→false` skips a real plugin list and is killable.)\n if (pluginsToReplay.length > 0) {\n newRouter.usePlugin(...pluginsToReplay);\n }\n\n return newRouter;\n}\n"],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import { ErrorCodeKeys, ErrorCodeToValueMap, ErrorCodeValues, EventToNameMap } from "./types/constants.js";
2
+ //#region src/constants.d.ts
3
+ type ConstantsKeys = "UNKNOWN_ROUTE";
4
+ type Constants = Record<ConstantsKeys, string>;
5
+ type ErrorCodes = Record<ErrorCodeKeys, ErrorCodeValues>;
6
+ /**
7
+ * Error codes for router operations.
8
+ * Used to identify specific failure scenarios in navigation and lifecycle.
9
+ * Frozen to prevent accidental modifications.
10
+ */
11
+ declare const errorCodes: ErrorCodeToValueMap;
12
+ /**
13
+ * General router constants.
14
+ * Special route names and identifiers.
15
+ */
16
+ declare const UNKNOWN_ROUTE = "@@router/UNKNOWN_ROUTE";
17
+ declare const constants: Constants;
18
+ /**
19
+ * Event names for router event system.
20
+ * Used with addEventListener/removeEventListener for reactive subscriptions.
21
+ */
22
+ declare const events: EventToNameMap;
23
+ //#endregion
24
+ export { Constants, ConstantsKeys, ErrorCodes, UNKNOWN_ROUTE, constants, errorCodes, events };
25
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","names":[],"sources":["../../src/constants.ts"],"mappings":";;KAWY;KAEA,YAAY,OAAO;KAMnB,aAAa,OAAO,eAAe;;;;;;cAOlC,YAAY;;;;;cAqBZ;cAEA,WAAW;;;;;cAsBX,QAAQ"}
@@ -0,0 +1,2 @@
1
+ const e=Object.freeze({ROUTER_NOT_STARTED:`NOT_STARTED`,NO_START_PATH_OR_STATE:`NO_START_PATH_OR_STATE`,ROUTER_ALREADY_STARTED:`ALREADY_STARTED`,ROUTE_NOT_FOUND:`ROUTE_NOT_FOUND`,SAME_STATES:`SAME_STATES`,CANNOT_DEACTIVATE:`CANNOT_DEACTIVATE`,CANNOT_ACTIVATE:`CANNOT_ACTIVATE`,TRANSITION_ERR:`TRANSITION_ERR`,TRANSITION_CANCELLED:`CANCELLED`,ROUTER_DISPOSED:`DISPOSED`,PLUGIN_CONFLICT:`PLUGIN_CONFLICT`,CONTEXT_NAMESPACE_ALREADY_CLAIMED:`CONTEXT_NAMESPACE_ALREADY_CLAIMED`,REENTRANT_NAVIGATION:`REENTRANT_NAVIGATION`,REENTRANT_TREE_MUTATION:`REENTRANT_TREE_MUTATION`}),t=`@@router/UNKNOWN_ROUTE`,n={UNKNOWN_ROUTE:t},r={ROUTER_START:`onStart`,ROUTER_STOP:`onStop`,TRANSITION_START:`onTransitionStart`,TRANSITION_LEAVE_APPROVE:`onTransitionLeaveApprove`,TRANSITION_CANCEL:`onTransitionCancel`,TRANSITION_SUCCESS:`onTransitionSuccess`,TRANSITION_ERROR:`onTransitionError`},i={ROUTER_START:`$start`,ROUTER_STOP:`$stop`,TRANSITION_START:`$$start`,TRANSITION_LEAVE_APPROVE:`$$leaveApprove`,TRANSITION_CANCEL:`$$cancel`,TRANSITION_SUCCESS:`$$success`,TRANSITION_ERROR:`$$error`},a={maxDependencies:100,maxPlugins:50,maxListeners:1e4,warnListeners:1e3,maxLifecycleHandlers:200},o=Object.freeze({}),s=Object.freeze({deactivated:Object.freeze([]),activated:Object.freeze([]),intersection:``}),c=Object.freeze({phase:`activating`,reason:`success`,segments:s});exports.DEFAULT_LIMITS=a,exports.DEFAULT_TRANSITION=c,exports.EMPTY_PARAMS=o,exports.UNKNOWN_ROUTE=t,exports.constants=n,exports.errorCodes=e,exports.events=i,exports.plugins=r;
2
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","names":[],"sources":["../../src/constants.ts"],"sourcesContent":["// packages/core/src/constants.ts\n\nimport type {\n EventToNameMap,\n EventToPluginMap,\n ErrorCodeToValueMap,\n ErrorCodeKeys,\n ErrorCodeValues,\n TransitionMeta,\n} from \"./types\";\n\nexport type ConstantsKeys = \"UNKNOWN_ROUTE\";\n\nexport type Constants = Record<ConstantsKeys, string>;\n\n// =============================================================================\n// Error Codes (migrated from router-error)\n// =============================================================================\n\nexport type ErrorCodes = Record<ErrorCodeKeys, ErrorCodeValues>;\n\n/**\n * Error codes for router operations.\n * Used to identify specific failure scenarios in navigation and lifecycle.\n * Frozen to prevent accidental modifications.\n */\nexport const errorCodes: ErrorCodeToValueMap = Object.freeze({\n ROUTER_NOT_STARTED: \"NOT_STARTED\", // navigate() called before start()\n NO_START_PATH_OR_STATE: \"NO_START_PATH_OR_STATE\", // start() without initial route\n ROUTER_ALREADY_STARTED: \"ALREADY_STARTED\", // start() called twice\n ROUTE_NOT_FOUND: \"ROUTE_NOT_FOUND\", // Navigation to non-existent route\n SAME_STATES: \"SAME_STATES\", // Navigate to current route without reload\n CANNOT_DEACTIVATE: \"CANNOT_DEACTIVATE\", // canDeactivate guard blocked navigation\n CANNOT_ACTIVATE: \"CANNOT_ACTIVATE\", // canActivate guard blocked navigation\n TRANSITION_ERR: \"TRANSITION_ERR\", // Generic transition failure\n TRANSITION_CANCELLED: \"CANCELLED\", // Navigation cancelled by user or new navigation\n ROUTER_DISPOSED: \"DISPOSED\", // Router has been disposed\n PLUGIN_CONFLICT: \"PLUGIN_CONFLICT\", // Plugin tried to extend router with already-existing property\n CONTEXT_NAMESPACE_ALREADY_CLAIMED: \"CONTEXT_NAMESPACE_ALREADY_CLAIMED\", // Plugin tried to claim a context namespace already owned by another plugin\n REENTRANT_NAVIGATION: \"REENTRANT_NAVIGATION\", // navigate() called synchronously from inside a transition-event listener (banned — use await/async listener)\n REENTRANT_TREE_MUTATION: \"REENTRANT_TREE_MUTATION\", // route-CRUD called synchronously from inside a subscribeChanges handler (banned — use await/async/queueMicrotask)\n});\n\n/**\n * General router constants.\n * Special route names and identifiers.\n */\nexport const UNKNOWN_ROUTE = \"@@router/UNKNOWN_ROUTE\";\n\nexport const constants: Constants = {\n UNKNOWN_ROUTE,\n};\n\n/**\n * Plugin method names.\n * Maps to methods that plugins can implement to hook into router lifecycle.\n */\nexport const plugins: EventToPluginMap = {\n ROUTER_START: \"onStart\", // Plugin method called when router starts\n ROUTER_STOP: \"onStop\", // Plugin method called when router stops\n TRANSITION_START: \"onTransitionStart\", // Plugin method called when navigation begins\n TRANSITION_LEAVE_APPROVE: \"onTransitionLeaveApprove\", // Plugin method called when deactivation guards pass\n TRANSITION_CANCEL: \"onTransitionCancel\", // Plugin method called when navigation cancelled\n TRANSITION_SUCCESS: \"onTransitionSuccess\", // Plugin method called when navigation succeeds\n TRANSITION_ERROR: \"onTransitionError\", // Plugin method called when navigation fails\n};\n\n/**\n * Event names for router event system.\n * Used with addEventListener/removeEventListener for reactive subscriptions.\n */\nexport const events: EventToNameMap = {\n ROUTER_START: \"$start\", // Emitted when router.start() succeeds\n ROUTER_STOP: \"$stop\", // Emitted when router.stop() is called\n TRANSITION_START: \"$$start\", // Emitted when navigation begins\n TRANSITION_LEAVE_APPROVE: \"$$leaveApprove\", // Emitted when deactivation guards pass\n TRANSITION_CANCEL: \"$$cancel\", // Emitted when navigation is cancelled\n TRANSITION_SUCCESS: \"$$success\", // Emitted when navigation completes successfully\n TRANSITION_ERROR: \"$$error\", // Emitted when navigation fails\n};\n\nexport const DEFAULT_LIMITS = {\n maxDependencies: 100,\n maxPlugins: 50,\n maxListeners: 10_000,\n warnListeners: 1000,\n maxLifecycleHandlers: 200,\n} as const;\n\nexport const EMPTY_PARAMS: Readonly<Record<string, never>> = Object.freeze({});\n\nconst FROZEN_EMPTY_SEGMENTS = Object.freeze({\n deactivated: Object.freeze([]) as unknown as string[],\n activated: Object.freeze([]) as unknown as string[],\n intersection: \"\",\n});\n\nexport const DEFAULT_TRANSITION = Object.freeze({\n phase: \"activating\",\n reason: \"success\",\n segments: FROZEN_EMPTY_SEGMENTS,\n}) as TransitionMeta;\n"],"mappings":"AA0BA,MAAa,EAAkC,OAAO,OAAO,CAC3D,mBAAoB,cACpB,uBAAwB,yBACxB,uBAAwB,kBACxB,gBAAiB,kBACjB,YAAa,cACb,kBAAmB,oBACnB,gBAAiB,kBACjB,eAAgB,iBAChB,qBAAsB,YACtB,gBAAiB,WACjB,gBAAiB,kBACjB,kCAAmC,oCACnC,qBAAsB,uBACtB,wBAAyB,yBAC3B,CAAC,EAMY,EAAgB,yBAEhB,EAAuB,CAClC,eACF,EAMa,EAA4B,CACvC,aAAc,UACd,YAAa,SACb,iBAAkB,oBAClB,yBAA0B,2BAC1B,kBAAmB,qBACnB,mBAAoB,sBACpB,iBAAkB,mBACpB,EAMa,EAAyB,CACpC,aAAc,SACd,YAAa,QACb,iBAAkB,UAClB,yBAA0B,iBAC1B,kBAAmB,WACnB,mBAAoB,YACpB,iBAAkB,SACpB,EAEa,EAAiB,CAC5B,gBAAiB,IACjB,WAAY,GACZ,aAAc,IACd,cAAe,IACf,qBAAsB,GACxB,EAEa,EAAgD,OAAO,OAAO,CAAC,CAAC,EAEvE,EAAwB,OAAO,OAAO,CAC1C,YAAa,OAAO,OAAO,CAAC,CAAC,EAC7B,UAAW,OAAO,OAAO,CAAC,CAAC,EAC3B,aAAc,EAChB,CAAC,EAEY,EAAqB,OAAO,OAAO,CAC9C,MAAO,aACP,OAAQ,UACR,SAAU,CACZ,CAAC"}
@@ -0,0 +1,23 @@
1
+ import { DefaultDependencies, Options, Route } from "./types/router.js";
2
+ import { Router } from "./Router.js";
3
+ //#region src/createRouter.d.ts
4
+ /**
5
+ * Creates a new router instance.
6
+ *
7
+ * @param routes - Array of route definitions
8
+ * @param options - Router configuration options
9
+ * @param dependencies - Dependencies to inject into the router
10
+ * @returns A new Router instance
11
+ *
12
+ * @example
13
+ * const router = createRouter([
14
+ * { name: 'home', path: '/' },
15
+ * { name: 'users', path: '/users' },
16
+ * ]);
17
+ *
18
+ * router.start('/');
19
+ */
20
+ declare const createRouter: <Dependencies extends DefaultDependencies = DefaultDependencies>(routes?: Route<Dependencies>[], options?: Partial<Options>, dependencies?: Dependencies) => Router<Dependencies>;
21
+ //#endregion
22
+ export { createRouter };
23
+ //# sourceMappingURL=createRouter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createRouter.d.ts","names":[],"sources":["../../src/createRouter.ts"],"mappings":";;;;;;;;;;;;;;;;;;;cAsBa,eACX,qBAAqB,sBAAsB,qBAE3C,SAAQ,MAAM,iBACd,UAAS,QAAQ,UACjB,eAAc,iBACb,OAAO"}
@@ -0,0 +1,2 @@
1
+ const e=require("./Router.js"),t=(t=[],n={},r={})=>new e.Router(t,n,r);exports.createRouter=t;
2
+ //# sourceMappingURL=createRouter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createRouter.js","names":["Router"],"sources":["../../src/createRouter.ts"],"sourcesContent":["// packages/core/src/createRouter.ts\n\nimport { Router } from \"./Router\";\n\nimport type { DefaultDependencies, Options, Route } from \"./types\";\n\n/**\n * Creates a new router instance.\n *\n * @param routes - Array of route definitions\n * @param options - Router configuration options\n * @param dependencies - Dependencies to inject into the router\n * @returns A new Router instance\n *\n * @example\n * const router = createRouter([\n * { name: 'home', path: '/' },\n * { name: 'users', path: '/users' },\n * ]);\n *\n * router.start('/');\n */\nexport const createRouter = <\n Dependencies extends DefaultDependencies = DefaultDependencies,\n>(\n routes: Route<Dependencies>[] = [],\n options: Partial<Options> = {},\n dependencies: Dependencies = {} as Dependencies,\n): Router<Dependencies> => {\n return new Router<Dependencies>(routes, options, dependencies);\n};\n"],"mappings":"+BAsBa,GAGX,EAAgC,CAAC,EACjC,EAA4B,CAAC,EAC7B,EAA6B,CAAC,IAEvB,IAAIA,EAAAA,OAAqB,EAAQ,EAAS,CAAY"}
@@ -0,0 +1,2 @@
1
+ function e(t,n){let r=t.path,i=r.startsWith(`~`),a=i?r.slice(1):r,o={name:t.name,path:a,absolute:i,children:[],parent:n};if(t.children)for(let n of t.children){let t=e(n,o);o.children.push(t)}return o}function t(t,n,r){let i=e({name:t,path:n},null);for(let t of r){let n=e(t,i);i.children.push(n)}return i}exports.buildTree=t;
2
+ //# sourceMappingURL=buildTree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buildTree.js","names":[],"sources":["../../../../src/engine/builder/buildTree.ts"],"sourcesContent":["/**\n * Tree Building.\n *\n * Constructs the mutable tree structure from route definitions.\n *\n * @module builder/buildTree\n */\n\nimport type { RouteDefinition } from \"../types\";\n\n// =============================================================================\n// Mutable Node Type (internal)\n// =============================================================================\n\n/**\n * Mutable version of RouteTree used during building.\n * After building, this is converted to immutable RouteTree.\n */\nexport interface MutableRouteNode {\n name: string;\n path: string;\n absolute: boolean;\n children: MutableRouteNode[];\n parent: MutableRouteNode | null;\n}\n\n// =============================================================================\n// Building Functions\n// =============================================================================\n\n/**\n * Creates a mutable node from a route definition.\n *\n * @param definition - Route definition object\n * @param definition.name - Route name\n * @param definition.path - Route path\n * @param definition.children - Optional child routes\n * @param parent - Parent node (null for root)\n * @returns Mutable node\n */\nfunction createNode(\n definition: {\n name: string;\n path: string;\n children?: RouteDefinition[] | undefined;\n },\n parent: MutableRouteNode | null,\n): MutableRouteNode {\n const path = definition.path;\n const absolute = path.startsWith(\"~\");\n const normalizedPath = absolute ? path.slice(1) : path;\n\n const node: MutableRouteNode = {\n name: definition.name,\n path: normalizedPath,\n absolute,\n children: [],\n parent,\n };\n\n // Recursively add children\n if (definition.children) {\n for (const childDef of definition.children) {\n const childNode = createNode(childDef, node);\n\n node.children.push(childNode);\n }\n }\n\n return node;\n}\n\n/**\n * Builds the mutable tree structure from route definitions.\n *\n * Simplified single-pass algorithm:\n * - Creates root node\n * - Adds each route as direct child of root\n * - createNode() handles nested children recursively\n *\n * @param rootName - Root node name (typically \"\")\n * @param rootPath - Root node path (typically \"\")\n * @param routes - Route definitions\n * @returns Mutable root node\n */\nexport function buildTree(\n rootName: string,\n rootPath: string,\n routes: readonly RouteDefinition[],\n): MutableRouteNode {\n const root = createNode({ name: rootName, path: rootPath }, null);\n\n for (const route of routes) {\n const node = createNode(route, root);\n\n root.children.push(node);\n }\n\n return root;\n}\n"],"mappings":"AAwCA,SAAS,EACP,EAKA,EACkB,CAClB,IAAM,EAAO,EAAW,KAClB,EAAW,EAAK,WAAW,GAAG,EAC9B,EAAiB,EAAW,EAAK,MAAM,CAAC,EAAI,EAE5C,EAAyB,CAC7B,KAAM,EAAW,KACjB,KAAM,EACN,WACA,SAAU,CAAC,EACX,QACF,EAGA,GAAI,EAAW,SACb,IAAK,IAAM,KAAY,EAAW,SAAU,CAC1C,IAAM,EAAY,EAAW,EAAU,CAAI,EAE3C,EAAK,SAAS,KAAK,CAAS,CAC9B,CAGF,OAAO,CACT,CAeA,SAAgB,EACd,EACA,EACA,EACkB,CAClB,IAAM,EAAO,EAAW,CAAE,KAAM,EAAU,KAAM,CAAS,EAAG,IAAI,EAEhE,IAAK,IAAM,KAAS,EAAQ,CAC1B,IAAM,EAAO,EAAW,EAAO,CAAI,EAEnC,EAAK,SAAS,KAAK,CAAI,CACzB,CAEA,OAAO,CACT"}
@@ -0,0 +1,2 @@
1
+ const e=require("../path-matcher/buildParamMeta.js"),t=Object.freeze(new Map),n=Object.freeze([]);function r(e){return e.parent?.name?`${e.parent.fullName}.${e.name}`:e.name}function i(e){let t=new Map;for(let n of e)t.set(n.name,n);return t}function a(e,t){let n=[],r=[];for(let i of e){let e=o(i,t);n.push(e),e.absolute||r.push(e)}return{childrenMap:i(n),nonAbsoluteChildren:r}}function o(i,o){let s=e.buildParamMeta(i.path),c=s.urlParams.length===0&&s.queryParams.length===0&&s.spatParams.length===0&&s.pathPattern===i.path?e.EMPTY_PARAM_META:s,l=c.paramTypeMap,u={name:i.name,path:i.path,absolute:i.absolute,parent:o,children:void 0,paramMeta:c,nonAbsoluteChildren:void 0,fullName:``,paramTypeMap:l};if(u.fullName=r(u),i.children.length===0)u.children=t,u.nonAbsoluteChildren=n;else{let{childrenMap:e,nonAbsoluteChildren:t}=a(i.children,u);u.children=e,u.nonAbsoluteChildren=t,Object.freeze(u.nonAbsoluteChildren),Object.freeze(u.children)}return Object.freeze(l),Object.freeze(c.urlParams),Object.freeze(c.queryParams),Object.freeze(c.spatParams),Object.freeze(c),Object.freeze(u),u}function s(e){return o(e,null)}exports.computeCaches=s;
2
+ //# sourceMappingURL=computeCaches.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"computeCaches.js","names":["buildParamMeta","EMPTY_PARAM_META"],"sources":["../../../../src/engine/builder/computeCaches.ts"],"sourcesContent":["/**\n * Cache Computation.\n *\n * Computes all pre-computed caches and freezes the tree.\n *\n * @module builder/computeCaches\n */\n\nimport { buildParamMeta, EMPTY_PARAM_META } from \"../path-matcher\";\n\nimport type { MutableRouteNode } from \"./buildTree\";\nimport type { RouteTree } from \"../types\";\n\n// =============================================================================\n// Shared Sentinels (avoid per-node allocation for leaf nodes)\n// =============================================================================\n\nconst EMPTY_CHILDREN_MAP: ReadonlyMap<string, RouteTree> = Object.freeze(\n new Map<string, RouteTree>(),\n);\nconst EMPTY_CHILDREN_ARRAY: readonly RouteTree[] = Object.freeze(\n [] as RouteTree[],\n);\n\n// =============================================================================\n// Cache Computation Functions\n// =============================================================================\n\n/**\n * Computes the full dot-notation name for a node.\n * Parent fullName is always computed before children.\n *\n * @param node - Node to compute for\n * @returns Full name (e.g., \"users.profile\")\n */\nfunction computeFullName(node: RouteTree): string {\n if (!node.parent?.name) {\n return node.name;\n }\n\n // Parent fullName is always set before children are processed\n return `${node.parent.fullName}.${node.name}`;\n}\n\n// =============================================================================\n// Main Computation Function\n// =============================================================================\n\n/**\n * Computes children Map from mutable children array.\n *\n * Children are stored in definition order. Matching priority is handled\n * by the segment trie, not by iteration order.\n *\n * @param childrenArray - Array of processed child nodes\n * @returns ReadonlyMap of child name -> child node (in definition order)\n */\nfunction computeChildrenMap(\n childrenArray: RouteTree[],\n): ReadonlyMap<string, RouteTree> {\n const map = new Map<string, RouteTree>();\n\n for (const child of childrenArray) {\n map.set(child.name, child);\n }\n\n return map;\n}\n\n/**\n * Recursively processes child nodes and computes the children map.\n *\n * @param mutableChildren - Array of mutable child nodes\n * @param parent - Already-processed parent node\n * @returns Children map and non-absolute children array\n */\nfunction processChildren(\n mutableChildren: readonly MutableRouteNode[],\n parent: RouteTree,\n): {\n childrenMap: ReadonlyMap<string, RouteTree>;\n nonAbsoluteChildren: RouteTree[];\n} {\n const childrenArray: RouteTree[] = [];\n const nonAbsoluteChildren: RouteTree[] = [];\n\n for (const childMutable of mutableChildren) {\n const child = processNode(childMutable, parent);\n\n childrenArray.push(child);\n\n if (!child.absolute) {\n nonAbsoluteChildren.push(child);\n }\n }\n\n return {\n childrenMap: computeChildrenMap(childrenArray),\n nonAbsoluteChildren,\n };\n}\n\n/**\n * Recursively processes a mutable node into a frozen RouteTree.\n *\n * This creates a new object with all caches computed and freezes it for\n * immutability.\n *\n * @param mutable - Mutable node to process\n * @param parent - Already-processed parent node (null for root)\n * @returns Frozen RouteTree\n */\nfunction processNode(\n mutable: MutableRouteNode,\n parent: RouteTree | null,\n): RouteTree {\n const freshParamMeta = buildParamMeta(mutable.path);\n // Fully-static node: every collection is a #1009 sentinel and pathPattern is\n // reference-equal to the input path (no query was sliced off) — the wrapper\n // carries zero information, so retain the ONE shared frozen EMPTY_PARAM_META\n // instead of a fresh 6-field object per node. The swap happens here (the\n // retaining consumer), NOT inside buildParamMeta: the validation gate reads\n // `pathPattern` off fresh results and must keep seeing the real pattern.\n const paramMeta =\n freshParamMeta.urlParams.length === 0 &&\n freshParamMeta.queryParams.length === 0 &&\n freshParamMeta.spatParams.length === 0 &&\n freshParamMeta.pathPattern === mutable.path\n ? EMPTY_PARAM_META\n : freshParamMeta;\n const paramTypeMap = paramMeta.paramTypeMap;\n\n // Skeleton node: children and nonAbsoluteChildren are set after recursive\n // child processing, which requires a parent reference to this node.\n const node = {\n name: mutable.name,\n path: mutable.path,\n absolute: mutable.absolute,\n parent,\n children: undefined as unknown as ReadonlyMap<string, RouteTree>,\n paramMeta,\n nonAbsoluteChildren: undefined as unknown as RouteTree[],\n // Stryker disable next-line StringLiteral: equivalent — placeholder overwritten unconditionally on the next statement (`node.fullName = computeFullName(node)`); the initial value is never observed.\n fullName: \"\",\n paramTypeMap,\n };\n\n node.fullName = computeFullName(node);\n\n if (mutable.children.length === 0) {\n node.children = EMPTY_CHILDREN_MAP;\n node.nonAbsoluteChildren = EMPTY_CHILDREN_ARRAY as RouteTree[];\n } else {\n const { childrenMap, nonAbsoluteChildren } = processChildren(\n mutable.children,\n node,\n );\n\n node.children = childrenMap;\n node.nonAbsoluteChildren = nonAbsoluteChildren;\n\n Object.freeze(node.nonAbsoluteChildren);\n Object.freeze(node.children);\n }\n\n Object.freeze(paramTypeMap);\n\n // Close the immutability contract on the nested paramMeta (#747): the node\n // is frozen, but its paramMeta object and arrays were left mutable, so a\n // tree reachable from the public API could be mutated. paramTypeMap is the\n // same ref frozen just above.\n Object.freeze(paramMeta.urlParams);\n Object.freeze(paramMeta.queryParams);\n Object.freeze(paramMeta.spatParams);\n Object.freeze(paramMeta);\n\n Object.freeze(node);\n\n return node;\n}\n\n/**\n * Computes all caches and freezes the tree.\n *\n * This is the final step in building a RouteTree — the result is completely\n * immutable.\n *\n * @param mutableRoot - Mutable root node\n * @returns Frozen RouteTree\n */\nexport function computeCaches(mutableRoot: MutableRouteNode): RouteTree {\n return processNode(mutableRoot, null);\n}\n"],"mappings":"qDAiBM,EAAqD,OAAO,OAChE,IAAI,GACN,EACM,EAA6C,OAAO,OACxD,CAAC,CACH,EAaA,SAAS,EAAgB,EAAyB,CAMhD,OALK,EAAK,QAAQ,KAKX,GAAG,EAAK,OAAO,SAAS,GAAG,EAAK,OAJ9B,EAAK,IAKhB,CAeA,SAAS,EACP,EACgC,CAChC,IAAM,EAAM,IAAI,IAEhB,IAAK,IAAM,KAAS,EAClB,EAAI,IAAI,EAAM,KAAM,CAAK,EAG3B,OAAO,CACT,CASA,SAAS,EACP,EACA,EAIA,CACA,IAAM,EAA6B,CAAC,EAC9B,EAAmC,CAAC,EAE1C,IAAK,IAAM,KAAgB,EAAiB,CAC1C,IAAM,EAAQ,EAAY,EAAc,CAAM,EAE9C,EAAc,KAAK,CAAK,EAEnB,EAAM,UACT,EAAoB,KAAK,CAAK,CAElC,CAEA,MAAO,CACL,YAAa,EAAmB,CAAa,EAC7C,qBACF,CACF,CAYA,SAAS,EACP,EACA,EACW,CACX,IAAM,EAAiBA,EAAAA,eAAe,EAAQ,IAAI,EAO5C,EACJ,EAAe,UAAU,SAAW,GACpC,EAAe,YAAY,SAAW,GACtC,EAAe,WAAW,SAAW,GACrC,EAAe,cAAgB,EAAQ,KACnCC,EAAAA,iBACA,EACA,EAAe,EAAU,aAIzB,EAAO,CACX,KAAM,EAAQ,KACd,KAAM,EAAQ,KACd,SAAU,EAAQ,SAClB,SACA,SAAU,IAAA,GACV,YACA,oBAAqB,IAAA,GAErB,SAAU,GACV,cACF,EAIA,GAFA,EAAK,SAAW,EAAgB,CAAI,EAEhC,EAAQ,SAAS,SAAW,EAC9B,EAAK,SAAW,EAChB,EAAK,oBAAsB,MACtB,CACL,GAAM,CAAE,cAAa,uBAAwB,EAC3C,EAAQ,SACR,CACF,EAEA,EAAK,SAAW,EAChB,EAAK,oBAAsB,EAE3B,OAAO,OAAO,EAAK,mBAAmB,EACtC,OAAO,OAAO,EAAK,QAAQ,CAC7B,CAeA,OAbA,OAAO,OAAO,CAAY,EAM1B,OAAO,OAAO,EAAU,SAAS,EACjC,OAAO,OAAO,EAAU,WAAW,EACnC,OAAO,OAAO,EAAU,UAAU,EAClC,OAAO,OAAO,CAAS,EAEvB,OAAO,OAAO,CAAI,EAEX,CACT,CAWA,SAAgB,EAAc,EAA0C,CACtE,OAAO,EAAY,EAAa,IAAI,CACtC"}
@@ -0,0 +1,2 @@
1
+ const e=require("./buildTree.js"),t=require("./computeCaches.js");function n(n,r,i){return t.computeCaches(e.buildTree(n,r,i))}exports.createRouteTree=n;
2
+ //# sourceMappingURL=createRouteTree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createRouteTree.js","names":["computeCaches","buildTree"],"sources":["../../../../src/engine/builder/createRouteTree.ts"],"sourcesContent":["/**\n * Route Tree Builder.\n *\n * Creates an immutable RouteTree from route definitions.\n *\n * @module builder/createRouteTree\n */\n\nimport { buildTree } from \"./buildTree\";\nimport { computeCaches } from \"./computeCaches\";\n\nimport type { RouteDefinition, RouteTree } from \"../types\";\n\n/**\n * Creates an immutable RouteTree from route definitions.\n *\n * @param name - Root node name (typically empty string)\n * @param path - Root node path (typically empty string)\n * @param routes - Route definitions to add\n * @returns Immutable RouteTree\n *\n * @example\n * ```typescript\n * const tree = createRouteTree(\"\", \"\", [\n * { name: \"users\", path: \"/users\" },\n * { name: \"users.profile\", path: \"/:id\" },\n * ]);\n * ```\n */\nexport function createRouteTree(\n name: string,\n path: string,\n routes: readonly RouteDefinition[],\n): RouteTree {\n return computeCaches(buildTree(name, path, routes));\n}\n"],"mappings":"kEA6BA,SAAgB,EACd,EACA,EACA,EACW,CACX,OAAOA,EAAAA,cAAcC,EAAAA,UAAU,EAAM,EAAM,CAAM,CAAC,CACpD"}
@@ -0,0 +1,48 @@
1
+ import { ParamMeta } from "../path-matcher/types.js";
2
+ //#region src/engine/builder/types.d.ts
3
+ /**
4
+ * Definition of a route for configuration.
5
+ */
6
+ interface RouteDefinition {
7
+ [key: string]: unknown;
8
+ name: string;
9
+ path: string;
10
+ children?: RouteDefinition[] | undefined;
11
+ }
12
+ /**
13
+ * Immutable route tree node.
14
+ *
15
+ * This is the core data structure of the new route-tree architecture.
16
+ * It contains only data (no methods) and is created by the builder.
17
+ *
18
+ * All caches are pre-computed at build time:
19
+ * - nonAbsoluteChildren: filtered children without absolute paths
20
+ * - fullName: pre-computed "users.profile" instead of runtime join
21
+ */
22
+ interface RouteTree {
23
+ /** Route segment name (e.g., "users" in "users.profile") */
24
+ readonly name: string;
25
+ /** Route path pattern (e.g., "/users/:id") */
26
+ readonly path: string;
27
+ /** Whether this route uses absolute path matching (path starts with "~") */
28
+ readonly absolute: boolean;
29
+ /** Child route nodes (Map for O(1) lookup by name) */
30
+ readonly children: ReadonlyMap<string, RouteTree>;
31
+ /** Parameter metadata extracted from path pattern (replaces parser dependency) */
32
+ readonly paramMeta: ParamMeta;
33
+ /** Parent node (null for root) */
34
+ readonly parent: RouteTree | null;
35
+ /** Children without absolute paths (for regular matching) */
36
+ readonly nonAbsoluteChildren: readonly RouteTree[];
37
+ /** Pre-computed full name (e.g., "users.profile") */
38
+ readonly fullName: string;
39
+ /**
40
+ * Pre-computed parameter type map for this segment.
41
+ * Cached to avoid recomputing on every navigation.
42
+ * Maps param name → "url" | "query".
43
+ */
44
+ readonly paramTypeMap: Readonly<Record<string, "url" | "query">>;
45
+ }
46
+ //#endregion
47
+ export { RouteDefinition, RouteTree };
48
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","names":[],"sources":["../../../../src/engine/builder/types.ts"],"mappings":";;;;;UAiBiB;GACd;EACD;EACA;EACA,WAAW;;;;;;;;;;;;UAiBI;;WAIN;;WAGA;;WAGA;;WAGA,UAAU,oBAAoB;;WAG9B,WAAW;;WAKX,QAAQ;;WAGR,8BAA8B;;WAG9B;;;;;;WAOA,cAAc,SAAS"}
@@ -0,0 +1,29 @@
1
+ import { Options } from "./search-params/types.js";
2
+ import { SegmentMatcher } from "./path-matcher/SegmentMatcher.js";
3
+ //#region src/engine/createMatcher.d.ts
4
+ /**
5
+ * Query string formatting options.
6
+ *
7
+ * Controls how arrays, booleans, nulls, and numbers are serialized in query strings.
8
+ */
9
+ type QueryParamsConfig = Readonly<Options>;
10
+ /**
11
+ * Options for creating a path matcher.
12
+ */
13
+ interface CreateMatcherOptions {
14
+ readonly caseSensitive?: boolean;
15
+ readonly strictTrailingSlash?: boolean;
16
+ readonly strictQueryParams?: boolean;
17
+ readonly urlParamsEncoding?: "default" | "uri" | "uriComponent" | "none";
18
+ readonly queryParams?: QueryParamsConfig;
19
+ }
20
+ /**
21
+ * Path matcher instance type.
22
+ *
23
+ * Opaque type — consumers use methods (match, buildPath, hasRoute, etc.)
24
+ * without knowing the underlying SegmentMatcher implementation.
25
+ */
26
+ type Matcher = SegmentMatcher;
27
+ //#endregion
28
+ export { CreateMatcherOptions, Matcher, QueryParamsConfig };
29
+ //# sourceMappingURL=createMatcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createMatcher.d.ts","names":[],"sources":["../../../src/engine/createMatcher.ts"],"mappings":";;;;;;;;KAwBY,oBAAoB,SAAS;;;;UAKxB;WACN;WACA;WACA;WACA;WACA,cAAc;;;;;;;;KASb,UAAU"}
@@ -0,0 +1,2 @@
1
+ const e=require("./path-matcher/SegmentMatcher.js"),t=require("./search-params/searchParams.js");function n(n){let r=n?.queryParams;return new e.SegmentMatcher({...n?.caseSensitive!==void 0&&{caseSensitive:n.caseSensitive},...n?.strictTrailingSlash!==void 0&&{strictTrailingSlash:n.strictTrailingSlash},...n?.strictQueryParams!==void 0&&{strictQueryParams:n.strictQueryParams},...n?.urlParamsEncoding!==void 0&&{urlParamsEncoding:n.urlParamsEncoding},parseQueryString:e=>t.parseQuery(e,r),buildQueryString:e=>t.build(e,r)})}exports.createMatcher=n;
2
+ //# sourceMappingURL=createMatcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createMatcher.js","names":["SegmentMatcher","parseQuery","build"],"sources":["../../../src/engine/createMatcher.ts"],"sourcesContent":["/**\n * Matcher Factory.\n *\n * Creates a pre-configured path matcher with search-params DI baked in.\n * This is the public API for creating matchers — SegmentMatcher class\n * and search-params functions are internal implementation details.\n *\n * @module route-tree/createMatcher\n */\n\nimport { SegmentMatcher } from \"./path-matcher\";\nimport { parseQuery, build } from \"./search-params\";\n\nimport type { Options } from \"./search-params\";\n\n// =============================================================================\n// Public Types (route-tree's own — no path-matcher or search-params types leak)\n// =============================================================================\n\n/**\n * Query string formatting options.\n *\n * Controls how arrays, booleans, nulls, and numbers are serialized in query strings.\n */\nexport type QueryParamsConfig = Readonly<Options>;\n\n/**\n * Options for creating a path matcher.\n */\nexport interface CreateMatcherOptions {\n readonly caseSensitive?: boolean;\n readonly strictTrailingSlash?: boolean;\n readonly strictQueryParams?: boolean;\n readonly urlParamsEncoding?: \"default\" | \"uri\" | \"uriComponent\" | \"none\";\n readonly queryParams?: QueryParamsConfig;\n}\n\n/**\n * Path matcher instance type.\n *\n * Opaque type — consumers use methods (match, buildPath, hasRoute, etc.)\n * without knowing the underlying SegmentMatcher implementation.\n */\nexport type Matcher = SegmentMatcher;\n\n// =============================================================================\n// Factory\n// =============================================================================\n\n/**\n * Creates a path matcher with search-params DI baked in.\n *\n * @param options - Matcher configuration\n * @returns Configured matcher instance\n *\n * @example\n * ```typescript\n * const matcher = createMatcher({\n * strictTrailingSlash: true,\n * queryParams: { booleanFormat: \"auto\" },\n * });\n * matcher.registerTree(tree);\n * const result = matcher.match(\"/users/123\");\n * ```\n */\nexport function createMatcher(options?: CreateMatcherOptions): Matcher {\n const qp = options?.queryParams;\n\n // Conditional spread: exactOptionalPropertyTypes forbids setting optional\n // properties to undefined — only include properties that are defined.\n return new SegmentMatcher({\n ...(options?.caseSensitive !== undefined && {\n caseSensitive: options.caseSensitive,\n }),\n ...(options?.strictTrailingSlash !== undefined && {\n strictTrailingSlash: options.strictTrailingSlash,\n }),\n ...(options?.strictQueryParams !== undefined && {\n strictQueryParams: options.strictQueryParams,\n }),\n ...(options?.urlParamsEncoding !== undefined && {\n urlParamsEncoding: options.urlParamsEncoding,\n }),\n // qs is ALREADY the query substring (SegmentMatcher split at the first \"?\");\n // parseQuery parses it verbatim — a path-accepting wrapper would re-split at a\n // \"?\" inside a query value and drop the param (#1292).\n parseQueryString: (qs: string) => parseQuery(qs, qp),\n buildQueryString: (params: Record<string, unknown>) => build(params, qp),\n });\n}\n"],"mappings":"iGAiEA,SAAgB,EAAc,EAAyC,CACrE,IAAM,EAAK,GAAS,YAIpB,OAAO,IAAIA,EAAAA,eAAe,CACxB,GAAI,GAAS,gBAAkB,IAAA,IAAa,CAC1C,cAAe,EAAQ,aACzB,EACA,GAAI,GAAS,sBAAwB,IAAA,IAAa,CAChD,oBAAqB,EAAQ,mBAC/B,EACA,GAAI,GAAS,oBAAsB,IAAA,IAAa,CAC9C,kBAAmB,EAAQ,iBAC7B,EACA,GAAI,GAAS,oBAAsB,IAAA,IAAa,CAC9C,kBAAmB,EAAQ,iBAC7B,EAIA,iBAAmB,GAAeC,EAAAA,WAAW,EAAI,CAAE,EACnD,iBAAmB,GAAoCC,EAAAA,MAAM,EAAQ,CAAE,CACzE,CAAC,CACH"}
@@ -0,0 +1,2 @@
1
+ function e(t){let n=t.absolute?`~${t.path}`:t.path,r={name:t.name,path:n};return t.children.size>0&&(r.children=Array.from(t.children.values(),e)),r}function t(t){return Array.from(t.children.values(),e)}exports.nodeToDefinition=e,exports.routeTreeToDefinitions=t;
2
+ //# sourceMappingURL=routeTreeToDefinitions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routeTreeToDefinitions.js","names":[],"sources":["../../../../src/engine/operations/routeTreeToDefinitions.ts"],"sourcesContent":["/**\n * RouteTree to RouteDefinition Converter.\n *\n * Converts a RouteTree back to an array of RouteDefinition objects.\n * Used for serialization and router cloning.\n *\n * @module operations/routeTreeToDefinitions\n */\n\nimport type { RouteDefinition, RouteTree } from \"../types\";\n\n/**\n * Converts a single RouteTree node to a RouteDefinition.\n *\n * If the node has absolute=true, the path is prefixed with '~'\n * to reconstruct the original definition format.\n *\n * @param node - RouteTree node to convert\n * @returns RouteDefinition with name, path, and optional children\n */\nexport function nodeToDefinition(node: RouteTree): RouteDefinition {\n // Reconstruct absolute path marker if needed\n const path = node.absolute ? `~${node.path}` : node.path;\n\n const def: RouteDefinition = {\n name: node.name,\n path,\n };\n\n if (node.children.size > 0) {\n def.children = Array.from(node.children.values(), nodeToDefinition);\n }\n\n return def;\n}\n\n/**\n * Converts a RouteTree back to an array of RouteDefinition objects.\n *\n * This is the inverse of createRouteTree - it extracts the minimal\n * definition data (name, path, children) from the computed tree.\n *\n * Note: Extra properties stored in RouteDefinition (like canActivate,\n * forwardTo, encodeParams, etc.) are NOT preserved in RouteTree,\n * so they won't be present in the output.\n *\n * @example\n * ```ts\n * const tree = createRouteTree(\"\", \"\", [\n * { name: \"users\", path: \"/users\", children: [\n * { name: \"profile\", path: \"/:id\" }\n * ]}\n * ]);\n *\n * const definitions = routeTreeToDefinitions(tree);\n * // [{ name: \"users\", path: \"/users\", children: [{ name: \"profile\", path: \"/:id\" }] }]\n * ```\n *\n * @param tree - RouteTree to convert\n * @returns Array of RouteDefinition objects (top-level routes only)\n */\nexport function routeTreeToDefinitions(tree: RouteTree): RouteDefinition[] {\n return Array.from(tree.children.values(), nodeToDefinition);\n}\n"],"mappings":"AAoBA,SAAgB,EAAiB,EAAkC,CAEjE,IAAM,EAAO,EAAK,SAAW,IAAI,EAAK,OAAS,EAAK,KAE9C,EAAuB,CAC3B,KAAM,EAAK,KACX,MACF,EAMA,OAJI,EAAK,SAAS,KAAO,IACvB,EAAI,SAAW,MAAM,KAAK,EAAK,SAAS,OAAO,EAAG,CAAgB,GAG7D,CACT,CA2BA,SAAgB,EAAuB,EAAoC,CACzE,OAAO,MAAM,KAAK,EAAK,SAAS,OAAO,EAAG,CAAgB,CAC5D"}
@@ -0,0 +1,16 @@
1
+ import { BuildPathOptions, MatchResult, MatcherInputNode, ResolvedMatcherOptions, SegmentMatcherOptions } from "./types.js";
2
+ //#region src/engine/path-matcher/SegmentMatcher.d.ts
3
+ declare class SegmentMatcher {
4
+ #private;
5
+ get options(): ResolvedMatcherOptions;
6
+ constructor(options: SegmentMatcherOptions);
7
+ registerTree(node: MatcherInputNode): void;
8
+ match(path: string): MatchResult | undefined;
9
+ buildPath(name: string, params?: Record<string, unknown>, options?: BuildPathOptions): string;
10
+ getSegmentsByName(name: string): readonly MatcherInputNode[] | undefined;
11
+ getMetaByName(name: string): Readonly<Record<string, Record<string, "url" | "query">>> | undefined;
12
+ hasRoute(name: string): boolean;
13
+ }
14
+ //#endregion
15
+ export { SegmentMatcher };
16
+ //# sourceMappingURL=SegmentMatcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SegmentMatcher.d.ts","names":[],"sources":["../../../../src/engine/path-matcher/SegmentMatcher.ts"],"mappings":";;cA6Da;;MACP,WAAW;EA2BH,YAAA,SAAS;EAiBrB,aAAa,MAAM;EAiBnB,MAAM,eAAe;EAqDrB,UACE,cACA,SAAS,yBACT,UAAU;EAmBZ,kBAAkB,wBAAwB;EAO1C,cACE,eACC,SAAS,eAAe;EAI3B,SAAS"}
@@ -0,0 +1,2 @@
1
+ const e=require("./encoding.js"),t=require("./pathUtils.js"),n=require("./percentEncoding.js"),r=require("./registration/index.js");function i(e){return typeof e==`string`?e:typeof e==`object`?JSON.stringify(e):String(e)}function a(e,t,n){t===`__proto__`?Object.defineProperty(e,t,{value:n,writable:!0,enumerable:!0,configurable:!0}):e[t]=n}var o=class{get options(){return this.#e}#e;#t=t.createSegmentNode();#n=new Map;#r=new Map;#i={cleanPath:``,normalized:``,queryString:void 0};#a=[];#o=``;#s;#c;constructor(t){this.#e={caseSensitive:t.caseSensitive??!0,strictTrailingSlash:t.strictTrailingSlash??!1,strictQueryParams:t.strictQueryParams??!1,urlParamsEncoding:t.urlParamsEncoding??`default`,parseQueryString:t.parseQueryString,buildQueryString:t.buildQueryString},this.#s=this.#e.caseSensitive,this.#c=this.#e.urlParamsEncoding===`none`?null:e.DECODING_METHODS[this.#e.urlParamsEncoding]}registerTree(e){this.#a=e.paramMeta.queryParams,r.registerNode({root:this.#t,options:this.#e,routesByName:this.#n,staticCache:this.#r,rootQueryParams:this.#a},e,``,[],null)}match(e){if(!this.#f(e))return;let{cleanPath:t,normalized:n,queryString:r}=this.#i,i=this.#s?n:n.toLowerCase(),a=this.#r.get(i);if(a)return this.#e.strictTrailingSlash&&!this.#g(t,a)?void 0:r===void 0&&a.cachedResult?a.cachedResult:this.#m(a,{},r);let o={},s=this.#_(n,o);if(s&&!(this.#e.strictTrailingSlash&&!this.#g(t,s))&&this.#b(o))return this.#m(s,o,r)}buildPath(e,t,n){let r=this.#n.get(e);if(!r)throw Error(`[SegmentMatcher.buildPath] '${e}' is not defined`);let i=this.#l(r,t),a=this.#u(i,n?.trailingSlash),o=this.#d(r,t,n?.queryParamsMode);return a+(o?`?${o}`:``)}getSegmentsByName(e){return this.#n.get(e)?.matchSegments}getMetaByName(e){return this.#n.get(e)?.meta}hasRoute(e){return this.#n.has(e)}#l(e,t){let n=e.buildStaticParts,r=e.buildParamSlots;if(r.length===0)return n[0];let a=n[0];for(let[e,o]of r.entries()){let r=t?.[o.paramName];if(r==null)throw Error(`[SegmentMatcher.buildPath] Missing required param '${o.paramName}'`);if(r===``)throw Error(`[SegmentMatcher.buildPath] Missing required param '${o.paramName}' (empty string)`);let s=o.encoder(i(r));a+=s+n[e+1]}return a}#u(e,t){return t===`always`&&!e.endsWith(`/`)?`${e}/`:t===`never`&&e!==`/`&&e.endsWith(`/`)?e.slice(0,-1):e}#d(e,t,n){if(!t||e.declaredQueryParams.length===0&&n!==`loose`)return``;let r={},i=!1;for(let n of e.declaredQueryParams)n in t&&(r[n]=t[n],i=!0);if(n===`loose`)for(let n in t)Object.hasOwn(t,n)&&!e.declaredQueryParamsSet.has(n)&&!e.buildParamNamesSet.has(n)&&(r[n]=t[n],i=!0);return i?this.#e.buildQueryString(r):``}#f(e){if(e===``&&(e=`/`),e.codePointAt(0)!==47)return!1;let n=this.#p(e);if(n===-2)return!1;n===-3&&(e=this.#o);let r=n>=0?e.slice(0,n):e,i=n>=0?e.slice(n+1):void 0;if(i!==void 0){let e=i.indexOf(`#`);e!==-1&&(i=i.slice(0,e))}let a=t.normalizeTrailingSlash(r);return this.#i.cleanPath=r,this.#i.normalized=a,this.#i.queryString=i,!0}#p(e){let t=!1;for(let n=0;n<e.length;n++){let r=e.codePointAt(n);if(r===35)return this.#o=e.slice(0,n),-3;if(r===63)return n;if(r>=128)return-2;if(r===47){if(t)return-2;t=!0}else t=!1}return-1}#m(e,t,n){if(!(n!==void 0&&!this.#h(e,t,n)))return{segments:e.matchSegments,params:t,meta:e.meta}}#h(e,t,n){let r;try{r=this.#e.parseQueryString(n)}catch{return!1}if(this.#e.strictQueryParams){let n=e.declaredQueryParamsSet;for(let e in r){if(!n.has(e))return!1;a(t,e,r[e])}}else for(let e in r)a(t,e,r[e]);return!0}#g(e,t){return(e.length>1&&e.endsWith(`/`))===t.hasTrailingSlash}#_(e,t){return e.length===1?this.#t.slashChildRoute??this.#t.route:this.#v(this.#t,e,1,t)}#v(e,t,n,r){let i=e,a=t.length,o=this.#s;for(;n<=a;){let e=t.indexOf(`/`,n),s=e===-1?a:e,c=t.slice(n,s),l=o?c:c.toLowerCase(),u;if(l in i.staticChildren)u=i.staticChildren[l];else if(i.paramChild){let e=i.paramChild;if(i.splatChild!==void 0){let a={[e.name]:c},o=this.#v(e.node,t,s+1,a);return o===void 0?this.#y(i.splatChild,t,n,r):(Object.assign(r,a),o)}u=e.node,r[e.name]=c}else if(i.splatChild)return this.#y(i.splatChild,t,n,r);else return;i=u,n=s+1}return i.slashChildRoute??i.route}#y(e,t,n,r){let i=e.node;if(!i.hasChildren)return r[e.name]=t.slice(n),i.route;let a={},o=this.#v(i,t,n,a);return o?(Object.assign(r,a),o):(r[e.name]=t.slice(n),i.route)}#b(e){let t=this.#c;if(!t)return!0;for(let r in e){let i=e[r];if(i.includes(`%`)){if(!n.validatePercentEncoding(i))return!1;try{e[r]=t(i)}catch{return!1}}}return!0}};exports.SegmentMatcher=o;
2
+ //# sourceMappingURL=SegmentMatcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SegmentMatcher.js","names":["#options","#root","createSegmentNode","#routesByName","#staticCache","#prepared","#caseSensitive","#decode","DECODING_METHODS","#rootQueryParams","#preparePath","#checkTrailingSlash","#buildResult","#traverse","#decodeParams","#buildUrlPath","#applyTrailingSlash","#buildQueryStringForBuild","#scanPath","#scanTruncated","normalizeTrailingSlash","#mergeQueryParams","#traverseFrom","#matchSplat","validatePercentEncoding"],"sources":["../../../../src/engine/path-matcher/SegmentMatcher.ts"],"sourcesContent":["import { DECODING_METHODS } from \"./encoding\";\nimport { createSegmentNode, normalizeTrailingSlash } from \"./pathUtils\";\nimport { validatePercentEncoding } from \"./percentEncoding\";\nimport { registerNode } from \"./registration\";\n\nimport type {\n BuildPathOptions,\n CompiledRoute,\n MatcherInputNode,\n MatchResult,\n ResolvedMatcherOptions,\n SegmentMatcherOptions,\n SegmentNode,\n} from \"./types\";\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\n/** Coerces a route param value (typed `unknown`, contractually primitive) to\n * the string the encoder receives. Objects are JSON-stringified. */\nfunction stringifyParamValue(value: unknown): string {\n // Stryker disable next-line BlockStatement: equivalent — String(value) === value for a string, so removing the early return is identical\n if (typeof value === \"string\") {\n return value;\n }\n\n if (typeof value === \"object\") {\n return JSON.stringify(value);\n }\n\n // eslint-disable-next-line @typescript-eslint/no-base-to-string -- route params are typed `unknown` but contractually primitive\n return String(value);\n}\n\n/** Folds a parsed query param into the accumulator as an own DATA property. A\n * plain `params[name] = value` invokes the inherited `__proto__` accessor for the\n * literal key `\"__proto__\"` — so the param silently vanishes (string value) or\n * swaps the local prototype (object value), annulling search-params' #855 own-key\n * hardening one layer up. `defineProperty` writes a genuine own entry. (#1293) */\nfunction assignQueryParam(\n params: Record<string, unknown>,\n name: string,\n value: unknown,\n): void {\n if (name === \"__proto__\") {\n Object.defineProperty(params, name, {\n value,\n writable: true,\n enumerable: true,\n configurable: true,\n });\n } else {\n params[name] = value;\n }\n}\n\n// =============================================================================\n// SegmentMatcher Class\n// =============================================================================\n\nexport class SegmentMatcher {\n get options(): ResolvedMatcherOptions {\n return this.#options;\n }\n\n readonly #options: ResolvedMatcherOptions;\n\n readonly #root: SegmentNode = createSegmentNode();\n readonly #routesByName = new Map<string, CompiledRoute>();\n readonly #staticCache = new Map<string, CompiledRoute>();\n\n // H1: Reusable object eliminates tuple allocation per match() call\n readonly #prepared = {\n // Stryker disable next-line StringLiteral: equivalent — #prepared.cleanPath is overwritten by #preparePath before any read (dead initializer)\n cleanPath: \"\",\n // Stryker disable next-line StringLiteral: equivalent — #prepared.normalized is overwritten by #preparePath before any read (dead initializer)\n normalized: \"\",\n queryString: undefined as string | undefined,\n };\n\n // Stryker disable next-line ArrayDeclaration: equivalent — #rootQueryParams is overwritten by registerTree before any read (dead initializer)\n #rootQueryParams: readonly string[] = [];\n // Stryker disable next-line StringLiteral: equivalent — #scanTruncated is set by #scanPath before it is read (dead initializer)\n #scanTruncated = \"\";\n\n readonly #caseSensitive: boolean;\n readonly #decode: ((param: string) => string) | null;\n\n constructor(options: SegmentMatcherOptions) {\n this.#options = {\n caseSensitive: options.caseSensitive ?? true,\n strictTrailingSlash: options.strictTrailingSlash ?? false,\n strictQueryParams: options.strictQueryParams ?? false,\n urlParamsEncoding: options.urlParamsEncoding ?? \"default\",\n parseQueryString: options.parseQueryString,\n buildQueryString: options.buildQueryString,\n };\n\n this.#caseSensitive = this.#options.caseSensitive;\n this.#decode =\n this.#options.urlParamsEncoding === \"none\"\n ? null\n : DECODING_METHODS[this.#options.urlParamsEncoding];\n }\n\n registerTree(node: MatcherInputNode): void {\n this.#rootQueryParams = node.paramMeta.queryParams;\n registerNode(\n {\n root: this.#root,\n options: this.#options,\n routesByName: this.#routesByName,\n staticCache: this.#staticCache,\n rootQueryParams: this.#rootQueryParams,\n },\n node,\n \"\",\n [],\n null,\n );\n }\n\n match(path: string): MatchResult | undefined {\n if (!this.#preparePath(path)) {\n return undefined;\n }\n\n const { cleanPath, normalized, queryString } = this.#prepared;\n\n const cacheKey = this.#caseSensitive\n ? normalized\n : // Stryker disable next-line MethodExpression: equivalent — the case-insensitive cache key only governs a hit; a miss falls through to #traverse (also case-insensitive), same result (proven by injection)\n normalized.toLowerCase();\n const cached = this.#staticCache.get(cacheKey);\n\n // Stryker disable next-line BlockStatement: equivalent — emptying the cache-hit block routes through #traverse, which resolves the same route (cache is a pure optimization)\n if (cached) {\n if (\n this.#options.strictTrailingSlash &&\n !this.#checkTrailingSlash(cleanPath, cached)\n ) {\n return undefined;\n }\n\n // Stryker disable next-line BlockStatement: equivalent — #buildResult recomputes cached.cachedResult identically (cache short-circuit)\n if (queryString === undefined && cached.cachedResult) {\n return cached.cachedResult;\n }\n\n return this.#buildResult(cached, {}, queryString);\n }\n\n const params: Record<string, string> = {};\n const route = this.#traverse(normalized, params);\n\n if (!route) {\n return undefined;\n }\n\n if (\n this.#options.strictTrailingSlash &&\n !this.#checkTrailingSlash(cleanPath, route)\n ) {\n return undefined;\n }\n\n // Decode the captured params. `match()` must never throw — a malformed\n // percent sequence (#737) makes this return false → the URL is unmatched.\n if (!this.#decodeParams(params)) {\n return undefined;\n }\n\n return this.#buildResult(route, params, queryString);\n }\n\n buildPath(\n name: string,\n params?: Record<string, unknown>,\n options?: BuildPathOptions,\n ): string {\n const route = this.#routesByName.get(name);\n\n if (!route) {\n throw new Error(`[SegmentMatcher.buildPath] '${name}' is not defined`);\n }\n\n const path = this.#buildUrlPath(route, params);\n const finalPath = this.#applyTrailingSlash(path, options?.trailingSlash);\n const queryString = this.#buildQueryStringForBuild(\n route,\n params,\n options?.queryParamsMode,\n );\n\n return finalPath + (queryString ? `?${queryString}` : \"\");\n }\n\n getSegmentsByName(name: string): readonly MatcherInputNode[] | undefined {\n // Derived from #routesByName — `compiled.matchSegments` is the same frozen\n // array a dedicated #segmentsByName index would store, so the index was\n // pure duplication (#1010).\n return this.#routesByName.get(name)?.matchSegments;\n }\n\n getMetaByName(\n name: string,\n ): Readonly<Record<string, Record<string, \"url\" | \"query\">>> | undefined {\n return this.#routesByName.get(name)?.meta;\n }\n\n hasRoute(name: string): boolean {\n return this.#routesByName.has(name);\n }\n\n #buildUrlPath(\n route: CompiledRoute,\n params: Record<string, unknown> | undefined,\n ): string {\n const parts = route.buildStaticParts;\n const slots = route.buildParamSlots;\n\n // Stryker disable next-line BlockStatement: equivalent — the general loop returns parts[0] when slots is empty, identical to this fast path\n if (slots.length === 0) {\n return parts[0];\n }\n\n let result = parts[0];\n\n for (const [i, slot] of slots.entries()) {\n const value = params?.[slot.paramName];\n\n // 3-token grammar (M1): every param slot is required — no optional-omit\n // branch. A missing param is an error.\n if (value === undefined || value === null) {\n throw new Error(\n `[SegmentMatcher.buildPath] Missing required param '${slot.paramName}'`,\n );\n }\n\n // #740 item 3: an empty value collapses the segment, silently producing a\n // path that matches the parent route (`buildPath(\"u.p\", {id:\"\"})` →\n // `/users/` → matches `u`). Reject it like a missing param.\n if (value === \"\") {\n throw new Error(\n `[SegmentMatcher.buildPath] Missing required param '${slot.paramName}' (empty string)`,\n );\n }\n\n const encoded = slot.encoder(stringifyParamValue(value));\n\n result += encoded + parts[i + 1];\n }\n\n return result;\n }\n\n #applyTrailingSlash(\n path: string,\n mode: BuildPathOptions[\"trailingSlash\"],\n ): string {\n if (mode === \"always\" && !path.endsWith(\"/\")) {\n return `${path}/`;\n }\n\n /* v8 ignore next 3 -- @preserve: trailing slash may not appear in buildStaticParts; integration-tested via core */\n if (mode === \"never\" && path !== \"/\" && path.endsWith(\"/\")) {\n return path.slice(0, -1);\n }\n\n return path;\n }\n\n #buildQueryStringForBuild(\n route: CompiledRoute,\n params: Record<string, unknown> | undefined,\n queryParamsMode: BuildPathOptions[\"queryParamsMode\"],\n ): string {\n if (!params) {\n return \"\";\n }\n\n // Stryker disable next-line BlockStatement: equivalent — the downstream !hasKeys guard also returns '' for a route with no declared query params\n if (route.declaredQueryParams.length === 0 && queryParamsMode !== \"loose\") {\n return \"\";\n }\n\n const queryObj: Record<string, unknown> = {};\n // Stryker disable next-line BooleanLiteral: equivalent — buildQueryString({}) === '' so the hasKeys initial value is unobservable when no keys are added\n let hasKeys = false;\n\n for (const name of route.declaredQueryParams) {\n // Stryker disable next-line BlockStatement: equivalent — buildQueryString strips undefined, so adding absent declared keys instead of continue changes nothing\n if (!(name in params)) {\n continue;\n }\n\n queryObj[name] = params[name];\n hasKeys = true;\n }\n\n if (queryParamsMode === \"loose\") {\n for (const paramKey in params) {\n if (!(\n Object.hasOwn(params, paramKey) &&\n !route.declaredQueryParamsSet.has(paramKey) &&\n !route.buildParamNamesSet.has(paramKey)\n )) {\n continue;\n }\n\n queryObj[paramKey] = params[paramKey];\n hasKeys = true;\n }\n }\n\n // Stryker disable next-line BlockStatement: equivalent — buildQueryString({}) === '' so removing the !hasKeys early return yields the same ''\n if (!hasKeys) {\n return \"\";\n }\n\n return this.#options.buildQueryString(queryObj);\n }\n\n // H2: Single-pass scanner — replaces 4 separate scans (indexOf(\"#\"), regex unicode, indexOf(\"?\"), includes(\"//\"))\n #preparePath(path: string): boolean {\n if (path === \"\") {\n path = \"/\";\n }\n\n if (path.codePointAt(0) !== 0x2f /* / */) {\n return false;\n }\n\n const qIdx = this.#scanPath(path);\n\n if (qIdx === -2) {\n return false;\n }\n\n if (qIdx === -3) {\n path = this.#scanTruncated;\n }\n\n const pathPart = qIdx >= 0 ? path.slice(0, qIdx) : path;\n let queryString = qIdx >= 0 ? path.slice(qIdx + 1) : undefined;\n\n // #842: a fragment (`#…`) AFTER the query separator is not seen by\n // #scanPath (it returns at the first `?`), so it would otherwise be folded\n // into the query string and parsed into a param value (e.g. `?a=1#frag` →\n // `a=\"1#frag\"`). A fragment is everything after the first `#` in the whole\n // URL and must be stripped before query parsing — a native indexOf on the\n // (short) query substring, only when a query exists, is ~free (a `#` BEFORE\n // the `?` is already handled by #scanPath via the -3 truncation branch).\n if (queryString !== undefined) {\n const hashIdx = queryString.indexOf(\"#\");\n\n if (hashIdx !== -1) {\n queryString = queryString.slice(0, hashIdx);\n }\n }\n\n const normalized = normalizeTrailingSlash(pathPart);\n\n this.#prepared.cleanPath = pathPart;\n this.#prepared.normalized = normalized;\n this.#prepared.queryString = queryString;\n\n return true;\n }\n\n // Returns: qIdx >= 0 (found ?), -1 (no ? or #), -2 (invalid), -3 (truncated at #, result in #scanTruncated)\n #scanPath(path: string): number {\n let prevSlash = false;\n\n for (let i = 0; i < path.length; i++) {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- bounds-checked by loop condition\n const ch = path.codePointAt(i)!;\n\n if (ch === 0x23 /* # */) {\n this.#scanTruncated = path.slice(0, i);\n\n return -3;\n }\n\n if (ch === 0x3f /* ? */) {\n return i;\n }\n\n if (ch >= 0x80) {\n return -2;\n }\n\n if (ch === 0x2f /* / */) {\n if (prevSlash) {\n return -2;\n }\n\n prevSlash = true;\n } else {\n prevSlash = false;\n }\n }\n\n return -1;\n }\n\n #buildResult(\n route: CompiledRoute,\n params: Record<string, unknown>,\n queryString: string | undefined,\n ): MatchResult | undefined {\n if (\n queryString !== undefined &&\n !this.#mergeQueryParams(route, params, queryString)\n ) {\n return undefined;\n }\n\n return {\n segments: route.matchSegments,\n params,\n meta: route.meta,\n };\n }\n\n // Parses the query string and folds it into `params`. Returns false (→ match\n // yields undefined) when the URL is unmatchable: the injected parser threw, or\n // strict mode saw an undeclared key.\n //\n // Precedence (#843, INVARIANTS Matching #25): query params are merged into the\n // SAME object that already holds the path params, so a query key equal to a\n // path-param name OVERWRITES the path value (`match(\"/u/5?id=9\")` → `{id:\"9\"}`).\n // Intentional and documented: `buildPath` never emits a path param as a query\n // key, so the build→match roundtrip is unaffected; the collision only arises\n // for hand-crafted/adversarial URLs where a query shadows a path segment.\n #mergeQueryParams(\n route: CompiledRoute,\n params: Record<string, unknown>,\n queryString: string,\n ): boolean {\n let queryParams: Record<string, unknown>;\n\n try {\n queryParams = this.#options.parseQueryString(queryString);\n } catch {\n // The injected query parser decodes percent-encoding too, so the same\n // valid-hex/invalid-UTF-8 sequence that breaks path params (e.g.\n // `?x=%E0%41`) makes it throw a URIError. `match()` must never throw —\n // treat the whole URL as unmatched so the router resolves to\n // UNKNOWN_ROUTE instead of crashing on start() (#737).\n return false;\n }\n\n if (this.#options.strictQueryParams) {\n const declared = route.declaredQueryParamsSet;\n\n for (const key in queryParams) {\n if (!declared.has(key)) {\n return false;\n }\n\n assignQueryParam(params, key, queryParams[key]);\n }\n } else {\n for (const key in queryParams) {\n assignQueryParam(params, key, queryParams[key]);\n }\n }\n\n return true;\n }\n\n #checkTrailingSlash(cleanPath: string, route: CompiledRoute): boolean {\n const inputHasSlash = cleanPath.length > 1 && cleanPath.endsWith(\"/\");\n\n return inputHasSlash === route.hasTrailingSlash;\n }\n\n #traverse(\n path: string,\n params: Record<string, string>,\n ): CompiledRoute | undefined {\n /* v8 ignore start -- @preserve: root \"/\" is always in #staticCache */\n if (path.length === 1) {\n return this.#root.slashChildRoute ?? this.#root.route;\n }\n /* v8 ignore stop */\n\n return this.#traverseFrom(this.#root, path, 1, params);\n }\n\n // The core match hot loop. The #1288 param+splat junction is inlined here\n // rather than extracted into a helper, because a per-param helper call\n // regresses the common single-param path ~5% (spike-measured).\n // eslint-disable-next-line sonarjs/cognitive-complexity -- inlined #1288 junction (see above): the static/param/splat dispatch is deliberately one function for the hot path\n #traverseFrom(\n startNode: SegmentNode,\n path: string,\n start: number,\n params: Record<string, string>,\n ): CompiledRoute | undefined {\n let node = startNode;\n const length = path.length;\n const caseSensitive = this.#caseSensitive;\n\n while (start <= length) {\n const end = path.indexOf(\"/\", start);\n const segmentEnd = end === -1 ? length : end;\n const segment = path.slice(start, segmentEnd);\n\n const lookupKey = caseSensitive ? segment : segment.toLowerCase();\n let next: SegmentNode;\n\n if (lookupKey in node.staticChildren) {\n next = node.staticChildren[lookupKey];\n } else if (node.paramChild) {\n const pc = node.paramChild;\n\n // #1288: validated sub-traverse on a param+splat junction. The param\n // branch is tried on a scratch object and commits ONLY if it structurally\n // completes (\"param wins if its branch can complete\", INVARIANTS Matching #8);\n // otherwise the splat sibling captures. Junction-free param hops (no splat\n // sibling) never enter this block — the common single-param path below is\n // untouched.\n if (node.splatChild !== undefined) {\n const childParams: Record<string, string> = { [pc.name]: segment };\n\n const taken = this.#traverseFrom(\n pc.node,\n path,\n segmentEnd + 1,\n childParams,\n );\n\n if (taken !== undefined) {\n Object.assign(params, childParams);\n\n return taken;\n }\n\n return this.#matchSplat(node.splatChild, path, start, params);\n }\n\n next = pc.node;\n params[pc.name] = segment;\n } else if (node.splatChild) {\n return this.#matchSplat(node.splatChild, path, start, params);\n } else {\n return undefined;\n }\n\n node = next;\n start = segmentEnd + 1;\n }\n\n return node.slashChildRoute ?? node.route;\n }\n\n #matchSplat(\n splatChild: { node: SegmentNode; name: string },\n path: string,\n start: number,\n params: Record<string, string>,\n ): CompiledRoute | undefined {\n const sn = splatChild.node;\n\n // Stryker disable next-line BlockStatement: equivalent — leaf-splat fast path; the #traverseFrom fallback returns the same route+params (proven via hasChildren injection)\n if (!sn.hasChildren) {\n params[splatChild.name] = path.slice(start);\n\n return sn.route;\n }\n\n const childParams: Record<string, string> = {};\n const specific = this.#traverseFrom(sn, path, start, childParams);\n\n // #1288: a structurally-complete specific child wins over the wildcard\n // capture; otherwise the splat captures the rest of the path.\n if (specific) {\n Object.assign(params, childParams);\n\n return specific;\n }\n\n params[splatChild.name] = path.slice(start);\n\n return sn.route;\n }\n\n #decodeParams(params: Record<string, string>): boolean {\n const decode = this.#decode;\n\n if (!decode) {\n return true;\n }\n\n for (const key in params) {\n const value = params[key];\n\n // Stryker disable next-line StringLiteral,BlockStatement: equivalent — includes('%') is a skip-optimization; decoding a %-free value is a no-op, so always-proceeding is identical\n if (!value.includes(\"%\")) {\n continue;\n }\n\n // Stryker disable next-line BlockStatement: equivalent — redundant with the try/catch below — decodeURIComponent throws on the same invalid-% input (proven by injection)\n if (!validatePercentEncoding(value)) {\n return false;\n }\n\n try {\n params[key] = decode(value);\n } catch {\n // `validatePercentEncoding` only checks `%XX` *syntax*. A sequence that\n // is syntactically valid but semantically invalid UTF-8 (e.g. `%E0%41`,\n // `%C0%80`, `%FF`) still makes `decodeURIComponent`/`decodeURI` throw a\n // URIError. `match()` must never throw — reject the path so the router\n // resolves to UNKNOWN_ROUTE instead of crashing on start() (#737).\n return false;\n }\n }\n\n return true;\n }\n}\n\nexport { createSegmentNode } from \"./pathUtils\";\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ const e=require("./parseSegment.js"),t=/[<>]/u;function n(e){for(let t=0;t<e.length;t+=1){if(e.codePointAt(t)!==63)continue;let n=t+1<e.length?e.codePointAt(t+1):-1;if(n!==-1&&n!==47&&n!==63&&n!==60)return t}return-1}const r=Object.freeze([]),i=Object.freeze({}),a=Object.freeze({urlParams:r,queryParams:r,spatParams:r,paramTypeMap:i,pathPattern:``});function o(t,n,r,i){for(let a of e.splitPathSegments(t)){if(a.length===0)continue;let t=e.parseSegment(a);`error`in t||t.kind===`static`||(n.push(t.name),i[t.name]=`url`,t.kind===`splat`&&r.push(t.name))}}function s(e){let t=[],r=[],i=[],a={},s=n(e);if(s!==-1){let t=e.slice(s+1).split(`&`);for(let e of t){let t=e.trim();t.length>0&&(r.push(t),a[t]=`query`)}e=e.slice(0,s)}return o(e,t,i,a),c(t,r,i,a,e)}function c(e,t,n,a,o){return{urlParams:e.length===0?r:e,queryParams:t.length===0?r:t,spatParams:n.length===0?r:n,paramTypeMap:e.length===0&&t.length===0?i:a,pathPattern:o}}exports.EMPTY_PARAM_META=a,exports.INVALID_QUERY_NAME_RGX=t,exports.buildParamMeta=s;
2
+ //# sourceMappingURL=buildParamMeta.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buildParamMeta.js","names":["splitPathSegments","parseSegment"],"sources":["../../../../src/engine/path-matcher/buildParamMeta.ts"],"sourcesContent":["/**\n * Route Parameter Metadata Extraction.\n *\n * Extracts parameter metadata from route path patterns without requiring\n * a full path-parser instance. Replaces parser.urlParams/queryParams/spatParams.\n *\n * @module buildParamMeta\n */\n\nimport { parseSegment, splitPathSegments } from \"./parseSegment\";\n\nimport type { ParamMeta } from \"./types\";\n\n/**\n * A query-param NAME may not contain `<`/`>` (#1242 §5.1) — a constraint\n * delimiter leaked into the query via a reverse-order modifier typo (`/a/:b?<c>`\n * parses the `?` as the query start, making `<c>` the query name). Consumed by\n * the route-tree gate and the `registerTree` backstop; relocated here from the\n * deleted `constraint-grammar.ts` when M1 removed constraints (query-param name\n * validation is a query concern, and this module owns query extraction).\n */\nexport const INVALID_QUERY_NAME_RGX = /[<>]/u;\n\nconst QUESTION = 0x3f; // ?\nconst SLASH = 0x2f; // /\nconst LT = 0x3c; // <\n\n/**\n * Locates the query separator `?` in a route path — the FIRST `?` whose tail is\n * non-empty and does not begin with `/`, `?`, or `<` (M1 §3.3). The 3-token\n * grammar leaves `?` a single role (there is no optional modifier and no\n * constraint body to hide one), so no length-preserving mask is needed. The three\n * excluded tails keep a REMOVED form in the path part, where `parseSegment`\n * rejects it with a recipe instead of mis-reading it as a query declaration:\n * - end-of-string (`/:id?`) and `/` (`/:id?/edit`) → a bare `:x?` optional;\n * - `?` (`/:id??tab`) → the leading `?` is the optional, the later `?` the query;\n * - `<` (`/a/:b?<x>`) → a reverse-order `:b?<x>` (optional then a former constraint).\n *\n * @param path - a route path\n * @returns the index of the query separator, or -1 if there is none\n */\nfunction findQuerySeparator(path: string): number {\n for (let i = 0; i < path.length; i += 1) {\n if (path.codePointAt(i) !== QUESTION) {\n continue;\n }\n\n // `next` is the code point after the `?`, or the `-1` sentinel at end-of-string.\n // The `-1` sentinel is the SOLE end-of-string guard — the former separate\n // `next !== undefined` conjunct was dead (the ternary bounds the index, so\n // `codePointAt` never returns `undefined`; the `!` is a type assertion, not a\n // runtime branch, so it keeps the scan at 100% coverage). Mirrors `#scanPath`.\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- ternary-bounded in-range index; codePointAt is defined\n const next = i + 1 < path.length ? path.codePointAt(i + 1)! : -1;\n\n if (next !== -1 && next !== SLASH && next !== QUESTION && next !== LT) {\n return i;\n }\n }\n\n return -1;\n}\n\n/**\n * Builds parameter metadata from a route path pattern.\n *\n * Extracts URL parameters, query parameters, and splat parameters\n * from the path pattern string.\n *\n * @param path - Route path pattern (e.g., \"/users/:id/posts/:postId?q\")\n * @returns Parameter metadata object\n *\n * @example\n * ```typescript\n * buildParamMeta(\"/users/:id\")\n * // → { urlParams: [\"id\"], queryParams: [], spatParams: [], paramTypeMap: { id: \"url\" } }\n *\n * buildParamMeta(\"/search?q&page\")\n * // → { urlParams: [], queryParams: [\"q\", \"page\"], spatParams: [],\n * // paramTypeMap: { q: \"query\", page: \"query\" } }\n *\n * buildParamMeta(\"/files/*path\")\n * // → { urlParams: [\"path\"], queryParams: [], spatParams: [\"path\"],\n * // paramTypeMap: { path: \"url\" } }\n * ```\n */\n// Shared frozen sentinels for the common no-params case — avoid a fresh empty\n// array/object per route (#1009). ParamMeta fields are Readonly*; match/build\n// only read them, and computeCaches' Object.freeze on the arrays/object is a\n// no-op on an already-frozen shared instance.\nconst EMPTY_PARAM_NAMES: readonly string[] = Object.freeze([]);\nconst EMPTY_PARAM_TYPE_MAP: Readonly<Record<string, \"url\" | \"query\">> =\n Object.freeze({});\n\n// Whole-meta shared sentinel for the fully-static case: every collection is a\n// #1009 sentinel AND pathPattern degenerates to the input path itself (no query\n// to strip), so the wrapper carries zero per-route information. The RETAINING\n// caller (route-tree's computeCaches) swaps a matching fresh result for this\n// instance — buildParamMeta itself keeps returning fresh objects so the\n// validation gate can read the real pathPattern of arbitrary input paths.\n// `pathPattern` is \"\" here; the one stored-meta reader (`registerNode`) falls\n// back to `node.path` on identity match.\nexport const EMPTY_PARAM_META: ParamMeta = Object.freeze({\n urlParams: EMPTY_PARAM_NAMES,\n queryParams: EMPTY_PARAM_NAMES,\n spatParams: EMPTY_PARAM_NAMES,\n paramTypeMap: EMPTY_PARAM_TYPE_MAP,\n pathPattern: \"\",\n});\n\n/**\n * Extracts URL/splat params from a path's segments into the given accumulators\n * via the canonical `parseSegment` tokenizer. Split out of `buildParamMeta` so\n * the builder stays under the cognitive-complexity budget. A malformed segment\n * (token errors) or a `static` segment contributes nothing — a malformed route is\n * rejected downstream before it compiles, so its meta is moot.\n */\nfunction collectUrlParams(\n path: string,\n urlParams: string[],\n spatParams: string[],\n paramTypeMap: Record<string, \"url\" | \"query\">,\n): void {\n for (const segment of splitPathSegments(path)) {\n if (segment.length === 0) {\n continue;\n }\n\n const token = parseSegment(segment);\n\n if (\"error\" in token || token.kind === \"static\") {\n continue;\n }\n\n urlParams.push(token.name);\n paramTypeMap[token.name] = \"url\";\n\n if (token.kind === \"splat\") {\n spatParams.push(token.name);\n }\n }\n}\n\nexport function buildParamMeta(path: string): ParamMeta {\n const urlParams: string[] = [];\n const queryParams: string[] = [];\n const spatParams: string[] = [];\n const paramTypeMap: Record<string, \"url\" | \"query\"> = {};\n\n // Locate the real query separator (M1 §3.3: first `?` whose tail is not a\n // former optional/reverse form).\n const separator = findQuerySeparator(path);\n\n if (separator !== -1) {\n const queryString = path.slice(separator + 1);\n const params = queryString.split(\"&\");\n\n for (const param of params) {\n const paramName = param.trim();\n\n if (paramName.length > 0) {\n queryParams.push(paramName);\n paramTypeMap[paramName] = \"query\";\n }\n }\n\n path = path.slice(0, separator);\n }\n\n collectUrlParams(path, urlParams, spatParams, paramTypeMap);\n\n return shareEmptyCollections(\n urlParams,\n queryParams,\n spatParams,\n paramTypeMap,\n path,\n );\n}\n\n// #1009: swap each freshly-built empty collection for a shared frozen sentinel\n// — factored out of buildParamMeta so the hot builder stays under the cognitive-\n// complexity budget. match/build only read these (Readonly*), and computeCaches'\n// Object.freeze is a no-op on an already-frozen shared instance.\nfunction shareEmptyCollections(\n urlParams: string[],\n queryParams: string[],\n spatParams: string[],\n paramTypeMap: Record<string, \"url\" | \"query\">,\n pathPattern: string,\n): ParamMeta {\n return {\n urlParams: urlParams.length === 0 ? EMPTY_PARAM_NAMES : urlParams,\n queryParams: queryParams.length === 0 ? EMPTY_PARAM_NAMES : queryParams,\n spatParams: spatParams.length === 0 ? EMPTY_PARAM_NAMES : spatParams,\n paramTypeMap:\n urlParams.length === 0 && queryParams.length === 0\n ? EMPTY_PARAM_TYPE_MAP\n : paramTypeMap,\n pathPattern,\n };\n}\n"],"mappings":"qCAqBa,EAAyB,QAoBtC,SAAS,EAAmB,EAAsB,CAChD,IAAK,IAAI,EAAI,EAAG,EAAI,EAAK,OAAQ,GAAK,EAAG,CACvC,GAAI,EAAK,YAAY,CAAC,IAAM,GAC1B,SASF,IAAM,EAAO,EAAI,EAAI,EAAK,OAAS,EAAK,YAAY,EAAI,CAAC,EAAK,GAE9D,GAAI,IAAS,IAAM,IAAS,IAAS,IAAS,IAAY,IAAS,GACjE,OAAO,CAEX,CAEA,MAAO,EACT,CA6BA,MAAM,EAAuC,OAAO,OAAO,CAAC,CAAC,EACvD,EACJ,OAAO,OAAO,CAAC,CAAC,EAUL,EAA8B,OAAO,OAAO,CACvD,UAAW,EACX,YAAa,EACb,WAAY,EACZ,aAAc,EACd,YAAa,EACf,CAAC,EASD,SAAS,EACP,EACA,EACA,EACA,EACM,CACN,IAAK,IAAM,KAAWA,EAAAA,kBAAkB,CAAI,EAAG,CAC7C,GAAI,EAAQ,SAAW,EACrB,SAGF,IAAM,EAAQC,EAAAA,aAAa,CAAO,EAE9B,UAAW,GAAS,EAAM,OAAS,WAIvC,EAAU,KAAK,EAAM,IAAI,EACzB,EAAa,EAAM,MAAQ,MAEvB,EAAM,OAAS,SACjB,EAAW,KAAK,EAAM,IAAI,EAE9B,CACF,CAEA,SAAgB,EAAe,EAAyB,CACtD,IAAM,EAAsB,CAAC,EACvB,EAAwB,CAAC,EACzB,EAAuB,CAAC,EACxB,EAAgD,CAAC,EAIjD,EAAY,EAAmB,CAAI,EAEzC,GAAI,IAAc,GAAI,CAEpB,IAAM,EADc,EAAK,MAAM,EAAY,CAClB,CAAC,CAAC,MAAM,GAAG,EAEpC,IAAK,IAAM,KAAS,EAAQ,CAC1B,IAAM,EAAY,EAAM,KAAK,EAEzB,EAAU,OAAS,IACrB,EAAY,KAAK,CAAS,EAC1B,EAAa,GAAa,QAE9B,CAEA,EAAO,EAAK,MAAM,EAAG,CAAS,CAChC,CAIA,OAFA,EAAiB,EAAM,EAAW,EAAY,CAAY,EAEnD,EACL,EACA,EACA,EACA,EACA,CACF,CACF,CAMA,SAAS,EACP,EACA,EACA,EACA,EACA,EACW,CACX,MAAO,CACL,UAAW,EAAU,SAAW,EAAI,EAAoB,EACxD,YAAa,EAAY,SAAW,EAAI,EAAoB,EAC5D,WAAY,EAAW,SAAW,EAAI,EAAoB,EAC1D,aACE,EAAU,SAAW,GAAK,EAAY,SAAW,EAC7C,EACA,EACN,aACF,CACF"}
@@ -0,0 +1,2 @@
1
+ const e=/[^\w!$'()*+,.:;|~-]/gu,t=/[^\w!$'()*+,.:;|~-]/u,n=/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g,r=e=>t=>{try{return e(t)}catch{return e(t.replaceAll(n,`�`))}},i=r(t=>t.replaceAll(e,e=>encodeURIComponent(e))),a=e=>t.test(e)?i(e):e,o={default:a,uri:r(encodeURI),uriComponent:r(encodeURIComponent),none:e=>e},s={default:decodeURIComponent,uri:decodeURI,uriComponent:decodeURIComponent,none:e=>e},c=(e,t)=>{let n=o[t],r=String(e).split(`/`),i=n(r[0]);for(let e=1;e<r.length;e++)i+=`/`+n(r[e]);return i};exports.DECODING_METHODS=s,exports.ENCODING_METHODS=o,exports.encodeParam=c,exports.encodeURIComponentExcludingSubDelims=a;
2
+ //# sourceMappingURL=encoding.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"encoding.js","names":[],"sources":["../../../../src/engine/path-matcher/encoding.ts"],"sourcesContent":["/**\n * URL Parameter Encoding.\n *\n * Encoding strategies for URL parameters.\n *\n * @module encoding\n */\n\n/**\n * Performance-critical code: string concatenation is 2x faster than template\n * literals/String.raw in V8 hot paths. See benchmarks for details.\n */\n/* eslint-disable prefer-template */\n\nimport type { URLParamsEncodingType } from \"./types\";\n\n// =============================================================================\n// Encoding Constants\n// =============================================================================\n\n/**\n * Optimized regex: only matches characters that ACTUALLY need encoding.\n * Excludes:\n * - A-Za-z0-9: alphanumeric (unreserved per RFC 3986)\n * - -._~: unreserved characters per RFC 3986\n * - !$'()*+,:;|: sub-delimiters we want to preserve\n *\n * Performance: This regex skips alphanumeric characters entirely,\n * avoiding unnecessary encodeURIComponent calls that return the same value.\n *\n * IMPORTANT: The 'u' (Unicode) flag makes the regex iterate by code point, so a\n * PAIRED surrogate (emoji, outside the BMP) coalesces into one code point that\n * encodeURIComponent accepts. An UNPAIRED (lone) surrogate is itself a single code\n * point that still matches the class and reaches encodeURIComponent, which throws\n * \"URI malformed\" on it — `encodeURIComponentExcludingSubDelims`'s slow path\n * catches that and sanitizes it to U+FFFD via a lone-surrogate regex, keeping\n * buildPath total (#1315).\n */\nconst NEEDS_ENCODING_REGEX = /[^\\w!$'()*+,.:;|~-]/gu;\n\n/**\n * Fast check regex (without global flag) to test if encoding is needed at all.\n * Used for pre-check optimization: if string contains only safe chars, skip encoding.\n *\n * IMPORTANT: The 'u' flag ensures proper handling of emoji/surrogate pairs.\n */\nconst NEEDS_ENCODING_TEST = /[^\\w!$'()*+,.:;|~-]/u;\n\n// =============================================================================\n// Encoding Helper Functions\n// =============================================================================\n\n/**\n * A lone (unpaired) surrogate — a high surrogate not followed by a low, or a low\n * not preceded by a high. A manual, lib-target-agnostic `String.prototype.toWellFormed`\n * (ES2024): consumers compile this `src` under their own `tsconfig` (whose `lib` may\n * predate es2024 — e.g. `hash-plugin`), so a regex `replace` avoids a `toWellFormed`\n * type error there while producing the identical result.\n */\nconst LONE_SURROGATE_RGX =\n /[\\uD800-\\uDBFF](?![\\uDC00-\\uDFFF])|(?<![\\uD800-\\uDBFF])[\\uDC00-\\uDFFF]/g;\n\n/**\n * Wraps an encoder so a lone (unpaired) surrogate — the only input\n * `encodeURIComponent` / `encodeURI` reject (`URIError`) — is sanitized to U+FFFD\n * and re-encoded instead of throwing, keeping `buildPath` total (#1315). The\n * surrogate is already non-round-trippable garbage. `path-matcher` has zero deps, so\n * this mirrors search-params' `safeEncode` rather than importing it (a deliberate\n * twin, like the `getTypeDescription` copy in route-tree).\n */\nconst totalize =\n (encoder: (s: string) => string) =>\n (segment: string): string => {\n try {\n return encoder(segment);\n } catch {\n return encoder(segment.replaceAll(LONE_SURROGATE_RGX, \"�\"));\n }\n };\n\n// Only the slow path can throw a `URIError` — a lone surrogate always matches\n// `NEEDS_ENCODING_REGEX`, so it never reaches the all-safe fast path — hence the\n// try/catch sits here and the 29-57x fast path below pays nothing for it.\nconst encodeSlowPath = totalize((segment: string): string =>\n segment.replaceAll(NEEDS_ENCODING_REGEX, (match) =>\n encodeURIComponent(match),\n ),\n);\n\n/**\n * Encode a segment while preserving sub-delimiters.\n * Uses pre-check optimization: fast path for strings without special chars.\n *\n * Performance improvement over naive approach:\n * - Alphanumeric strings: 29-57x faster\n * - Strings with special chars: 2-3x faster\n */\nexport const encodeURIComponentExcludingSubDelims = (\n segment: string,\n): string => {\n // Fast path: if no special chars, return as-is\n // Stryker disable next-line BlockStatement: equivalent — pure optimization; `replaceAll(NEEDS_ENCODING_REGEX, ...)` below is a no-op on a string with no encodable chars, so emptying this early return yields the identical value. ConditionalExpression stays live (killable `->true` sibling returns unencoded strings).\n if (!NEEDS_ENCODING_TEST.test(segment)) {\n return segment;\n }\n\n return encodeSlowPath(segment);\n};\n\n// =============================================================================\n// Encoding Methods Map\n// =============================================================================\n\n/**\n * Encoding functions for each encoding type.\n *\n * Modes:\n * - `default` - encodeURIComponent preserving sub-delimiters (+, :, ', !, ,, ;, *)\n * - `uri` - encodeURI/decodeURI\n * - `uriComponent` - encodeURIComponent/decodeURIComponent\n * - `none` - no encoding/decoding\n */\nexport const ENCODING_METHODS: Record<\n URLParamsEncodingType,\n (param: string) => string\n> = {\n default: encodeURIComponentExcludingSubDelims,\n uri: totalize(encodeURI),\n uriComponent: totalize(encodeURIComponent),\n none: (val) => val,\n};\n\n/**\n * Decoding functions for each encoding type.\n */\nexport const DECODING_METHODS: Record<\n URLParamsEncodingType,\n (param: string) => string\n> = {\n default: decodeURIComponent,\n uri: decodeURI,\n uriComponent: decodeURIComponent,\n // `none` decoding is never reached through `match`: `SegmentMatcher` special-cases\n // `urlParamsEncoding === \"none\"` to `#decode = null` and skips `#decodeParams`\n // entirely (so a \"none\" route also skips %-validation — a deliberate behaviour, not\n // merely perf). This identity entry exists only for `Record` type completeness and is\n // exercised by the exempt `tests/property/encoding.properties.ts` round-trip.\n /* v8 ignore next -- unreachable via match (none → null); see the comment above */\n none: (val) => val,\n};\n\n// =============================================================================\n// Main Encoding Function\n// =============================================================================\n\n/**\n * Encodes a SPLAT URL parameter value: each `/`-delimited segment is encoded with the\n * strategy's encoder, preserving the `/` separators.\n *\n * Splat-only by design (#860): a NON-splat param is encoded by `ENCODING_METHODS[encoding]`\n * directly — `registration/buildParts.ts`'s `makeBuildParamSlot` routes only SPLAT slots\n * through here — so the former `!isSpatParam` fast path was unreachable dead code (surfaced\n * by the public-API test migration) and was dropped.\n *\n * @param param - The splat parameter value to encode\n * @param encoding - The encoding strategy to use\n * @returns The encoded splat value (each segment encoded, `/` preserved)\n *\n * @example\n * ```typescript\n * encodeParam('docs/readme.md', 'default'); // => 'docs/readme.md'\n * encodeParam('a/hello world', 'default'); // => 'a/hello%20world'\n * ```\n */\nexport const encodeParam = (\n param: string | number | boolean,\n encoding: URLParamsEncodingType,\n): string => {\n const encoder = ENCODING_METHODS[encoding];\n const str = String(param);\n\n // Encode each \"/\"-segment separately, preserving the separators.\n // H6 optimization: string concatenation is 2x faster than template literals\n const segments = str.split(\"/\");\n let result = encoder(segments[0]);\n\n for (let i = 1; i < segments.length; i++) {\n result += \"/\" + encoder(segments[i]);\n }\n\n return result;\n};\n"],"mappings":"AAsCA,MAAM,EAAuB,wBAQvB,EAAsB,uBAatB,EACJ,0EAUI,EACH,GACA,GAA4B,CAC3B,GAAI,CACF,OAAO,EAAQ,CAAO,CACxB,MAAQ,CACN,OAAO,EAAQ,EAAQ,WAAW,EAAoB,GAAG,CAAC,CAC5D,CACF,EAKI,EAAiB,EAAU,GAC/B,EAAQ,WAAW,EAAuB,GACxC,mBAAmB,CAAK,CAC1B,CACF,EAUa,EACX,GAIK,EAAoB,KAAK,CAAO,EAI9B,EAAe,CAAO,EAHpB,EAmBE,EAGT,CACF,QAAS,EACT,IAAK,EAAS,SAAS,EACvB,aAAc,EAAS,kBAAkB,EACzC,KAAO,GAAQ,CACjB,EAKa,EAGT,CACF,QAAS,mBACT,IAAK,UACL,aAAc,mBAOd,KAAO,GAAQ,CACjB,EAyBa,GACX,EACA,IACW,CACX,IAAM,EAAU,EAAiB,GAK3B,EAJM,OAAO,CAIA,CAAC,CAAC,MAAM,GAAG,EAC1B,EAAS,EAAQ,EAAS,EAAE,EAEhC,IAAK,IAAI,EAAI,EAAG,EAAI,EAAS,OAAQ,IACnC,GAAU,IAAM,EAAQ,EAAS,EAAE,EAGrC,OAAO,CACT"}