@objectstack/service-queue 4.0.3 → 4.0.5
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.md +453 -0
- package/package.json +31 -6
- package/.turbo/turbo-build.log +0 -22
- package/CHANGELOG.md +0 -169
- package/src/bullmq-queue-adapter.ts +0 -58
- package/src/index.ts +0 -8
- package/src/memory-queue-adapter.test.ts +0 -87
- package/src/memory-queue-adapter.ts +0 -82
- package/src/queue-service-plugin.ts +0 -62
- package/tsconfig.json +0 -17
package/README.md
ADDED
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
# @objectstack/service-queue
|
|
2
|
+
|
|
3
|
+
Queue Service for ObjectStack — implements `IQueueService` with in-memory and BullMQ adapters.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Multiple Adapters**: In-memory (development) and BullMQ/Redis (production)
|
|
8
|
+
- **Job Queues**: Organize work into named queues with priorities
|
|
9
|
+
- **Worker Pools**: Process jobs concurrently with configurable workers
|
|
10
|
+
- **Retry Logic**: Automatic retry with exponential backoff
|
|
11
|
+
- **Job Scheduling**: Delay job execution or schedule for future
|
|
12
|
+
- **Progress Tracking**: Track job progress and completion
|
|
13
|
+
- **Job Events**: Listen to job lifecycle events (active, completed, failed)
|
|
14
|
+
- **Rate Limiting**: Control job processing rate
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pnpm add @objectstack/service-queue
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
For BullMQ adapter (production):
|
|
23
|
+
```bash
|
|
24
|
+
pnpm add bullmq ioredis
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Basic Usage
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import { defineStack } from '@objectstack/spec';
|
|
31
|
+
import { ServiceQueue } from '@objectstack/service-queue';
|
|
32
|
+
|
|
33
|
+
const stack = defineStack({
|
|
34
|
+
services: [
|
|
35
|
+
ServiceQueue.configure({
|
|
36
|
+
adapter: 'memory', // or 'bullmq'
|
|
37
|
+
defaultQueue: 'default',
|
|
38
|
+
}),
|
|
39
|
+
],
|
|
40
|
+
});
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Configuration
|
|
44
|
+
|
|
45
|
+
### In-Memory Adapter (Development)
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
ServiceQueue.configure({
|
|
49
|
+
adapter: 'memory',
|
|
50
|
+
concurrency: 5, // Max concurrent jobs
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### BullMQ Adapter (Production)
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
ServiceQueue.configure({
|
|
58
|
+
adapter: 'bullmq',
|
|
59
|
+
redis: {
|
|
60
|
+
host: 'localhost',
|
|
61
|
+
port: 6379,
|
|
62
|
+
password: process.env.REDIS_PASSWORD,
|
|
63
|
+
},
|
|
64
|
+
queues: {
|
|
65
|
+
default: { concurrency: 10 },
|
|
66
|
+
email: { concurrency: 5, rateLimit: { max: 100, duration: 60000 } },
|
|
67
|
+
reports: { concurrency: 2 },
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Service API
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
// Get queue service
|
|
76
|
+
const queue = kernel.getService<IQueueService>('queue');
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Adding Jobs
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
// Add a simple job
|
|
83
|
+
await queue.add('email', 'send_welcome', {
|
|
84
|
+
to: 'user@example.com',
|
|
85
|
+
template: 'welcome',
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// Add job with options
|
|
89
|
+
await queue.add('reports', 'generate_monthly', {
|
|
90
|
+
month: '2024-01',
|
|
91
|
+
format: 'pdf',
|
|
92
|
+
}, {
|
|
93
|
+
priority: 1, // Higher number = higher priority
|
|
94
|
+
attempts: 3, // Retry up to 3 times
|
|
95
|
+
backoff: {
|
|
96
|
+
type: 'exponential',
|
|
97
|
+
delay: 1000,
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// Add delayed job (runs in 1 hour)
|
|
102
|
+
await queue.add('notifications', 'reminder', {
|
|
103
|
+
userId: '123',
|
|
104
|
+
message: 'Don't forget!',
|
|
105
|
+
}, {
|
|
106
|
+
delay: 3600000, // 1 hour in milliseconds
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// Schedule job for specific time
|
|
110
|
+
await queue.add('cleanup', 'old_files', {}, {
|
|
111
|
+
timestamp: new Date('2024-12-31T23:59:59Z').getTime(),
|
|
112
|
+
});
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Processing Jobs
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
// Register a job processor
|
|
119
|
+
queue.process('email', async (job) => {
|
|
120
|
+
console.log('Processing email job:', job.data);
|
|
121
|
+
|
|
122
|
+
// Access job data
|
|
123
|
+
const { to, template } = job.data;
|
|
124
|
+
|
|
125
|
+
// Update progress
|
|
126
|
+
await job.updateProgress(25);
|
|
127
|
+
|
|
128
|
+
// Send email
|
|
129
|
+
await sendEmail(to, template);
|
|
130
|
+
|
|
131
|
+
await job.updateProgress(100);
|
|
132
|
+
|
|
133
|
+
// Return result
|
|
134
|
+
return { sent: true, messageId: 'msg_123' };
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// Process with concurrency
|
|
138
|
+
queue.process('reports', 5, async (job) => {
|
|
139
|
+
// Up to 5 reports generated concurrently
|
|
140
|
+
return await generateReport(job.data);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// Process with named handler
|
|
144
|
+
queue.process('default', 'calculate_metrics', async (job) => {
|
|
145
|
+
return await calculateMetrics(job.data);
|
|
146
|
+
});
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Job Management
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
// Get job by ID
|
|
153
|
+
const job = await queue.getJob('email', 'job_abc123');
|
|
154
|
+
|
|
155
|
+
// Get job status
|
|
156
|
+
const status = await job.getState();
|
|
157
|
+
// 'waiting' | 'active' | 'completed' | 'failed' | 'delayed'
|
|
158
|
+
|
|
159
|
+
// Remove job
|
|
160
|
+
await queue.removeJob('email', 'job_abc123');
|
|
161
|
+
|
|
162
|
+
// Retry failed job
|
|
163
|
+
await queue.retryJob('email', 'job_abc123');
|
|
164
|
+
|
|
165
|
+
// Get job result
|
|
166
|
+
const result = await job.returnvalue;
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Queue Operations
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
// Pause queue (stop processing new jobs)
|
|
173
|
+
await queue.pause('email');
|
|
174
|
+
|
|
175
|
+
// Resume queue
|
|
176
|
+
await queue.resume('email');
|
|
177
|
+
|
|
178
|
+
// Clear all jobs in queue
|
|
179
|
+
await queue.clear('email');
|
|
180
|
+
|
|
181
|
+
// Get queue statistics
|
|
182
|
+
const stats = await queue.getStats('email');
|
|
183
|
+
// {
|
|
184
|
+
// waiting: 45,
|
|
185
|
+
// active: 5,
|
|
186
|
+
// completed: 1250,
|
|
187
|
+
// failed: 12,
|
|
188
|
+
// delayed: 3
|
|
189
|
+
// }
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## Advanced Features
|
|
193
|
+
|
|
194
|
+
### Job Events
|
|
195
|
+
|
|
196
|
+
```typescript
|
|
197
|
+
// Listen to job lifecycle events
|
|
198
|
+
queue.on('email', 'completed', async (job, result) => {
|
|
199
|
+
console.log(`Email sent: ${result.messageId}`);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
queue.on('email', 'failed', async (job, error) => {
|
|
203
|
+
console.error(`Email failed: ${error.message}`);
|
|
204
|
+
// Send alert to admin
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
queue.on('email', 'progress', async (job, progress) => {
|
|
208
|
+
console.log(`Email progress: ${progress}%`);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
queue.on('email', 'active', async (job) => {
|
|
212
|
+
console.log(`Email job started: ${job.id}`);
|
|
213
|
+
});
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Bulk Operations
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
// Add multiple jobs at once
|
|
220
|
+
await queue.addBulk('email', [
|
|
221
|
+
{ name: 'send_welcome', data: { to: 'user1@example.com' } },
|
|
222
|
+
{ name: 'send_welcome', data: { to: 'user2@example.com' } },
|
|
223
|
+
{ name: 'send_welcome', data: { to: 'user3@example.com' } },
|
|
224
|
+
]);
|
|
225
|
+
|
|
226
|
+
// Get multiple jobs
|
|
227
|
+
const jobs = await queue.getJobs('email', ['waiting', 'active']);
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### Job Patterns
|
|
231
|
+
|
|
232
|
+
#### Worker Pattern
|
|
233
|
+
|
|
234
|
+
```typescript
|
|
235
|
+
// Dedicated worker process
|
|
236
|
+
queue.process('heavy_processing', async (job) => {
|
|
237
|
+
// CPU-intensive work
|
|
238
|
+
const result = await processLargeDataset(job.data);
|
|
239
|
+
return result;
|
|
240
|
+
});
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
#### Fan-Out Pattern
|
|
244
|
+
|
|
245
|
+
```typescript
|
|
246
|
+
// Split work across multiple jobs
|
|
247
|
+
await queue.add('orchestrator', 'process_batch', { batchId: '123' });
|
|
248
|
+
|
|
249
|
+
queue.process('orchestrator', async (job) => {
|
|
250
|
+
const items = await loadBatchItems(job.data.batchId);
|
|
251
|
+
|
|
252
|
+
// Create sub-jobs for each item
|
|
253
|
+
for (const item of items) {
|
|
254
|
+
await queue.add('worker', 'process_item', { item });
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
queue.process('worker', async (job) => {
|
|
259
|
+
return await processItem(job.data.item);
|
|
260
|
+
});
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
#### Priority Queues
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
// High priority
|
|
267
|
+
await queue.add('tasks', 'urgent', data, { priority: 10 });
|
|
268
|
+
|
|
269
|
+
// Normal priority
|
|
270
|
+
await queue.add('tasks', 'normal', data, { priority: 5 });
|
|
271
|
+
|
|
272
|
+
// Low priority
|
|
273
|
+
await queue.add('tasks', 'background', data, { priority: 1 });
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### Rate Limiting
|
|
277
|
+
|
|
278
|
+
```typescript
|
|
279
|
+
// Limit queue to 100 jobs per minute
|
|
280
|
+
ServiceQueue.configure({
|
|
281
|
+
adapter: 'bullmq',
|
|
282
|
+
queues: {
|
|
283
|
+
api_calls: {
|
|
284
|
+
concurrency: 5,
|
|
285
|
+
rateLimit: {
|
|
286
|
+
max: 100,
|
|
287
|
+
duration: 60000, // 1 minute
|
|
288
|
+
},
|
|
289
|
+
},
|
|
290
|
+
},
|
|
291
|
+
});
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### Repeatable Jobs
|
|
295
|
+
|
|
296
|
+
```typescript
|
|
297
|
+
// Add cron-based repeatable job
|
|
298
|
+
await queue.addRepeatable('cleanup', 'old_sessions', {}, {
|
|
299
|
+
cron: '0 2 * * *', // Daily at 2 AM
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
// Add interval-based repeatable job
|
|
303
|
+
await queue.addRepeatable('sync', 'data', {}, {
|
|
304
|
+
every: 300000, // Every 5 minutes
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
// Remove repeatable job
|
|
308
|
+
await queue.removeRepeatable('cleanup', 'old_sessions');
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
## Common Use Cases
|
|
312
|
+
|
|
313
|
+
### Email Queue
|
|
314
|
+
|
|
315
|
+
```typescript
|
|
316
|
+
queue.process('email', async (job) => {
|
|
317
|
+
const { to, subject, body, template } = job.data;
|
|
318
|
+
|
|
319
|
+
try {
|
|
320
|
+
const result = await emailProvider.send({
|
|
321
|
+
to,
|
|
322
|
+
subject,
|
|
323
|
+
html: renderTemplate(template, job.data),
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
return { messageId: result.id, sentAt: new Date() };
|
|
327
|
+
} catch (error) {
|
|
328
|
+
// Throw error to trigger retry
|
|
329
|
+
throw new Error(`Failed to send email: ${error.message}`);
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
// Add email job
|
|
334
|
+
await queue.add('email', 'welcome', {
|
|
335
|
+
to: 'newuser@example.com',
|
|
336
|
+
template: 'welcome',
|
|
337
|
+
name: 'John Doe',
|
|
338
|
+
}, {
|
|
339
|
+
attempts: 3,
|
|
340
|
+
backoff: { type: 'exponential', delay: 5000 },
|
|
341
|
+
});
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
### Report Generation
|
|
345
|
+
|
|
346
|
+
```typescript
|
|
347
|
+
queue.process('reports', async (job) => {
|
|
348
|
+
const { reportType, userId, dateRange } = job.data;
|
|
349
|
+
|
|
350
|
+
await job.updateProgress(10);
|
|
351
|
+
|
|
352
|
+
// Fetch data
|
|
353
|
+
const data = await fetchReportData(reportType, dateRange);
|
|
354
|
+
|
|
355
|
+
await job.updateProgress(50);
|
|
356
|
+
|
|
357
|
+
// Generate report
|
|
358
|
+
const report = await generatePDF(data);
|
|
359
|
+
|
|
360
|
+
await job.updateProgress(90);
|
|
361
|
+
|
|
362
|
+
// Upload to storage
|
|
363
|
+
const url = await uploadReport(report);
|
|
364
|
+
|
|
365
|
+
await job.updateProgress(100);
|
|
366
|
+
|
|
367
|
+
// Notify user
|
|
368
|
+
await notifyUser(userId, { reportUrl: url });
|
|
369
|
+
|
|
370
|
+
return { url, size: report.length };
|
|
371
|
+
});
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
### Webhook Processing
|
|
375
|
+
|
|
376
|
+
```typescript
|
|
377
|
+
queue.process('webhooks', async (job) => {
|
|
378
|
+
const { url, payload, headers } = job.data;
|
|
379
|
+
|
|
380
|
+
const response = await fetch(url, {
|
|
381
|
+
method: 'POST',
|
|
382
|
+
headers,
|
|
383
|
+
body: JSON.stringify(payload),
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
if (!response.ok) {
|
|
387
|
+
throw new Error(`Webhook failed: ${response.status}`);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
return { status: response.status, responseTime: Date.now() - job.timestamp };
|
|
391
|
+
});
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
## REST API Endpoints
|
|
395
|
+
|
|
396
|
+
```
|
|
397
|
+
POST /api/v1/queues/:queue/jobs # Add job
|
|
398
|
+
GET /api/v1/queues/:queue/jobs/:id # Get job
|
|
399
|
+
DELETE /api/v1/queues/:queue/jobs/:id # Remove job
|
|
400
|
+
POST /api/v1/queues/:queue/jobs/:id/retry # Retry failed job
|
|
401
|
+
GET /api/v1/queues/:queue/stats # Get queue stats
|
|
402
|
+
POST /api/v1/queues/:queue/pause # Pause queue
|
|
403
|
+
POST /api/v1/queues/:queue/resume # Resume queue
|
|
404
|
+
DELETE /api/v1/queues/:queue # Clear queue
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
## Best Practices
|
|
408
|
+
|
|
409
|
+
1. **Idempotent Jobs**: Design jobs to be safely retried
|
|
410
|
+
2. **Error Handling**: Always handle errors and throw to trigger retry
|
|
411
|
+
3. **Progress Updates**: Update progress for long-running jobs
|
|
412
|
+
4. **Resource Limits**: Set appropriate concurrency limits
|
|
413
|
+
5. **Job Data**: Keep job data small (< 1MB)
|
|
414
|
+
6. **Monitoring**: Track queue metrics and job failure rates
|
|
415
|
+
7. **Cleanup**: Remove completed jobs periodically
|
|
416
|
+
|
|
417
|
+
## Performance Considerations
|
|
418
|
+
|
|
419
|
+
- **Concurrency**: Tune based on system resources and external API limits
|
|
420
|
+
- **Rate Limiting**: Prevent overwhelming external services
|
|
421
|
+
- **Job Size**: Keep job payloads small for faster serialization
|
|
422
|
+
- **Redis Connection**: Use connection pooling for BullMQ
|
|
423
|
+
- **Queue Organization**: Use separate queues for different job types
|
|
424
|
+
|
|
425
|
+
## Contract Implementation
|
|
426
|
+
|
|
427
|
+
Implements `IQueueService` from `@objectstack/spec/contracts`:
|
|
428
|
+
|
|
429
|
+
```typescript
|
|
430
|
+
interface IQueueService {
|
|
431
|
+
add(queue: string, name: string, data: any, options?: JobOptions): Promise<Job>;
|
|
432
|
+
addBulk(queue: string, jobs: JobDefinition[]): Promise<Job[]>;
|
|
433
|
+
process(queue: string, handler: JobHandler): void;
|
|
434
|
+
getJob(queue: string, jobId: string): Promise<Job | null>;
|
|
435
|
+
removeJob(queue: string, jobId: string): Promise<void>;
|
|
436
|
+
retryJob(queue: string, jobId: string): Promise<void>;
|
|
437
|
+
getStats(queue: string): Promise<QueueStats>;
|
|
438
|
+
pause(queue: string): Promise<void>;
|
|
439
|
+
resume(queue: string): Promise<void>;
|
|
440
|
+
clear(queue: string): Promise<void>;
|
|
441
|
+
on(queue: string, event: JobEvent, handler: EventHandler): void;
|
|
442
|
+
}
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
## License
|
|
446
|
+
|
|
447
|
+
Apache-2.0
|
|
448
|
+
|
|
449
|
+
## See Also
|
|
450
|
+
|
|
451
|
+
- [BullMQ Documentation](https://docs.bullmq.io/)
|
|
452
|
+
- [@objectstack/spec/contracts](../../spec/src/contracts/)
|
|
453
|
+
- [Queue Patterns Guide](/content/docs/guides/queues/)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@objectstack/service-queue",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.5",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"description": "Queue Service for ObjectStack — implements IQueueService with in-memory and BullMQ adapters",
|
|
6
6
|
"type": "module",
|
|
@@ -14,13 +14,38 @@
|
|
|
14
14
|
}
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@objectstack/core": "4.0.
|
|
18
|
-
"@objectstack/spec": "4.0.
|
|
17
|
+
"@objectstack/core": "4.0.5",
|
|
18
|
+
"@objectstack/spec": "4.0.5"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
|
-
"@types/node": "^25.6.
|
|
22
|
-
"typescript": "^6.0.
|
|
23
|
-
"vitest": "^4.1.
|
|
21
|
+
"@types/node": "^25.6.2",
|
|
22
|
+
"typescript": "^6.0.3",
|
|
23
|
+
"vitest": "^4.1.5"
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"objectstack",
|
|
27
|
+
"service",
|
|
28
|
+
"queue",
|
|
29
|
+
"jobs",
|
|
30
|
+
"background"
|
|
31
|
+
],
|
|
32
|
+
"author": "ObjectStack",
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "https://github.com/objectstack-ai/framework.git",
|
|
36
|
+
"directory": "packages/services/service-queue"
|
|
37
|
+
},
|
|
38
|
+
"homepage": "https://objectstack.ai/docs",
|
|
39
|
+
"bugs": "https://github.com/objectstack-ai/framework/issues",
|
|
40
|
+
"publishConfig": {
|
|
41
|
+
"access": "public"
|
|
42
|
+
},
|
|
43
|
+
"files": [
|
|
44
|
+
"dist",
|
|
45
|
+
"README.md"
|
|
46
|
+
],
|
|
47
|
+
"engines": {
|
|
48
|
+
"node": ">=18.0.0"
|
|
24
49
|
},
|
|
25
50
|
"scripts": {
|
|
26
51
|
"build": "tsup --config ../../../tsup.config.ts",
|
package/.turbo/turbo-build.log
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
> @objectstack/service-queue@4.0.3 build /home/runner/work/framework/framework/packages/services/service-queue
|
|
3
|
-
> tsup --config ../../../tsup.config.ts
|
|
4
|
-
|
|
5
|
-
[34mCLI[39m Building entry: src/index.ts
|
|
6
|
-
[34mCLI[39m Using tsconfig: tsconfig.json
|
|
7
|
-
[34mCLI[39m tsup v8.5.1
|
|
8
|
-
[34mCLI[39m Using tsup config: /home/runner/work/framework/framework/tsup.config.ts
|
|
9
|
-
[34mCLI[39m Target: es2020
|
|
10
|
-
[34mCLI[39m Cleaning output folder
|
|
11
|
-
[34mESM[39m Build start
|
|
12
|
-
[34mCJS[39m Build start
|
|
13
|
-
[32mESM[39m [1mdist/index.js [22m[32m2.78 KB[39m
|
|
14
|
-
[32mESM[39m [1mdist/index.js.map [22m[32m8.33 KB[39m
|
|
15
|
-
[32mESM[39m ⚡️ Build success in 49ms
|
|
16
|
-
[32mCJS[39m [1mdist/index.cjs [22m[32m3.89 KB[39m
|
|
17
|
-
[32mCJS[39m [1mdist/index.cjs.map [22m[32m8.90 KB[39m
|
|
18
|
-
[32mCJS[39m ⚡️ Build success in 59ms
|
|
19
|
-
[34mDTS[39m Build start
|
|
20
|
-
[32mDTS[39m ⚡️ Build success in 6379ms
|
|
21
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[32m3.63 KB[39m
|
|
22
|
-
[32mDTS[39m [1mdist/index.d.cts [22m[32m3.63 KB[39m
|
package/CHANGELOG.md
DELETED
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
# @objectstack/service-queue
|
|
2
|
-
|
|
3
|
-
## 4.0.3
|
|
4
|
-
|
|
5
|
-
### Patch Changes
|
|
6
|
-
|
|
7
|
-
- @objectstack/spec@4.0.3
|
|
8
|
-
- @objectstack/core@4.0.3
|
|
9
|
-
|
|
10
|
-
## 4.0.2
|
|
11
|
-
|
|
12
|
-
### Patch Changes
|
|
13
|
-
|
|
14
|
-
- Updated dependencies [5f659e9]
|
|
15
|
-
- @objectstack/spec@4.0.2
|
|
16
|
-
- @objectstack/core@4.0.2
|
|
17
|
-
|
|
18
|
-
## 4.0.0
|
|
19
|
-
|
|
20
|
-
### Patch Changes
|
|
21
|
-
|
|
22
|
-
- Updated dependencies [f08ffc3]
|
|
23
|
-
- Updated dependencies [e0b0a78]
|
|
24
|
-
- @objectstack/spec@4.0.0
|
|
25
|
-
- @objectstack/core@4.0.0
|
|
26
|
-
|
|
27
|
-
## 3.3.1
|
|
28
|
-
|
|
29
|
-
### Patch Changes
|
|
30
|
-
|
|
31
|
-
- @objectstack/spec@3.3.1
|
|
32
|
-
- @objectstack/core@3.3.1
|
|
33
|
-
|
|
34
|
-
## 3.3.0
|
|
35
|
-
|
|
36
|
-
### Patch Changes
|
|
37
|
-
|
|
38
|
-
- @objectstack/spec@3.3.0
|
|
39
|
-
- @objectstack/core@3.3.0
|
|
40
|
-
|
|
41
|
-
## 3.2.9
|
|
42
|
-
|
|
43
|
-
### Patch Changes
|
|
44
|
-
|
|
45
|
-
- @objectstack/spec@3.2.9
|
|
46
|
-
- @objectstack/core@3.2.9
|
|
47
|
-
|
|
48
|
-
## 3.2.8
|
|
49
|
-
|
|
50
|
-
### Patch Changes
|
|
51
|
-
|
|
52
|
-
- @objectstack/spec@3.2.8
|
|
53
|
-
- @objectstack/core@3.2.8
|
|
54
|
-
|
|
55
|
-
## 3.2.7
|
|
56
|
-
|
|
57
|
-
### Patch Changes
|
|
58
|
-
|
|
59
|
-
- @objectstack/spec@3.2.7
|
|
60
|
-
- @objectstack/core@3.2.7
|
|
61
|
-
|
|
62
|
-
## 3.2.6
|
|
63
|
-
|
|
64
|
-
### Patch Changes
|
|
65
|
-
|
|
66
|
-
- @objectstack/spec@3.2.6
|
|
67
|
-
- @objectstack/core@3.2.6
|
|
68
|
-
|
|
69
|
-
## 3.2.5
|
|
70
|
-
|
|
71
|
-
### Patch Changes
|
|
72
|
-
|
|
73
|
-
- @objectstack/spec@3.2.5
|
|
74
|
-
- @objectstack/core@3.2.5
|
|
75
|
-
|
|
76
|
-
## 3.2.4
|
|
77
|
-
|
|
78
|
-
### Patch Changes
|
|
79
|
-
|
|
80
|
-
- @objectstack/spec@3.2.4
|
|
81
|
-
- @objectstack/core@3.2.4
|
|
82
|
-
|
|
83
|
-
## 3.2.3
|
|
84
|
-
|
|
85
|
-
### Patch Changes
|
|
86
|
-
|
|
87
|
-
- @objectstack/spec@3.2.3
|
|
88
|
-
- @objectstack/core@3.2.3
|
|
89
|
-
|
|
90
|
-
## 3.2.2
|
|
91
|
-
|
|
92
|
-
### Patch Changes
|
|
93
|
-
|
|
94
|
-
- Updated dependencies [46defbb]
|
|
95
|
-
- @objectstack/spec@3.2.2
|
|
96
|
-
- @objectstack/core@3.2.2
|
|
97
|
-
|
|
98
|
-
## 3.2.1
|
|
99
|
-
|
|
100
|
-
### Patch Changes
|
|
101
|
-
|
|
102
|
-
- Updated dependencies [850b546]
|
|
103
|
-
- @objectstack/spec@3.2.1
|
|
104
|
-
- @objectstack/core@3.2.1
|
|
105
|
-
|
|
106
|
-
## 3.2.0
|
|
107
|
-
|
|
108
|
-
### Patch Changes
|
|
109
|
-
|
|
110
|
-
- Updated dependencies [5901c29]
|
|
111
|
-
- @objectstack/spec@3.2.0
|
|
112
|
-
- @objectstack/core@3.2.0
|
|
113
|
-
|
|
114
|
-
## 3.1.1
|
|
115
|
-
|
|
116
|
-
### Patch Changes
|
|
117
|
-
|
|
118
|
-
- Updated dependencies [953d667]
|
|
119
|
-
- @objectstack/spec@3.1.1
|
|
120
|
-
- @objectstack/core@3.1.1
|
|
121
|
-
|
|
122
|
-
## 3.1.0
|
|
123
|
-
|
|
124
|
-
### Patch Changes
|
|
125
|
-
|
|
126
|
-
- Updated dependencies [0088830]
|
|
127
|
-
- @objectstack/spec@3.1.0
|
|
128
|
-
- @objectstack/core@3.1.0
|
|
129
|
-
|
|
130
|
-
## 3.0.11
|
|
131
|
-
|
|
132
|
-
### Patch Changes
|
|
133
|
-
|
|
134
|
-
- Updated dependencies [92d9d99]
|
|
135
|
-
- @objectstack/spec@3.0.11
|
|
136
|
-
- @objectstack/core@3.0.11
|
|
137
|
-
|
|
138
|
-
## 3.0.10
|
|
139
|
-
|
|
140
|
-
### Patch Changes
|
|
141
|
-
|
|
142
|
-
- Updated dependencies [d1e5d31]
|
|
143
|
-
- @objectstack/spec@3.0.10
|
|
144
|
-
- @objectstack/core@3.0.10
|
|
145
|
-
|
|
146
|
-
## 3.0.9
|
|
147
|
-
|
|
148
|
-
### Patch Changes
|
|
149
|
-
|
|
150
|
-
- Updated dependencies [15e0df6]
|
|
151
|
-
- @objectstack/spec@3.0.9
|
|
152
|
-
- @objectstack/core@3.0.9
|
|
153
|
-
|
|
154
|
-
## 3.0.8
|
|
155
|
-
|
|
156
|
-
### Patch Changes
|
|
157
|
-
|
|
158
|
-
- Updated dependencies [5a968a2]
|
|
159
|
-
- @objectstack/spec@3.0.8
|
|
160
|
-
- @objectstack/core@3.0.8
|
|
161
|
-
|
|
162
|
-
## 3.0.7
|
|
163
|
-
|
|
164
|
-
### Patch Changes
|
|
165
|
-
|
|
166
|
-
- Updated dependencies [0119bd7]
|
|
167
|
-
- Updated dependencies [5426bdf]
|
|
168
|
-
- @objectstack/spec@3.0.7
|
|
169
|
-
- @objectstack/core@3.0.7
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
|
-
|
|
3
|
-
import type { IQueueService, QueuePublishOptions, QueueHandler } from '@objectstack/spec/contracts';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Configuration for the BullMQ queue adapter.
|
|
7
|
-
*/
|
|
8
|
-
export interface BullMQQueueAdapterOptions {
|
|
9
|
-
/** Redis connection URL (e.g. 'redis://localhost:6379') */
|
|
10
|
-
redisUrl: string;
|
|
11
|
-
/** Default job options */
|
|
12
|
-
defaultJobOptions?: {
|
|
13
|
-
/** Number of retry attempts */
|
|
14
|
-
attempts?: number;
|
|
15
|
-
/** Backoff strategy */
|
|
16
|
-
backoff?: { type: 'fixed' | 'exponential'; delay: number };
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* BullMQ queue adapter skeleton implementing IQueueService.
|
|
22
|
-
*
|
|
23
|
-
* This is a placeholder for future BullMQ integration.
|
|
24
|
-
* Concrete implementation will use the `bullmq` package.
|
|
25
|
-
*
|
|
26
|
-
* @example
|
|
27
|
-
* ```ts
|
|
28
|
-
* const queue = new BullMQQueueAdapter({ redisUrl: 'redis://localhost:6379' });
|
|
29
|
-
* await queue.publish('orders', { orderId: 123 });
|
|
30
|
-
* ```
|
|
31
|
-
*/
|
|
32
|
-
export class BullMQQueueAdapter implements IQueueService {
|
|
33
|
-
private readonly redisUrl: string;
|
|
34
|
-
|
|
35
|
-
constructor(options: BullMQQueueAdapterOptions) {
|
|
36
|
-
this.redisUrl = options.redisUrl;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
async publish<T = unknown>(_queue: string, _data: T, _options?: QueuePublishOptions): Promise<string> {
|
|
40
|
-
throw new Error(`BullMQQueueAdapter not yet implemented (url: ${this.redisUrl})`);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
async subscribe<T = unknown>(_queue: string, _handler: QueueHandler<T>): Promise<void> {
|
|
44
|
-
throw new Error('BullMQQueueAdapter not yet implemented');
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
async unsubscribe(_queue: string): Promise<void> {
|
|
48
|
-
throw new Error('BullMQQueueAdapter not yet implemented');
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
async getQueueSize(_queue: string): Promise<number> {
|
|
52
|
-
throw new Error('BullMQQueueAdapter not yet implemented');
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
async purge(_queue: string): Promise<void> {
|
|
56
|
-
throw new Error('BullMQQueueAdapter not yet implemented');
|
|
57
|
-
}
|
|
58
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
|
-
|
|
3
|
-
export { QueueServicePlugin } from './queue-service-plugin.js';
|
|
4
|
-
export type { QueueServicePluginOptions } from './queue-service-plugin.js';
|
|
5
|
-
export { MemoryQueueAdapter } from './memory-queue-adapter.js';
|
|
6
|
-
export type { MemoryQueueAdapterOptions } from './memory-queue-adapter.js';
|
|
7
|
-
export { BullMQQueueAdapter } from './bullmq-queue-adapter.js';
|
|
8
|
-
export type { BullMQQueueAdapterOptions } from './bullmq-queue-adapter.js';
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
|
-
|
|
3
|
-
import { describe, it, expect } from 'vitest';
|
|
4
|
-
import { MemoryQueueAdapter } from './memory-queue-adapter';
|
|
5
|
-
import type { IQueueService, QueueMessage } from '@objectstack/spec/contracts';
|
|
6
|
-
|
|
7
|
-
describe('MemoryQueueAdapter', () => {
|
|
8
|
-
it('should implement IQueueService contract', () => {
|
|
9
|
-
const queue: IQueueService = new MemoryQueueAdapter();
|
|
10
|
-
expect(typeof queue.publish).toBe('function');
|
|
11
|
-
expect(typeof queue.subscribe).toBe('function');
|
|
12
|
-
expect(typeof queue.unsubscribe).toBe('function');
|
|
13
|
-
expect(typeof queue.getQueueSize).toBe('function');
|
|
14
|
-
expect(typeof queue.purge).toBe('function');
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it('should publish and deliver to subscriber', async () => {
|
|
18
|
-
const queue = new MemoryQueueAdapter();
|
|
19
|
-
const received: QueueMessage[] = [];
|
|
20
|
-
|
|
21
|
-
await queue.subscribe('orders', async (msg) => {
|
|
22
|
-
received.push(msg);
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
const id = await queue.publish('orders', { orderId: 123 });
|
|
26
|
-
expect(id).toBe('msg-1');
|
|
27
|
-
expect(received).toHaveLength(1);
|
|
28
|
-
expect(received[0].data).toEqual({ orderId: 123 });
|
|
29
|
-
expect(received[0].attempts).toBe(1);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it('should support multiple subscribers', async () => {
|
|
33
|
-
const queue = new MemoryQueueAdapter();
|
|
34
|
-
const log1: unknown[] = [];
|
|
35
|
-
const log2: unknown[] = [];
|
|
36
|
-
|
|
37
|
-
await queue.subscribe('events', async (msg) => { log1.push(msg.data); });
|
|
38
|
-
await queue.subscribe('events', async (msg) => { log2.push(msg.data); });
|
|
39
|
-
|
|
40
|
-
await queue.publish('events', 'hello');
|
|
41
|
-
expect(log1).toEqual(['hello']);
|
|
42
|
-
expect(log2).toEqual(['hello']);
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it('should unsubscribe from a queue', async () => {
|
|
46
|
-
const queue = new MemoryQueueAdapter();
|
|
47
|
-
const received: unknown[] = [];
|
|
48
|
-
|
|
49
|
-
await queue.subscribe('q1', async (msg) => { received.push(msg.data); });
|
|
50
|
-
await queue.unsubscribe('q1');
|
|
51
|
-
await queue.publish('q1', 'data');
|
|
52
|
-
expect(received).toHaveLength(0);
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it('should retain dead letters when no subscribers', async () => {
|
|
56
|
-
const queue = new MemoryQueueAdapter();
|
|
57
|
-
const id = await queue.publish('orphan', { lost: true });
|
|
58
|
-
expect(id).toBe('msg-1');
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it('should return queue size of 0 for in-memory queue', async () => {
|
|
62
|
-
const queue = new MemoryQueueAdapter();
|
|
63
|
-
expect(await queue.getQueueSize('test')).toBe(0);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it('should purge a queue by removing handlers', async () => {
|
|
67
|
-
const queue = new MemoryQueueAdapter();
|
|
68
|
-
const received: unknown[] = [];
|
|
69
|
-
|
|
70
|
-
await queue.subscribe('q1', async (msg) => { received.push(msg.data); });
|
|
71
|
-
await queue.purge('q1');
|
|
72
|
-
await queue.publish('q1', 'data');
|
|
73
|
-
expect(received).toHaveLength(0);
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
it('should increment message counter across publishes', async () => {
|
|
77
|
-
const queue = new MemoryQueueAdapter();
|
|
78
|
-
const ids: string[] = [];
|
|
79
|
-
|
|
80
|
-
await queue.subscribe('q', async () => {});
|
|
81
|
-
ids.push(await queue.publish('q', 'a'));
|
|
82
|
-
ids.push(await queue.publish('q', 'b'));
|
|
83
|
-
ids.push(await queue.publish('q', 'c'));
|
|
84
|
-
|
|
85
|
-
expect(ids).toEqual(['msg-1', 'msg-2', 'msg-3']);
|
|
86
|
-
});
|
|
87
|
-
});
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
|
-
|
|
3
|
-
import type { IQueueService, QueuePublishOptions, QueueMessage, QueueHandler } from '@objectstack/spec/contracts';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Configuration options for MemoryQueueAdapter.
|
|
7
|
-
*/
|
|
8
|
-
export interface MemoryQueueAdapterOptions {
|
|
9
|
-
/** Maximum number of messages retained per queue (0 = unlimited) */
|
|
10
|
-
maxQueueSize?: number;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* In-memory queue adapter implementing IQueueService.
|
|
15
|
-
*
|
|
16
|
-
* Provides synchronous in-process pub/sub delivery.
|
|
17
|
-
* Suitable for single-process environments, development, and testing.
|
|
18
|
-
*/
|
|
19
|
-
export class MemoryQueueAdapter implements IQueueService {
|
|
20
|
-
private readonly handlers = new Map<string, QueueHandler[]>();
|
|
21
|
-
private readonly deadLetters: QueueMessage[] = [];
|
|
22
|
-
private msgCounter = 0;
|
|
23
|
-
private readonly maxQueueSize: number;
|
|
24
|
-
|
|
25
|
-
constructor(options: MemoryQueueAdapterOptions = {}) {
|
|
26
|
-
this.maxQueueSize = options.maxQueueSize ?? 0;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
async publish<T = unknown>(queue: string, data: T, options?: QueuePublishOptions): Promise<string> {
|
|
30
|
-
const id = `msg-${++this.msgCounter}`;
|
|
31
|
-
const msg: QueueMessage<T> = {
|
|
32
|
-
id,
|
|
33
|
-
data,
|
|
34
|
-
attempts: 0,
|
|
35
|
-
timestamp: Date.now(),
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
const fns = this.handlers.get(queue) ?? [];
|
|
39
|
-
if (fns.length === 0) {
|
|
40
|
-
// No subscribers — retain as dead letter if within limits
|
|
41
|
-
if (this.maxQueueSize === 0 || this.deadLetters.length < this.maxQueueSize) {
|
|
42
|
-
this.deadLetters.push(msg);
|
|
43
|
-
}
|
|
44
|
-
return id;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const maxRetries = options?.retries ?? 0;
|
|
48
|
-
for (const handler of fns) {
|
|
49
|
-
let attempt = 0;
|
|
50
|
-
let success = false;
|
|
51
|
-
while (!success && attempt <= maxRetries) {
|
|
52
|
-
try {
|
|
53
|
-
msg.attempts = attempt + 1;
|
|
54
|
-
await handler(msg as QueueMessage);
|
|
55
|
-
success = true;
|
|
56
|
-
} catch {
|
|
57
|
-
attempt++;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return id;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
async subscribe<T = unknown>(queue: string, handler: QueueHandler<T>): Promise<void> {
|
|
66
|
-
const existing = this.handlers.get(queue) ?? [];
|
|
67
|
-
this.handlers.set(queue, [...existing, handler as QueueHandler]);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
async unsubscribe(queue: string): Promise<void> {
|
|
71
|
-
this.handlers.delete(queue);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
async getQueueSize(_queue: string): Promise<number> {
|
|
75
|
-
// In-memory: no persistent queue depth tracking
|
|
76
|
-
return 0;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
async purge(queue: string): Promise<void> {
|
|
80
|
-
this.handlers.delete(queue);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
|
-
|
|
3
|
-
import type { Plugin, PluginContext } from '@objectstack/core';
|
|
4
|
-
import { MemoryQueueAdapter } from './memory-queue-adapter.js';
|
|
5
|
-
import type { MemoryQueueAdapterOptions } from './memory-queue-adapter.js';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Configuration options for the QueueServicePlugin.
|
|
9
|
-
*/
|
|
10
|
-
export interface QueueServicePluginOptions {
|
|
11
|
-
/** Queue adapter type (default: 'memory') */
|
|
12
|
-
adapter?: 'memory' | 'bullmq';
|
|
13
|
-
/** Options for the memory queue adapter */
|
|
14
|
-
memory?: MemoryQueueAdapterOptions;
|
|
15
|
-
/** Redis connection URL (used when adapter is 'bullmq') */
|
|
16
|
-
redisUrl?: string;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* QueueServicePlugin — Production IQueueService implementation.
|
|
21
|
-
*
|
|
22
|
-
* Registers a queue service with the kernel during the init phase.
|
|
23
|
-
* Supports in-memory and BullMQ adapters.
|
|
24
|
-
*
|
|
25
|
-
* @example
|
|
26
|
-
* ```ts
|
|
27
|
-
* import { ObjectKernel } from '@objectstack/core';
|
|
28
|
-
* import { QueueServicePlugin } from '@objectstack/service-queue';
|
|
29
|
-
*
|
|
30
|
-
* const kernel = new ObjectKernel();
|
|
31
|
-
* kernel.use(new QueueServicePlugin({ adapter: 'memory' }));
|
|
32
|
-
* await kernel.bootstrap();
|
|
33
|
-
*
|
|
34
|
-
* const queue = kernel.getService('queue');
|
|
35
|
-
* await queue.publish('orders', { orderId: 123 });
|
|
36
|
-
* ```
|
|
37
|
-
*/
|
|
38
|
-
export class QueueServicePlugin implements Plugin {
|
|
39
|
-
name = 'com.objectstack.service.queue';
|
|
40
|
-
version = '1.0.0';
|
|
41
|
-
type = 'standard';
|
|
42
|
-
|
|
43
|
-
private readonly options: QueueServicePluginOptions;
|
|
44
|
-
|
|
45
|
-
constructor(options: QueueServicePluginOptions = {}) {
|
|
46
|
-
this.options = { adapter: 'memory', ...options };
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
async init(ctx: PluginContext): Promise<void> {
|
|
50
|
-
const adapter = this.options.adapter;
|
|
51
|
-
if (adapter === 'bullmq') {
|
|
52
|
-
throw new Error(
|
|
53
|
-
'BullMQ queue adapter is not yet implemented. ' +
|
|
54
|
-
'Use adapter: "memory" or provide a custom IQueueService via ctx.registerService("queue", impl).'
|
|
55
|
-
);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const queue = new MemoryQueueAdapter(this.options.memory);
|
|
59
|
-
ctx.registerService('queue', queue);
|
|
60
|
-
ctx.logger.info('QueueServicePlugin: registered memory queue adapter');
|
|
61
|
-
}
|
|
62
|
-
}
|
package/tsconfig.json
DELETED