sentri 1.0.5 → 1.1.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 (48) hide show
  1. package/README.md +182 -46
  2. package/dist/client.d.ts +43 -82
  3. package/dist/client.d.ts.map +1 -1
  4. package/dist/client.js +0 -7
  5. package/dist/client.js.map +1 -1
  6. package/dist/errors/AuthError.d.ts +9 -8
  7. package/dist/errors/AuthError.d.ts.map +1 -1
  8. package/dist/errors/AuthError.js +9 -8
  9. package/dist/errors/AuthError.js.map +1 -1
  10. package/dist/index.d.ts +1 -1
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js.map +1 -1
  13. package/dist/libs/config.d.ts +45 -1
  14. package/dist/libs/config.d.ts.map +1 -1
  15. package/dist/libs/config.js +40 -2
  16. package/dist/libs/config.js.map +1 -1
  17. package/dist/libs/hash.d.ts +14 -0
  18. package/dist/libs/hash.d.ts.map +1 -1
  19. package/dist/libs/hash.js +14 -0
  20. package/dist/libs/hash.js.map +1 -1
  21. package/dist/libs/token.d.ts +40 -2
  22. package/dist/libs/token.d.ts.map +1 -1
  23. package/dist/libs/token.js +64 -0
  24. package/dist/libs/token.js.map +1 -1
  25. package/dist/middleware/authorize.d.ts +15 -0
  26. package/dist/middleware/authorize.d.ts.map +1 -1
  27. package/dist/middleware/authorize.js +15 -0
  28. package/dist/middleware/authorize.js.map +1 -1
  29. package/dist/middleware/protect.d.ts +27 -0
  30. package/dist/middleware/protect.d.ts.map +1 -1
  31. package/dist/middleware/protect.js +37 -2
  32. package/dist/middleware/protect.js.map +1 -1
  33. package/dist/middleware/router.d.ts +13 -6
  34. package/dist/middleware/router.d.ts.map +1 -1
  35. package/dist/middleware/router.js +49 -15
  36. package/dist/middleware/router.js.map +1 -1
  37. package/dist/services/auth.d.ts +77 -0
  38. package/dist/services/auth.d.ts.map +1 -1
  39. package/dist/services/auth.js +81 -2
  40. package/dist/services/auth.js.map +1 -1
  41. package/dist/types/auth.d.ts +189 -3
  42. package/dist/types/auth.d.ts.map +1 -1
  43. package/dist/types/auth.js.map +1 -1
  44. package/package.json +15 -4
  45. package/templates/drizzle/adapter.ts +3 -9
  46. package/templates/drizzle/auth.ts +20 -0
  47. package/templates/prisma/adapter.ts +3 -9
  48. package/templates/prisma/auth.ts +20 -0
@@ -1,4 +1,9 @@
1
1
  import type { AuthAdapter, AuthConfig } from '../types/auth.js';
2
+ /**
3
+ * Fully-resolved configuration with all optional fields filled in by
4
+ * their defaults. Produced by {@link resolveConfig} and used internally
5
+ * wherever the library needs guaranteed values.
6
+ */
2
7
  export interface ResolvedConfig {
3
8
  secret: string;
4
9
  accessExpiresIn: string | number;
@@ -9,10 +14,49 @@ export interface ResolvedConfig {
9
14
  adapter: AuthAdapter;
10
15
  }
11
16
  /**
12
- * Validates config at startup so misconfiguration is caught immediately,
17
+ * Validate configuration at startup so misconfiguration is caught immediately,
13
18
  * not at the first login attempt.
19
+ *
20
+ * Throws {@link AuthError} with code `CONFIGURATION_ERROR` for any of:
21
+ * - `secret` missing or shorter than 32 characters
22
+ * - `saltRounds` outside the range 10–31
23
+ * - `validRoles` is empty or missing
24
+ * - `adapter` is missing
25
+ *
26
+ * @param config - The raw config passed to {@link createAuth}.
27
+ * @throws {AuthError} With code `CONFIGURATION_ERROR` on any invalid field.
14
28
  */
15
29
  export declare function validateConfig(config: AuthConfig): void;
30
+ /**
31
+ * Merge a partial {@link AuthConfig} with library defaults and return a
32
+ * fully-resolved configuration object.
33
+ *
34
+ * Does **not** validate the config — call {@link validateConfig} first.
35
+ *
36
+ * Defaults applied:
37
+ * - `accessExpiresIn` → `'15m'`
38
+ * - `refreshExpiresIn` → `'7d'`
39
+ * - `algorithm` → `'HS256'`
40
+ * - `saltRounds` → `12`
41
+ *
42
+ * @param partial - The raw config passed to {@link createAuth}.
43
+ * @returns A {@link ResolvedConfig} with every field guaranteed to be present.
44
+ */
16
45
  export declare function resolveConfig(partial: AuthConfig): ResolvedConfig;
46
+ /**
47
+ * Convert a duration string or a number of seconds into milliseconds.
48
+ *
49
+ * Supported unit suffixes: `s` (seconds), `m` (minutes), `h` (hours),
50
+ * `d` (days), `w` (weeks). Numeric inputs are treated as seconds.
51
+ *
52
+ * @example
53
+ * parseExpiry('15m') // 900_000
54
+ * parseExpiry('7d') // 604_800_000
55
+ * parseExpiry(60) // 60_000
56
+ *
57
+ * @param expiresIn - A duration string (e.g. `'15m'`, `'7d'`) or a number of seconds.
58
+ * @returns The equivalent duration in milliseconds.
59
+ * @throws {Error} If the string format is unrecognised.
60
+ */
17
61
  export declare function parseExpiry(expiresIn: string | number): number;
18
62
  //# sourceMappingURL=config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/libs/config.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEhE,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,GAAG,MAAM,CAAC;IACjC,gBAAgB,EAAE,MAAM,GAAG,MAAM,CAAC;IAClC,SAAS,EAAE,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9B,OAAO,EAAE,WAAW,CAAC;CACtB;AAMD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CA0BvD;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,UAAU,GAAG,cAAc,CAUjE;AAGD,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAkB9D"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/libs/config.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEhE;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,GAAG,MAAM,CAAC;IACjC,gBAAgB,EAAE,MAAM,GAAG,MAAM,CAAC;IAClC,SAAS,EAAE,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9B,OAAO,EAAE,WAAW,CAAC;CACtB;AAMD;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CA0BvD;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,UAAU,GAAG,cAAc,CAUjE;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAkB9D"}
@@ -3,8 +3,17 @@ const MIN_SECRET_LENGTH = 32;
3
3
  const MIN_SALT_ROUNDS = 10;
4
4
  const MAX_SALT_ROUNDS = 31;
5
5
  /**
6
- * Validates config at startup so misconfiguration is caught immediately,
6
+ * Validate configuration at startup so misconfiguration is caught immediately,
7
7
  * not at the first login attempt.
8
+ *
9
+ * Throws {@link AuthError} with code `CONFIGURATION_ERROR` for any of:
10
+ * - `secret` missing or shorter than 32 characters
11
+ * - `saltRounds` outside the range 10–31
12
+ * - `validRoles` is empty or missing
13
+ * - `adapter` is missing
14
+ *
15
+ * @param config - The raw config passed to {@link createAuth}.
16
+ * @throws {AuthError} With code `CONFIGURATION_ERROR` on any invalid field.
8
17
  */
9
18
  export function validateConfig(config) {
10
19
  if (!config.secret || config.secret.trim().length === 0) {
@@ -24,6 +33,21 @@ export function validateConfig(config) {
24
33
  throw new AuthError('CONFIGURATION_ERROR', 'adapter is required');
25
34
  }
26
35
  }
36
+ /**
37
+ * Merge a partial {@link AuthConfig} with library defaults and return a
38
+ * fully-resolved configuration object.
39
+ *
40
+ * Does **not** validate the config — call {@link validateConfig} first.
41
+ *
42
+ * Defaults applied:
43
+ * - `accessExpiresIn` → `'15m'`
44
+ * - `refreshExpiresIn` → `'7d'`
45
+ * - `algorithm` → `'HS256'`
46
+ * - `saltRounds` → `12`
47
+ *
48
+ * @param partial - The raw config passed to {@link createAuth}.
49
+ * @returns A {@link ResolvedConfig} with every field guaranteed to be present.
50
+ */
27
51
  export function resolveConfig(partial) {
28
52
  return {
29
53
  secret: partial.secret,
@@ -35,7 +59,21 @@ export function resolveConfig(partial) {
35
59
  adapter: partial.adapter,
36
60
  };
37
61
  }
38
- // Parses '15m', '7d', '1h', '30s', '2w' → milliseconds
62
+ /**
63
+ * Convert a duration string or a number of seconds into milliseconds.
64
+ *
65
+ * Supported unit suffixes: `s` (seconds), `m` (minutes), `h` (hours),
66
+ * `d` (days), `w` (weeks). Numeric inputs are treated as seconds.
67
+ *
68
+ * @example
69
+ * parseExpiry('15m') // 900_000
70
+ * parseExpiry('7d') // 604_800_000
71
+ * parseExpiry(60) // 60_000
72
+ *
73
+ * @param expiresIn - A duration string (e.g. `'15m'`, `'7d'`) or a number of seconds.
74
+ * @returns The equivalent duration in milliseconds.
75
+ * @throws {Error} If the string format is unrecognised.
76
+ */
39
77
  export function parseExpiry(expiresIn) {
40
78
  if (typeof expiresIn === 'number')
41
79
  return expiresIn * 1000;
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/libs/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAanD,MAAM,iBAAiB,GAAG,EAAE,CAAC;AAC7B,MAAM,eAAe,GAAG,EAAE,CAAC;AAC3B,MAAM,eAAe,GAAG,EAAE,CAAC;AAE3B;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,MAAkB;IAC/C,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxD,MAAM,IAAI,SAAS,CAAC,qBAAqB,EAAE,0BAA0B,CAAC,CAAC;IACzE,CAAC;IACD,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,iBAAiB,EAAE,CAAC;QAC7C,MAAM,IAAI,SAAS,CACjB,qBAAqB,EACrB,2BAA2B,iBAAiB,0CAA0C,CACvF,CAAC;IACJ,CAAC;IAED,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,IAAI,EAAE,CAAC;IAC3C,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,UAAU,GAAG,eAAe,IAAI,UAAU,GAAG,eAAe,EAAE,CAAC;QAClG,MAAM,IAAI,SAAS,CACjB,qBAAqB,EACrB,yCAAyC,eAAe,QAAQ,eAAe,EAAE,CAClF,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,MAAM,CAAC,UAAU,IAAI,MAAM,CAAC,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzD,MAAM,IAAI,SAAS,CAAC,qBAAqB,EAAE,2CAA2C,CAAC,CAAC;IAC1F,CAAC;IAED,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,IAAI,SAAS,CAAC,qBAAqB,EAAE,qBAAqB,CAAC,CAAC;IACpE,CAAC;AACH,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,OAAmB;IAC/C,OAAO;QACL,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,eAAe,EAAE,OAAO,CAAC,eAAe,IAAI,KAAK;QACjD,gBAAgB,EAAE,OAAO,CAAC,gBAAgB,IAAI,IAAI;QAClD,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,OAAO;QACvC,UAAU,EAAE,OAAO,CAAC,UAAU,IAAI,EAAE;QACpC,UAAU,EAAE,OAAO,CAAC,UAAU;QAC9B,OAAO,EAAE,OAAO,CAAC,OAAO;KACzB,CAAC;AACJ,CAAC;AAED,uDAAuD;AACvD,MAAM,UAAU,WAAW,CAAC,SAA0B;IACpD,IAAI,OAAO,SAAS,KAAK,QAAQ;QAAE,OAAO,SAAS,GAAG,IAAI,CAAC;IAC3D,MAAM,WAAW,GAA2B;QAC1C,CAAC,EAAE,KAAK;QACR,CAAC,EAAE,MAAM;QACT,CAAC,EAAE,SAAS;QACZ,CAAC,EAAE,UAAU;QACb,CAAC,EAAE,WAAW;KACf,CAAC;IACF,MAAM,KAAK,GAAG,kBAAkB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACjD,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CAAC,uBAAuB,SAAS,gCAAgC,CAAC,CAAC;IACpF,CAAC;IACD,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACnC,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,uBAAuB,SAAS,gCAAgC,CAAC,CAAC;IACpF,CAAC;IACD,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC;AACvC,CAAC"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/libs/config.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,17 @@
1
+ /**
2
+ * Hash a plain-text password using bcrypt.
3
+ *
4
+ * @param plain - The raw password string supplied by the user.
5
+ * @param saltRounds - bcrypt cost factor; higher values increase security at the cost of speed.
6
+ * @returns The bcrypt hash string to persist in the database.
7
+ */
1
8
  export declare function hashPassword(plain: string, saltRounds?: number): Promise<string>;
9
+ /**
10
+ * Compare a plain-text password against a stored bcrypt hash.
11
+ *
12
+ * @param plain - The raw password string to verify.
13
+ * @param hash - The stored bcrypt hash from the database.
14
+ * @returns `true` if the password matches the hash, `false` otherwise.
15
+ */
2
16
  export declare function verifyPassword(plain: string, hash: string): Promise<boolean>;
3
17
  //# sourceMappingURL=hash.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"hash.d.ts","sourceRoot":"","sources":["../../src/libs/hash.ts"],"names":[],"mappings":"AAEA,wBAAsB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,SAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAElF;AAED,wBAAsB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAElF"}
1
+ {"version":3,"file":"hash.d.ts","sourceRoot":"","sources":["../../src/libs/hash.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,wBAAsB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,SAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAElF;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAElF"}
package/dist/libs/hash.js CHANGED
@@ -1,7 +1,21 @@
1
1
  import bcrypt from 'bcrypt';
2
+ /**
3
+ * Hash a plain-text password using bcrypt.
4
+ *
5
+ * @param plain - The raw password string supplied by the user.
6
+ * @param saltRounds - bcrypt cost factor; higher values increase security at the cost of speed.
7
+ * @returns The bcrypt hash string to persist in the database.
8
+ */
2
9
  export async function hashPassword(plain, saltRounds = 12) {
3
10
  return bcrypt.hash(plain, saltRounds);
4
11
  }
12
+ /**
13
+ * Compare a plain-text password against a stored bcrypt hash.
14
+ *
15
+ * @param plain - The raw password string to verify.
16
+ * @param hash - The stored bcrypt hash from the database.
17
+ * @returns `true` if the password matches the hash, `false` otherwise.
18
+ */
5
19
  export async function verifyPassword(plain, hash) {
6
20
  return bcrypt.compare(plain, hash);
7
21
  }
@@ -1 +1 @@
1
- {"version":3,"file":"hash.js","sourceRoot":"","sources":["../../src/libs/hash.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAE5B,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,KAAa,EAAE,UAAU,GAAG,EAAE;IAC/D,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;AACxC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,KAAa,EAAE,IAAY;IAC9D,OAAO,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;AACrC,CAAC"}
1
+ {"version":3,"file":"hash.js","sourceRoot":"","sources":["../../src/libs/hash.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAE5B;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,KAAa,EAAE,UAAU,GAAG,EAAE;IAC/D,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;AACxC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,KAAa,EAAE,IAAY;IAC9D,OAAO,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;AACrC,CAAC"}
@@ -1,7 +1,45 @@
1
- import type { AuthConfig, AuthUser } from '../types/auth.js';
2
- export declare function signAccessToken(payload: AuthUser, config: AuthConfig): string;
1
+ import type { AccessTokenPayload, AuthConfig, AuthUser } from '../types/auth.js';
2
+ /**
3
+ * Sign a short-lived access token containing the user's identity, roles, and
4
+ * the session ID that `protect()` uses to verify the session is still active.
5
+ *
6
+ * Uses the access-specific secret derived from config and the configured
7
+ * `accessExpiresIn` duration and HMAC algorithm.
8
+ *
9
+ * @param payload - The {@link AccessTokenPayload} to embed (id, identifier, roles, sessionId).
10
+ * @param config - Auth configuration used to derive the secret and options.
11
+ * @returns Compact JWT string.
12
+ */
13
+ export declare function signAccessToken(payload: AuthUser | AccessTokenPayload, config: AuthConfig): string;
14
+ /**
15
+ * Sign a long-lived refresh token bound to a specific session ID.
16
+ *
17
+ * Uses the refresh-specific secret derived from config and the configured
18
+ * `refreshExpiresIn` duration. The session ID is embedded as the sole claim
19
+ * so the token can be used to look up and rotate the session.
20
+ *
21
+ * @param sessionId - The database session ID to bind to this token.
22
+ * @param config - Auth configuration used to derive the secret and options.
23
+ * @returns Compact JWT string.
24
+ */
3
25
  export declare function signRefreshToken(sessionId: string, config: AuthConfig): string;
26
+ /**
27
+ * Verify an access token and return its decoded {@link AuthUser} payload.
28
+ *
29
+ * @param token - Compact JWT access token string.
30
+ * @param config - Auth configuration used to derive the secret and algorithm.
31
+ * @returns Decoded `AuthUser` payload (id, identifier, roles).
32
+ * @throws {AuthError} With `TOKEN_EXPIRED` if expired, `TOKEN_INVALID` otherwise.
33
+ */
4
34
  export declare function verifyAccessToken(token: string, config: AuthConfig): AuthUser;
35
+ /**
36
+ * Verify a refresh token and return the embedded session ID.
37
+ *
38
+ * @param token - Compact JWT refresh token string.
39
+ * @param config - Auth configuration used to derive the secret and algorithm.
40
+ * @returns Object with `sessionId` matching the one passed to {@link signRefreshToken}.
41
+ * @throws {AuthError} With `TOKEN_EXPIRED` if expired, `TOKEN_INVALID` otherwise.
42
+ */
5
43
  export declare function verifyRefreshToken(token: string, config: AuthConfig): {
6
44
  sessionId: string;
7
45
  };
@@ -1 +1 @@
1
- {"version":3,"file":"token.d.ts","sourceRoot":"","sources":["../../src/libs/token.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AA2C7D,wBAAgB,eAAe,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,GAAG,MAAM,CAI7E;AAED,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,MAAM,CAI9E;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,QAAQ,CAI7E;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,CAI3F"}
1
+ {"version":3,"file":"token.d.ts","sourceRoot":"","sources":["../../src/libs/token.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAqEjF;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,QAAQ,GAAG,kBAAkB,EAAE,MAAM,EAAE,UAAU,GAAG,MAAM,CAIlG;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,MAAM,CAI9E;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG,QAAQ,CAI7E;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,GAAG;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,CAI3F"}
@@ -1,12 +1,28 @@
1
1
  import jwt, {} from 'jsonwebtoken';
2
2
  import { AuthError } from '../errors/AuthError.js';
3
3
  import { resolveConfig } from './config.js';
4
+ /**
5
+ * Derive separate HMAC secrets for access and refresh tokens from a single
6
+ * root secret by appending a domain suffix.
7
+ *
8
+ * Using distinct secrets prevents a refresh token from being accepted as an
9
+ * access token and vice versa.
10
+ */
4
11
  function deriveSecrets(secret) {
5
12
  return {
6
13
  access: `${secret}:access`,
7
14
  refresh: `${secret}:refresh`,
8
15
  };
9
16
  }
17
+ /**
18
+ * Sign a JWT payload with the given secret and options.
19
+ *
20
+ * @param payload - Claims to embed in the token.
21
+ * @param secret - HMAC signing key.
22
+ * @param expiresIn - Expiry duration string (e.g. `'15m'`) or seconds.
23
+ * @param algorithm - HMAC algorithm variant.
24
+ * @returns Compact JWT string.
25
+ */
10
26
  function sign(payload, secret, expiresIn, algorithm) {
11
27
  const options = {
12
28
  expiresIn: expiresIn,
@@ -14,6 +30,16 @@ function sign(payload, secret, expiresIn, algorithm) {
14
30
  };
15
31
  return jwt.sign(payload, secret, options);
16
32
  }
33
+ /**
34
+ * Verify and decode a JWT, mapping jsonwebtoken errors to typed {@link AuthError}s.
35
+ *
36
+ * @param token - Compact JWT string to verify.
37
+ * @param secret - HMAC key used to sign the token.
38
+ * @param algorithm - Expected signing algorithm.
39
+ * @returns Decoded payload cast to `T`.
40
+ * @throws {AuthError} With `TOKEN_EXPIRED` if the token's `exp` claim is in the past.
41
+ * @throws {AuthError} With `TOKEN_INVALID` for any other verification failure.
42
+ */
17
43
  function verify(token, secret, algorithm) {
18
44
  try {
19
45
  const decoded = jwt.verify(token, secret, { algorithms: [algorithm] });
@@ -31,21 +57,59 @@ function verify(token, secret, algorithm) {
31
57
  throw new AuthError('TOKEN_INVALID', 'Token is invalid or malformed');
32
58
  }
33
59
  }
60
+ /**
61
+ * Sign a short-lived access token containing the user's identity, roles, and
62
+ * the session ID that `protect()` uses to verify the session is still active.
63
+ *
64
+ * Uses the access-specific secret derived from config and the configured
65
+ * `accessExpiresIn` duration and HMAC algorithm.
66
+ *
67
+ * @param payload - The {@link AccessTokenPayload} to embed (id, identifier, roles, sessionId).
68
+ * @param config - Auth configuration used to derive the secret and options.
69
+ * @returns Compact JWT string.
70
+ */
34
71
  export function signAccessToken(payload, config) {
35
72
  const resolved = resolveConfig(config);
36
73
  const { access } = deriveSecrets(resolved.secret);
37
74
  return sign(payload, access, resolved.accessExpiresIn, resolved.algorithm);
38
75
  }
76
+ /**
77
+ * Sign a long-lived refresh token bound to a specific session ID.
78
+ *
79
+ * Uses the refresh-specific secret derived from config and the configured
80
+ * `refreshExpiresIn` duration. The session ID is embedded as the sole claim
81
+ * so the token can be used to look up and rotate the session.
82
+ *
83
+ * @param sessionId - The database session ID to bind to this token.
84
+ * @param config - Auth configuration used to derive the secret and options.
85
+ * @returns Compact JWT string.
86
+ */
39
87
  export function signRefreshToken(sessionId, config) {
40
88
  const resolved = resolveConfig(config);
41
89
  const { refresh } = deriveSecrets(resolved.secret);
42
90
  return sign({ sessionId }, refresh, resolved.refreshExpiresIn, resolved.algorithm);
43
91
  }
92
+ /**
93
+ * Verify an access token and return its decoded {@link AuthUser} payload.
94
+ *
95
+ * @param token - Compact JWT access token string.
96
+ * @param config - Auth configuration used to derive the secret and algorithm.
97
+ * @returns Decoded `AuthUser` payload (id, identifier, roles).
98
+ * @throws {AuthError} With `TOKEN_EXPIRED` if expired, `TOKEN_INVALID` otherwise.
99
+ */
44
100
  export function verifyAccessToken(token, config) {
45
101
  const resolved = resolveConfig(config);
46
102
  const { access } = deriveSecrets(resolved.secret);
47
103
  return verify(token, access, resolved.algorithm);
48
104
  }
105
+ /**
106
+ * Verify a refresh token and return the embedded session ID.
107
+ *
108
+ * @param token - Compact JWT refresh token string.
109
+ * @param config - Auth configuration used to derive the secret and algorithm.
110
+ * @returns Object with `sessionId` matching the one passed to {@link signRefreshToken}.
111
+ * @throws {AuthError} With `TOKEN_EXPIRED` if expired, `TOKEN_INVALID` otherwise.
112
+ */
49
113
  export function verifyRefreshToken(token, config) {
50
114
  const resolved = resolveConfig(config);
51
115
  const { refresh } = deriveSecrets(resolved.secret);
@@ -1 +1 @@
1
- {"version":3,"file":"token.js","sourceRoot":"","sources":["../../src/libs/token.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"token.js","sourceRoot":"","sources":["../../src/libs/token.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,18 @@
1
1
  import type { RequestHandler } from 'express';
2
+ /**
3
+ * Express middleware factory for role-based access control (RBAC).
4
+ *
5
+ * Passes if the authenticated user (set by `protect()`) has **at least one**
6
+ * of the specified `allowedRoles`. Calls `next(AuthError)` with code `FORBIDDEN`
7
+ * if no roles match, or `UNAUTHORIZED` if `req.user` is absent.
8
+ *
9
+ * Must be used **after** `protect()`.
10
+ *
11
+ * @param allowedRoles - One or more role strings. The user needs at least one of them.
12
+ * @returns An Express `RequestHandler` that enforces the role check.
13
+ *
14
+ * @example
15
+ * router.delete('/posts/:id', protect(config), authorize('admin', 'moderator'), handler);
16
+ */
2
17
  export declare function authorize<TRole extends string>(...allowedRoles: TRole[]): RequestHandler;
3
18
  //# sourceMappingURL=authorize.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"authorize.d.ts","sourceRoot":"","sources":["../../src/middleware/authorize.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAG9C,wBAAgB,SAAS,CAAC,KAAK,SAAS,MAAM,EAAE,GAAG,YAAY,EAAE,KAAK,EAAE,GAAG,cAAc,CAcxF"}
1
+ {"version":3,"file":"authorize.d.ts","sourceRoot":"","sources":["../../src/middleware/authorize.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAG9C;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,SAAS,CAAC,KAAK,SAAS,MAAM,EAAE,GAAG,YAAY,EAAE,KAAK,EAAE,GAAG,cAAc,CAcxF"}
@@ -1,4 +1,19 @@
1
1
  import { AuthError } from '../errors/AuthError.js';
2
+ /**
3
+ * Express middleware factory for role-based access control (RBAC).
4
+ *
5
+ * Passes if the authenticated user (set by `protect()`) has **at least one**
6
+ * of the specified `allowedRoles`. Calls `next(AuthError)` with code `FORBIDDEN`
7
+ * if no roles match, or `UNAUTHORIZED` if `req.user` is absent.
8
+ *
9
+ * Must be used **after** `protect()`.
10
+ *
11
+ * @param allowedRoles - One or more role strings. The user needs at least one of them.
12
+ * @returns An Express `RequestHandler` that enforces the role check.
13
+ *
14
+ * @example
15
+ * router.delete('/posts/:id', protect(config), authorize('admin', 'moderator'), handler);
16
+ */
2
17
  export function authorize(...allowedRoles) {
3
18
  return (request, _response, next) => {
4
19
  if (!request.user) {
@@ -1 +1 @@
1
- {"version":3,"file":"authorize.js","sourceRoot":"","sources":["../../src/middleware/authorize.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAEnD,MAAM,UAAU,SAAS,CAAuB,GAAG,YAAqB;IACtE,OAAO,CAAC,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE;QAClC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;YAClB,OAAO,IAAI,CAAC,IAAI,SAAS,CAAC,cAAc,EAAE,mBAAmB,CAAC,CAAC,CAAC;QAClE,CAAC;QACD,MAAM,SAAS,GAAsB,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC;QACxD,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;QACtE,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,IAAI,CACT,IAAI,SAAS,CAAC,WAAW,EAAE,0BAA0B,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAChF,CAAC;QACJ,CAAC;QACD,IAAI,EAAE,CAAC;IACT,CAAC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"authorize.js","sourceRoot":"","sources":["../../src/middleware/authorize.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAEnD;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,SAAS,CAAuB,GAAG,YAAqB;IACtE,OAAO,CAAC,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE;QAClC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;YAClB,OAAO,IAAI,CAAC,IAAI,SAAS,CAAC,cAAc,EAAE,mBAAmB,CAAC,CAAC,CAAC;QAClE,CAAC;QACD,MAAM,SAAS,GAAsB,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC;QACxD,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;QACtE,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,IAAI,CACT,IAAI,SAAS,CAAC,WAAW,EAAE,0BAA0B,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAChF,CAAC;QACJ,CAAC;QACD,IAAI,EAAE,CAAC;IACT,CAAC,CAAC;AACJ,CAAC"}
@@ -1,4 +1,31 @@
1
1
  import type { RequestHandler } from 'express';
2
2
  import type { AuthConfig } from '../types/auth.js';
3
+ /**
4
+ * Express middleware factory that enforces JWT authentication and session validity.
5
+ *
6
+ * Reads the `Authorization: Bearer <token>` header, verifies the access token,
7
+ * and attaches the decoded payload to `req.user`. Calls `next(AuthError)` on
8
+ * any failure so your error handler can convert it to an HTTP response.
9
+ *
10
+ * Since sentri 1.1.0 access tokens embed a `sessionId` claim. When this claim
11
+ * is present, `protect()` performs a lightweight database lookup
12
+ * (`adapter.session.findById`) to confirm the session is still active. This
13
+ * means a user who has logged out (or been logged out from all devices) cannot
14
+ * use an access token that was issued before the logout — even if the token has
15
+ * not yet expired.
16
+ *
17
+ * Tokens issued before 1.1.0 (without the `sessionId` claim) are still accepted
18
+ * but bypass the session check.
19
+ *
20
+ * Must be used **before** `authorize()` or `permit()`.
21
+ *
22
+ * @param config - Auth configuration (secret, algorithm, adapter).
23
+ * @returns An Express `RequestHandler` that populates `req.user` on success.
24
+ *
25
+ * @example
26
+ * router.get('/profile', protect(config), (request, response) => {
27
+ * response.json(request.user);
28
+ * });
29
+ */
3
30
  export declare function protect(config: AuthConfig): RequestHandler;
4
31
  //# sourceMappingURL=protect.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"protect.d.ts","sourceRoot":"","sources":["../../src/middleware/protect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAG9C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,wBAAgB,OAAO,CAAC,MAAM,EAAE,UAAU,GAAG,cAAc,CAc1D"}
1
+ {"version":3,"file":"protect.d.ts","sourceRoot":"","sources":["../../src/middleware/protect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAG9C,OAAO,KAAK,EAAsB,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEvE;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,OAAO,CAAC,MAAM,EAAE,UAAU,GAAG,cAAc,CAwB1D"}
@@ -1,14 +1,49 @@
1
1
  import { AuthError } from '../errors/AuthError.js';
2
2
  import { verifyAccessToken } from '../libs/token.js';
3
+ /**
4
+ * Express middleware factory that enforces JWT authentication and session validity.
5
+ *
6
+ * Reads the `Authorization: Bearer <token>` header, verifies the access token,
7
+ * and attaches the decoded payload to `req.user`. Calls `next(AuthError)` on
8
+ * any failure so your error handler can convert it to an HTTP response.
9
+ *
10
+ * Since sentri 1.1.0 access tokens embed a `sessionId` claim. When this claim
11
+ * is present, `protect()` performs a lightweight database lookup
12
+ * (`adapter.session.findById`) to confirm the session is still active. This
13
+ * means a user who has logged out (or been logged out from all devices) cannot
14
+ * use an access token that was issued before the logout — even if the token has
15
+ * not yet expired.
16
+ *
17
+ * Tokens issued before 1.1.0 (without the `sessionId` claim) are still accepted
18
+ * but bypass the session check.
19
+ *
20
+ * Must be used **before** `authorize()` or `permit()`.
21
+ *
22
+ * @param config - Auth configuration (secret, algorithm, adapter).
23
+ * @returns An Express `RequestHandler` that populates `req.user` on success.
24
+ *
25
+ * @example
26
+ * router.get('/profile', protect(config), (request, response) => {
27
+ * response.json(request.user);
28
+ * });
29
+ */
3
30
  export function protect(config) {
4
- return (request, _response, next) => {
31
+ return async (request, _response, next) => {
5
32
  const authHeader = request.headers['authorization'];
6
33
  if (!authHeader?.startsWith('Bearer ')) {
7
34
  return next(new AuthError('UNAUTHORIZED', 'Missing or malformed Authorization header'));
8
35
  }
9
36
  const token = authHeader.slice(7);
10
37
  try {
11
- request.user = verifyAccessToken(token, config);
38
+ const payload = verifyAccessToken(token, config);
39
+ request.user = { id: payload.id, identifier: payload.identifier, roles: payload.roles };
40
+ // Session-bound validation: reject the request if the session was revoked (logout).
41
+ if (payload.sessionId) {
42
+ const session = await config.adapter.session.findById(payload.sessionId);
43
+ if (!session) {
44
+ return next(new AuthError('UNAUTHORIZED', 'Session has been revoked'));
45
+ }
46
+ }
12
47
  next();
13
48
  }
14
49
  catch (error) {
@@ -1 +1 @@
1
- {"version":3,"file":"protect.js","sourceRoot":"","sources":["../../src/middleware/protect.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAGrD,MAAM,UAAU,OAAO,CAAC,MAAkB;IACxC,OAAO,CAAC,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE;QAClC,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;QACpD,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YACvC,OAAO,IAAI,CAAC,IAAI,SAAS,CAAC,cAAc,EAAE,2CAA2C,CAAC,CAAC,CAAC;QAC1F,CAAC;QACD,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAClC,IAAI,CAAC;YACH,OAAO,CAAC,IAAI,GAAG,iBAAiB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;YAChD,IAAI,EAAE,CAAC;QACT,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,KAAK,CAAC,CAAC;QACd,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"protect.js","sourceRoot":"","sources":["../../src/middleware/protect.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAGrD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,UAAU,OAAO,CAAC,MAAkB;IACxC,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE;QACxC,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;QACpD,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YACvC,OAAO,IAAI,CAAC,IAAI,SAAS,CAAC,cAAc,EAAE,2CAA2C,CAAC,CAAC,CAAC;QAC1F,CAAC;QACD,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAClC,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,iBAAiB,CAAC,KAAK,EAAE,MAAM,CAAuB,CAAC;YACvE,OAAO,CAAC,IAAI,GAAG,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC;YAExF,oFAAoF;YACpF,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;gBACtB,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;gBACzE,IAAI,CAAC,OAAO,EAAE,CAAC;oBACb,OAAO,IAAI,CAAC,IAAI,SAAS,CAAC,cAAc,EAAE,0BAA0B,CAAC,CAAC,CAAC;gBACzE,CAAC;YACH,CAAC;YAED,IAAI,EAAE,CAAC;QACT,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,KAAK,CAAC,CAAC;QACd,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
@@ -6,12 +6,13 @@ import type { AuthConfig } from '../types/auth.js';
6
6
  * Mount it once and all routes are ready:
7
7
  *
8
8
  * ```
9
- * POST /signup — register a new user
10
- * POST /login — authenticate and get tokens
11
- * POST /refresh — rotate refresh token
12
- * POST /logout — invalidate current session
13
- * POST /logout-all — invalidate all sessions for the authenticated user
14
- * GET /me — return the currently authenticated user
9
+ * POST /register — register a new user (protected by X-Api-Key when config.apiKey is set)
10
+ * POST /login — authenticate and get tokens
11
+ * POST /refresh — rotate refresh token
12
+ * POST /logout — invalidate current session; access tokens issued before logout become invalid
13
+ * POST /logout-all — invalidate all sessions for the authenticated user
14
+ * GET /me — return the currently authenticated user
15
+ * POST /users/:userId/roles — assign roles (admin only)
15
16
  * ```
16
17
  *
17
18
  * Requires `express.json()` to be applied before the router.
@@ -19,6 +20,12 @@ import type { AuthConfig } from '../types/auth.js';
19
20
  * When `cookie` is set in config, the refresh token is stored in an httpOnly
20
21
  * cookie automatically — no `cookie-parser` needed.
21
22
  *
23
+ * When `apiKey` is set in config, `POST /register` requires the caller to send
24
+ * an `X-Api-Key: <key>` header matching the configured value.
25
+ *
26
+ * When `router` is set in config, individual service functions can be replaced
27
+ * while the router still handles validation and response formatting.
28
+ *
22
29
  * @example
23
30
  * app.use(express.json());
24
31
  * app.use('/auth', auth.router());
@@ -1 +1 @@
1
- {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../src/middleware/router.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAkD,MAAM,SAAS,CAAC;AAEjF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAkEnD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,SAAS,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,KAAK,CAAC,GAAG,MAAM,CAsKxF"}
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../src/middleware/router.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAkD,MAAM,SAAS,CAAC;AAEjF,OAAO,KAAK,EAAE,UAAU,EAA2B,MAAM,kBAAkB,CAAC;AA+E5E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,SAAS,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,KAAK,CAAC,GAAG,MAAM,CAwLxF"}