flashq 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.md +283 -0
- package/dist/client/advanced.d.ts +174 -0
- package/dist/client/advanced.d.ts.map +1 -0
- package/dist/client/advanced.js +248 -0
- package/dist/client/advanced.js.map +1 -0
- package/dist/client/connection.d.ts +103 -0
- package/dist/client/connection.d.ts.map +1 -0
- package/dist/client/connection.js +570 -0
- package/dist/client/connection.js.map +1 -0
- package/dist/client/core.d.ts +119 -0
- package/dist/client/core.d.ts.map +1 -0
- package/dist/client/core.js +257 -0
- package/dist/client/core.js.map +1 -0
- package/dist/client/cron.d.ts +59 -0
- package/dist/client/cron.d.ts.map +1 -0
- package/dist/client/cron.js +82 -0
- package/dist/client/cron.js.map +1 -0
- package/dist/client/dlq.d.ts +52 -0
- package/dist/client/dlq.d.ts.map +1 -0
- package/dist/client/dlq.js +73 -0
- package/dist/client/dlq.js.map +1 -0
- package/dist/client/flows.d.ts +49 -0
- package/dist/client/flows.d.ts.map +1 -0
- package/dist/client/flows.js +67 -0
- package/dist/client/flows.js.map +1 -0
- package/dist/client/index.d.ts +644 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +829 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/jobs.d.ts +183 -0
- package/dist/client/jobs.d.ts.map +1 -0
- package/dist/client/jobs.js +272 -0
- package/dist/client/jobs.js.map +1 -0
- package/dist/client/kv.d.ts +63 -0
- package/dist/client/kv.d.ts.map +1 -0
- package/dist/client/kv.js +131 -0
- package/dist/client/kv.js.map +1 -0
- package/dist/client/metrics.d.ts +34 -0
- package/dist/client/metrics.d.ts.map +1 -0
- package/dist/client/metrics.js +49 -0
- package/dist/client/metrics.js.map +1 -0
- package/dist/client/pubsub.d.ts +42 -0
- package/dist/client/pubsub.d.ts.map +1 -0
- package/dist/client/pubsub.js +92 -0
- package/dist/client/pubsub.js.map +1 -0
- package/dist/client/queue.d.ts +111 -0
- package/dist/client/queue.d.ts.map +1 -0
- package/dist/client/queue.js +160 -0
- package/dist/client/queue.js.map +1 -0
- package/dist/client/types.d.ts +23 -0
- package/dist/client/types.d.ts.map +1 -0
- package/dist/client/types.js +3 -0
- package/dist/client/types.js.map +1 -0
- package/dist/client.d.ts +17 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +23 -0
- package/dist/client.js.map +1 -0
- package/dist/events.d.ts +184 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +340 -0
- package/dist/events.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +43 -0
- package/dist/index.js.map +1 -0
- package/dist/queue.d.ts +104 -0
- package/dist/queue.d.ts.map +1 -0
- package/dist/queue.js +139 -0
- package/dist/queue.js.map +1 -0
- package/dist/types.d.ts +185 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/dist/worker.d.ts +88 -0
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +296 -0
- package/dist/worker.js.map +1 -0
- package/package.json +70 -0
package/README.md
ADDED
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
# flashQ TypeScript SDK
|
|
2
|
+
|
|
3
|
+
BullMQ-compatible API for [flashQ](https://github.com/egeominotti/flashq).
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun add flashq
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { Queue, Worker } from 'flashq';
|
|
15
|
+
|
|
16
|
+
// Create queue
|
|
17
|
+
const queue = new Queue('emails');
|
|
18
|
+
|
|
19
|
+
// Add job
|
|
20
|
+
await queue.add('send', { to: 'user@example.com' });
|
|
21
|
+
|
|
22
|
+
// Process jobs (auto-starts)
|
|
23
|
+
const worker = new Worker('emails', async (job) => {
|
|
24
|
+
console.log('Processing:', job.data);
|
|
25
|
+
return { sent: true };
|
|
26
|
+
});
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## ⚡ Performance Benchmark: flashQ vs BullMQ
|
|
32
|
+
|
|
33
|
+
> **flashQ is 3x to 10x faster than BullMQ** in real-world benchmarks.
|
|
34
|
+
|
|
35
|
+
### Test Environment
|
|
36
|
+
|
|
37
|
+
| Component | Version | Configuration |
|
|
38
|
+
|-----------|---------|---------------|
|
|
39
|
+
| **flashQ Server** | 0.1.0 | Docker with `io_uring` enabled, Rust + tokio async runtime |
|
|
40
|
+
| **BullMQ** | 5.66.5 | npm package |
|
|
41
|
+
| **Redis** | 7.4.7 | Docker (`redis:7-alpine`), jemalloc allocator |
|
|
42
|
+
| **Bun** | 1.3.6 | TypeScript runtime |
|
|
43
|
+
| **Platform** | Linux/macOS | Docker containers |
|
|
44
|
+
|
|
45
|
+
### Benchmark Configuration
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
Workers: 8
|
|
49
|
+
Concurrency/worker: 50
|
|
50
|
+
Total concurrency: 400
|
|
51
|
+
Batch size: 1,000 jobs
|
|
52
|
+
Data verification: Enabled (input === output)
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Results: No-op Jobs (100,000 jobs)
|
|
56
|
+
|
|
57
|
+
Minimal job processing to measure pure queue overhead.
|
|
58
|
+
|
|
59
|
+
| Metric | flashQ | BullMQ | Speedup |
|
|
60
|
+
|--------|-------:|-------:|--------:|
|
|
61
|
+
| **Push Rate** | 307,692 jobs/sec | 43,649 jobs/sec | **7.0x** |
|
|
62
|
+
| **Process Rate** | 292,398 jobs/sec | 27,405 jobs/sec | **10.7x** |
|
|
63
|
+
| **Total Time** | 0.67s | 5.94s | **8.9x** |
|
|
64
|
+
|
|
65
|
+
### Results: CPU-Bound Jobs (100,000 jobs)
|
|
66
|
+
|
|
67
|
+
Each job performs realistic CPU work:
|
|
68
|
+
- JSON serialize/deserialize
|
|
69
|
+
- 10x SHA256 hash rounds
|
|
70
|
+
- Array sort/filter/reduce (100 elements)
|
|
71
|
+
- String manipulation
|
|
72
|
+
|
|
73
|
+
| Metric | flashQ | BullMQ | Speedup |
|
|
74
|
+
|--------|-------:|-------:|--------:|
|
|
75
|
+
| **Push Rate** | 220,751 jobs/sec | 43,422 jobs/sec | **5.1x** |
|
|
76
|
+
| **Process Rate** | 62,814 jobs/sec | 23,923 jobs/sec | **2.6x** |
|
|
77
|
+
| **Total Time** | 2.04s | 6.48s | **3.2x** |
|
|
78
|
+
|
|
79
|
+
### Results: 1 Million Jobs (flashQ only)
|
|
80
|
+
|
|
81
|
+
| Scenario | Push Rate | Process Rate | Total Time | Data Integrity |
|
|
82
|
+
|----------|----------:|-------------:|-----------:|:--------------:|
|
|
83
|
+
| **No-op** | 266,809/s | 262,536/s | 7.56s | ✅ 100% |
|
|
84
|
+
| **CPU-bound** | 257,334/s | 65,240/s | 19.21s | ✅ 100% |
|
|
85
|
+
|
|
86
|
+
### Why flashQ is Faster
|
|
87
|
+
|
|
88
|
+
| Optimization | Description |
|
|
89
|
+
|--------------|-------------|
|
|
90
|
+
| **Rust + tokio** | Zero-cost abstractions, no GC pauses |
|
|
91
|
+
| **io_uring** | Linux kernel async I/O (when available) |
|
|
92
|
+
| **32 Shards** | Lock-free concurrent access via DashMap |
|
|
93
|
+
| **MessagePack** | 40% smaller payloads vs JSON |
|
|
94
|
+
| **Batch Operations** | Amortized network overhead |
|
|
95
|
+
| **No Redis Dependency** | Direct TCP protocol, no intermediary |
|
|
96
|
+
|
|
97
|
+
### Run Benchmarks
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
# flashQ benchmarks
|
|
101
|
+
bun run examples/heavy-benchmark.ts # No-op 100K
|
|
102
|
+
bun run examples/cpu-benchmark.ts # CPU-bound 100K
|
|
103
|
+
bun run examples/million-benchmark.ts # 1M jobs
|
|
104
|
+
|
|
105
|
+
# BullMQ comparison (requires Redis)
|
|
106
|
+
docker run -d -p 6379:6379 redis:7-alpine
|
|
107
|
+
bun run examples/bullmq-benchmark.ts # No-op 100K
|
|
108
|
+
bun run examples/bullmq-cpu-benchmark.ts # CPU-bound 100K
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Queue
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
const queue = new Queue('emails', {
|
|
117
|
+
host: 'localhost',
|
|
118
|
+
port: 6789,
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
// Add single job
|
|
122
|
+
await queue.add('send', data, {
|
|
123
|
+
priority: 10,
|
|
124
|
+
delay: 5000,
|
|
125
|
+
attempts: 3,
|
|
126
|
+
backoff: { type: 'exponential', delay: 1000 },
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// Add bulk
|
|
130
|
+
await queue.addBulk([
|
|
131
|
+
{ name: 'send', data: { to: 'a@test.com' } },
|
|
132
|
+
{ name: 'send', data: { to: 'b@test.com' }, opts: { priority: 10 } },
|
|
133
|
+
]);
|
|
134
|
+
|
|
135
|
+
// Control
|
|
136
|
+
await queue.pause();
|
|
137
|
+
await queue.resume();
|
|
138
|
+
await queue.drain(); // remove waiting
|
|
139
|
+
await queue.obliterate(); // remove all
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Worker
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
// Auto-starts by default (like BullMQ)
|
|
146
|
+
const worker = new Worker('emails', async (job) => {
|
|
147
|
+
return { done: true };
|
|
148
|
+
}, {
|
|
149
|
+
concurrency: 10,
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// Events
|
|
153
|
+
worker.on('completed', (job, result) => {});
|
|
154
|
+
worker.on('failed', (job, error) => {});
|
|
155
|
+
|
|
156
|
+
// Shutdown
|
|
157
|
+
await worker.close();
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Job Options
|
|
161
|
+
|
|
162
|
+
| Option | Type | Description |
|
|
163
|
+
|--------|------|-------------|
|
|
164
|
+
| `priority` | number | Higher = first (default: 0) |
|
|
165
|
+
| `delay` | number | Delay in ms |
|
|
166
|
+
| `attempts` | number | Retry count |
|
|
167
|
+
| `backoff` | number \| object | Backoff config |
|
|
168
|
+
| `timeout` | number | Processing timeout |
|
|
169
|
+
| `jobId` | string | Custom ID |
|
|
170
|
+
|
|
171
|
+
## Key-Value Storage
|
|
172
|
+
|
|
173
|
+
Redis-like KV store with TTL support and batch operations.
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
import { FlashQ } from 'flashq';
|
|
177
|
+
|
|
178
|
+
const client = new FlashQ();
|
|
179
|
+
|
|
180
|
+
// Basic operations
|
|
181
|
+
await client.kvSet('user:123', { name: 'John', email: 'john@example.com' });
|
|
182
|
+
const user = await client.kvGet('user:123');
|
|
183
|
+
await client.kvDel('user:123');
|
|
184
|
+
|
|
185
|
+
// With TTL (milliseconds)
|
|
186
|
+
await client.kvSet('session:abc', { token: 'xyz' }, { ttl: 3600000 }); // 1 hour
|
|
187
|
+
|
|
188
|
+
// TTL operations
|
|
189
|
+
await client.kvExpire('user:123', 60000); // Set TTL
|
|
190
|
+
const ttl = await client.kvTtl('user:123'); // Get remaining TTL
|
|
191
|
+
|
|
192
|
+
// Batch operations (10-100x faster!)
|
|
193
|
+
await client.kvMset([
|
|
194
|
+
{ key: 'user:1', value: { name: 'Alice' } },
|
|
195
|
+
{ key: 'user:2', value: { name: 'Bob' } },
|
|
196
|
+
{ key: 'user:3', value: { name: 'Charlie' }, ttl: 60000 },
|
|
197
|
+
]);
|
|
198
|
+
|
|
199
|
+
const users = await client.kvMget(['user:1', 'user:2', 'user:3']);
|
|
200
|
+
|
|
201
|
+
// Pattern matching
|
|
202
|
+
const userKeys = await client.kvKeys('user:*');
|
|
203
|
+
const sessionKeys = await client.kvKeys('session:???');
|
|
204
|
+
|
|
205
|
+
// Atomic counters
|
|
206
|
+
await client.kvIncr('page:views'); // +1
|
|
207
|
+
await client.kvIncr('user:123:score', 10); // +10
|
|
208
|
+
await client.kvDecr('stock:item:456'); // -1
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### KV Performance
|
|
212
|
+
|
|
213
|
+
| Operation | Throughput |
|
|
214
|
+
|-----------|------------|
|
|
215
|
+
| Sequential SET/GET | ~30K ops/sec |
|
|
216
|
+
| **Batch MSET** | **640K ops/sec** |
|
|
217
|
+
| **Batch MGET** | **1.2M ops/sec** |
|
|
218
|
+
|
|
219
|
+
> Use batch operations (MSET/MGET) for best performance!
|
|
220
|
+
|
|
221
|
+
## Pub/Sub
|
|
222
|
+
|
|
223
|
+
Redis-like publish/subscribe messaging.
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
import { FlashQ } from 'flashq';
|
|
227
|
+
|
|
228
|
+
const client = new FlashQ();
|
|
229
|
+
|
|
230
|
+
// Publish messages to a channel
|
|
231
|
+
const receivers = await client.publish('notifications', { type: 'alert', text: 'Hello!' });
|
|
232
|
+
console.log(`Message sent to ${receivers} subscribers`);
|
|
233
|
+
|
|
234
|
+
// Subscribe to channels
|
|
235
|
+
await client.pubsubSubscribe(['notifications', 'alerts']);
|
|
236
|
+
|
|
237
|
+
// Pattern subscribe (e.g., "events:*" matches "events:user:signup")
|
|
238
|
+
await client.pubsubPsubscribe(['events:*', 'logs:*']);
|
|
239
|
+
|
|
240
|
+
// List active channels
|
|
241
|
+
const allChannels = await client.pubsubChannels();
|
|
242
|
+
const eventChannels = await client.pubsubChannels('events:*');
|
|
243
|
+
|
|
244
|
+
// Get subscriber counts
|
|
245
|
+
const counts = await client.pubsubNumsub(['notifications', 'alerts']);
|
|
246
|
+
// [['notifications', 5], ['alerts', 2]]
|
|
247
|
+
|
|
248
|
+
// Unsubscribe
|
|
249
|
+
await client.pubsubUnsubscribe(['notifications']);
|
|
250
|
+
await client.pubsubPunsubscribe(['events:*']);
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
## Examples
|
|
254
|
+
|
|
255
|
+
```bash
|
|
256
|
+
bun run examples/01-basic.ts
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
| File | Description |
|
|
260
|
+
|------|-------------|
|
|
261
|
+
| 01-basic.ts | Queue + Worker basics |
|
|
262
|
+
| 02-job-options.ts | Priority, delay, retry |
|
|
263
|
+
| 03-bulk-jobs.ts | Add multiple jobs |
|
|
264
|
+
| 04-events.ts | Worker events |
|
|
265
|
+
| 05-queue-control.ts | Pause, resume, drain |
|
|
266
|
+
| 06-delayed.ts | Scheduled jobs |
|
|
267
|
+
| 07-retry.ts | Retry with backoff |
|
|
268
|
+
| 08-priority.ts | Priority ordering |
|
|
269
|
+
| 09-concurrency.ts | Parallel processing |
|
|
270
|
+
| 10-benchmark.ts | Basic performance test |
|
|
271
|
+
| **heavy-benchmark.ts** | 100K no-op benchmark |
|
|
272
|
+
| **cpu-benchmark.ts** | 100K CPU-bound benchmark |
|
|
273
|
+
| **million-benchmark.ts** | 1M jobs with verification |
|
|
274
|
+
| **benchmark-full.ts** | Memory + latency + throughput |
|
|
275
|
+
| **bullmq-benchmark.ts** | BullMQ comparison (no-op) |
|
|
276
|
+
| **bullmq-cpu-benchmark.ts** | BullMQ comparison (CPU) |
|
|
277
|
+
| **bullmq-benchmark-full.ts** | BullMQ memory + latency |
|
|
278
|
+
| kv-benchmark.ts | KV store benchmark |
|
|
279
|
+
| pubsub-example.ts | Pub/Sub messaging |
|
|
280
|
+
|
|
281
|
+
## License
|
|
282
|
+
|
|
283
|
+
MIT
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BullMQ-like advanced operations
|
|
3
|
+
*/
|
|
4
|
+
import type { IFlashQClient, JobState, JobWithState } from './types';
|
|
5
|
+
/**
|
|
6
|
+
* Get jobs filtered by queue and/or state with pagination.
|
|
7
|
+
*
|
|
8
|
+
* @param client - FlashQ client instance
|
|
9
|
+
* @param options - Filter options
|
|
10
|
+
* @returns Jobs and total count
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* // Get all waiting jobs
|
|
15
|
+
* const { jobs, total } = await client.getJobs({ state: 'waiting' });
|
|
16
|
+
*
|
|
17
|
+
* // Paginate results
|
|
18
|
+
* const { jobs } = await client.getJobs({ limit: 10, offset: 20 });
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export declare function getJobs(client: IFlashQClient, options?: {
|
|
22
|
+
queue?: string;
|
|
23
|
+
state?: JobState;
|
|
24
|
+
limit?: number;
|
|
25
|
+
offset?: number;
|
|
26
|
+
}): Promise<{
|
|
27
|
+
jobs: JobWithState[];
|
|
28
|
+
total: number;
|
|
29
|
+
}>;
|
|
30
|
+
/**
|
|
31
|
+
* Get job counts by state for a queue.
|
|
32
|
+
*
|
|
33
|
+
* @param client - FlashQ client instance
|
|
34
|
+
* @param queue - Queue name
|
|
35
|
+
* @returns Counts by state
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```typescript
|
|
39
|
+
* const counts = await client.getJobCounts('emails');
|
|
40
|
+
* console.log(`Waiting: ${counts.waiting}`);
|
|
41
|
+
* console.log(`Active: ${counts.active}`);
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export declare function getJobCounts(client: IFlashQClient, queue: string): Promise<{
|
|
45
|
+
waiting: number;
|
|
46
|
+
active: number;
|
|
47
|
+
delayed: number;
|
|
48
|
+
completed: number;
|
|
49
|
+
failed: number;
|
|
50
|
+
}>;
|
|
51
|
+
/**
|
|
52
|
+
* Get total count of jobs in a queue (waiting + delayed).
|
|
53
|
+
*
|
|
54
|
+
* @param client - FlashQ client instance
|
|
55
|
+
* @param queue - Queue name
|
|
56
|
+
* @returns Total job count
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```typescript
|
|
60
|
+
* const total = await client.count('emails');
|
|
61
|
+
* console.log(`${total} jobs in queue`);
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
export declare function count(client: IFlashQClient, queue: string): Promise<number>;
|
|
65
|
+
/**
|
|
66
|
+
* Clean jobs older than grace period by state.
|
|
67
|
+
*
|
|
68
|
+
* @param client - FlashQ client instance
|
|
69
|
+
* @param queue - Queue name
|
|
70
|
+
* @param grace - Grace period in ms
|
|
71
|
+
* @param state - Job state to clean
|
|
72
|
+
* @param limit - Optional max jobs to clean
|
|
73
|
+
* @returns Number of jobs cleaned
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* ```typescript
|
|
77
|
+
* // Clean completed jobs older than 1 hour
|
|
78
|
+
* const cleaned = await client.clean('emails', 3600000, 'completed');
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
export declare function clean(client: IFlashQClient, queue: string, grace: number, state: 'waiting' | 'delayed' | 'completed' | 'failed', limit?: number): Promise<number>;
|
|
82
|
+
/**
|
|
83
|
+
* Drain all waiting jobs from a queue.
|
|
84
|
+
*
|
|
85
|
+
* @param client - FlashQ client instance
|
|
86
|
+
* @param queue - Queue name
|
|
87
|
+
* @returns Number of jobs drained
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* ```typescript
|
|
91
|
+
* const drained = await client.drain('emails');
|
|
92
|
+
* console.log(`Drained ${drained} jobs`);
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
export declare function drain(client: IFlashQClient, queue: string): Promise<number>;
|
|
96
|
+
/**
|
|
97
|
+
* Remove ALL data for a queue (jobs, DLQ, cron, state).
|
|
98
|
+
* Use with caution - this is destructive!
|
|
99
|
+
*
|
|
100
|
+
* @param client - FlashQ client instance
|
|
101
|
+
* @param queue - Queue name
|
|
102
|
+
* @returns Total items removed
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* ```typescript
|
|
106
|
+
* const removed = await client.obliterate('test-queue');
|
|
107
|
+
* ```
|
|
108
|
+
*/
|
|
109
|
+
export declare function obliterate(client: IFlashQClient, queue: string): Promise<number>;
|
|
110
|
+
/**
|
|
111
|
+
* Change job priority.
|
|
112
|
+
*
|
|
113
|
+
* @param client - FlashQ client instance
|
|
114
|
+
* @param jobId - Job ID
|
|
115
|
+
* @param priority - New priority (higher = processed first)
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* ```typescript
|
|
119
|
+
* await client.changePriority(jobId, 100);
|
|
120
|
+
* ```
|
|
121
|
+
*/
|
|
122
|
+
export declare function changePriority(client: IFlashQClient, jobId: number, priority: number): Promise<void>;
|
|
123
|
+
/**
|
|
124
|
+
* Move job from processing back to delayed.
|
|
125
|
+
*
|
|
126
|
+
* @param client - FlashQ client instance
|
|
127
|
+
* @param jobId - Job ID
|
|
128
|
+
* @param delay - Delay in ms
|
|
129
|
+
*
|
|
130
|
+
* @example
|
|
131
|
+
* ```typescript
|
|
132
|
+
* // Delay for 5 minutes
|
|
133
|
+
* await client.moveToDelayed(jobId, 300000);
|
|
134
|
+
* ```
|
|
135
|
+
*/
|
|
136
|
+
export declare function moveToDelayed(client: IFlashQClient, jobId: number, delay: number): Promise<void>;
|
|
137
|
+
/**
|
|
138
|
+
* Promote delayed job to waiting immediately.
|
|
139
|
+
*
|
|
140
|
+
* @param client - FlashQ client instance
|
|
141
|
+
* @param jobId - Job ID
|
|
142
|
+
*
|
|
143
|
+
* @example
|
|
144
|
+
* ```typescript
|
|
145
|
+
* await client.promote(jobId);
|
|
146
|
+
* ```
|
|
147
|
+
*/
|
|
148
|
+
export declare function promote(client: IFlashQClient, jobId: number): Promise<void>;
|
|
149
|
+
/**
|
|
150
|
+
* Update job data.
|
|
151
|
+
*
|
|
152
|
+
* @param client - FlashQ client instance
|
|
153
|
+
* @param jobId - Job ID
|
|
154
|
+
* @param data - New data payload
|
|
155
|
+
*
|
|
156
|
+
* @example
|
|
157
|
+
* ```typescript
|
|
158
|
+
* await client.update(jobId, { email: 'new@example.com' });
|
|
159
|
+
* ```
|
|
160
|
+
*/
|
|
161
|
+
export declare function update<T = unknown>(client: IFlashQClient, jobId: number, data: T): Promise<void>;
|
|
162
|
+
/**
|
|
163
|
+
* Discard job - move directly to DLQ.
|
|
164
|
+
*
|
|
165
|
+
* @param client - FlashQ client instance
|
|
166
|
+
* @param jobId - Job ID
|
|
167
|
+
*
|
|
168
|
+
* @example
|
|
169
|
+
* ```typescript
|
|
170
|
+
* await client.discard(jobId);
|
|
171
|
+
* ```
|
|
172
|
+
*/
|
|
173
|
+
export declare function discard(client: IFlashQClient, jobId: number): Promise<void>;
|
|
174
|
+
//# sourceMappingURL=advanced.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"advanced.d.ts","sourceRoot":"","sources":["../../src/client/advanced.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,aAAa,EAAO,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE1E;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,OAAO,CAC3B,MAAM,EAAE,aAAa,EACrB,OAAO,GAAE;IACP,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,QAAQ,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACZ,GACL,OAAO,CAAC;IAAE,IAAI,EAAE,YAAY,EAAE,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAgBlD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,aAAa,EACrB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC;IACT,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC,CAmBD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,KAAK,CACzB,MAAM,EAAE,aAAa,EACrB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,MAAM,CAAC,CAMjB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,KAAK,CACzB,MAAM,EAAE,aAAa,EACrB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,EACrD,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,MAAM,CAAC,CASjB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,KAAK,CACzB,MAAM,EAAE,aAAa,EACrB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,MAAM,CAAC,CAMjB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,UAAU,CAC9B,MAAM,EAAE,aAAa,EACrB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,MAAM,CAAC,CAMjB;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,aAAa,EACrB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC,CAMf;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,aAAa,EACrB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC,CAMf;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,OAAO,CAC3B,MAAM,EAAE,aAAa,EACrB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC,CAKf;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,MAAM,CAAC,CAAC,GAAG,OAAO,EACtC,MAAM,EAAE,aAAa,EACrB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,CAAC,GACN,OAAO,CAAC,IAAI,CAAC,CAMf;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,OAAO,CAC3B,MAAM,EAAE,aAAa,EACrB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC,CAKf"}
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getJobs = getJobs;
|
|
4
|
+
exports.getJobCounts = getJobCounts;
|
|
5
|
+
exports.count = count;
|
|
6
|
+
exports.clean = clean;
|
|
7
|
+
exports.drain = drain;
|
|
8
|
+
exports.obliterate = obliterate;
|
|
9
|
+
exports.changePriority = changePriority;
|
|
10
|
+
exports.moveToDelayed = moveToDelayed;
|
|
11
|
+
exports.promote = promote;
|
|
12
|
+
exports.update = update;
|
|
13
|
+
exports.discard = discard;
|
|
14
|
+
/**
|
|
15
|
+
* Get jobs filtered by queue and/or state with pagination.
|
|
16
|
+
*
|
|
17
|
+
* @param client - FlashQ client instance
|
|
18
|
+
* @param options - Filter options
|
|
19
|
+
* @returns Jobs and total count
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```typescript
|
|
23
|
+
* // Get all waiting jobs
|
|
24
|
+
* const { jobs, total } = await client.getJobs({ state: 'waiting' });
|
|
25
|
+
*
|
|
26
|
+
* // Paginate results
|
|
27
|
+
* const { jobs } = await client.getJobs({ limit: 10, offset: 20 });
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
async function getJobs(client, options = {}) {
|
|
31
|
+
const response = await client.send({
|
|
32
|
+
cmd: 'GETJOBS',
|
|
33
|
+
queue: options.queue,
|
|
34
|
+
state: options.state,
|
|
35
|
+
limit: options.limit ?? 100,
|
|
36
|
+
offset: options.offset ?? 0,
|
|
37
|
+
});
|
|
38
|
+
return {
|
|
39
|
+
jobs: response.jobs.map((j) => ({ job: j.job, state: j.state })),
|
|
40
|
+
total: response.total,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Get job counts by state for a queue.
|
|
45
|
+
*
|
|
46
|
+
* @param client - FlashQ client instance
|
|
47
|
+
* @param queue - Queue name
|
|
48
|
+
* @returns Counts by state
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```typescript
|
|
52
|
+
* const counts = await client.getJobCounts('emails');
|
|
53
|
+
* console.log(`Waiting: ${counts.waiting}`);
|
|
54
|
+
* console.log(`Active: ${counts.active}`);
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
async function getJobCounts(client, queue) {
|
|
58
|
+
const response = await client.send({
|
|
59
|
+
cmd: 'GETJOBCOUNTS',
|
|
60
|
+
queue,
|
|
61
|
+
});
|
|
62
|
+
return {
|
|
63
|
+
waiting: response.waiting,
|
|
64
|
+
active: response.active,
|
|
65
|
+
delayed: response.delayed,
|
|
66
|
+
completed: response.completed,
|
|
67
|
+
failed: response.failed,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Get total count of jobs in a queue (waiting + delayed).
|
|
72
|
+
*
|
|
73
|
+
* @param client - FlashQ client instance
|
|
74
|
+
* @param queue - Queue name
|
|
75
|
+
* @returns Total job count
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```typescript
|
|
79
|
+
* const total = await client.count('emails');
|
|
80
|
+
* console.log(`${total} jobs in queue`);
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
async function count(client, queue) {
|
|
84
|
+
const response = await client.send({
|
|
85
|
+
cmd: 'COUNT',
|
|
86
|
+
queue,
|
|
87
|
+
});
|
|
88
|
+
return response.count;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Clean jobs older than grace period by state.
|
|
92
|
+
*
|
|
93
|
+
* @param client - FlashQ client instance
|
|
94
|
+
* @param queue - Queue name
|
|
95
|
+
* @param grace - Grace period in ms
|
|
96
|
+
* @param state - Job state to clean
|
|
97
|
+
* @param limit - Optional max jobs to clean
|
|
98
|
+
* @returns Number of jobs cleaned
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* ```typescript
|
|
102
|
+
* // Clean completed jobs older than 1 hour
|
|
103
|
+
* const cleaned = await client.clean('emails', 3600000, 'completed');
|
|
104
|
+
* ```
|
|
105
|
+
*/
|
|
106
|
+
async function clean(client, queue, grace, state, limit) {
|
|
107
|
+
const response = await client.send({
|
|
108
|
+
cmd: 'CLEAN',
|
|
109
|
+
queue,
|
|
110
|
+
grace,
|
|
111
|
+
state,
|
|
112
|
+
limit,
|
|
113
|
+
});
|
|
114
|
+
return response.count;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Drain all waiting jobs from a queue.
|
|
118
|
+
*
|
|
119
|
+
* @param client - FlashQ client instance
|
|
120
|
+
* @param queue - Queue name
|
|
121
|
+
* @returns Number of jobs drained
|
|
122
|
+
*
|
|
123
|
+
* @example
|
|
124
|
+
* ```typescript
|
|
125
|
+
* const drained = await client.drain('emails');
|
|
126
|
+
* console.log(`Drained ${drained} jobs`);
|
|
127
|
+
* ```
|
|
128
|
+
*/
|
|
129
|
+
async function drain(client, queue) {
|
|
130
|
+
const response = await client.send({
|
|
131
|
+
cmd: 'DRAIN',
|
|
132
|
+
queue,
|
|
133
|
+
});
|
|
134
|
+
return response.count;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Remove ALL data for a queue (jobs, DLQ, cron, state).
|
|
138
|
+
* Use with caution - this is destructive!
|
|
139
|
+
*
|
|
140
|
+
* @param client - FlashQ client instance
|
|
141
|
+
* @param queue - Queue name
|
|
142
|
+
* @returns Total items removed
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* ```typescript
|
|
146
|
+
* const removed = await client.obliterate('test-queue');
|
|
147
|
+
* ```
|
|
148
|
+
*/
|
|
149
|
+
async function obliterate(client, queue) {
|
|
150
|
+
const response = await client.send({
|
|
151
|
+
cmd: 'OBLITERATE',
|
|
152
|
+
queue,
|
|
153
|
+
});
|
|
154
|
+
return response.count;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Change job priority.
|
|
158
|
+
*
|
|
159
|
+
* @param client - FlashQ client instance
|
|
160
|
+
* @param jobId - Job ID
|
|
161
|
+
* @param priority - New priority (higher = processed first)
|
|
162
|
+
*
|
|
163
|
+
* @example
|
|
164
|
+
* ```typescript
|
|
165
|
+
* await client.changePriority(jobId, 100);
|
|
166
|
+
* ```
|
|
167
|
+
*/
|
|
168
|
+
async function changePriority(client, jobId, priority) {
|
|
169
|
+
await client.send({
|
|
170
|
+
cmd: 'CHANGEPRIORITY',
|
|
171
|
+
id: jobId,
|
|
172
|
+
priority,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Move job from processing back to delayed.
|
|
177
|
+
*
|
|
178
|
+
* @param client - FlashQ client instance
|
|
179
|
+
* @param jobId - Job ID
|
|
180
|
+
* @param delay - Delay in ms
|
|
181
|
+
*
|
|
182
|
+
* @example
|
|
183
|
+
* ```typescript
|
|
184
|
+
* // Delay for 5 minutes
|
|
185
|
+
* await client.moveToDelayed(jobId, 300000);
|
|
186
|
+
* ```
|
|
187
|
+
*/
|
|
188
|
+
async function moveToDelayed(client, jobId, delay) {
|
|
189
|
+
await client.send({
|
|
190
|
+
cmd: 'MOVETODELAYED',
|
|
191
|
+
id: jobId,
|
|
192
|
+
delay,
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Promote delayed job to waiting immediately.
|
|
197
|
+
*
|
|
198
|
+
* @param client - FlashQ client instance
|
|
199
|
+
* @param jobId - Job ID
|
|
200
|
+
*
|
|
201
|
+
* @example
|
|
202
|
+
* ```typescript
|
|
203
|
+
* await client.promote(jobId);
|
|
204
|
+
* ```
|
|
205
|
+
*/
|
|
206
|
+
async function promote(client, jobId) {
|
|
207
|
+
await client.send({
|
|
208
|
+
cmd: 'PROMOTE',
|
|
209
|
+
id: jobId,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Update job data.
|
|
214
|
+
*
|
|
215
|
+
* @param client - FlashQ client instance
|
|
216
|
+
* @param jobId - Job ID
|
|
217
|
+
* @param data - New data payload
|
|
218
|
+
*
|
|
219
|
+
* @example
|
|
220
|
+
* ```typescript
|
|
221
|
+
* await client.update(jobId, { email: 'new@example.com' });
|
|
222
|
+
* ```
|
|
223
|
+
*/
|
|
224
|
+
async function update(client, jobId, data) {
|
|
225
|
+
await client.send({
|
|
226
|
+
cmd: 'UPDATEJOB',
|
|
227
|
+
id: jobId,
|
|
228
|
+
data,
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Discard job - move directly to DLQ.
|
|
233
|
+
*
|
|
234
|
+
* @param client - FlashQ client instance
|
|
235
|
+
* @param jobId - Job ID
|
|
236
|
+
*
|
|
237
|
+
* @example
|
|
238
|
+
* ```typescript
|
|
239
|
+
* await client.discard(jobId);
|
|
240
|
+
* ```
|
|
241
|
+
*/
|
|
242
|
+
async function discard(client, jobId) {
|
|
243
|
+
await client.send({
|
|
244
|
+
cmd: 'DISCARD',
|
|
245
|
+
id: jobId,
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
//# sourceMappingURL=advanced.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"advanced.js","sourceRoot":"","sources":["../../src/client/advanced.ts"],"names":[],"mappings":"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"}
|