@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/README.ko.md +402 -0
- package/README.md +402 -0
- package/dist/index.cjs +1037 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +900 -0
- package/dist/index.d.ts +900 -0
- package/dist/index.js +1014 -0
- package/dist/index.js.map +1 -0
- package/package.json +85 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,900 @@
|
|
|
1
|
+
import { ModuleMetadata, InjectionToken, OptionalFactoryDependency, Type, DynamicModule, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
|
|
2
|
+
import { Job } from 'sidequest';
|
|
3
|
+
export { Job } from 'sidequest';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 데이터베이스 백엔드 설정
|
|
7
|
+
*/
|
|
8
|
+
interface BackendConfig {
|
|
9
|
+
/**
|
|
10
|
+
* 백엔드 드라이버
|
|
11
|
+
* @example '@sidequest/postgres-backend', '@sidequest/mysql-backend', '@sidequest/sqlite-backend', '@sidequest/mongo-backend'
|
|
12
|
+
*/
|
|
13
|
+
driver: string;
|
|
14
|
+
/**
|
|
15
|
+
* 연결 설정 (연결 문자열 또는 설정 객체)
|
|
16
|
+
* @example 'postgresql://user:pass@localhost:5432/db'
|
|
17
|
+
*/
|
|
18
|
+
config: string | Record<string, unknown>;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* 큐 설정
|
|
22
|
+
*/
|
|
23
|
+
interface QueueConfig {
|
|
24
|
+
/** 큐 이름 */
|
|
25
|
+
name: string;
|
|
26
|
+
/** 동시 처리 작업 수 (기본값: 10) */
|
|
27
|
+
concurrency?: number;
|
|
28
|
+
/** 우선순위 (높을수록 먼저 처리, 기본값: 50) */
|
|
29
|
+
priority?: number;
|
|
30
|
+
/** 초기 상태 */
|
|
31
|
+
state?: 'active' | 'paused';
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* 대시보드 설정
|
|
35
|
+
*/
|
|
36
|
+
interface DashboardConfig {
|
|
37
|
+
/** 활성화 여부 (기본값: false) */
|
|
38
|
+
enabled?: boolean;
|
|
39
|
+
/** 포트 (기본값: 8678) */
|
|
40
|
+
port?: number;
|
|
41
|
+
/** 경로 (기본값: '/') */
|
|
42
|
+
path?: string;
|
|
43
|
+
/** 기본 인증 */
|
|
44
|
+
auth?: {
|
|
45
|
+
user: string;
|
|
46
|
+
password: string;
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* 로거 설정
|
|
51
|
+
*/
|
|
52
|
+
interface LoggerConfig {
|
|
53
|
+
/** 로그 레벨 */
|
|
54
|
+
level?: 'debug' | 'info' | 'warn' | 'error';
|
|
55
|
+
/** JSON 형식 출력 여부 */
|
|
56
|
+
json?: boolean;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Graceful Shutdown 설정
|
|
60
|
+
*/
|
|
61
|
+
interface GracefulShutdownConfig {
|
|
62
|
+
/** 활성화 여부 (기본값: true) */
|
|
63
|
+
enabled?: boolean;
|
|
64
|
+
/** 타임아웃 (ms, 기본값: 30000) */
|
|
65
|
+
timeout?: number;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* SidequestModule 옵션
|
|
69
|
+
*/
|
|
70
|
+
interface SidequestModuleOptions {
|
|
71
|
+
/**
|
|
72
|
+
* 전역 모듈 여부 (기본값: true)
|
|
73
|
+
*/
|
|
74
|
+
isGlobal?: boolean;
|
|
75
|
+
/**
|
|
76
|
+
* 데이터베이스 백엔드 설정
|
|
77
|
+
*/
|
|
78
|
+
backend: BackendConfig;
|
|
79
|
+
/**
|
|
80
|
+
* 큐 설정 목록
|
|
81
|
+
*/
|
|
82
|
+
queues?: QueueConfig[];
|
|
83
|
+
/**
|
|
84
|
+
* 최대 동시 작업 수 (전체, 기본값: 10)
|
|
85
|
+
*/
|
|
86
|
+
maxConcurrentJobs?: number;
|
|
87
|
+
/**
|
|
88
|
+
* 최소 워커 스레드 수 (기본값: CPU 코어 수)
|
|
89
|
+
*/
|
|
90
|
+
minThreads?: number;
|
|
91
|
+
/**
|
|
92
|
+
* 최대 워커 스레드 수 (기본값: minThreads * 2)
|
|
93
|
+
*/
|
|
94
|
+
maxThreads?: number;
|
|
95
|
+
/**
|
|
96
|
+
* Job 폴링 간격 (ms, 기본값: 100)
|
|
97
|
+
*/
|
|
98
|
+
jobPollingInterval?: number;
|
|
99
|
+
/**
|
|
100
|
+
* 오래된 Job 해제 간격 (분, 기본값: 60)
|
|
101
|
+
*/
|
|
102
|
+
releaseStaleJobsIntervalMin?: number;
|
|
103
|
+
/**
|
|
104
|
+
* 완료된 Job 정리 간격 (분, 기본값: 60)
|
|
105
|
+
*/
|
|
106
|
+
cleanupFinishedJobsIntervalMin?: number;
|
|
107
|
+
/**
|
|
108
|
+
* 로거 설정
|
|
109
|
+
*/
|
|
110
|
+
logger?: LoggerConfig;
|
|
111
|
+
/**
|
|
112
|
+
* 대시보드 설정
|
|
113
|
+
*/
|
|
114
|
+
dashboard?: DashboardConfig;
|
|
115
|
+
/**
|
|
116
|
+
* Graceful Shutdown 설정
|
|
117
|
+
*/
|
|
118
|
+
gracefulShutdown?: GracefulShutdownConfig;
|
|
119
|
+
/**
|
|
120
|
+
* CLS(Continuation Local Storage) 통합 활성화 (기본값: false)
|
|
121
|
+
* nestjs-cls가 설치되어 있어야 함
|
|
122
|
+
*/
|
|
123
|
+
enableCls?: boolean;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* SidequestModule 비동기 옵션
|
|
127
|
+
*/
|
|
128
|
+
interface SidequestModuleAsyncOptions extends Pick<ModuleMetadata, 'imports'> {
|
|
129
|
+
/**
|
|
130
|
+
* 전역 모듈 여부 (기본값: true)
|
|
131
|
+
*/
|
|
132
|
+
isGlobal?: boolean;
|
|
133
|
+
/**
|
|
134
|
+
* 팩토리 함수
|
|
135
|
+
*/
|
|
136
|
+
useFactory?: (...args: unknown[]) => Promise<SidequestModuleOptions> | SidequestModuleOptions;
|
|
137
|
+
/**
|
|
138
|
+
* 주입할 프로바이더
|
|
139
|
+
*/
|
|
140
|
+
inject?: (InjectionToken | OptionalFactoryDependency)[];
|
|
141
|
+
/**
|
|
142
|
+
* 옵션 팩토리 클래스
|
|
143
|
+
*/
|
|
144
|
+
useClass?: Type<SidequestOptionsFactory>;
|
|
145
|
+
/**
|
|
146
|
+
* 기존 옵션 팩토리 사용
|
|
147
|
+
*/
|
|
148
|
+
useExisting?: Type<SidequestOptionsFactory>;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* SidequestModule 옵션 팩토리 인터페이스
|
|
152
|
+
*/
|
|
153
|
+
interface SidequestOptionsFactory {
|
|
154
|
+
createSidequestOptions(): Promise<SidequestModuleOptions> | SidequestModuleOptions;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* SidequestModule
|
|
159
|
+
*
|
|
160
|
+
* NestJS에서 Sidequest.js를 사용하기 위한 메인 모듈입니다.
|
|
161
|
+
*
|
|
162
|
+
* @example
|
|
163
|
+
* ```typescript
|
|
164
|
+
* // 동기 설정
|
|
165
|
+
* @Module({
|
|
166
|
+
* imports: [
|
|
167
|
+
* SidequestModule.forRoot({
|
|
168
|
+
* backend: {
|
|
169
|
+
* driver: '@sidequest/postgres-backend',
|
|
170
|
+
* config: process.env.DATABASE_URL,
|
|
171
|
+
* },
|
|
172
|
+
* queues: [
|
|
173
|
+
* { name: 'email', concurrency: 5 },
|
|
174
|
+
* { name: 'report', concurrency: 2 },
|
|
175
|
+
* ],
|
|
176
|
+
* }),
|
|
177
|
+
* ],
|
|
178
|
+
* })
|
|
179
|
+
* export class AppModule {}
|
|
180
|
+
*
|
|
181
|
+
* // 비동기 설정 (ConfigService 사용)
|
|
182
|
+
* @Module({
|
|
183
|
+
* imports: [
|
|
184
|
+
* SidequestModule.forRootAsync({
|
|
185
|
+
* imports: [ConfigModule],
|
|
186
|
+
* useFactory: (config: ConfigService) => ({
|
|
187
|
+
* backend: {
|
|
188
|
+
* driver: '@sidequest/postgres-backend',
|
|
189
|
+
* config: config.get('DATABASE_URL'),
|
|
190
|
+
* },
|
|
191
|
+
* queues: [{ name: 'default' }],
|
|
192
|
+
* }),
|
|
193
|
+
* inject: [ConfigService],
|
|
194
|
+
* }),
|
|
195
|
+
* ],
|
|
196
|
+
* })
|
|
197
|
+
* export class AppModule {}
|
|
198
|
+
* ```
|
|
199
|
+
*/
|
|
200
|
+
declare class SidequestModule {
|
|
201
|
+
/**
|
|
202
|
+
* 동기 설정으로 모듈 등록
|
|
203
|
+
*/
|
|
204
|
+
static forRoot(options: SidequestModuleOptions): DynamicModule;
|
|
205
|
+
/**
|
|
206
|
+
* 비동기 설정으로 모듈 등록
|
|
207
|
+
*/
|
|
208
|
+
static forRootAsync(asyncOptions: SidequestModuleAsyncOptions): DynamicModule;
|
|
209
|
+
/**
|
|
210
|
+
* 큐별 DI 프로바이더 생성
|
|
211
|
+
*/
|
|
212
|
+
private static createQueueProviders;
|
|
213
|
+
/**
|
|
214
|
+
* 비동기 프로바이더 생성
|
|
215
|
+
*/
|
|
216
|
+
private static createAsyncProviders;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* @Processor 데코레이터 옵션
|
|
221
|
+
*/
|
|
222
|
+
interface ProcessorOptions {
|
|
223
|
+
/**
|
|
224
|
+
* 동시 처리 작업 수 (큐 설정 오버라이드)
|
|
225
|
+
*/
|
|
226
|
+
concurrency?: number;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* 프로세서 메타데이터
|
|
230
|
+
*/
|
|
231
|
+
interface ProcessorMetadata {
|
|
232
|
+
/** 큐 이름 */
|
|
233
|
+
queueName: string;
|
|
234
|
+
/** 프로세서 옵션 */
|
|
235
|
+
options?: ProcessorOptions;
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* @OnJob 데코레이터 옵션
|
|
239
|
+
*/
|
|
240
|
+
interface OnJobOptions {
|
|
241
|
+
/**
|
|
242
|
+
* Job 우선순위 (높을수록 먼저 처리)
|
|
243
|
+
*/
|
|
244
|
+
priority?: number;
|
|
245
|
+
/**
|
|
246
|
+
* 최대 실행 시간 (ms)
|
|
247
|
+
*/
|
|
248
|
+
timeout?: number;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* OnJob 핸들러 메타데이터
|
|
252
|
+
*/
|
|
253
|
+
interface OnJobMetadata {
|
|
254
|
+
/** Job 이름 */
|
|
255
|
+
jobName: string;
|
|
256
|
+
/** Job 옵션 */
|
|
257
|
+
options?: OnJobOptions;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* 등록된 핸들러 정보
|
|
261
|
+
*/
|
|
262
|
+
interface RegisteredHandler {
|
|
263
|
+
/** 메서드 이름 */
|
|
264
|
+
methodName: string;
|
|
265
|
+
/** Job 이름 */
|
|
266
|
+
jobName: string;
|
|
267
|
+
/** Job 옵션 */
|
|
268
|
+
options?: OnJobOptions;
|
|
269
|
+
/** 재시도 옵션 */
|
|
270
|
+
retryOptions?: RetryOptions;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* 재시도 옵션
|
|
274
|
+
*/
|
|
275
|
+
interface RetryOptions {
|
|
276
|
+
/**
|
|
277
|
+
* 최대 시도 횟수 (기본값: 3)
|
|
278
|
+
*/
|
|
279
|
+
maxAttempts?: number;
|
|
280
|
+
/**
|
|
281
|
+
* 재시도 지연 시간 (ms, 기본값: 1000)
|
|
282
|
+
*/
|
|
283
|
+
delay?: number;
|
|
284
|
+
/**
|
|
285
|
+
* 백오프 전략 (기본값: 'exponential')
|
|
286
|
+
*/
|
|
287
|
+
backoff?: 'fixed' | 'exponential';
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* 등록된 프로세서 정보
|
|
291
|
+
*/
|
|
292
|
+
interface RegisteredProcessor {
|
|
293
|
+
/** 큐 이름 */
|
|
294
|
+
queueName: string;
|
|
295
|
+
/** 프로세서 인스턴스 */
|
|
296
|
+
instance: unknown;
|
|
297
|
+
/** 프로세서 클래스 */
|
|
298
|
+
metatype: Function;
|
|
299
|
+
/** 등록된 핸들러 맵 (jobName -> handler) */
|
|
300
|
+
handlers: Map<string, RegisteredHandler>;
|
|
301
|
+
/** 완료 이벤트 핸들러 맵 (jobName -> methodName) */
|
|
302
|
+
completeHandlers: Map<string, string>;
|
|
303
|
+
/** 실패 이벤트 핸들러 맵 (jobName -> methodName) */
|
|
304
|
+
failedHandlers: Map<string, string>;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* 클래스를 Sidequest Job Processor로 지정합니다.
|
|
309
|
+
*
|
|
310
|
+
* @Processor 데코레이터가 붙은 클래스는 모듈 초기화 시 자동으로 스캔되어
|
|
311
|
+
* 해당 큐의 Job 핸들러로 등록됩니다.
|
|
312
|
+
*
|
|
313
|
+
* @param queueName - 처리할 큐 이름
|
|
314
|
+
* @param options - 프로세서 옵션
|
|
315
|
+
*
|
|
316
|
+
* @example
|
|
317
|
+
* ```typescript
|
|
318
|
+
* @Processor('email')
|
|
319
|
+
* export class EmailProcessor {
|
|
320
|
+
* @OnJob('send-welcome-email')
|
|
321
|
+
* async handleWelcomeEmail(job: JobContext) {
|
|
322
|
+
* const { to, subject, body } = job.data;
|
|
323
|
+
* await this.mailer.send({ to, subject, body });
|
|
324
|
+
* }
|
|
325
|
+
* }
|
|
326
|
+
*
|
|
327
|
+
* // 동시성 설정과 함께
|
|
328
|
+
* @Processor('report', { concurrency: 2 })
|
|
329
|
+
* export class ReportProcessor {
|
|
330
|
+
* @OnJob('generate-daily-report')
|
|
331
|
+
* async handleDailyReport(job: JobContext) {
|
|
332
|
+
* // 리포트 생성 로직
|
|
333
|
+
* }
|
|
334
|
+
* }
|
|
335
|
+
* ```
|
|
336
|
+
*/
|
|
337
|
+
declare function Processor(queueName: string, options?: ProcessorOptions): ClassDecorator;
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* 메서드를 특정 Job 타입의 핸들러로 지정합니다.
|
|
341
|
+
*
|
|
342
|
+
* @Processor 데코레이터가 붙은 클래스 내부에서 사용합니다.
|
|
343
|
+
* 해당 Job이 큐에서 처리될 때 이 메서드가 호출됩니다.
|
|
344
|
+
*
|
|
345
|
+
* @param jobName - Job 이름 (일반적으로 Job 클래스명)
|
|
346
|
+
* @param options - Job 실행 옵션 (우선순위, 타임아웃 등)
|
|
347
|
+
*
|
|
348
|
+
* @example
|
|
349
|
+
* ```typescript
|
|
350
|
+
* @Processor('email')
|
|
351
|
+
* export class EmailProcessor {
|
|
352
|
+
* @OnJob('SendWelcomeEmailJob')
|
|
353
|
+
* async handleWelcomeEmail(job: JobContext) {
|
|
354
|
+
* const { to, subject, body } = job.data;
|
|
355
|
+
* await this.mailer.send({ to, subject, body });
|
|
356
|
+
* }
|
|
357
|
+
*
|
|
358
|
+
* // 우선순위와 타임아웃 설정
|
|
359
|
+
* @OnJob('SendPriorityEmailJob', { priority: 100, timeout: 5000 })
|
|
360
|
+
* async handlePriorityEmail(job: JobContext) {
|
|
361
|
+
* // 우선순위가 높은 이메일 처리
|
|
362
|
+
* }
|
|
363
|
+
* }
|
|
364
|
+
* ```
|
|
365
|
+
*/
|
|
366
|
+
declare function OnJob(jobName: string, options?: OnJobOptions): MethodDecorator;
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Job 재시도 정책을 설정합니다.
|
|
370
|
+
*
|
|
371
|
+
* @OnJob 데코레이터와 함께 사용하여 Job 실패 시 재시도 동작을 정의합니다.
|
|
372
|
+
*
|
|
373
|
+
* @param options - 재시도 옵션
|
|
374
|
+
*
|
|
375
|
+
* @example
|
|
376
|
+
* ```typescript
|
|
377
|
+
* @Processor('payment')
|
|
378
|
+
* export class PaymentProcessor {
|
|
379
|
+
* // 고정 지연 재시도
|
|
380
|
+
* @OnJob('ProcessPaymentJob')
|
|
381
|
+
* @Retry({ maxAttempts: 3, delay: 1000 })
|
|
382
|
+
* async handlePayment(job: JobContext) {
|
|
383
|
+
* // 결제 처리 로직
|
|
384
|
+
* }
|
|
385
|
+
*
|
|
386
|
+
* // 지수 백오프 재시도
|
|
387
|
+
* @OnJob('RefundJob')
|
|
388
|
+
* @Retry({ maxAttempts: 5, delay: 500, backoff: 'exponential' })
|
|
389
|
+
* async handleRefund(job: JobContext) {
|
|
390
|
+
* // 환불 처리 로직
|
|
391
|
+
* }
|
|
392
|
+
* }
|
|
393
|
+
* ```
|
|
394
|
+
*/
|
|
395
|
+
declare function Retry(options: RetryOptions): MethodDecorator;
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* Queue 인스턴스를 주입합니다.
|
|
399
|
+
*
|
|
400
|
+
* SidequestModule.forRoot()에서 등록된 큐를 서비스에 주입받아 사용할 수 있습니다.
|
|
401
|
+
*
|
|
402
|
+
* @param queueName - 주입받을 큐 이름
|
|
403
|
+
*
|
|
404
|
+
* @example
|
|
405
|
+
* ```typescript
|
|
406
|
+
* @Injectable()
|
|
407
|
+
* export class UserService {
|
|
408
|
+
* constructor(
|
|
409
|
+
* @InjectQueue('email') private emailQueue: IQueueService,
|
|
410
|
+
* @InjectQueue('notification') private notificationQueue: IQueueService,
|
|
411
|
+
* ) {}
|
|
412
|
+
*
|
|
413
|
+
* async createUser(email: string, name: string) {
|
|
414
|
+
* // 사용자 생성 로직...
|
|
415
|
+
*
|
|
416
|
+
* // 환영 이메일 발송
|
|
417
|
+
* await this.emailQueue.add(SendWelcomeEmailJob, email, `Welcome, ${name}!`);
|
|
418
|
+
*
|
|
419
|
+
* // 알림 발송
|
|
420
|
+
* await this.notificationQueue.add(SendNotificationJob, email, 'new-user');
|
|
421
|
+
* }
|
|
422
|
+
* }
|
|
423
|
+
* ```
|
|
424
|
+
*/
|
|
425
|
+
declare function InjectQueue(queueName: string): ParameterDecorator;
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Job 완료 이벤트 핸들러를 지정합니다.
|
|
429
|
+
*
|
|
430
|
+
* @Processor 클래스 내부에서 사용하며, Job이 성공적으로 완료되면 호출됩니다.
|
|
431
|
+
*
|
|
432
|
+
* @param jobName - 대상 Job 이름 (생략 시 모든 Job에 대해 호출)
|
|
433
|
+
*
|
|
434
|
+
* @example
|
|
435
|
+
* ```typescript
|
|
436
|
+
* @Processor('email')
|
|
437
|
+
* export class EmailProcessor {
|
|
438
|
+
* @OnJob('SendWelcomeEmailJob')
|
|
439
|
+
* async handleWelcomeEmail(job: JobContext) {
|
|
440
|
+
* await this.mailer.send(job.data);
|
|
441
|
+
* }
|
|
442
|
+
*
|
|
443
|
+
* // 특정 Job 완료 이벤트
|
|
444
|
+
* @OnJobComplete('SendWelcomeEmailJob')
|
|
445
|
+
* async onWelcomeEmailComplete(event: JobCompleteEvent) {
|
|
446
|
+
* this.logger.log(`환영 이메일 발송 완료: ${event.result}`);
|
|
447
|
+
* await this.analytics.track('email_sent', { type: 'welcome' });
|
|
448
|
+
* }
|
|
449
|
+
*
|
|
450
|
+
* // 모든 Job 완료 이벤트 (jobName 생략)
|
|
451
|
+
* @OnJobComplete()
|
|
452
|
+
* async onAnyJobComplete(event: JobCompleteEvent) {
|
|
453
|
+
* this.logger.log(`Job 완료: ${event.jobName}`);
|
|
454
|
+
* }
|
|
455
|
+
* }
|
|
456
|
+
* ```
|
|
457
|
+
*/
|
|
458
|
+
declare function OnJobComplete(jobName?: string): MethodDecorator;
|
|
459
|
+
/**
|
|
460
|
+
* Job 완료 이벤트 데이터
|
|
461
|
+
*/
|
|
462
|
+
interface JobCompleteEvent {
|
|
463
|
+
/** Job 이름 */
|
|
464
|
+
jobName: string;
|
|
465
|
+
/** Job 실행 인자 */
|
|
466
|
+
args: unknown[];
|
|
467
|
+
/** Job 실행 결과 */
|
|
468
|
+
result: unknown;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* Job 실패 이벤트 핸들러를 지정합니다.
|
|
473
|
+
*
|
|
474
|
+
* @Processor 클래스 내부에서 사용하며, Job 실행이 실패하면 호출됩니다.
|
|
475
|
+
* 재시도 횟수를 모두 소진한 후 최종 실패 시에만 호출됩니다.
|
|
476
|
+
*
|
|
477
|
+
* @param jobName - 대상 Job 이름 (생략 시 모든 Job에 대해 호출)
|
|
478
|
+
*
|
|
479
|
+
* @example
|
|
480
|
+
* ```typescript
|
|
481
|
+
* @Processor('payment')
|
|
482
|
+
* export class PaymentProcessor {
|
|
483
|
+
* @OnJob('ProcessPaymentJob')
|
|
484
|
+
* @Retry({ maxAttempts: 3 })
|
|
485
|
+
* async handlePayment(job: JobContext) {
|
|
486
|
+
* await this.paymentService.process(job.data);
|
|
487
|
+
* }
|
|
488
|
+
*
|
|
489
|
+
* // 특정 Job 실패 이벤트
|
|
490
|
+
* @OnJobFailed('ProcessPaymentJob')
|
|
491
|
+
* async onPaymentFailed(event: JobFailedEvent) {
|
|
492
|
+
* this.logger.error(`결제 처리 실패: ${event.error.message}`);
|
|
493
|
+
* await this.alertService.notify('payment-failure', {
|
|
494
|
+
* orderId: event.args[0],
|
|
495
|
+
* error: event.error.message,
|
|
496
|
+
* });
|
|
497
|
+
* }
|
|
498
|
+
*
|
|
499
|
+
* // 모든 Job 실패 이벤트 (jobName 생략)
|
|
500
|
+
* @OnJobFailed()
|
|
501
|
+
* async onAnyJobFailed(event: JobFailedEvent) {
|
|
502
|
+
* this.logger.error(`Job 실패: ${event.jobName}`, event.error);
|
|
503
|
+
* }
|
|
504
|
+
* }
|
|
505
|
+
* ```
|
|
506
|
+
*/
|
|
507
|
+
declare function OnJobFailed(jobName?: string): MethodDecorator;
|
|
508
|
+
/**
|
|
509
|
+
* Job 실패 이벤트 데이터
|
|
510
|
+
*/
|
|
511
|
+
interface JobFailedEvent {
|
|
512
|
+
/** Job 이름 */
|
|
513
|
+
jobName: string;
|
|
514
|
+
/** Job 실행 인자 */
|
|
515
|
+
args: unknown[];
|
|
516
|
+
/** 발생한 에러 */
|
|
517
|
+
error: Error;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* Queue 서비스 인터페이스
|
|
522
|
+
* @InjectQueue()로 주입되는 객체의 타입
|
|
523
|
+
*/
|
|
524
|
+
interface IQueueService {
|
|
525
|
+
/** 큐 이름 */
|
|
526
|
+
readonly name: string;
|
|
527
|
+
/**
|
|
528
|
+
* Job 추가
|
|
529
|
+
*
|
|
530
|
+
* @param JobClass - Job 클래스
|
|
531
|
+
* @param args - Job 실행 인자
|
|
532
|
+
* @returns Job ID
|
|
533
|
+
*
|
|
534
|
+
* @example
|
|
535
|
+
* ```typescript
|
|
536
|
+
* await this.emailQueue.add(SendEmailJob, 'user@example.com', 'Welcome!');
|
|
537
|
+
* ```
|
|
538
|
+
*/
|
|
539
|
+
add<T>(JobClass: new (...args: unknown[]) => T, ...args: unknown[]): Promise<string>;
|
|
540
|
+
/**
|
|
541
|
+
* Job 추가 (옵션 포함)
|
|
542
|
+
*
|
|
543
|
+
* @param JobClass - Job 클래스
|
|
544
|
+
* @param options - Job 옵션
|
|
545
|
+
* @param args - Job 실행 인자
|
|
546
|
+
* @returns Job ID
|
|
547
|
+
*
|
|
548
|
+
* @example
|
|
549
|
+
* ```typescript
|
|
550
|
+
* await this.emailQueue.addWithOptions(
|
|
551
|
+
* SendEmailJob,
|
|
552
|
+
* { priority: 10, timeout: 5000 },
|
|
553
|
+
* 'user@example.com',
|
|
554
|
+
* 'Welcome!'
|
|
555
|
+
* );
|
|
556
|
+
* ```
|
|
557
|
+
*/
|
|
558
|
+
addWithOptions<T>(JobClass: new (...args: unknown[]) => T, options: JobAddOptions, ...args: unknown[]): Promise<string>;
|
|
559
|
+
/**
|
|
560
|
+
* 예약된 Job 추가
|
|
561
|
+
*
|
|
562
|
+
* @param JobClass - Job 클래스
|
|
563
|
+
* @param scheduledAt - 실행 예정 시간
|
|
564
|
+
* @param args - Job 실행 인자
|
|
565
|
+
* @returns Job ID
|
|
566
|
+
*
|
|
567
|
+
* @example
|
|
568
|
+
* ```typescript
|
|
569
|
+
* const tomorrow = new Date();
|
|
570
|
+
* tomorrow.setDate(tomorrow.getDate() + 1);
|
|
571
|
+
* await this.emailQueue.addScheduled(SendEmailJob, tomorrow, 'user@example.com');
|
|
572
|
+
* ```
|
|
573
|
+
*/
|
|
574
|
+
addScheduled<T>(JobClass: new (...args: unknown[]) => T, scheduledAt: Date, ...args: unknown[]): Promise<string>;
|
|
575
|
+
/**
|
|
576
|
+
* Bulk Job 추가
|
|
577
|
+
*
|
|
578
|
+
* @param jobs - Job 목록
|
|
579
|
+
* @returns Job ID 배열
|
|
580
|
+
*/
|
|
581
|
+
addBulk<T>(jobs: Array<{
|
|
582
|
+
JobClass: new (...args: unknown[]) => T;
|
|
583
|
+
args: unknown[];
|
|
584
|
+
options?: JobAddOptions;
|
|
585
|
+
}>): Promise<string[]>;
|
|
586
|
+
}
|
|
587
|
+
/**
|
|
588
|
+
* Job 추가 옵션
|
|
589
|
+
*/
|
|
590
|
+
interface JobAddOptions {
|
|
591
|
+
/**
|
|
592
|
+
* 우선순위 (높을수록 먼저 처리)
|
|
593
|
+
*/
|
|
594
|
+
priority?: number;
|
|
595
|
+
/**
|
|
596
|
+
* 최대 실행 시간 (ms)
|
|
597
|
+
*/
|
|
598
|
+
timeout?: number;
|
|
599
|
+
/**
|
|
600
|
+
* 최대 시도 횟수
|
|
601
|
+
*/
|
|
602
|
+
maxAttempts?: number;
|
|
603
|
+
/**
|
|
604
|
+
* 재시도 간격 (ms, 기본값: 1000)
|
|
605
|
+
*/
|
|
606
|
+
retryDelay?: number;
|
|
607
|
+
/**
|
|
608
|
+
* 백오프 전략
|
|
609
|
+
*/
|
|
610
|
+
backoffStrategy?: 'fixed' | 'exponential';
|
|
611
|
+
/**
|
|
612
|
+
* 고유성 키 (중복 방지)
|
|
613
|
+
*/
|
|
614
|
+
uniqueKey?: string;
|
|
615
|
+
/**
|
|
616
|
+
* 예약 실행 시간
|
|
617
|
+
*/
|
|
618
|
+
scheduledAt?: Date;
|
|
619
|
+
/**
|
|
620
|
+
* 메타데이터 (traceId 등)
|
|
621
|
+
*/
|
|
622
|
+
metadata?: Record<string, unknown>;
|
|
623
|
+
}
|
|
624
|
+
/**
|
|
625
|
+
* 에러 데이터
|
|
626
|
+
*/
|
|
627
|
+
interface ErrorData {
|
|
628
|
+
/** 에러 이름 */
|
|
629
|
+
name?: string | null;
|
|
630
|
+
/** 에러 메시지 */
|
|
631
|
+
message?: string | null;
|
|
632
|
+
/** 스택 트레이스 */
|
|
633
|
+
stack?: string | null;
|
|
634
|
+
/** 추가 데이터 */
|
|
635
|
+
[key: string]: unknown;
|
|
636
|
+
}
|
|
637
|
+
/**
|
|
638
|
+
* Job 정보
|
|
639
|
+
*/
|
|
640
|
+
interface JobInfo {
|
|
641
|
+
/** Job ID */
|
|
642
|
+
id: string;
|
|
643
|
+
/** 큐 이름 */
|
|
644
|
+
queue: string;
|
|
645
|
+
/** Job 이름 */
|
|
646
|
+
name?: string;
|
|
647
|
+
/** 현재 상태 */
|
|
648
|
+
state: JobState;
|
|
649
|
+
/** 시도 횟수 */
|
|
650
|
+
attempt: number;
|
|
651
|
+
/** 최대 시도 횟수 */
|
|
652
|
+
maxAttempts: number;
|
|
653
|
+
/** 실행 인자 */
|
|
654
|
+
args?: unknown[];
|
|
655
|
+
/** 실행 결과 */
|
|
656
|
+
result?: unknown;
|
|
657
|
+
/** 발생한 에러 목록 */
|
|
658
|
+
errors?: ErrorData[] | null;
|
|
659
|
+
/** 생성 시간 */
|
|
660
|
+
insertedAt: Date;
|
|
661
|
+
/** 시도 시간 */
|
|
662
|
+
attemptedAt?: Date;
|
|
663
|
+
/** 완료 시간 */
|
|
664
|
+
completedAt?: Date;
|
|
665
|
+
/** 메타데이터 */
|
|
666
|
+
metadata?: Record<string, unknown>;
|
|
667
|
+
}
|
|
668
|
+
/**
|
|
669
|
+
* Job 상태
|
|
670
|
+
*/
|
|
671
|
+
type JobState = 'available' | 'scheduled' | 'executing' | 'completed' | 'failed' | 'discarded' | 'cancelled';
|
|
672
|
+
|
|
673
|
+
type JobClassType = new (...args: unknown[]) => Job;
|
|
674
|
+
/**
|
|
675
|
+
* Sidequest.js API Adapter
|
|
676
|
+
*
|
|
677
|
+
* Sidequest.js의 API를 NestJS와 통합합니다.
|
|
678
|
+
* 실제 sidequest 패키지를 사용하여 Job 처리를 수행합니다.
|
|
679
|
+
*/
|
|
680
|
+
declare class SidequestAdapter {
|
|
681
|
+
private readonly logger;
|
|
682
|
+
private isStarted;
|
|
683
|
+
private readonly registeredJobs;
|
|
684
|
+
/**
|
|
685
|
+
* Sidequest 엔진 시작
|
|
686
|
+
*/
|
|
687
|
+
start(options: SidequestModuleOptions): Promise<void>;
|
|
688
|
+
/**
|
|
689
|
+
* Sidequest 엔진 종료
|
|
690
|
+
*/
|
|
691
|
+
shutdown(): Promise<void>;
|
|
692
|
+
/**
|
|
693
|
+
* Job 클래스 등록
|
|
694
|
+
*/
|
|
695
|
+
registerJob(jobName: string, JobClass: JobClassType): void;
|
|
696
|
+
/**
|
|
697
|
+
* Job 추가
|
|
698
|
+
*/
|
|
699
|
+
addJob(queueName: string, jobName: string, args: unknown[], options?: JobAddOptions): Promise<string>;
|
|
700
|
+
/**
|
|
701
|
+
* Bulk Job 추가
|
|
702
|
+
*/
|
|
703
|
+
addBulkJobs(queueName: string, jobs: Array<{
|
|
704
|
+
jobName: string;
|
|
705
|
+
args: unknown[];
|
|
706
|
+
options?: JobAddOptions;
|
|
707
|
+
}>): Promise<string[]>;
|
|
708
|
+
/**
|
|
709
|
+
* Job 조회
|
|
710
|
+
*/
|
|
711
|
+
getJob(jobId: string | number): Promise<JobInfo | undefined>;
|
|
712
|
+
/**
|
|
713
|
+
* 엔진 시작 여부
|
|
714
|
+
*/
|
|
715
|
+
get started(): boolean;
|
|
716
|
+
/**
|
|
717
|
+
* 등록된 Job 클래스 반환
|
|
718
|
+
*/
|
|
719
|
+
getRegisteredJob(jobName: string): JobClassType | undefined;
|
|
720
|
+
/**
|
|
721
|
+
* 모든 등록된 Job 클래스 반환
|
|
722
|
+
*/
|
|
723
|
+
getAllRegisteredJobs(): Map<string, JobClassType>;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
/**
|
|
727
|
+
* Sidequest 엔진 관리 서비스
|
|
728
|
+
*
|
|
729
|
+
* Sidequest.js 엔진의 라이프사이클을 관리합니다.
|
|
730
|
+
* 모듈 초기화 시 엔진을 시작하고, 종료 시 graceful shutdown을 수행합니다.
|
|
731
|
+
*/
|
|
732
|
+
declare class SidequestEngineService implements OnModuleInit, OnModuleDestroy {
|
|
733
|
+
private readonly options;
|
|
734
|
+
private readonly adapter;
|
|
735
|
+
private readonly logger;
|
|
736
|
+
constructor(options: SidequestModuleOptions, adapter: SidequestAdapter);
|
|
737
|
+
/**
|
|
738
|
+
* 모듈 초기화 시 엔진 시작
|
|
739
|
+
*/
|
|
740
|
+
onModuleInit(): Promise<void>;
|
|
741
|
+
/**
|
|
742
|
+
* 모듈 종료 시 엔진 종료
|
|
743
|
+
*/
|
|
744
|
+
onModuleDestroy(): Promise<void>;
|
|
745
|
+
/**
|
|
746
|
+
* 엔진 시작 여부
|
|
747
|
+
*/
|
|
748
|
+
get isStarted(): boolean;
|
|
749
|
+
/**
|
|
750
|
+
* Adapter 인스턴스 반환
|
|
751
|
+
*/
|
|
752
|
+
getAdapter(): SidequestAdapter;
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
/**
|
|
756
|
+
* Queue 레지스트리 서비스
|
|
757
|
+
*
|
|
758
|
+
* 큐 인스턴스를 관리하고 @InjectQueue()를 위한 DI 프로바이더를 제공합니다.
|
|
759
|
+
*/
|
|
760
|
+
declare class QueueRegistryService {
|
|
761
|
+
private readonly options;
|
|
762
|
+
private readonly adapter;
|
|
763
|
+
private readonly logger;
|
|
764
|
+
private readonly queueServices;
|
|
765
|
+
constructor(options: SidequestModuleOptions, adapter: SidequestAdapter);
|
|
766
|
+
/**
|
|
767
|
+
* 설정된 큐 초기화
|
|
768
|
+
*/
|
|
769
|
+
private initializeQueues;
|
|
770
|
+
/**
|
|
771
|
+
* 큐 등록
|
|
772
|
+
*/
|
|
773
|
+
registerQueue(config: QueueConfig): void;
|
|
774
|
+
/**
|
|
775
|
+
* 큐 서비스 조회
|
|
776
|
+
*/
|
|
777
|
+
getQueue(name: string): IQueueService | undefined;
|
|
778
|
+
/**
|
|
779
|
+
* 큐 서비스 조회 (없으면 에러)
|
|
780
|
+
*/
|
|
781
|
+
getQueueOrThrow(name: string): IQueueService;
|
|
782
|
+
/**
|
|
783
|
+
* 모든 큐 조회
|
|
784
|
+
*/
|
|
785
|
+
getAllQueues(): Map<string, IQueueService>;
|
|
786
|
+
/**
|
|
787
|
+
* 큐 서비스 인스턴스 생성
|
|
788
|
+
*/
|
|
789
|
+
private createQueueService;
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
interface ClsService {
|
|
793
|
+
getId(): string | undefined;
|
|
794
|
+
get<T>(key: string): T | undefined;
|
|
795
|
+
set<T>(key: string, value: T): void;
|
|
796
|
+
run<T>(callback: () => T): T;
|
|
797
|
+
}
|
|
798
|
+
/**
|
|
799
|
+
* CLS(Continuation Local Storage) 통합 서비스
|
|
800
|
+
*
|
|
801
|
+
* nestjs-cls가 설치되어 있을 경우 Job 실행 시 context를 전파합니다.
|
|
802
|
+
* nestjs-cls가 없으면 no-op으로 동작합니다.
|
|
803
|
+
*/
|
|
804
|
+
declare class ClsIntegrationService {
|
|
805
|
+
private readonly logger;
|
|
806
|
+
private readonly enabled;
|
|
807
|
+
private clsService;
|
|
808
|
+
constructor(options: SidequestModuleOptions, clsService?: ClsService);
|
|
809
|
+
/**
|
|
810
|
+
* CLS context 내에서 콜백 실행
|
|
811
|
+
*/
|
|
812
|
+
runInContext<T>(metadata: Record<string, unknown>, callback: () => Promise<T>): Promise<T>;
|
|
813
|
+
/**
|
|
814
|
+
* 현재 traceId 조회
|
|
815
|
+
*/
|
|
816
|
+
getTraceId(): string | undefined;
|
|
817
|
+
/**
|
|
818
|
+
* traceId 설정
|
|
819
|
+
*/
|
|
820
|
+
setTraceId(traceId: string): void;
|
|
821
|
+
/**
|
|
822
|
+
* CLS에서 값 조회
|
|
823
|
+
*/
|
|
824
|
+
get<T>(key: string): T | undefined;
|
|
825
|
+
/**
|
|
826
|
+
* CLS에 값 설정
|
|
827
|
+
*/
|
|
828
|
+
set<T>(key: string, value: T): void;
|
|
829
|
+
/**
|
|
830
|
+
* CLS 통합 활성화 여부
|
|
831
|
+
*/
|
|
832
|
+
isEnabled(): boolean;
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
/**
|
|
836
|
+
* Processor 레지스트리 서비스
|
|
837
|
+
*
|
|
838
|
+
* @Processor 데코레이터로 등록된 프로세서와 핸들러를 관리합니다.
|
|
839
|
+
*/
|
|
840
|
+
declare class ProcessorRegistryService {
|
|
841
|
+
private readonly clsService?;
|
|
842
|
+
private readonly logger;
|
|
843
|
+
private readonly processors;
|
|
844
|
+
constructor(clsService?: ClsIntegrationService | undefined);
|
|
845
|
+
/**
|
|
846
|
+
* 프로세서 등록
|
|
847
|
+
*/
|
|
848
|
+
register(processor: RegisteredProcessor): void;
|
|
849
|
+
/**
|
|
850
|
+
* 프로세서 조회
|
|
851
|
+
*/
|
|
852
|
+
getProcessor(queueName: string): RegisteredProcessor | undefined;
|
|
853
|
+
/**
|
|
854
|
+
* 모든 프로세서 조회
|
|
855
|
+
*/
|
|
856
|
+
getAllProcessors(): Map<string, RegisteredProcessor>;
|
|
857
|
+
/**
|
|
858
|
+
* Job 핸들러 조회
|
|
859
|
+
*/
|
|
860
|
+
getJobHandler(queueName: string, jobName: string): RegisteredHandler | undefined;
|
|
861
|
+
/**
|
|
862
|
+
* Job 완료 핸들러 조회
|
|
863
|
+
*/
|
|
864
|
+
getCompleteHandler(queueName: string, jobName: string): string | undefined;
|
|
865
|
+
/**
|
|
866
|
+
* Job 실패 핸들러 조회
|
|
867
|
+
*/
|
|
868
|
+
getFailedHandler(queueName: string, jobName: string): string | undefined;
|
|
869
|
+
/**
|
|
870
|
+
* Job 디스패치 (실행)
|
|
871
|
+
*
|
|
872
|
+
* @param queueName - 큐 이름
|
|
873
|
+
* @param jobName - Job 이름
|
|
874
|
+
* @param args - Job 인자
|
|
875
|
+
* @param metadata - Job 메타데이터 (traceId 등)
|
|
876
|
+
*/
|
|
877
|
+
dispatch(queueName: string, jobName: string, args: unknown[], metadata?: Record<string, unknown>): Promise<unknown>;
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
/**
|
|
881
|
+
* SidequestModule 상수
|
|
882
|
+
*/
|
|
883
|
+
declare const SIDEQUEST_MODULE_OPTIONS: unique symbol;
|
|
884
|
+
declare const SIDEQUEST_ENGINE: unique symbol;
|
|
885
|
+
declare const PROCESSOR_METADATA_KEY: unique symbol;
|
|
886
|
+
declare const ON_JOB_METADATA_KEY: unique symbol;
|
|
887
|
+
declare const RETRY_OPTIONS_METADATA_KEY: unique symbol;
|
|
888
|
+
declare const ON_JOB_COMPLETE_METADATA_KEY: unique symbol;
|
|
889
|
+
declare const ON_JOB_FAILED_METADATA_KEY: unique symbol;
|
|
890
|
+
declare const QUEUE_TOKEN_PREFIX = "SIDEQUEST_QUEUE_";
|
|
891
|
+
/**
|
|
892
|
+
* Queue DI 토큰 생성
|
|
893
|
+
*/
|
|
894
|
+
declare function getQueueToken(queueName: string): string;
|
|
895
|
+
declare const DEFAULT_QUEUE_NAME = "default";
|
|
896
|
+
declare const DEFAULT_MAX_ATTEMPTS = 3;
|
|
897
|
+
declare const DEFAULT_TIMEOUT = 30000;
|
|
898
|
+
declare const DEFAULT_CONCURRENCY = 10;
|
|
899
|
+
|
|
900
|
+
export { type BackendConfig, ClsIntegrationService, DEFAULT_CONCURRENCY, DEFAULT_MAX_ATTEMPTS, DEFAULT_QUEUE_NAME, DEFAULT_TIMEOUT, type DashboardConfig, type GracefulShutdownConfig, type IQueueService, InjectQueue, type JobAddOptions, type JobCompleteEvent, type JobFailedEvent, type JobInfo, type JobState, ON_JOB_COMPLETE_METADATA_KEY, ON_JOB_FAILED_METADATA_KEY, ON_JOB_METADATA_KEY, OnJob, OnJobComplete, OnJobFailed, type OnJobMetadata, type OnJobOptions, PROCESSOR_METADATA_KEY, Processor, type ProcessorMetadata, type ProcessorOptions, ProcessorRegistryService, QUEUE_TOKEN_PREFIX, type QueueConfig, QueueRegistryService, RETRY_OPTIONS_METADATA_KEY, type RegisteredHandler, type RegisteredProcessor, Retry, type RetryOptions, SIDEQUEST_ENGINE, SIDEQUEST_MODULE_OPTIONS, SidequestAdapter, SidequestEngineService, SidequestModule, type SidequestModuleAsyncOptions, type SidequestModuleOptions, type SidequestOptionsFactory, getQueueToken };
|