@real-router/core 0.55.0 → 0.57.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 (145) hide show
  1. package/dist/cjs/Router-BSGzVINO.js +6 -0
  2. package/dist/cjs/Router-BSGzVINO.js.map +1 -0
  3. package/dist/{esm/Router-Dg-zk8AS.d.mts → cjs/Router-hW6ivqrX.d.ts} +2 -2
  4. package/dist/cjs/Router-hW6ivqrX.d.ts.map +1 -0
  5. package/dist/cjs/api.d.ts +2 -2
  6. package/dist/cjs/api.d.ts.map +1 -1
  7. package/dist/cjs/api.js +1 -1
  8. package/dist/cjs/api.js.map +1 -1
  9. package/dist/cjs/{cloneRouter-C9Rth_8U.js → cloneRouter-7z-60z_f.js} +2 -2
  10. package/dist/cjs/{cloneRouter-C9Rth_8U.js.map → cloneRouter-7z-60z_f.js.map} +1 -1
  11. package/dist/cjs/{index-C-i6vx5Y.d.ts → index-BWUmnecT.d.ts} +1 -2
  12. package/dist/cjs/index-BWUmnecT.d.ts.map +1 -0
  13. package/dist/cjs/{RouterError-WhCzIWuc.d.ts → index-CYpAZCoc.d.ts} +19 -2
  14. package/dist/cjs/index-CYpAZCoc.d.ts.map +1 -0
  15. package/dist/cjs/{index-K1U_fqfJ.d.ts → index-D2WRiyWS.d.ts} +2 -2
  16. package/dist/cjs/index-D2WRiyWS.d.ts.map +1 -0
  17. package/dist/cjs/index.d.ts +5 -5
  18. package/dist/cjs/index.js +1 -1
  19. package/dist/cjs/{internals-CWMOL1B8.js → internals-DJjgSePy.js} +2 -2
  20. package/dist/cjs/internals-DJjgSePy.js.map +1 -0
  21. package/dist/cjs/utils.d.ts +1 -1
  22. package/dist/cjs/utils.js +1 -1
  23. package/dist/cjs/utils.js.map +1 -1
  24. package/dist/cjs/validation.d.ts +17 -5
  25. package/dist/cjs/validation.d.ts.map +1 -1
  26. package/dist/cjs/validation.js +1 -1
  27. package/dist/esm/Router-B7txWo9N.mjs +6 -0
  28. package/dist/esm/Router-B7txWo9N.mjs.map +1 -0
  29. package/dist/{cjs/Router-Dg-zk8AS.d.ts → esm/Router-hW6ivqrX.d.mts} +2 -2
  30. package/dist/esm/Router-hW6ivqrX.d.mts.map +1 -0
  31. package/dist/esm/api.d.mts +2 -2
  32. package/dist/esm/api.d.mts.map +1 -1
  33. package/dist/esm/api.mjs +1 -1
  34. package/dist/esm/api.mjs.map +1 -1
  35. package/dist/esm/{cloneRouter-BYNiwchg.mjs → cloneRouter-BNCQ7tIa.mjs} +2 -2
  36. package/dist/esm/{cloneRouter-BYNiwchg.mjs.map → cloneRouter-BNCQ7tIa.mjs.map} +1 -1
  37. package/dist/esm/{index-C-i6vx5Y.d.mts → index-BWUmnecT.d.mts} +1 -2
  38. package/dist/esm/index-BWUmnecT.d.mts.map +1 -0
  39. package/dist/esm/{RouterError-WhCzIWuc.d.mts → index-CYpAZCoc.d.mts} +19 -2
  40. package/dist/esm/index-CYpAZCoc.d.mts.map +1 -0
  41. package/dist/esm/{index-DKzxav48.d.mts → index-CjWKWPY6.d.mts} +2 -2
  42. package/dist/esm/index-CjWKWPY6.d.mts.map +1 -0
  43. package/dist/esm/index.d.mts +5 -5
  44. package/dist/esm/index.mjs +1 -1
  45. package/dist/esm/index.mjs.map +1 -1
  46. package/dist/esm/{internals-DT4mneSz.mjs → internals-C8mRvTxc.mjs} +2 -2
  47. package/dist/esm/internals-C8mRvTxc.mjs.map +1 -0
  48. package/dist/esm/utils.d.mts +1 -1
  49. package/dist/esm/utils.mjs +1 -1
  50. package/dist/esm/utils.mjs.map +1 -1
  51. package/dist/esm/validation.d.mts +17 -5
  52. package/dist/esm/validation.d.mts.map +1 -1
  53. package/dist/esm/validation.mjs +1 -1
  54. package/package.json +3 -4
  55. package/dist/cjs/Router-C7eE1kIK.js +0 -6
  56. package/dist/cjs/Router-C7eE1kIK.js.map +0 -1
  57. package/dist/cjs/Router-Dg-zk8AS.d.ts.map +0 -1
  58. package/dist/cjs/RouterError-WhCzIWuc.d.ts.map +0 -1
  59. package/dist/cjs/index-C-i6vx5Y.d.ts.map +0 -1
  60. package/dist/cjs/index-K1U_fqfJ.d.ts.map +0 -1
  61. package/dist/cjs/internals-CWMOL1B8.js.map +0 -1
  62. package/dist/esm/Router-Dg-zk8AS.d.mts.map +0 -1
  63. package/dist/esm/Router-DiZbYMLx.mjs +0 -6
  64. package/dist/esm/Router-DiZbYMLx.mjs.map +0 -1
  65. package/dist/esm/RouterError-WhCzIWuc.d.mts.map +0 -1
  66. package/dist/esm/index-C-i6vx5Y.d.mts.map +0 -1
  67. package/dist/esm/index-DKzxav48.d.mts.map +0 -1
  68. package/dist/esm/internals-DT4mneSz.mjs.map +0 -1
  69. package/src/Router.ts +0 -725
  70. package/src/RouterError.ts +0 -324
  71. package/src/api/cloneRouter.ts +0 -159
  72. package/src/api/getDependenciesApi.ts +0 -160
  73. package/src/api/getLifecycleApi.ts +0 -65
  74. package/src/api/getPluginApi.ts +0 -228
  75. package/src/api/getRoutesApi.ts +0 -546
  76. package/src/api/helpers.ts +0 -10
  77. package/src/api/index.ts +0 -16
  78. package/src/api/types.ts +0 -12
  79. package/src/constants.ts +0 -101
  80. package/src/createRouter.ts +0 -32
  81. package/src/fsm/index.ts +0 -5
  82. package/src/fsm/routerFSM.ts +0 -130
  83. package/src/getNavigator.ts +0 -30
  84. package/src/guards.ts +0 -46
  85. package/src/helpers.ts +0 -197
  86. package/src/index.ts +0 -50
  87. package/src/internals.ts +0 -200
  88. package/src/namespaces/DependenciesNamespace/dependenciesStore.ts +0 -30
  89. package/src/namespaces/DependenciesNamespace/index.ts +0 -5
  90. package/src/namespaces/EventBusNamespace/EventBusNamespace.ts +0 -485
  91. package/src/namespaces/EventBusNamespace/index.ts +0 -5
  92. package/src/namespaces/EventBusNamespace/types.ts +0 -11
  93. package/src/namespaces/NavigationNamespace/NavigationNamespace.ts +0 -552
  94. package/src/namespaces/NavigationNamespace/constants.ts +0 -55
  95. package/src/namespaces/NavigationNamespace/index.ts +0 -5
  96. package/src/namespaces/NavigationNamespace/transition/completeTransition.ts +0 -108
  97. package/src/namespaces/NavigationNamespace/transition/errorHandling.ts +0 -124
  98. package/src/namespaces/NavigationNamespace/transition/guardPhase.ts +0 -283
  99. package/src/namespaces/NavigationNamespace/types.ts +0 -110
  100. package/src/namespaces/OptionsNamespace/OptionsNamespace.ts +0 -28
  101. package/src/namespaces/OptionsNamespace/constants.ts +0 -19
  102. package/src/namespaces/OptionsNamespace/helpers.ts +0 -50
  103. package/src/namespaces/OptionsNamespace/index.ts +0 -7
  104. package/src/namespaces/OptionsNamespace/validators.ts +0 -13
  105. package/src/namespaces/PluginsNamespace/PluginsNamespace.ts +0 -291
  106. package/src/namespaces/PluginsNamespace/constants.ts +0 -34
  107. package/src/namespaces/PluginsNamespace/index.ts +0 -7
  108. package/src/namespaces/PluginsNamespace/types.ts +0 -22
  109. package/src/namespaces/PluginsNamespace/validators.ts +0 -28
  110. package/src/namespaces/RouteLifecycleNamespace/RouteLifecycleNamespace.ts +0 -558
  111. package/src/namespaces/RouteLifecycleNamespace/index.ts +0 -5
  112. package/src/namespaces/RouteLifecycleNamespace/types.ts +0 -10
  113. package/src/namespaces/RouterLifecycleNamespace/RouterLifecycleNamespace.ts +0 -81
  114. package/src/namespaces/RouterLifecycleNamespace/constants.ts +0 -25
  115. package/src/namespaces/RouterLifecycleNamespace/index.ts +0 -5
  116. package/src/namespaces/RouterLifecycleNamespace/types.ts +0 -30
  117. package/src/namespaces/RoutesNamespace/RoutesNamespace.ts +0 -582
  118. package/src/namespaces/RoutesNamespace/constants.ts +0 -6
  119. package/src/namespaces/RoutesNamespace/forwardChain.ts +0 -34
  120. package/src/namespaces/RoutesNamespace/helpers.ts +0 -204
  121. package/src/namespaces/RoutesNamespace/index.ts +0 -11
  122. package/src/namespaces/RoutesNamespace/routeGuards.ts +0 -62
  123. package/src/namespaces/RoutesNamespace/routesStore.ts +0 -566
  124. package/src/namespaces/RoutesNamespace/types.ts +0 -81
  125. package/src/namespaces/StateNamespace/StateNamespace.ts +0 -224
  126. package/src/namespaces/StateNamespace/helpers.ts +0 -24
  127. package/src/namespaces/StateNamespace/index.ts +0 -5
  128. package/src/namespaces/StateNamespace/types.ts +0 -15
  129. package/src/namespaces/index.ts +0 -35
  130. package/src/stateMetaStore.ts +0 -15
  131. package/src/transitionPath.ts +0 -436
  132. package/src/typeGuards.ts +0 -59
  133. package/src/types/RouterValidator.ts +0 -156
  134. package/src/types.ts +0 -69
  135. package/src/utils/createRequestScope.ts +0 -174
  136. package/src/utils/getStaticPaths.ts +0 -50
  137. package/src/utils/hydrateRouter.ts +0 -89
  138. package/src/utils/index.ts +0 -27
  139. package/src/utils/serializeRouterState.ts +0 -120
  140. package/src/utils/serializeState.ts +0 -63
  141. package/src/validation.ts +0 -12
  142. package/src/wiring/RouterWiringBuilder.ts +0 -275
  143. package/src/wiring/index.ts +0 -7
  144. package/src/wiring/types.ts +0 -47
  145. package/src/wiring/wireRouter.ts +0 -26
@@ -1,30 +0,0 @@
1
- import { DEFAULT_LIMITS } from "../../constants";
2
-
3
- import type { Limits } from "../../types";
4
- import type { DefaultDependencies } from "@real-router/types";
5
-
6
- export interface DependenciesStore<
7
- Dependencies extends DefaultDependencies = DefaultDependencies,
8
- > {
9
- dependencies: Partial<Dependencies>;
10
- limits: Limits;
11
- }
12
-
13
- export function createDependenciesStore<
14
- Dependencies extends DefaultDependencies = DefaultDependencies,
15
- >(
16
- initialDependencies: Partial<Dependencies> = {},
17
- ): DependenciesStore<Dependencies> {
18
- const dependencies = Object.create(null) as Partial<Dependencies>;
19
-
20
- for (const key in initialDependencies) {
21
- if (initialDependencies[key] !== undefined) {
22
- dependencies[key] = initialDependencies[key];
23
- }
24
- }
25
-
26
- return {
27
- dependencies,
28
- limits: DEFAULT_LIMITS,
29
- };
30
- }
@@ -1,5 +0,0 @@
1
- // packages/core/src/namespaces/DependenciesNamespace/index.ts
2
-
3
- export { createDependenciesStore } from "./dependenciesStore";
4
-
5
- export type { DependenciesStore } from "./dependenciesStore";
@@ -1,485 +0,0 @@
1
- // packages/core/src/namespaces/EventBusNamespace/EventBusNamespace.ts
2
-
3
- import { events } from "../../constants";
4
- import { routerEvents, routerStates } from "../../fsm";
5
-
6
- import type { EventBusOptions } from "./types";
7
- import type { RouterEvent, RouterPayloads, RouterState } from "../../fsm";
8
- import type { RouterError } from "../../RouterError";
9
- import type { EventMethodMap, RouterEventMap } from "../../types";
10
- import type { FSM } from "@real-router/fsm";
11
- import type {
12
- EventName,
13
- LeaveFn,
14
- LeaveState,
15
- NavigationOptions,
16
- Plugin,
17
- State,
18
- SubscribeFn,
19
- Unsubscribe,
20
- } from "@real-router/types";
21
- import type { EventEmitter } from "event-emitter";
22
-
23
- function ensureError(value: unknown): Error {
24
- /* v8 ignore next -- @preserve: defensive guard — listeners should always throw Error objects */
25
- return value instanceof Error ? value : new Error(String(value));
26
- }
27
-
28
- function settleLeavePromises(
29
- promises: Promise<void>[],
30
- firstSyncError: unknown,
31
- signal: AbortSignal,
32
- ): Promise<void> {
33
- return new Promise<void>((resolve, reject) => {
34
- const onAbort = (): void => {
35
- reject(ensureError(signal.reason));
36
- };
37
-
38
- if (signal.aborted) {
39
- onAbort();
40
-
41
- return;
42
- }
43
-
44
- signal.addEventListener("abort", onAbort, { once: true });
45
-
46
- void Promise.allSettled(promises).then((results) => {
47
- signal.removeEventListener("abort", onAbort);
48
-
49
- if (signal.aborted) {
50
- // Race lost to abort — the abort handler already rejected; do nothing
51
- return;
52
- }
53
-
54
- if (firstSyncError !== undefined) {
55
- reject(ensureError(firstSyncError));
56
-
57
- return;
58
- }
59
-
60
- const rejected = results.find(
61
- (result): result is PromiseRejectedResult =>
62
- result.status === "rejected",
63
- );
64
-
65
- if (rejected !== undefined) {
66
- reject(ensureError(rejected.reason));
67
-
68
- return;
69
- }
70
-
71
- resolve();
72
- });
73
- });
74
- }
75
-
76
- export class EventBusNamespace {
77
- readonly #fsm: FSM<RouterState, RouterEvent, null, RouterPayloads>;
78
- readonly #emitter: EventEmitter<RouterEventMap>;
79
- readonly #leaveListeners: LeaveFn[] = [];
80
-
81
- #currentToState: State | undefined;
82
- #pendingToState: State | undefined;
83
- #pendingFromState: State | undefined;
84
- #pendingError: unknown;
85
-
86
- constructor(options: EventBusOptions) {
87
- this.#fsm = options.routerFSM;
88
- this.#emitter = options.emitter;
89
- this.#currentToState = undefined;
90
- this.#setupFSMActions();
91
- }
92
-
93
- static validateSubscribeListener(listener: unknown): void {
94
- if (typeof listener !== "function") {
95
- throw new TypeError(
96
- "[router.subscribe] Expected a function. " +
97
- "For Observable pattern use @real-router/rx package",
98
- );
99
- }
100
- }
101
-
102
- static validateSubscribeLeaveListener(listener: unknown): void {
103
- if (typeof listener !== "function") {
104
- throw new TypeError("[router.subscribeLeave] Expected a function");
105
- }
106
- }
107
-
108
- emitRouterStart(): void {
109
- this.#emitter.emit(events.ROUTER_START);
110
- }
111
-
112
- emitRouterStop(): void {
113
- this.#emitter.emit(events.ROUTER_STOP);
114
- }
115
-
116
- emitTransitionStart(toState: State, fromState?: State): void {
117
- this.#emitter.emit(events.TRANSITION_START, toState, fromState);
118
- }
119
-
120
- emitTransitionSuccess(
121
- toState: State,
122
- fromState?: State,
123
- opts?: NavigationOptions,
124
- ): void {
125
- this.#emitter.emit(events.TRANSITION_SUCCESS, toState, fromState, opts);
126
- }
127
-
128
- emitTransitionError(
129
- toState?: State,
130
- fromState?: State,
131
- error?: RouterError,
132
- ): void {
133
- this.#emitter.emit(events.TRANSITION_ERROR, toState, fromState, error);
134
- }
135
-
136
- emitTransitionCancel(toState: State, fromState?: State): void {
137
- this.#emitter.emit(events.TRANSITION_CANCEL, toState, fromState);
138
- }
139
-
140
- emitTransitionLeaveApprove(toState: State, fromState?: State): void {
141
- this.#emitter.emit(events.TRANSITION_LEAVE_APPROVE, toState, fromState);
142
- }
143
-
144
- sendStart(): void {
145
- this.#fsm.send(routerEvents.START);
146
- }
147
-
148
- sendStop(): void {
149
- this.#fsm.send(routerEvents.STOP);
150
- }
151
-
152
- sendDispose(): void {
153
- this.#fsm.send(routerEvents.DISPOSE);
154
- }
155
-
156
- sendStarted(): void {
157
- this.#fsm.send(routerEvents.STARTED);
158
- }
159
-
160
- sendNavigate(toState: State, fromState?: State): void {
161
- this.#currentToState = toState;
162
- // Bypass FSM dispatch — forceState + direct emit (no action lookup, no rest params)
163
- this.#fsm.forceState(routerStates.TRANSITION_STARTED);
164
- this.emitTransitionStart(toState, fromState);
165
- }
166
-
167
- sendComplete(
168
- state: State,
169
- fromState?: State,
170
- opts: NavigationOptions = {},
171
- ): void {
172
- // Bypass FSM dispatch — forceState + direct emit
173
- this.#fsm.forceState(routerStates.READY);
174
- this.emitTransitionSuccess(state, fromState, opts);
175
-
176
- if (this.#currentToState === state) {
177
- this.#currentToState = undefined;
178
- }
179
- }
180
-
181
- sendLeaveApprove(toState: State, fromState?: State): void {
182
- // Bypass FSM dispatch — forceState + direct emit (no action lookup, no rest params)
183
- this.#fsm.forceState(routerStates.LEAVE_APPROVED);
184
- this.emitTransitionLeaveApprove(toState, fromState);
185
- }
186
-
187
- sendFail(toState?: State, fromState?: State, error?: unknown): void {
188
- const prev = this.#currentToState;
189
-
190
- this.#pendingToState = toState;
191
- this.#pendingFromState = fromState;
192
- this.#pendingError = error;
193
- this.#fsm.send(routerEvents.FAIL);
194
-
195
- if (this.#currentToState === prev) {
196
- this.#currentToState = undefined;
197
- }
198
- }
199
-
200
- sendFailSafe(toState?: State, fromState?: State, error?: unknown): void {
201
- if (this.isReady()) {
202
- this.sendFail(toState, fromState, error);
203
- } else {
204
- this.emitTransitionError(toState, fromState, error as RouterError);
205
- }
206
- }
207
-
208
- sendCancel(toState: State, fromState?: State): void {
209
- const prev = this.#currentToState;
210
-
211
- this.#pendingToState = toState;
212
- this.#pendingFromState = fromState;
213
- this.#fsm.send(routerEvents.CANCEL);
214
-
215
- if (this.#currentToState === prev) {
216
- this.#currentToState = undefined;
217
- }
218
- }
219
-
220
- canBeginTransition(): boolean {
221
- return this.#fsm.canSend(routerEvents.NAVIGATE);
222
- }
223
-
224
- canStart(): boolean {
225
- return this.#fsm.canSend(routerEvents.START);
226
- }
227
-
228
- canCancel(): boolean {
229
- return this.#fsm.canSend(routerEvents.CANCEL);
230
- }
231
-
232
- isActive(): boolean {
233
- const fsmState = this.#fsm.getState();
234
-
235
- return fsmState !== routerStates.IDLE && fsmState !== routerStates.DISPOSED;
236
- }
237
-
238
- isDisposed(): boolean {
239
- return this.#fsm.getState() === routerStates.DISPOSED;
240
- }
241
-
242
- isTransitioning(): boolean {
243
- const state = this.#fsm.getState();
244
-
245
- return (
246
- state === routerStates.TRANSITION_STARTED ||
247
- state === routerStates.LEAVE_APPROVED
248
- );
249
- }
250
-
251
- isLeaveApproved(): boolean {
252
- return this.#fsm.getState() === routerStates.LEAVE_APPROVED;
253
- }
254
-
255
- isReady(): boolean {
256
- return this.#fsm.getState() === routerStates.READY;
257
- }
258
-
259
- isStarting(): boolean {
260
- return this.#fsm.getState() === routerStates.STARTING;
261
- }
262
-
263
- getCurrentToState(): State | undefined {
264
- return this.#currentToState;
265
- }
266
-
267
- /**
268
- * Plugin-author API for subscribing to internal router events.
269
- *
270
- * @remarks
271
- *
272
- * **Duplicate-registration semantics — strict (throws).** Passing the same
273
- * callback reference twice for the same event throws
274
- * `Error("Duplicate listener for ...")` from the underlying `EventEmitter`.
275
- * This is loud-on-misuse by design: plugin code is expected to register
276
- * each callback once. The contract differs from {@link subscribe} /
277
- * {@link subscribeLeave}, which are end-user surfaces and silently accept
278
- * duplicates.
279
- */
280
- addEventListener<E extends EventName>(
281
- eventName: E,
282
- cb: Plugin[EventMethodMap[E]],
283
- ): Unsubscribe {
284
- return this.#emitter.on(
285
- eventName,
286
- cb as (...args: RouterEventMap[typeof eventName]) => void,
287
- );
288
- }
289
-
290
- /**
291
- * End-user / UI-binding API for subscribing to successful transitions.
292
- *
293
- * @remarks
294
- *
295
- * **Duplicate-registration semantics — independent.** Each call wraps
296
- * `listener` in a fresh closure and registers it as a distinct internal
297
- * slot. `router.subscribe(fn)` twice produces **two** active subscriptions;
298
- * `fn` fires twice per `TRANSITION_SUCCESS`. The returned `Unsubscribe` is
299
- * paired with its specific call — invoking it removes exactly that
300
- * registration.
301
- *
302
- * This contract differs from {@link addEventListener} (plugin API, throws
303
- * on duplicate). End-user code that wants idempotent registration must
304
- * gate itself, e.g. `if (!unsub) unsub = router.subscribe(fn);`.
305
- */
306
- subscribe(listener: SubscribeFn): Unsubscribe {
307
- return this.#emitter.on(
308
- events.TRANSITION_SUCCESS,
309
- (toState: State, fromState?: State) => {
310
- listener({ route: toState, previousRoute: fromState });
311
- },
312
- );
313
- }
314
-
315
- /**
316
- * End-user / UI-binding API for subscribing to confirmed route departures
317
- * (`LEAVE_APPROVED` phase). Async listeners block the activation phase.
318
- *
319
- * @remarks
320
- *
321
- * **Duplicate-registration semantics — independent (with internal quirk).**
322
- * Each call pushes `listener` onto the internal array; `router.subscribeLeave(fn)`
323
- * twice produces two array entries. `fn` fires **once** per leave when
324
- * iteration snapshots the array (a snapshot is taken on entry to
325
- * `awaitLeaveListeners`), but the function reference is invoked once per
326
- * array slot — so in practice the wrapper fires twice through the same
327
- * closure (no observable difference for stateless `fn`).
328
- *
329
- * The returned `Unsubscribe` removes the **first** array entry matching the
330
- * function reference (`indexOf` semantic), not the most recently added one.
331
- * Net effect of N subscribes + M unsubscribes is correct (N - M entries
332
- * remain), but the specific physical entry that survives is reverse of the
333
- * unsubscribe-call order. Irrelevant in practice — the function reference
334
- * is the same; observable behaviour is identical regardless of which
335
- * physical entry is removed.
336
- *
337
- * Contract differs from {@link addEventListener} (throws on duplicate).
338
- * For idempotent registration, gate at the call site.
339
- */
340
- subscribeLeave(listener: LeaveFn): Unsubscribe {
341
- this.#leaveListeners.push(listener);
342
-
343
- return () => {
344
- const idx = this.#leaveListeners.indexOf(listener);
345
-
346
- if (idx !== -1) {
347
- this.#leaveListeners.splice(idx, 1);
348
- }
349
- };
350
- }
351
-
352
- hasLeaveListeners(): boolean {
353
- return this.#leaveListeners.length > 0;
354
- }
355
-
356
- awaitLeaveListeners(
357
- toState: State,
358
- fromState: State | undefined,
359
- signal: AbortSignal,
360
- ): Promise<void> | undefined {
361
- if (fromState === undefined) {
362
- return undefined;
363
- }
364
-
365
- // Freeze the payload wrapper so listeners cannot mutate it (`payload.route`
366
- // is already deep-frozen via the State immutability invariant; this closes
367
- // the wrapper-mutation gap surfaced by audit `probe-05-payload-frozen`).
368
- const leaveState: LeaveState = Object.freeze({
369
- route: fromState,
370
- nextRoute: toState,
371
- signal,
372
- });
373
-
374
- let promises: Promise<void>[] | undefined;
375
- let firstSyncError: unknown;
376
-
377
- // Snapshot before iteration — a listener that reentrantly calls
378
- // `subscribeLeave(newFn)` or its own `unsubscribe()` must not affect the
379
- // current emit cycle. Symmetric with the EventEmitter snapshot invariant
380
- // (PR #666 / #659). Use `Array.from` rather than `[...array]` to keep the
381
- // intent explicit (some lint rules treat spread-of-own-array as
382
- // redundant and silently revert it).
383
- const snapshot = [...this.#leaveListeners];
384
-
385
- for (const listener of snapshot) {
386
- try {
387
- const result = listener(leaveState);
388
-
389
- if (result !== undefined && typeof result.then === "function") {
390
- promises ??= [];
391
- promises.push(result);
392
- }
393
- } catch (error: unknown) {
394
- if (firstSyncError === undefined) {
395
- firstSyncError = error;
396
- }
397
- }
398
- }
399
-
400
- if (promises === undefined) {
401
- if (firstSyncError !== undefined) {
402
- throw ensureError(firstSyncError);
403
- }
404
-
405
- return undefined;
406
- }
407
-
408
- return settleLeavePromises(promises, firstSyncError, signal);
409
- }
410
-
411
- clearAll(): void {
412
- this.#emitter.clearAll();
413
- this.#leaveListeners.length = 0;
414
- }
415
-
416
- setLimits(limits: {
417
- maxListeners: number;
418
- warnListeners: number;
419
- maxEventDepth: number;
420
- }): void {
421
- this.#emitter.setLimits(limits);
422
- }
423
-
424
- sendCancelIfPossible(fromState: State | undefined): void {
425
- const toState = this.#currentToState;
426
-
427
- if (!this.canCancel() || toState === undefined) {
428
- return;
429
- }
430
-
431
- this.sendCancel(toState, fromState);
432
- }
433
-
434
- #emitPendingError(): void {
435
- this.emitTransitionError(
436
- this.#pendingToState,
437
- this.#pendingFromState,
438
- this.#pendingError as RouterError | undefined,
439
- );
440
- }
441
-
442
- #setupFSMActions(): void {
443
- const fsm = this.#fsm;
444
-
445
- fsm.on(routerStates.STARTING, routerEvents.STARTED, () => {
446
- this.emitRouterStart();
447
- });
448
-
449
- fsm.on(routerStates.READY, routerEvents.STOP, () => {
450
- this.emitRouterStop();
451
- });
452
-
453
- // NAVIGATE and COMPLETE actions bypassed — sendNavigate/sendComplete
454
- // use fsm.forceState() + direct emit for zero-allocation hot path.
455
- const handleCancel = () => {
456
- const toState = this.#pendingToState;
457
-
458
- /* v8 ignore next -- @preserve: #pendingToState guaranteed set by sendCancel before send() */
459
- if (toState === undefined) {
460
- return;
461
- }
462
-
463
- this.emitTransitionCancel(toState, this.#pendingFromState);
464
- };
465
-
466
- fsm.on(routerStates.TRANSITION_STARTED, routerEvents.CANCEL, handleCancel);
467
- fsm.on(routerStates.LEAVE_APPROVED, routerEvents.CANCEL, handleCancel);
468
-
469
- fsm.on(routerStates.LEAVE_APPROVED, routerEvents.FAIL, () => {
470
- this.#emitPendingError();
471
- });
472
-
473
- fsm.on(routerStates.STARTING, routerEvents.FAIL, () => {
474
- this.#emitPendingError();
475
- });
476
-
477
- fsm.on(routerStates.READY, routerEvents.FAIL, () => {
478
- this.#emitPendingError();
479
- });
480
-
481
- fsm.on(routerStates.TRANSITION_STARTED, routerEvents.FAIL, () => {
482
- this.#emitPendingError();
483
- });
484
- }
485
- }
@@ -1,5 +0,0 @@
1
- // packages/core/src/namespaces/EventBusNamespace/index.ts
2
-
3
- export { EventBusNamespace } from "./EventBusNamespace";
4
-
5
- export type { EventBusOptions } from "./types";
@@ -1,11 +0,0 @@
1
- // packages/core/src/namespaces/EventBusNamespace/types.ts
2
-
3
- import type { RouterEvent, RouterPayloads, RouterState } from "../../fsm";
4
- import type { RouterEventMap } from "../../types";
5
- import type { FSM } from "@real-router/fsm";
6
- import type { EventEmitter } from "event-emitter";
7
-
8
- export interface EventBusOptions {
9
- routerFSM: FSM<RouterState, RouterEvent, null, RouterPayloads>;
10
- emitter: EventEmitter<RouterEventMap>;
11
- }