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
package/dist/lock.js DELETED
@@ -1,397 +0,0 @@
1
- import { RedisError } from './errors.js';
2
- import { randomBytes } from 'node:crypto';
3
- import { defaultLogger } from './logger.js';
4
- /**
5
- * Distributed mutual-exclusion lock backed by Redis.
6
- *
7
- * Works in standalone, sentinel and cluster modes. Acquisition uses atomic
8
- * `SET ... PX NX`; release and extension use Lua scripts so only the lock owner
9
- * can release or extend. `withLock` auto-extends the lock at half TTL while the
10
- * critical section runs and always releases afterwards.
11
- *
12
- * @example
13
- * ```ts
14
- * const lock = new DistributedLock(client, { ttl: 30000, retryCount: 5 });
15
- * const acquired = await lock.acquire('order:42');
16
- * if (acquired) {
17
- * try {
18
- * // critical section
19
- * } finally {
20
- * await lock.release('order:42');
21
- * }
22
- * }
23
- * ```
24
- */
25
- export class DistributedLock {
26
- client;
27
- logger;
28
- defaultTTL;
29
- defaultRetryCount;
30
- defaultRetryDelay;
31
- /**
32
- * Creates a distributed lock bound to a Redis client.
33
- *
34
- * @param client - The underlying {@link RedisClientWrapper}.
35
- * @param logger - Optional pino-compatible logger; defaults to `console`.
36
- * @param options - Defaults for `ttl` (ms), `retryCount` and `retryDelay`.
37
- *
38
- * @example
39
- * ```ts
40
- * const lock = new DistributedLock(client, { ttl: 10000, retryCount: 3 });
41
- * ```
42
- */
43
- constructor(client, logger = defaultLogger, options = {}) {
44
- this.client = client;
45
- this.logger = logger.child({ component: 'DistributedLock' });
46
- this.defaultTTL = options.ttl || 30000;
47
- this.defaultRetryCount = options.retryCount || 3;
48
- this.defaultRetryDelay = options.retryDelay || 200;
49
- }
50
- getLockKey(key) {
51
- return `lock:${key}`;
52
- }
53
- generateLockId() {
54
- return randomBytes(16).toString('hex');
55
- }
56
- async executeWithRetry(fn, retryCount = this.defaultRetryCount, retryDelay = this.defaultRetryDelay) {
57
- let lastError = null;
58
- for (let i = 0; i < retryCount; i++) {
59
- try {
60
- return await fn();
61
- }
62
- catch (error) {
63
- lastError = error;
64
- if (i < retryCount - 1) {
65
- const delay = retryDelay * Math.pow(2, i) * (0.5 + Math.random() * 0.5);
66
- await new Promise(resolve => setTimeout(resolve, delay));
67
- }
68
- }
69
- }
70
- throw lastError || new Error('Retry failed');
71
- }
72
- /**
73
- * Attempts to acquire the lock for a key.
74
- *
75
- * Uses atomic `SET lock:<key> <id> PX <ttl> NX` with exponential backoff
76
- * retries. Locks expire automatically after `ttl` ms, so a crashed holder
77
- * never blocks others forever.
78
- *
79
- * @param key - The resource to lock, e.g. `'order:42'` (stored as `lock:order:42`).
80
- * @param ttl - Lock TTL in milliseconds (default: `30000`).
81
- *
82
- * @returns `true` when the lock was acquired.
83
- *
84
- * @example
85
- * ```ts
86
- * const acquired = await lock.acquire('order:42', 10000);
87
- * // acquired === true when lock was successfully acquired
88
- * ```
89
- */
90
- async acquire(key, ttl = this.defaultTTL) {
91
- const lockKey = this.getLockKey(key);
92
- const lockId = this.generateLockId();
93
- return this.executeWithRetry(async () => {
94
- // Using SET with PX and NX for atomic lock acquisition
95
- const result = await this.client.raw.set(lockKey, lockId, 'PX', ttl, 'NX');
96
- return result === 'OK';
97
- });
98
- }
99
- /**
100
- * Releases the lock, but only if this process still owns it.
101
- *
102
- * Uses an atomic Lua check-and-delete so a lock whose TTL expired (and was
103
- * re-acquired by someone else) is never removed by the old owner.
104
- *
105
- * @param key - The locked resource.
106
- *
107
- * @returns `true` if the lock was released, `false` if not owned or missing.
108
- *
109
- * @example
110
- * ```ts
111
- * await lock.release('order:42');
112
- * ```
113
- */
114
- async release(key) {
115
- const lockKey = this.getLockKey(key);
116
- try {
117
- // Use Lua script for atomic check-and-delete
118
- const script = `
119
- if redis.call('get', KEYS[1]) == ARGV[1] then
120
- return redis.call('del', KEYS[1])
121
- else
122
- return 0
123
- end
124
- `;
125
- const lockId = await this.client.raw.get(lockKey);
126
- if (!lockId) {
127
- this.logger.warn('Lock not found for release', { key });
128
- return false;
129
- }
130
- const result = await this.client.raw.eval(script, 1, lockKey, lockId);
131
- return result === 1;
132
- }
133
- catch (error) {
134
- this.logger.error('Failed to release lock', { key, error });
135
- return false;
136
- }
137
- }
138
- /**
139
- * Force-releases a lock without checking ownership.
140
- *
141
- * Use with care: only for emergency cleanup or when the holder is known to
142
- * be gone. This is what `withLock` falls back to when a normal release fails.
143
- *
144
- * @param key - The locked resource.
145
- *
146
- * @returns `true` if a lock existed and was deleted.
147
- *
148
- * @example
149
- * ```ts
150
- * await lock.releaseForce('order:42');
151
- * ```
152
- */
153
- async releaseForce(key) {
154
- const lockKey = this.getLockKey(key);
155
- const result = await this.client.raw.del(lockKey);
156
- return result === 1;
157
- }
158
- /**
159
- * Extends the TTL of a lock this process still owns.
160
- *
161
- * Uses an atomic Lua script so a re-acquired lock is never extended by the
162
- * old owner.
163
- *
164
- * @param key - The locked resource.
165
- * @param ttl - New TTL in milliseconds (default: `30000`).
166
- *
167
- * @returns `true` if the lock was extended.
168
- *
169
- * @example
170
- * ```ts
171
- * const extended = await lock.extend('order:42', 30000);
172
- * // extended === true when lock TTL was renewed
173
- * ```
174
- */
175
- async extend(key, ttl = this.defaultTTL) {
176
- const lockKey = this.getLockKey(key);
177
- const script = `
178
- if redis.call('get', KEYS[1]) == ARGV[1] then
179
- return redis.call('pexpire', KEYS[1], ARGV[2])
180
- else
181
- return 0
182
- end
183
- `;
184
- try {
185
- const lockId = await this.client.raw.get(lockKey);
186
- if (!lockId) {
187
- return false;
188
- }
189
- const result = await this.client.raw.eval(script, 1, lockKey, lockId, ttl);
190
- return result === 1;
191
- }
192
- catch (error) {
193
- this.logger.error('Failed to extend lock', { key, error });
194
- return false;
195
- }
196
- }
197
- /**
198
- * Runs a critical section while holding a lock.
199
- *
200
- * Acquires the lock (with retries), auto-extends it at half TTL while `fn`
201
- * runs, detects a lost lock, and always releases afterwards (force-releasing
202
- * if a normal release fails).
203
- *
204
- * @param key - The resource to lock.
205
- * @param fn - The critical section to run exclusively.
206
- * @param options - Per-call `ttl` (ms), `retryCount`, `retryDelay`.
207
- *
208
- * @returns The return value of `fn`.
209
- *
210
- * @throws {@link RedisError} with code `LOCK_ACQUISITION_FAILED` when the lock
211
- * cannot be acquired, or `LOCK_LOST` when the lock expired mid-execution.
212
- *
213
- * @example
214
- * ```ts
215
- * const result = await lock.withLock('inventory:sku-1', async () => {
216
- * return await updateStock();
217
- * });
218
- * ```
219
- */
220
- async withLock(key, fn, options = {}) {
221
- const ttl = options.ttl || this.defaultTTL;
222
- const retryCount = options.retryCount || this.defaultRetryCount;
223
- const retryDelay = options.retryDelay || this.defaultRetryDelay;
224
- // Try to acquire the lock with retries
225
- const acquired = await this.acquire(key, ttl);
226
- if (!acquired) {
227
- throw new RedisError(`Failed to acquire lock for key: ${key} after ${retryCount} attempts`, 'LOCK_ACQUISITION_FAILED');
228
- }
229
- let extensionTimer = null;
230
- let lockRenewed = true;
231
- try {
232
- // Start auto-extension timer at half TTL
233
- const extendInterval = Math.floor(ttl / 2);
234
- let isExtending = false;
235
- const extendLock = async () => {
236
- if (isExtending || !lockRenewed)
237
- return;
238
- isExtending = true;
239
- try {
240
- const extended = await this.extend(key, ttl);
241
- if (!extended) {
242
- lockRenewed = false;
243
- this.logger.warn('Lock extension failed', { key });
244
- }
245
- }
246
- catch (error) {
247
- this.logger.error('Lock extension error', { key, error });
248
- lockRenewed = false;
249
- }
250
- finally {
251
- isExtending = false;
252
- }
253
- };
254
- // Schedule auto-extension
255
- extensionTimer = setInterval(() => {
256
- extendLock().catch((error) => {
257
- this.logger.error('Extension interval error', { key, error });
258
- });
259
- }, extendInterval);
260
- // Execute the function
261
- const result = await fn();
262
- // Check if lock was maintained during execution
263
- if (!lockRenewed) {
264
- throw new RedisError(`Lock was lost during execution for key: ${key}`, 'LOCK_LOST');
265
- }
266
- return result;
267
- }
268
- catch (error) {
269
- this.logger.error('Error in locked operation', { key, error });
270
- throw error;
271
- }
272
- finally {
273
- // Clean up extension timer
274
- if (extensionTimer) {
275
- clearInterval(extensionTimer);
276
- extensionTimer = null;
277
- }
278
- // Release the lock
279
- try {
280
- await this.release(key);
281
- }
282
- catch (releaseError) {
283
- this.logger.error('Failed to release lock', { key, releaseError });
284
- try {
285
- await this.releaseForce(key);
286
- }
287
- catch (forceError) {
288
- this.logger.error('Failed to force release lock', { key, forceError });
289
- }
290
- }
291
- }
292
- }
293
- /**
294
- * Checks whether a lock is currently held.
295
- *
296
- * @param key - The locked resource.
297
- *
298
- * @returns `true` if the lock exists (held by anyone).
299
- *
300
- * @example
301
- * ```ts
302
- * const busy = await lock.isLocked('order:42');
303
- * ```
304
- */
305
- async isLocked(key) {
306
- const lockKey = this.getLockKey(key);
307
- const exists = await this.client.raw.exists(lockKey);
308
- return exists === 1;
309
- }
310
- /**
311
- * Returns details about a lock.
312
- *
313
- * @param key - The locked resource.
314
- *
315
- * @returns `{ locked: false }` when not held, otherwise `{ locked: true, ttl, lockId }`.
316
- *
317
- * @example
318
- * ```ts
319
- * const info = await lock.getLockInfo('order:42');
320
- * // { locked: true, ttl: 29, lockId: 'a1b2c3...' }
321
- * ```
322
- */
323
- async getLockInfo(key) {
324
- const lockKey = this.getLockKey(key);
325
- const exists = await this.client.raw.exists(lockKey);
326
- if (!exists) {
327
- return { locked: false };
328
- }
329
- const [lockId, ttl] = await Promise.all([
330
- this.client.raw.get(lockKey),
331
- this.client.raw.ttl(lockKey),
332
- ]);
333
- // Build the result object with proper undefined handling
334
- const result = { locked: true };
335
- if (lockId !== null && lockId !== undefined) {
336
- result.lockId = lockId;
337
- }
338
- if (ttl !== null && ttl !== undefined && ttl > 0) {
339
- result.ttl = ttl;
340
- }
341
- return result;
342
- }
343
- /**
344
- * Returns the owner id of a lock.
345
- *
346
- * @param key - The locked resource.
347
- *
348
- * @returns The lock id (random hex token), or `null` when not held.
349
- *
350
- * @example
351
- * ```ts
352
- * const owner = await lock.getLockOwner('order:42');
353
- * ```
354
- */
355
- async getLockOwner(key) {
356
- const lockKey = this.getLockKey(key);
357
- return this.client.raw.get(lockKey);
358
- }
359
- /**
360
- * Returns the remaining TTL of a lock in seconds.
361
- *
362
- * @param key - The locked resource.
363
- *
364
- * @returns Remaining seconds (`0` when not held or expired).
365
- *
366
- * @example
367
- * ```ts
368
- * const remaining = await lock.getLockTTL('order:42');
369
- * ```
370
- */
371
- async getLockTTL(key) {
372
- const lockKey = this.getLockKey(key);
373
- const ttl = await this.client.raw.ttl(lockKey);
374
- return ttl > 0 ? ttl : 0;
375
- }
376
- // Clean up all locks (for testing or emergency)
377
- /**
378
- * Deletes every lock key (`lock:*`) from Redis.
379
- *
380
- * Intended for tests and emergency recovery only.
381
- *
382
- * @returns The number of deleted locks.
383
- *
384
- * @example
385
- * ```ts
386
- * const removed = await lock.cleanupAll();
387
- * ```
388
- */
389
- async cleanupAll() {
390
- let deleted = 0;
391
- for await (const key of this.client.scanIterator('lock:*')) {
392
- const result = await this.client.raw.del(key);
393
- deleted += result;
394
- }
395
- return deleted;
396
- }
397
- }
package/dist/logger.d.ts DELETED
@@ -1,12 +0,0 @@
1
- export type LogMeta = Record<string, unknown>;
2
- export interface LoggerLike {
3
- trace(message: string, meta?: LogMeta): void;
4
- debug(message: string, meta?: LogMeta): void;
5
- info(message: string, meta?: LogMeta): void;
6
- warn(message: string, meta?: LogMeta): void;
7
- error(message: string, meta?: LogMeta): void;
8
- fatal(message: string, meta?: LogMeta): void;
9
- child(bindings: LogMeta): LoggerLike;
10
- }
11
- export declare const defaultLogger: LoggerLike;
12
- export declare function createConsoleLogger(bindings?: LogMeta): LoggerLike;
package/dist/logger.js DELETED
@@ -1,40 +0,0 @@
1
- function mergeMeta(bindings, meta) {
2
- return {
3
- ...bindings,
4
- ...(meta ?? {}),
5
- };
6
- }
7
- class ConsoleLogger {
8
- bindings;
9
- constructor(bindings = {}) {
10
- this.bindings = bindings;
11
- }
12
- trace(message, meta) {
13
- console.trace(message, mergeMeta(this.bindings, meta));
14
- }
15
- debug(message, meta) {
16
- console.debug(message, mergeMeta(this.bindings, meta));
17
- }
18
- info(message, meta) {
19
- console.info(message, mergeMeta(this.bindings, meta));
20
- }
21
- warn(message, meta) {
22
- console.warn(message, mergeMeta(this.bindings, meta));
23
- }
24
- error(message, meta) {
25
- console.error(message, mergeMeta(this.bindings, meta));
26
- }
27
- fatal(message, meta) {
28
- console.error(message, mergeMeta(this.bindings, meta));
29
- }
30
- child(bindings) {
31
- return new ConsoleLogger({
32
- ...this.bindings,
33
- ...bindings,
34
- });
35
- }
36
- }
37
- export const defaultLogger = new ConsoleLogger();
38
- export function createConsoleLogger(bindings) {
39
- return new ConsoleLogger(bindings);
40
- }