@real-router/core 0.23.0 → 0.24.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.
@@ -219,6 +219,13 @@ export class PluginsNamespace<
219
219
  return this.#plugins.has(factory);
220
220
  }
221
221
 
222
+ /**
223
+ * Disposes all registered plugins by running their teardown callbacks
224
+ * and removing event listener subscriptions.
225
+ * Unlike {@link MiddlewareNamespace.clearAll}, active disposal is required —
226
+ * plugins have an active lifecycle (event subscriptions, teardown hooks).
227
+ * Named "dispose" (not "clear") because there is active cleanup to perform.
228
+ */
222
229
  disposeAll(): void {
223
230
  for (const unsubscribe of this.#unsubscribes) {
224
231
  unsubscribe();
@@ -244,9 +251,17 @@ export class PluginsNamespace<
244
251
  const { warn, error } = computeThresholds(maxPlugins);
245
252
 
246
253
  if (totalCount >= error) {
247
- logger.error(LOGGER_CONTEXT, `${totalCount} plugins registered!`);
254
+ logger.error(
255
+ LOGGER_CONTEXT,
256
+ `${totalCount} plugins registered! ` +
257
+ `This is excessive and will impact performance. ` +
258
+ `Hard limit at ${maxPlugins}.`,
259
+ );
248
260
  } else if (totalCount >= warn) {
249
- logger.warn(LOGGER_CONTEXT, `${totalCount} plugins registered`);
261
+ logger.warn(
262
+ LOGGER_CONTEXT,
263
+ `${totalCount} plugins registered. ` + `Consider if all are necessary.`,
264
+ );
250
265
  }
251
266
  }
252
267
 
@@ -19,10 +19,10 @@ import type { DefaultDependencies, Plugin } from "@real-router/types";
19
19
  export function validateUsePluginArgs<D extends DefaultDependencies>(
20
20
  plugins: unknown[],
21
21
  ): asserts plugins is PluginFactory<D>[] {
22
- for (const plugin of plugins) {
22
+ for (const [i, plugin] of plugins.entries()) {
23
23
  if (typeof plugin !== "function") {
24
24
  throw new TypeError(
25
- `[router.usePlugin] Expected plugin factory function, got ${typeof plugin}`,
25
+ `[router.usePlugin] Expected plugin factory function at index ${i}, got ${getTypeDescription(plugin)}`,
26
26
  );
27
27
  }
28
28
  }
@@ -74,6 +74,10 @@ export function validatePluginLimit(
74
74
  const totalCount = currentCount + newCount;
75
75
 
76
76
  if (totalCount > maxPlugins) {
77
- throw new Error(`[router.usePlugin] Plugin limit exceeded (${maxPlugins})`);
77
+ throw new Error(
78
+ `[router.usePlugin] Plugin limit exceeded (${maxPlugins}). ` +
79
+ `Current: ${currentCount}, Attempting to add: ${newCount}. ` +
80
+ `This indicates an architectural problem. Consider consolidating plugins.`,
81
+ );
78
82
  }
79
83
  }
@@ -1,7 +1,7 @@
1
1
  // packages/core/src/namespaces/RouteLifecycleNamespace/RouteLifecycleNamespace.ts
2
2
 
3
3
  import { logger } from "@real-router/logger";
4
- import { isBoolean, isPromise, getTypeDescription } from "type-guards";
4
+ import { isBoolean, getTypeDescription } from "type-guards";
5
5
 
6
6
  import {
7
7
  validateHandler,
@@ -13,23 +13,19 @@ import { computeThresholds } from "../../helpers";
13
13
 
14
14
  import type { RouteLifecycleDependencies } from "./types";
15
15
  import type { Router } from "../../Router";
16
- import type { ActivationFnFactory, Limits } from "../../types";
17
- import type {
18
- ActivationFn,
19
- DefaultDependencies,
20
- State,
21
- } from "@real-router/types";
16
+ import type { GuardFnFactory, Limits } from "../../types";
17
+ import type { DefaultDependencies, GuardFn, State } from "@real-router/types";
22
18
 
23
19
  /**
24
- * Converts a boolean value to an activation function factory.
20
+ * Converts a boolean value to a guard function factory.
25
21
  * Used for the shorthand syntax where true/false is passed instead of a function.
26
22
  */
27
23
  function booleanToFactory<Dependencies extends DefaultDependencies>(
28
24
  value: boolean,
29
- ): ActivationFnFactory<Dependencies> {
30
- const activationFn: ActivationFn = () => value;
25
+ ): GuardFnFactory<Dependencies> {
26
+ const guardFn: GuardFn = () => value;
31
27
 
32
- return () => activationFn;
28
+ return () => guardFn;
33
29
  }
34
30
 
35
31
  /**
@@ -43,14 +39,14 @@ export class RouteLifecycleNamespace<
43
39
  > {
44
40
  readonly #canDeactivateFactories = new Map<
45
41
  string,
46
- ActivationFnFactory<Dependencies>
42
+ GuardFnFactory<Dependencies>
47
43
  >();
48
44
  readonly #canActivateFactories = new Map<
49
45
  string,
50
- ActivationFnFactory<Dependencies>
46
+ GuardFnFactory<Dependencies>
51
47
  >();
52
- readonly #canDeactivateFunctions = new Map<string, ActivationFn>();
53
- readonly #canActivateFunctions = new Map<string, ActivationFn>();
48
+ readonly #canDeactivateFunctions = new Map<string, GuardFn>();
49
+ readonly #canActivateFunctions = new Map<string, GuardFn>();
54
50
 
55
51
  readonly #registering = new Set<string>();
56
52
 
@@ -65,7 +61,7 @@ export class RouteLifecycleNamespace<
65
61
  static validateHandler<D extends DefaultDependencies>(
66
62
  handler: unknown,
67
63
  methodName: string,
68
- ): asserts handler is ActivationFnFactory<D> | boolean {
64
+ ): asserts handler is GuardFnFactory<D> | boolean {
69
65
  validateHandler<D>(handler, methodName);
70
66
  }
71
67
 
@@ -95,7 +91,7 @@ export class RouteLifecycleNamespace<
95
91
  */
96
92
  addCanActivate(
97
93
  name: string,
98
- handler: ActivationFnFactory<Dependencies> | boolean,
94
+ handler: GuardFnFactory<Dependencies> | boolean,
99
95
  skipValidation: boolean,
100
96
  ): void {
101
97
  if (!skipValidation) {
@@ -137,7 +133,7 @@ export class RouteLifecycleNamespace<
137
133
  */
138
134
  addCanDeactivate(
139
135
  name: string,
140
- handler: ActivationFnFactory<Dependencies> | boolean,
136
+ handler: GuardFnFactory<Dependencies> | boolean,
141
137
  skipValidation: boolean,
142
138
  ): void {
143
139
  if (!skipValidation) {
@@ -208,17 +204,11 @@ export class RouteLifecycleNamespace<
208
204
  * @returns Tuple of [canDeactivateFactories, canActivateFactories]
209
205
  */
210
206
  getFactories(): [
211
- Record<string, ActivationFnFactory<Dependencies>>,
212
- Record<string, ActivationFnFactory<Dependencies>>,
207
+ Record<string, GuardFnFactory<Dependencies>>,
208
+ Record<string, GuardFnFactory<Dependencies>>,
213
209
  ] {
214
- const deactivateRecord: Record<
215
- string,
216
- ActivationFnFactory<Dependencies>
217
- > = {};
218
- const activateRecord: Record<
219
- string,
220
- ActivationFnFactory<Dependencies>
221
- > = {};
210
+ const deactivateRecord: Record<string, GuardFnFactory<Dependencies>> = {};
211
+ const activateRecord: Record<string, GuardFnFactory<Dependencies>> = {};
222
212
 
223
213
  for (const [name, factory] of this.#canDeactivateFactories) {
224
214
  deactivateRecord[name] = factory;
@@ -236,7 +226,7 @@ export class RouteLifecycleNamespace<
236
226
  *
237
227
  * @returns Tuple of [canDeactivateFunctions, canActivateFunctions] as Maps
238
228
  */
239
- getFunctions(): [Map<string, ActivationFn>, Map<string, ActivationFn>] {
229
+ getFunctions(): [Map<string, GuardFn>, Map<string, GuardFn>] {
240
230
  return [this.#canDeactivateFunctions, this.#canActivateFunctions];
241
231
  }
242
232
 
@@ -279,9 +269,9 @@ export class RouteLifecycleNamespace<
279
269
  #registerHandler(
280
270
  type: "activate" | "deactivate",
281
271
  name: string,
282
- handler: ActivationFnFactory<Dependencies> | boolean,
283
- factories: Map<string, ActivationFnFactory<Dependencies>>,
284
- functions: Map<string, ActivationFn>,
272
+ handler: GuardFnFactory<Dependencies> | boolean,
273
+ factories: Map<string, GuardFnFactory<Dependencies>>,
274
+ functions: Map<string, GuardFn>,
285
275
  methodName: string,
286
276
  isOverwrite: boolean,
287
277
  ): void {
@@ -328,7 +318,7 @@ export class RouteLifecycleNamespace<
328
318
  }
329
319
 
330
320
  #checkGuardSync(
331
- functions: Map<string, ActivationFn>,
321
+ functions: Map<string, GuardFn>,
332
322
  name: string,
333
323
  toState: State,
334
324
  fromState: State | undefined,
@@ -347,17 +337,12 @@ export class RouteLifecycleNamespace<
347
337
  return result;
348
338
  }
349
339
 
350
- if (isPromise(result)) {
351
- logger.warn(
352
- `router.${methodName}`,
353
- `Guard for "${name}" returned a Promise. Sync check cannot resolve async guards — returning false.`,
354
- );
355
-
356
- return false;
357
- }
340
+ logger.warn(
341
+ `router.${methodName}`,
342
+ `Guard for "${name}" returned a Promise. Sync check cannot resolve async guards — returning false.`,
343
+ );
358
344
 
359
- // Guard returned void/State — permissive default
360
- return true;
345
+ return false;
361
346
  } catch {
362
347
  return false;
363
348
  }
@@ -9,7 +9,7 @@ import { isBoolean, getTypeDescription } from "type-guards";
9
9
 
10
10
  import { DEFAULT_LIMITS } from "../../constants";
11
11
 
12
- import type { ActivationFnFactory } from "../../types";
12
+ import type { GuardFnFactory } from "../../types";
13
13
  import type { DefaultDependencies } from "@real-router/types";
14
14
 
15
15
  /**
@@ -18,7 +18,7 @@ import type { DefaultDependencies } from "@real-router/types";
18
18
  export function validateHandler<D extends DefaultDependencies>(
19
19
  handler: unknown,
20
20
  methodName: string,
21
- ): asserts handler is ActivationFnFactory<D> | boolean {
21
+ ): asserts handler is GuardFnFactory<D> | boolean {
22
22
  if (!isBoolean(handler) && typeof handler !== "function") {
23
23
  throw new TypeError(
24
24
  `[router.${methodName}] Handler must be a boolean or factory function, ` +
@@ -39,8 +39,8 @@ import { getTransitionPath } from "../../transitionPath";
39
39
 
40
40
  import type { RouteConfig, RoutesDependencies } from "./types";
41
41
  import type {
42
- ActivationFnFactory,
43
42
  BuildStateResultWithSegments,
43
+ GuardFnFactory,
44
44
  Route,
45
45
  RouteConfigUpdate,
46
46
  } from "../../types";
@@ -85,14 +85,14 @@ export class RoutesNamespace<
85
85
  // Key: route name, Value: canActivate factory
86
86
  readonly #pendingCanActivate = new Map<
87
87
  string,
88
- ActivationFnFactory<Dependencies>
88
+ GuardFnFactory<Dependencies>
89
89
  >();
90
90
 
91
91
  // Pending canDeactivate handlers that need to be registered after router is set
92
92
  // Key: route name, Value: canDeactivate factory
93
93
  readonly #pendingCanDeactivate = new Map<
94
94
  string,
95
- ActivationFnFactory<Dependencies>
95
+ GuardFnFactory<Dependencies>
96
96
  >();
97
97
 
98
98
  #rootPath = "";
@@ -1,6 +1,6 @@
1
1
  // packages/core/src/namespaces/RoutesNamespace/types.ts
2
2
 
3
- import type { ActivationFnFactory } from "../../types";
3
+ import type { GuardFnFactory } from "../../types";
4
4
  import type {
5
5
  DefaultDependencies,
6
6
  ForwardToCallback,
@@ -22,13 +22,13 @@ export interface RoutesDependencies<
22
22
  /** Register canActivate handler for a route */
23
23
  addActivateGuard: (
24
24
  name: string,
25
- handler: ActivationFnFactory<Dependencies>,
25
+ handler: GuardFnFactory<Dependencies>,
26
26
  ) => void;
27
27
 
28
28
  /** Register canDeactivate handler for a route */
29
29
  addDeactivateGuard: (
30
30
  name: string,
31
- handler: ActivationFnFactory<Dependencies>,
31
+ handler: GuardFnFactory<Dependencies>,
32
32
  ) => void;
33
33
 
34
34
  /** Create state object */
package/src/typeGuards.ts CHANGED
@@ -11,7 +11,6 @@ export {
11
11
  isState,
12
12
  isParams,
13
13
  isNavigationOptions,
14
- isPromise,
15
14
  isBoolean,
16
15
  validateRouteName,
17
16
  } from "type-guards";
package/src/types.ts CHANGED
@@ -14,6 +14,7 @@ import type {
14
14
  DefaultDependencies,
15
15
  EventsKeys,
16
16
  ForwardToCallback,
17
+ GuardFn,
17
18
  LimitsConfig,
18
19
  Middleware,
19
20
  NavigationOptions,
@@ -81,9 +82,9 @@ export interface Route<
81
82
  /** URL path pattern for this route. */
82
83
  path: string;
83
84
  /** Factory function that returns a guard for route activation. */
84
- canActivate?: ActivationFnFactory<Dependencies>;
85
+ canActivate?: GuardFnFactory<Dependencies>;
85
86
  /** Factory function that returns a guard for route deactivation. */
86
- canDeactivate?: ActivationFnFactory<Dependencies>;
87
+ canDeactivate?: GuardFnFactory<Dependencies>;
87
88
  /**
88
89
  * Redirects navigation to another route.
89
90
  *
@@ -155,9 +156,9 @@ export interface RouteConfigUpdate<
155
156
  /** Set to null to remove encoder */
156
157
  encodeParams?: ((params: Params) => Params) | null;
157
158
  /** Set to null to remove canActivate */
158
- canActivate?: ActivationFnFactory<Dependencies> | null;
159
+ canActivate?: GuardFnFactory<Dependencies> | null;
159
160
  /** Set to null to remove canDeactivate */
160
- canDeactivate?: ActivationFnFactory<Dependencies> | null;
161
+ canDeactivate?: GuardFnFactory<Dependencies> | null;
161
162
  }
162
163
 
163
164
  /**
@@ -171,6 +172,17 @@ export type ActivationFnFactory<
171
172
  getDependency: <K extends keyof Dependencies>(key: K) => Dependencies[K],
172
173
  ) => ActivationFn;
173
174
 
175
+ /**
176
+ * Factory function for creating guards.
177
+ * Receives the router instance and a dependency getter.
178
+ */
179
+ export type GuardFnFactory<
180
+ Dependencies extends DefaultDependencies = DefaultDependencies,
181
+ > = (
182
+ router: Router<Dependencies>,
183
+ getDependency: <K extends keyof Dependencies>(key: K) => Dependencies[K],
184
+ ) => GuardFn;
185
+
174
186
  /**
175
187
  * Factory function for creating middleware.
176
188
  * Receives the router instance and a dependency getter.