@prsm/queue 1.0.2 → 2.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 +109 -24
- package/package.json +11 -21
- package/src/index.js +1 -0
- package/src/queue.js +525 -0
- package/types/index.d.ts +1 -0
- package/types/queue.d.ts +4768 -0
- package/dist/index.cjs +0 -290
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -77
- package/dist/index.d.ts +0 -77
- package/dist/index.js +0 -257
- package/dist/index.js.map +0 -1
package/dist/index.cjs
DELETED
|
@@ -1,290 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __create = Object.create;
|
|
3
|
-
var __defProp = Object.defineProperty;
|
|
4
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
-
var __export = (target, all) => {
|
|
9
|
-
for (var name in all)
|
|
10
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
-
};
|
|
12
|
-
var __copyProps = (to, from, except, desc) => {
|
|
13
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
-
for (let key of __getOwnPropNames(from))
|
|
15
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
-
}
|
|
18
|
-
return to;
|
|
19
|
-
};
|
|
20
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
-
mod
|
|
27
|
-
));
|
|
28
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
-
|
|
30
|
-
// src/index.ts
|
|
31
|
-
var index_exports = {};
|
|
32
|
-
__export(index_exports, {
|
|
33
|
-
default: () => Queue
|
|
34
|
-
});
|
|
35
|
-
module.exports = __toCommonJS(index_exports);
|
|
36
|
-
|
|
37
|
-
// src/queue.ts
|
|
38
|
-
var import_redis = require("redis");
|
|
39
|
-
var import_events = require("events");
|
|
40
|
-
var import_crypto = require("crypto");
|
|
41
|
-
var import_ms = __toESM(require("@prsm/ms"), 1);
|
|
42
|
-
var Queue = class extends import_events.EventEmitter {
|
|
43
|
-
redis;
|
|
44
|
-
workerClients = [];
|
|
45
|
-
options;
|
|
46
|
-
handler;
|
|
47
|
-
workers = /* @__PURE__ */ new Map();
|
|
48
|
-
groupWorkers = /* @__PURE__ */ new Map();
|
|
49
|
-
cleanupTimer;
|
|
50
|
-
_ready;
|
|
51
|
-
_inFlight = 0;
|
|
52
|
-
_totalSettled = 0;
|
|
53
|
-
constructor(options = {}) {
|
|
54
|
-
super();
|
|
55
|
-
this.options = {
|
|
56
|
-
concurrency: options.concurrency ?? 1,
|
|
57
|
-
delay: (0, import_ms.default)(options.delay ?? 0),
|
|
58
|
-
timeout: (0, import_ms.default)(options.timeout ?? 0),
|
|
59
|
-
maxRetries: options.maxRetries ?? 3,
|
|
60
|
-
groups: {
|
|
61
|
-
concurrency: options.groups?.concurrency ?? options.concurrency ?? 1,
|
|
62
|
-
delay: (0, import_ms.default)(options.groups?.delay ?? options.delay ?? 0),
|
|
63
|
-
timeout: (0, import_ms.default)(options.groups?.timeout ?? options.timeout ?? 0),
|
|
64
|
-
maxRetries: options.groups?.maxRetries ?? options.maxRetries ?? 3
|
|
65
|
-
},
|
|
66
|
-
redisOptions: options.redisOptions ?? {},
|
|
67
|
-
cleanupInterval: options.cleanupInterval ?? 3e4
|
|
68
|
-
};
|
|
69
|
-
this.redis = (0, import_redis.createClient)(this.options.redisOptions);
|
|
70
|
-
this.redis.on("error", () => {
|
|
71
|
-
});
|
|
72
|
-
this._ready = this.initializeRedis();
|
|
73
|
-
}
|
|
74
|
-
/** resolves when redis is connected and all workers are ready to accept tasks */
|
|
75
|
-
ready() {
|
|
76
|
-
return this._ready;
|
|
77
|
-
}
|
|
78
|
-
get inFlight() {
|
|
79
|
-
return this._inFlight;
|
|
80
|
-
}
|
|
81
|
-
/** register the handler that processes each task. only one handler per queue. */
|
|
82
|
-
process(handler) {
|
|
83
|
-
this.handler = handler;
|
|
84
|
-
}
|
|
85
|
-
/** push a task to the main queue. returns the task uuid. */
|
|
86
|
-
async push(payload) {
|
|
87
|
-
const task = {
|
|
88
|
-
uuid: (0, import_crypto.randomUUID)(),
|
|
89
|
-
payload,
|
|
90
|
-
createdAt: Date.now(),
|
|
91
|
-
attempts: 0
|
|
92
|
-
};
|
|
93
|
-
await this.redis.lPush("queue:tasks", JSON.stringify(task));
|
|
94
|
-
this.emit("new", { task });
|
|
95
|
-
return task.uuid;
|
|
96
|
-
}
|
|
97
|
-
/** returns a scoped pusher for a named group. each group gets its own worker pool, spun up on first push. */
|
|
98
|
-
group(key) {
|
|
99
|
-
return {
|
|
100
|
-
push: async (payload) => {
|
|
101
|
-
const task = {
|
|
102
|
-
uuid: (0, import_crypto.randomUUID)(),
|
|
103
|
-
payload,
|
|
104
|
-
createdAt: Date.now(),
|
|
105
|
-
groupKey: key,
|
|
106
|
-
attempts: 0
|
|
107
|
-
};
|
|
108
|
-
await this.redis.lPush(`queue:groups:${key}`, JSON.stringify(task));
|
|
109
|
-
this.emit("new", { task });
|
|
110
|
-
if (!this.groupWorkers.has(key)) {
|
|
111
|
-
this.groupWorkers.set(key, /* @__PURE__ */ new Map());
|
|
112
|
-
await this.startGroupWorkers(key);
|
|
113
|
-
}
|
|
114
|
-
return task.uuid;
|
|
115
|
-
}
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
async createWorkerClient() {
|
|
119
|
-
const client = this.redis.duplicate();
|
|
120
|
-
client.on("error", () => {
|
|
121
|
-
});
|
|
122
|
-
await client.connect();
|
|
123
|
-
this.workerClients.push(client);
|
|
124
|
-
return client;
|
|
125
|
-
}
|
|
126
|
-
async startWorkers() {
|
|
127
|
-
const ready = [];
|
|
128
|
-
for (let i = 0; i < this.options.concurrency; i++) {
|
|
129
|
-
ready.push(this.startWorker(`worker-${i}`));
|
|
130
|
-
}
|
|
131
|
-
await Promise.all(ready);
|
|
132
|
-
}
|
|
133
|
-
async startGroupWorkers(groupKey) {
|
|
134
|
-
const groupWorkers = this.groupWorkers.get(groupKey);
|
|
135
|
-
const ready = [];
|
|
136
|
-
for (let i = 0; i < this.options.groups.concurrency; i++) {
|
|
137
|
-
const workerId = `group-${groupKey}-worker-${i}`;
|
|
138
|
-
groupWorkers.set(workerId, true);
|
|
139
|
-
ready.push(this.startGroupWorker(workerId, groupKey));
|
|
140
|
-
}
|
|
141
|
-
await Promise.all(ready);
|
|
142
|
-
}
|
|
143
|
-
async startWorker(workerId) {
|
|
144
|
-
this.workers.set(workerId, true);
|
|
145
|
-
const client = await this.createWorkerClient();
|
|
146
|
-
this.runWorkerLoop(workerId, client, "queue:tasks", this.workers, (task) => this.processTask(task));
|
|
147
|
-
}
|
|
148
|
-
async startGroupWorker(workerId, groupKey) {
|
|
149
|
-
const groupWorkers = this.groupWorkers.get(groupKey);
|
|
150
|
-
const client = await this.createWorkerClient();
|
|
151
|
-
this.runWorkerLoop(workerId, client, `queue:groups:${groupKey}`, groupWorkers, (task) => this.processGroupTask(task));
|
|
152
|
-
}
|
|
153
|
-
async runWorkerLoop(workerId, client, key, activeMap, processFn) {
|
|
154
|
-
const isGrouped = key.startsWith("queue:groups:");
|
|
155
|
-
const delay = isGrouped ? this.options.groups.delay : this.options.delay;
|
|
156
|
-
while (activeMap.get(workerId)) {
|
|
157
|
-
try {
|
|
158
|
-
if (!client.isOpen) break;
|
|
159
|
-
const taskData = await client.brPop(key, 1);
|
|
160
|
-
if (taskData) {
|
|
161
|
-
const task = JSON.parse(taskData.element);
|
|
162
|
-
this._inFlight++;
|
|
163
|
-
await processFn(task);
|
|
164
|
-
}
|
|
165
|
-
if (delay > 0) {
|
|
166
|
-
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
167
|
-
}
|
|
168
|
-
} catch (err) {
|
|
169
|
-
const error = err;
|
|
170
|
-
if (error.message?.includes("closed") || error.message?.includes("ClientClosedError")) {
|
|
171
|
-
break;
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
async processTask(task) {
|
|
177
|
-
task.attempts++;
|
|
178
|
-
try {
|
|
179
|
-
if (!this.handler) {
|
|
180
|
-
this.emit("complete", { task, result: void 0 });
|
|
181
|
-
this.settle();
|
|
182
|
-
return;
|
|
183
|
-
}
|
|
184
|
-
const timeoutPromise = this.options.timeout > 0 ? new Promise(
|
|
185
|
-
(_, reject) => setTimeout(() => reject(new Error("Task timeout")), this.options.timeout)
|
|
186
|
-
) : null;
|
|
187
|
-
const workPromise = Promise.resolve(this.handler(task.payload, task));
|
|
188
|
-
const result = timeoutPromise ? await Promise.race([workPromise, timeoutPromise]) : await workPromise;
|
|
189
|
-
this.emit("complete", { task, result });
|
|
190
|
-
this.settle();
|
|
191
|
-
} catch (error) {
|
|
192
|
-
if (task.attempts < this.options.maxRetries) {
|
|
193
|
-
this.emit("retry", { task, error, attempt: task.attempts });
|
|
194
|
-
this._inFlight--;
|
|
195
|
-
await this.redis.lPush("queue:tasks", JSON.stringify(task));
|
|
196
|
-
} else {
|
|
197
|
-
this.emit("failed", { task, error });
|
|
198
|
-
this.settle();
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
async processGroupTask(task) {
|
|
203
|
-
task.attempts++;
|
|
204
|
-
try {
|
|
205
|
-
if (!this.handler) {
|
|
206
|
-
this.emit("complete", { task, result: void 0 });
|
|
207
|
-
this.settle();
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
210
|
-
const timeoutPromise = this.options.groups.timeout > 0 ? new Promise(
|
|
211
|
-
(_, reject) => setTimeout(() => reject(new Error("Task timeout")), this.options.groups.timeout)
|
|
212
|
-
) : null;
|
|
213
|
-
const workPromise = Promise.resolve(this.handler(task.payload, task));
|
|
214
|
-
const result = timeoutPromise ? await Promise.race([workPromise, timeoutPromise]) : await workPromise;
|
|
215
|
-
this.emit("complete", { task, result });
|
|
216
|
-
this.settle();
|
|
217
|
-
} catch (error) {
|
|
218
|
-
if (task.attempts < this.options.groups.maxRetries) {
|
|
219
|
-
this.emit("retry", { task, error, attempt: task.attempts });
|
|
220
|
-
this._inFlight--;
|
|
221
|
-
await this.redis.lPush(`queue:groups:${task.groupKey}`, JSON.stringify(task));
|
|
222
|
-
} else {
|
|
223
|
-
this.emit("failed", { task, error });
|
|
224
|
-
this.settle();
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
settle() {
|
|
229
|
-
this._inFlight--;
|
|
230
|
-
this._totalSettled++;
|
|
231
|
-
if (this._inFlight === 0 && this._totalSettled > 0) {
|
|
232
|
-
this.emit("drain");
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
async initializeRedis() {
|
|
236
|
-
await this.redis.connect();
|
|
237
|
-
await this.startWorkers();
|
|
238
|
-
if (this.options.cleanupInterval > 0) {
|
|
239
|
-
this.cleanupTimer = setInterval(() => {
|
|
240
|
-
this.performPeriodicCleanup();
|
|
241
|
-
}, this.options.cleanupInterval);
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
async performPeriodicCleanup() {
|
|
245
|
-
try {
|
|
246
|
-
if (!this.redis.isOpen) {
|
|
247
|
-
return;
|
|
248
|
-
}
|
|
249
|
-
const groupKeys = Array.from(this.groupWorkers.keys());
|
|
250
|
-
for (const groupKey of groupKeys) {
|
|
251
|
-
const length = await this.redis.lLen(`queue:groups:${groupKey}`);
|
|
252
|
-
if (length === 0) {
|
|
253
|
-
const keyExists = await this.redis.exists(`queue:groups:${groupKey}`);
|
|
254
|
-
if (keyExists) {
|
|
255
|
-
await this.redis.del(`queue:groups:${groupKey}`);
|
|
256
|
-
}
|
|
257
|
-
const groupWorkers = this.groupWorkers.get(groupKey);
|
|
258
|
-
if (groupWorkers) {
|
|
259
|
-
groupWorkers.clear();
|
|
260
|
-
this.groupWorkers.delete(groupKey);
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
} catch (error) {
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
/** shuts down all workers and disconnects from redis. waits for initialization to complete first. */
|
|
268
|
-
async close() {
|
|
269
|
-
await this._ready.catch(() => {
|
|
270
|
-
});
|
|
271
|
-
if (this.cleanupTimer) {
|
|
272
|
-
clearInterval(this.cleanupTimer);
|
|
273
|
-
}
|
|
274
|
-
this.workers.clear();
|
|
275
|
-
for (const groupWorkers of this.groupWorkers.values()) {
|
|
276
|
-
groupWorkers.clear();
|
|
277
|
-
}
|
|
278
|
-
this.groupWorkers.clear();
|
|
279
|
-
for (const client of this.workerClients) {
|
|
280
|
-
if (client.isOpen) {
|
|
281
|
-
await client.disconnect();
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
this.workerClients = [];
|
|
285
|
-
if (this.redis.isOpen) {
|
|
286
|
-
await this.redis.quit();
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
};
|
|
290
|
-
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/queue.ts"],"sourcesContent":["export { default } from './queue.js'\nexport type { QueueOptions, Task, TaskHandler } from './queue.js'\n","import { createClient, RedisClientType } from 'redis'\nimport { EventEmitter } from 'events'\nimport { randomUUID } from 'crypto'\nimport ms from '@prsm/ms'\n\nexport interface QueueOptions<T = any> {\n /** number of tasks to process in parallel. @default 1 */\n concurrency?: number\n /** wait time between finishing one task and picking up the next (ms or string like \"500ms\"). @default 0 */\n delay?: number | string\n /** max time a single task handler can run before it's killed with a \"Task timeout\" error (ms or string like \"30s\"). 0 = no limit. @default 0 */\n timeout?: number | string\n /** how many times to re-attempt a failed task before emitting \"failed\". @default 3 */\n maxRetries?: number\n /** overrides for grouped queues, falls back to top-level values */\n groups?: {\n concurrency?: number\n delay?: number | string\n timeout?: number | string\n maxRetries?: number\n }\n redisOptions?: {\n url?: string\n host?: string\n port?: number\n password?: string\n }\n /** how often to clean up empty group keys in redis (ms). 0 = disabled. @default 30000 */\n cleanupInterval?: number\n}\n\n/** called for each task, return value is passed to the \"complete\" event */\nexport type TaskHandler<T, R = any> = (payload: T, task: Task<T>) => Promise<R> | R\n\nexport interface Task<T = any> {\n uuid: string\n payload: T\n createdAt: number\n /** present when the task was pushed via queue.group(key).push() */\n groupKey?: string\n /** number of times this task has been attempted so far */\n attempts: number\n}\n\n\nexport default class Queue<T = any, R = any> extends EventEmitter {\n private redis: RedisClientType\n private workerClients: RedisClientType[] = []\n private options: {\n concurrency: number\n delay: number\n timeout: number\n maxRetries: number\n groups: {\n concurrency: number\n delay: number\n timeout: number\n maxRetries: number\n }\n redisOptions: object\n cleanupInterval: number\n }\n private handler?: TaskHandler<T, R>\n private workers = new Map<string, boolean>()\n private groupWorkers = new Map<string, Map<string, boolean>>()\n private cleanupTimer?: NodeJS.Timeout\n private _ready: Promise<void>\n private _inFlight = 0\n private _totalSettled = 0\n\n constructor(options: QueueOptions<T> = {}) {\n super()\n\n this.options = {\n concurrency: options.concurrency ?? 1,\n delay: ms(options.delay ?? 0),\n timeout: ms(options.timeout ?? 0),\n maxRetries: options.maxRetries ?? 3,\n groups: {\n concurrency: options.groups?.concurrency ?? options.concurrency ?? 1,\n delay: ms(options.groups?.delay ?? options.delay ?? 0),\n timeout: ms(options.groups?.timeout ?? options.timeout ?? 0),\n maxRetries: options.groups?.maxRetries ?? options.maxRetries ?? 3\n },\n redisOptions: options.redisOptions ?? {},\n cleanupInterval: options.cleanupInterval ?? 30000\n }\n\n this.redis = createClient(this.options.redisOptions)\n this.redis.on('error', () => {})\n this._ready = this.initializeRedis()\n }\n\n /** resolves when redis is connected and all workers are ready to accept tasks */\n ready() {\n return this._ready\n }\n\n get inFlight() {\n return this._inFlight\n }\n\n /** register the handler that processes each task. only one handler per queue. */\n process(handler: TaskHandler<T, R>) {\n this.handler = handler\n }\n\n /** push a task to the main queue. returns the task uuid. */\n async push(payload: T): Promise<string> {\n const task: Task<T> = {\n uuid: randomUUID(),\n payload,\n createdAt: Date.now(),\n attempts: 0\n }\n\n await this.redis.lPush('queue:tasks', JSON.stringify(task))\n this.emit('new', { task })\n\n return task.uuid\n }\n\n /** returns a scoped pusher for a named group. each group gets its own worker pool, spun up on first push. */\n group(key: string) {\n return {\n push: async (payload: T): Promise<string> => {\n const task: Task<T> = {\n uuid: randomUUID(),\n payload,\n createdAt: Date.now(),\n groupKey: key,\n attempts: 0\n }\n\n await this.redis.lPush(`queue:groups:${key}`, JSON.stringify(task))\n this.emit('new', { task })\n\n if (!this.groupWorkers.has(key)) {\n this.groupWorkers.set(key, new Map())\n await this.startGroupWorkers(key)\n }\n\n return task.uuid\n }\n }\n }\n\n private async createWorkerClient(): Promise<RedisClientType> {\n const client = this.redis.duplicate() as RedisClientType\n client.on('error', () => {})\n await client.connect()\n this.workerClients.push(client)\n return client\n }\n\n private async startWorkers() {\n const ready = []\n for (let i = 0; i < this.options.concurrency; i++) {\n ready.push(this.startWorker(`worker-${i}`))\n }\n await Promise.all(ready)\n }\n\n private async startGroupWorkers(groupKey: string) {\n const groupWorkers = this.groupWorkers.get(groupKey)!\n const ready = []\n for (let i = 0; i < this.options.groups.concurrency; i++) {\n const workerId = `group-${groupKey}-worker-${i}`\n groupWorkers.set(workerId, true)\n ready.push(this.startGroupWorker(workerId, groupKey))\n }\n await Promise.all(ready)\n }\n\n private async startWorker(workerId: string) {\n this.workers.set(workerId, true)\n const client = await this.createWorkerClient()\n\n this.runWorkerLoop(workerId, client, 'queue:tasks', this.workers, (task) => this.processTask(task))\n }\n\n private async startGroupWorker(workerId: string, groupKey: string) {\n const groupWorkers = this.groupWorkers.get(groupKey)!\n const client = await this.createWorkerClient()\n\n this.runWorkerLoop(workerId, client, `queue:groups:${groupKey}`, groupWorkers, (task) => this.processGroupTask(task))\n }\n\n private async runWorkerLoop(\n workerId: string,\n client: RedisClientType,\n key: string,\n activeMap: Map<string, boolean>,\n processFn: (task: Task<T>) => Promise<void>\n ) {\n const isGrouped = key.startsWith('queue:groups:')\n const delay = isGrouped ? this.options.groups.delay : this.options.delay\n\n while (activeMap.get(workerId)) {\n try {\n if (!client.isOpen) break\n\n const taskData = await client.brPop(key, 1)\n\n if (taskData) {\n const task: Task<T> = JSON.parse(taskData.element)\n this._inFlight++\n await processFn(task)\n }\n\n if (delay > 0) {\n await new Promise(resolve => setTimeout(resolve, delay))\n }\n } catch (err) {\n const error = err as Error\n if (error.message?.includes('closed') || error.message?.includes('ClientClosedError')) {\n break\n }\n }\n }\n }\n\n private async processTask(task: Task<T>) {\n task.attempts++\n\n try {\n if (!this.handler) {\n this.emit('complete', { task, result: undefined })\n this.settle()\n return\n }\n\n const timeoutPromise = this.options.timeout > 0\n ? new Promise<never>((_, reject) =>\n setTimeout(() => reject(new Error('Task timeout')), this.options.timeout)\n )\n : null\n\n const workPromise = Promise.resolve(this.handler(task.payload, task))\n\n const result = timeoutPromise\n ? await Promise.race([workPromise, timeoutPromise])\n : await workPromise\n\n this.emit('complete', { task, result })\n this.settle()\n } catch (error) {\n if (task.attempts < this.options.maxRetries) {\n this.emit('retry', { task, error, attempt: task.attempts })\n this._inFlight--\n await this.redis.lPush('queue:tasks', JSON.stringify(task))\n } else {\n this.emit('failed', { task, error })\n this.settle()\n }\n }\n }\n\n private async processGroupTask(task: Task<T>) {\n task.attempts++\n\n try {\n if (!this.handler) {\n this.emit('complete', { task, result: undefined })\n this.settle()\n return\n }\n\n const timeoutPromise = this.options.groups.timeout > 0\n ? new Promise<never>((_, reject) =>\n setTimeout(() => reject(new Error('Task timeout')), this.options.groups.timeout)\n )\n : null\n\n const workPromise = Promise.resolve(this.handler(task.payload, task))\n\n const result = timeoutPromise\n ? await Promise.race([workPromise, timeoutPromise])\n : await workPromise\n\n this.emit('complete', { task, result })\n this.settle()\n } catch (error) {\n if (task.attempts < this.options.groups.maxRetries) {\n this.emit('retry', { task, error, attempt: task.attempts })\n this._inFlight--\n await this.redis.lPush(`queue:groups:${task.groupKey}`, JSON.stringify(task))\n } else {\n this.emit('failed', { task, error })\n this.settle()\n }\n }\n }\n\n private settle() {\n this._inFlight--\n this._totalSettled++\n if (this._inFlight === 0 && this._totalSettled > 0) {\n this.emit('drain')\n }\n }\n\n private async initializeRedis() {\n await this.redis.connect()\n await this.startWorkers()\n\n if (this.options.cleanupInterval > 0) {\n this.cleanupTimer = setInterval(() => {\n this.performPeriodicCleanup()\n }, this.options.cleanupInterval)\n }\n }\n\n private async performPeriodicCleanup() {\n try {\n if (!this.redis.isOpen) {\n return\n }\n\n const groupKeys = Array.from(this.groupWorkers.keys())\n\n for (const groupKey of groupKeys) {\n const length = await this.redis.lLen(`queue:groups:${groupKey}`)\n\n if (length === 0) {\n const keyExists = await this.redis.exists(`queue:groups:${groupKey}`)\n if (keyExists) {\n await this.redis.del(`queue:groups:${groupKey}`)\n }\n\n const groupWorkers = this.groupWorkers.get(groupKey)\n if (groupWorkers) {\n groupWorkers.clear()\n this.groupWorkers.delete(groupKey)\n }\n }\n }\n } catch (error) {\n // cleanup error handled silently\n }\n }\n\n /** shuts down all workers and disconnects from redis. waits for initialization to complete first. */\n async close() {\n await this._ready.catch(() => {})\n\n if (this.cleanupTimer) {\n clearInterval(this.cleanupTimer)\n }\n this.workers.clear()\n for (const groupWorkers of this.groupWorkers.values()) {\n groupWorkers.clear()\n }\n this.groupWorkers.clear()\n\n for (const client of this.workerClients) {\n if (client.isOpen) {\n await client.disconnect()\n }\n }\n this.workerClients = []\n\n if (this.redis.isOpen) {\n await this.redis.quit()\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,mBAA8C;AAC9C,oBAA6B;AAC7B,oBAA2B;AAC3B,gBAAe;AA0Cf,IAAqB,QAArB,cAAqD,2BAAa;AAAA,EACxD;AAAA,EACA,gBAAmC,CAAC;AAAA,EACpC;AAAA,EAcA;AAAA,EACA,UAAU,oBAAI,IAAqB;AAAA,EACnC,eAAe,oBAAI,IAAkC;AAAA,EACrD;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ,gBAAgB;AAAA,EAExB,YAAY,UAA2B,CAAC,GAAG;AACzC,UAAM;AAEN,SAAK,UAAU;AAAA,MACb,aAAa,QAAQ,eAAe;AAAA,MACpC,WAAO,UAAAA,SAAG,QAAQ,SAAS,CAAC;AAAA,MAC5B,aAAS,UAAAA,SAAG,QAAQ,WAAW,CAAC;AAAA,MAChC,YAAY,QAAQ,cAAc;AAAA,MAClC,QAAQ;AAAA,QACN,aAAa,QAAQ,QAAQ,eAAe,QAAQ,eAAe;AAAA,QACnE,WAAO,UAAAA,SAAG,QAAQ,QAAQ,SAAS,QAAQ,SAAS,CAAC;AAAA,QACrD,aAAS,UAAAA,SAAG,QAAQ,QAAQ,WAAW,QAAQ,WAAW,CAAC;AAAA,QAC3D,YAAY,QAAQ,QAAQ,cAAc,QAAQ,cAAc;AAAA,MAClE;AAAA,MACA,cAAc,QAAQ,gBAAgB,CAAC;AAAA,MACvC,iBAAiB,QAAQ,mBAAmB;AAAA,IAC9C;AAEA,SAAK,YAAQ,2BAAa,KAAK,QAAQ,YAAY;AACnD,SAAK,MAAM,GAAG,SAAS,MAAM;AAAA,IAAC,CAAC;AAC/B,SAAK,SAAS,KAAK,gBAAgB;AAAA,EACrC;AAAA;AAAA,EAGA,QAAQ;AACN,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,WAAW;AACb,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,QAAQ,SAA4B;AAClC,SAAK,UAAU;AAAA,EACjB;AAAA;AAAA,EAGA,MAAM,KAAK,SAA6B;AACtC,UAAM,OAAgB;AAAA,MACpB,UAAM,0BAAW;AAAA,MACjB;AAAA,MACA,WAAW,KAAK,IAAI;AAAA,MACpB,UAAU;AAAA,IACZ;AAEA,UAAM,KAAK,MAAM,MAAM,eAAe,KAAK,UAAU,IAAI,CAAC;AAC1D,SAAK,KAAK,OAAO,EAAE,KAAK,CAAC;AAEzB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,MAAM,KAAa;AACjB,WAAO;AAAA,MACL,MAAM,OAAO,YAAgC;AAC3C,cAAM,OAAgB;AAAA,UACpB,UAAM,0BAAW;AAAA,UACjB;AAAA,UACA,WAAW,KAAK,IAAI;AAAA,UACpB,UAAU;AAAA,UACV,UAAU;AAAA,QACZ;AAEA,cAAM,KAAK,MAAM,MAAM,gBAAgB,GAAG,IAAI,KAAK,UAAU,IAAI,CAAC;AAClE,aAAK,KAAK,OAAO,EAAE,KAAK,CAAC;AAEzB,YAAI,CAAC,KAAK,aAAa,IAAI,GAAG,GAAG;AAC/B,eAAK,aAAa,IAAI,KAAK,oBAAI,IAAI,CAAC;AACpC,gBAAM,KAAK,kBAAkB,GAAG;AAAA,QAClC;AAEA,eAAO,KAAK;AAAA,MACd;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,qBAA+C;AAC3D,UAAM,SAAS,KAAK,MAAM,UAAU;AACpC,WAAO,GAAG,SAAS,MAAM;AAAA,IAAC,CAAC;AAC3B,UAAM,OAAO,QAAQ;AACrB,SAAK,cAAc,KAAK,MAAM;AAC9B,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,eAAe;AAC3B,UAAM,QAAQ,CAAC;AACf,aAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,aAAa,KAAK;AACjD,YAAM,KAAK,KAAK,YAAY,UAAU,CAAC,EAAE,CAAC;AAAA,IAC5C;AACA,UAAM,QAAQ,IAAI,KAAK;AAAA,EACzB;AAAA,EAEA,MAAc,kBAAkB,UAAkB;AAChD,UAAM,eAAe,KAAK,aAAa,IAAI,QAAQ;AACnD,UAAM,QAAQ,CAAC;AACf,aAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,OAAO,aAAa,KAAK;AACxD,YAAM,WAAW,SAAS,QAAQ,WAAW,CAAC;AAC9C,mBAAa,IAAI,UAAU,IAAI;AAC/B,YAAM,KAAK,KAAK,iBAAiB,UAAU,QAAQ,CAAC;AAAA,IACtD;AACA,UAAM,QAAQ,IAAI,KAAK;AAAA,EACzB;AAAA,EAEA,MAAc,YAAY,UAAkB;AAC1C,SAAK,QAAQ,IAAI,UAAU,IAAI;AAC/B,UAAM,SAAS,MAAM,KAAK,mBAAmB;AAE7C,SAAK,cAAc,UAAU,QAAQ,eAAe,KAAK,SAAS,CAAC,SAAS,KAAK,YAAY,IAAI,CAAC;AAAA,EACpG;AAAA,EAEA,MAAc,iBAAiB,UAAkB,UAAkB;AACjE,UAAM,eAAe,KAAK,aAAa,IAAI,QAAQ;AACnD,UAAM,SAAS,MAAM,KAAK,mBAAmB;AAE7C,SAAK,cAAc,UAAU,QAAQ,gBAAgB,QAAQ,IAAI,cAAc,CAAC,SAAS,KAAK,iBAAiB,IAAI,CAAC;AAAA,EACtH;AAAA,EAEA,MAAc,cACZ,UACA,QACA,KACA,WACA,WACA;AACA,UAAM,YAAY,IAAI,WAAW,eAAe;AAChD,UAAM,QAAQ,YAAY,KAAK,QAAQ,OAAO,QAAQ,KAAK,QAAQ;AAEnE,WAAO,UAAU,IAAI,QAAQ,GAAG;AAC9B,UAAI;AACF,YAAI,CAAC,OAAO,OAAQ;AAEpB,cAAM,WAAW,MAAM,OAAO,MAAM,KAAK,CAAC;AAE1C,YAAI,UAAU;AACZ,gBAAM,OAAgB,KAAK,MAAM,SAAS,OAAO;AACjD,eAAK;AACL,gBAAM,UAAU,IAAI;AAAA,QACtB;AAEA,YAAI,QAAQ,GAAG;AACb,gBAAM,IAAI,QAAQ,aAAW,WAAW,SAAS,KAAK,CAAC;AAAA,QACzD;AAAA,MACF,SAAS,KAAK;AACZ,cAAM,QAAQ;AACd,YAAI,MAAM,SAAS,SAAS,QAAQ,KAAK,MAAM,SAAS,SAAS,mBAAmB,GAAG;AACrF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,YAAY,MAAe;AACvC,SAAK;AAEL,QAAI;AACF,UAAI,CAAC,KAAK,SAAS;AACjB,aAAK,KAAK,YAAY,EAAE,MAAM,QAAQ,OAAU,CAAC;AACjD,aAAK,OAAO;AACZ;AAAA,MACF;AAEA,YAAM,iBAAiB,KAAK,QAAQ,UAAU,IAC1C,IAAI;AAAA,QAAe,CAAC,GAAG,WACrB,WAAW,MAAM,OAAO,IAAI,MAAM,cAAc,CAAC,GAAG,KAAK,QAAQ,OAAO;AAAA,MAC1E,IACA;AAEJ,YAAM,cAAc,QAAQ,QAAQ,KAAK,QAAQ,KAAK,SAAS,IAAI,CAAC;AAEpE,YAAM,SAAS,iBACX,MAAM,QAAQ,KAAK,CAAC,aAAa,cAAc,CAAC,IAChD,MAAM;AAEV,WAAK,KAAK,YAAY,EAAE,MAAM,OAAO,CAAC;AACtC,WAAK,OAAO;AAAA,IACd,SAAS,OAAO;AACd,UAAI,KAAK,WAAW,KAAK,QAAQ,YAAY;AAC3C,aAAK,KAAK,SAAS,EAAE,MAAM,OAAO,SAAS,KAAK,SAAS,CAAC;AAC1D,aAAK;AACL,cAAM,KAAK,MAAM,MAAM,eAAe,KAAK,UAAU,IAAI,CAAC;AAAA,MAC5D,OAAO;AACL,aAAK,KAAK,UAAU,EAAE,MAAM,MAAM,CAAC;AACnC,aAAK,OAAO;AAAA,MACd;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,iBAAiB,MAAe;AAC5C,SAAK;AAEL,QAAI;AACF,UAAI,CAAC,KAAK,SAAS;AACjB,aAAK,KAAK,YAAY,EAAE,MAAM,QAAQ,OAAU,CAAC;AACjD,aAAK,OAAO;AACZ;AAAA,MACF;AAEA,YAAM,iBAAiB,KAAK,QAAQ,OAAO,UAAU,IACjD,IAAI;AAAA,QAAe,CAAC,GAAG,WACrB,WAAW,MAAM,OAAO,IAAI,MAAM,cAAc,CAAC,GAAG,KAAK,QAAQ,OAAO,OAAO;AAAA,MACjF,IACA;AAEJ,YAAM,cAAc,QAAQ,QAAQ,KAAK,QAAQ,KAAK,SAAS,IAAI,CAAC;AAEpE,YAAM,SAAS,iBACX,MAAM,QAAQ,KAAK,CAAC,aAAa,cAAc,CAAC,IAChD,MAAM;AAEV,WAAK,KAAK,YAAY,EAAE,MAAM,OAAO,CAAC;AACtC,WAAK,OAAO;AAAA,IACd,SAAS,OAAO;AACd,UAAI,KAAK,WAAW,KAAK,QAAQ,OAAO,YAAY;AAClD,aAAK,KAAK,SAAS,EAAE,MAAM,OAAO,SAAS,KAAK,SAAS,CAAC;AAC1D,aAAK;AACL,cAAM,KAAK,MAAM,MAAM,gBAAgB,KAAK,QAAQ,IAAI,KAAK,UAAU,IAAI,CAAC;AAAA,MAC9E,OAAO;AACL,aAAK,KAAK,UAAU,EAAE,MAAM,MAAM,CAAC;AACnC,aAAK,OAAO;AAAA,MACd;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,SAAS;AACf,SAAK;AACL,SAAK;AACL,QAAI,KAAK,cAAc,KAAK,KAAK,gBAAgB,GAAG;AAClD,WAAK,KAAK,OAAO;AAAA,IACnB;AAAA,EACF;AAAA,EAEA,MAAc,kBAAkB;AAC9B,UAAM,KAAK,MAAM,QAAQ;AACzB,UAAM,KAAK,aAAa;AAExB,QAAI,KAAK,QAAQ,kBAAkB,GAAG;AACpC,WAAK,eAAe,YAAY,MAAM;AACpC,aAAK,uBAAuB;AAAA,MAC9B,GAAG,KAAK,QAAQ,eAAe;AAAA,IACjC;AAAA,EACF;AAAA,EAEA,MAAc,yBAAyB;AACrC,QAAI;AACF,UAAI,CAAC,KAAK,MAAM,QAAQ;AACtB;AAAA,MACF;AAEA,YAAM,YAAY,MAAM,KAAK,KAAK,aAAa,KAAK,CAAC;AAErD,iBAAW,YAAY,WAAW;AAChC,cAAM,SAAS,MAAM,KAAK,MAAM,KAAK,gBAAgB,QAAQ,EAAE;AAE/D,YAAI,WAAW,GAAG;AAChB,gBAAM,YAAY,MAAM,KAAK,MAAM,OAAO,gBAAgB,QAAQ,EAAE;AACpE,cAAI,WAAW;AACb,kBAAM,KAAK,MAAM,IAAI,gBAAgB,QAAQ,EAAE;AAAA,UACjD;AAEA,gBAAM,eAAe,KAAK,aAAa,IAAI,QAAQ;AACnD,cAAI,cAAc;AAChB,yBAAa,MAAM;AACnB,iBAAK,aAAa,OAAO,QAAQ;AAAA,UACnC;AAAA,QACF;AAAA,MACF;AAAA,IACF,SAAS,OAAO;AAAA,IAEhB;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,QAAQ;AACZ,UAAM,KAAK,OAAO,MAAM,MAAM;AAAA,IAAC,CAAC;AAEhC,QAAI,KAAK,cAAc;AACrB,oBAAc,KAAK,YAAY;AAAA,IACjC;AACA,SAAK,QAAQ,MAAM;AACnB,eAAW,gBAAgB,KAAK,aAAa,OAAO,GAAG;AACrD,mBAAa,MAAM;AAAA,IACrB;AACA,SAAK,aAAa,MAAM;AAExB,eAAW,UAAU,KAAK,eAAe;AACvC,UAAI,OAAO,QAAQ;AACjB,cAAM,OAAO,WAAW;AAAA,MAC1B;AAAA,IACF;AACA,SAAK,gBAAgB,CAAC;AAEtB,QAAI,KAAK,MAAM,QAAQ;AACrB,YAAM,KAAK,MAAM,KAAK;AAAA,IACxB;AAAA,EACF;AACF;","names":["ms"]}
|
package/dist/index.d.cts
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import { EventEmitter } from 'events';
|
|
2
|
-
|
|
3
|
-
interface QueueOptions<T = any> {
|
|
4
|
-
/** number of tasks to process in parallel. @default 1 */
|
|
5
|
-
concurrency?: number;
|
|
6
|
-
/** wait time between finishing one task and picking up the next (ms or string like "500ms"). @default 0 */
|
|
7
|
-
delay?: number | string;
|
|
8
|
-
/** max time a single task handler can run before it's killed with a "Task timeout" error (ms or string like "30s"). 0 = no limit. @default 0 */
|
|
9
|
-
timeout?: number | string;
|
|
10
|
-
/** how many times to re-attempt a failed task before emitting "failed". @default 3 */
|
|
11
|
-
maxRetries?: number;
|
|
12
|
-
/** overrides for grouped queues, falls back to top-level values */
|
|
13
|
-
groups?: {
|
|
14
|
-
concurrency?: number;
|
|
15
|
-
delay?: number | string;
|
|
16
|
-
timeout?: number | string;
|
|
17
|
-
maxRetries?: number;
|
|
18
|
-
};
|
|
19
|
-
redisOptions?: {
|
|
20
|
-
url?: string;
|
|
21
|
-
host?: string;
|
|
22
|
-
port?: number;
|
|
23
|
-
password?: string;
|
|
24
|
-
};
|
|
25
|
-
/** how often to clean up empty group keys in redis (ms). 0 = disabled. @default 30000 */
|
|
26
|
-
cleanupInterval?: number;
|
|
27
|
-
}
|
|
28
|
-
/** called for each task, return value is passed to the "complete" event */
|
|
29
|
-
type TaskHandler<T, R = any> = (payload: T, task: Task<T>) => Promise<R> | R;
|
|
30
|
-
interface Task<T = any> {
|
|
31
|
-
uuid: string;
|
|
32
|
-
payload: T;
|
|
33
|
-
createdAt: number;
|
|
34
|
-
/** present when the task was pushed via queue.group(key).push() */
|
|
35
|
-
groupKey?: string;
|
|
36
|
-
/** number of times this task has been attempted so far */
|
|
37
|
-
attempts: number;
|
|
38
|
-
}
|
|
39
|
-
declare class Queue<T = any, R = any> extends EventEmitter {
|
|
40
|
-
private redis;
|
|
41
|
-
private workerClients;
|
|
42
|
-
private options;
|
|
43
|
-
private handler?;
|
|
44
|
-
private workers;
|
|
45
|
-
private groupWorkers;
|
|
46
|
-
private cleanupTimer?;
|
|
47
|
-
private _ready;
|
|
48
|
-
private _inFlight;
|
|
49
|
-
private _totalSettled;
|
|
50
|
-
constructor(options?: QueueOptions<T>);
|
|
51
|
-
/** resolves when redis is connected and all workers are ready to accept tasks */
|
|
52
|
-
ready(): Promise<void>;
|
|
53
|
-
get inFlight(): number;
|
|
54
|
-
/** register the handler that processes each task. only one handler per queue. */
|
|
55
|
-
process(handler: TaskHandler<T, R>): void;
|
|
56
|
-
/** push a task to the main queue. returns the task uuid. */
|
|
57
|
-
push(payload: T): Promise<string>;
|
|
58
|
-
/** returns a scoped pusher for a named group. each group gets its own worker pool, spun up on first push. */
|
|
59
|
-
group(key: string): {
|
|
60
|
-
push: (payload: T) => Promise<string>;
|
|
61
|
-
};
|
|
62
|
-
private createWorkerClient;
|
|
63
|
-
private startWorkers;
|
|
64
|
-
private startGroupWorkers;
|
|
65
|
-
private startWorker;
|
|
66
|
-
private startGroupWorker;
|
|
67
|
-
private runWorkerLoop;
|
|
68
|
-
private processTask;
|
|
69
|
-
private processGroupTask;
|
|
70
|
-
private settle;
|
|
71
|
-
private initializeRedis;
|
|
72
|
-
private performPeriodicCleanup;
|
|
73
|
-
/** shuts down all workers and disconnects from redis. waits for initialization to complete first. */
|
|
74
|
-
close(): Promise<void>;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export { type QueueOptions, type Task, type TaskHandler, Queue as default };
|
package/dist/index.d.ts
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import { EventEmitter } from 'events';
|
|
2
|
-
|
|
3
|
-
interface QueueOptions<T = any> {
|
|
4
|
-
/** number of tasks to process in parallel. @default 1 */
|
|
5
|
-
concurrency?: number;
|
|
6
|
-
/** wait time between finishing one task and picking up the next (ms or string like "500ms"). @default 0 */
|
|
7
|
-
delay?: number | string;
|
|
8
|
-
/** max time a single task handler can run before it's killed with a "Task timeout" error (ms or string like "30s"). 0 = no limit. @default 0 */
|
|
9
|
-
timeout?: number | string;
|
|
10
|
-
/** how many times to re-attempt a failed task before emitting "failed". @default 3 */
|
|
11
|
-
maxRetries?: number;
|
|
12
|
-
/** overrides for grouped queues, falls back to top-level values */
|
|
13
|
-
groups?: {
|
|
14
|
-
concurrency?: number;
|
|
15
|
-
delay?: number | string;
|
|
16
|
-
timeout?: number | string;
|
|
17
|
-
maxRetries?: number;
|
|
18
|
-
};
|
|
19
|
-
redisOptions?: {
|
|
20
|
-
url?: string;
|
|
21
|
-
host?: string;
|
|
22
|
-
port?: number;
|
|
23
|
-
password?: string;
|
|
24
|
-
};
|
|
25
|
-
/** how often to clean up empty group keys in redis (ms). 0 = disabled. @default 30000 */
|
|
26
|
-
cleanupInterval?: number;
|
|
27
|
-
}
|
|
28
|
-
/** called for each task, return value is passed to the "complete" event */
|
|
29
|
-
type TaskHandler<T, R = any> = (payload: T, task: Task<T>) => Promise<R> | R;
|
|
30
|
-
interface Task<T = any> {
|
|
31
|
-
uuid: string;
|
|
32
|
-
payload: T;
|
|
33
|
-
createdAt: number;
|
|
34
|
-
/** present when the task was pushed via queue.group(key).push() */
|
|
35
|
-
groupKey?: string;
|
|
36
|
-
/** number of times this task has been attempted so far */
|
|
37
|
-
attempts: number;
|
|
38
|
-
}
|
|
39
|
-
declare class Queue<T = any, R = any> extends EventEmitter {
|
|
40
|
-
private redis;
|
|
41
|
-
private workerClients;
|
|
42
|
-
private options;
|
|
43
|
-
private handler?;
|
|
44
|
-
private workers;
|
|
45
|
-
private groupWorkers;
|
|
46
|
-
private cleanupTimer?;
|
|
47
|
-
private _ready;
|
|
48
|
-
private _inFlight;
|
|
49
|
-
private _totalSettled;
|
|
50
|
-
constructor(options?: QueueOptions<T>);
|
|
51
|
-
/** resolves when redis is connected and all workers are ready to accept tasks */
|
|
52
|
-
ready(): Promise<void>;
|
|
53
|
-
get inFlight(): number;
|
|
54
|
-
/** register the handler that processes each task. only one handler per queue. */
|
|
55
|
-
process(handler: TaskHandler<T, R>): void;
|
|
56
|
-
/** push a task to the main queue. returns the task uuid. */
|
|
57
|
-
push(payload: T): Promise<string>;
|
|
58
|
-
/** returns a scoped pusher for a named group. each group gets its own worker pool, spun up on first push. */
|
|
59
|
-
group(key: string): {
|
|
60
|
-
push: (payload: T) => Promise<string>;
|
|
61
|
-
};
|
|
62
|
-
private createWorkerClient;
|
|
63
|
-
private startWorkers;
|
|
64
|
-
private startGroupWorkers;
|
|
65
|
-
private startWorker;
|
|
66
|
-
private startGroupWorker;
|
|
67
|
-
private runWorkerLoop;
|
|
68
|
-
private processTask;
|
|
69
|
-
private processGroupTask;
|
|
70
|
-
private settle;
|
|
71
|
-
private initializeRedis;
|
|
72
|
-
private performPeriodicCleanup;
|
|
73
|
-
/** shuts down all workers and disconnects from redis. waits for initialization to complete first. */
|
|
74
|
-
close(): Promise<void>;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export { type QueueOptions, type Task, type TaskHandler, Queue as default };
|