@comity/router-path-to-regexp 0.9.0 → 0.9.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/README.md CHANGED
@@ -29,7 +29,6 @@ This package does NOT:
29
29
 
30
30
  - `PathRouter`
31
31
 
32
-
33
32
  No exhaustive reference; see docs for constraints.
34
33
  ---
35
34
 
@@ -50,7 +49,3 @@ No exhaustive reference; see docs for constraints.
50
49
  ## Status
51
50
 
52
51
  Stable
53
-
54
- _Review Completed: 2026-07-25_
55
- _Reviewer: Hobiri MAGI (DeepSeek v4 Pro)_
56
- _Compliance Score: 99.5% (Green)_
@@ -4,6 +4,27 @@ exports.PathRouter = void 0;
4
4
  const path_to_regexp_1 = require("path-to-regexp");
5
5
  /**
6
6
  * PathRouter is a router implementation that matches incoming HTTP requests based on their URL path and HTTP method. It supports static paths and can be extended to support dynamic path parameters in the future.
7
+ *
8
+ * @typeParam State - Route state type carried by the registered routes.
9
+ * @typeParam Services - Application service map carried by the registered routes.
10
+ *
11
+ * @remarks
12
+ * The constructor is generic in `State` and `Services` (with the same defaults
13
+ * as `Route`) so a service-typed route — `Route<{}, AppServices>` — is
14
+ * *checked* at registration instead of being rejected against `Route[]`'s
15
+ * erased defaults. All routes registered with one router must share the same
16
+ * state and service map; routes whose maps differ belong in separate routers
17
+ * (routers themselves are heterogeneous inside `Router[]`).
18
+ *
19
+ * The service typing deliberately stops at this boundary: matching returns
20
+ * the non-generic `RouteMatch`, whose `route` slot is `Route` with defaults.
21
+ * This is an intentional erasure, not an oversight — `HttpContext` is
22
+ * invariant in `Services` (its `resolve` return type is covariant in
23
+ * `Services` and its key is contravariant), so no erased route slot could
24
+ * accept a typed route soundly, and the request context handed to a handler
25
+ * at dispatch time is the kernel's real, fully-registered container at
26
+ * runtime regardless of the static type. The erase happens once, inside this
27
+ * constructor, immediately after the routes have been type-checked.
7
28
  */
8
29
  class PathRouter {
9
30
  #routes;
@@ -14,6 +35,9 @@ class PathRouter {
14
35
  this.#routes = routes
15
36
  .filter((r) => r.path)
16
37
  .map((r) => ({
38
+ // Local, justified erase: the routes were just checked against
39
+ // Route<State, Services>; storage and dispatch are the router's
40
+ // intentional erased boundary (see class JSDoc).
17
41
  route: r,
18
42
  match: (0, path_to_regexp_1.match)(r.path, { decode: decodeURIComponent }),
19
43
  }));
@@ -1,6 +1,27 @@
1
1
  import { match } from "path-to-regexp";
2
2
  /**
3
3
  * PathRouter is a router implementation that matches incoming HTTP requests based on their URL path and HTTP method. It supports static paths and can be extended to support dynamic path parameters in the future.
4
+ *
5
+ * @typeParam State - Route state type carried by the registered routes.
6
+ * @typeParam Services - Application service map carried by the registered routes.
7
+ *
8
+ * @remarks
9
+ * The constructor is generic in `State` and `Services` (with the same defaults
10
+ * as `Route`) so a service-typed route — `Route<{}, AppServices>` — is
11
+ * *checked* at registration instead of being rejected against `Route[]`'s
12
+ * erased defaults. All routes registered with one router must share the same
13
+ * state and service map; routes whose maps differ belong in separate routers
14
+ * (routers themselves are heterogeneous inside `Router[]`).
15
+ *
16
+ * The service typing deliberately stops at this boundary: matching returns
17
+ * the non-generic `RouteMatch`, whose `route` slot is `Route` with defaults.
18
+ * This is an intentional erasure, not an oversight — `HttpContext` is
19
+ * invariant in `Services` (its `resolve` return type is covariant in
20
+ * `Services` and its key is contravariant), so no erased route slot could
21
+ * accept a typed route soundly, and the request context handed to a handler
22
+ * at dispatch time is the kernel's real, fully-registered container at
23
+ * runtime regardless of the static type. The erase happens once, inside this
24
+ * constructor, immediately after the routes have been type-checked.
4
25
  */
5
26
  export class PathRouter {
6
27
  #routes;
@@ -11,6 +32,9 @@ export class PathRouter {
11
32
  this.#routes = routes
12
33
  .filter((r) => r.path)
13
34
  .map((r) => ({
35
+ // Local, justified erase: the routes were just checked against
36
+ // Route<State, Services>; storage and dispatch are the router's
37
+ // intentional erased boundary (see class JSDoc).
14
38
  route: r,
15
39
  match: match(r.path, { decode: decodeURIComponent }),
16
40
  }));
@@ -1,13 +1,34 @@
1
1
  import type { Route, RouteMatch, Router, RouteResolutionContext } from "@comity/router";
2
2
  /**
3
3
  * PathRouter is a router implementation that matches incoming HTTP requests based on their URL path and HTTP method. It supports static paths and can be extended to support dynamic path parameters in the future.
4
+ *
5
+ * @typeParam State - Route state type carried by the registered routes.
6
+ * @typeParam Services - Application service map carried by the registered routes.
7
+ *
8
+ * @remarks
9
+ * The constructor is generic in `State` and `Services` (with the same defaults
10
+ * as `Route`) so a service-typed route — `Route<{}, AppServices>` — is
11
+ * *checked* at registration instead of being rejected against `Route[]`'s
12
+ * erased defaults. All routes registered with one router must share the same
13
+ * state and service map; routes whose maps differ belong in separate routers
14
+ * (routers themselves are heterogeneous inside `Router[]`).
15
+ *
16
+ * The service typing deliberately stops at this boundary: matching returns
17
+ * the non-generic `RouteMatch`, whose `route` slot is `Route` with defaults.
18
+ * This is an intentional erasure, not an oversight — `HttpContext` is
19
+ * invariant in `Services` (its `resolve` return type is covariant in
20
+ * `Services` and its key is contravariant), so no erased route slot could
21
+ * accept a typed route soundly, and the request context handed to a handler
22
+ * at dispatch time is the kernel's real, fully-registered container at
23
+ * runtime regardless of the static type. The erase happens once, inside this
24
+ * constructor, immediately after the routes have been type-checked.
4
25
  */
5
- export declare class PathRouter implements Router {
26
+ export declare class PathRouter<State = Record<string, unknown>, Services extends Record<keyof Services, unknown> = Record<string, unknown>> implements Router {
6
27
  #private;
7
28
  /**
8
29
  * @param routes - An array of Route objects that define the routing rules for this router.
9
30
  */
10
- constructor(routes: Route[]);
31
+ constructor(routes: Route<State, Services>[]);
11
32
  /**
12
33
  * Matches an incoming HTTP request against the defined routes.
13
34
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@comity/router-path-to-regexp",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
4
4
  "description": "path-to-regexp adapter for Comity applications.",
5
5
  "type": "module",
6
6
  "private": false,
@@ -58,13 +58,13 @@
58
58
  "sideEffects": false,
59
59
  "peerDependencies": {
60
60
  "path-to-regexp": "^8.0.0",
61
- "@comity/router": "0.9.0"
61
+ "@comity/router": "0.9.1"
62
62
  },
63
63
  "dependencies": {
64
- "@comity/router": "0.9.0"
64
+ "@comity/router": "0.9.1"
65
65
  },
66
66
  "devDependencies": {
67
- "@types/node": "^24.13.4",
67
+ "@types/node": "^24.19.1",
68
68
  "path-to-regexp": "^8.4.2",
69
69
  "typescript": "^5.9.3"
70
70
  },