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
@@ -1,90 +0,0 @@
1
- -- validate.lua (version 1)
2
- -- Single-round-trip validation read: fetches the session record, the
3
- -- user's security version (when the key exists) and lazily cleans up
4
- -- expired records and their index members, all in one same-slot script.
5
- --
6
- -- The script performs the cheap, non-cryptographic state checks; binding
7
- -- checks and (for encrypted sessions) decryption happen app-side on the
8
- -- returned payload. A missing security-version key means the check is
9
- -- skipped (the version is only enforced once setSecurityVersion has run).
10
- --
11
- -- KEYS[1] = session record key
12
- -- KEYS[2] = user security version key (same slot)
13
- -- KEYS[3] = user session index key (same slot)
14
- --
15
- -- ARGV[1] = jti (used for the lazy index cleanup of encrypted records,
16
- -- whose headers do not carry their own jti)
17
- --
18
- -- Returns:
19
- -- {1, raw, securityVersion?} record exists and passed script checks;
20
- -- raw is the stored envelope; securityVersion
21
- -- is the current user version when set
22
- -- {0} not found
23
- -- {-1, status} not active (consumed/revoked)
24
- -- {-2} expired (absolute); record + index entry
25
- -- removed
26
- -- {-3} idle expired; record + index entry removed
27
- -- {-4} security version mismatch (plain records)
28
- local raw = redis.call('GET', KEYS[1])
29
-
30
- if not raw then
31
- return { 0 }
32
- end
33
-
34
- local currentRaw = redis.call('GET', KEYS[2])
35
- local currentVersion = nil
36
-
37
- if currentRaw then
38
- currentVersion = tonumber(currentRaw)
39
- end
40
-
41
- local env = cjson.decode(raw)
42
- local now = tonumber(redis.call('TIME')[1])
43
-
44
- if env.v == 1 then
45
- local s = env.s
46
-
47
- if s.status ~= 'active' then
48
- return { -1, s.status }
49
- end
50
-
51
- if tonumber(s.absoluteExpiresAt) <= now then
52
- redis.call('DEL', KEYS[1])
53
- redis.call('ZREM', KEYS[3], s.jti)
54
- return { -2 }
55
- end
56
-
57
- if s.idleExpiresAt and s.idleExpiresAt ~= cjson.null and tonumber(s.idleExpiresAt) <= now then
58
- redis.call('DEL', KEYS[1])
59
- redis.call('ZREM', KEYS[3], s.jti)
60
- return { -3 }
61
- end
62
-
63
- if currentVersion and (s.securityVersion == nil or tonumber(s.securityVersion) ~= currentVersion) then
64
- return { -4 }
65
- end
66
- elseif env.v == 2 then
67
- -- Encrypted envelope: only the plaintext header mirrors are readable in
68
- -- Lua. They are the script-level authority for state decisions; the
69
- -- ciphertext remains authoritative for the payload (assertHeaderMatches).
70
- if env.st ~= 'active' then
71
- return { -1, env.st }
72
- end
73
-
74
- if tonumber(env.exp) <= now then
75
- redis.call('DEL', KEYS[1])
76
- redis.call('ZREM', KEYS[3], ARGV[1])
77
- return { -2 }
78
- end
79
-
80
- if env.idle ~= cjson.null and tonumber(env.idle) <= now then
81
- redis.call('DEL', KEYS[1])
82
- redis.call('ZREM', KEYS[3], ARGV[1])
83
- return { -3 }
84
- end
85
-
86
- -- Security version is checked app-side for encrypted records (it lives
87
- -- only inside the ciphertext).
88
- end
89
-
90
- return { 1, raw, currentVersion }
@@ -1,42 +0,0 @@
1
- import type { SessionCircuitBreakerConfig } from './session-config.js';
2
- export type CircuitBreakerState = 'closed' | 'open' | 'half_open';
3
- /**
4
- * Fail-closed circuit breaker around session storage operations.
5
- *
6
- * `run()` executes a function while the circuit is closed or a half-open
7
- * probe is allowed, and throws {@link CircuitBreakerOpenError} when open.
8
- * Callers may instead use {@link tryAcquire} / {@link recordSuccess} /
9
- * {@link recordFailure} explicitly.
10
- */
11
- export declare class SessionCircuitBreaker {
12
- private readonly config;
13
- private readonly circuit;
14
- private readonly now;
15
- private readonly onTransition;
16
- constructor(config: SessionCircuitBreakerConfig, options?: {
17
- now?: () => number;
18
- onTransition?: (state: CircuitBreakerState) => void;
19
- });
20
- get state(): CircuitBreakerState;
21
- /**
22
- * Runs an operation under circuit protection. Records success/failure
23
- * based on the promise outcome.
24
- *
25
- * @throws {CircuitBreakerOpenError} when the circuit is open.
26
- */
27
- run<T>(fn: () => Promise<T>): Promise<T>;
28
- /**
29
- * Attempts to acquire a call slot synchronously. Returns false (fail
30
- * closed) when the circuit is open and not yet ready for probes.
31
- */
32
- tryAcquire(): boolean;
33
- /** Records a successful operation (closes a half-open circuit). */
34
- recordSuccess(): void;
35
- /** Records a failed operation (may open the circuit). */
36
- recordFailure(): void;
37
- /** Evaluates whether the open timer has elapsed, rolling to half-open. */
38
- private rollHalfOpen;
39
- private transitionTo;
40
- /** Resets the breaker to closed (admin/repair). */
41
- reset(): void;
42
- }
@@ -1,129 +0,0 @@
1
- import { CircuitBreakerOpenError } from './session-errors.js';
2
- /**
3
- * Fail-closed circuit breaker around session storage operations.
4
- *
5
- * `run()` executes a function while the circuit is closed or a half-open
6
- * probe is allowed, and throws {@link CircuitBreakerOpenError} when open.
7
- * Callers may instead use {@link tryAcquire} / {@link recordSuccess} /
8
- * {@link recordFailure} explicitly.
9
- */
10
- export class SessionCircuitBreaker {
11
- config;
12
- circuit;
13
- now;
14
- onTransition;
15
- constructor(config, options = {}) {
16
- this.config = config;
17
- this.now = options.now ?? (() => Date.now());
18
- this.onTransition = options.onTransition ?? null;
19
- this.circuit = {
20
- state: 'closed',
21
- consecutiveFailures: 0,
22
- openedAt: 0,
23
- halfOpenInFlight: 0,
24
- halfOpenSucceeded: 0,
25
- };
26
- }
27
- get state() {
28
- return this.circuit.state;
29
- }
30
- /**
31
- * Runs an operation under circuit protection. Records success/failure
32
- * based on the promise outcome.
33
- *
34
- * @throws {CircuitBreakerOpenError} when the circuit is open.
35
- */
36
- async run(fn) {
37
- if (!this.tryAcquire()) {
38
- throw new CircuitBreakerOpenError();
39
- }
40
- try {
41
- const result = await fn();
42
- this.recordSuccess();
43
- return result;
44
- }
45
- catch (error) {
46
- this.recordFailure();
47
- throw error;
48
- }
49
- }
50
- /**
51
- * Attempts to acquire a call slot synchronously. Returns false (fail
52
- * closed) when the circuit is open and not yet ready for probes.
53
- */
54
- tryAcquire() {
55
- this.rollHalfOpen();
56
- if (this.circuit.state === 'closed')
57
- return true;
58
- if (this.circuit.state === 'half_open') {
59
- if (this.circuit.halfOpenInFlight >= this.config.halfOpenMaxRequests) {
60
- return false;
61
- }
62
- this.circuit.halfOpenInFlight += 1;
63
- return true;
64
- }
65
- return false;
66
- }
67
- /** Records a successful operation (closes a half-open circuit). */
68
- recordSuccess() {
69
- if (this.circuit.state === 'closed') {
70
- this.circuit.consecutiveFailures = 0;
71
- return;
72
- }
73
- if (this.circuit.state !== 'half_open')
74
- return;
75
- this.circuit.halfOpenInFlight = Math.max(0, this.circuit.halfOpenInFlight - 1);
76
- this.circuit.halfOpenSucceeded += 1;
77
- if (this.circuit.halfOpenInFlight === 0 && this.circuit.halfOpenSucceeded >= 1) {
78
- this.transitionTo('closed');
79
- }
80
- }
81
- /** Records a failed operation (may open the circuit). */
82
- recordFailure() {
83
- if (this.circuit.state === 'closed') {
84
- this.circuit.consecutiveFailures += 1;
85
- if (this.circuit.consecutiveFailures >= this.config.failureThreshold) {
86
- this.transitionTo('open');
87
- }
88
- return;
89
- }
90
- if (this.circuit.state === 'half_open') {
91
- this.circuit.halfOpenInFlight = Math.max(0, this.circuit.halfOpenInFlight - 1);
92
- this.transitionTo('open');
93
- }
94
- }
95
- /** Evaluates whether the open timer has elapsed, rolling to half-open. */
96
- rollHalfOpen() {
97
- if (this.circuit.state === 'open' &&
98
- this.now() - this.circuit.openedAt >= this.config.resetTimeoutMs) {
99
- this.circuit.state = 'half_open';
100
- this.circuit.halfOpenInFlight = 0;
101
- this.circuit.halfOpenSucceeded = 0;
102
- this.onTransition?.('half_open');
103
- }
104
- }
105
- transitionTo(state) {
106
- if (this.circuit.state === state)
107
- return;
108
- this.circuit.state = state;
109
- if (state === 'open') {
110
- this.circuit.openedAt = this.now();
111
- this.circuit.halfOpenInFlight = 0;
112
- this.circuit.halfOpenSucceeded = 0;
113
- }
114
- if (state === 'half_open') {
115
- this.circuit.halfOpenInFlight = 0;
116
- this.circuit.halfOpenSucceeded = 0;
117
- }
118
- if (state === 'closed') {
119
- this.circuit.consecutiveFailures = 0;
120
- this.circuit.halfOpenInFlight = 0;
121
- this.circuit.halfOpenSucceeded = 0;
122
- }
123
- this.onTransition?.(state);
124
- }
125
- /** Resets the breaker to closed (admin/repair). */
126
- reset() {
127
- this.transitionTo('closed');
128
- }
129
- }
@@ -1,335 +0,0 @@
1
- import { z } from 'zod';
2
- /** Default absolute session lifetime: 7 days. */
3
- export declare const TTL: number;
4
- /** Default idle timeout: 24 hours. */
5
- export declare const IDLE_TIMEOUT: number;
6
- /** Default touch throttle interval: 5 minutes. */
7
- export declare const TOUCH_INTERVAL: number;
8
- export declare const SessionStatusSchema: z.ZodEnum<{
9
- active: "active";
10
- consumed: "consumed";
11
- revoked: "revoked";
12
- }>;
13
- /** How strictly session binding metadata (IP/UA/device) is enforced. */
14
- export declare const SessionBindingPolicySchema: z.ZodEnum<{
15
- disabled: "disabled";
16
- advisory: "advisory";
17
- strict: "strict";
18
- }>;
19
- /** Optional fail-closed circuit breaker around session operations. */
20
- export interface SessionCircuitBreakerConfig {
21
- /** Enable the fail-closed circuit breaker. Default: false. */
22
- enabled: boolean;
23
- /** Consecutive failures needed to open the circuit. */
24
- failureThreshold: number;
25
- /** Milliseconds the circuit stays open before half-open probes. */
26
- resetTimeoutMs: number;
27
- /** Maximum concurrent probe requests while half-open. */
28
- halfOpenMaxRequests: number;
29
- }
30
- export declare const SessionCircuitBreakerConfigSchema: z.ZodPrefault<z.ZodObject<{
31
- enabled: z.ZodDefault<z.ZodBoolean>;
32
- failureThreshold: z.ZodDefault<z.ZodNumber>;
33
- resetTimeoutMs: z.ZodDefault<z.ZodNumber>;
34
- halfOpenMaxRequests: z.ZodDefault<z.ZodNumber>;
35
- }, z.core.$strip>>;
36
- /** Optional AES-256-GCM encryption of session data at rest. */
37
- export interface SessionEncryptionConfig {
38
- /**
39
- * Enable AES-256-GCM encryption at rest. Default: false.
40
- *
41
- * Evaluate first whether transport TLS, ACLs, private networking and
42
- * infrastructure controls already cover your threat model. Encryption
43
- * protects session data against a compromised Redis instance or its
44
- * disk; it does NOT protect against a compromised application process.
45
- */
46
- enabled: boolean;
47
- /**
48
- * Re-encrypt with the current key version on touch/update (lazy key
49
- * rotation). Default: true.
50
- */
51
- reEncryptOnWrite: boolean;
52
- }
53
- export declare const SessionEncryptionConfigSchema: z.ZodObject<{
54
- enabled: z.ZodDefault<z.ZodBoolean>;
55
- reEncryptOnWrite: z.ZodDefault<z.ZodBoolean>;
56
- }, z.core.$strip>;
57
- /** Metrics collection for session operations. */
58
- export interface SessionMetricsConfig {
59
- /**
60
- * Collect internal session metrics through the injected metrics adapter.
61
- * When no adapter is provided, metrics are a safe no-op regardless.
62
- */
63
- enabled: boolean;
64
- }
65
- export declare const SessionMetricsConfigSchema: z.ZodObject<{
66
- enabled: z.ZodDefault<z.ZodBoolean>;
67
- }, z.core.$strip>;
68
- /** Health check thresholds for the session dependency. */
69
- export interface SessionHealthConfig {
70
- /** PING latency above this (ms) marks the dependency degraded. */
71
- latencyThresholdMs: number;
72
- /** Recent operation error rate above this marks the dependency degraded. */
73
- errorRateThreshold: number;
74
- /** Number of recent operations sampled for the error rate. */
75
- errorWindowSize: number;
76
- }
77
- export declare const SessionHealthConfigSchema: z.ZodObject<{
78
- latencyThresholdMs: z.ZodDefault<z.ZodNumber>;
79
- errorRateThreshold: z.ZodDefault<z.ZodNumber>;
80
- errorWindowSize: z.ZodDefault<z.ZodNumber>;
81
- }, z.core.$strip>;
82
- /** Cookie defaults for the framework-independent cookie manager. */
83
- export interface SessionCookieConfig {
84
- /** Cookie name. Default: 'sid'. */
85
- name: string;
86
- /** Cookie Path attribute. Default: '/'. */
87
- path: string;
88
- /** Cookie Domain attribute (empty = host-only cookie). */
89
- domain?: string;
90
- /** HttpOnly attribute. Default: true. */
91
- httpOnly: boolean;
92
- /** Secure attribute. Default: true. */
93
- secure: boolean;
94
- /** SameSite attribute. Default: 'lax'. */
95
- sameSite: 'strict' | 'lax' | 'none';
96
- /** Max-Age in seconds (falls back to the session TTL when unset). */
97
- maxAge?: number;
98
- }
99
- export declare const SessionCookieConfigSchema: z.ZodObject<{
100
- name: z.ZodDefault<z.ZodString>;
101
- path: z.ZodDefault<z.ZodString>;
102
- domain: z.ZodOptional<z.ZodString>;
103
- httpOnly: z.ZodDefault<z.ZodBoolean>;
104
- secure: z.ZodDefault<z.ZodBoolean>;
105
- sameSite: z.ZodDefault<z.ZodEnum<{
106
- none: "none";
107
- strict: "strict";
108
- lax: "lax";
109
- }>>;
110
- maxAge: z.ZodOptional<z.ZodNumber>;
111
- }, z.core.$strip>;
112
- /** Limits protecting Redis memory, Lua and pipelines. */
113
- export interface SessionLimitsConfig {
114
- /** Maximum serialized metadata size in bytes (reject larger writes). */
115
- maxMetadataSize: number;
116
- /** Maximum sessions fetched per list page. */
117
- maxListPageSize: number;
118
- /** Maximum session keys touched by one Lua script invocation. */
119
- maxBatchSize: number;
120
- /** Maximum concurrent cross-slot pipelines (revokeAll, jti cleanup). */
121
- maxFanOutConcurrency: number;
122
- /** Maximum sessions evicted by a single enforce-limit script call. */
123
- maxEvictionsPerCall: number;
124
- /** Maximum session deletions per user-request path (revokeAll/destroy-all). */
125
- maxSessionsPerUserHardCap: number;
126
- }
127
- export declare const SessionLimitsConfigSchema: z.ZodObject<{
128
- maxMetadataSize: z.ZodDefault<z.ZodNumber>;
129
- maxListPageSize: z.ZodDefault<z.ZodNumber>;
130
- maxBatchSize: z.ZodDefault<z.ZodNumber>;
131
- maxFanOutConcurrency: z.ZodDefault<z.ZodNumber>;
132
- maxEvictionsPerCall: z.ZodDefault<z.ZodNumber>;
133
- maxSessionsPerUserHardCap: z.ZodDefault<z.ZodNumber>;
134
- }, z.core.$strip>;
135
- /** Full parsed session configuration (defaults applied by the schema). */
136
- export interface SessionConfig {
137
- /**
138
- * Master switch. Sessions are NOT enabled implicitly; an application
139
- * must explicitly opt in. Default: false.
140
- */
141
- enabled: boolean;
142
- /** Key namespace for all session keys. Default: 'authcore'. */
143
- namespace: string;
144
- /**
145
- * Raw session token entropy in bytes (32 = 256 bits). Minimum 16
146
- * (128 bits). Default: 32.
147
- */
148
- tokenBytes: number;
149
- /**
150
- * Absolute session lifetime in seconds (the hard maximum). Redis TTL is
151
- * derived from this boundary; touch/rolling NEVER extends past it.
152
- * Default: 7 days.
153
- */
154
- ttl: number;
155
- /**
156
- * Idle timeout in seconds. When null, sessions never expire through
157
- * inactivity. Default: 1 day.
158
- */
159
- idleTimeout: number | null;
160
- /**
161
- * Rolling sessions: valid activity extends the idle boundary (never the
162
- * absolute boundary). Only meaningful when idleTimeout is set.
163
- * Default: true.
164
- */
165
- rolling: boolean;
166
- /**
167
- * Touch throttling in seconds: touch() performs no write when the last
168
- * activity is more recent than this interval. Default: 300.
169
- */
170
- touchInterval: number;
171
- /**
172
- * Maximum concurrent sessions per user. 0 disables the limit.
173
- * Default: 20.
174
- *
175
- * Enforcement is atomic per create (same-slot Lua): the create that
176
- * pushes the count over the limit evicts the oldest excess sessions in
177
- * the same script, so concurrent logins cannot both observe spare
178
- * capacity. For extremely large per-user session counts, eviction is
179
- * bounded per script call and converges over subsequent creates.
180
- */
181
- maxSessionsPerUser: number;
182
- /** Store the device id on creation (advisory binding). Default: false. */
183
- storeDeviceId: boolean;
184
- /** Store the IP address on creation (advisory binding). Default: false. */
185
- storeIpAddress: boolean;
186
- /** Store the user agent on creation (advisory binding). Default: false. */
187
- storeUserAgent: boolean;
188
- /**
189
- * Session binding policy. 'disabled' (default) ignores binding fields;
190
- * 'advisory' reports mismatches on validation; 'strict' rejects with
191
- * reason 'binding_mismatch'. IP addresses change (NAT, mobile), user
192
- * agents are spoofable, device ids may be absent — do not enable strict
193
- * binding lightly.
194
- */
195
- bindingPolicy: 'disabled' | 'advisory' | 'strict';
196
- /**
197
- * Security versioning. When enabled, validate() requires the session's
198
- * securityVersion to match the current version stored at
199
- * `{ns}:security-version:{userId}`. Use setSecurityVersion(userId, v)
200
- * after password changes / MFA resets to invalidate all older sessions.
201
- */
202
- securityVersion: {
203
- enabled: boolean;
204
- };
205
- /**
206
- * Optional global JTI -> userId lookup index.
207
- *
208
- * Default: false. Prefer passing userId to validate()/get()/rotate() —
209
- * the authentication layer already knows it, and the index adds a write,
210
- * a read, a second consistency boundary and a second key family. The
211
- * index is NEVER authoritative: the session record is. It has its own
212
- * TTL, self-heals on read, and a missing entry is not proof of absence
213
- * (see docs/architecture for exact semantics).
214
- */
215
- jtiIndex: {
216
- enabled: boolean;
217
- };
218
- /**
219
- * Check the revocation store during validate(). Off by default:
220
- * in-record revocation (status revoked/consumed) already covers rotation
221
- * reuse and session-level revoke; the revocation store is for external
222
- * JTI revocations (e.g. JWT jti denylists) and adds a second read.
223
- */
224
- checkRevocationStore: boolean;
225
- /** Optional AES-256-GCM encryption at rest. Default: disabled. */
226
- encryption: SessionEncryptionConfig;
227
- /** Optional fail-closed circuit breaker. Default: disabled. */
228
- circuitBreaker: SessionCircuitBreakerConfig;
229
- /** Metrics collection. Default: enabled (no-op without an adapter). */
230
- metrics: SessionMetricsConfig;
231
- /** Health check thresholds. */
232
- health: SessionHealthConfig;
233
- /** Cookie defaults for the framework-independent cookie manager. */
234
- cookie: SessionCookieConfig;
235
- /** Operational limits (memory, Lua, pipeline bounds). */
236
- limits: SessionLimitsConfig;
237
- /**
238
- * Idempotent creation: when SessionCreateInput.idempotencyKey is set,
239
- * store a short-lived claim so retries return the original session.
240
- * Default: false.
241
- */
242
- enableCreateIdempotency: boolean;
243
- /**
244
- * Retain a short-lived consumed tombstone after rotation instead of
245
- * deleting the old record, enabling replay detection. The tombstone is
246
- * bounded by the remaining absolute lifetime. Default: true.
247
- */
248
- retainConsumedTombstones: boolean;
249
- }
250
- export declare const SessionConfigSchema: z.ZodObject<{
251
- enabled: z.ZodDefault<z.ZodBoolean>;
252
- namespace: z.ZodDefault<z.ZodString>;
253
- tokenBytes: z.ZodDefault<z.ZodNumber>;
254
- ttl: z.ZodDefault<z.ZodNumber>;
255
- idleTimeout: z.ZodDefault<z.ZodNullable<z.ZodNumber>>;
256
- rolling: z.ZodDefault<z.ZodBoolean>;
257
- touchInterval: z.ZodDefault<z.ZodNumber>;
258
- maxSessionsPerUser: z.ZodDefault<z.ZodNumber>;
259
- storeDeviceId: z.ZodDefault<z.ZodBoolean>;
260
- storeIpAddress: z.ZodDefault<z.ZodBoolean>;
261
- storeUserAgent: z.ZodDefault<z.ZodBoolean>;
262
- bindingPolicy: z.ZodDefault<z.ZodEnum<{
263
- disabled: "disabled";
264
- advisory: "advisory";
265
- strict: "strict";
266
- }>>;
267
- securityVersion: z.ZodPrefault<z.ZodObject<{
268
- enabled: z.ZodDefault<z.ZodBoolean>;
269
- }, z.core.$strip>>;
270
- jtiIndex: z.ZodPrefault<z.ZodObject<{
271
- enabled: z.ZodDefault<z.ZodBoolean>;
272
- }, z.core.$strip>>;
273
- checkRevocationStore: z.ZodDefault<z.ZodBoolean>;
274
- encryption: z.ZodPrefault<z.ZodObject<{
275
- enabled: z.ZodDefault<z.ZodBoolean>;
276
- reEncryptOnWrite: z.ZodDefault<z.ZodBoolean>;
277
- }, z.core.$strip>>;
278
- circuitBreaker: z.ZodPrefault<z.ZodObject<{
279
- enabled: z.ZodDefault<z.ZodBoolean>;
280
- failureThreshold: z.ZodDefault<z.ZodNumber>;
281
- resetTimeoutMs: z.ZodDefault<z.ZodNumber>;
282
- halfOpenMaxRequests: z.ZodDefault<z.ZodNumber>;
283
- }, z.core.$strip>>;
284
- metrics: z.ZodPrefault<z.ZodObject<{
285
- enabled: z.ZodDefault<z.ZodBoolean>;
286
- }, z.core.$strip>>;
287
- health: z.ZodPrefault<z.ZodObject<{
288
- latencyThresholdMs: z.ZodDefault<z.ZodNumber>;
289
- errorRateThreshold: z.ZodDefault<z.ZodNumber>;
290
- errorWindowSize: z.ZodDefault<z.ZodNumber>;
291
- }, z.core.$strip>>;
292
- cookie: z.ZodPrefault<z.ZodObject<{
293
- name: z.ZodDefault<z.ZodString>;
294
- path: z.ZodDefault<z.ZodString>;
295
- domain: z.ZodOptional<z.ZodString>;
296
- httpOnly: z.ZodDefault<z.ZodBoolean>;
297
- secure: z.ZodDefault<z.ZodBoolean>;
298
- sameSite: z.ZodDefault<z.ZodEnum<{
299
- none: "none";
300
- strict: "strict";
301
- lax: "lax";
302
- }>>;
303
- maxAge: z.ZodOptional<z.ZodNumber>;
304
- }, z.core.$strip>>;
305
- limits: z.ZodPrefault<z.ZodObject<{
306
- maxMetadataSize: z.ZodDefault<z.ZodNumber>;
307
- maxListPageSize: z.ZodDefault<z.ZodNumber>;
308
- maxBatchSize: z.ZodDefault<z.ZodNumber>;
309
- maxFanOutConcurrency: z.ZodDefault<z.ZodNumber>;
310
- maxEvictionsPerCall: z.ZodDefault<z.ZodNumber>;
311
- maxSessionsPerUserHardCap: z.ZodDefault<z.ZodNumber>;
312
- }, z.core.$strip>>;
313
- enableCreateIdempotency: z.ZodDefault<z.ZodBoolean>;
314
- retainConsumedTombstones: z.ZodDefault<z.ZodBoolean>;
315
- }, z.core.$strip>;
316
- /** Recursively makes every config field optional (matches the schema's input shape). */
317
- export type DeepPartial<T> = {
318
- [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
319
- };
320
- /** Raw unparsed config input (all fields optional, nested included). */
321
- export type SessionConfigInput = DeepPartial<SessionConfig>;
322
- /** Raw unparsed config (partial, defaults applied). */
323
- export type PartialSessionConfig = Partial<SessionConfigInput>;
324
- /**
325
- * Parses and validates session configuration.
326
- *
327
- * @throws {SessionConfigurationError} with a safe message on invalid config.
328
- */
329
- export declare function parseSessionConfig(input?: PartialSessionConfig): SessionConfig;
330
- /**
331
- * Returns a redacted copy of the config suitable for logging.
332
- * Strips nothing by default (no secrets are allowed in config), but the
333
- * serializer is explicit so future secret-bearing fields cannot leak.
334
- */
335
- export declare function redactSessionConfig(config: SessionConfig): Record<string, unknown>;