@nestjs-redisx/locks 1.0.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 (33) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +50 -0
  3. package/dist/index.d.ts +9 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +635 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/index.mjs +625 -0
  8. package/dist/index.mjs.map +1 -0
  9. package/dist/lock/api/decorators/with-lock.decorator.d.ts +105 -0
  10. package/dist/lock/api/decorators/with-lock.decorator.d.ts.map +1 -0
  11. package/dist/lock/application/ports/lock-service.port.d.ts +120 -0
  12. package/dist/lock/application/ports/lock-service.port.d.ts.map +1 -0
  13. package/dist/lock/application/ports/lock-store.port.d.ts +63 -0
  14. package/dist/lock/application/ports/lock-store.port.d.ts.map +1 -0
  15. package/dist/lock/application/services/lock-decorator-initializer.service.d.ts +19 -0
  16. package/dist/lock/application/services/lock-decorator-initializer.service.d.ts.map +1 -0
  17. package/dist/lock/application/services/lock.service.d.ts +90 -0
  18. package/dist/lock/application/services/lock.service.d.ts.map +1 -0
  19. package/dist/lock/domain/entities/lock.entity.d.ts +130 -0
  20. package/dist/lock/domain/entities/lock.entity.d.ts.map +1 -0
  21. package/dist/lock/infrastructure/adapters/redis-lock-store.adapter.d.ts +45 -0
  22. package/dist/lock/infrastructure/adapters/redis-lock-store.adapter.d.ts.map +1 -0
  23. package/dist/lock/infrastructure/scripts/lua-scripts.d.ts +24 -0
  24. package/dist/lock/infrastructure/scripts/lua-scripts.d.ts.map +1 -0
  25. package/dist/locks.plugin.d.ts +41 -0
  26. package/dist/locks.plugin.d.ts.map +1 -0
  27. package/dist/shared/constants/index.d.ts +13 -0
  28. package/dist/shared/constants/index.d.ts.map +1 -0
  29. package/dist/shared/errors/index.d.ts +64 -0
  30. package/dist/shared/errors/index.d.ts.map +1 -0
  31. package/dist/shared/types/index.d.ts +97 -0
  32. package/dist/shared/types/index.d.ts.map +1 -0
  33. package/package.json +78 -0
package/dist/index.mjs ADDED
@@ -0,0 +1,625 @@
1
+ import { Reflector } from '@nestjs/core';
2
+ import { Logger, Injectable, Inject, Optional } from '@nestjs/common';
3
+ import 'reflect-metadata';
4
+ import { REDIS_DRIVER, RedisXError, ErrorCode } from '@nestjs-redisx/core';
5
+
6
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
7
+ var __decorateClass = (decorators, target, key, kind) => {
8
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
9
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
10
+ if (decorator = decorators[i])
11
+ result = (decorator(result)) || result;
12
+ return result;
13
+ };
14
+ var __decorateParam = (index, decorator) => (target, key) => decorator(target, key, index);
15
+
16
+ // src/shared/constants/index.ts
17
+ var LOCKS_PLUGIN_OPTIONS = /* @__PURE__ */ Symbol.for("LOCKS_PLUGIN_OPTIONS");
18
+ var LOCK_SERVICE = /* @__PURE__ */ Symbol.for("LOCK_SERVICE");
19
+ var LOCK_STORE = /* @__PURE__ */ Symbol.for("LOCK_STORE");
20
+ var LockError = class extends RedisXError {
21
+ constructor(message, code, lockKey, cause) {
22
+ super(message, code, cause, { lockKey });
23
+ this.lockKey = lockKey;
24
+ }
25
+ };
26
+ var LockAcquisitionError = class extends LockError {
27
+ constructor(key, reason, cause) {
28
+ super(`Failed to acquire lock "${key}": ${reason}`, reason === "timeout" ? ErrorCode.LOCK_ACQUISITION_TIMEOUT : ErrorCode.LOCK_ACQUISITION_FAILED, key, cause);
29
+ this.reason = reason;
30
+ }
31
+ };
32
+ var LockNotOwnedError = class extends LockError {
33
+ constructor(key, token) {
34
+ super(`Lock "${key}" not owned by token "${token}"`, ErrorCode.LOCK_NOT_OWNED, key);
35
+ this.token = token;
36
+ }
37
+ toJSON() {
38
+ return {
39
+ ...super.toJSON(),
40
+ token: this.token
41
+ };
42
+ }
43
+ };
44
+ var LockExtensionError = class extends LockError {
45
+ constructor(key, token, cause) {
46
+ super(`Failed to extend lock "${key}"`, ErrorCode.LOCK_EXTENSION_FAILED, key, cause);
47
+ this.token = token;
48
+ }
49
+ toJSON() {
50
+ return {
51
+ ...super.toJSON(),
52
+ token: this.token
53
+ };
54
+ }
55
+ };
56
+ var LockExpiredError = class extends LockError {
57
+ constructor(key) {
58
+ super(`Lock "${key}" expired`, ErrorCode.LOCK_EXPIRED, key);
59
+ }
60
+ };
61
+
62
+ // src/lock/api/decorators/with-lock.decorator.ts
63
+ var logger = new Logger("WithLock");
64
+ var WITH_LOCK_OPTIONS = /* @__PURE__ */ Symbol.for("WITH_LOCK_OPTIONS");
65
+ var globalLockServiceGetter = null;
66
+ function registerLockServiceGetter(getter) {
67
+ globalLockServiceGetter = getter;
68
+ }
69
+ function WithLock(options) {
70
+ return (target, propertyKey, descriptor) => {
71
+ const originalMethod = descriptor.value;
72
+ descriptor.value = async function(...args) {
73
+ if (!globalLockServiceGetter) {
74
+ logger.warn(`@WithLock: LockService not yet available, executing method without lock`);
75
+ return originalMethod.apply(this, args);
76
+ }
77
+ const lockService = globalLockServiceGetter();
78
+ if (!lockService) {
79
+ logger.warn(`@WithLock: LockService getter returned null, executing method without lock`);
80
+ return originalMethod.apply(this, args);
81
+ }
82
+ const key = buildLockKey(args, options);
83
+ let lock = null;
84
+ try {
85
+ lock = await lockService.acquire(key, {
86
+ ttl: options.ttl,
87
+ waitTimeout: options.waitTimeout,
88
+ autoRenew: options.autoRenew
89
+ });
90
+ const result = await originalMethod.apply(this, args);
91
+ return result;
92
+ } catch (error) {
93
+ if (error instanceof LockAcquisitionError) {
94
+ return handleLockFailed(key, options, error);
95
+ }
96
+ throw error;
97
+ } finally {
98
+ if (lock) {
99
+ await lock.release().catch((err) => {
100
+ logger.error(`@WithLock: Failed to release lock ${key}:`, err);
101
+ });
102
+ }
103
+ }
104
+ };
105
+ Object.defineProperty(descriptor.value, "name", {
106
+ value: originalMethod.name,
107
+ writable: false
108
+ });
109
+ Reflect.defineMetadata(WITH_LOCK_OPTIONS, options, descriptor.value);
110
+ return descriptor;
111
+ };
112
+ }
113
+ function buildLockKey(args, options) {
114
+ if (typeof options.key === "function") {
115
+ return options.key(...args);
116
+ }
117
+ return interpolateKey(options.key, args);
118
+ }
119
+ function interpolateKey(template, args) {
120
+ return template.replace(/\{(\d+)(?:\.(\w+))?\}/g, (_, index, prop) => {
121
+ const arg = args[Number(index)];
122
+ if (prop && typeof arg === "object" && arg !== null) {
123
+ return String(arg[prop]);
124
+ }
125
+ return String(arg);
126
+ });
127
+ }
128
+ function handleLockFailed(key, options, error) {
129
+ const handler = options.onLockFailed ?? "throw";
130
+ if (handler === "throw") {
131
+ throw error;
132
+ }
133
+ if (handler === "skip") {
134
+ return void 0;
135
+ }
136
+ if (typeof handler === "function") {
137
+ throw handler(key);
138
+ }
139
+ throw error;
140
+ }
141
+
142
+ // src/lock/application/services/lock-decorator-initializer.service.ts
143
+ var LockDecoratorInitializerService = class {
144
+ constructor(lockService) {
145
+ this.lockService = lockService;
146
+ }
147
+ logger = new Logger(LockDecoratorInitializerService.name);
148
+ /**
149
+ * Called after all modules are initialized.
150
+ * Registers lock service getter for @WithLock decorator.
151
+ */
152
+ // eslint-disable-next-line @typescript-eslint/require-await
153
+ async onModuleInit() {
154
+ this.logger.debug("Registering LockService getter for @WithLock decorator");
155
+ registerLockServiceGetter(() => this.lockService);
156
+ this.logger.log("@WithLock decorator initialized and ready to use");
157
+ }
158
+ };
159
+ LockDecoratorInitializerService = __decorateClass([
160
+ Injectable(),
161
+ __decorateParam(0, Inject(LOCK_SERVICE))
162
+ ], LockDecoratorInitializerService);
163
+
164
+ // src/lock/domain/entities/lock.entity.ts
165
+ var Lock = class {
166
+ /**
167
+ * Creates a new Lock instance.
168
+ *
169
+ * @param key - Lock key in Redis
170
+ * @param token - Unique ownership token
171
+ * @param ttl - Time-to-live in milliseconds
172
+ * @param store - Lock store for persistence operations
173
+ */
174
+ constructor(key, token, ttl, store) {
175
+ this.store = store;
176
+ this.key = key;
177
+ this.token = token;
178
+ this.ttl = ttl;
179
+ this.acquiredAt = /* @__PURE__ */ new Date();
180
+ this._expiresAt = new Date(Date.now() + ttl);
181
+ }
182
+ key;
183
+ token;
184
+ ttl;
185
+ acquiredAt;
186
+ _expiresAt;
187
+ autoRenewTimer = null;
188
+ released = false;
189
+ /**
190
+ * Gets the expiration timestamp.
191
+ */
192
+ get expiresAt() {
193
+ return this._expiresAt;
194
+ }
195
+ /**
196
+ * Checks if auto-renewal is active.
197
+ */
198
+ get isAutoRenewing() {
199
+ return this.autoRenewTimer !== null;
200
+ }
201
+ /**
202
+ * Releases the lock.
203
+ *
204
+ * Stops auto-renewal and removes the lock from Redis.
205
+ * Idempotent - can be called multiple times safely.
206
+ *
207
+ * @throws {LockNotOwnedError} If lock is not owned by this token
208
+ */
209
+ async release() {
210
+ if (this.released) {
211
+ return;
212
+ }
213
+ this.stopAutoRenew();
214
+ const success = await this.store.release(this.key, this.token);
215
+ if (!success) {
216
+ throw new LockNotOwnedError(this.key, this.token);
217
+ }
218
+ this.released = true;
219
+ }
220
+ /**
221
+ * Extends the lock TTL.
222
+ *
223
+ * @param ttl - New TTL in milliseconds
224
+ * @throws {LockNotOwnedError} If lock was already released
225
+ * @throws {LockExtensionError} If extension fails (lock expired or not owned)
226
+ */
227
+ async extend(ttl) {
228
+ if (this.released) {
229
+ throw new LockNotOwnedError(this.key, this.token);
230
+ }
231
+ const success = await this.store.extend(this.key, this.token, ttl);
232
+ if (!success) {
233
+ throw new LockExtensionError(this.key, this.token);
234
+ }
235
+ this._expiresAt = new Date(Date.now() + ttl);
236
+ }
237
+ /**
238
+ * Checks if lock is still held.
239
+ *
240
+ * @returns False if released, otherwise checks Redis
241
+ */
242
+ async isHeld() {
243
+ if (this.released) {
244
+ return false;
245
+ }
246
+ return this.store.isHeldBy(this.key, this.token);
247
+ }
248
+ /**
249
+ * Starts automatic lock renewal.
250
+ *
251
+ * The lock will be extended at the specified interval
252
+ * until stopAutoRenew() is called or extension fails.
253
+ *
254
+ * @param intervalMs - Renewal interval in milliseconds
255
+ */
256
+ startAutoRenew(intervalMs) {
257
+ if (this.autoRenewTimer) {
258
+ return;
259
+ }
260
+ this.autoRenewTimer = setInterval(async () => {
261
+ try {
262
+ await this.extend(this.ttl);
263
+ } catch {
264
+ this.stopAutoRenew();
265
+ }
266
+ }, intervalMs);
267
+ if (this.autoRenewTimer.unref) {
268
+ this.autoRenewTimer.unref();
269
+ }
270
+ }
271
+ /**
272
+ * Stops automatic lock renewal.
273
+ *
274
+ * Safe to call multiple times.
275
+ */
276
+ stopAutoRenew() {
277
+ if (this.autoRenewTimer) {
278
+ clearInterval(this.autoRenewTimer);
279
+ this.autoRenewTimer = null;
280
+ }
281
+ }
282
+ };
283
+
284
+ // src/lock/application/services/lock.service.ts
285
+ var METRICS_SERVICE = /* @__PURE__ */ Symbol.for("METRICS_SERVICE");
286
+ var TRACING_SERVICE = /* @__PURE__ */ Symbol.for("TRACING_SERVICE");
287
+ var LockService = class {
288
+ constructor(config, store, metrics, tracing) {
289
+ this.config = config;
290
+ this.store = store;
291
+ this.metrics = metrics;
292
+ this.tracing = tracing;
293
+ }
294
+ logger = new Logger(LockService.name);
295
+ activeLocks = /* @__PURE__ */ new Set();
296
+ /**
297
+ * Lifecycle hook: releases all active locks on shutdown.
298
+ */
299
+ async onModuleDestroy() {
300
+ const releasePromises = Array.from(this.activeLocks).map(
301
+ (lock) => lock.release().catch((error) => {
302
+ this.logger.error(`Failed to release lock ${lock.key} during shutdown:`, error);
303
+ })
304
+ );
305
+ await Promise.all(releasePromises);
306
+ this.activeLocks.clear();
307
+ }
308
+ /**
309
+ * Acquires lock with exponential backoff retry.
310
+ */
311
+ async acquire(key, options = {}) {
312
+ const span = this.tracing?.startSpan("lock.acquire", {
313
+ kind: "INTERNAL",
314
+ attributes: { "lock.key": key, "lock.ttl": options.ttl }
315
+ });
316
+ const fullKey = this.buildKey(key);
317
+ const ttl = this.resolveTtl(options.ttl);
318
+ const token = this.generateToken();
319
+ const retry = this.resolveRetryConfig(options);
320
+ const startTime = Date.now();
321
+ let delay = retry.initialDelay;
322
+ try {
323
+ for (let attempt = 0; attempt <= retry.maxRetries; attempt++) {
324
+ const acquired = await this.store.acquire(fullKey, token, ttl);
325
+ if (acquired) {
326
+ const waitDuration = (Date.now() - startTime) / 1e3;
327
+ this.metrics?.observeHistogram("redisx_lock_wait_duration_seconds", waitDuration);
328
+ this.metrics?.incrementCounter("redisx_lock_acquisitions_total", { status: "acquired" });
329
+ this.metrics?.incrementGauge("redisx_locks_active");
330
+ span?.setAttribute("lock.acquired", true);
331
+ span?.setAttribute("lock.attempts", attempt + 1);
332
+ span?.setStatus("OK");
333
+ const lock = this.createLock(fullKey, token, ttl, options);
334
+ this.activeLocks.add(lock);
335
+ return lock;
336
+ }
337
+ if (attempt === retry.maxRetries) {
338
+ this.metrics?.incrementCounter("redisx_lock_acquisitions_total", { status: "failed" });
339
+ span?.setAttribute("lock.acquired", false);
340
+ span?.setAttribute("lock.attempts", attempt + 1);
341
+ span?.setStatus("ERROR");
342
+ throw new LockAcquisitionError(key, "timeout");
343
+ }
344
+ await this.sleep(delay);
345
+ delay = Math.min(delay * retry.multiplier, retry.maxDelay);
346
+ }
347
+ this.metrics?.incrementCounter("redisx_lock_acquisitions_total", { status: "failed" });
348
+ throw new LockAcquisitionError(key, "timeout");
349
+ } catch (error) {
350
+ span?.recordException(error);
351
+ span?.setStatus("ERROR");
352
+ throw error;
353
+ } finally {
354
+ span?.end();
355
+ }
356
+ }
357
+ /**
358
+ * Tries to acquire lock once without retry.
359
+ */
360
+ async tryAcquire(key, options = {}) {
361
+ const fullKey = this.buildKey(key);
362
+ const ttl = this.resolveTtl(options.ttl);
363
+ const token = this.generateToken();
364
+ const acquired = await this.store.acquire(fullKey, token, ttl);
365
+ if (!acquired) {
366
+ this.metrics?.incrementCounter("redisx_lock_acquisitions_total", { status: "failed" });
367
+ return null;
368
+ }
369
+ this.metrics?.incrementCounter("redisx_lock_acquisitions_total", { status: "acquired" });
370
+ this.metrics?.incrementGauge("redisx_locks_active");
371
+ const lock = this.createLock(fullKey, token, ttl, options);
372
+ this.activeLocks.add(lock);
373
+ return lock;
374
+ }
375
+ /**
376
+ * Executes function with automatic lock management.
377
+ */
378
+ async withLock(key, fn, options = {}) {
379
+ const lock = await this.acquire(key, options);
380
+ const holdStart = Date.now();
381
+ try {
382
+ return await fn();
383
+ } finally {
384
+ const holdDuration = (Date.now() - holdStart) / 1e3;
385
+ this.metrics?.observeHistogram("redisx_lock_hold_duration_seconds", holdDuration);
386
+ this.metrics?.decrementGauge("redisx_locks_active");
387
+ await lock.release().catch((error) => {
388
+ this.logger.error(`Lock release failed for ${key}:`, error);
389
+ }).finally(() => {
390
+ this.activeLocks.delete(lock);
391
+ });
392
+ }
393
+ }
394
+ /**
395
+ * Checks if key is locked.
396
+ */
397
+ async isLocked(key) {
398
+ const fullKey = this.buildKey(key);
399
+ return this.store.exists(fullKey);
400
+ }
401
+ /**
402
+ * Force releases lock without ownership check.
403
+ */
404
+ async forceRelease(key) {
405
+ const fullKey = this.buildKey(key);
406
+ return this.store.forceRelease(fullKey);
407
+ }
408
+ /**
409
+ * Creates lock instance with optional auto-renewal.
410
+ */
411
+ createLock(fullKey, token, ttl, options) {
412
+ const lock = new Lock(fullKey, token, ttl, this.store);
413
+ const autoRenewEnabled = options.autoRenew ?? this.config.autoRenew?.enabled ?? true;
414
+ if (autoRenewEnabled) {
415
+ const intervalFraction = this.config.autoRenew?.intervalFraction ?? 0.5;
416
+ const interval = ttl * intervalFraction;
417
+ lock.startAutoRenew(interval);
418
+ }
419
+ return lock;
420
+ }
421
+ /**
422
+ * Builds full lock key with prefix.
423
+ */
424
+ buildKey(key) {
425
+ const prefix = this.config.keyPrefix ?? "_lock:";
426
+ return `${prefix}${key}`;
427
+ }
428
+ /**
429
+ * Generates unique lock token.
430
+ */
431
+ generateToken() {
432
+ return `${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
433
+ }
434
+ /**
435
+ * Resolves TTL with defaults and limits.
436
+ */
437
+ resolveTtl(ttl) {
438
+ const resolvedTtl = ttl ?? this.config.defaultTtl ?? 3e4;
439
+ const maxTtl = this.config.maxTtl ?? 3e5;
440
+ return Math.min(resolvedTtl, maxTtl);
441
+ }
442
+ /**
443
+ * Resolves retry configuration.
444
+ */
445
+ resolveRetryConfig(options) {
446
+ return {
447
+ maxRetries: options.retry?.maxRetries ?? this.config.retry?.maxRetries ?? 3,
448
+ initialDelay: options.retry?.initialDelay ?? this.config.retry?.initialDelay ?? 100,
449
+ maxDelay: this.config.retry?.maxDelay ?? 3e3,
450
+ multiplier: this.config.retry?.multiplier ?? 2
451
+ };
452
+ }
453
+ /**
454
+ * Sleeps for specified milliseconds.
455
+ */
456
+ sleep(ms) {
457
+ return new Promise((resolve) => setTimeout(resolve, ms));
458
+ }
459
+ };
460
+ LockService = __decorateClass([
461
+ Injectable(),
462
+ __decorateParam(0, Inject(LOCKS_PLUGIN_OPTIONS)),
463
+ __decorateParam(1, Inject(LOCK_STORE)),
464
+ __decorateParam(2, Optional()),
465
+ __decorateParam(2, Inject(METRICS_SERVICE)),
466
+ __decorateParam(3, Optional()),
467
+ __decorateParam(3, Inject(TRACING_SERVICE))
468
+ ], LockService);
469
+
470
+ // src/lock/infrastructure/scripts/lua-scripts.ts
471
+ var RELEASE_LOCK_SCRIPT = `
472
+ if redis.call("get", KEYS[1]) == ARGV[1] then
473
+ return redis.call("del", KEYS[1])
474
+ else
475
+ return 0
476
+ end
477
+ `.trim();
478
+ var EXTEND_LOCK_SCRIPT = `
479
+ if redis.call("get", KEYS[1]) == ARGV[1] then
480
+ return redis.call("pexpire", KEYS[1], ARGV[2])
481
+ else
482
+ return 0
483
+ end
484
+ `.trim();
485
+
486
+ // src/lock/infrastructure/adapters/redis-lock-store.adapter.ts
487
+ var RedisLockStoreAdapter = class {
488
+ constructor(driver) {
489
+ this.driver = driver;
490
+ }
491
+ releaseSha = null;
492
+ extendSha = null;
493
+ /**
494
+ * Lifecycle hook: loads Lua scripts into Redis on initialization.
495
+ */
496
+ async onModuleInit() {
497
+ this.releaseSha = await this.driver.scriptLoad(RELEASE_LOCK_SCRIPT);
498
+ this.extendSha = await this.driver.scriptLoad(EXTEND_LOCK_SCRIPT);
499
+ }
500
+ /**
501
+ * Acquires lock using SET NX PX.
502
+ */
503
+ async acquire(key, token, ttlMs) {
504
+ const result = await this.driver.set(key, token, {
505
+ nx: true,
506
+ // Only set if key doesn't exist
507
+ px: ttlMs
508
+ // TTL in milliseconds
509
+ });
510
+ return result === "OK";
511
+ }
512
+ /**
513
+ * Releases lock if owned by token (Lua script).
514
+ */
515
+ async release(key, token) {
516
+ if (!this.releaseSha) {
517
+ this.releaseSha = await this.driver.scriptLoad(RELEASE_LOCK_SCRIPT);
518
+ }
519
+ const result = await this.driver.evalsha(this.releaseSha, [key], [token]);
520
+ return result === 1;
521
+ }
522
+ /**
523
+ * Extends lock TTL if owned by token (Lua script).
524
+ */
525
+ async extend(key, token, ttlMs) {
526
+ if (!this.extendSha) {
527
+ this.extendSha = await this.driver.scriptLoad(EXTEND_LOCK_SCRIPT);
528
+ }
529
+ const result = await this.driver.evalsha(this.extendSha, [key], [token, ttlMs]);
530
+ return result === 1;
531
+ }
532
+ /**
533
+ * Checks if lock key exists.
534
+ */
535
+ async exists(key) {
536
+ const count = await this.driver.exists(key);
537
+ return count > 0;
538
+ }
539
+ /**
540
+ * Checks if lock is held by specific token.
541
+ */
542
+ async isHeldBy(key, token) {
543
+ const value = await this.driver.get(key);
544
+ return value === token;
545
+ }
546
+ /**
547
+ * Force removes lock without ownership check.
548
+ */
549
+ async forceRelease(key) {
550
+ const count = await this.driver.del(key);
551
+ return count > 0;
552
+ }
553
+ };
554
+ RedisLockStoreAdapter = __decorateClass([
555
+ Injectable(),
556
+ __decorateParam(0, Inject(REDIS_DRIVER))
557
+ ], RedisLockStoreAdapter);
558
+
559
+ // src/locks.plugin.ts
560
+ var DEFAULT_LOCKS_CONFIG = {
561
+ defaultTtl: 3e4,
562
+ maxTtl: 3e5,
563
+ keyPrefix: "_lock:",
564
+ retry: {
565
+ maxRetries: 3,
566
+ initialDelay: 100,
567
+ maxDelay: 3e3,
568
+ multiplier: 2
569
+ },
570
+ autoRenew: {
571
+ enabled: true,
572
+ intervalFraction: 0.5
573
+ }
574
+ };
575
+ var LocksPlugin = class {
576
+ constructor(options = {}) {
577
+ this.options = options;
578
+ }
579
+ name = "locks";
580
+ version = "0.1.0";
581
+ description = "Distributed locking with auto-renewal and retry strategies";
582
+ getProviders() {
583
+ const config = {
584
+ defaultTtl: this.options.defaultTtl ?? DEFAULT_LOCKS_CONFIG.defaultTtl,
585
+ maxTtl: this.options.maxTtl ?? DEFAULT_LOCKS_CONFIG.maxTtl,
586
+ keyPrefix: this.options.keyPrefix ?? DEFAULT_LOCKS_CONFIG.keyPrefix,
587
+ retry: {
588
+ ...DEFAULT_LOCKS_CONFIG.retry,
589
+ ...this.options.retry
590
+ },
591
+ autoRenew: {
592
+ ...DEFAULT_LOCKS_CONFIG.autoRenew,
593
+ ...this.options.autoRenew
594
+ }
595
+ };
596
+ return [
597
+ // Configuration
598
+ {
599
+ provide: LOCKS_PLUGIN_OPTIONS,
600
+ useValue: config
601
+ },
602
+ // Store adapter
603
+ {
604
+ provide: LOCK_STORE,
605
+ useClass: RedisLockStoreAdapter
606
+ },
607
+ // Application service
608
+ {
609
+ provide: LOCK_SERVICE,
610
+ useClass: LockService
611
+ },
612
+ // @WithLock decorator initialization (proxy-based)
613
+ LockDecoratorInitializerService,
614
+ // Reflector is needed for decorator metadata
615
+ Reflector
616
+ ];
617
+ }
618
+ getExports() {
619
+ return [LOCK_SERVICE];
620
+ }
621
+ };
622
+
623
+ export { LOCKS_PLUGIN_OPTIONS, LOCK_SERVICE, LockAcquisitionError, LockError, LockExpiredError, LockExtensionError, LockNotOwnedError, LockService, LocksPlugin, WithLock };
624
+ //# sourceMappingURL=index.mjs.map
625
+ //# sourceMappingURL=index.mjs.map