@real-router/validation-plugin 0.5.1 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@real-router/validation-plugin",
3
- "version": "0.5.1",
3
+ "version": "0.6.1",
4
4
  "type": "commonjs",
5
5
  "description": "Route validation plugin with type guards and parameter validation",
6
6
  "main": "./dist/cjs/index.js",
@@ -44,23 +44,23 @@
44
44
  },
45
45
  "sideEffects": false,
46
46
  "dependencies": {
47
- "@real-router/core": "^0.48.0",
47
+ "@real-router/core": "^0.50.0",
48
48
  "@real-router/logger": "^0.3.0"
49
49
  },
50
50
  "peerDependencies": {
51
- "@real-router/core": "^0.48.0"
51
+ "@real-router/core": "^0.50.0"
52
52
  },
53
53
  "devDependencies": {
54
54
  "route-tree": "^0.3.4",
55
55
  "type-guards": "^0.4.8"
56
56
  },
57
57
  "scripts": {
58
- "build": "tsdown --config-loader unrun",
59
58
  "test": "vitest",
60
59
  "type-check": "tsc --noEmit",
61
60
  "lint": "eslint --cache --ext .ts src/ tests/ --fix --max-warnings 0",
62
61
  "lint:package": "publint",
63
62
  "lint:types": "attw --pack .",
64
- "test:properties": "vitest run --config vitest.config.properties.mts"
63
+ "test:properties": "vitest run --config vitest.config.properties.mts",
64
+ "bundle": "tsdown --config-loader unrun"
65
65
  }
66
66
  }
@@ -57,6 +57,7 @@ import {
57
57
  validateForwardToTargetsStore,
58
58
  validateDependenciesStructure,
59
59
  validateLimitsConsistency,
60
+ validateResolvedDefaultRoute,
60
61
  } from "./validators/retrospective";
61
62
  import {
62
63
  validateBuildPathArgs,
@@ -186,6 +187,7 @@ function buildValidatorObject(ctx: RouterInternals): RouterValidator {
186
187
  validateLimits(limits, "validate");
187
188
  },
188
189
  validateOptions,
190
+ validateResolvedDefaultRoute,
189
191
  },
190
192
  dependencies: {
191
193
  validateDependencyName,
@@ -315,6 +317,10 @@ export function validationPlugin(): PluginFactory {
315
317
  options as unknown,
316
318
  "constructor (retrospective)",
317
319
  );
320
+
321
+ if (typeof options.defaultRoute === "string") {
322
+ validateResolvedDefaultRoute(options.defaultRoute, store);
323
+ }
318
324
  } catch (error) {
319
325
  ctx.validator = null;
320
326
 
@@ -1,5 +1,6 @@
1
1
  // packages/validation-plugin/src/validators/options.ts
2
2
 
3
+ import { logger } from "@real-router/logger";
3
4
  import { isObjKey } from "type-guards";
4
5
 
5
6
  const VALID_OPTION_VALUES = {
@@ -98,6 +99,19 @@ export function validateLimits(
98
99
 
99
100
  validateLimitValue(key as keyof LimitsConfig, value, methodName);
100
101
  }
102
+
103
+ const { warnListeners, maxListeners } = limits as Partial<LimitsConfig>;
104
+
105
+ if (
106
+ typeof warnListeners === "number" &&
107
+ typeof maxListeners === "number" &&
108
+ maxListeners > 0 &&
109
+ warnListeners > maxListeners
110
+ ) {
111
+ throw new RangeError(
112
+ `[router.${methodName}] "limits.warnListeners" (${warnListeners}) must not exceed "limits.maxListeners" (${maxListeners}) — the warning channel would be unreachable`,
113
+ );
114
+ }
101
115
  }
102
116
 
103
117
  function validateStringEnum(
@@ -241,6 +255,16 @@ function validateLoggerOption(loggerOpt: unknown, methodName: string): void {
241
255
  `[router.${methodName}] Invalid "logger.callbackIgnoresLevel": expected boolean`,
242
256
  );
243
257
  }
258
+
259
+ if (
260
+ loggerOptions.callbackIgnoresLevel === true &&
261
+ loggerOptions.callback === undefined
262
+ ) {
263
+ logger.error(
264
+ `router.${methodName}`,
265
+ `"logger.callbackIgnoresLevel: true" has no effect without "logger.callback" — the option is ignored`,
266
+ );
267
+ }
244
268
  }
245
269
 
246
270
  export function validateOptions(options: unknown, methodName: string): void {
@@ -538,3 +538,36 @@ export function validateLimitsConsistency(
538
538
  checkRouteCountLimit(store, configuredLimits);
539
539
  checkDepCountLimit(deps, configuredLimits);
540
540
  }
541
+
542
+ // =============================================================================
543
+ // 7. validateResolvedDefaultRoute
544
+ // =============================================================================
545
+
546
+ /**
547
+ * Validates that a resolved defaultRoute name points to a route that exists
548
+ * in the tree. Called at two places:
549
+ *
550
+ * 1. At plugin registration (retrospective) — with the static string value
551
+ * of options.defaultRoute, if any.
552
+ * 2. At runtime inside resolveDefault() — with the return value of a
553
+ * DefaultRouteCallback, on every navigateToDefault() / start() fallback.
554
+ *
555
+ * No-op for empty string (means "no default configured" — handled upstream by
556
+ * NavigationNamespace.navigateToDefault).
557
+ */
558
+ export function validateResolvedDefaultRoute(
559
+ routeName: unknown,
560
+ store: unknown,
561
+ ): void {
562
+ if (typeof routeName !== "string" || !routeName) {
563
+ return;
564
+ }
565
+
566
+ const routesStore = assertRoutesStore(store, "validateResolvedDefaultRoute");
567
+
568
+ if (!routeExistsInTree(routesStore.tree, routeName)) {
569
+ throw new Error(
570
+ `[validation-plugin] defaultRoute resolved to non-existent route: "${routeName}"`,
571
+ );
572
+ }
573
+ }