@real-router/core 0.80.0 → 0.82.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (179) hide show
  1. package/README.md +68 -55
  2. package/dist/cjs/Router-CCmcEi-S.js +2 -0
  3. package/dist/cjs/Router-CCmcEi-S.js.map +1 -0
  4. package/dist/cjs/Router.d.ts +7 -6
  5. package/dist/cjs/Router.d.ts.map +1 -1
  6. package/dist/cjs/Router.js +1 -1
  7. package/dist/cjs/Router.js.map +1 -1
  8. package/dist/cjs/api/getPluginApi.d.ts.map +1 -1
  9. package/dist/cjs/api/getPluginApi.js +1 -1
  10. package/dist/cjs/api/getPluginApi.js.map +1 -1
  11. package/dist/cjs/api/getRoutesApi.d.ts.map +1 -1
  12. package/dist/cjs/api/getRoutesApi.js +1 -1
  13. package/dist/cjs/api/getRoutesApi.js.map +1 -1
  14. package/dist/cjs/api.js +2 -1
  15. package/dist/cjs/api.js.map +1 -1
  16. package/dist/cjs/buildParamMeta-0m11OIJS.js +2 -0
  17. package/dist/cjs/buildParamMeta-0m11OIJS.js.map +1 -0
  18. package/dist/cjs/channels/defaults.js +2 -0
  19. package/dist/cjs/channels/defaults.js.map +1 -0
  20. package/dist/cjs/channels/guard.js +2 -0
  21. package/dist/cjs/channels/guard.js.map +1 -0
  22. package/dist/cjs/channels/modeGate.js +2 -0
  23. package/dist/cjs/channels/modeGate.js.map +1 -0
  24. package/dist/cjs/constants.d.ts.map +1 -1
  25. package/dist/cjs/constants.js +1 -1
  26. package/dist/cjs/constants.js.map +1 -1
  27. package/dist/cjs/createRouter.d.ts +1 -1
  28. package/dist/cjs/createRouter.d.ts.map +1 -1
  29. package/dist/cjs/createRouter.js.map +1 -1
  30. package/dist/cjs/engine/builder/buildTree.js +1 -1
  31. package/dist/cjs/engine/builder/buildTree.js.map +1 -1
  32. package/dist/cjs/engine/path-matcher/SegmentMatcher.d.ts +14 -1
  33. package/dist/cjs/engine/path-matcher/SegmentMatcher.d.ts.map +1 -1
  34. package/dist/cjs/engine/path-matcher/SegmentMatcher.js +1 -1
  35. package/dist/cjs/engine/path-matcher/SegmentMatcher.js.map +1 -1
  36. package/dist/cjs/engine/path-matcher/registration/buildParts.js +1 -1
  37. package/dist/cjs/engine/path-matcher/registration/buildParts.js.map +1 -1
  38. package/dist/cjs/engine/path-matcher/registration/context.js +1 -1
  39. package/dist/cjs/engine/path-matcher/registration/context.js.map +1 -1
  40. package/dist/cjs/engine/path-matcher/registration/errors.js +1 -1
  41. package/dist/cjs/engine/path-matcher/registration/errors.js.map +1 -1
  42. package/dist/cjs/engine/path-matcher/registration/index.js +1 -1
  43. package/dist/cjs/engine/path-matcher/registration/index.js.map +1 -1
  44. package/dist/cjs/engine/path-matcher/registration/trieNodes.js.map +1 -1
  45. package/dist/cjs/engine/path-matcher/types.d.ts +3 -0
  46. package/dist/cjs/engine/path-matcher/types.d.ts.map +1 -1
  47. package/dist/cjs/engine/search-params/strategies/number.js +1 -1
  48. package/dist/cjs/engine/search-params/strategies/number.js.map +1 -1
  49. package/dist/cjs/engine/validation/routes.js +1 -1
  50. package/dist/cjs/engine/validation/routes.js.map +1 -1
  51. package/dist/cjs/helpers.js +1 -1
  52. package/dist/cjs/helpers.js.map +1 -1
  53. package/dist/cjs/index.d.ts +3 -3
  54. package/dist/cjs/index.js +2 -1
  55. package/dist/cjs/index.js.map +1 -1
  56. package/dist/cjs/internals.d.ts +30 -8
  57. package/dist/cjs/internals.d.ts.map +1 -1
  58. package/dist/cjs/internals.js +1 -1
  59. package/dist/cjs/internals.js.map +1 -1
  60. package/dist/cjs/limits.js +2 -0
  61. package/dist/cjs/limits.js.map +1 -0
  62. package/dist/cjs/namespaces/EventBusNamespace/EventBusNamespace.js.map +1 -1
  63. package/dist/cjs/namespaces/NavigationNamespace/NavigationNamespace.js +1 -1
  64. package/dist/cjs/namespaces/NavigationNamespace/NavigationNamespace.js.map +1 -1
  65. package/dist/cjs/namespaces/OptionsNamespace/OptionsNamespace.js.map +1 -1
  66. package/dist/cjs/namespaces/OptionsNamespace/constants.js +1 -1
  67. package/dist/cjs/namespaces/OptionsNamespace/constants.js.map +1 -1
  68. package/dist/cjs/namespaces/OptionsNamespace/helpers.js.map +1 -1
  69. package/dist/cjs/namespaces/RoutesNamespace/RoutesNamespace.js +1 -1
  70. package/dist/cjs/namespaces/RoutesNamespace/RoutesNamespace.js.map +1 -1
  71. package/dist/cjs/namespaces/RoutesNamespace/helpers.js +1 -1
  72. package/dist/cjs/namespaces/RoutesNamespace/helpers.js.map +1 -1
  73. package/dist/cjs/namespaces/RoutesNamespace/routesStore.d.ts +8 -0
  74. package/dist/cjs/namespaces/RoutesNamespace/routesStore.d.ts.map +1 -1
  75. package/dist/cjs/namespaces/RoutesNamespace/routesStore.js +1 -1
  76. package/dist/cjs/namespaces/RoutesNamespace/routesStore.js.map +1 -1
  77. package/dist/cjs/namespaces/RoutesNamespace/types.d.ts +24 -7
  78. package/dist/cjs/namespaces/RoutesNamespace/types.d.ts.map +1 -1
  79. package/dist/cjs/namespaces/StateNamespace/StateNamespace.js +1 -1
  80. package/dist/cjs/namespaces/StateNamespace/StateNamespace.js.map +1 -1
  81. package/dist/cjs/pipeline/buildURL.js +2 -0
  82. package/dist/cjs/pipeline/buildURL.js.map +1 -0
  83. package/dist/cjs/pipeline/canonicalize.js +2 -0
  84. package/dist/cjs/pipeline/canonicalize.js.map +1 -0
  85. package/dist/cjs/pipeline/materialize.js +2 -0
  86. package/dist/cjs/pipeline/materialize.js.map +1 -0
  87. package/dist/cjs/pipeline/port.d.ts +147 -0
  88. package/dist/cjs/pipeline/port.d.ts.map +1 -0
  89. package/dist/cjs/routerFSM.js +1 -1
  90. package/dist/cjs/routerFSM.js.map +1 -1
  91. package/dist/cjs/transitionPath.js +1 -1
  92. package/dist/cjs/transitionPath.js.map +1 -1
  93. package/dist/cjs/types/RouterValidator.d.ts +14 -0
  94. package/dist/cjs/types/RouterValidator.d.ts.map +1 -1
  95. package/dist/cjs/types/api.d.ts +20 -11
  96. package/dist/cjs/types/api.d.ts.map +1 -1
  97. package/dist/cjs/types/base.d.ts +63 -4
  98. package/dist/cjs/types/base.d.ts.map +1 -1
  99. package/dist/cjs/types/constants.d.ts +1 -0
  100. package/dist/cjs/types/constants.d.ts.map +1 -1
  101. package/dist/cjs/types/router.d.ts +112 -23
  102. package/dist/cjs/types/router.d.ts.map +1 -1
  103. package/dist/cjs/types/tree-changed.d.ts +1 -1
  104. package/dist/cjs/types/tree-changed.d.ts.map +1 -1
  105. package/dist/cjs/types.d.ts +20 -11
  106. package/dist/cjs/types.d.ts.map +1 -1
  107. package/dist/cjs/utils/event-emitter/EventEmitter.js.map +1 -0
  108. package/dist/cjs/utils/fsm/fsm.js.map +1 -0
  109. package/dist/cjs/utils/logger/RouterLogger.js.map +1 -0
  110. package/dist/cjs/utils/logger/constants.js.map +1 -0
  111. package/dist/cjs/validation.js +2 -1
  112. package/dist/cjs/validation.js.map +1 -1
  113. package/dist/cjs/wiring/wireNamespaces.js +1 -1
  114. package/dist/cjs/wiring/wireNamespaces.js.map +1 -1
  115. package/dist/esm/Router-DEpNuwBW.mjs +2 -0
  116. package/dist/esm/Router-DEpNuwBW.mjs.map +1 -0
  117. package/dist/esm/Router.d.mts +7 -6
  118. package/dist/esm/Router.d.mts.map +1 -1
  119. package/dist/esm/api/getPluginApi.d.mts.map +1 -1
  120. package/dist/esm/api/getRoutesApi.d.mts.map +1 -1
  121. package/dist/esm/api.mjs +1 -1
  122. package/dist/esm/api.mjs.map +1 -1
  123. package/dist/esm/buildParamMeta-D8td47fL.mjs +2 -0
  124. package/dist/esm/buildParamMeta-D8td47fL.mjs.map +1 -0
  125. package/dist/esm/constants.d.mts.map +1 -1
  126. package/dist/esm/createRouter.d.mts +1 -1
  127. package/dist/esm/createRouter.d.mts.map +1 -1
  128. package/dist/esm/engine/path-matcher/SegmentMatcher.d.mts +14 -1
  129. package/dist/esm/engine/path-matcher/SegmentMatcher.d.mts.map +1 -1
  130. package/dist/esm/engine/path-matcher/types.d.mts +3 -0
  131. package/dist/esm/engine/path-matcher/types.d.mts.map +1 -1
  132. package/dist/esm/index.d.mts +3 -3
  133. package/dist/esm/index.mjs +1 -1
  134. package/dist/esm/index.mjs.map +1 -1
  135. package/dist/esm/internals.d.mts +30 -8
  136. package/dist/esm/internals.d.mts.map +1 -1
  137. package/dist/esm/namespaces/RoutesNamespace/routesStore.d.mts +8 -0
  138. package/dist/esm/namespaces/RoutesNamespace/routesStore.d.mts.map +1 -1
  139. package/dist/esm/namespaces/RoutesNamespace/types.d.mts +24 -7
  140. package/dist/esm/namespaces/RoutesNamespace/types.d.mts.map +1 -1
  141. package/dist/esm/pipeline/port.d.mts +147 -0
  142. package/dist/esm/pipeline/port.d.mts.map +1 -0
  143. package/dist/esm/types/RouterValidator.d.mts +14 -0
  144. package/dist/esm/types/RouterValidator.d.mts.map +1 -1
  145. package/dist/esm/types/api.d.mts +20 -11
  146. package/dist/esm/types/api.d.mts.map +1 -1
  147. package/dist/esm/types/base.d.mts +63 -4
  148. package/dist/esm/types/base.d.mts.map +1 -1
  149. package/dist/esm/types/constants.d.mts +1 -0
  150. package/dist/esm/types/constants.d.mts.map +1 -1
  151. package/dist/esm/types/router.d.mts +112 -23
  152. package/dist/esm/types/router.d.mts.map +1 -1
  153. package/dist/esm/types/tree-changed.d.mts +1 -1
  154. package/dist/esm/types/tree-changed.d.mts.map +1 -1
  155. package/dist/esm/types.d.mts +20 -11
  156. package/dist/esm/types.d.mts.map +1 -1
  157. package/dist/esm/validation.mjs +1 -1
  158. package/dist/esm/validation.mjs.map +1 -1
  159. package/package.json +2 -2
  160. package/dist/cjs/Router-DGUed2F4.js +0 -2
  161. package/dist/cjs/Router-DGUed2F4.js.map +0 -1
  162. package/dist/cjs/buildParamMeta-DniluAIV.js +0 -2
  163. package/dist/cjs/buildParamMeta-DniluAIV.js.map +0 -1
  164. package/dist/cjs/foundation/event-emitter/EventEmitter.js.map +0 -1
  165. package/dist/cjs/foundation/fsm/fsm.js.map +0 -1
  166. package/dist/cjs/foundation/logger/RouterLogger.js.map +0 -1
  167. package/dist/cjs/foundation/logger/constants.js.map +0 -1
  168. package/dist/cjs/namespaces/StateNamespace/helpers.js +0 -2
  169. package/dist/cjs/namespaces/StateNamespace/helpers.js.map +0 -1
  170. package/dist/cjs/stateMetaStore.js +0 -2
  171. package/dist/cjs/stateMetaStore.js.map +0 -1
  172. package/dist/esm/Router-Bk6PfSE8.mjs +0 -2
  173. package/dist/esm/Router-Bk6PfSE8.mjs.map +0 -1
  174. package/dist/esm/buildParamMeta-bOLhLF9h.mjs +0 -2
  175. package/dist/esm/buildParamMeta-bOLhLF9h.mjs.map +0 -1
  176. /package/dist/cjs/{foundation → utils}/event-emitter/EventEmitter.js +0 -0
  177. /package/dist/cjs/{foundation → utils}/fsm/fsm.js +0 -0
  178. /package/dist/cjs/{foundation → utils}/logger/RouterLogger.js +0 -0
  179. /package/dist/cjs/{foundation → utils}/logger/constants.js +0 -0
@@ -1,5 +1,5 @@
1
1
  import { QueryParamsMode, QueryParamsOptions } from "./route-node-types.js";
2
- import { Params, RouterError, State, Unsubscribe } from "./base.js";
2
+ import { NavigationTarget, Params, ParamsSearch, RouterError, SearchParams, State, Unsubscribe } from "./base.js";
3
3
  import { LimitsConfig } from "./limits.js";
4
4
  import { NavigationOptions } from "../types.js";
5
5
  //#region src/types/router.d.ts
@@ -18,7 +18,7 @@ interface LoggerConfig {
18
18
  * Core stores a concrete instance on `RouterInternals.logger` (built from
19
19
  * `options.logger` in the `Router` constructor); plugins and other consumers
20
20
  * reach that same per-instance logger via `getPluginApi(router).logger`. The
21
- * concrete `RouterLogger` class lives in core's `foundation/logger` — this
21
+ * concrete `RouterLogger` class lives in core's `utils/logger` — this
22
22
  * interface is the shared contract, so nothing outside core needs to import the
23
23
  * class (or depend on the former standalone `@real-router/logger` package).
24
24
  */
@@ -42,26 +42,60 @@ type ForwardToCallback<Dependencies = object> = (getDependency: <K extends keyof
42
42
  * Receives a dependency getter function to access router dependencies.
43
43
  */
44
44
  type DefaultParamsCallback<Dependencies = object> = (getDependency: <K extends keyof Dependencies>(name: K) => Dependencies[K]) => Params;
45
+ /**
46
+ * Callback function for dynamically resolving the default query params.
47
+ * Receives a dependency getter function to access router dependencies.
48
+ */
49
+ type DefaultSearchCallback<Dependencies = object> = (getDependency: <K extends keyof Dependencies>(name: K) => Dependencies[K]) => SearchParams;
45
50
  /**
46
51
  * Router configuration options.
47
52
  *
48
53
  * Note: For input, use `Partial<Options>` as all fields have defaults.
49
54
  * After initialization, `getOptions()` returns resolved `Options` with all fields populated.
55
+ *
56
+ * Generic over the router's dependency map so the three resolver callbacks
57
+ * (`defaultRoute` / `defaultParams` / `defaultSearch`) receive a TYPED
58
+ * `getDependency`. Code that cannot know that map — anything plugin-facing,
59
+ * and every consumer that reads configuration rather than resolving it —
60
+ * takes {@link AnyOptions} instead of infecting itself with the parameter.
50
61
  */
51
- interface Options {
62
+ interface Options<Dependencies extends DefaultDependencies = DefaultDependencies> {
52
63
  /**
53
64
  * Default route to navigate to on start.
54
65
  * Empty string means no default route.
55
66
  *
56
67
  * @default ""
57
68
  */
58
- defaultRoute: string | DefaultRouteCallback;
69
+ defaultRoute: string | DefaultRouteCallback<Dependencies>;
59
70
  /**
60
- * Default parameters for the default route.
71
+ * Default **path** parameters for the default route.
72
+ *
73
+ * Query defaults belong in {@link defaultSearch} — the two are separate
74
+ * channels (RFC-4 M2 / #1548), exactly as on a route's own config. Until that
75
+ * twin existed, a query-declared name written here reached the URL only
76
+ * because the `forwardState` seam still re-channelled the bag on its way
77
+ * through; that repair is scheduled for removal, so this slot is for path
78
+ * params and arbitrary app-level data, nothing else.
61
79
  *
62
80
  * @default {}
63
81
  */
64
- defaultParams: Params | DefaultParamsCallback;
82
+ defaultParams: Params | DefaultParamsCallback<Dependencies>;
83
+ /**
84
+ * Default **query** parameters for the default route (RFC-4 M2 / #1548) —
85
+ * the query-channel twin of {@link defaultParams}, and the router-level
86
+ * counterpart of a route's own `defaultSearch`.
87
+ *
88
+ * Passed to `navigateToDefault()` in the query slot, so it is merged into
89
+ * `state.search` and, subject to `queryParamsMode`, printed into the URL
90
+ * query string. Like `defaultRoute` / `defaultParams`, it may be a callback,
91
+ * re-evaluated on every `navigateToDefault()` — the default route can itself
92
+ * be chosen dynamically, so its query defaults have to be able to follow.
93
+ * `Options` is generic over the dependency map, so the callback's
94
+ * `getDependency` is typed against the router's OWN dependencies.
95
+ *
96
+ * @default {}
97
+ */
98
+ defaultSearch: SearchParams | DefaultSearchCallback<Dependencies>;
65
99
  /**
66
100
  * How to handle trailing slashes in URLs.
67
101
  * - "strict": Route must match exactly
@@ -144,6 +178,21 @@ interface Options {
144
178
  */
145
179
  limits?: Partial<LimitsConfig>;
146
180
  }
181
+ /**
182
+ * `Options` as seen by code that cannot know the router's dependency map —
183
+ * `PluginApi.getOptions()`, the matcher, the URL builders.
184
+ *
185
+ * `Options<never>` rather than `Options<object>`, and the difference is the
186
+ * whole point: `keyof never` is `PropertyKey`, so the erased `getDependency`
187
+ * accepts ANY key and returns `never` — a wider parameter and a narrower
188
+ * return, which is exactly what contravariance needs for `Options<D>` to flow
189
+ * in for every `D`. `Options<object>` erases `keyof` to `never` instead and
190
+ * therefore accepts NOTHING but itself (verified: the assignment fails).
191
+ *
192
+ * Every field stays visible; only the callbacks become uncallable, which is
193
+ * honest — a plugin has no dependency map to resolve them against.
194
+ */
195
+ type AnyOptions = Options<never>;
147
196
  type GuardFn = (toState: State, fromState: State | undefined, signal?: AbortSignal) => boolean | Promise<boolean>;
148
197
  type DefaultDependencies = object;
149
198
  interface Plugin {
@@ -186,10 +235,13 @@ interface Subscription {
186
235
  * For full router access, use the Router interface directly or the useRouter() hook.
187
236
  */
188
237
  interface Navigator {
189
- navigate: (routeName: string, routeParams?: Params, options?: NavigationOptions) => Promise<State>;
238
+ navigate: {
239
+ (target: NavigationTarget, options?: NavigationOptions): Promise<State>;
240
+ (routeName: string, routeParams?: Params, routeSearch?: SearchParams, options?: NavigationOptions): Promise<State>;
241
+ };
190
242
  getState: () => State | undefined;
191
- isActiveRoute: (name: string, params?: Params, strictEquality?: boolean, ignoreQueryParams?: boolean) => boolean;
192
- canNavigateTo: (name: string, params?: Params) => boolean;
243
+ isActiveRoute: (name: string, params?: Params, search?: SearchParams, strictEquality?: boolean, ignoreQueryParams?: boolean) => boolean;
244
+ canNavigateTo: (name: string, params?: Params, search?: SearchParams) => boolean;
193
245
  subscribe: (listener: SubscribeFn) => Unsubscribe;
194
246
  subscribeLeave: (listener: LeaveFn) => Unsubscribe;
195
247
  isLeaveApproved: () => boolean;
@@ -203,8 +255,8 @@ interface Navigator {
203
255
  */
204
256
  interface Router<D extends DefaultDependencies = DefaultDependencies> {
205
257
  [key: string]: unknown;
206
- isActiveRoute: (name: string, params?: Params, strictEquality?: boolean, ignoreQueryParams?: boolean) => boolean;
207
- buildPath: (route: string, params?: Params) => string;
258
+ isActiveRoute: (name: string, params?: Params, search?: SearchParams, strictEquality?: boolean, ignoreQueryParams?: boolean) => boolean;
259
+ buildPath: (route: string, params?: Params, search?: SearchParams) => string;
208
260
  getState: <P extends Params = Params>() => State<P> | undefined;
209
261
  getPreviousState: () => State | undefined;
210
262
  areStatesEqual: (state1: State | undefined, state2: State | undefined, ignoreQueryParams?: boolean) => boolean;
@@ -213,12 +265,15 @@ interface Router<D extends DefaultDependencies = DefaultDependencies> {
213
265
  start: (startPath: string) => Promise<State>;
214
266
  stop: () => this;
215
267
  dispose: () => void;
216
- canNavigateTo: (name: string, params?: Params) => boolean;
268
+ canNavigateTo: (name: string, params?: Params, search?: SearchParams) => boolean;
217
269
  usePlugin: (...plugins: (PluginFactory<D> | false | null | undefined)[]) => Unsubscribe;
218
270
  subscribe: (listener: SubscribeFn) => Unsubscribe;
219
271
  subscribeLeave: (listener: LeaveFn) => Unsubscribe;
220
272
  isLeaveApproved: () => boolean;
221
- navigate: (routeName: string, routeParams?: Params, options?: NavigationOptions) => Promise<State>;
273
+ navigate: {
274
+ (target: NavigationTarget, options?: NavigationOptions): Promise<State>;
275
+ (routeName: string, routeParams?: Params, routeSearch?: SearchParams, options?: NavigationOptions): Promise<State>;
276
+ };
222
277
  navigateToDefault: (options?: NavigationOptions) => Promise<State>;
223
278
  navigateToNotFound: (path?: string) => State;
224
279
  }
@@ -257,17 +312,49 @@ interface Route<Dependencies extends DefaultDependencies = DefaultDependencies>
257
312
  forwardTo?: string | ForwardToCallback<Dependencies>;
258
313
  /** Nested child routes. */
259
314
  children?: Route<Dependencies>[];
260
- /** Encodes state params to URL params. */
261
- encodeParams?: (stateParams: Params) => Params;
262
- /** Decodes URL params to state params. */
263
- decodeParams?: (pathParams: Params) => Params;
264
315
  /**
265
- * Default parameters for this route.
316
+ * Encodes the state channels to URL channels before path building (RFC-4 M2 /
317
+ * #1548). Receives `{ params, search }` and returns `{ params, search }` —
318
+ * transform whichever channel you own and pass the other through. The path
319
+ * slots are built from the returned `params`, the query string from the
320
+ * returned `search`.
321
+ */
322
+ encodeParams?: (channels: ParamsSearch) => ParamsSearch;
323
+ /**
324
+ * Decodes the matched URL channels to state channels (RFC-4 M2 / #1548).
325
+ * Receives `{ params, search }` (path params + parsed query) and returns
326
+ * `{ params, search }`. Runs inside `matchPath`, **before** any search-schema
327
+ * plugin validation — the v1 transformation order (engine codec → plugin).
328
+ */
329
+ decodeParams?: (channels: ParamsSearch) => ParamsSearch;
330
+ /**
331
+ * Default **path** parameters for this route (and arbitrary app-level
332
+ * defaults). Merged into `state.params`; missing path params are filled from
333
+ * here. Query defaults belong in {@link defaultSearch} (RFC-4 M2 / #1548).
334
+ *
335
+ * ⚠ **The slot IS the channel** — `ba0f6b18b` retired the routing #1549
336
+ * introduced, and this doc described it for one release. A name the route
337
+ * declares with `?` written here is NOT re-routed to the query string: it is
338
+ * REFUSED at registration, so `createRouter` / `add` / `replace` / `update` /
339
+ * `setRootPath` throw, naming the key and telling you to move it to
340
+ * {@link defaultSearch}. Without that check the router would build a state out
341
+ * of config it had just accepted and its own always-on channel guard would
342
+ * reject it on `start()`.
266
343
  *
267
- * These values are merged into state.params when creating route states.
268
- * Missing URL params are filled from defaultParams.
344
+ * A key the route declares NOWHERE is legitimate here and stays in
345
+ * `state.params` as app-level data it never reaches the URL, which
346
+ * `@real-router/validation-plugin` reports once per route+key (#1579).
269
347
  */
270
348
  defaultParams?: Params;
349
+ /**
350
+ * Default **query** (search) parameters for this route (RFC-4 M2 / #1548) —
351
+ * the query-channel twin of {@link defaultParams}. Merged into `state.search`
352
+ * and, subject to `queryParamsMode`, printed into the URL query string. A key
353
+ * the route does not declare as a query param (`?name`) follows the same
354
+ * `queryParamsMode` rules as a runtime `search` value (loose prints,
355
+ * default/strict drops).
356
+ */
357
+ defaultSearch?: SearchParams;
271
358
  }
272
359
  /**
273
360
  * Configuration update options for `updateRoute()`.
@@ -299,15 +386,17 @@ interface RouteConfigUpdate<Dependencies extends DefaultDependencies = DefaultDe
299
386
  forwardTo?: string | ForwardToCallback<Dependencies> | null;
300
387
  /** Set to null to remove defaultParams */
301
388
  defaultParams?: Params | null;
389
+ /** Set to null to remove defaultSearch (RFC-4 M2 / #1548) */
390
+ defaultSearch?: SearchParams | null;
302
391
  /** Set to null to remove decoder */
303
- decodeParams?: ((params: Params) => Params) | null;
392
+ decodeParams?: ((channels: ParamsSearch) => ParamsSearch) | null;
304
393
  /** Set to null to remove encoder */
305
- encodeParams?: ((params: Params) => Params) | null;
394
+ encodeParams?: ((channels: ParamsSearch) => ParamsSearch) | null;
306
395
  /** Set to null to remove canActivate */
307
396
  canActivate?: GuardFnFactory<Dependencies> | null;
308
397
  /** Set to null to remove canDeactivate */
309
398
  canDeactivate?: GuardFnFactory<Dependencies> | null;
310
399
  }
311
400
  //#endregion
312
- export { DefaultDependencies, DefaultParamsCallback, DefaultRouteCallback, ForwardToCallback, GuardFn, GuardFnFactory, LeaveFn, LeaveState, Listener, LogCallback, LogLevel, LogLevelConfig, LoggerConfig, Navigator, Options, Plugin, PluginFactory, Route, RouteConfigUpdate, Router, RouterLogger, SubscribeFn, SubscribeState, Subscription };
401
+ export { AnyOptions, DefaultDependencies, DefaultParamsCallback, DefaultRouteCallback, DefaultSearchCallback, ForwardToCallback, GuardFn, GuardFnFactory, LeaveFn, LeaveState, Listener, LogCallback, LogLevel, LogLevelConfig, LoggerConfig, Navigator, Options, Plugin, PluginFactory, Route, RouteConfigUpdate, Router, RouterLogger, SubscribeFn, SubscribeState, Subscription };
313
402
  //# sourceMappingURL=router.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"router.d.ts","names":[],"sources":["../../../src/types/router.ts"],"mappings":"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"}
1
+ {"version":3,"file":"router.d.ts","names":[],"sources":["../../../src/types/router.ts"],"mappings":"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"}
@@ -8,7 +8,7 @@ import { DefaultDependencies, Route, RouteConfigUpdate } from "./router.js";
8
8
  * guards are invoked-on-demand (fresh-read per navigation), not cached derived
9
9
  * state, so they need no observation channel.
10
10
  */
11
- type TreeStructuralPatch<Dependencies extends DefaultDependencies = DefaultDependencies> = Pick<RouteConfigUpdate<Dependencies>, "forwardTo" | "defaultParams" | "encodeParams" | "decodeParams">;
11
+ type TreeStructuralPatch<Dependencies extends DefaultDependencies = DefaultDependencies> = Pick<RouteConfigUpdate<Dependencies>, "forwardTo" | "defaultParams" | "defaultSearch" | "encodeParams" | "decodeParams">;
12
12
  interface TreeChangedAdd<Dependencies extends DefaultDependencies = DefaultDependencies> {
13
13
  readonly op: "add";
14
14
  /** Top-level routes that were added (deep-cloned + frozen; caller untouched). */
@@ -1 +1 @@
1
- {"version":3,"file":"tree-changed.d.ts","names":[],"sources":["../../../src/types/tree-changed.ts"],"mappings":";;;;;;;;;;KAsBY,oBACV,qBAAqB,sBAAsB,uBACzC,KACF,kBAAkB;UAIH,eACf,qBAAqB,sBAAsB;WAElC;;WAEA,gBAAgB,MAAM;;WAEtB;;UAGM,kBACf,qBAAqB,sBAAsB;WAElC;WACA;;;;;WAKA,yBAAyB,MAAM;;UAGzB,kBACf,qBAAqB,sBAAsB;WAElC;WACA;;WAEA,OAAO,SAAS,oBAAoB;;UAG9B,mBACf,qBAAqB,sBAAsB;WAElC;;WAEA,kBAAkB,MAAM;;WAExB,gBAAgB,MAAM;;UAGhB,iBACf,qBAAqB,sBAAsB;WAElC;;WAEA,kBAAkB,MAAM;;;;;;;;KASvB,iBACV,qBAAqB,sBAAsB,uBAEzC,eAAe,gBACf,kBAAkB,gBAClB,kBAAkB,gBAClB,mBAAmB,gBACnB,iBAAiB"}
1
+ {"version":3,"file":"tree-changed.d.ts","names":[],"sources":["../../../src/types/tree-changed.ts"],"mappings":";;;;;;;;;;KAsBY,oBACV,qBAAqB,sBAAsB,uBACzC,KACF,kBAAkB;UAQH,eACf,qBAAqB,sBAAsB;WAElC;;WAEA,gBAAgB,MAAM;;WAEtB;;UAGM,kBACf,qBAAqB,sBAAsB;WAElC;WACA;;;;;WAKA,yBAAyB,MAAM;;UAGzB,kBACf,qBAAqB,sBAAsB;WAElC;WACA;;WAEA,OAAO,SAAS,oBAAoB;;UAG9B,mBACf,qBAAqB,sBAAsB;WAElC;;WAEA,kBAAkB,MAAM;;WAExB,gBAAgB,MAAM;;UAGhB,iBACf,qBAAqB,sBAAsB;WAElC;;WAEA,kBAAkB,MAAM;;;;;;;;KASvB,iBACV,qBAAqB,sBAAsB,uBAEzC,eAAe,gBACf,kBAAkB,gBAClB,kBAAkB,gBAClB,mBAAmB,gBACnB,iBAAiB"}
@@ -1,7 +1,7 @@
1
1
  import { QueryParamsMode, QueryParamsOptions, RouteParams, RouteTreeState } from "./types/route-node-types.js";
2
- import { Params, RouterError, SerializedRouterState, SimpleState, State, StateMetaInput, TransitionMeta, TransitionPhase, TransitionReason, Unsubscribe } from "./types/base.js";
2
+ import { NavigationTarget, Params, ParamsSearch, RouterError, SearchParamPrimitive, SearchParamValue, SearchParams, SerializedRouterState, SimpleState, State, TransitionMeta, TransitionPhase, TransitionReason, Unsubscribe } from "./types/base.js";
3
3
  import { LimitsConfig } from "./types/limits.js";
4
- import { DefaultDependencies, DefaultParamsCallback, DefaultRouteCallback, ForwardToCallback, GuardFn, GuardFnFactory, LeaveFn, LeaveState, Listener, LogCallback, LogLevel, LogLevelConfig, LoggerConfig, Navigator, Options, Plugin, PluginFactory, Route, RouteConfigUpdate, Router, RouterLogger, SubscribeFn, SubscribeState, Subscription } from "./types/router.js";
4
+ import { AnyOptions, DefaultDependencies, DefaultParamsCallback, DefaultRouteCallback, DefaultSearchCallback, ForwardToCallback, GuardFn, GuardFnFactory, LeaveFn, LeaveState, Listener, LogCallback, LogLevel, LogLevelConfig, LoggerConfig, Navigator, Options, Plugin, PluginFactory, Route, RouteConfigUpdate, Router, RouterLogger, SubscribeFn, SubscribeState, Subscription } from "./types/router.js";
5
5
  import { ErrorCodeKeys, ErrorCodeToValueMap, ErrorCodeValues, EventMethodMap, EventName, EventToNameMap, EventToPluginMap, EventsKeys, PluginMethod } from "./types/constants.js";
6
6
  import { TreeChangedAdd, TreeChangedClear, TreeChangedEvent, TreeChangedRemove, TreeChangedReplace, TreeChangedUpdate, TreeStructuralPatch } from "./types/tree-changed.js";
7
7
  import { ContextNamespaceClaim, DependenciesApi, InterceptableMethodMap, InterceptorFn, LifecycleApi, PluginApi, RoutesApi } from "./types/api.js";
@@ -55,7 +55,7 @@ interface NavigationOptions {
55
55
  *
56
56
  * @example
57
57
  * // Redirect after login - prevent back button to login page
58
- * router.navigate('dashboard', {}, { replace: true });
58
+ * router.navigate('dashboard', {}, undefined, { replace: true });
59
59
  *
60
60
  * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/History/replaceState}
61
61
  */
@@ -97,13 +97,22 @@ interface NavigationOptions {
97
97
  * @default false
98
98
  *
99
99
  * @example
100
- * // Refresh current page data
101
- * router.navigate(currentRoute.name, currentRoute.params, { reload: true });
100
+ * // Refresh current page data. Options sit at slot 4 since RFC-4 M2
101
+ * // (#1548) — slot 3 is the query channel, so the pre-M2 three-argument
102
+ * // spelling puts `{ reload: true }` in `search`: the reload never happens
103
+ * // and the page's own query is rebuilt from an object that does not have
104
+ * // it (measured: `/search?term=react` → `/search`).
105
+ * router.navigate(
106
+ * currentRoute.name,
107
+ * currentRoute.params,
108
+ * currentRoute.search,
109
+ * { reload: true },
110
+ * );
102
111
  *
103
112
  * @example
104
- * // Force re-fetch on same route with different query params
105
- * // Note: query params are in path, not checked for equality
106
- * router.navigate('search', { term: 'react' }, { reload: true });
113
+ * // Force re-fetch on the same route with a different query. The query is
114
+ * // its own channel now pass it at slot 3, not inside the path bag.
115
+ * router.navigate('search', {}, { term: 'react' }, { reload: true });
107
116
  *
108
117
  * @see {@link force} for alternative that forces transition
109
118
  * @see {@link Router.areStatesEqual} for state comparison logic
@@ -130,7 +139,7 @@ interface NavigationOptions {
130
139
  *
131
140
  * @example
132
141
  * // Force transition for tracking even if params didn't change
133
- * router.navigate('analytics', { event: 'pageview' }, { force: true });
142
+ * router.navigate('analytics', {}, { event: 'pageview' }, { force: true });
134
143
  *
135
144
  * @see {@link reload} for semantic equivalent (preferred for refresh scenarios)
136
145
  */
@@ -159,7 +168,7 @@ interface NavigationOptions {
159
168
  * @example
160
169
  * // Force logout even with unsaved changes
161
170
  * function forceLogout() {
162
- * router.navigate('login', {}, {
171
+ * router.navigate('login', {}, undefined, {
163
172
  * forceDeactivate: true,
164
173
  * replace: true
165
174
  * });
@@ -204,5 +213,5 @@ interface NavigationOptions {
204
213
  signal?: AbortSignal | undefined;
205
214
  }
206
215
  //#endregion
207
- export { type ContextNamespaceClaim, type DefaultDependencies, type DefaultParamsCallback, type DefaultRouteCallback, type DependenciesApi, type ErrorCodeKeys, type ErrorCodeToValueMap, type ErrorCodeValues, type EventMethodMap, type EventName, type EventToNameMap, type EventToPluginMap, type EventsKeys, type ForwardToCallback, type GuardFn, type GuardFnFactory, type InterceptableMethodMap, type InterceptorFn, type LeaveFn, type LeaveState, type LifecycleApi, type LimitsConfig, type Listener, type LogCallback, type LogLevel, type LogLevelConfig, type LoggerConfig, NavigationOptions, type Navigator, type Options, type Params, type Plugin, type PluginApi, type PluginFactory, type PluginMethod, type QueryParamsMode, type QueryParamsOptions, type Route, type RouteConfigUpdate, type RouteParams, type RouteTreeState, type Router, type RouterError, type RouterLogger, type RoutesApi, type SerializedRouterState, type SimpleState, type State, StateContext, type StateMetaInput, type SubscribeFn, type SubscribeState, type Subscription, type TransitionMeta, type TransitionPhase, type TransitionReason, type TreeChangedAdd, type TreeChangedClear, type TreeChangedEvent, type TreeChangedRemove, type TreeChangedReplace, type TreeChangedUpdate, type TreeStructuralPatch, type Unsubscribe };
216
+ export { type AnyOptions, type ContextNamespaceClaim, type DefaultDependencies, type DefaultParamsCallback, type DefaultRouteCallback, type DefaultSearchCallback, type DependenciesApi, type ErrorCodeKeys, type ErrorCodeToValueMap, type ErrorCodeValues, type EventMethodMap, type EventName, type EventToNameMap, type EventToPluginMap, type EventsKeys, type ForwardToCallback, type GuardFn, type GuardFnFactory, type InterceptableMethodMap, type InterceptorFn, type LeaveFn, type LeaveState, type LifecycleApi, type LimitsConfig, type Listener, type LogCallback, type LogLevel, type LogLevelConfig, type LoggerConfig, NavigationOptions, type NavigationTarget, type Navigator, type Options, type Params, type ParamsSearch, type Plugin, type PluginApi, type PluginFactory, type PluginMethod, type QueryParamsMode, type QueryParamsOptions, type Route, type RouteConfigUpdate, type RouteParams, type RouteTreeState, type Router, type RouterError, type RouterLogger, type RoutesApi, type SearchParamPrimitive, type SearchParamValue, type SearchParams, type SerializedRouterState, type SimpleState, type State, StateContext, type SubscribeFn, type SubscribeState, type Subscription, type TransitionMeta, type TransitionPhase, type TransitionReason, type TreeChangedAdd, type TreeChangedClear, type TreeChangedEvent, type TreeChangedRemove, type TreeChangedReplace, type TreeChangedUpdate, type TreeStructuralPatch, type Unsubscribe };
208
217
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","names":[],"sources":["../../src/types/index.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UA2DiB;;;;;;;;;;;;;;UAeA;;;;;;;;;;;;;;;;;EAiBf;;;;;;;;;;;;;;;EAgBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAmCA;;;;;;;;;;;;;;;;;;;;;;;;;;EA2BA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAkCA;;;;;;;;;;;;;;;;;;;;;;;EAwBA;;;;;;;;;;;EAYA,SAAS"}
1
+ {"version":3,"file":"types.d.ts","names":[],"sources":["../../src/types/index.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UA+DiB;;;;;;;;;;;;;;UAeA;;;;;;;;;;;;;;;;;EAiBf;;;;;;;;;;;;;;;EAgBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA4CA;;;;;;;;;;;;;;;;;;;;;;;;;;EA2BA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAkCA;;;;;;;;;;;;;;;;;;;;;;;EAwBA;;;;;;;;;;;EAYA,SAAS"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EventEmitter.js","names":["#callbacks","#dispatching","#onListenerError","#onListenerWarn","#limits","#warnedEvents","#invokeIsolated","#callListener"],"sources":["../../../../src/utils/event-emitter/EventEmitter.ts"],"sourcesContent":["import type {\n EventEmitterLimits,\n EventEmitterOptions,\n Unsubscribe,\n} from \"./types\";\n\nconst DEFAULT_LIMITS: EventEmitterLimits = {\n maxListeners: 0,\n warnListeners: 0,\n};\n\n// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\ntype AnyCallback = Function;\n\n/**\n * Generic typed event emitter with listener limits, duplicate detection,\n * re-entrancy coalescing, and per-listener error isolation.\n *\n * All limits are opt-in via constructor options.\n */\nexport class EventEmitter<TEventMap extends Record<string, unknown[]>> {\n readonly #callbacks = new Map<string, Set<AnyCallback>>();\n // Names currently being dispatched. A re-entrant `emit` of an event already\n // on this set is coalesced to a no-op (see `emit`), so an event can never\n // re-enter its own dispatch — recursion is structurally impossible (depth ≤ 1)\n // with no depth bound and no stack-overflow path (#1033).\n readonly #dispatching = new Set<string>();\n #warnedEvents: Set<string> | null = null;\n #limits: EventEmitterLimits = DEFAULT_LIMITS;\n readonly #onListenerError:\n ((eventName: string, error: unknown) => void) | null;\n readonly #onListenerWarn: ((eventName: string, count: number) => void) | null;\n\n constructor(options?: EventEmitterOptions) {\n if (options?.limits) {\n this.#limits = options.limits;\n }\n\n this.#onListenerError = options?.onListenerError ?? null;\n this.#onListenerWarn = options?.onListenerWarn ?? null;\n }\n\n /**\n * Validates that a callback is a function.\n */\n static validateCallback(\n cb: unknown,\n eventName: string,\n // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\n ): asserts cb is Function {\n if (typeof cb !== \"function\") {\n throw new TypeError(\n `Expected callback to be a function for event ${eventName}`,\n );\n }\n }\n\n /**\n * Replaces current limits with the provided limits.\n */\n setLimits(limits: EventEmitterLimits): void {\n this.#limits = limits;\n }\n\n /**\n * Adds an event listener and returns an unsubscribe function.\n * Throws on duplicate listeners or when maxListeners is reached.\n *\n * Registration is atomic (validate-before-mutate, #1358): every rejection\n * check runs against the CURRENT record (read once, never created early), the\n * advisory warn hook runs before any mutation, and the record is created +\n * the listener added only after all checks pass. So a throw — a rejected\n * limit, or a throwing `onListenerWarn` — leaves NO side-effect behind: no\n * orphaned empty record (#1167) and no burnt warn latch (#1168).\n */\n on<E extends keyof TEventMap & string>(\n eventName: E,\n cb: (...args: TEventMap[E]) => void,\n ): Unsubscribe {\n const existing = this.#callbacks.get(eventName);\n const size = existing?.size ?? 0;\n\n if (existing?.has(cb)) {\n throw new Error(`Duplicate listener for \"${eventName}\"`);\n }\n\n const { maxListeners, warnListeners } = this.#limits;\n\n // Enforce the hard limit before warning, so onListenerWarn never fires for\n // a registration that then throws (the warnListeners === maxListeners case).\n if (maxListeners !== 0 && size >= maxListeners) {\n throw new Error(\n `Listener limit (${maxListeners}) reached for \"${eventName}\"`,\n );\n }\n\n // Warn at most once per emitter+event, using the PRE-add size. The hook is\n // invoked first and the latch set only after it returns without throwing, so\n // a throwing hook fails the registration atomically and leaves the latch\n // unspent — the next (W+1)th registration warns as documented (#1168). The\n // latch keeps the advisory hint \"exactly once\" across off/on churn around\n // the threshold; reset by clearAll() or by removing the last listener.\n if (\n warnListeners !== 0 &&\n size === warnListeners &&\n this.#onListenerWarn !== null\n ) {\n this.#warnedEvents ??= new Set();\n\n if (!this.#warnedEvents.has(eventName)) {\n this.#onListenerWarn(eventName, warnListeners);\n this.#warnedEvents.add(eventName);\n }\n }\n\n // Mutate last — create the record only now, so a rejected registration\n // above never strands an empty record (#1167).\n let set = existing;\n\n if (set === undefined) {\n set = new Set();\n this.#callbacks.set(eventName, set);\n }\n\n set.add(cb);\n\n return () => {\n this.off(eventName, cb);\n };\n }\n\n /**\n * Removes an event listener.\n */\n off<E extends keyof TEventMap & string>(\n eventName: E,\n cb: (...args: TEventMap[E]) => void,\n ): void {\n const set = this.#callbacks.get(eventName);\n\n if (!set) {\n return;\n }\n\n set.delete(cb);\n\n if (set.size === 0) {\n // Release per-event records once the last listener is gone, so consumers\n // with dynamic event names don't accumulate empty Sets unbounded\n // (listenerCount stays 0 either way, masking the growth). See #750.\n this.#callbacks.delete(eventName);\n this.#warnedEvents?.delete(eventName);\n }\n }\n\n /**\n * Emits an event, calling all registered listeners with the provided args.\n *\n * Uses snapshot iteration — listeners added/removed during emit don't affect\n * the current invocation. Per-listener errors are caught and reported via the\n * `onListenerError` callback; other listeners still run.\n *\n * Re-entrant emit is coalesced: emitting an event that is already being\n * dispatched (a listener that synchronously re-emits the same event) is a\n * no-op, so dispatch never recurses into itself (#1033).\n *\n * Uses explicit params instead of rest params to avoid V8 array materialization.\n * Extra undefined args are harmless — JS functions ignore extra arguments.\n */\n emit(\n eventName: keyof TEventMap & string,\n arg1?: unknown,\n arg2?: unknown,\n arg3?: unknown,\n arg4?: unknown,\n ): void {\n const set = this.#callbacks.get(eventName);\n\n if (!set || set.size === 0) {\n return;\n }\n\n // Coalesce a re-entrant emit of an in-flight event (depth ≤ 1, #1033).\n if (this.#dispatching.has(eventName)) {\n return;\n }\n\n // arguments.length is O(1) in V8 strict mode — no deopt\n const argc = arguments.length - 1;\n\n this.#dispatching.add(eventName);\n\n try {\n // Single-listener fast path — skip the [...set] snapshot allocation.\n if (set.size === 1) {\n const [cb] = set;\n\n this.#invokeIsolated(eventName, cb, argc, arg1, arg2, arg3, arg4);\n } else {\n const listeners = [...set];\n\n for (const cb of listeners) {\n this.#invokeIsolated(eventName, cb, argc, arg1, arg2, arg3, arg4);\n }\n }\n } finally {\n this.#dispatching.delete(eventName);\n }\n }\n\n /**\n * Removes all listeners and resets the warn latch.\n *\n * Does NOT touch `#dispatching`: the in-flight coalesce guard is owned by the\n * active `emit` frame (added when dispatch starts, self-released in that\n * frame's `finally`). Clearing it here would lift the guard for a live frame\n * when `clearAll()` runs from inside a listener, so a re-entrant same-event\n * emit would no longer coalesce and would re-enter — violating the depth-≤-1\n * contract (#1164). The guard self-releases; `clearAll()` has no business\n * sweeping state owned by active emit frames.\n */\n clearAll(): void {\n this.#callbacks.clear();\n this.#warnedEvents = null;\n }\n\n /**\n * Returns the number of listeners for the given event.\n */\n listenerCount(eventName: keyof TEventMap & string): number {\n return this.#callbacks.get(eventName)?.size ?? 0;\n }\n\n /**\n * Returns whether the given event is currently being dispatched (an `emit`\n * for it is on the stack). Single source of truth for \"is this event\n * in-flight\" — consumers read it to reject re-entrant operations that would\n * trigger such an emit (the emit itself would be coalesced regardless).\n */\n isDispatching(eventName: keyof TEventMap & string): boolean {\n return this.#dispatching.has(eventName);\n }\n\n // ===========================================================================\n // Private methods\n // ===========================================================================\n\n /**\n * Calls a listener with the correct number of arguments.\n * Dispatches by argc to preserve exact call semantics.\n */\n #invokeIsolated(\n eventName: keyof TEventMap & string,\n cb: AnyCallback,\n argc: number,\n arg1: unknown,\n arg2: unknown,\n arg3: unknown,\n arg4: unknown,\n ): void {\n try {\n const result = this.#callListener(cb, argc, arg1, arg2, arg3, arg4);\n\n // A listener typed `=> void` may still return a Promise at runtime (an\n // async hook or any-cast misuse). The sync `catch` below cannot see its\n // rejection, so route it to the same `#onListenerError` sink — otherwise\n // it escapes as a Node `unhandledRejection` (fatal under\n // `--unhandled-rejections=strict`, the Node 22+ default). Centralised here\n // so every listener kind (plugin hooks, `subscribe`, …) is isolated\n // symmetrically (#1412; `subscribe`'s per-site #944 wrapper folds in).\n if (\n result !== null &&\n result !== undefined &&\n typeof (result as PromiseLike<unknown>).then === \"function\"\n ) {\n Promise.resolve(result as PromiseLike<unknown>).catch(\n (error: unknown) => {\n this.#onListenerError?.(eventName, error);\n },\n );\n }\n } catch (error) {\n this.#onListenerError?.(eventName, error);\n }\n }\n\n #callListener(\n cb: AnyCallback,\n argc: number,\n arg1: unknown,\n arg2: unknown,\n arg3: unknown,\n arg4: unknown,\n ): unknown {\n switch (argc) {\n case 0: {\n return (cb as () => unknown)();\n }\n case 1: {\n return (cb as (a: unknown) => unknown)(arg1);\n }\n case 2: {\n return (cb as (a: unknown, b: unknown) => unknown)(arg1, arg2);\n }\n case 3: {\n return (cb as (a: unknown, b: unknown, c: unknown) => unknown)(\n arg1,\n arg2,\n arg3,\n );\n }\n default: {\n return (\n cb as (a: unknown, b: unknown, c: unknown, d: unknown) => unknown\n )(arg1, arg2, arg3, arg4);\n }\n }\n }\n\n // (record creation is inlined into `on()` so a rejected registration never\n // creates one — see the atomicity note there, #1167/#1358.)\n}\n"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fsm.js","names":["#context","#transitions","#listeners","#state","#currentTransitions","#actions","#listenerCount"],"sources":["../../../../src/utils/fsm/fsm.ts"],"sourcesContent":["import type { FSMConfig, TransitionInfo, TransitionListener } from \"./types\";\n\n/**\n * Shared guard for the engine-wide invariant \"the state is declared in\n * `config.transitions`\". Applied at every state-entry-point (constructor\n * `initial` and `on`'s `from`) so an undeclared state fails loud with\n * an explicit error instead of bricking the FSM or dead-registering an action\n * (#885). Returns the state's transition map for the caller to reuse.\n */\nfunction requireDeclared<TStates extends string, TEvents extends string>(\n transitions: Record<TStates, Partial<Record<TEvents, TStates>>>,\n state: TStates,\n where: string,\n): Partial<Record<TEvents, TStates>> {\n const stateTransitions = transitions[state];\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- runtime guard for JS / cast / string-typed callers passing a state outside TStates\n if (stateTransitions === undefined) {\n throw new Error(\n `[FSM.${where}] state \"${state}\" is not declared in config.transitions`,\n );\n }\n\n return stateTransitions;\n}\n\n/**\n * Synchronous finite state machine engine.\n *\n * Reentrancy: `send()` inside `onTransition` listener is allowed but unbounded —\n * callers are responsible for preventing infinite loops.\n *\n * Exceptions: if a listener throws, the exception propagates to the caller.\n * State is already updated before listeners fire, so `getState()` reflects the\n * new state even if the exception escapes `send()`.\n */\nexport class FSM<\n TStates extends string,\n TEvents extends string,\n TContext,\n TPayloadMap extends Partial<Record<TEvents, unknown>> = Record<never, never>,\n> {\n #state: TStates;\n #currentTransitions: Partial<Record<TEvents, TStates>>;\n #listenerCount = 0;\n #actions: Map<TStates, Map<TEvents, (payload: unknown) => void>> | null =\n null;\n readonly #context: TContext;\n readonly #transitions: Record<TStates, Partial<Record<TEvents, TStates>>>;\n readonly #listeners: (TransitionListener<\n TStates,\n TEvents,\n TPayloadMap\n > | null)[] = [];\n\n constructor(config: FSMConfig<TStates, TEvents, TContext>) {\n this.#state = config.initial;\n this.#context = config.context;\n this.#transitions = config.transitions;\n this.#currentTransitions = requireDeclared(\n config.transitions,\n config.initial,\n \"constructor\",\n );\n\n // #1159: validate table closure — every declared transition target must\n // itself be a declared state. `send()` applies table values\n // (`this.#transitions[nextState]`) without re-checking, so a dangling\n // target would silently enter an undeclared state (violating Validity #1)\n // and brick `canSend()` (violating No-bricking #10). One cold-path\n // O(states×events) pass at construction fails loud instead — the fourth\n // state-entry-point, mirroring the `initial` / `on` guards. Explicit\n // `undefined` values are the declared \"no transition\" no-op (send() returns\n // the current state) and are skipped. Post-construction mutation of the\n // shared table stays a documented GIGO boundary (Edge #5).\n for (const state of Object.keys(config.transitions)) {\n const stateTransitions = config.transitions[state as TStates];\n\n for (const event of Object.keys(stateTransitions)) {\n const target = stateTransitions[event as TEvents];\n\n if (target !== undefined) {\n requireDeclared(config.transitions, target, \"constructor\");\n }\n }\n }\n }\n\n send<E extends TEvents>(\n event: E,\n ...args: E extends keyof TPayloadMap ? [TPayloadMap[E]] : [undefined?]\n ): TStates {\n const nextState = this.#currentTransitions[event];\n\n if (nextState === undefined) {\n return this.#state;\n }\n\n const from = this.#state;\n\n this.#state = nextState;\n this.#currentTransitions = this.#transitions[nextState];\n\n const payload = args[0] as TPayloadMap[TEvents] | undefined;\n\n if (this.#actions !== null) {\n const action = this.#actions.get(from)?.get(event);\n\n if (action !== undefined) {\n action(payload);\n }\n }\n\n // Stryker disable next-line ConditionalExpression: equivalent — count>0 is a perf gate to skip the dispatch loop; `true` always enters it, but with no live listener `#listeners` holds only null slots and the loop body guards `listener !== null`, so dispatch is a no-op either way. The EqualityOperator `<=0` sibling on this line stays killed (not silenced here).\n if (this.#listenerCount > 0) {\n // `info` is structurally a valid TransitionInfo, but the distributive\n // union can't be matched to one variant while `event`/`payload` are\n // generic here — erase through `unknown` (TS2352), same spirit as the\n // `args[0]` cast above.\n const info = {\n from,\n to: nextState,\n event,\n payload,\n } as unknown as TransitionInfo<TStates, TEvents, TPayloadMap>;\n\n for (const listener of this.#listeners) {\n if (listener !== null) {\n listener(info);\n }\n }\n }\n\n return this.#state;\n }\n\n canSend(event: TEvents): boolean {\n return this.#currentTransitions[event] !== undefined;\n }\n\n getState(): TStates {\n return this.#state;\n }\n\n getContext(): TContext {\n return this.#context;\n }\n\n on<E extends TEvents>(\n from: TStates,\n event: E,\n action: E extends keyof TPayloadMap\n ? (payload: TPayloadMap[E]) => void\n : () => void,\n ): () => void {\n requireDeclared(this.#transitions, from, \"on\");\n\n this.#actions ??= new Map();\n\n let stateActions = this.#actions.get(from);\n\n if (!stateActions) {\n stateActions = new Map();\n this.#actions.set(from, stateActions);\n }\n\n const capturedAction = action as (payload: unknown) => void;\n\n stateActions.set(event, capturedAction);\n\n return () => {\n // Stryker disable next-line OptionalChaining: equivalent — `#actions` is assigned (`??= new Map()` above) before this unsubscribe closure is created and returned, so it is never null when the closure runs; `?.` can't short-circuit and behaves identically to `.get`.\n const stateMap = this.#actions?.get(from);\n\n if (stateMap?.get(event) === capturedAction) {\n stateMap.delete(event);\n }\n };\n }\n\n onTransition(\n listener: (info: TransitionInfo<TStates, TEvents, TPayloadMap>) => void,\n ): () => void {\n const nullIndex = this.#listeners.indexOf(null);\n let index: number;\n\n if (nullIndex === -1) {\n index = this.#listeners.length;\n this.#listeners.push(listener);\n } else {\n this.#listeners[nullIndex] = listener;\n index = nullIndex;\n }\n\n this.#listenerCount++;\n let subscribed = true;\n\n return () => {\n if (!subscribed) {\n return;\n }\n\n subscribed = false;\n this.#listeners[index] = null;\n // Stryker disable next-line UpdateOperator: equivalent — #listenerCount feeds only the `> 0` loop gate; `++` inflates it but the loop then iterates already-nulled slots (no-op), and no public reader exposes the count, so the miscount is unobservable.\n this.#listenerCount--;\n };\n }\n}\n"],"mappings":"AASA,SAAS,EACP,EACA,EACA,EACmC,CACnC,IAAM,EAAmB,EAAY,GAGrC,GAAI,IAAqB,IAAA,GACvB,MAAU,MACR,QAAQ,EAAM,WAAW,EAAM,wCACjC,EAGF,OAAO,CACT,CAYA,IAAa,EAAb,KAKE,CACA,GACA,GACA,GAAiB,EACjB,GACE,KACF,GACA,GACA,GAIc,CAAC,EAEf,YAAY,EAA+C,CACzD,KAAKG,GAAS,EAAO,QACrB,KAAKH,GAAW,EAAO,QACvB,KAAKC,GAAe,EAAO,YAC3B,KAAKG,GAAsB,EACzB,EAAO,YACP,EAAO,QACP,aACF,EAYA,IAAK,IAAM,KAAS,OAAO,KAAK,EAAO,WAAW,EAAG,CACnD,IAAM,EAAmB,EAAO,YAAY,GAE5C,IAAK,IAAM,KAAS,OAAO,KAAK,CAAgB,EAAG,CACjD,IAAM,EAAS,EAAiB,GAE5B,IAAW,IAAA,IACb,EAAgB,EAAO,YAAa,EAAQ,aAAa,CAE7D,CACF,CACF,CAEA,KACE,EACA,GAAG,EACM,CACT,IAAM,EAAY,KAAKA,GAAoB,GAE3C,GAAI,IAAc,IAAA,GAChB,OAAO,KAAKD,GAGd,IAAM,EAAO,KAAKA,GAElB,KAAKA,GAAS,EACd,KAAKC,GAAsB,KAAKH,GAAa,GAE7C,IAAM,EAAU,EAAK,GAErB,GAAI,KAAKI,KAAa,KAAM,CAC1B,IAAM,EAAS,KAAKA,GAAS,IAAI,CAAI,CAAC,EAAE,IAAI,CAAK,EAE7C,IAAW,IAAA,IACb,EAAO,CAAO,CAElB,CAGA,GAAI,KAAKC,GAAiB,EAAG,CAK3B,IAAM,EAAO,CACX,OACA,GAAI,EACJ,QACA,SACF,EAEA,IAAK,IAAM,KAAY,KAAKJ,GACtB,IAAa,MACf,EAAS,CAAI,CAGnB,CAEA,OAAO,KAAKC,EACd,CAEA,QAAQ,EAAyB,CAC/B,OAAO,KAAKC,GAAoB,KAAW,IAAA,EAC7C,CAEA,UAAoB,CAClB,OAAO,KAAKD,EACd,CAEA,YAAuB,CACrB,OAAO,KAAKH,EACd,CAEA,GACE,EACA,EACA,EAGY,CACZ,EAAgB,KAAKC,GAAc,EAAM,IAAI,EAE7C,KAAKI,KAAa,IAAI,IAEtB,IAAI,EAAe,KAAKA,GAAS,IAAI,CAAI,EAEpC,IACH,EAAe,IAAI,IACnB,KAAKA,GAAS,IAAI,EAAM,CAAY,GAGtC,IAAM,EAAiB,EAIvB,OAFA,EAAa,IAAI,EAAO,CAAc,MAEzB,CAEX,IAAM,EAAW,KAAKA,IAAU,IAAI,CAAI,EAEpC,GAAU,IAAI,CAAK,IAAM,GAC3B,EAAS,OAAO,CAAK,CAEzB,CACF,CAEA,aACE,EACY,CACZ,IAAM,EAAY,KAAKH,GAAW,QAAQ,IAAI,EAC1C,EAEA,IAAc,IAChB,EAAQ,KAAKA,GAAW,OACxB,KAAKA,GAAW,KAAK,CAAQ,IAE7B,KAAKA,GAAW,GAAa,EAC7B,EAAQ,GAGV,KAAKI,KACL,IAAI,EAAa,GAEjB,UAAa,CACN,IAIL,EAAa,GACb,KAAKJ,GAAW,GAAS,KAEzB,KAAKI,KACP,CACF,CACF"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RouterLogger.js","names":["#config","LEVEL_CONFIGS","#currentThreshold","#writeLog","LOG_LEVELS","#writeToConsole","#invokeCallback","#inCallback","#reportError"],"sources":["../../../../src/utils/logger/RouterLogger.ts"],"sourcesContent":["// packages/core/src/utils/logger/RouterLogger.ts\n\nimport { LOG_LEVELS, LEVEL_CONFIGS } from \"./constants\";\n\nimport type {\n LogLevel,\n LoggerConfig,\n LogLevelConfig,\n LogCallback,\n} from \"../../types\";\n\n/**\n * Internal config type with required callbackIgnoresLevel\n * (always initialized to false)\n */\ninterface InternalLoggerConfig {\n level: LogLevelConfig;\n callback?: LogCallback | undefined;\n callbackIgnoresLevel: boolean;\n}\n\n/**\n * Logger class for centralized logging with configurable levels and callbacks.\n *\n * Features:\n * - Three log levels: log, warn, error\n * - Configurable threshold filtering (all, warn-error, error-only, none)\n * - Optional callback for custom log processing\n * - Callback can optionally ignore level threshold\n * - Context-based message formatting\n *\n * @example\n * ```ts\n * import { logger } from './Logger';\n *\n * // Configure logger\n * logger.configure({ level: 'warn-error' });\n *\n * // Use logger\n * logger.log('Router', 'Navigation started'); // Won't show (below threshold)\n * logger.warn('Router', 'Deprecated API used'); // Will show\n * ```\n */\nexport class RouterLogger {\n /** Internal configuration storage using private field */\n readonly #config: InternalLoggerConfig = {\n level: \"all\",\n callbackIgnoresLevel: false,\n };\n\n /** Cached numeric threshold value for performance (avoids repeated lookups) */\n #currentThreshold = 0;\n\n /**\n * Re-entrancy guard: true while a user callback is executing. Prevents a\n * callback that itself calls `logger.*` from recursing back through\n * `#invokeCallback` (which would otherwise spin ~5.9k deep until a swallowed\n * RangeError, see #791). Console output is unaffected.\n */\n #inCallback = false;\n\n /**\n * @param config - Optional initial configuration (level / callback /\n * callbackIgnoresLevel), applied once at construction.\n *\n * Each router owns its own `RouterLogger` instance, built from\n * `options.logger` in the `Router` constructor. This replaces the former\n * process-global singleton whose `configure()` leaked across every router in\n * the process — the last `createRouter` won (#724).\n */\n constructor(config?: Partial<LoggerConfig>) {\n if (config) {\n this.configure(config);\n }\n }\n\n /**\n * Configures the logger with new settings.\n *\n * @param config - Partial configuration to merge with existing config\n * @param config.level - Minimum log level to output ('all' | 'warn-error' | 'error-only' | 'none')\n * @param config.callback - Optional callback function to receive log messages\n * @param config.callbackIgnoresLevel - If true, callback receives all messages regardless of level\n *\n * @example\n * ```ts\n * // Set minimum level to warnings\n * logger.configure({ level: 'warn-error' });\n *\n * // Add custom callback that ignores level\n * logger.configure({\n * callback: (level, context, message) => {\n * sendToAnalytics({ level, context, message });\n * },\n * callbackIgnoresLevel: true\n * });\n * ```\n */\n configure(config: Partial<LoggerConfig>): void {\n // Read each field ONCE into a local — an unstable getter must not be re-read\n // between validation and storage: re-reading could pass validation with a\n // valid level and then store a later, unvalidated one, disabling the\n // threshold filter (a TOCTOU, #1162).\n const level = config.level;\n\n if (level !== undefined) {\n // Validate that the provided level is a valid configuration level\n if (!Object.hasOwn(LEVEL_CONFIGS, level)) {\n throw new Error(\n `Invalid log level: \"${level}\". Valid levels are: ${Object.keys(LEVEL_CONFIGS).join(\", \")}`,\n );\n }\n\n this.#config.level = level;\n this.#currentThreshold = LEVEL_CONFIGS[level];\n }\n if (Object.hasOwn(config, \"callback\")) {\n this.#config.callback = config.callback;\n }\n\n const callbackIgnoresLevel = config.callbackIgnoresLevel;\n\n if (callbackIgnoresLevel !== undefined) {\n this.#config.callbackIgnoresLevel = callbackIgnoresLevel;\n }\n }\n\n /**\n * Returns the current logger configuration.\n *\n * @returns Current configuration object with level, callback, and callbackIgnoresLevel\n *\n * @example\n * ```ts\n * const config = logger.getConfig();\n * console.log(config.level); // 'warn'\n * console.log(config.callbackIgnoresLevel); // false\n * ```\n */\n getConfig(): LoggerConfig {\n return {\n level: this.#config.level,\n callback: this.#config.callback,\n callbackIgnoresLevel: this.#config.callbackIgnoresLevel,\n };\n }\n\n /**\n * Logs an informational message at 'log' level.\n *\n * This is the lowest severity level. Messages are shown when level is 'all'.\n *\n * @param context - Context identifier (e.g., 'Router', 'Plugin')\n * @param message - Main log message\n * @param args - Additional arguments to log (objects, arrays, etc.)\n *\n * @example\n * ```ts\n * logger.log('Router', 'Navigation started', { from: '/home', to: '/about' });\n * // Output: [Router] Navigation started { from: '/home', to: '/about' }\n * ```\n */\n log(context: string, message: string, ...args: unknown[]): void {\n this.#writeLog(\"log\", context, message, args);\n }\n\n /**\n * Logs a warning message at 'warn' level.\n *\n * Use for deprecation notices, non-critical issues, or potential problems.\n * Messages are shown when level is 'all' or 'warn-error'.\n *\n * @param context - Context identifier (e.g., 'Router', 'Plugin')\n * @param message - Warning message\n * @param args - Additional arguments to log\n *\n * @example\n * ```ts\n * logger.warn('Router', 'Using deprecated API', { method: 'oldNavigate' });\n * // Output: [Router] Using deprecated API { method: 'oldNavigate' }\n * ```\n */\n warn(context: string, message: string, ...args: unknown[]): void {\n this.#writeLog(\"warn\", context, message, args);\n }\n\n /**\n * Logs an error message at 'error' level.\n *\n * Use for critical errors, exceptions, or failures that require attention.\n * Messages are shown when level is 'all', 'warn-error', or 'error-only'.\n *\n * @param context - Context identifier (e.g., 'Router', 'Plugin')\n * @param message - Error message\n * @param args - Additional arguments to log (often error objects)\n *\n * @example\n * ```ts\n * logger.error('Router', 'Navigation failed', new Error('Route not found'));\n * // Output: [Router] Navigation failed Error: Route not found\n * ```\n */\n error(context: string, message: string, ...args: unknown[]): void {\n this.#writeLog(\"error\", context, message, args);\n }\n\n /**\n * Central logging method that coordinates console output and callback invocation.\n *\n * This method implements the core logging logic:\n * 1. Early exit optimization for 'none' level (unless callback ignores level)\n * 2. Level threshold comparison for console output filtering\n * 3. Delegates to #writeToConsole and #invokeCallback\n *\n * @param level - Log level ('log' | 'warn' | 'error')\n * @param context - Context identifier\n * @param message - Log message\n * @param args - Additional arguments\n *\n * @private\n */\n #writeLog(\n level: LogLevel,\n context: string,\n message: string,\n args: unknown[],\n ): void {\n // Early exit optimization: if level is 'none' and callback doesn't ignore level,\n // skip all processing (both console and callback)\n // Stryker disable next-line BlockStatement: equivalent — emptying this early-exit block falls through, but at level \"none\" the downstream guards already yield no output: #writeToConsole skips (threshold 3 > every message level) and #invokeCallback returns (this branch runs only when callbackIgnoresLevel is false). Pure perf shortcut; the ConditionalExpression →true sibling on this line stays killed (not silenced here).\n if (this.#config.level === \"none\" && !this.#config.callbackIgnoresLevel) {\n return;\n }\n\n // Convert message level to numeric value for threshold comparison\n // LOG_LEVELS: { log: 0, warn: 1, error: 2 }\n const messageLevelValue = LOG_LEVELS[level];\n\n // Determine if this message should skip console output\n // Example: if threshold is 'warn' (1), then 'log' messages (0) are skipped\n const shouldSkipConsole = messageLevelValue < this.#currentThreshold;\n\n // Console output (respects level threshold)\n if (!shouldSkipConsole) {\n this.#writeToConsole(level, context, message, args);\n }\n\n // Callback handling (may ignore level threshold based on config)\n this.#invokeCallback(level, context, message, shouldSkipConsole, args);\n }\n\n /**\n * Writes a formatted log message to the console.\n *\n * Features:\n * - Formats message with context: \"[Context] message\"\n * - Uses appropriate console method (log/warn/error)\n * - Safe: checks for console existence (for non-browser environments)\n *\n * @param level - Console method to use ('log' | 'warn' | 'error')\n * @param context - Context identifier (prepended to message if present)\n * @param message - Log message\n * @param args - Additional arguments to pass to console\n *\n * @private\n */\n #writeToConsole(\n level: LogLevel,\n context: string,\n message: string,\n args: unknown[],\n ): void {\n // Safety check: ensure console exists and has the required method\n // This is important for environments like Node.js tests or edge cases\n if (\n typeof console !== \"undefined\" &&\n typeof console[level] === \"function\"\n ) {\n // Format message with context bracket notation for visual clarity\n // Note: formatting is done inside the check to avoid unnecessary string allocation\n // when console is not available\n const formattedMessage = context ? `[${context}] ${message}` : message;\n\n console[level](formattedMessage, ...args);\n }\n }\n\n /**\n * Invokes the configured callback with log data, respecting level settings.\n *\n * Complex logic handling:\n * 1. Skip if no callback configured\n * 2. Skip if callback respects level AND message is below threshold\n * 3. Call callback with error handling (prevents callback errors from breaking logger)\n *\n * The callbackIgnoresLevel flag enables two modes:\n * - false (default): callback only receives messages that pass threshold (same as console)\n * - true: callback receives ALL messages regardless of threshold (useful for analytics)\n *\n * @param level - Log level\n * @param context - Context identifier\n * @param message - Log message\n * @param shouldSkipConsole - Whether console output was skipped (used for level logic)\n * @param args - Additional arguments\n *\n * @private\n */\n #invokeCallback(\n level: LogLevel,\n context: string,\n message: string,\n shouldSkipConsole: boolean,\n args: unknown[],\n ): void {\n // Early exit: no callback configured, or callback respects level and message is filtered\n if (\n !this.#config.callback ||\n (!this.#config.callbackIgnoresLevel && shouldSkipConsole)\n ) {\n return;\n }\n\n // Re-entrancy guard: a callback calling logger.* re-enters here via\n // #writeLog → #invokeCallback. Skip the nested invocation so the pattern is\n // a safe no-op (console output already happened in #writeLog) instead of\n // recursing to a swallowed RangeError (#791).\n if (this.#inCallback) {\n return;\n }\n\n // Wrap callback invocation in try-catch to prevent user code errors\n // from breaking the logger or causing cascading failures\n this.#inCallback = true;\n try {\n // An async callback (`(...) => Promise<void>` is assignable to the\n // void-typed LogCallback) returns a Promise whose rejection would otherwise\n // leak as a Node `unhandledRejection` — process-fatal under\n // `--unhandled-rejections=strict` (Node 22+ default). Read the runtime\n // return and isolate it like core's subscribe (#944): duck-check the\n // thenable + `.catch` into the same console.error sink a sync throw uses\n // (#1161).\n // eslint-disable-next-line @typescript-eslint/no-confusing-void-expression -- read the runtime Promise of a void-typed async callback (#1161)\n const result: unknown = this.#config.callback(\n level,\n context,\n message,\n ...args,\n );\n\n if (\n result !== null &&\n result !== undefined &&\n typeof (result as PromiseLike<unknown>).then === \"function\"\n ) {\n Promise.resolve(result as PromiseLike<unknown>).catch(\n (error: unknown) => {\n this.#reportError(\"[Logger] Error in async callback:\", error);\n },\n );\n }\n } catch (error) {\n // Fallback error reporting if the callback throws synchronously\n this.#reportError(\"[Logger] Error in callback:\", error);\n } finally {\n this.#inCallback = false;\n }\n }\n\n // Report a callback error via console.error directly — never call the logger\n // (would recurse). Shared by the sync-throw catch and the async-rejection\n // `.catch` (#1161). Console-safety guard mirrors #writeToConsole.\n #reportError(message: string, error: unknown): void {\n if (typeof console !== \"undefined\" && typeof console.error === \"function\") {\n console.error(message, error);\n }\n }\n}\n"],"mappings":"kCA2CA,IAAa,EAAb,KAA0B,CAExB,GAAyC,CACvC,MAAO,MACP,qBAAsB,EACxB,EAGA,GAAoB,EAQpB,GAAc,GAWd,YAAY,EAAgC,CACtC,GACF,KAAK,UAAU,CAAM,CAEzB,CAwBA,UAAU,EAAqC,CAK7C,IAAM,EAAQ,EAAO,MAErB,GAAI,IAAU,IAAA,GAAW,CAEvB,GAAI,CAAC,OAAO,OAAOC,EAAAA,cAAe,CAAK,EACrC,MAAU,MACR,uBAAuB,EAAM,uBAAuB,OAAO,KAAKA,EAAAA,aAAa,CAAC,CAAC,KAAK,IAAI,GAC1F,EAGF,KAAKD,GAAQ,MAAQ,EACrB,KAAKE,GAAoBD,EAAAA,cAAc,EACzC,CACI,OAAO,OAAO,EAAQ,UAAU,IAClC,KAAKD,GAAQ,SAAW,EAAO,UAGjC,IAAM,EAAuB,EAAO,qBAEhC,IAAyB,IAAA,KAC3B,KAAKA,GAAQ,qBAAuB,EAExC,CAcA,WAA0B,CACxB,MAAO,CACL,MAAO,KAAKA,GAAQ,MACpB,SAAU,KAAKA,GAAQ,SACvB,qBAAsB,KAAKA,GAAQ,oBACrC,CACF,CAiBA,IAAI,EAAiB,EAAiB,GAAG,EAAuB,CAC9D,KAAKG,GAAU,MAAO,EAAS,EAAS,CAAI,CAC9C,CAkBA,KAAK,EAAiB,EAAiB,GAAG,EAAuB,CAC/D,KAAKA,GAAU,OAAQ,EAAS,EAAS,CAAI,CAC/C,CAkBA,MAAM,EAAiB,EAAiB,GAAG,EAAuB,CAChE,KAAKA,GAAU,QAAS,EAAS,EAAS,CAAI,CAChD,CAiBA,GACE,EACA,EACA,EACA,EACM,CAIN,GAAI,KAAKH,GAAQ,QAAU,QAAU,CAAC,KAAKA,GAAQ,qBACjD,OASF,IAAM,EAJoBI,EAAAA,WAAW,GAIS,KAAKF,GAG9C,GACH,KAAKG,GAAgB,EAAO,EAAS,EAAS,CAAI,EAIpD,KAAKC,GAAgB,EAAO,EAAS,EAAS,EAAmB,CAAI,CACvE,CAiBA,GACE,EACA,EACA,EACA,EACM,CAGN,GACE,OAAO,QAAY,KACnB,OAAO,QAAQ,IAAW,WAC1B,CAIA,IAAM,EAAmB,EAAU,IAAI,EAAQ,IAAI,IAAY,EAE/D,QAAQ,EAAM,CAAC,EAAkB,GAAG,CAAI,CAC1C,CACF,CAsBA,GACE,EACA,EACA,EACA,EACA,EACM,CAGJ,MAAC,KAAKN,GAAQ,UACb,CAAC,KAAKA,GAAQ,sBAAwB,IASrC,MAAKO,GAMT,MAAKA,GAAc,GACnB,GAAI,CASF,IAAM,EAAkB,KAAKP,GAAQ,SACnC,EACA,EACA,EACA,GAAG,CACL,EAGE,GAAW,MAEX,OAAQ,EAAgC,MAAS,YAEjD,QAAQ,QAAQ,CAA8B,CAAC,CAAC,MAC7C,GAAmB,CAClB,KAAKQ,GAAa,oCAAqC,CAAK,CAC9D,CACF,CAEJ,OAAS,EAAO,CAEd,KAAKA,GAAa,8BAA+B,CAAK,CACxD,QAAU,CACR,KAAKD,GAAc,EACrB,CAjCmB,CAkCrB,CAKA,GAAa,EAAiB,EAAsB,CAC9C,OAAO,QAAY,KAAe,OAAO,QAAQ,OAAU,YAC7D,QAAQ,MAAM,EAAS,CAAK,CAEhC,CACF"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","names":[],"sources":["../../../../src/utils/logger/constants.ts"],"sourcesContent":["import type { LogLevel, LogLevelConfig } from \"../../types\";\n\n/**\n * Numeric mapping for log message severity levels.\n *\n * Maps each severity level to a numeric value for threshold comparison.\n * Lower values = lower severity, higher values = higher severity.\n *\n * Used internally to determine if a message should be filtered based on\n * the configured threshold level.\n *\n * Mapping:\n * - `log`: 0 (lowest severity - informational)\n * - `warn`: 1 (medium severity - warnings)\n * - `error`: 2 (highest severity - critical errors)\n *\n * @example\n * ```ts\n * const messageLevel = LOG_LEVELS['warn']; // 1\n * const threshold = 2; // error-only\n * const shouldFilter = messageLevel < threshold; // true (warn is filtered)\n * ```\n *\n * @internal This is used for internal filtering logic\n */\nexport const LOG_LEVELS: Record<LogLevel, number> = Object.freeze({\n log: 0,\n warn: 1,\n error: 2,\n});\n\n/**\n * Numeric thresholds for logger configuration levels.\n *\n * Maps each configuration level to a minimum threshold value.\n * Messages with a severity level below this threshold are filtered out.\n *\n * Threshold logic:\n * - A message is shown if: `LOG_LEVELS[messageLevel] >= LEVEL_CONFIGS[configLevel]`\n * - Higher threshold value = stricter filtering = fewer messages shown\n *\n * Mapping:\n * - `all`: 0 (no filtering - show everything)\n * - Shows: log (0), warn (1), error (2) ✓\n * - `warn-error`: 1 (filter log messages)\n * - Shows: warn (1), error (2) ✓\n * - Filters: log (0) ✗\n * - `error-only`: 2 (filter log and warn messages)\n * - Shows: error (2) ✓\n * - Filters: log (0), warn (1) ✗\n * - `none`: 3 (filter all messages - complete silence)\n * - Filters: log (0), warn (1), error (2) ✗\n *\n * @example\n * ```ts\n * // Configuration: warn-error\n * const threshold = LEVEL_CONFIGS['warn-error']; // 1\n *\n * // Check if 'log' message should be shown\n * LOG_LEVELS['log'] >= threshold // 0 >= 1 = false (filtered)\n *\n * // Check if 'warn' message should be shown\n * LOG_LEVELS['warn'] >= threshold // 1 >= 1 = true (shown)\n *\n * // Check if 'error' message should be shown\n * LOG_LEVELS['error'] >= threshold // 2 >= 1 = true (shown)\n * ```\n *\n * @internal This is used for internal threshold comparison\n */\nexport const LEVEL_CONFIGS: Record<LogLevelConfig, number> = Object.freeze({\n all: 0,\n \"warn-error\": 1,\n \"error-only\": 2,\n none: 3,\n});\n"],"mappings":"AAyBA,MAAa,EAAuC,OAAO,OAAO,CAChE,IAAK,EACL,KAAM,EACN,MAAO,CACT,CAAC,EAyCY,EAAgD,OAAO,OAAO,CACzE,IAAK,EACL,aAAc,EACd,aAAc,EACd,KAAM,CACR,CAAC"}
@@ -1 +1,2 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./internals.js"),t=require("./engine/validation/route-batch.js");exports.getInternals=e.getInternals,exports.validateRoute=t.validateRoute;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./buildParamMeta-0m11OIJS.js");function t(e,t){return TypeError(`[router.${e}] ${t}`)}function n(e,n,r,i){let a=new Set;for(let o of e){if(a.has(o))throw t(r,`Invalid path for route "${n}": duplicate parameter name ':${o}' in "${i}" (a param name must be unique within a route — the second binding would overwrite the first)`);a.add(o)}}function r(n,r,i,a){for(let o of n)if(e.n.test(o))throw t(i,`Invalid path for route "${r}": invalid query-param name "${o}" in "${a}" (a query-param name cannot contain '<' or '>' — it would never round-trip; rename the query param)`)}function i(e){let t=!0,n=!1;for(let r of e){if(r===`/`){t=!0;continue}if(t&&=(n=r===`:`||r===`*`,!1),!n&&r.codePointAt(0)>=128)return!0}return!1}function a(e,t,n){switch(e){case`name-less`:case`trailing-marker`:return`Invalid path for route "${t}": parameter marker (':' or '*') without a name in "${n}"`;case`fused-marker`:return`Invalid path for route "${t}": parameter marker (':' or '*') must begin a segment, but "${n}" fuses one to a static prefix (use a boundary marker like "/a/:b")`}}function o(e,t){return e.code===`optional-removed`?`Invalid path for route "${t}": optional params are not supported — "${e.segment}". Declare two sibling routes instead: "${e.withoutSegment}" and "${e.requiredForm}" (the route hierarchy already expresses optionality)`:`Invalid path for route "${t}": regex constraints are not supported — '<' and '>' are reserved in path segments ("${e.segment}"). Match the segment as a plain string and validate the value in a guard (canActivate) or app code`}function s(s,c,l,u){if(typeof s!=`string`){let e;throw e=s===null?`null`:Array.isArray(s)?`array`:typeof s,t(l,`Route path must be a string, got ${e}`)}if(s===``)return;if(/\s/.test(s))throw t(l,`Invalid path for route "${c}": whitespace not allowed in "${s}"`);if(!/^([/?~]|[^/]+$)/.test(s))throw t(l,`Route "${c}" has invalid path format: "${s}". Path should start with '/', '~', '?' or be a relative segment.`);if(s.includes(`//`))throw t(l,`Invalid path for route "${c}": double slashes not allowed in "${s}"`);let{pathPattern:d,urlParams:f,queryParams:p}=e.r(s);n(f,c,l,s),r(p,c,l,s);let m=e.i(d);if(m!==void 0)throw t(l,o(m,c));let h=e.a(d);if(h!==void 0)throw t(l,a(h,c,s));if(i(d))throw t(l,`Invalid path for route "${c}": non-ASCII static segment in "${s}" — match compares static segments raw and rejects non-ASCII input, so this route would never match. Percent-encode it (e.g. '/caf%C3%A9') or use a param`);let g=u&&u.paramMeta.urlParams.length>0;if(s.startsWith(`~`)&&g)throw t(l,`Absolute path "${s}" cannot be used under parent route with URL parameters`)}const c=/^[A-Z_a-z][\w-]*$/,l=/\S/,u=1e4;function d(e){if(e===null)return`null`;if(typeof e==`object`){try{let t=e.constructor;if(typeof t==`function`&&t.name!==`Object`)return t.name||`object`}catch{}return`object`}return typeof e}function f(e){for(let t of Object.keys(e)){let n=Object.getOwnPropertyDescriptor(e,t);if(n&&(n.get||n.set))return!0}return!1}function p(e,t){if(!e||typeof e!=`object`)throw TypeError(`[router.${t}] Route must be an object, got ${d(e)}`);let n=Object.getPrototypeOf(e);if(n!==Object.prototype&&n!==null)throw TypeError(`[router.${t}] Route must be a plain object, got ${d(e)}`);if(f(e))throw TypeError(`[router.${t}] Route must not have getters or setters`)}function m(e,t){if(e.encodeParams!==void 0&&typeof e.encodeParams!=`function`)throw TypeError(`[router.${t}] Route "${String(e.name)}" encodeParams must be a function`)}function h(e,t){if(e.decodeParams!==void 0&&typeof e.decodeParams!=`function`)throw TypeError(`[router.${t}] Route "${String(e.name)}" decodeParams must be a function`)}function g(e,t){if(typeof e.name!=`string`)throw TypeError(`[router.${t}] Route name must be a string, got ${d(e.name)}`);let n=e.name;if(n===``)throw TypeError(`[router.${t}] Route name cannot be empty`);if(!l.test(n))throw TypeError(`[router.${t}] Route name cannot contain only whitespace`);if(n.length>u)throw TypeError(`[router.${t}] Route name exceeds maximum length of ${u} characters`);if(!n.startsWith(`@@`)){if(n.includes(`.`))throw TypeError(`[router.${t}] Route name "${n}" cannot contain dots. Use children array or { parent } option in addRoute() instead.`);if(!c.test(n))throw TypeError(`[router.${t}] Invalid route name "${n}". Name must start with a letter or underscore, followed by letters, numbers, underscores, or hyphens.`)}}function _(e,t){if(!t.includes(`.`))return e.children.get(t);let n=e;for(let e of t.split(`.`))if(n=n.children.get(e),!n)return;return n}function v(e,t,n){if(_(e,t))throw Error(`[router.${n}] Route "${t}" already exists`)}function y(e,t,n){if(e.has(t))throw Error(`[router.${n}] Duplicate route "${t}" in batch`);e.add(t)}function b(e,t,n,r){let i=t===``?e:_(e,t);if(i){for(let e of i.children.values())if(e.path===n)throw Error(`[router.${r}] Path "${n}" is already defined`)}}function x(e,t,n,r){let i=e.get(t);if(i?.has(n))throw Error(`[router.${r}] Path "${n}" is already defined`);i?i.add(n):e.set(t,new Set([n]))}function S(e,t,n,r=``,i,a){p(e,t);let o=e;g(o,t),s(o.path,o.name,t,n),m(o,t),h(o,t);let c=o.name,l=r?`${r}.${c}`:c;n&&l&&v(n,l,t),i&&y(i,l,t);let u=o.path,f=r;if(n&&b(n,f,u,t),a&&x(a,f,u,t),o.children!==void 0){if(!Array.isArray(o.children))throw TypeError(`[router.${t}] Route "${c}" children must be an array, got ${d(o.children)}`);for(let e of o.children)S(e,t,n,l,i,a)}}exports.getInternals=e.u,exports.validateRoute=S;
2
+ //# sourceMappingURL=validation.js.map