ioredis-toolkit 0.0.9 → 0.5.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 (267) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/LICENSE +1 -1
  3. package/README.md +68 -1058
  4. package/dist/cache/cache.d.ts +30 -0
  5. package/dist/cache/cache.d.ts.map +1 -0
  6. package/dist/cache/cache.js +59 -0
  7. package/dist/cache/cache.js.map +1 -0
  8. package/dist/cache/config.d.ts +12 -0
  9. package/dist/cache/config.d.ts.map +1 -0
  10. package/dist/cache/config.js +13 -0
  11. package/dist/cache/config.js.map +1 -0
  12. package/dist/cache/types.d.ts +32 -0
  13. package/dist/cache/types.d.ts.map +1 -0
  14. package/dist/cache/types.js +5 -0
  15. package/dist/cache/types.js.map +1 -0
  16. package/dist/index.d.ts +43 -51
  17. package/dist/index.d.ts.map +1 -0
  18. package/dist/index.js +32 -44
  19. package/dist/index.js.map +1 -0
  20. package/dist/lock/config.d.ts +12 -0
  21. package/dist/lock/config.d.ts.map +1 -0
  22. package/dist/lock/config.js +8 -0
  23. package/dist/lock/config.js.map +1 -0
  24. package/dist/lock/lock.d.ts +20 -0
  25. package/dist/lock/lock.d.ts.map +1 -0
  26. package/dist/lock/lock.js +44 -0
  27. package/dist/lock/lock.js.map +1 -0
  28. package/dist/lock/types.d.ts +19 -0
  29. package/dist/lock/types.d.ts.map +1 -0
  30. package/dist/lock/types.js +2 -0
  31. package/dist/lock/types.js.map +1 -0
  32. package/dist/modules-config.d.ts +3 -0
  33. package/dist/modules-config.d.ts.map +1 -0
  34. package/dist/modules-config.js +2 -0
  35. package/dist/modules-config.js.map +1 -0
  36. package/dist/pubsub/config.d.ts +11 -0
  37. package/dist/pubsub/config.d.ts.map +1 -0
  38. package/dist/pubsub/config.js +6 -0
  39. package/dist/pubsub/config.js.map +1 -0
  40. package/dist/pubsub/pubsub.d.ts +20 -0
  41. package/dist/pubsub/pubsub.d.ts.map +1 -0
  42. package/dist/pubsub/pubsub.js +54 -0
  43. package/dist/pubsub/pubsub.js.map +1 -0
  44. package/dist/pubsub/types.d.ts +24 -0
  45. package/dist/pubsub/types.d.ts.map +1 -0
  46. package/dist/pubsub/types.js +2 -0
  47. package/dist/pubsub/types.js.map +1 -0
  48. package/dist/rate-limit/config.d.ts +12 -0
  49. package/dist/rate-limit/config.d.ts.map +1 -0
  50. package/dist/rate-limit/config.js +6 -0
  51. package/dist/rate-limit/config.js.map +1 -0
  52. package/dist/rate-limit/rate-limiter.d.ts +18 -0
  53. package/dist/rate-limit/rate-limiter.d.ts.map +1 -0
  54. package/dist/rate-limit/rate-limiter.js +37 -0
  55. package/dist/rate-limit/rate-limiter.js.map +1 -0
  56. package/dist/rate-limit/types.d.ts +27 -0
  57. package/dist/rate-limit/types.d.ts.map +1 -0
  58. package/dist/rate-limit/types.js +2 -0
  59. package/dist/rate-limit/types.js.map +1 -0
  60. package/dist/redis/client-facade.d.ts +77 -0
  61. package/dist/redis/client-facade.d.ts.map +1 -0
  62. package/dist/redis/client-facade.js +102 -0
  63. package/dist/redis/client-facade.js.map +1 -0
  64. package/dist/redis/client.d.ts +10 -0
  65. package/dist/redis/client.d.ts.map +1 -0
  66. package/dist/redis/client.js +29 -0
  67. package/dist/redis/client.js.map +1 -0
  68. package/dist/redis/cluster.d.ts +7 -0
  69. package/dist/redis/cluster.d.ts.map +1 -0
  70. package/dist/redis/cluster.js +47 -0
  71. package/dist/redis/cluster.js.map +1 -0
  72. package/dist/redis/config.d.ts +39 -0
  73. package/dist/redis/config.d.ts.map +1 -0
  74. package/dist/redis/config.js +52 -0
  75. package/dist/redis/config.js.map +1 -0
  76. package/dist/redis/errors.d.ts +5 -0
  77. package/dist/redis/errors.d.ts.map +1 -0
  78. package/dist/redis/errors.js +5 -0
  79. package/dist/redis/errors.js.map +1 -0
  80. package/dist/redis/types.d.ts +135 -0
  81. package/dist/redis/types.d.ts.map +1 -0
  82. package/dist/redis/types.js +2 -0
  83. package/dist/redis/types.js.map +1 -0
  84. package/dist/redis/wrapper.d.ts +88 -0
  85. package/dist/redis/wrapper.d.ts.map +1 -0
  86. package/dist/redis/wrapper.js +206 -0
  87. package/dist/redis/wrapper.js.map +1 -0
  88. package/dist/session/config.d.ts +47 -0
  89. package/dist/session/config.d.ts.map +1 -0
  90. package/dist/session/config.js +101 -0
  91. package/dist/session/config.js.map +1 -0
  92. package/dist/session/cookie.d.ts +16 -0
  93. package/dist/session/cookie.d.ts.map +1 -0
  94. package/dist/session/cookie.js +28 -0
  95. package/dist/session/cookie.js.map +1 -0
  96. package/dist/session/errors.d.ts +56 -0
  97. package/dist/session/errors.d.ts.map +1 -0
  98. package/dist/session/errors.js +58 -0
  99. package/dist/session/errors.js.map +1 -0
  100. package/dist/session/factory.d.ts +21 -0
  101. package/dist/session/factory.d.ts.map +1 -0
  102. package/dist/session/factory.js +30 -0
  103. package/dist/session/factory.js.map +1 -0
  104. package/dist/session/health.d.ts +12 -0
  105. package/dist/session/health.d.ts.map +1 -0
  106. package/dist/session/health.js +23 -0
  107. package/dist/session/health.js.map +1 -0
  108. package/dist/session/keys.d.ts +23 -0
  109. package/dist/session/keys.d.ts.map +1 -0
  110. package/dist/session/keys.js +27 -0
  111. package/dist/session/keys.js.map +1 -0
  112. package/dist/session/manager.d.ts +34 -0
  113. package/dist/session/manager.d.ts.map +1 -0
  114. package/dist/session/manager.js +31 -0
  115. package/dist/session/manager.js.map +1 -0
  116. package/dist/session/metrics.d.ts +11 -0
  117. package/dist/session/metrics.d.ts.map +1 -0
  118. package/dist/session/metrics.js +10 -0
  119. package/dist/session/metrics.js.map +1 -0
  120. package/dist/session/repository.d.ts +49 -0
  121. package/dist/session/repository.d.ts.map +1 -0
  122. package/dist/session/repository.js +203 -0
  123. package/dist/session/repository.js.map +1 -0
  124. package/dist/session/revocation.d.ts +22 -0
  125. package/dist/session/revocation.d.ts.map +1 -0
  126. package/dist/session/revocation.js +41 -0
  127. package/dist/session/revocation.js.map +1 -0
  128. package/dist/session/script-sources.d.ts +11 -0
  129. package/dist/session/script-sources.d.ts.map +1 -0
  130. package/dist/session/script-sources.js +140 -0
  131. package/dist/session/script-sources.js.map +1 -0
  132. package/dist/session/scripts.d.ts +15 -0
  133. package/dist/session/scripts.d.ts.map +1 -0
  134. package/dist/session/scripts.js +41 -0
  135. package/dist/session/scripts.js.map +1 -0
  136. package/dist/session/serializer.d.ts +12 -0
  137. package/dist/session/serializer.d.ts.map +1 -0
  138. package/dist/session/serializer.js +77 -0
  139. package/dist/session/serializer.js.map +1 -0
  140. package/dist/session/service.d.ts +48 -0
  141. package/dist/session/service.d.ts.map +1 -0
  142. package/dist/session/service.js +235 -0
  143. package/dist/session/service.js.map +1 -0
  144. package/dist/session/token.d.ts +16 -0
  145. package/dist/session/token.d.ts.map +1 -0
  146. package/dist/session/token.js +32 -0
  147. package/dist/session/token.js.map +1 -0
  148. package/dist/session/types.d.ts +134 -0
  149. package/dist/session/types.d.ts.map +1 -0
  150. package/dist/session/types.js +2 -0
  151. package/dist/session/types.js.map +1 -0
  152. package/dist/streams/config.d.ts +12 -0
  153. package/dist/streams/config.d.ts.map +1 -0
  154. package/dist/streams/config.js +6 -0
  155. package/dist/streams/config.js.map +1 -0
  156. package/dist/streams/streams.d.ts +24 -0
  157. package/dist/streams/streams.d.ts.map +1 -0
  158. package/dist/streams/streams.js +55 -0
  159. package/dist/streams/streams.js.map +1 -0
  160. package/dist/streams/types.d.ts +32 -0
  161. package/dist/streams/types.d.ts.map +1 -0
  162. package/dist/streams/types.js +2 -0
  163. package/dist/streams/types.js.map +1 -0
  164. package/docs/ACCEPTANCE-REPORT.md +70 -0
  165. package/docs/ARCHITECTURE.md +61 -0
  166. package/docs/CAPACITY.md +33 -0
  167. package/docs/DEPLOYMENT.md +22 -0
  168. package/docs/README-API.md +15 -0
  169. package/docs/STATE-MACHINE.md +38 -0
  170. package/docs/TESTING.md +37 -0
  171. package/docs/THREAT-MODEL.md +23 -0
  172. package/docs/TYPE-SAFETY.md +34 -0
  173. package/docs/modules/cache/README.md +7 -0
  174. package/docs/modules/cache/usage.md +156 -0
  175. package/docs/modules/lock/README.md +7 -0
  176. package/docs/modules/lock/usage.md +105 -0
  177. package/docs/modules/pubsub/README.md +7 -0
  178. package/docs/modules/pubsub/usage.md +106 -0
  179. package/docs/modules/rate-limit/README.md +7 -0
  180. package/docs/modules/rate-limit/usage.md +100 -0
  181. package/docs/modules/sessions/README.md +7 -0
  182. package/docs/modules/sessions/usage.md +262 -0
  183. package/docs/modules/streams/README.md +7 -0
  184. package/docs/modules/streams/usage.md +141 -0
  185. package/package.json +50 -60
  186. package/src/scripts/cleanup-index.lua +4 -0
  187. package/src/scripts/conditional-update.lua +21 -0
  188. package/src/scripts/consume-session.lua +21 -0
  189. package/src/scripts/create-session.lua +28 -0
  190. package/src/scripts/delete.lua +2 -0
  191. package/src/scripts/destroy-user.lua +13 -0
  192. package/src/scripts/enforce-limit.lua +17 -0
  193. package/src/scripts/revoke-session.lua +13 -0
  194. package/src/scripts/rotate.lua +24 -0
  195. package/src/scripts/touch-session.lua +28 -0
  196. package/src/scripts/update-session.lua +18 -0
  197. package/dist/cache.d.ts +0 -796
  198. package/dist/cache.js +0 -1120
  199. package/dist/client.d.ts +0 -284
  200. package/dist/client.js +0 -1114
  201. package/dist/cluster-slot.d.ts +0 -4
  202. package/dist/cluster-slot.js +0 -31
  203. package/dist/cluster.d.ts +0 -79
  204. package/dist/cluster.js +0 -156
  205. package/dist/errors.d.ts +0 -30
  206. package/dist/errors.js +0 -63
  207. package/dist/health.d.ts +0 -180
  208. package/dist/health.js +0 -239
  209. package/dist/lock.d.ts +0 -248
  210. package/dist/lock.js +0 -397
  211. package/dist/logger.d.ts +0 -12
  212. package/dist/logger.js +0 -40
  213. package/dist/pubsub.d.ts +0 -423
  214. package/dist/pubsub.js +0 -537
  215. package/dist/ratelimiter.d.ts +0 -441
  216. package/dist/ratelimiter.js +0 -539
  217. package/dist/session/index.d.ts +0 -23
  218. package/dist/session/index.js +0 -16
  219. package/dist/session/revocation-store.d.ts +0 -176
  220. package/dist/session/revocation-store.js +0 -318
  221. package/dist/session/scripts/cleanup-index.lua +0 -21
  222. package/dist/session/scripts/conditional-update-encrypted.lua +0 -60
  223. package/dist/session/scripts/conditional-update.lua +0 -63
  224. package/dist/session/scripts/create.lua +0 -83
  225. package/dist/session/scripts/delete-by-user.lua +0 -29
  226. package/dist/session/scripts/delete.lua +0 -15
  227. package/dist/session/scripts/enforce-limit.lua +0 -38
  228. package/dist/session/scripts/revoke.lua +0 -61
  229. package/dist/session/scripts/rotate-encrypted.lua +0 -110
  230. package/dist/session/scripts/rotate.lua +0 -122
  231. package/dist/session/scripts/touch-encrypted.lua +0 -89
  232. package/dist/session/scripts/touch.lua +0 -72
  233. package/dist/session/scripts/validate.lua +0 -90
  234. package/dist/session/session-circuit-breaker.d.ts +0 -42
  235. package/dist/session/session-circuit-breaker.js +0 -129
  236. package/dist/session/session-config.d.ts +0 -335
  237. package/dist/session/session-config.js +0 -162
  238. package/dist/session/session-cookie.d.ts +0 -72
  239. package/dist/session/session-cookie.js +0 -101
  240. package/dist/session/session-encryption.d.ts +0 -87
  241. package/dist/session/session-encryption.js +0 -139
  242. package/dist/session/session-errors.d.ts +0 -85
  243. package/dist/session/session-errors.js +0 -145
  244. package/dist/session/session-health.d.ts +0 -38
  245. package/dist/session/session-health.js +0 -60
  246. package/dist/session/session-keys.d.ts +0 -51
  247. package/dist/session/session-keys.js +0 -113
  248. package/dist/session/session-manager.d.ts +0 -73
  249. package/dist/session/session-manager.js +0 -94
  250. package/dist/session/session-metrics.d.ts +0 -33
  251. package/dist/session/session-metrics.js +0 -112
  252. package/dist/session/session-repository.d.ts +0 -161
  253. package/dist/session/session-repository.js +0 -683
  254. package/dist/session/session-scripts.d.ts +0 -36
  255. package/dist/session/session-scripts.js +0 -130
  256. package/dist/session/session-serializer.d.ts +0 -42
  257. package/dist/session/session-serializer.js +0 -248
  258. package/dist/session/session-service.d.ts +0 -104
  259. package/dist/session/session-service.js +0 -611
  260. package/dist/session/session-token.d.ts +0 -38
  261. package/dist/session/session-token.js +0 -86
  262. package/dist/session/session-types.d.ts +0 -253
  263. package/dist/session/session-types.js +0 -16
  264. package/dist/types.d.ts +0 -924
  265. package/dist/types.js +0 -151
  266. package/dist/utils/deepmerge.d.ts +0 -9
  267. package/dist/utils/deepmerge.js +0 -61
@@ -0,0 +1,77 @@
1
+ import { createCipheriv, createDecipheriv, randomBytes } from 'node:crypto';
2
+ import { SessionSerializationError } from './errors.js';
3
+ /** Versioned serializer with optional authenticated encryption for session records. */
4
+ export class SessionSerializer {
5
+ keyManager;
6
+ /** Creates a serializer. Supplying a key manager enables AES-256-GCM envelopes. */
7
+ constructor(keyManager) {
8
+ this.keyManager = keyManager;
9
+ }
10
+ /** Serializes and optionally encrypts a validated session record. */
11
+ serialize(session) {
12
+ try {
13
+ const sessionJson = JSON.stringify(session);
14
+ if (!this.keyManager)
15
+ return JSON.stringify({ v: 1, data: sessionJson });
16
+ const { version, key } = this.keyManager.current();
17
+ if (key.length !== 32)
18
+ throw new SessionSerializationError('AES-256-GCM key must be 32 bytes');
19
+ const iv = randomBytes(12);
20
+ const cipher = createCipheriv('aes-256-gcm', key, iv);
21
+ const ciphertext = Buffer.concat([cipher.update(sessionJson, 'utf8'), cipher.final()]);
22
+ const tag = cipher.getAuthTag();
23
+ const envelope = { v: 1, enc: 1, keyVersion: version, iv: iv.toString('base64url'), tag: tag.toString('base64url'), data: ciphertext.toString('base64url') };
24
+ return JSON.stringify(envelope);
25
+ }
26
+ catch (error) {
27
+ if (error instanceof SessionSerializationError)
28
+ throw error;
29
+ throw new SessionSerializationError(undefined, error);
30
+ }
31
+ }
32
+ /** Parses, optionally decrypts, validates, and returns a session record. */
33
+ deserialize(serialized) {
34
+ try {
35
+ const outer = JSON.parse(serialized);
36
+ if (!isRecord(outer) || outer.v !== 1 || typeof outer.data !== 'string')
37
+ throw new Error('invalid envelope');
38
+ let payload = outer.data;
39
+ if (outer.enc === 1) {
40
+ if (!this.keyManager || typeof outer.keyVersion !== 'string' || typeof outer.iv !== 'string' || typeof outer.tag !== 'string')
41
+ throw new Error('encryption unavailable');
42
+ const key = this.keyManager.get(outer.keyVersion);
43
+ if (!key || key.length !== 32)
44
+ throw new Error('unknown encryption key');
45
+ const decipher = createDecipheriv('aes-256-gcm', key, Buffer.from(outer.iv, 'base64url'));
46
+ decipher.setAuthTag(Buffer.from(outer.tag, 'base64url'));
47
+ payload = Buffer.concat([decipher.update(Buffer.from(payload, 'base64url')), decipher.final()]).toString('utf8');
48
+ }
49
+ const session = JSON.parse(payload);
50
+ if (!isSessionRecord(session))
51
+ throw new Error('invalid session schema');
52
+ return session;
53
+ }
54
+ catch (error) {
55
+ throw new SessionSerializationError(undefined, error);
56
+ }
57
+ }
58
+ }
59
+ function isRecord(value) { return typeof value === 'object' && value !== null && !Array.isArray(value); }
60
+ function isOptionalString(value) { return value === undefined || typeof value === 'string'; }
61
+ function isOptionalSafeInteger(value) { return value === undefined || Number.isSafeInteger(value); }
62
+ function isSessionRecord(value) {
63
+ if (!isRecord(value))
64
+ return false;
65
+ return typeof value.id === 'string' && typeof value.jti === 'string' && typeof value.userId === 'string' &&
66
+ Number.isSafeInteger(value.createdAt) && Number.isSafeInteger(value.lastAccessedAt) && Number.isSafeInteger(value.expiresAt) &&
67
+ (value.idleExpiresAt === null || Number.isSafeInteger(value.idleExpiresAt)) &&
68
+ (value.absoluteExpiresAt === null || Number.isSafeInteger(value.absoluteExpiresAt)) &&
69
+ (value.status === 'active' || value.status === 'consumed' || value.status === 'revoked') && Number.isSafeInteger(value.version) &&
70
+ (value.securityVersion === null || Number.isSafeInteger(value.securityVersion)) &&
71
+ isOptionalSafeInteger(value.idleTimeoutSeconds) &&
72
+ isOptionalString(value.deviceId) && isOptionalString(value.ipAddress) && isOptionalString(value.userAgent) &&
73
+ isOptionalString(value.rotatedFrom) && isOptionalString(value.successorJti) && isOptionalString(value.rotationId) &&
74
+ isOptionalSafeInteger(value.consumedAt) &&
75
+ (value.metadata === undefined || isRecord(value.metadata));
76
+ }
77
+ //# sourceMappingURL=serializer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serializer.js","sourceRoot":"","sources":["../../src/session/serializer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ import type { SessionConfig } from './config.js';
2
+ import type { SessionRepository } from './repository.js';
3
+ import type { SessionTokenManager } from './token.js';
4
+ import type { CreateSessionInput, CreatedSession, RotationResult, SessionMetrics, SessionPatch, SessionRecord, ValidationResult } from './types.js';
5
+ import type { RedisClientWrapper } from '../redis/wrapper.js';
6
+ /** Dependencies required by {@link SessionService}. */
7
+ export interface SessionServiceOptions {
8
+ repository: SessionRepository;
9
+ tokens: SessionTokenManager;
10
+ redis: RedisClientWrapper;
11
+ config: SessionConfig;
12
+ metrics?: SessionMetrics;
13
+ }
14
+ /** Domain service enforcing session lifecycle and authentication invariants. */
15
+ export declare class SessionService {
16
+ private readonly o;
17
+ private readonly metrics;
18
+ /** Creates the domain service from repository, token, Redis-clock, and normalized configuration dependencies. */
19
+ constructor(o: SessionServiceOptions);
20
+ /** Creates a new session and returns its raw credential exactly once. */
21
+ create(input: CreateSessionInput): Promise<CreatedSession>;
22
+ /** Validates an opaque credential and returns a discriminated authentication result. */
23
+ validate(token: string): Promise<ValidationResult>;
24
+ private validateInternal;
25
+ /** Validates and returns an active session, throwing a typed error for invalid state. */
26
+ get(token: string): Promise<SessionRecord>;
27
+ /** Applies rolling idle-expiration rules with repository-level touch throttling. */
28
+ touch(token: string): Promise<void>;
29
+ /** Updates mutable session fields with optional optimistic-concurrency protection. */
30
+ update(token: string, patch: SessionPatch, expectedVersion?: number): Promise<SessionRecord>;
31
+ /** Consumes the predecessor credential and creates a successor credential with explicit partial-failure semantics. */
32
+ rotate(token: string): Promise<RotationResult>;
33
+ /** Best-effort idempotently removes a session and its derived indexes. */
34
+ destroy(token: string): Promise<void>;
35
+ /** Marks a session revoked and persists a bounded revocation tombstone when applicable. */
36
+ revoke(token: string): Promise<void>;
37
+ /** Sets the user's security version used to invalidate older sessions. */
38
+ setSecurityVersion(userId: string, version: number): Promise<void>;
39
+ /** Revokes a bounded batch of the user's indexed sessions without unbounded memory use. */
40
+ revokeAll(userId: string, limit?: number): Promise<{
41
+ affected: number;
42
+ remaining: number;
43
+ }>;
44
+ /** Returns a bounded page of authoritative sessions for a user. */
45
+ list(userId: string, offset?: number, limit?: number): Promise<SessionRecord[]>;
46
+ private now;
47
+ }
48
+ //# sourceMappingURL=service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/session/service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAGjD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,KAAK,EAAE,kBAAkB,EAAE,cAAc,EAAE,cAAc,EAAE,cAAc,EAAE,YAAY,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACpJ,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAE9D,uDAAuD;AACvD,MAAM,WAAW,qBAAqB;IAAG,UAAU,EAAE,iBAAiB,CAAC;IAAC,MAAM,EAAE,mBAAmB,CAAC;IAAC,KAAK,EAAE,kBAAkB,CAAC;IAAC,MAAM,EAAE,aAAa,CAAC;IAAC,OAAO,CAAC,EAAE,cAAc,CAAC;CAAE;AAElL,gFAAgF;AAChF,qBAAa,cAAc;IAGb,OAAO,CAAC,QAAQ,CAAC,CAAC;IAF9B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiB;IACzC,iHAAiH;gBACpF,CAAC,EAAE,qBAAqB;IAErD,yEAAyE;IACnE,MAAM,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,cAAc,CAAC;IAiChE,wFAAwF;IAClF,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;YAM1C,gBAAgB;IAmB9B,yFAAyF;IACnF,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAMhD,oFAAoF;IAC9E,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOzC,sFAAsF;IAChF,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,eAAe,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAalG,sHAAsH;IAChH,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAwBpD,0EAA0E;IACpE,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAS3C,2FAA2F;IACrF,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAU1C,0EAA0E;IACpE,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMxE,2FAA2F;IACrF,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,SAA6B,GAAG,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IAKrH,mEAAmE;IAC7D,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,SAAI,EAAE,KAAK,SAA6B,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;YAOtF,GAAG;CAIlB"}
@@ -0,0 +1,235 @@
1
+ import { SessionConflictError, SessionExpiredError, SessionInvalidError, SessionLimitError, SessionNotFoundError, SessionReplayError, SessionRotationError, SessionStorageError, SessionRevokedError } from './errors.js';
2
+ import { NoopMetrics } from './metrics.js';
3
+ /** Domain service enforcing session lifecycle and authentication invariants. */
4
+ export class SessionService {
5
+ o;
6
+ metrics;
7
+ /** Creates the domain service from repository, token, Redis-clock, and normalized configuration dependencies. */
8
+ constructor(o) {
9
+ this.o = o;
10
+ this.metrics = o.metrics ?? new NoopMetrics();
11
+ }
12
+ /** Creates a new session and returns its raw credential exactly once. */
13
+ async create(input) {
14
+ validateUserId(input.userId);
15
+ validateMetadata(input.metadata, this.o.config.maxMetadataBytes);
16
+ validateOptionalString(input.deviceId, 256);
17
+ validateOptionalString(input.ipAddress, 128);
18
+ validateOptionalString(input.userAgent, 1024);
19
+ if (this.o.config.securityVersionEnabled && input.securityVersion === undefined)
20
+ throw new SessionInvalidError();
21
+ const now = await this.now();
22
+ const ttl = input.ttl ?? this.o.config.ttl;
23
+ const absoluteDuration = input.absoluteTimeout ?? this.o.config.absoluteTimeout;
24
+ const idleDuration = input.idleTimeout ?? this.o.config.idleTimeout;
25
+ if (ttl <= 0 || (absoluteDuration !== undefined && absoluteDuration <= 0) || (idleDuration !== undefined && idleDuration <= 0))
26
+ throw new SessionInvalidError();
27
+ if (absoluteDuration !== undefined && ttl > absoluteDuration)
28
+ throw new SessionInvalidError();
29
+ const tokenData = this.o.tokens.generate();
30
+ const tokenHash = this.o.tokens.hash(tokenData.token);
31
+ const absoluteExpiresAt = absoluteDuration === undefined ? null : now + absoluteDuration;
32
+ const expiresAt = absoluteExpiresAt === null ? now + ttl : Math.min(now + ttl, absoluteExpiresAt);
33
+ const idleExpiresAt = idleDuration === undefined ? null : Math.min(now + idleDuration, expiresAt);
34
+ const session = {
35
+ id: tokenHash, jti: tokenData.jti, userId: input.userId, createdAt: now, lastAccessedAt: now,
36
+ expiresAt, idleExpiresAt, absoluteExpiresAt, status: 'active', version: 1,
37
+ securityVersion: input.securityVersion ?? null,
38
+ ...(idleDuration !== undefined ? { idleTimeoutSeconds: idleDuration } : {}),
39
+ ...(this.o.config.storeDeviceId && input.deviceId !== undefined ? { deviceId: input.deviceId } : {}),
40
+ ...(this.o.config.storeIpAddress && input.ipAddress !== undefined ? { ipAddress: input.ipAddress } : {}),
41
+ ...(this.o.config.storeUserAgent && input.userAgent !== undefined ? { userAgent: input.userAgent } : {}),
42
+ ...(input.metadata ? { metadata: input.metadata } : {})
43
+ };
44
+ await this.o.repository.create(session);
45
+ this.metrics.increment('session.created');
46
+ return { token: tokenData.token, session };
47
+ }
48
+ /** Validates an opaque credential and returns a discriminated authentication result. */
49
+ async validate(token) {
50
+ const result = await this.validateInternal(token);
51
+ this.metrics.increment('session.validate', 1, result.valid ? { result: 'valid' } : { result: 'invalid', reason: result.reason });
52
+ return result;
53
+ }
54
+ async validateInternal(token) {
55
+ if (!this.o.tokens.validateFormat(token))
56
+ return { valid: false, reason: 'invalid' };
57
+ const tokenHash = this.o.tokens.hash(token);
58
+ const session = await this.o.repository.resolve(tokenHash);
59
+ if (!session)
60
+ return { valid: false, reason: 'not_found' };
61
+ const now = await this.now();
62
+ if (session.status === 'revoked')
63
+ return { valid: false, reason: 'revoked' };
64
+ if (session.status === 'consumed')
65
+ return { valid: false, reason: 'consumed' };
66
+ if (session.expiresAt <= now)
67
+ return { valid: false, reason: 'expired' };
68
+ if (session.absoluteExpiresAt !== null && session.absoluteExpiresAt <= now)
69
+ return { valid: false, reason: 'absolute_timeout' };
70
+ if (session.idleExpiresAt !== null && session.idleExpiresAt <= now)
71
+ return { valid: false, reason: 'idle_timeout' };
72
+ if (!(await this.o.repository.isIndexed(session)))
73
+ return { valid: false, reason: 'invalid' };
74
+ if (this.o.config.securityVersionEnabled) {
75
+ const current = await this.o.repository.getSecurityVersion(session.userId);
76
+ if (current === null || session.securityVersion !== Number(current))
77
+ return { valid: false, reason: 'invalid' };
78
+ }
79
+ return { valid: true, session };
80
+ }
81
+ /** Validates and returns an active session, throwing a typed error for invalid state. */
82
+ async get(token) {
83
+ const result = await this.validate(token);
84
+ if (!result.valid)
85
+ throw errorForReason(result.reason);
86
+ return result.session;
87
+ }
88
+ /** Applies rolling idle-expiration rules with repository-level touch throttling. */
89
+ async touch(token) {
90
+ const session = await this.get(token);
91
+ if (!this.o.config.rolling)
92
+ return;
93
+ const result = await this.o.repository.touch(session, await this.now());
94
+ if (result === 'invalid')
95
+ throw new SessionExpiredError();
96
+ }
97
+ /** Updates mutable session fields with optional optimistic-concurrency protection. */
98
+ async update(token, patch, expectedVersion) {
99
+ const session = await this.get(token);
100
+ validateMetadata(patch.metadata, this.o.config.maxMetadataBytes);
101
+ const replacement = { ...session, version: session.version + 1 };
102
+ if (patch.metadata !== undefined)
103
+ replacement.metadata = patch.metadata;
104
+ if (patch.deviceId !== undefined) {
105
+ if (patch.deviceId === null)
106
+ delete replacement.deviceId;
107
+ else
108
+ replacement.deviceId = patch.deviceId;
109
+ }
110
+ if (patch.ipAddress !== undefined) {
111
+ if (patch.ipAddress === null)
112
+ delete replacement.ipAddress;
113
+ else
114
+ replacement.ipAddress = patch.ipAddress;
115
+ }
116
+ if (patch.userAgent !== undefined) {
117
+ if (patch.userAgent === null)
118
+ delete replacement.userAgent;
119
+ else
120
+ replacement.userAgent = patch.userAgent;
121
+ }
122
+ if (expectedVersion !== undefined && expectedVersion !== session.version)
123
+ throw new SessionConflictError();
124
+ await this.o.repository.update(session, replacement, expectedVersion ?? session.version, await this.now());
125
+ return replacement;
126
+ }
127
+ /** Consumes the predecessor credential and creates a successor credential with explicit partial-failure semantics. */
128
+ async rotate(token) {
129
+ const current = await this.get(token);
130
+ if (current.status !== 'active')
131
+ throw new SessionReplayError();
132
+ const now = await this.now();
133
+ const remaining = current.expiresAt - now;
134
+ if (remaining <= 0)
135
+ throw new SessionExpiredError();
136
+ const tokenData = this.o.tokens.generate();
137
+ const successorHash = this.o.tokens.hash(tokenData.token);
138
+ const absoluteExpiresAt = current.absoluteExpiresAt;
139
+ const expiresAt = absoluteExpiresAt === null ? current.expiresAt : Math.min(current.expiresAt, absoluteExpiresAt);
140
+ const idleExpiresAt = current.idleExpiresAt === null ? null : Math.min(expiresAt, now + (current.idleTimeoutSeconds ?? this.o.config.idleTimeout ?? Math.max(1, current.idleExpiresAt - current.lastAccessedAt)));
141
+ const successor = {
142
+ ...current, id: successorHash, jti: tokenData.jti, createdAt: now, lastAccessedAt: now,
143
+ expiresAt, idleExpiresAt, status: 'active', version: 1, rotatedFrom: current.jti
144
+ };
145
+ // Cross-slot successor creation is deliberately two-phase. Consuming the predecessor
146
+ // is authoritative and atomic; failure afterwards can only deny access, never create two valid successors.
147
+ await this.o.repository.consume(current, now, Math.min(300, Math.max(1, remaining)));
148
+ try {
149
+ await this.o.repository.create(successor);
150
+ }
151
+ catch (error) {
152
+ throw new SessionRotationError('Rotation consumed the predecessor but successor creation failed', error);
153
+ }
154
+ this.metrics.increment('session.rotated');
155
+ return { token: tokenData.token, session: successor, previousJti: current.jti };
156
+ }
157
+ /** Best-effort idempotently removes a session and its derived indexes. */
158
+ async destroy(token) {
159
+ if (!this.o.tokens.validateFormat(token))
160
+ return;
161
+ const hash = this.o.tokens.hash(token);
162
+ const session = await this.o.repository.resolve(hash);
163
+ if (!session)
164
+ return;
165
+ await this.o.repository.destroy(session);
166
+ this.metrics.increment('session.destroyed');
167
+ }
168
+ /** Marks a session revoked and persists a bounded revocation tombstone when applicable. */
169
+ async revoke(token) {
170
+ if (!this.o.tokens.validateFormat(token))
171
+ return;
172
+ const hash = this.o.tokens.hash(token);
173
+ const session = await this.o.repository.resolve(hash);
174
+ if (!session)
175
+ return;
176
+ const now = await this.now();
177
+ await this.o.repository.revoke(session, now, Math.min(300, Math.max(1, session.expiresAt - now)));
178
+ this.metrics.increment('session.revoked');
179
+ }
180
+ /** Sets the user's security version used to invalidate older sessions. */
181
+ async setSecurityVersion(userId, version) {
182
+ validateUserId(userId);
183
+ if (!Number.isSafeInteger(version) || version < 0)
184
+ throw new SessionInvalidError();
185
+ await this.o.repository.setSecurityVersion(userId, version);
186
+ }
187
+ /** Revokes a bounded batch of the user's indexed sessions without unbounded memory use. */
188
+ async revokeAll(userId, limit = this.o.config.maxBatchSize) {
189
+ validateUserId(userId);
190
+ return this.o.repository.revokeAll(userId, limit);
191
+ }
192
+ /** Returns a bounded page of authoritative sessions for a user. */
193
+ async list(userId, offset = 0, limit = this.o.config.maxBatchSize) {
194
+ validateUserId(userId);
195
+ if (!Number.isInteger(offset) || offset < 0 || !Number.isInteger(limit) || limit < 1)
196
+ throw new SessionInvalidError();
197
+ if (limit > this.o.config.maxBatchSize)
198
+ throw new SessionLimitError(`limit must not exceed maxBatchSize (${this.o.config.maxBatchSize})`);
199
+ return this.o.repository.list(userId, offset, limit);
200
+ }
201
+ async now() {
202
+ try {
203
+ const [seconds] = await this.o.redis.time();
204
+ return Number(seconds);
205
+ }
206
+ catch (error) {
207
+ throw new SessionStorageError('Redis time unavailable', error);
208
+ }
209
+ }
210
+ }
211
+ function validateUserId(value) { if (typeof value !== 'string' || value.length < 1 || value.length > 256)
212
+ throw new SessionInvalidError(); }
213
+ function validateOptionalString(value, max) { if (value !== undefined && (typeof value !== 'string' || value.length > max))
214
+ throw new SessionInvalidError(); }
215
+ function validateMetadata(value, maxBytes) { if (value === undefined)
216
+ return; try {
217
+ const encoded = JSON.stringify(value);
218
+ if (encoded === undefined || Buffer.byteLength(encoded, 'utf8') > maxBytes)
219
+ throw new SessionInvalidError();
220
+ }
221
+ catch (error) {
222
+ if (error instanceof SessionInvalidError)
223
+ throw error;
224
+ throw new SessionInvalidError();
225
+ } }
226
+ function errorForReason(reason) {
227
+ if (reason === 'not_found')
228
+ return new SessionNotFoundError();
229
+ if (reason === 'expired' || reason === 'idle_timeout' || reason === 'absolute_timeout')
230
+ return new SessionExpiredError();
231
+ if (reason === 'revoked')
232
+ return new SessionRevokedError();
233
+ return new SessionInvalidError();
234
+ }
235
+ //# sourceMappingURL=service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/session/service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /** Generates, hashes, and validates opaque session credentials. */
2
+ export declare class SessionTokenManager {
3
+ private readonly bytes;
4
+ /** Creates a token manager; `bytes` controls random secret entropy and must be at least 32. */
5
+ constructor(bytes?: number);
6
+ /** Generates an opaque token containing a random JTI and random secret. */
7
+ generate(): {
8
+ token: string;
9
+ jti: string;
10
+ };
11
+ /** Returns the SHA-256 digest used as the Redis lookup identifier. */
12
+ hash(token: string): string;
13
+ /** Checks the token's non-secret syntactic structure without contacting Redis. */
14
+ validateFormat(token: string): boolean;
15
+ }
16
+ //# sourceMappingURL=token.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token.d.ts","sourceRoot":"","sources":["../../src/session/token.ts"],"names":[],"mappings":"AAGA,mEAAmE;AACnE,qBAAa,mBAAmB;IAElB,OAAO,CAAC,QAAQ,CAAC,KAAK;IADlC,+FAA+F;gBAClE,KAAK,SAAK;IAGvC,2EAA2E;IAC3E,QAAQ,IAAI;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE;IAK1C,sEAAsE;IACtE,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAI3B,kFAAkF;IAClF,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;CAKvC"}
@@ -0,0 +1,32 @@
1
+ import { createHash, randomBytes } from 'node:crypto';
2
+ import { SessionInputError } from './errors.js';
3
+ /** Generates, hashes, and validates opaque session credentials. */
4
+ export class SessionTokenManager {
5
+ bytes;
6
+ /** Creates a token manager; `bytes` controls random secret entropy and must be at least 32. */
7
+ constructor(bytes = 32) {
8
+ this.bytes = bytes;
9
+ if (!Number.isInteger(bytes) || bytes < 32)
10
+ throw new SessionInputError('Token entropy must be at least 256 bits');
11
+ }
12
+ /** Generates an opaque token containing a random JTI and random secret. */
13
+ generate() {
14
+ const jti = randomBytes(16).toString('base64url');
15
+ const secret = randomBytes(this.bytes).toString('base64url');
16
+ return { token: `${jti}.${secret}`, jti };
17
+ }
18
+ /** Returns the SHA-256 digest used as the Redis lookup identifier. */
19
+ hash(token) {
20
+ if (!this.validateFormat(token))
21
+ throw new SessionInputError('Invalid session token format');
22
+ return createHash('sha256').update(token, 'utf8').digest('hex');
23
+ }
24
+ /** Checks the token's non-secret syntactic structure without contacting Redis. */
25
+ validateFormat(token) {
26
+ if (typeof token !== 'string' || token.length < 45 || token.length > 512)
27
+ return false;
28
+ const dot = token.indexOf('.');
29
+ return dot > 0 && dot === token.lastIndexOf('.') && dot < token.length - 1 && /^[A-Za-z0-9_-]+$/.test(token.slice(0, dot)) && /^[A-Za-z0-9_-]+$/.test(token.slice(dot + 1));
30
+ }
31
+ }
32
+ //# sourceMappingURL=token.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token.js","sourceRoot":"","sources":["../../src/session/token.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD,mEAAmE;AACnE,MAAM,OAAO,mBAAmB;IAED;IAD7B,+FAA+F;IAC/F,YAA6B,QAAQ,EAAE;QAAV,UAAK,GAAL,KAAK,CAAK;QACrC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,EAAE;YAAE,MAAM,IAAI,iBAAiB,CAAC,yCAAyC,CAAC,CAAC;IACrH,CAAC;IACD,2EAA2E;IAC3E,QAAQ;QACN,MAAM,GAAG,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;QAClD,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;QAC7D,OAAO,EAAE,KAAK,EAAE,GAAG,GAAG,IAAI,MAAM,EAAE,EAAE,GAAG,EAAE,CAAC;IAC5C,CAAC;IACD,sEAAsE;IACtE,IAAI,CAAC,KAAa;QAChB,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC;YAAE,MAAM,IAAI,iBAAiB,CAAC,8BAA8B,CAAC,CAAC;QAC7F,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAClE,CAAC;IACD,kFAAkF;IAClF,cAAc,CAAC,KAAa;QAC1B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,EAAE,IAAI,KAAK,CAAC,MAAM,GAAG,GAAG;YAAE,OAAO,KAAK,CAAC;QACvF,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC/B,OAAO,GAAG,GAAG,CAAC,IAAI,GAAG,KAAK,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,kBAAkB,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,IAAI,kBAAkB,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;IAC9K,CAAC;CACF"}
@@ -0,0 +1,134 @@
1
+ /** Lifecycle states that may be persisted for a session. */
2
+ export type SessionStatus = 'active' | 'consumed' | 'revoked';
3
+ /** Authoritative session state stored by the repository. Raw credentials are deliberately absent. */
4
+ export interface SessionRecord {
5
+ /** Stable identifier derived from the token hash. */
6
+ id: string;
7
+ /** Public token identifier used for rotation/revocation bookkeeping; it is not the raw token. */
8
+ jti: string;
9
+ /** Application user identifier that owns the session. */
10
+ userId: string;
11
+ /** Unix timestamp in seconds at which the session was created. */
12
+ createdAt: number;
13
+ /** Unix timestamp in seconds of the last accepted access/touch. */
14
+ lastAccessedAt: number;
15
+ /** Hard storage/authentication expiration timestamp in Unix seconds. */
16
+ expiresAt: number;
17
+ /** Rolling idle expiration boundary, or `null` when idle expiry is disabled. */
18
+ idleExpiresAt: number | null;
19
+ /** Originally configured rolling idle-timeout duration in seconds, absent when idle expiry is disabled. Preserved across touch and rotation so the idle window does not drift from its configured value. */
20
+ idleTimeoutSeconds?: number;
21
+ /** Absolute application expiration boundary, or `null` when disabled. */
22
+ absoluteExpiresAt: number | null;
23
+ /** Current lifecycle state. Only `active` sessions can authenticate. */
24
+ status: SessionStatus;
25
+ /** Monotonically increasing optimistic-concurrency version. */
26
+ version: number;
27
+ /** User security version captured at creation, or `null` when disabled. */
28
+ securityVersion: number | null;
29
+ /** Optional application device identifier. */
30
+ deviceId?: string;
31
+ /** Optional source IP address when configured for storage. */
32
+ ipAddress?: string;
33
+ /** Optional user-agent string when configured for storage. */
34
+ userAgent?: string;
35
+ /** Bounded JSON-compatible application metadata. */
36
+ metadata?: Record<string, string | number | boolean | null>;
37
+ /** JTI of the predecessor session when this record came from rotation. */
38
+ rotatedFrom?: string;
39
+ /** Unix timestamp at which this session was consumed by rotation. */
40
+ consumedAt?: number;
41
+ /** Successor JTI when rotation recorded it. */
42
+ successorJti?: string;
43
+ /** Optional application-level rotation correlation ID. */
44
+ rotationId?: string;
45
+ }
46
+ /** Input used to create a new authentication session. */
47
+ export interface CreateSessionInput {
48
+ /** Owning application user ID. */
49
+ userId: string;
50
+ /** Optional hard TTL override in seconds. */
51
+ ttl?: number;
52
+ /** Optional rolling idle timeout in seconds. */
53
+ idleTimeout?: number;
54
+ /** Optional absolute timeout in seconds. */
55
+ absoluteTimeout?: number;
56
+ /** Optional user security version to capture. */
57
+ securityVersion?: number;
58
+ /** Optional device identifier. */
59
+ deviceId?: string;
60
+ /** Optional source IP address. */
61
+ ipAddress?: string;
62
+ /** Optional user-agent string. */
63
+ userAgent?: string;
64
+ /** Optional bounded metadata. */
65
+ metadata?: Record<string, string | number | boolean | null>;
66
+ }
67
+ /** Mutable fields accepted by a session update. */
68
+ export interface SessionPatch {
69
+ /** Replaces the complete metadata object when supplied. */
70
+ metadata?: Record<string, string | number | boolean | null>;
71
+ /** Replaces or clears the device identifier. */
72
+ deviceId?: string | null;
73
+ /** Replaces or clears the IP address. */
74
+ ipAddress?: string | null;
75
+ /** Replaces or clears the user-agent string. */
76
+ userAgent?: string | null;
77
+ }
78
+ /** Newly created session plus the raw credential, which is returned only to the caller. */
79
+ export interface CreatedSession {
80
+ /** Raw opaque session token. Never persist or log this value. */
81
+ token: string;
82
+ /** Authoritative persisted session representation. */
83
+ session: SessionRecord;
84
+ }
85
+ /** Reasons returned when a raw session token cannot authenticate. */
86
+ export type InvalidReason = 'not_found' | 'expired' | 'idle_timeout' | 'absolute_timeout' | 'revoked' | 'consumed' | 'invalid';
87
+ /** Discriminated authentication result that separates invalid credentials from infrastructure failures. */
88
+ export type ValidationResult = {
89
+ valid: true;
90
+ session: SessionRecord;
91
+ } | {
92
+ valid: false;
93
+ reason: InvalidReason;
94
+ };
95
+ /** Result of a successful session rotation. */
96
+ export interface RotationResult {
97
+ /** Raw successor token; replace the predecessor credential with this value. */
98
+ token: string;
99
+ /** Newly created authoritative successor session. */
100
+ session: SessionRecord;
101
+ /** JTI of the consumed predecessor. */
102
+ previousJti: string;
103
+ }
104
+ /** Lightweight result from the Redis-backed session health probe. */
105
+ export interface SessionHealth {
106
+ /** Aggregate health classification. */
107
+ status: 'healthy' | 'degraded' | 'unhealthy';
108
+ /** Round-trip latency measured by the probe. */
109
+ latencyMs: number;
110
+ /** Error-rate signal supplied by the health implementation. */
111
+ errorRate: number;
112
+ /** Redis client status string. */
113
+ redisStatus: string;
114
+ }
115
+ /** Minimal metrics sink accepted by the session subsystem. */
116
+ export interface SessionMetrics {
117
+ /** Increments a counter metric. */
118
+ increment(name: string, value?: number, labels?: Record<string, string>): void;
119
+ /** Records an observation such as latency. */
120
+ observe(name: string, value: number, labels?: Record<string, string>): void;
121
+ /** Sets a gauge value. */
122
+ gauge(name: string, value: number, labels?: Record<string, string>): void;
123
+ }
124
+ /** Supplies versioned 32-byte encryption keys to the optional session serializer. */
125
+ export interface KeyManager {
126
+ /** Returns the current encryption key and its version identifier. */
127
+ current(): {
128
+ version: string;
129
+ key: Buffer;
130
+ };
131
+ /** Returns a historical key by version for decryption, or `undefined` if unavailable. */
132
+ get(version: string): Buffer | undefined;
133
+ }
134
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/session/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/session/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,12 @@
1
+ import { z } from 'zod';
2
+ import type { StreamsConfig } from './types.js';
3
+ /** Runtime schema for Redis Streams configuration. */
4
+ export declare const StreamsConfigSchema: z.ZodObject<{
5
+ enabled: z.ZodDefault<z.ZodBoolean>;
6
+ keyPrefix: z.ZodDefault<z.ZodString>;
7
+ maxEntries: z.ZodDefault<z.ZodNumber>;
8
+ blockMs: z.ZodDefault<z.ZodNumber>;
9
+ }, z.core.$strip>;
10
+ /** Validates and normalizes Streams configuration. */
11
+ export declare function parseStreamsConfig(input: unknown): StreamsConfig;
12
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/streams/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,sDAAsD;AACtD,eAAO,MAAM,mBAAmB;;;;;iBAA2P,CAAC;AAC5R,sDAAsD;AACtD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,aAAa,CAAoE"}
@@ -0,0 +1,6 @@
1
+ import { z } from 'zod';
2
+ /** Runtime schema for Redis Streams configuration. */
3
+ export const StreamsConfigSchema = z.object({ enabled: z.boolean().default(false), keyPrefix: z.string().min(1).max(128).default('stream'), maxEntries: z.number().int().positive().max(10_000_000).default(100_000), blockMs: z.number().int().nonnegative().max(300_000).default(5000) });
4
+ /** Validates and normalizes Streams configuration. */
5
+ export function parseStreamsConfig(input) { return StreamsConfigSchema.parse(input ?? {}); }
6
+ //# sourceMappingURL=config.js.map