@m16khb/nestjs-sidequest 0.1.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.
package/dist/index.js ADDED
@@ -0,0 +1,1014 @@
1
+ import { Logger, Injectable, Inject, Optional, Global, Module, SetMetadata } from '@nestjs/common';
2
+ import { DiscoveryModule, DiscoveryService, MetadataScanner, Reflector } from '@nestjs/core';
3
+ import { Sidequest } from 'sidequest';
4
+ export { Job } from 'sidequest';
5
+
6
+ var __defProp = Object.defineProperty;
7
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
8
+
9
+ // src/constants.ts
10
+ var SIDEQUEST_MODULE_OPTIONS = Symbol("SIDEQUEST_MODULE_OPTIONS");
11
+ var SIDEQUEST_ENGINE = Symbol("SIDEQUEST_ENGINE");
12
+ var PROCESSOR_METADATA_KEY = Symbol("sidequest:processor");
13
+ var ON_JOB_METADATA_KEY = Symbol("sidequest:on-job");
14
+ var RETRY_OPTIONS_METADATA_KEY = Symbol("sidequest:retry");
15
+ var ON_JOB_COMPLETE_METADATA_KEY = Symbol("sidequest:on-job-complete");
16
+ var ON_JOB_FAILED_METADATA_KEY = Symbol("sidequest:on-job-failed");
17
+ var QUEUE_TOKEN_PREFIX = "SIDEQUEST_QUEUE_";
18
+ function getQueueToken(queueName) {
19
+ return `${QUEUE_TOKEN_PREFIX}${queueName}`;
20
+ }
21
+ __name(getQueueToken, "getQueueToken");
22
+ var DEFAULT_QUEUE_NAME = "default";
23
+ var DEFAULT_MAX_ATTEMPTS = 3;
24
+ var DEFAULT_TIMEOUT = 3e4;
25
+ var DEFAULT_CONCURRENCY = 10;
26
+ function _ts_decorate(decorators, target, key, desc) {
27
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
28
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
29
+ 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;
30
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
31
+ }
32
+ __name(_ts_decorate, "_ts_decorate");
33
+ var SidequestAdapter = class _SidequestAdapter {
34
+ static {
35
+ __name(this, "SidequestAdapter");
36
+ }
37
+ logger = new Logger(_SidequestAdapter.name);
38
+ isStarted = false;
39
+ registeredJobs = /* @__PURE__ */ new Map();
40
+ /**
41
+ * Sidequest 엔진 시작
42
+ */
43
+ async start(options) {
44
+ if (this.isStarted) {
45
+ this.logger.warn("Sidequest engine is already started");
46
+ return;
47
+ }
48
+ this.logger.log("Starting Sidequest engine...");
49
+ try {
50
+ const config = {
51
+ backend: {
52
+ driver: options.backend.driver,
53
+ config: options.backend.config
54
+ }
55
+ };
56
+ if (options.queues) {
57
+ config.queues = options.queues.map((q) => ({
58
+ name: q.name,
59
+ concurrency: q.concurrency,
60
+ priority: q.priority,
61
+ state: q.state
62
+ }));
63
+ }
64
+ if (options.maxConcurrentJobs !== void 0) {
65
+ config.maxConcurrentJobs = options.maxConcurrentJobs;
66
+ }
67
+ if (options.minThreads !== void 0) {
68
+ config.minThreads = options.minThreads;
69
+ }
70
+ if (options.maxThreads !== void 0) {
71
+ config.maxThreads = options.maxThreads;
72
+ }
73
+ if (options.jobPollingInterval !== void 0) {
74
+ config.jobPollingInterval = options.jobPollingInterval;
75
+ }
76
+ if (options.releaseStaleJobsIntervalMin !== void 0) {
77
+ config.releaseStaleJobsIntervalMin = options.releaseStaleJobsIntervalMin;
78
+ }
79
+ if (options.cleanupFinishedJobsIntervalMin !== void 0) {
80
+ config.cleanupFinishedJobsIntervalMin = options.cleanupFinishedJobsIntervalMin;
81
+ }
82
+ if (options.logger) {
83
+ config.logger = {
84
+ level: options.logger.level ?? "info",
85
+ json: options.logger.json ?? false
86
+ };
87
+ }
88
+ if (options.dashboard) {
89
+ config.dashboard = options.dashboard;
90
+ }
91
+ await Sidequest.start(config);
92
+ this.isStarted = true;
93
+ this.logger.log("Sidequest engine started");
94
+ } catch (error) {
95
+ this.logger.error("Failed to start Sidequest engine", error);
96
+ throw error;
97
+ }
98
+ }
99
+ /**
100
+ * Sidequest 엔진 종료
101
+ */
102
+ async shutdown() {
103
+ if (!this.isStarted) {
104
+ return;
105
+ }
106
+ this.logger.log("Stopping Sidequest engine...");
107
+ try {
108
+ await Sidequest.stop();
109
+ this.isStarted = false;
110
+ this.logger.log("Sidequest engine stopped");
111
+ } catch (error) {
112
+ this.logger.error("Failed to stop Sidequest engine", error);
113
+ throw error;
114
+ }
115
+ }
116
+ /**
117
+ * Job 클래스 등록
118
+ */
119
+ registerJob(jobName, JobClass) {
120
+ this.registeredJobs.set(jobName, JobClass);
121
+ this.logger.debug(`Job '${jobName}' registered`);
122
+ }
123
+ /**
124
+ * Job 추가
125
+ */
126
+ async addJob(queueName, jobName, args, options) {
127
+ const JobClass = this.registeredJobs.get(jobName);
128
+ if (!JobClass) {
129
+ throw new Error(`Job class '${jobName}' not found. Make sure it's registered.`);
130
+ }
131
+ let builder = Sidequest.build(JobClass).queue(queueName);
132
+ if (options?.maxAttempts !== void 0) {
133
+ builder = builder.maxAttempts(options.maxAttempts);
134
+ }
135
+ if (options?.timeout !== void 0) {
136
+ builder = builder.timeout(options.timeout);
137
+ }
138
+ if (options?.priority !== void 0) ;
139
+ if (options?.scheduledAt !== void 0) {
140
+ builder = builder.availableAt(options.scheduledAt);
141
+ }
142
+ if (options?.retryDelay !== void 0) {
143
+ builder = builder.retryDelay(options.retryDelay);
144
+ }
145
+ if (options?.backoffStrategy !== void 0) {
146
+ builder = builder.backoffStrategy(options.backoffStrategy);
147
+ }
148
+ if (options?.uniqueKey !== void 0) {
149
+ builder = builder.unique(true);
150
+ }
151
+ const jobData = await builder.enqueue(...args);
152
+ const jobId = String(jobData?.id ?? `job_${Date.now()}`);
153
+ this.logger.debug(`Job '${jobName}' (ID: ${jobId}) added to Queue '${queueName}'`);
154
+ return jobId;
155
+ }
156
+ /**
157
+ * Bulk Job 추가
158
+ */
159
+ async addBulkJobs(queueName, jobs) {
160
+ const jobIds = [];
161
+ for (const { jobName, args, options } of jobs) {
162
+ const jobId = await this.addJob(queueName, jobName, args, options);
163
+ jobIds.push(jobId);
164
+ }
165
+ return jobIds;
166
+ }
167
+ /**
168
+ * Job 조회
169
+ */
170
+ async getJob(jobId) {
171
+ try {
172
+ const numericId = typeof jobId === "string" ? parseInt(jobId, 10) : jobId;
173
+ if (isNaN(numericId)) {
174
+ return void 0;
175
+ }
176
+ const job = await Sidequest.job.get(numericId);
177
+ if (!job) {
178
+ return void 0;
179
+ }
180
+ const jobData = job;
181
+ return {
182
+ id: String(job.id),
183
+ name: jobData.name,
184
+ queue: job.queue,
185
+ state: job.state,
186
+ attempt: job.attempt,
187
+ maxAttempts: job.max_attempts,
188
+ insertedAt: job.inserted_at,
189
+ attemptedAt: job.attempted_at ?? void 0,
190
+ completedAt: job.completed_at ?? void 0,
191
+ result: job.result,
192
+ errors: job.errors
193
+ };
194
+ } catch {
195
+ return void 0;
196
+ }
197
+ }
198
+ /**
199
+ * 엔진 시작 여부
200
+ */
201
+ get started() {
202
+ return this.isStarted;
203
+ }
204
+ /**
205
+ * 등록된 Job 클래스 반환
206
+ */
207
+ getRegisteredJob(jobName) {
208
+ return this.registeredJobs.get(jobName);
209
+ }
210
+ /**
211
+ * 모든 등록된 Job 클래스 반환
212
+ */
213
+ getAllRegisteredJobs() {
214
+ return this.registeredJobs;
215
+ }
216
+ };
217
+ SidequestAdapter = _ts_decorate([
218
+ Injectable()
219
+ ], SidequestAdapter);
220
+ function _ts_decorate2(decorators, target, key, desc) {
221
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
222
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
223
+ 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;
224
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
225
+ }
226
+ __name(_ts_decorate2, "_ts_decorate");
227
+ function _ts_metadata(k, v) {
228
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
229
+ }
230
+ __name(_ts_metadata, "_ts_metadata");
231
+ function _ts_param(paramIndex, decorator) {
232
+ return function(target, key) {
233
+ decorator(target, key, paramIndex);
234
+ };
235
+ }
236
+ __name(_ts_param, "_ts_param");
237
+ var SidequestEngineService = class _SidequestEngineService {
238
+ static {
239
+ __name(this, "SidequestEngineService");
240
+ }
241
+ options;
242
+ adapter;
243
+ logger = new Logger(_SidequestEngineService.name);
244
+ constructor(options, adapter) {
245
+ this.options = options;
246
+ this.adapter = adapter;
247
+ }
248
+ /**
249
+ * 모듈 초기화 시 엔진 시작
250
+ */
251
+ async onModuleInit() {
252
+ this.logger.log("Initializing Sidequest engine...");
253
+ await this.adapter.start(this.options);
254
+ this.logger.log("Sidequest engine initialized");
255
+ }
256
+ /**
257
+ * 모듈 종료 시 엔진 종료
258
+ */
259
+ async onModuleDestroy() {
260
+ const gracefulShutdown = this.options.gracefulShutdown ?? {
261
+ enabled: true,
262
+ timeout: 3e4
263
+ };
264
+ if (gracefulShutdown.enabled) {
265
+ this.logger.log(`Sidequest engine graceful shutdown (timeout: ${gracefulShutdown.timeout}ms)`);
266
+ }
267
+ await this.adapter.shutdown();
268
+ this.logger.log("Sidequest engine stopped");
269
+ }
270
+ /**
271
+ * 엔진 시작 여부
272
+ */
273
+ get isStarted() {
274
+ return this.adapter.started;
275
+ }
276
+ /**
277
+ * Adapter 인스턴스 반환
278
+ */
279
+ getAdapter() {
280
+ return this.adapter;
281
+ }
282
+ };
283
+ SidequestEngineService = _ts_decorate2([
284
+ Injectable(),
285
+ _ts_param(0, Inject(SIDEQUEST_MODULE_OPTIONS)),
286
+ _ts_metadata("design:type", Function),
287
+ _ts_metadata("design:paramtypes", [
288
+ typeof SidequestModuleOptions === "undefined" ? Object : SidequestModuleOptions,
289
+ typeof SidequestAdapter === "undefined" ? Object : SidequestAdapter
290
+ ])
291
+ ], SidequestEngineService);
292
+ function _ts_decorate3(decorators, target, key, desc) {
293
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
294
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
295
+ 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;
296
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
297
+ }
298
+ __name(_ts_decorate3, "_ts_decorate");
299
+ function _ts_metadata2(k, v) {
300
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
301
+ }
302
+ __name(_ts_metadata2, "_ts_metadata");
303
+ function _ts_param2(paramIndex, decorator) {
304
+ return function(target, key) {
305
+ decorator(target, key, paramIndex);
306
+ };
307
+ }
308
+ __name(_ts_param2, "_ts_param");
309
+ var QueueRegistryService = class _QueueRegistryService {
310
+ static {
311
+ __name(this, "QueueRegistryService");
312
+ }
313
+ options;
314
+ adapter;
315
+ logger = new Logger(_QueueRegistryService.name);
316
+ queueServices = /* @__PURE__ */ new Map();
317
+ constructor(options, adapter) {
318
+ this.options = options;
319
+ this.adapter = adapter;
320
+ this.initializeQueues();
321
+ }
322
+ /**
323
+ * 설정된 큐 초기화
324
+ */
325
+ initializeQueues() {
326
+ const queues = this.options.queues ?? [];
327
+ for (const queueConfig of queues) {
328
+ this.registerQueue(queueConfig);
329
+ }
330
+ if (!this.queueServices.has("default")) {
331
+ this.registerQueue({
332
+ name: "default"
333
+ });
334
+ }
335
+ }
336
+ /**
337
+ * 큐 등록
338
+ */
339
+ registerQueue(config) {
340
+ const queueService = this.createQueueService(config.name);
341
+ this.queueServices.set(config.name, queueService);
342
+ this.logger.log(`Queue '${config.name}' registered`);
343
+ }
344
+ /**
345
+ * 큐 서비스 조회
346
+ */
347
+ getQueue(name) {
348
+ return this.queueServices.get(name);
349
+ }
350
+ /**
351
+ * 큐 서비스 조회 (없으면 에러)
352
+ */
353
+ getQueueOrThrow(name) {
354
+ const queue = this.getQueue(name);
355
+ if (!queue) {
356
+ throw new Error(`Queue '${name}' not found. Make sure it is registered in SidequestModule.forRoot().`);
357
+ }
358
+ return queue;
359
+ }
360
+ /**
361
+ * 모든 큐 조회
362
+ */
363
+ getAllQueues() {
364
+ return this.queueServices;
365
+ }
366
+ /**
367
+ * 큐 서비스 인스턴스 생성
368
+ */
369
+ createQueueService(queueName) {
370
+ const adapter = this.adapter;
371
+ return {
372
+ name: queueName,
373
+ async add(JobClass, ...args) {
374
+ const jobName = JobClass.name;
375
+ return adapter.addJob(queueName, jobName, args);
376
+ },
377
+ async addWithOptions(JobClass, options, ...args) {
378
+ const jobName = JobClass.name;
379
+ return adapter.addJob(queueName, jobName, args, options);
380
+ },
381
+ async addScheduled(JobClass, scheduledAt, ...args) {
382
+ const jobName = JobClass.name;
383
+ return adapter.addJob(queueName, jobName, args, {
384
+ scheduledAt
385
+ });
386
+ },
387
+ async addBulk(jobs) {
388
+ const bulkJobs = jobs.map((job) => {
389
+ const result = {
390
+ jobName: job.JobClass.name,
391
+ args: job.args
392
+ };
393
+ if (job.options !== void 0) {
394
+ result.options = job.options;
395
+ }
396
+ return result;
397
+ });
398
+ return adapter.addBulkJobs(queueName, bulkJobs);
399
+ }
400
+ };
401
+ }
402
+ };
403
+ QueueRegistryService = _ts_decorate3([
404
+ Injectable(),
405
+ _ts_param2(0, Inject(SIDEQUEST_MODULE_OPTIONS)),
406
+ _ts_metadata2("design:type", Function),
407
+ _ts_metadata2("design:paramtypes", [
408
+ typeof SidequestModuleOptions === "undefined" ? Object : SidequestModuleOptions,
409
+ typeof SidequestAdapter === "undefined" ? Object : SidequestAdapter
410
+ ])
411
+ ], QueueRegistryService);
412
+ function _ts_decorate4(decorators, target, key, desc) {
413
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
414
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
415
+ 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;
416
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
417
+ }
418
+ __name(_ts_decorate4, "_ts_decorate");
419
+ function _ts_metadata3(k, v) {
420
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
421
+ }
422
+ __name(_ts_metadata3, "_ts_metadata");
423
+ function _ts_param3(paramIndex, decorator) {
424
+ return function(target, key) {
425
+ decorator(target, key, paramIndex);
426
+ };
427
+ }
428
+ __name(_ts_param3, "_ts_param");
429
+ var ClsIntegrationService = class _ClsIntegrationService {
430
+ static {
431
+ __name(this, "ClsIntegrationService");
432
+ }
433
+ logger = new Logger(_ClsIntegrationService.name);
434
+ enabled;
435
+ clsService = null;
436
+ constructor(options, clsService) {
437
+ this.enabled = options.enableCls ?? false;
438
+ this.clsService = clsService ?? null;
439
+ if (this.enabled && !this.clsService) {
440
+ this.logger.warn("CLS integration is enabled but nestjs-cls is not installed. Install nestjs-cls to enable context propagation.");
441
+ }
442
+ }
443
+ /**
444
+ * CLS context 내에서 콜백 실행
445
+ */
446
+ async runInContext(metadata, callback) {
447
+ if (!this.enabled || !this.clsService) {
448
+ return callback();
449
+ }
450
+ return new Promise((resolve, reject) => {
451
+ try {
452
+ this.clsService.run(() => {
453
+ for (const [key, value] of Object.entries(metadata)) {
454
+ this.clsService.set(key, value);
455
+ }
456
+ callback().then(resolve).catch(reject);
457
+ });
458
+ } catch (error) {
459
+ reject(error);
460
+ }
461
+ });
462
+ }
463
+ /**
464
+ * 현재 traceId 조회
465
+ */
466
+ getTraceId() {
467
+ if (!this.enabled || !this.clsService) {
468
+ return void 0;
469
+ }
470
+ return this.clsService.get("traceId");
471
+ }
472
+ /**
473
+ * traceId 설정
474
+ */
475
+ setTraceId(traceId) {
476
+ if (!this.enabled || !this.clsService) {
477
+ return;
478
+ }
479
+ this.clsService.set("traceId", traceId);
480
+ }
481
+ /**
482
+ * CLS에서 값 조회
483
+ */
484
+ get(key) {
485
+ if (!this.enabled || !this.clsService) {
486
+ return void 0;
487
+ }
488
+ return this.clsService.get(key);
489
+ }
490
+ /**
491
+ * CLS에 값 설정
492
+ */
493
+ set(key, value) {
494
+ if (!this.enabled || !this.clsService) {
495
+ return;
496
+ }
497
+ this.clsService.set(key, value);
498
+ }
499
+ /**
500
+ * CLS 통합 활성화 여부
501
+ */
502
+ isEnabled() {
503
+ return this.enabled && this.clsService !== null;
504
+ }
505
+ };
506
+ ClsIntegrationService = _ts_decorate4([
507
+ Injectable(),
508
+ _ts_param3(0, Inject(SIDEQUEST_MODULE_OPTIONS)),
509
+ _ts_param3(1, Optional()),
510
+ _ts_param3(1, Inject("ClsService")),
511
+ _ts_metadata3("design:type", Function),
512
+ _ts_metadata3("design:paramtypes", [
513
+ typeof SidequestModuleOptions === "undefined" ? Object : SidequestModuleOptions,
514
+ typeof ClsService === "undefined" ? Object : ClsService
515
+ ])
516
+ ], ClsIntegrationService);
517
+
518
+ // src/services/processor-registry.service.ts
519
+ function _ts_decorate5(decorators, target, key, desc) {
520
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
521
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
522
+ 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;
523
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
524
+ }
525
+ __name(_ts_decorate5, "_ts_decorate");
526
+ function _ts_metadata4(k, v) {
527
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
528
+ }
529
+ __name(_ts_metadata4, "_ts_metadata");
530
+ function _ts_param4(paramIndex, decorator) {
531
+ return function(target, key) {
532
+ decorator(target, key, paramIndex);
533
+ };
534
+ }
535
+ __name(_ts_param4, "_ts_param");
536
+ var ProcessorRegistryService = class _ProcessorRegistryService {
537
+ static {
538
+ __name(this, "ProcessorRegistryService");
539
+ }
540
+ clsService;
541
+ logger = new Logger(_ProcessorRegistryService.name);
542
+ processors = /* @__PURE__ */ new Map();
543
+ constructor(clsService) {
544
+ this.clsService = clsService;
545
+ }
546
+ /**
547
+ * 프로세서 등록
548
+ */
549
+ register(processor) {
550
+ this.processors.set(processor.queueName, processor);
551
+ this.logger.log(`Processor '${processor.metatype.name}' (Queue: ${processor.queueName}) registered`);
552
+ }
553
+ /**
554
+ * 프로세서 조회
555
+ */
556
+ getProcessor(queueName) {
557
+ return this.processors.get(queueName);
558
+ }
559
+ /**
560
+ * 모든 프로세서 조회
561
+ */
562
+ getAllProcessors() {
563
+ return this.processors;
564
+ }
565
+ /**
566
+ * Job 핸들러 조회
567
+ */
568
+ getJobHandler(queueName, jobName) {
569
+ const processor = this.processors.get(queueName);
570
+ if (!processor) {
571
+ return void 0;
572
+ }
573
+ return processor.handlers.get(jobName);
574
+ }
575
+ /**
576
+ * Job 완료 핸들러 조회
577
+ */
578
+ getCompleteHandler(queueName, jobName) {
579
+ const processor = this.processors.get(queueName);
580
+ if (!processor) {
581
+ return void 0;
582
+ }
583
+ return processor.completeHandlers.get(jobName) ?? processor.completeHandlers.get("*");
584
+ }
585
+ /**
586
+ * Job 실패 핸들러 조회
587
+ */
588
+ getFailedHandler(queueName, jobName) {
589
+ const processor = this.processors.get(queueName);
590
+ if (!processor) {
591
+ return void 0;
592
+ }
593
+ return processor.failedHandlers.get(jobName) ?? processor.failedHandlers.get("*");
594
+ }
595
+ /**
596
+ * Job 디스패치 (실행)
597
+ *
598
+ * @param queueName - 큐 이름
599
+ * @param jobName - Job 이름
600
+ * @param args - Job 인자
601
+ * @param metadata - Job 메타데이터 (traceId 등)
602
+ */
603
+ async dispatch(queueName, jobName, args, metadata) {
604
+ const processor = this.processors.get(queueName);
605
+ if (!processor) {
606
+ throw new Error(`Processor not found for Queue '${queueName}'`);
607
+ }
608
+ const handler = processor.handlers.get(jobName);
609
+ if (!handler) {
610
+ throw new Error(`Handler not found for Job '${jobName}' (Queue: ${queueName})`);
611
+ }
612
+ const instance = processor.instance;
613
+ const method = instance[handler.methodName];
614
+ if (typeof method !== "function") {
615
+ throw new Error(`Handler method '${handler.methodName}' not found`);
616
+ }
617
+ const executeJob = /* @__PURE__ */ __name(async () => {
618
+ try {
619
+ const result = await method.apply(processor.instance, args);
620
+ const completeHandlerName = this.getCompleteHandler(queueName, jobName);
621
+ if (completeHandlerName) {
622
+ const completeHandler = instance[completeHandlerName];
623
+ if (typeof completeHandler === "function") {
624
+ await completeHandler.apply(processor.instance, [
625
+ {
626
+ jobName,
627
+ args,
628
+ result
629
+ }
630
+ ]);
631
+ }
632
+ }
633
+ return result;
634
+ } catch (error) {
635
+ const failedHandlerName = this.getFailedHandler(queueName, jobName);
636
+ if (failedHandlerName) {
637
+ const failedHandler = instance[failedHandlerName];
638
+ if (typeof failedHandler === "function") {
639
+ await failedHandler.apply(processor.instance, [
640
+ {
641
+ jobName,
642
+ args,
643
+ error
644
+ }
645
+ ]);
646
+ }
647
+ }
648
+ throw error;
649
+ }
650
+ }, "executeJob");
651
+ if (this.clsService?.isEnabled() && metadata) {
652
+ return this.clsService.runInContext(metadata, executeJob);
653
+ }
654
+ return executeJob();
655
+ }
656
+ };
657
+ ProcessorRegistryService = _ts_decorate5([
658
+ Injectable(),
659
+ _ts_param4(0, Optional()),
660
+ _ts_metadata4("design:type", Function),
661
+ _ts_metadata4("design:paramtypes", [
662
+ typeof ClsIntegrationService === "undefined" ? Object : ClsIntegrationService
663
+ ])
664
+ ], ProcessorRegistryService);
665
+ function _ts_decorate6(decorators, target, key, desc) {
666
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
667
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
668
+ 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;
669
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
670
+ }
671
+ __name(_ts_decorate6, "_ts_decorate");
672
+ function _ts_metadata5(k, v) {
673
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
674
+ }
675
+ __name(_ts_metadata5, "_ts_metadata");
676
+ var ProcessorExplorer = class _ProcessorExplorer {
677
+ static {
678
+ __name(this, "ProcessorExplorer");
679
+ }
680
+ discoveryService;
681
+ metadataScanner;
682
+ reflector;
683
+ processorRegistry;
684
+ logger = new Logger(_ProcessorExplorer.name);
685
+ constructor(discoveryService, metadataScanner, reflector, processorRegistry) {
686
+ this.discoveryService = discoveryService;
687
+ this.metadataScanner = metadataScanner;
688
+ this.reflector = reflector;
689
+ this.processorRegistry = processorRegistry;
690
+ }
691
+ /**
692
+ * 모듈 초기화 시 프로세서 스캔
693
+ */
694
+ async onModuleInit() {
695
+ this.logger.log("Scanning for processors...");
696
+ await this.explore();
697
+ this.logger.log("Processor scan completed");
698
+ }
699
+ /**
700
+ * 모든 프로바이더를 스캔하여 @Processor 클래스 찾기
701
+ */
702
+ async explore() {
703
+ const providers = this.discoveryService.getProviders();
704
+ for (const wrapper of providers) {
705
+ await this.processWrapper(wrapper);
706
+ }
707
+ }
708
+ /**
709
+ * 프로바이더 래퍼 처리
710
+ */
711
+ async processWrapper(wrapper) {
712
+ const { instance, metatype } = wrapper;
713
+ if (!instance || !metatype) {
714
+ return;
715
+ }
716
+ const processorMetadata = this.reflector.get(PROCESSOR_METADATA_KEY, metatype);
717
+ if (!processorMetadata) {
718
+ return;
719
+ }
720
+ const handlers = this.scanHandlers(instance, metatype);
721
+ const completeHandlers = this.scanCompleteHandlers(instance, metatype);
722
+ const failedHandlers = this.scanFailedHandlers(instance, metatype);
723
+ const registeredProcessor = {
724
+ queueName: processorMetadata.queueName,
725
+ instance,
726
+ metatype,
727
+ handlers,
728
+ completeHandlers,
729
+ failedHandlers
730
+ };
731
+ this.processorRegistry.register(registeredProcessor);
732
+ this.logger.log(`Processor '${metatype.name}' registered (Queue: ${processorMetadata.queueName}, Handlers: ${handlers.size})`);
733
+ }
734
+ /**
735
+ * @OnJob 메서드 스캔
736
+ */
737
+ scanHandlers(instance, _metatype) {
738
+ const handlers = /* @__PURE__ */ new Map();
739
+ const prototype = Object.getPrototypeOf(instance);
740
+ const methodNames = this.metadataScanner.getAllMethodNames(prototype);
741
+ for (const methodName of methodNames) {
742
+ const methodRef = prototype[methodName];
743
+ const onJobMetadata = this.reflector.get(ON_JOB_METADATA_KEY, methodRef);
744
+ if (!onJobMetadata) {
745
+ continue;
746
+ }
747
+ const retryOptions = this.reflector.get(RETRY_OPTIONS_METADATA_KEY, methodRef);
748
+ const handler = {
749
+ methodName,
750
+ jobName: onJobMetadata.jobName,
751
+ options: onJobMetadata.options,
752
+ retryOptions
753
+ };
754
+ handlers.set(onJobMetadata.jobName, handler);
755
+ this.logger.debug(` - Job '${onJobMetadata.jobName}' -> ${methodName}()`);
756
+ }
757
+ return handlers;
758
+ }
759
+ /**
760
+ * @OnJobComplete 메서드 스캔
761
+ */
762
+ scanCompleteHandlers(instance, _metatype) {
763
+ const handlers = /* @__PURE__ */ new Map();
764
+ const prototype = Object.getPrototypeOf(instance);
765
+ const methodNames = this.metadataScanner.getAllMethodNames(prototype);
766
+ for (const methodName of methodNames) {
767
+ const methodRef = prototype[methodName];
768
+ const jobName = this.reflector.get(ON_JOB_COMPLETE_METADATA_KEY, methodRef);
769
+ if (jobName !== void 0) {
770
+ const key = jobName || "*";
771
+ handlers.set(key, methodName);
772
+ this.logger.debug(` - OnJobComplete '${key}' -> ${methodName}()`);
773
+ }
774
+ }
775
+ return handlers;
776
+ }
777
+ /**
778
+ * @OnJobFailed 메서드 스캔
779
+ */
780
+ scanFailedHandlers(instance, _metatype) {
781
+ const handlers = /* @__PURE__ */ new Map();
782
+ const prototype = Object.getPrototypeOf(instance);
783
+ const methodNames = this.metadataScanner.getAllMethodNames(prototype);
784
+ for (const methodName of methodNames) {
785
+ const methodRef = prototype[methodName];
786
+ const jobName = this.reflector.get(ON_JOB_FAILED_METADATA_KEY, methodRef);
787
+ if (jobName !== void 0) {
788
+ const key = jobName || "*";
789
+ handlers.set(key, methodName);
790
+ this.logger.debug(` - OnJobFailed '${key}' -> ${methodName}()`);
791
+ }
792
+ }
793
+ return handlers;
794
+ }
795
+ };
796
+ ProcessorExplorer = _ts_decorate6([
797
+ Injectable(),
798
+ _ts_metadata5("design:type", Function),
799
+ _ts_metadata5("design:paramtypes", [
800
+ typeof DiscoveryService === "undefined" ? Object : DiscoveryService,
801
+ typeof MetadataScanner === "undefined" ? Object : MetadataScanner,
802
+ typeof Reflector === "undefined" ? Object : Reflector,
803
+ typeof ProcessorRegistryService === "undefined" ? Object : ProcessorRegistryService
804
+ ])
805
+ ], ProcessorExplorer);
806
+
807
+ // src/modules/sidequest.module.ts
808
+ function _ts_decorate7(decorators, target, key, desc) {
809
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
810
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
811
+ 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;
812
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
813
+ }
814
+ __name(_ts_decorate7, "_ts_decorate");
815
+ var SidequestModule = class _SidequestModule {
816
+ static {
817
+ __name(this, "SidequestModule");
818
+ }
819
+ /**
820
+ * 동기 설정으로 모듈 등록
821
+ */
822
+ static forRoot(options) {
823
+ const isGlobal = options.isGlobal ?? true;
824
+ const queueProviders = this.createQueueProviders(options);
825
+ return {
826
+ module: _SidequestModule,
827
+ global: isGlobal,
828
+ imports: [
829
+ DiscoveryModule
830
+ ],
831
+ providers: [
832
+ {
833
+ provide: SIDEQUEST_MODULE_OPTIONS,
834
+ useValue: options
835
+ },
836
+ SidequestAdapter,
837
+ SidequestEngineService,
838
+ QueueRegistryService,
839
+ ProcessorRegistryService,
840
+ ClsIntegrationService,
841
+ ProcessorExplorer,
842
+ ...queueProviders
843
+ ],
844
+ exports: [
845
+ SIDEQUEST_MODULE_OPTIONS,
846
+ SidequestAdapter,
847
+ SidequestEngineService,
848
+ QueueRegistryService,
849
+ ProcessorRegistryService,
850
+ ClsIntegrationService,
851
+ ...queueProviders.map((p) => p.provide)
852
+ ]
853
+ };
854
+ }
855
+ /**
856
+ * 비동기 설정으로 모듈 등록
857
+ */
858
+ static forRootAsync(asyncOptions) {
859
+ const isGlobal = asyncOptions.isGlobal ?? true;
860
+ return {
861
+ module: _SidequestModule,
862
+ global: isGlobal,
863
+ imports: [
864
+ DiscoveryModule,
865
+ ...asyncOptions.imports || []
866
+ ],
867
+ providers: [
868
+ ...this.createAsyncProviders(asyncOptions),
869
+ SidequestAdapter,
870
+ SidequestEngineService,
871
+ QueueRegistryService,
872
+ ProcessorRegistryService,
873
+ ClsIntegrationService,
874
+ ProcessorExplorer,
875
+ // 동적 큐 프로바이더 - options에서 queues를 읽어 생성
876
+ {
877
+ provide: "SIDEQUEST_QUEUE_PROVIDERS_INIT",
878
+ useFactory: /* @__PURE__ */ __name(() => {
879
+ return true;
880
+ }, "useFactory"),
881
+ inject: [
882
+ QueueRegistryService
883
+ ]
884
+ }
885
+ ],
886
+ exports: [
887
+ SIDEQUEST_MODULE_OPTIONS,
888
+ SidequestAdapter,
889
+ SidequestEngineService,
890
+ QueueRegistryService,
891
+ ProcessorRegistryService,
892
+ ClsIntegrationService
893
+ ]
894
+ };
895
+ }
896
+ /**
897
+ * 큐별 DI 프로바이더 생성
898
+ */
899
+ static createQueueProviders(options) {
900
+ const queues = options.queues ?? [
901
+ {
902
+ name: "default"
903
+ }
904
+ ];
905
+ return queues.map((queue) => ({
906
+ provide: getQueueToken(queue.name),
907
+ useFactory: /* @__PURE__ */ __name((registry) => registry.getQueueOrThrow(queue.name), "useFactory"),
908
+ inject: [
909
+ QueueRegistryService
910
+ ]
911
+ }));
912
+ }
913
+ /**
914
+ * 비동기 프로바이더 생성
915
+ */
916
+ static createAsyncProviders(asyncOptions) {
917
+ if (asyncOptions.useFactory) {
918
+ return [
919
+ {
920
+ provide: SIDEQUEST_MODULE_OPTIONS,
921
+ useFactory: asyncOptions.useFactory,
922
+ inject: asyncOptions.inject || []
923
+ }
924
+ ];
925
+ }
926
+ if (asyncOptions.useClass) {
927
+ return [
928
+ {
929
+ provide: asyncOptions.useClass,
930
+ useClass: asyncOptions.useClass
931
+ },
932
+ {
933
+ provide: SIDEQUEST_MODULE_OPTIONS,
934
+ useFactory: /* @__PURE__ */ __name(async (optionsFactory) => optionsFactory.createSidequestOptions(), "useFactory"),
935
+ inject: [
936
+ asyncOptions.useClass
937
+ ]
938
+ }
939
+ ];
940
+ }
941
+ if (asyncOptions.useExisting) {
942
+ return [
943
+ {
944
+ provide: SIDEQUEST_MODULE_OPTIONS,
945
+ useFactory: /* @__PURE__ */ __name(async (optionsFactory) => optionsFactory.createSidequestOptions(), "useFactory"),
946
+ inject: [
947
+ asyncOptions.useExisting
948
+ ]
949
+ }
950
+ ];
951
+ }
952
+ return [
953
+ {
954
+ provide: SIDEQUEST_MODULE_OPTIONS,
955
+ useValue: {}
956
+ }
957
+ ];
958
+ }
959
+ };
960
+ SidequestModule = _ts_decorate7([
961
+ Global(),
962
+ Module({})
963
+ ], SidequestModule);
964
+ function Processor(queueName, options) {
965
+ return (target) => {
966
+ const metadata = {
967
+ queueName,
968
+ options
969
+ };
970
+ SetMetadata(PROCESSOR_METADATA_KEY, metadata)(target);
971
+ Injectable()(target);
972
+ };
973
+ }
974
+ __name(Processor, "Processor");
975
+ function OnJob(jobName, options) {
976
+ return (target, propertyKey, descriptor) => {
977
+ const metadata = {
978
+ jobName,
979
+ options
980
+ };
981
+ SetMetadata(ON_JOB_METADATA_KEY, metadata)(target, propertyKey, descriptor);
982
+ return descriptor;
983
+ };
984
+ }
985
+ __name(OnJob, "OnJob");
986
+ function Retry(options) {
987
+ return (target, propertyKey, descriptor) => {
988
+ SetMetadata(RETRY_OPTIONS_METADATA_KEY, options)(target, propertyKey, descriptor);
989
+ return descriptor;
990
+ };
991
+ }
992
+ __name(Retry, "Retry");
993
+ function InjectQueue(queueName) {
994
+ return Inject(getQueueToken(queueName));
995
+ }
996
+ __name(InjectQueue, "InjectQueue");
997
+ function OnJobComplete(jobName) {
998
+ return (target, propertyKey, descriptor) => {
999
+ SetMetadata(ON_JOB_COMPLETE_METADATA_KEY, jobName ?? "")(target, propertyKey, descriptor);
1000
+ return descriptor;
1001
+ };
1002
+ }
1003
+ __name(OnJobComplete, "OnJobComplete");
1004
+ function OnJobFailed(jobName) {
1005
+ return (target, propertyKey, descriptor) => {
1006
+ SetMetadata(ON_JOB_FAILED_METADATA_KEY, jobName ?? "")(target, propertyKey, descriptor);
1007
+ return descriptor;
1008
+ };
1009
+ }
1010
+ __name(OnJobFailed, "OnJobFailed");
1011
+
1012
+ export { ClsIntegrationService, DEFAULT_CONCURRENCY, DEFAULT_MAX_ATTEMPTS, DEFAULT_QUEUE_NAME, DEFAULT_TIMEOUT, InjectQueue, ON_JOB_COMPLETE_METADATA_KEY, ON_JOB_FAILED_METADATA_KEY, ON_JOB_METADATA_KEY, OnJob, OnJobComplete, OnJobFailed, PROCESSOR_METADATA_KEY, Processor, ProcessorRegistryService, QUEUE_TOKEN_PREFIX, QueueRegistryService, RETRY_OPTIONS_METADATA_KEY, Retry, SIDEQUEST_ENGINE, SIDEQUEST_MODULE_OPTIONS, SidequestAdapter, SidequestEngineService, SidequestModule, getQueueToken };
1013
+ //# sourceMappingURL=index.js.map
1014
+ //# sourceMappingURL=index.js.map