atomic-queues 1.0.13

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 (110) hide show
  1. package/README.md +686 -0
  2. package/dist/decorators/decorators.d.ts +67 -0
  3. package/dist/decorators/decorators.d.ts.map +1 -0
  4. package/dist/decorators/decorators.js +91 -0
  5. package/dist/decorators/decorators.js.map +1 -0
  6. package/dist/decorators/index.d.ts +2 -0
  7. package/dist/decorators/index.d.ts.map +1 -0
  8. package/dist/decorators/index.js +18 -0
  9. package/dist/decorators/index.js.map +1 -0
  10. package/dist/domain/index.d.ts +5 -0
  11. package/dist/domain/index.d.ts.map +1 -0
  12. package/dist/domain/index.js +21 -0
  13. package/dist/domain/index.js.map +1 -0
  14. package/dist/domain/interfaces.d.ts +614 -0
  15. package/dist/domain/interfaces.d.ts.map +1 -0
  16. package/dist/domain/interfaces.js +19 -0
  17. package/dist/domain/interfaces.js.map +1 -0
  18. package/dist/index.d.ts +40 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +61 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/module/atomic-queues.module.d.ts +97 -0
  23. package/dist/module/atomic-queues.module.d.ts.map +1 -0
  24. package/dist/module/atomic-queues.module.js +197 -0
  25. package/dist/module/atomic-queues.module.js.map +1 -0
  26. package/dist/module/index.d.ts +2 -0
  27. package/dist/module/index.d.ts.map +1 -0
  28. package/dist/module/index.js +18 -0
  29. package/dist/module/index.js.map +1 -0
  30. package/dist/services/constants.d.ts +10 -0
  31. package/dist/services/constants.d.ts.map +1 -0
  32. package/dist/services/constants.js +13 -0
  33. package/dist/services/constants.js.map +1 -0
  34. package/dist/services/cron-manager/cron-manager.service.d.ts +188 -0
  35. package/dist/services/cron-manager/cron-manager.service.d.ts.map +1 -0
  36. package/dist/services/cron-manager/cron-manager.service.js +534 -0
  37. package/dist/services/cron-manager/cron-manager.service.js.map +1 -0
  38. package/dist/services/cron-manager/index.d.ts +2 -0
  39. package/dist/services/cron-manager/index.d.ts.map +1 -0
  40. package/dist/services/cron-manager/index.js +18 -0
  41. package/dist/services/cron-manager/index.js.map +1 -0
  42. package/dist/services/index-manager/index-manager.service.d.ts +146 -0
  43. package/dist/services/index-manager/index-manager.service.d.ts.map +1 -0
  44. package/dist/services/index-manager/index-manager.service.js +337 -0
  45. package/dist/services/index-manager/index-manager.service.js.map +1 -0
  46. package/dist/services/index-manager/index.d.ts +2 -0
  47. package/dist/services/index-manager/index.d.ts.map +1 -0
  48. package/dist/services/index-manager/index.js +18 -0
  49. package/dist/services/index-manager/index.js.map +1 -0
  50. package/dist/services/index.d.ts +10 -0
  51. package/dist/services/index.d.ts.map +1 -0
  52. package/dist/services/index.js +26 -0
  53. package/dist/services/index.js.map +1 -0
  54. package/dist/services/job-processor/index.d.ts +2 -0
  55. package/dist/services/job-processor/index.d.ts.map +1 -0
  56. package/dist/services/job-processor/index.js +18 -0
  57. package/dist/services/job-processor/index.js.map +1 -0
  58. package/dist/services/job-processor/job-processor.service.d.ts +156 -0
  59. package/dist/services/job-processor/job-processor.service.d.ts.map +1 -0
  60. package/dist/services/job-processor/job-processor.service.js +331 -0
  61. package/dist/services/job-processor/job-processor.service.js.map +1 -0
  62. package/dist/services/queue-manager/index.d.ts +2 -0
  63. package/dist/services/queue-manager/index.d.ts.map +1 -0
  64. package/dist/services/queue-manager/index.js +18 -0
  65. package/dist/services/queue-manager/index.js.map +1 -0
  66. package/dist/services/queue-manager/queue-manager.service.d.ts +128 -0
  67. package/dist/services/queue-manager/queue-manager.service.d.ts.map +1 -0
  68. package/dist/services/queue-manager/queue-manager.service.js +308 -0
  69. package/dist/services/queue-manager/queue-manager.service.js.map +1 -0
  70. package/dist/services/resource-lock/index.d.ts +2 -0
  71. package/dist/services/resource-lock/index.d.ts.map +1 -0
  72. package/dist/services/resource-lock/index.js +18 -0
  73. package/dist/services/resource-lock/index.js.map +1 -0
  74. package/dist/services/resource-lock/resource-lock.service.d.ts +124 -0
  75. package/dist/services/resource-lock/resource-lock.service.d.ts.map +1 -0
  76. package/dist/services/resource-lock/resource-lock.service.js +379 -0
  77. package/dist/services/resource-lock/resource-lock.service.js.map +1 -0
  78. package/dist/services/service-queue/index.d.ts +2 -0
  79. package/dist/services/service-queue/index.d.ts.map +1 -0
  80. package/dist/services/service-queue/index.js +18 -0
  81. package/dist/services/service-queue/index.js.map +1 -0
  82. package/dist/services/service-queue/service-queue.service.d.ts +232 -0
  83. package/dist/services/service-queue/service-queue.service.d.ts.map +1 -0
  84. package/dist/services/service-queue/service-queue.service.js +647 -0
  85. package/dist/services/service-queue/service-queue.service.js.map +1 -0
  86. package/dist/services/shutdown-state/index.d.ts +2 -0
  87. package/dist/services/shutdown-state/index.d.ts.map +1 -0
  88. package/dist/services/shutdown-state/index.js +18 -0
  89. package/dist/services/shutdown-state/index.js.map +1 -0
  90. package/dist/services/shutdown-state/shutdown-state.service.d.ts +69 -0
  91. package/dist/services/shutdown-state/shutdown-state.service.d.ts.map +1 -0
  92. package/dist/services/shutdown-state/shutdown-state.service.js +127 -0
  93. package/dist/services/shutdown-state/shutdown-state.service.js.map +1 -0
  94. package/dist/services/worker-manager/index.d.ts +2 -0
  95. package/dist/services/worker-manager/index.d.ts.map +1 -0
  96. package/dist/services/worker-manager/index.js +18 -0
  97. package/dist/services/worker-manager/index.js.map +1 -0
  98. package/dist/services/worker-manager/worker-manager.service.d.ts +163 -0
  99. package/dist/services/worker-manager/worker-manager.service.d.ts.map +1 -0
  100. package/dist/services/worker-manager/worker-manager.service.js +460 -0
  101. package/dist/services/worker-manager/worker-manager.service.js.map +1 -0
  102. package/dist/utils/helpers.d.ts +124 -0
  103. package/dist/utils/helpers.d.ts.map +1 -0
  104. package/dist/utils/helpers.js +229 -0
  105. package/dist/utils/helpers.js.map +1 -0
  106. package/dist/utils/index.d.ts +2 -0
  107. package/dist/utils/index.d.ts.map +1 -0
  108. package/dist/utils/index.js +18 -0
  109. package/dist/utils/index.js.map +1 -0
  110. package/package.json +80 -0
@@ -0,0 +1,308 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ var __importDefault = (this && this.__importDefault) || function (mod) {
15
+ return (mod && mod.__esModule) ? mod : { "default": mod };
16
+ };
17
+ var QueueManagerService_1;
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ exports.QueueManagerService = void 0;
20
+ const common_1 = require("@nestjs/common");
21
+ const bullmq_1 = require("bullmq");
22
+ const ioredis_1 = __importDefault(require("ioredis"));
23
+ const constants_1 = require("../constants");
24
+ const common_2 = require("@nestjs/common");
25
+ /**
26
+ * QueueManagerService
27
+ *
28
+ * Manages dynamic queue creation and destruction per entity.
29
+ * This is the core service for creating queues on-demand for users, tables,
30
+ * or any other entity type that requires atomic processing.
31
+ *
32
+ * Key Features:
33
+ * - Dynamic queue creation with lazy initialization
34
+ * - Entity-specific queue naming conventions
35
+ * - Automatic cleanup on module destroy
36
+ * - Job management (add, delete)
37
+ * - Queue lifecycle management
38
+ *
39
+ * @example
40
+ * ```typescript
41
+ * // Get or create a queue for a user
42
+ * const queue = queueManager.getOrCreateEntityQueue('user', '123');
43
+ *
44
+ * // Add a job to the queue
45
+ * await queueManager.addJob('user-123-queue', 'process-message', { text: 'hello' });
46
+ * ```
47
+ */
48
+ let QueueManagerService = QueueManagerService_1 = class QueueManagerService {
49
+ constructor(redis, config) {
50
+ this.redis = redis;
51
+ this.config = config;
52
+ this.logger = new common_1.Logger(QueueManagerService_1.name);
53
+ this.queues = new Map();
54
+ this.keyPrefix = config.keyPrefix || 'aq';
55
+ }
56
+ /**
57
+ * Get or create a queue by name.
58
+ * If the queue doesn't exist, it will be created with default configuration.
59
+ */
60
+ getOrCreateQueue(queueName) {
61
+ const normalizedName = this.normalizeQueueName(queueName);
62
+ if (this.queues.has(normalizedName)) {
63
+ return this.queues.get(normalizedName).queue;
64
+ }
65
+ this.logger.debug(`Creating new queue: ${normalizedName}`);
66
+ const queue = this.createQueue(normalizedName);
67
+ const managedQueue = {
68
+ name: normalizedName,
69
+ queue,
70
+ createdAt: new Date(),
71
+ entityId: '',
72
+ entityType: 'generic',
73
+ };
74
+ this.queues.set(normalizedName, managedQueue);
75
+ return queue;
76
+ }
77
+ /**
78
+ * Get or create an entity-specific queue.
79
+ * Uses naming convention: {entityType}-{entityId}-queue
80
+ */
81
+ getOrCreateEntityQueue(entityType, entityId) {
82
+ const queueName = this.getEntityQueueName(entityType, entityId);
83
+ if (this.queues.has(queueName)) {
84
+ return this.queues.get(queueName).queue;
85
+ }
86
+ this.logger.debug(`Creating entity queue: ${queueName}`);
87
+ const queue = this.createQueue(queueName);
88
+ const managedQueue = {
89
+ name: queueName,
90
+ queue,
91
+ createdAt: new Date(),
92
+ entityId,
93
+ entityType,
94
+ };
95
+ this.queues.set(queueName, managedQueue);
96
+ return queue;
97
+ }
98
+ /**
99
+ * Close and remove a specific queue.
100
+ * This will gracefully close the queue and clean up resources.
101
+ */
102
+ async closeQueue(queueName) {
103
+ const normalizedName = this.normalizeQueueName(queueName);
104
+ const managedQueue = this.queues.get(normalizedName);
105
+ if (!managedQueue) {
106
+ this.logger.warn(`Queue ${normalizedName} not found for closing.`);
107
+ return;
108
+ }
109
+ this.logger.debug(`Closing queue: ${normalizedName}`);
110
+ try {
111
+ await managedQueue.queue.close();
112
+ this.queues.delete(normalizedName);
113
+ this.logger.debug(`Closed queue: ${normalizedName}`);
114
+ }
115
+ catch (error) {
116
+ this.logger.error(`Error closing queue ${normalizedName}:`, error);
117
+ throw error;
118
+ }
119
+ }
120
+ /**
121
+ * Close all managed queues.
122
+ * Called automatically on module destroy.
123
+ */
124
+ async closeAllQueues() {
125
+ this.logger.debug('Closing all queues...');
126
+ const closePromises = Array.from(this.queues.entries()).map(async ([queueName, managedQueue]) => {
127
+ try {
128
+ await managedQueue.queue.close();
129
+ this.logger.debug(`Closed queue: ${queueName}`);
130
+ }
131
+ catch (error) {
132
+ this.logger.error(`Error closing queue ${queueName}:`, error);
133
+ }
134
+ });
135
+ await Promise.all(closePromises);
136
+ this.queues.clear();
137
+ this.logger.debug('All queues closed.');
138
+ }
139
+ /**
140
+ * Get all managed queue names.
141
+ */
142
+ getQueueNames() {
143
+ return Array.from(this.queues.keys());
144
+ }
145
+ /**
146
+ * Get all queues for a specific entity type.
147
+ */
148
+ getEntityTypeQueues(entityType) {
149
+ return Array.from(this.queues.values()).filter((q) => q.entityType === entityType);
150
+ }
151
+ /**
152
+ * Delete a specific job from a queue.
153
+ */
154
+ async deleteJob(queueName, jobId) {
155
+ try {
156
+ const queue = this.getOrCreateQueue(queueName);
157
+ const result = await queue.remove(jobId);
158
+ this.logger.debug(`Deleted job ${jobId} from queue ${queueName}, result: ${result}`);
159
+ }
160
+ catch (error) {
161
+ this.logger.error(`Error deleting job ${jobId} from queue ${queueName}:`, error);
162
+ throw error;
163
+ }
164
+ }
165
+ /**
166
+ * Add a job to a queue with optional configuration.
167
+ */
168
+ async addJob(queueName, jobName, data, options) {
169
+ const queue = this.getOrCreateQueue(queueName);
170
+ const mergedOptions = this.mergeJobOptions(options);
171
+ try {
172
+ const job = await queue.add(jobName, data, mergedOptions);
173
+ this.logger.debug(`Added job ${job.id} (${jobName}) to queue ${queueName}`);
174
+ return job;
175
+ }
176
+ catch (error) {
177
+ this.logger.error(`Error adding job to queue ${queueName}:`, error);
178
+ throw error;
179
+ }
180
+ }
181
+ /**
182
+ * Obliterate a queue (remove all jobs and the queue itself).
183
+ * Use with caution - this is destructive.
184
+ */
185
+ async obliterateQueue(queueName) {
186
+ const normalizedName = this.normalizeQueueName(queueName);
187
+ const managedQueue = this.queues.get(normalizedName);
188
+ if (!managedQueue) {
189
+ this.logger.warn(`Queue ${normalizedName} not found for obliteration.`);
190
+ return;
191
+ }
192
+ this.logger.warn(`Obliterating queue: ${normalizedName}`);
193
+ try {
194
+ await managedQueue.queue.obliterate({ force: true });
195
+ this.queues.delete(normalizedName);
196
+ this.logger.debug(`Obliterated queue: ${normalizedName}`);
197
+ }
198
+ catch (error) {
199
+ this.logger.error(`Error obliterating queue ${normalizedName}:`, error);
200
+ throw error;
201
+ }
202
+ }
203
+ /**
204
+ * Get jobs from a queue by state.
205
+ */
206
+ async getJobs(queueName, states, start = 0, end = 100) {
207
+ const queue = this.getOrCreateQueue(queueName);
208
+ return queue.getJobs(states, start, end);
209
+ }
210
+ /**
211
+ * Get the job count for a queue.
212
+ */
213
+ async getJobCounts(queueName) {
214
+ const queue = this.getOrCreateQueue(queueName);
215
+ return queue.getJobCounts('waiting', 'active', 'completed', 'failed', 'delayed');
216
+ }
217
+ /**
218
+ * Pause a queue.
219
+ */
220
+ async pauseQueue(queueName) {
221
+ const queue = this.getOrCreateQueue(queueName);
222
+ await queue.pause();
223
+ this.logger.debug(`Paused queue: ${queueName}`);
224
+ }
225
+ /**
226
+ * Resume a paused queue.
227
+ */
228
+ async resumeQueue(queueName) {
229
+ const queue = this.getOrCreateQueue(queueName);
230
+ await queue.resume();
231
+ this.logger.debug(`Resumed queue: ${queueName}`);
232
+ }
233
+ /**
234
+ * Check if a queue exists.
235
+ */
236
+ hasQueue(queueName) {
237
+ return this.queues.has(this.normalizeQueueName(queueName));
238
+ }
239
+ /**
240
+ * Get managed queue info.
241
+ */
242
+ getManagedQueue(queueName) {
243
+ return this.queues.get(this.normalizeQueueName(queueName));
244
+ }
245
+ // =========================================================================
246
+ // PRIVATE METHODS
247
+ // =========================================================================
248
+ /**
249
+ * Create a new BullMQ Queue instance with configuration.
250
+ */
251
+ createQueue(queueName, config) {
252
+ const defaultConfig = this.config.queueDefaults || {};
253
+ const mergedConfig = { ...defaultConfig, ...config };
254
+ return new bullmq_1.Queue(queueName, {
255
+ connection: this.redis.duplicate(),
256
+ defaultJobOptions: mergedConfig.defaultJobOptions,
257
+ ...(mergedConfig.limiter && {
258
+ limiter: mergedConfig.limiter,
259
+ }),
260
+ });
261
+ }
262
+ /**
263
+ * Generate queue name for an entity.
264
+ */
265
+ getEntityQueueName(entityType, entityId) {
266
+ return `${this.keyPrefix}:${entityType}:${entityId}:queue`;
267
+ }
268
+ /**
269
+ * Normalize queue name to ensure consistency.
270
+ */
271
+ normalizeQueueName(queueName) {
272
+ // If already has prefix or is a full queue name, return as is
273
+ if (queueName.startsWith(this.keyPrefix) || queueName.includes(':')) {
274
+ return queueName;
275
+ }
276
+ // Otherwise, add suffix if not present
277
+ return queueName.endsWith('-queue') ? queueName : `${queueName}-queue`;
278
+ }
279
+ /**
280
+ * Merge job options with defaults.
281
+ */
282
+ mergeJobOptions(options) {
283
+ const defaults = {
284
+ removeOnComplete: true,
285
+ removeOnFail: false,
286
+ attempts: Number.MAX_SAFE_INTEGER,
287
+ backoff: {
288
+ type: 'fixed',
289
+ delay: 1000,
290
+ },
291
+ };
292
+ return { ...defaults, ...options };
293
+ }
294
+ /**
295
+ * Cleanup on module destroy.
296
+ */
297
+ async onModuleDestroy() {
298
+ await this.closeAllQueues();
299
+ }
300
+ };
301
+ exports.QueueManagerService = QueueManagerService;
302
+ exports.QueueManagerService = QueueManagerService = QueueManagerService_1 = __decorate([
303
+ (0, common_1.Injectable)(),
304
+ __param(0, (0, common_2.Inject)(constants_1.ATOMIC_QUEUES_REDIS)),
305
+ __param(1, (0, common_2.Inject)(constants_1.ATOMIC_QUEUES_CONFIG)),
306
+ __metadata("design:paramtypes", [ioredis_1.default, Object])
307
+ ], QueueManagerService);
308
+ //# sourceMappingURL=queue-manager.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queue-manager.service.js","sourceRoot":"","sources":["../../../src/services/queue-manager/queue-manager.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export * from './resource-lock.service';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/services/resource-lock/index.ts"],"names":[],"mappings":"AAAA,cAAc,yBAAyB,CAAC"}
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./resource-lock.service"), exports);
18
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/services/resource-lock/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,0DAAwC"}
@@ -0,0 +1,124 @@
1
+ import Redis from 'ioredis';
2
+ import { IResourceLockService, IResourceLock, ILockResult, IAtomicQueuesModuleConfig } from '../../domain';
3
+ /**
4
+ * ResourceLockService
5
+ *
6
+ * Provides distributed resource locking using Redis.
7
+ * Implements patterns from both Whatsapi (context locking) and bl-blackjack-service.
8
+ *
9
+ * Key Features:
10
+ * - Atomic lock acquisition using Lua scripts
11
+ * - TTL-based lock expiration
12
+ * - Lock ownership verification
13
+ * - Pool-based resource allocation (get first available from pool)
14
+ * - Lock extension for long-running operations
15
+ *
16
+ * Use Cases:
17
+ * - Whatsapi: Lock WhatsApp contexts for message sending
18
+ * - Blackjack: Lock table resources for game operations
19
+ * - General: Any resource that needs exclusive access
20
+ *
21
+ * @example
22
+ * ```typescript
23
+ * // Acquire a lock on a context
24
+ * const result = await lockService.acquireLock(
25
+ * 'context',
26
+ * 'context-123',
27
+ * 'user-456',
28
+ * 'user',
29
+ * 60, // 60 second TTL
30
+ * );
31
+ *
32
+ * if (result.acquired) {
33
+ * // Do work with the context
34
+ * await lockService.releaseLock('context', 'context-123');
35
+ * }
36
+ *
37
+ * // Get first available context from a pool
38
+ * const available = await lockService.getAvailableResource(
39
+ * 'context',
40
+ * ['ctx-1', 'ctx-2', 'ctx-3'],
41
+ * 'user-456',
42
+ * 'user',
43
+ * );
44
+ * ```
45
+ */
46
+ export declare class ResourceLockService implements IResourceLockService {
47
+ private readonly redis;
48
+ private readonly config;
49
+ private readonly logger;
50
+ private readonly keyPrefix;
51
+ private readonly ACQUIRE_LOCK_SCRIPT;
52
+ private readonly RELEASE_LOCK_SCRIPT;
53
+ private readonly EXTEND_LOCK_SCRIPT;
54
+ constructor(redis: Redis, config: IAtomicQueuesModuleConfig);
55
+ /**
56
+ * Acquire a lock on a resource.
57
+ *
58
+ * Uses Lua script for atomic check-and-set operation.
59
+ * The lock includes ownership information for later verification.
60
+ */
61
+ acquireLock(resourceType: string, resourceId: string, ownerId: string, ownerType: string, ttlSeconds?: number, metadata?: Record<string, unknown>): Promise<ILockResult>;
62
+ /**
63
+ * Release a lock on a resource.
64
+ */
65
+ releaseLock(resourceType: string, resourceId: string): Promise<boolean>;
66
+ /**
67
+ * Release a lock with ownership verification.
68
+ * Only releases if the current owner matches the expected owner.
69
+ */
70
+ releaseOwnedLock(resourceType: string, resourceId: string, ownerId: string): Promise<boolean>;
71
+ /**
72
+ * Check if a resource is locked.
73
+ */
74
+ isLocked(resourceType: string, resourceId: string): Promise<boolean>;
75
+ /**
76
+ * Get lock info for a resource.
77
+ */
78
+ getLockInfo(resourceType: string, resourceId: string): Promise<IResourceLock | null>;
79
+ /**
80
+ * Get all locked resources of a type for an owner.
81
+ */
82
+ getOwnerLocks(ownerType: string, ownerId: string): Promise<IResourceLock[]>;
83
+ /**
84
+ * Get an available (unlocked) resource from a pool of candidates.
85
+ * Shuffles the pool for fair distribution.
86
+ * Atomically locks and returns the first available resource.
87
+ *
88
+ * This is the pattern used by Whatsapi to find an available WhatsApp context.
89
+ */
90
+ getAvailableResource(resourceType: string, candidateIds: string[], ownerId: string, ownerType: string, ttlSeconds?: number): Promise<ILockResult>;
91
+ /**
92
+ * Extend the TTL of an existing lock.
93
+ */
94
+ extendLock(resourceType: string, resourceId: string, ttlSeconds: number): Promise<boolean>;
95
+ /**
96
+ * Get all locks for a resource type.
97
+ */
98
+ getResourceTypeLocks(resourceType: string): Promise<{
99
+ locked: IResourceLock[];
100
+ free: string[];
101
+ }>;
102
+ /**
103
+ * Cross-verify that a lock is still held by the expected owner.
104
+ * Useful for operations that need to verify lock ownership mid-operation.
105
+ */
106
+ verifyLockOwnership(resourceType: string, resourceId: string, ownerId: string): Promise<boolean>;
107
+ /**
108
+ * Clear all locks for an owner (useful for cleanup).
109
+ */
110
+ clearOwnerLocks(ownerType: string, ownerId: string): Promise<number>;
111
+ /**
112
+ * Get the Redis key for a lock.
113
+ */
114
+ private getLockKey;
115
+ /**
116
+ * Scan Redis keys matching a pattern.
117
+ */
118
+ private scanKeys;
119
+ /**
120
+ * Fisher-Yates shuffle algorithm.
121
+ */
122
+ private shuffleArray;
123
+ }
124
+ //# sourceMappingURL=resource-lock.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource-lock.service.d.ts","sourceRoot":"","sources":["../../../src/services/resource-lock/resource-lock.service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,SAAS,CAAC;AAC5B,OAAO,EACL,oBAAoB,EACpB,aAAa,EACb,WAAW,EACX,yBAAyB,EAC1B,MAAM,cAAc,CAAC;AAGtB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,qBACa,mBAAoB,YAAW,oBAAoB;IAwD/B,OAAO,CAAC,QAAQ,CAAC,KAAK;IAEnD,OAAO,CAAC,QAAQ,CAAC,MAAM;IAzDzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAwC;IAC/D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IAGnC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAalC;IAGF,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAiBlC;IAGF,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAYjC;gBAG8C,KAAK,EAAE,KAAK,EAEzC,MAAM,EAAE,yBAAyB;IAKpD;;;;;OAKG;IACG,WAAW,CACf,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,UAAU,SAAK,EACf,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACjC,OAAO,CAAC,WAAW,CAAC;IAiDvB;;OAEG;IACG,WAAW,CACf,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,OAAO,CAAC;IAgBnB;;;OAGG;IACG,gBAAgB,CACpB,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,OAAO,CAAC;IA+BnB;;OAEG;IACG,QAAQ,CAAC,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAM1E;;OAEG;IACG,WAAW,CACf,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAkBhC;;OAEG;IACG,aAAa,CACjB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,aAAa,EAAE,CAAC;IAyB3B;;;;;;OAMG;IACG,oBAAoB,CACxB,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,EAAE,EACtB,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,UAAU,SAAK,GACd,OAAO,CAAC,WAAW,CAAC;IA8BvB;;OAEG;IACG,UAAU,CACd,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,OAAO,CAAC;IA2BnB;;OAEG;IACG,oBAAoB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC;QACxD,MAAM,EAAE,aAAa,EAAE,CAAC;QACxB,IAAI,EAAE,MAAM,EAAE,CAAC;KAChB,CAAC;IAyBF;;;OAGG;IACG,mBAAmB,CACvB,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,OAAO,CAAC;IAKnB;;OAEG;IACG,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAsB1E;;OAEG;IACH,OAAO,CAAC,UAAU;IAIlB;;OAEG;YACW,QAAQ;IAmBtB;;OAEG;IACH,OAAO,CAAC,YAAY;CAOrB"}