@koa/router 15.5.0 → 15.6.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.
package/README.md CHANGED
@@ -40,6 +40,7 @@
40
40
  - ✅ **405 Method Not Allowed** - Automatic method validation
41
41
  - ✅ **501 Not Implemented** - Proper HTTP status codes
42
42
  - ✅ **Async/Await** - Full promise-based middleware support
43
+ - ✅ **Clean ctx.params** - Only URL parameters you define are present in `ctx.params`; no internal routing keys leak out
43
44
 
44
45
  ## Installation
45
46
 
@@ -457,7 +458,26 @@ router.get('/users', (ctx) => {
457
458
  // Responds to /api/v1/users, /api/v2/users, etc.
458
459
  ```
459
460
 
460
- **Note:** Middleware now correctly executes when the prefix contains parameters.
461
+ **Middleware on a parameterized prefix:**
462
+
463
+ When `router.use()` is attached to a router whose prefix contains URL parameters, `ctx.params` inside the middleware will contain **only** the parameters defined by the prefix — no internal routing keys are ever added.
464
+
465
+ ```javascript
466
+ const router = new Router({ prefix: '/:tenantId' });
467
+
468
+ // Auth / authz middleware — receives only the defined prefix param
469
+ router.use(async (ctx, next) => {
470
+ console.log(ctx.params); // => { tenantId: 'acme' } (no extraneous keys)
471
+ await next();
472
+ });
473
+
474
+ router.get('/users', (ctx) => {
475
+ console.log(ctx.params); // => { tenantId: 'acme' }
476
+ ctx.body = ctx.params;
477
+ });
478
+ ```
479
+
480
+ This is particularly useful when running **strict parameter validation** inside a `router.use()` middleware — the shape of `ctx.params` is predictable and contains only what you defined.
461
481
 
462
482
  ### URL Parameters
463
483
 
@@ -565,6 +585,8 @@ router.use('/nested', nestedRouter.routes());
565
585
 
566
586
  **Note:** Middleware path boundaries are correctly enforced. Middleware scoped to `/api` will only run for routes matching `/api/*`, not for unrelated routes.
567
587
 
588
+ **Note:** When `router.use()` is used on a router with a parameterized prefix (e.g. `prefix: '/:id'`), `ctx.params` inside that middleware will contain **only** the parameters defined by the prefix and route path — no internal wildcard keys are exposed. See [Router Prefixes](#router-prefixes) for a full example.
589
+
568
590
  ### router.prefix()
569
591
 
570
592
  Set the path prefix for a Router instance after initialization.
package/dist/index.d.mts CHANGED
@@ -821,6 +821,12 @@ type LayerOptions = {
821
821
  * Ignore captures in route matching
822
822
  */
823
823
  ignoreCaptures?: boolean;
824
+ /**
825
+ * Ignore a generated wildcard route parameter when populating ctx.params
826
+ *
827
+ * @internal
828
+ */
829
+ ignoreWildcardParameter?: string | number;
824
830
  /**
825
831
  * Treat path as a regular expression
826
832
  */
package/dist/index.d.ts CHANGED
@@ -821,6 +821,12 @@ type LayerOptions = {
821
821
  * Ignore captures in route matching
822
822
  */
823
823
  ignoreCaptures?: boolean;
824
+ /**
825
+ * Ignore a generated wildcard route parameter when populating ctx.params
826
+ *
827
+ * @internal
828
+ */
829
+ ignoreWildcardParameter?: string | number;
824
830
  /**
825
831
  * Treat path as a regular expression
826
832
  */
package/dist/index.js CHANGED
@@ -310,6 +310,9 @@ var Layer = class {
310
310
  const parameterDefinition = this.paramNames[captureIndex];
311
311
  if (parameterDefinition && capturedValue && capturedValue.length > 0) {
312
312
  const parameterName = parameterDefinition.name;
313
+ if (this.opts.ignoreWildcardParameter === parameterName && parameterDefinition.type === "wildcard") {
314
+ continue;
315
+ }
313
316
  parameterValues[parameterName] = safeDecodeURIComponent(capturedValue);
314
317
  }
315
318
  }
@@ -942,9 +945,11 @@ var RouterImplementation = class {
942
945
  finalPath = middlewarePath;
943
946
  usePathToRegexp = false;
944
947
  }
948
+ const ignoreWildcardParameter = effectiveExplicitPath === "" && middlewarePath === "{/*rest}" ? "rest" : void 0;
945
949
  this.register(finalPath, [], middleware, {
946
950
  end: isRootPath,
947
951
  ignoreCaptures: !effectiveHasExplicitPath && !prefixHasParameters,
952
+ ignoreWildcardParameter,
948
953
  pathAsRegExp: usePathToRegexp
949
954
  });
950
955
  }
@@ -1397,6 +1402,7 @@ var RouterImplementation = class {
1397
1402
  strict: options.strict || false,
1398
1403
  prefix: options.prefix || "",
1399
1404
  ignoreCaptures: options.ignoreCaptures,
1405
+ ignoreWildcardParameter: options.ignoreWildcardParameter,
1400
1406
  pathAsRegExp: options.pathAsRegExp
1401
1407
  });
1402
1408
  }
package/dist/index.mjs CHANGED
@@ -271,6 +271,9 @@ var Layer = class {
271
271
  const parameterDefinition = this.paramNames[captureIndex];
272
272
  if (parameterDefinition && capturedValue && capturedValue.length > 0) {
273
273
  const parameterName = parameterDefinition.name;
274
+ if (this.opts.ignoreWildcardParameter === parameterName && parameterDefinition.type === "wildcard") {
275
+ continue;
276
+ }
274
277
  parameterValues[parameterName] = safeDecodeURIComponent(capturedValue);
275
278
  }
276
279
  }
@@ -903,9 +906,11 @@ var RouterImplementation = class {
903
906
  finalPath = middlewarePath;
904
907
  usePathToRegexp = false;
905
908
  }
909
+ const ignoreWildcardParameter = effectiveExplicitPath === "" && middlewarePath === "{/*rest}" ? "rest" : void 0;
906
910
  this.register(finalPath, [], middleware, {
907
911
  end: isRootPath,
908
912
  ignoreCaptures: !effectiveHasExplicitPath && !prefixHasParameters,
913
+ ignoreWildcardParameter,
909
914
  pathAsRegExp: usePathToRegexp
910
915
  });
911
916
  }
@@ -1358,6 +1363,7 @@ var RouterImplementation = class {
1358
1363
  strict: options.strict || false,
1359
1364
  prefix: options.prefix || "",
1360
1365
  ignoreCaptures: options.ignoreCaptures,
1366
+ ignoreWildcardParameter: options.ignoreWildcardParameter,
1361
1367
  pathAsRegExp: options.pathAsRegExp
1362
1368
  });
1363
1369
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@koa/router",
3
3
  "description": "Router middleware for Koa.",
4
- "version": "15.5.0",
4
+ "version": "15.6.0",
5
5
  "author": "Koa.js",
6
6
  "bugs": {
7
7
  "url": "https://github.com/koajs/router/issues",
@@ -31,27 +31,27 @@
31
31
  "path-to-regexp": "^8.4.2"
32
32
  },
33
33
  "devDependencies": {
34
- "@commitlint/cli": "^20.5.3",
35
- "@commitlint/config-conventional": "^20.5.3",
34
+ "@commitlint/cli": "^21.0.2",
35
+ "@commitlint/config-conventional": "^21.0.2",
36
36
  "@eslint/js": "^10.0.1",
37
37
  "@koa/bodyparser": "^6.1.0",
38
38
  "@types/debug": "^4.1.13",
39
39
  "@types/jsonwebtoken": "^9.0.10",
40
- "@types/koa": "^3.0.2",
41
- "@types/node": "^25.6.0",
40
+ "@types/koa": "^3.0.3",
41
+ "@types/node": "^25.9.1",
42
42
  "@types/supertest": "^7.2.0",
43
- "@typescript-eslint/eslint-plugin": "^8.59.1",
44
- "@typescript-eslint/parser": "^8.59.1",
43
+ "@typescript-eslint/eslint-plugin": "^8.60.0",
44
+ "@typescript-eslint/parser": "^8.60.0",
45
45
  "c8": "^11.0.0",
46
46
  "chalk": "^5.6.2",
47
- "eslint": "^10.3.0",
47
+ "eslint": "^10.4.1",
48
48
  "eslint-plugin-unicorn": "^64.0.0",
49
49
  "husky": "^9.1.7",
50
- "joi": "^18.1.2",
50
+ "joi": "^18.2.1",
51
51
  "jsonwebtoken": "^9.0.3",
52
- "koa": "^3.2.0",
53
- "lint-staged": "^16.4.0",
54
- "np": "^11.2.0",
52
+ "koa": "^3.2.1",
53
+ "lint-staged": "^17.0.6",
54
+ "np": "^11.2.1",
55
55
  "prettier": "^3.8.3",
56
56
  "rimraf": "^6.1.3",
57
57
  "supertest": "^7.2.2",