@bymax-one/nest-queue 1.0.6 → 1.0.8
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/CHANGELOG.md +59 -0
- package/README.md +42 -11
- package/dist/server/index.cjs +81 -40
- package/dist/server/index.d.cts +4 -9
- package/dist/server/index.d.ts +4 -9
- package/dist/server/index.mjs +81 -40
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,63 @@ versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## [1.0.8] — 2026-08-04
|
|
11
|
+
|
|
12
|
+
**Runtime change.** `dist/` differs from `1.0.7`.
|
|
13
|
+
|
|
14
|
+
### Security
|
|
15
|
+
|
|
16
|
+
- The Redis credentials are no longer disclosed when a service that holds them is
|
|
17
|
+
serialized. `connection` moves from a plain field on the resolved options to a
|
|
18
|
+
non-enumerable accessor, and `ConnectionResolver` keeps the ioredis client and the
|
|
19
|
+
consumer's module options in ECMAScript private fields, as do the maps holding the
|
|
20
|
+
`Queue`, `Worker` and `QueueEvents` instances. Those objects were all reachable by
|
|
21
|
+
walking a service: a `url` carries the password inline, and an ioredis instance carries
|
|
22
|
+
`options.password` as a plain field, so `JSON.stringify`, object spread and
|
|
23
|
+
`util.inspect` on `QueueService` emitted the password in plaintext — which is what a
|
|
24
|
+
structured logger does when it renders a provider it was handed, and what an error
|
|
25
|
+
reporter does when it captures the scope of a throw.
|
|
26
|
+
|
|
27
|
+
Reading on purpose is unchanged: `options.connection` resolves as before, and no public
|
|
28
|
+
type or export changed.
|
|
29
|
+
|
|
30
|
+
## [1.0.7] — 2026-08-04
|
|
31
|
+
|
|
32
|
+
**Runtime change.** `dist/` differs from `1.0.6`: the four decorators no longer carry
|
|
33
|
+
a `reflect-metadata` side-effect import, so the built bundle no longer references the
|
|
34
|
+
package at all.
|
|
35
|
+
|
|
36
|
+
### Fixed
|
|
37
|
+
|
|
38
|
+
- **The application owns the `reflect-metadata` polyfill.** `@Processor`, `@Process`,
|
|
39
|
+
`@OnWorkerEvent` and `@OnQueueEvent` each imported it for its side effect. None of
|
|
40
|
+
the other eight `@bymax-one` libraries does — the polyfill is global state the
|
|
41
|
+
application initialises once in `main.ts`, and NestJS pulls it in regardless:
|
|
42
|
+
importing `@nestjs/common` alone takes `Reflect.defineMetadata` from `undefined` to
|
|
43
|
+
`function`. Nothing here needed to load it.
|
|
44
|
+
|
|
45
|
+
Carrying it also contradicted this package's own `"sideEffects": false`, which
|
|
46
|
+
asserts that no module has a side effect while importing something whose entire
|
|
47
|
+
purpose is one.
|
|
48
|
+
|
|
49
|
+
The cost was measurable in a consumer's bundle: with the import present, esbuild
|
|
50
|
+
inlines the polyfill, taking a minimal bundle from **53 kB to 95 kB** even when the
|
|
51
|
+
application had already loaded it.
|
|
52
|
+
|
|
53
|
+
Nothing changes for a correctly wired application. The decorators are reachable
|
|
54
|
+
only through the `.` subpath, whose bundle imports `@nestjs/common` on its first
|
|
55
|
+
line — so the polyfill is present before any decorator body runs, which the tests
|
|
56
|
+
and a real consumer both confirm.
|
|
57
|
+
|
|
58
|
+
### Changed
|
|
59
|
+
|
|
60
|
+
- **The Quick Start shows the `main.ts` entry point**, with `import 'reflect-metadata'`
|
|
61
|
+
as its first line, and the peer matrix states plainly that the polyfill belongs to
|
|
62
|
+
the application. Previously the README named `reflect-metadata` only as a peer, which
|
|
63
|
+
was survivable while the library loaded it and is not now.
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
10
67
|
## [1.0.6] — 2026-08-02
|
|
11
68
|
|
|
12
69
|
Metadata only. `dist/` is byte-identical to `1.0.5` — verified by diffing a fresh
|
|
@@ -332,6 +389,8 @@ v6 peer range.
|
|
|
332
389
|
|
|
333
390
|
---
|
|
334
391
|
|
|
392
|
+
[1.0.8]: https://github.com/bymaxone/nest-queue/compare/v1.0.7...v1.0.8
|
|
393
|
+
[1.0.7]: https://github.com/bymaxone/nest-queue/compare/v1.0.6...v1.0.7
|
|
335
394
|
[1.0.6]: https://github.com/bymaxone/nest-queue/releases/tag/v1.0.6
|
|
336
395
|
[1.0.5]: https://github.com/bymaxone/nest-queue/releases/tag/v1.0.5
|
|
337
396
|
[1.0.4]: https://github.com/bymaxone/nest-queue/releases/tag/v1.0.4
|
package/README.md
CHANGED
|
@@ -120,6 +120,12 @@ Both subpaths ship ESM **and** CommonJS with declarations for each format, so a
|
|
|
120
120
|
| `./shared` | None |
|
|
121
121
|
| Optional | `bullmq-otel ^1` (OpenTelemetry) |
|
|
122
122
|
|
|
123
|
+
> [!IMPORTANT]
|
|
124
|
+
> `reflect-metadata` is a **global polyfill the application owns** — import it once at
|
|
125
|
+
> the top of `main.ts`. This library never imports it, so it cannot be shipped twice
|
|
126
|
+
> into your bundle; NestJS pulls it in as well, which is why a correctly wired
|
|
127
|
+
> application already has it before a decorator runs.
|
|
128
|
+
|
|
123
129
|
---
|
|
124
130
|
|
|
125
131
|
> [!TIP]
|
|
@@ -137,7 +143,24 @@ Both subpaths ship ESM **and** CommonJS with declarations for each format, so a
|
|
|
137
143
|
## 🚀 Quick Start
|
|
138
144
|
|
|
139
145
|
```bash
|
|
140
|
-
pnpm add @bymax-one/nest-queue bullmq ioredis
|
|
146
|
+
pnpm add @bymax-one/nest-queue bullmq ioredis reflect-metadata
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
`reflect-metadata` is loaded **once by your application**, before anything else — this
|
|
150
|
+
is the standard NestJS entry point and the decorators in this library depend on it:
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
// main.ts
|
|
154
|
+
import 'reflect-metadata'
|
|
155
|
+
import { NestFactory } from '@nestjs/core'
|
|
156
|
+
import { AppModule } from './app.module'
|
|
157
|
+
|
|
158
|
+
async function bootstrap(): Promise<void> {
|
|
159
|
+
const app = await NestFactory.create(AppModule)
|
|
160
|
+
await app.listen(3000)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
void bootstrap()
|
|
141
164
|
```
|
|
142
165
|
|
|
143
166
|
Wire the module in your root `AppModule`:
|
|
@@ -688,6 +711,14 @@ message, or an exception payload. `QueueException.details` carries only scalar
|
|
|
688
711
|
configuration values (`{ actualValue, expectedValue, limit, received }`), never a
|
|
689
712
|
connection object and never `job.data`.
|
|
690
713
|
|
|
714
|
+
They are also not reachable by serializing the objects that hold them. The connection is
|
|
715
|
+
attached to the resolved options as a non-enumerable accessor, and the resolver keeps the
|
|
716
|
+
ioredis client — which carries `options.password` as a plain field — in a private field,
|
|
717
|
+
as do the registries that hold the `Queue`, `Worker` and `QueueEvents` instances. So
|
|
718
|
+
`JSON.stringify`, object spread, `util.inspect` and `util.inspect` with `showHidden` all
|
|
719
|
+
come back without credentials, which is what matters when a structured logger renders a
|
|
720
|
+
provider it was handed or an error reporter captures the scope of a throw.
|
|
721
|
+
|
|
691
722
|
### Job data is opaque
|
|
692
723
|
|
|
693
724
|
The library treats `job.data` as a payload to transport, never to inspect. It is never
|
|
@@ -732,16 +763,16 @@ key will do it twice at some point.
|
|
|
732
763
|
|
|
733
764
|
## 🛡️ Security Table
|
|
734
765
|
|
|
735
|
-
| Layer | Implementation
|
|
736
|
-
| ----------------- |
|
|
737
|
-
| Credentials | Injected options only; never read from `process.env`, never logged, never placed in an exception
|
|
738
|
-
| Error payloads | `QueueException.details` restricted to scalar configuration values — no `job.data`, no connection object
|
|
739
|
-
| Job payloads | Treated as opaque; never deep-merged (prototype-pollution guard), never logged
|
|
740
|
-
| Namespacing | `prefix` applied uniformly to `Queue`, `Worker`, `QueueEvents` and `FlowProducer`
|
|
741
|
-
| Connection policy | `maxRetriesPerRequest: null` confined to duplicated consumer connections; producers keep fail-fast retries
|
|
742
|
-
| Delivery | At-least-once with a bounded drain; unacknowledged work is re-queued by BullMQ's stalled check
|
|
743
|
-
| Supply chain | `dependencies: {}`; SHA-pinned Actions, OSV-Scanner, TruffleHog, OpenSSF Scorecard; npm publish over OIDC
|
|
744
|
-
| Input validation | Options validated at bootstrap; cron patterns validated by BullMQ's own parser, never a hand-rolled regex
|
|
766
|
+
| Layer | Implementation |
|
|
767
|
+
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
768
|
+
| Credentials | Injected options only; never read from `process.env`, never logged, never placed in an exception; held in a non-enumerable accessor and private fields, so serializing a service omits them |
|
|
769
|
+
| Error payloads | `QueueException.details` restricted to scalar configuration values — no `job.data`, no connection object |
|
|
770
|
+
| Job payloads | Treated as opaque; never deep-merged (prototype-pollution guard), never logged |
|
|
771
|
+
| Namespacing | `prefix` applied uniformly to `Queue`, `Worker`, `QueueEvents` and `FlowProducer` |
|
|
772
|
+
| Connection policy | `maxRetriesPerRequest: null` confined to duplicated consumer connections; producers keep fail-fast retries |
|
|
773
|
+
| Delivery | At-least-once with a bounded drain; unacknowledged work is re-queued by BullMQ's stalled check |
|
|
774
|
+
| Supply chain | `dependencies: {}`; SHA-pinned Actions, OSV-Scanner, TruffleHog, OpenSSF Scorecard; npm publish over OIDC |
|
|
775
|
+
| Input validation | Options validated at bootstrap; cron patterns validated by BullMQ's own parser, never a hand-rolled regex |
|
|
745
776
|
|
|
746
777
|
> [!IMPORTANT]
|
|
747
778
|
> **Delivery is at-least-once, and `prefix` is not an access boundary.** Handlers must be
|
package/dist/server/index.cjs
CHANGED
|
@@ -5,9 +5,13 @@ var core = require('@nestjs/core');
|
|
|
5
5
|
var ioredis = require('ioredis');
|
|
6
6
|
var bullmq = require('bullmq');
|
|
7
7
|
var path = require('path');
|
|
8
|
-
require('reflect-metadata');
|
|
9
8
|
|
|
9
|
+
var __defProp = Object.defineProperty;
|
|
10
10
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
11
|
+
var __typeError = (msg) => {
|
|
12
|
+
throw TypeError(msg);
|
|
13
|
+
};
|
|
14
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
11
15
|
var __decorateClass = (decorators, target, key, kind) => {
|
|
12
16
|
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
13
17
|
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
@@ -16,6 +20,11 @@ var __decorateClass = (decorators, target, key, kind) => {
|
|
|
16
20
|
return result;
|
|
17
21
|
};
|
|
18
22
|
var __decorateParam = (index, decorator) => (target, key) => decorator(target, key, index);
|
|
23
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
24
|
+
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
|
25
|
+
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
26
|
+
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
27
|
+
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), member.set(obj, value), value);
|
|
19
28
|
|
|
20
29
|
// src/shared/constants/error-codes.ts
|
|
21
30
|
var QUEUE_ERROR_CODES = {
|
|
@@ -112,8 +121,8 @@ var DEFAULT_SHUTDOWN_DRAIN_TIMEOUT_MS = 3e4;
|
|
|
112
121
|
|
|
113
122
|
// src/server/config/resolved-options.ts
|
|
114
123
|
function applyDefaults(opts) {
|
|
124
|
+
const connection = opts.connection;
|
|
115
125
|
const base = {
|
|
116
|
-
connection: opts.connection,
|
|
117
126
|
defaultJobOptions: { ...DEFAULT_JOB_OPTIONS, ...opts.defaultJobOptions ?? {} },
|
|
118
127
|
prefix: opts.prefix ?? "bull",
|
|
119
128
|
queueOptions: opts.queueOptions ?? {},
|
|
@@ -129,6 +138,11 @@ function applyDefaults(opts) {
|
|
|
129
138
|
connectionReadyTimeoutMs: opts.connectionReadyTimeoutMs ?? DEFAULT_CONNECTION_READY_TIMEOUT_MS
|
|
130
139
|
};
|
|
131
140
|
if (opts.telemetry !== void 0) base.telemetry = opts.telemetry;
|
|
141
|
+
Object.defineProperty(base, "connection", {
|
|
142
|
+
get: () => connection,
|
|
143
|
+
enumerable: false,
|
|
144
|
+
configurable: false
|
|
145
|
+
});
|
|
132
146
|
return Object.freeze(base);
|
|
133
147
|
}
|
|
134
148
|
|
|
@@ -158,34 +172,44 @@ function duplicateConnection(client) {
|
|
|
158
172
|
}
|
|
159
173
|
|
|
160
174
|
// src/server/services/connection-resolver.service.ts
|
|
175
|
+
var _client, _options;
|
|
161
176
|
exports.ConnectionResolver = class ConnectionResolver {
|
|
162
177
|
constructor(options) {
|
|
163
|
-
|
|
178
|
+
/**
|
|
179
|
+
* The resolved Redis client.
|
|
180
|
+
*
|
|
181
|
+
* An ECMAScript private field rather than a TypeScript `private` one, which
|
|
182
|
+
* is erased at runtime: an ioredis instance carries `options.password` as a
|
|
183
|
+
* plain field, so leaving this enumerable would let anything that serializes
|
|
184
|
+
* a service holding this resolver walk into the credentials.
|
|
185
|
+
*/
|
|
186
|
+
__privateAdd(this, _client);
|
|
187
|
+
/** The consumer's module options, which carry the Redis credentials. */
|
|
188
|
+
__privateAdd(this, _options);
|
|
189
|
+
__publicField(this, "mode");
|
|
190
|
+
__privateSet(this, _options, options);
|
|
164
191
|
}
|
|
165
|
-
options;
|
|
166
|
-
client;
|
|
167
|
-
mode;
|
|
168
192
|
/** Resolve and validate the connection. Call once during module bootstrap. */
|
|
169
193
|
async init() {
|
|
170
|
-
const cfg = this.
|
|
194
|
+
const cfg = __privateGet(this, _options).connection;
|
|
171
195
|
if ("client" in cfg) {
|
|
172
196
|
this.initModeA(cfg.client);
|
|
173
197
|
return;
|
|
174
198
|
}
|
|
175
199
|
this.mode = "mode-b-owned";
|
|
176
|
-
this
|
|
200
|
+
__privateSet(this, _client, "url" in cfg ? new ioredis.Redis(cfg.url, { ...cfg.options ?? {}, lazyConnect: false }) : new ioredis.Redis({ ...cfg.options, lazyConnect: false }));
|
|
177
201
|
await this.waitReady(
|
|
178
|
-
this.
|
|
202
|
+
__privateGet(this, _options).connectionReadyTimeoutMs ?? DEFAULT_CONNECTION_READY_TIMEOUT_MS
|
|
179
203
|
);
|
|
180
204
|
}
|
|
181
205
|
/** The resolved Queue-role client. */
|
|
182
206
|
getClient() {
|
|
183
|
-
if (!this
|
|
207
|
+
if (!__privateGet(this, _client)) {
|
|
184
208
|
throw new QueueException(QUEUE_ERROR_CODES.CONNECTION_INVALID, 500, {
|
|
185
209
|
reason: "not initialized"
|
|
186
210
|
});
|
|
187
211
|
}
|
|
188
|
-
return this
|
|
212
|
+
return __privateGet(this, _client);
|
|
189
213
|
}
|
|
190
214
|
/** The resolved connection mode. */
|
|
191
215
|
getMode() {
|
|
@@ -202,8 +226,8 @@ exports.ConnectionResolver = class ConnectionResolver {
|
|
|
202
226
|
}
|
|
203
227
|
/** Close the library-owned connection on shutdown; never touch a BYO client. */
|
|
204
228
|
async teardown() {
|
|
205
|
-
if (this.isOwned() && this
|
|
206
|
-
const client = this
|
|
229
|
+
if (this.isOwned() && __privateGet(this, _client)) {
|
|
230
|
+
const client = __privateGet(this, _client);
|
|
207
231
|
await client.quit().catch(() => {
|
|
208
232
|
client.disconnect();
|
|
209
233
|
});
|
|
@@ -212,7 +236,7 @@ exports.ConnectionResolver = class ConnectionResolver {
|
|
|
212
236
|
/** Validate and adopt a bring-your-own client for the Queue role. */
|
|
213
237
|
initModeA(client) {
|
|
214
238
|
this.mode = "mode-a-byo";
|
|
215
|
-
this
|
|
239
|
+
__privateSet(this, _client, client);
|
|
216
240
|
if (!isClientUsable(client)) {
|
|
217
241
|
throw new QueueException(QUEUE_ERROR_CODES.CONNECTION_INVALID, 500, { status: client.status });
|
|
218
242
|
}
|
|
@@ -250,6 +274,8 @@ exports.ConnectionResolver = class ConnectionResolver {
|
|
|
250
274
|
});
|
|
251
275
|
}
|
|
252
276
|
};
|
|
277
|
+
_client = new WeakMap();
|
|
278
|
+
_options = new WeakMap();
|
|
253
279
|
exports.ConnectionResolver = __decorateClass([
|
|
254
280
|
common.Injectable(),
|
|
255
281
|
__decorateParam(0, common.Inject(BYMAX_QUEUE_OPTIONS))
|
|
@@ -298,15 +324,16 @@ var MAX_BULK_SIZE = 1e3;
|
|
|
298
324
|
function isOperationalError(err) {
|
|
299
325
|
return typeof err?.code === "string";
|
|
300
326
|
}
|
|
327
|
+
var _queues;
|
|
301
328
|
exports.QueueService = class QueueService {
|
|
302
329
|
constructor(connection, options) {
|
|
303
330
|
this.connection = connection;
|
|
304
331
|
this.options = options;
|
|
332
|
+
__publicField(this, "logger", new common.Logger(exports.QueueService.name));
|
|
333
|
+
__privateAdd(this, _queues, /* @__PURE__ */ new Map());
|
|
305
334
|
}
|
|
306
335
|
connection;
|
|
307
336
|
options;
|
|
308
|
-
logger = new common.Logger(exports.QueueService.name);
|
|
309
|
-
queues = /* @__PURE__ */ new Map();
|
|
310
337
|
/**
|
|
311
338
|
* Return the cached `Queue` for `queueName`, or create one with the module
|
|
312
339
|
* defaults applied. Subsequent calls with the same name return the same
|
|
@@ -316,7 +343,7 @@ exports.QueueService = class QueueService {
|
|
|
316
343
|
* @param overrides - Optional per-queue option overrides.
|
|
317
344
|
*/
|
|
318
345
|
getOrCreateQueue(queueName, overrides) {
|
|
319
|
-
const existing = this.
|
|
346
|
+
const existing = __privateGet(this, _queues).get(queueName);
|
|
320
347
|
if (existing) return existing;
|
|
321
348
|
const queue = new bullmq.Queue(queueName, {
|
|
322
349
|
connection: this.connection.getClient(),
|
|
@@ -327,7 +354,7 @@ exports.QueueService = class QueueService {
|
|
|
327
354
|
...overrides
|
|
328
355
|
});
|
|
329
356
|
attachDefaultErrorListener(queue, this.logger, "Queue", queueName);
|
|
330
|
-
this.
|
|
357
|
+
__privateGet(this, _queues).set(queueName, queue);
|
|
331
358
|
return queue;
|
|
332
359
|
}
|
|
333
360
|
/** Resolve the raw cached queue (default generics) for internal delegation. */
|
|
@@ -531,7 +558,7 @@ exports.QueueService = class QueueService {
|
|
|
531
558
|
}
|
|
532
559
|
/** Return a read-only view of the cached queues. */
|
|
533
560
|
getCachedQueues() {
|
|
534
|
-
return this
|
|
561
|
+
return __privateGet(this, _queues);
|
|
535
562
|
}
|
|
536
563
|
/**
|
|
537
564
|
* Close every cached queue (swallowing per-queue errors) and clear the cache.
|
|
@@ -543,12 +570,13 @@ exports.QueueService = class QueueService {
|
|
|
543
570
|
* exists to prevent. `QueueLifecycle` is the only caller.
|
|
544
571
|
*/
|
|
545
572
|
async closeAll() {
|
|
546
|
-
for (const queue of this.
|
|
573
|
+
for (const queue of __privateGet(this, _queues).values()) {
|
|
547
574
|
await queue.close().catch(() => void 0);
|
|
548
575
|
}
|
|
549
|
-
this.
|
|
576
|
+
__privateGet(this, _queues).clear();
|
|
550
577
|
}
|
|
551
578
|
};
|
|
579
|
+
_queues = new WeakMap();
|
|
552
580
|
exports.QueueService = __decorateClass([
|
|
553
581
|
common.Injectable(),
|
|
554
582
|
__decorateParam(0, common.Inject(exports.ConnectionResolver)),
|
|
@@ -705,15 +733,16 @@ exports.MetricsService = class MetricsService {
|
|
|
705
733
|
exports.MetricsService = __decorateClass([
|
|
706
734
|
common.Injectable()
|
|
707
735
|
], exports.MetricsService);
|
|
736
|
+
var _entries;
|
|
708
737
|
exports.WorkerRegistry = class WorkerRegistry {
|
|
709
738
|
constructor(connection, options) {
|
|
710
739
|
this.connection = connection;
|
|
711
740
|
this.options = options;
|
|
741
|
+
__publicField(this, "logger", new common.Logger(exports.WorkerRegistry.name));
|
|
742
|
+
__privateAdd(this, _entries, /* @__PURE__ */ new Map());
|
|
712
743
|
}
|
|
713
744
|
connection;
|
|
714
745
|
options;
|
|
715
|
-
logger = new common.Logger(exports.WorkerRegistry.name);
|
|
716
|
-
entries = /* @__PURE__ */ new Map();
|
|
717
746
|
/**
|
|
718
747
|
* Register an in-process worker with a handler function. The BullMQ `Worker`
|
|
719
748
|
* constructor is synchronous — no Redis handshake is performed at registration
|
|
@@ -744,7 +773,7 @@ exports.WorkerRegistry = class WorkerRegistry {
|
|
|
744
773
|
});
|
|
745
774
|
}
|
|
746
775
|
attachDefaultErrorListener(worker, this.logger, "Worker", queueName);
|
|
747
|
-
this.
|
|
776
|
+
__privateGet(this, _entries).set(queueName, { worker, connection: conn });
|
|
748
777
|
return worker;
|
|
749
778
|
}
|
|
750
779
|
/**
|
|
@@ -783,7 +812,7 @@ exports.WorkerRegistry = class WorkerRegistry {
|
|
|
783
812
|
});
|
|
784
813
|
}
|
|
785
814
|
attachDefaultErrorListener(worker, this.logger, "Worker", queueName);
|
|
786
|
-
this.
|
|
815
|
+
__privateGet(this, _entries).set(queueName, { worker, connection: conn });
|
|
787
816
|
return worker;
|
|
788
817
|
}
|
|
789
818
|
/**
|
|
@@ -794,11 +823,11 @@ exports.WorkerRegistry = class WorkerRegistry {
|
|
|
794
823
|
* @param queueName - The queue whose worker should be removed.
|
|
795
824
|
*/
|
|
796
825
|
async unregister(queueName) {
|
|
797
|
-
const entry = this.
|
|
826
|
+
const entry = __privateGet(this, _entries).get(queueName);
|
|
798
827
|
if (!entry) return;
|
|
799
828
|
await entry.worker.close();
|
|
800
829
|
await this.closeConnection(entry.connection);
|
|
801
|
-
this.
|
|
830
|
+
__privateGet(this, _entries).delete(queueName);
|
|
802
831
|
}
|
|
803
832
|
/**
|
|
804
833
|
* Returns the queue names of all currently registered workers.
|
|
@@ -806,7 +835,7 @@ exports.WorkerRegistry = class WorkerRegistry {
|
|
|
806
835
|
* @returns An immutable array of queue names.
|
|
807
836
|
*/
|
|
808
837
|
list() {
|
|
809
|
-
return Array.from(this.
|
|
838
|
+
return Array.from(__privateGet(this, _entries).keys());
|
|
810
839
|
}
|
|
811
840
|
/**
|
|
812
841
|
* Returns a read-only view of the registered workers keyed by queue name.
|
|
@@ -816,7 +845,7 @@ exports.WorkerRegistry = class WorkerRegistry {
|
|
|
816
845
|
*/
|
|
817
846
|
getAll() {
|
|
818
847
|
return new Map(
|
|
819
|
-
Array.from(this
|
|
848
|
+
Array.from(__privateGet(this, _entries), ([name, entry]) => [name, entry.worker])
|
|
820
849
|
);
|
|
821
850
|
}
|
|
822
851
|
/**
|
|
@@ -829,7 +858,7 @@ exports.WorkerRegistry = class WorkerRegistry {
|
|
|
829
858
|
*/
|
|
830
859
|
getConnections() {
|
|
831
860
|
return new Map(
|
|
832
|
-
Array.from(this
|
|
861
|
+
Array.from(__privateGet(this, _entries), ([name, entry]) => [name, entry.connection])
|
|
833
862
|
);
|
|
834
863
|
}
|
|
835
864
|
/**
|
|
@@ -852,7 +881,7 @@ exports.WorkerRegistry = class WorkerRegistry {
|
|
|
852
881
|
* @throws {QueueException} `DUPLICATE_PROCESSOR` if the queue is already registered.
|
|
853
882
|
*/
|
|
854
883
|
guardDuplicate(queueName) {
|
|
855
|
-
if (this.
|
|
884
|
+
if (__privateGet(this, _entries).has(queueName)) {
|
|
856
885
|
throw new QueueException(QUEUE_ERROR_CODES.DUPLICATE_PROCESSOR, 500, { queueName });
|
|
857
886
|
}
|
|
858
887
|
}
|
|
@@ -961,21 +990,23 @@ exports.WorkerRegistry = class WorkerRegistry {
|
|
|
961
990
|
return result;
|
|
962
991
|
}
|
|
963
992
|
};
|
|
993
|
+
_entries = new WeakMap();
|
|
964
994
|
exports.WorkerRegistry = __decorateClass([
|
|
965
995
|
common.Injectable(),
|
|
966
996
|
__decorateParam(0, common.Inject(exports.ConnectionResolver)),
|
|
967
997
|
__decorateParam(1, common.Inject(BYMAX_QUEUE_RESOLVED_OPTIONS))
|
|
968
998
|
], exports.WorkerRegistry);
|
|
999
|
+
var _events, _connections;
|
|
969
1000
|
exports.QueueEventsRegistry = class QueueEventsRegistry {
|
|
970
1001
|
constructor(connection, options) {
|
|
971
1002
|
this.connection = connection;
|
|
972
1003
|
this.options = options;
|
|
1004
|
+
__publicField(this, "logger", new common.Logger(exports.QueueEventsRegistry.name));
|
|
1005
|
+
__privateAdd(this, _events, /* @__PURE__ */ new Map());
|
|
1006
|
+
__privateAdd(this, _connections, /* @__PURE__ */ new Map());
|
|
973
1007
|
}
|
|
974
1008
|
connection;
|
|
975
1009
|
options;
|
|
976
|
-
logger = new common.Logger(exports.QueueEventsRegistry.name);
|
|
977
|
-
events = /* @__PURE__ */ new Map();
|
|
978
|
-
connections = /* @__PURE__ */ new Map();
|
|
979
1010
|
/**
|
|
980
1011
|
* Returns the cached `QueueEvents` for `queueName`, creating one on first
|
|
981
1012
|
* call. Subsequent calls for the same name return the same instance without
|
|
@@ -985,7 +1016,7 @@ exports.QueueEventsRegistry = class QueueEventsRegistry {
|
|
|
985
1016
|
* @returns The `QueueEvents` instance for the queue.
|
|
986
1017
|
*/
|
|
987
1018
|
getOrCreate(queueName) {
|
|
988
|
-
const existing = this.
|
|
1019
|
+
const existing = __privateGet(this, _events).get(queueName);
|
|
989
1020
|
if (existing) return existing;
|
|
990
1021
|
const conn = duplicateConnection(this.connection.getClient());
|
|
991
1022
|
let qe;
|
|
@@ -996,8 +1027,8 @@ exports.QueueEventsRegistry = class QueueEventsRegistry {
|
|
|
996
1027
|
throw err;
|
|
997
1028
|
}
|
|
998
1029
|
attachDefaultErrorListener(qe, this.logger, "QueueEvents", queueName);
|
|
999
|
-
this.
|
|
1000
|
-
this.
|
|
1030
|
+
__privateGet(this, _events).set(queueName, qe);
|
|
1031
|
+
__privateGet(this, _connections).set(queueName, conn);
|
|
1001
1032
|
return qe;
|
|
1002
1033
|
}
|
|
1003
1034
|
/**
|
|
@@ -1006,7 +1037,7 @@ exports.QueueEventsRegistry = class QueueEventsRegistry {
|
|
|
1006
1037
|
* @returns An immutable array of queue names.
|
|
1007
1038
|
*/
|
|
1008
1039
|
list() {
|
|
1009
|
-
return Array.from(this.
|
|
1040
|
+
return Array.from(__privateGet(this, _events).keys());
|
|
1010
1041
|
}
|
|
1011
1042
|
/**
|
|
1012
1043
|
* Returns the live `QueueEvents` map. Intended for the shutdown orchestrator.
|
|
@@ -1014,7 +1045,7 @@ exports.QueueEventsRegistry = class QueueEventsRegistry {
|
|
|
1014
1045
|
* @returns A read-only view of the internal map.
|
|
1015
1046
|
*/
|
|
1016
1047
|
getAll() {
|
|
1017
|
-
return this
|
|
1048
|
+
return __privateGet(this, _events);
|
|
1018
1049
|
}
|
|
1019
1050
|
/**
|
|
1020
1051
|
* Returns a read-only view of the duplicated connections the library opened
|
|
@@ -1030,9 +1061,11 @@ exports.QueueEventsRegistry = class QueueEventsRegistry {
|
|
|
1030
1061
|
* @returns A read-only snapshot of queue name to duplicated connection.
|
|
1031
1062
|
*/
|
|
1032
1063
|
getConnections() {
|
|
1033
|
-
return new Map(this
|
|
1064
|
+
return new Map(__privateGet(this, _connections));
|
|
1034
1065
|
}
|
|
1035
1066
|
};
|
|
1067
|
+
_events = new WeakMap();
|
|
1068
|
+
_connections = new WeakMap();
|
|
1036
1069
|
exports.QueueEventsRegistry = __decorateClass([
|
|
1037
1070
|
common.Injectable(),
|
|
1038
1071
|
__decorateParam(0, common.Inject(exports.ConnectionResolver)),
|
|
@@ -1440,6 +1473,8 @@ exports.BymaxQueueModule = class BymaxQueueModule extends ConfigurableModuleClas
|
|
|
1440
1473
|
exports.BymaxQueueModule = __decorateClass([
|
|
1441
1474
|
common.Module({})
|
|
1442
1475
|
], exports.BymaxQueueModule);
|
|
1476
|
+
|
|
1477
|
+
// src/server/decorators/processor.decorator.ts
|
|
1443
1478
|
function Processor(queueName, options) {
|
|
1444
1479
|
return (target) => {
|
|
1445
1480
|
const concurrencyOmitted = options?.concurrency === void 0;
|
|
@@ -1455,6 +1490,8 @@ function Processor(queueName, options) {
|
|
|
1455
1490
|
Reflect.defineMetadata(PROCESSOR_METADATA_KEY, metadata, target);
|
|
1456
1491
|
};
|
|
1457
1492
|
}
|
|
1493
|
+
|
|
1494
|
+
// src/server/decorators/process.decorator.ts
|
|
1458
1495
|
function Process(jobName) {
|
|
1459
1496
|
return (target, propertyKey) => {
|
|
1460
1497
|
const existing = Reflect.getOwnMetadata(PROCESS_HANDLERS_METADATA_KEY, target.constructor) ?? [];
|
|
@@ -1462,6 +1499,8 @@ function Process(jobName) {
|
|
|
1462
1499
|
Reflect.defineMetadata(PROCESS_HANDLERS_METADATA_KEY, [...existing, entry], target.constructor);
|
|
1463
1500
|
};
|
|
1464
1501
|
}
|
|
1502
|
+
|
|
1503
|
+
// src/server/decorators/on-worker-event.decorator.ts
|
|
1465
1504
|
function OnWorkerEvent(eventName) {
|
|
1466
1505
|
return (target, propertyKey) => {
|
|
1467
1506
|
const existing = Reflect.getOwnMetadata(
|
|
@@ -1475,6 +1514,8 @@ function OnWorkerEvent(eventName) {
|
|
|
1475
1514
|
);
|
|
1476
1515
|
};
|
|
1477
1516
|
}
|
|
1517
|
+
|
|
1518
|
+
// src/server/decorators/on-queue-event.decorator.ts
|
|
1478
1519
|
function OnQueueEvent(eventName) {
|
|
1479
1520
|
return (target, propertyKey) => {
|
|
1480
1521
|
const existing = Reflect.getOwnMetadata(
|
package/dist/server/index.d.cts
CHANGED
|
@@ -354,8 +354,7 @@ type JobSchedulerRepeatOptions = {
|
|
|
354
354
|
* for `ready`, closing it on shutdown.
|
|
355
355
|
*/
|
|
356
356
|
declare class ConnectionResolver {
|
|
357
|
-
private
|
|
358
|
-
private client;
|
|
357
|
+
#private;
|
|
359
358
|
private mode;
|
|
360
359
|
constructor(options: BymaxQueueModuleOptions);
|
|
361
360
|
/** Resolve and validate the connection. Call once during module bootstrap. */
|
|
@@ -390,10 +389,10 @@ type CleanableStatus = 'completed' | 'failed' | 'delayed' | 'wait' | 'active' |
|
|
|
390
389
|
* enqueueing, inspection, metrics, and lifecycle control.
|
|
391
390
|
*/
|
|
392
391
|
declare class QueueService {
|
|
392
|
+
#private;
|
|
393
393
|
private readonly connection;
|
|
394
394
|
private readonly options;
|
|
395
395
|
private readonly logger;
|
|
396
|
-
private readonly queues;
|
|
397
396
|
constructor(connection: ConnectionResolver, options: ResolvedQueueOptions);
|
|
398
397
|
/**
|
|
399
398
|
* Return the cached `Queue` for `queueName`, or create one with the module
|
|
@@ -792,10 +791,10 @@ interface SandboxedWorkerConfig {
|
|
|
792
791
|
* })
|
|
793
792
|
*/
|
|
794
793
|
declare class WorkerRegistry {
|
|
794
|
+
#private;
|
|
795
795
|
private readonly connection;
|
|
796
796
|
private readonly options;
|
|
797
797
|
private readonly logger;
|
|
798
|
-
private readonly entries;
|
|
799
798
|
constructor(connection: ConnectionResolver, options: ResolvedQueueOptions);
|
|
800
799
|
/**
|
|
801
800
|
* Register an in-process worker with a handler function. The BullMQ `Worker`
|
|
@@ -937,11 +936,10 @@ declare class WorkerRegistry {
|
|
|
937
936
|
* qe.on('completed', ({ jobId }) => console.log(jobId))
|
|
938
937
|
*/
|
|
939
938
|
declare class QueueEventsRegistry {
|
|
939
|
+
#private;
|
|
940
940
|
private readonly connection;
|
|
941
941
|
private readonly options;
|
|
942
942
|
private readonly logger;
|
|
943
|
-
private readonly events;
|
|
944
|
-
private readonly connections;
|
|
945
943
|
constructor(connection: ConnectionResolver, options: ResolvedQueueOptions);
|
|
946
944
|
/**
|
|
947
945
|
* Returns the cached `QueueEvents` for `queueName`, creating one on first
|
|
@@ -1102,7 +1100,6 @@ declare function Processor(queueName: string, options?: WorkerOptions): ClassDec
|
|
|
1102
1100
|
* class coexist without overwriting each other.
|
|
1103
1101
|
* @layer server/decorators
|
|
1104
1102
|
*/
|
|
1105
|
-
|
|
1106
1103
|
/**
|
|
1107
1104
|
* Marks a method as a job processor. If `jobName` is supplied the method is
|
|
1108
1105
|
* only invoked for jobs whose `name` matches; if omitted the method acts as a
|
|
@@ -1135,7 +1132,6 @@ declare function Process(jobName?: string): MethodDecorator;
|
|
|
1135
1132
|
* and timing fields. Use `'progress'` to observe `job.updateProgress()`.
|
|
1136
1133
|
* @layer server/decorators
|
|
1137
1134
|
*/
|
|
1138
|
-
|
|
1139
1135
|
/**
|
|
1140
1136
|
* Union of BullMQ `Worker` event names surfaced by this library. Handlers
|
|
1141
1137
|
* registered with `@OnWorkerEvent` receive the **full** `Job` instance for
|
|
@@ -1195,7 +1191,6 @@ declare function OnWorkerEvent(eventName: WorkerEventName): MethodDecorator;
|
|
|
1195
1191
|
* To inspect the full `Job`, call `QueueService.getJob(jobId)`.
|
|
1196
1192
|
* @layer server/decorators
|
|
1197
1193
|
*/
|
|
1198
|
-
|
|
1199
1194
|
/**
|
|
1200
1195
|
* Union of BullMQ `QueueEvents` event names surfaced by this library. Listeners
|
|
1201
1196
|
* registered with `@OnQueueEvent` receive a serialized event payload, NOT the
|
package/dist/server/index.d.ts
CHANGED
|
@@ -354,8 +354,7 @@ type JobSchedulerRepeatOptions = {
|
|
|
354
354
|
* for `ready`, closing it on shutdown.
|
|
355
355
|
*/
|
|
356
356
|
declare class ConnectionResolver {
|
|
357
|
-
private
|
|
358
|
-
private client;
|
|
357
|
+
#private;
|
|
359
358
|
private mode;
|
|
360
359
|
constructor(options: BymaxQueueModuleOptions);
|
|
361
360
|
/** Resolve and validate the connection. Call once during module bootstrap. */
|
|
@@ -390,10 +389,10 @@ type CleanableStatus = 'completed' | 'failed' | 'delayed' | 'wait' | 'active' |
|
|
|
390
389
|
* enqueueing, inspection, metrics, and lifecycle control.
|
|
391
390
|
*/
|
|
392
391
|
declare class QueueService {
|
|
392
|
+
#private;
|
|
393
393
|
private readonly connection;
|
|
394
394
|
private readonly options;
|
|
395
395
|
private readonly logger;
|
|
396
|
-
private readonly queues;
|
|
397
396
|
constructor(connection: ConnectionResolver, options: ResolvedQueueOptions);
|
|
398
397
|
/**
|
|
399
398
|
* Return the cached `Queue` for `queueName`, or create one with the module
|
|
@@ -792,10 +791,10 @@ interface SandboxedWorkerConfig {
|
|
|
792
791
|
* })
|
|
793
792
|
*/
|
|
794
793
|
declare class WorkerRegistry {
|
|
794
|
+
#private;
|
|
795
795
|
private readonly connection;
|
|
796
796
|
private readonly options;
|
|
797
797
|
private readonly logger;
|
|
798
|
-
private readonly entries;
|
|
799
798
|
constructor(connection: ConnectionResolver, options: ResolvedQueueOptions);
|
|
800
799
|
/**
|
|
801
800
|
* Register an in-process worker with a handler function. The BullMQ `Worker`
|
|
@@ -937,11 +936,10 @@ declare class WorkerRegistry {
|
|
|
937
936
|
* qe.on('completed', ({ jobId }) => console.log(jobId))
|
|
938
937
|
*/
|
|
939
938
|
declare class QueueEventsRegistry {
|
|
939
|
+
#private;
|
|
940
940
|
private readonly connection;
|
|
941
941
|
private readonly options;
|
|
942
942
|
private readonly logger;
|
|
943
|
-
private readonly events;
|
|
944
|
-
private readonly connections;
|
|
945
943
|
constructor(connection: ConnectionResolver, options: ResolvedQueueOptions);
|
|
946
944
|
/**
|
|
947
945
|
* Returns the cached `QueueEvents` for `queueName`, creating one on first
|
|
@@ -1102,7 +1100,6 @@ declare function Processor(queueName: string, options?: WorkerOptions): ClassDec
|
|
|
1102
1100
|
* class coexist without overwriting each other.
|
|
1103
1101
|
* @layer server/decorators
|
|
1104
1102
|
*/
|
|
1105
|
-
|
|
1106
1103
|
/**
|
|
1107
1104
|
* Marks a method as a job processor. If `jobName` is supplied the method is
|
|
1108
1105
|
* only invoked for jobs whose `name` matches; if omitted the method acts as a
|
|
@@ -1135,7 +1132,6 @@ declare function Process(jobName?: string): MethodDecorator;
|
|
|
1135
1132
|
* and timing fields. Use `'progress'` to observe `job.updateProgress()`.
|
|
1136
1133
|
* @layer server/decorators
|
|
1137
1134
|
*/
|
|
1138
|
-
|
|
1139
1135
|
/**
|
|
1140
1136
|
* Union of BullMQ `Worker` event names surfaced by this library. Handlers
|
|
1141
1137
|
* registered with `@OnWorkerEvent` receive the **full** `Job` instance for
|
|
@@ -1195,7 +1191,6 @@ declare function OnWorkerEvent(eventName: WorkerEventName): MethodDecorator;
|
|
|
1195
1191
|
* To inspect the full `Job`, call `QueueService.getJob(jobId)`.
|
|
1196
1192
|
* @layer server/decorators
|
|
1197
1193
|
*/
|
|
1198
|
-
|
|
1199
1194
|
/**
|
|
1200
1195
|
* Union of BullMQ `QueueEvents` event names surfaced by this library. Listeners
|
|
1201
1196
|
* registered with `@OnQueueEvent` receive a serialized event payload, NOT the
|
package/dist/server/index.mjs
CHANGED
|
@@ -3,9 +3,13 @@ import { DiscoveryService, DiscoveryModule } from '@nestjs/core';
|
|
|
3
3
|
import { Redis } from 'ioredis';
|
|
4
4
|
import { Worker, QueueEvents, Queue, FlowProducer } from 'bullmq';
|
|
5
5
|
import { isAbsolute, extname } from 'path';
|
|
6
|
-
import 'reflect-metadata';
|
|
7
6
|
|
|
7
|
+
var __defProp = Object.defineProperty;
|
|
8
8
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
9
|
+
var __typeError = (msg) => {
|
|
10
|
+
throw TypeError(msg);
|
|
11
|
+
};
|
|
12
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
9
13
|
var __decorateClass = (decorators, target, key, kind) => {
|
|
10
14
|
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
11
15
|
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
@@ -14,6 +18,11 @@ var __decorateClass = (decorators, target, key, kind) => {
|
|
|
14
18
|
return result;
|
|
15
19
|
};
|
|
16
20
|
var __decorateParam = (index, decorator) => (target, key) => decorator(target, key, index);
|
|
21
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
22
|
+
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
|
23
|
+
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
24
|
+
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
25
|
+
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), member.set(obj, value), value);
|
|
17
26
|
|
|
18
27
|
// src/shared/constants/error-codes.ts
|
|
19
28
|
var QUEUE_ERROR_CODES = {
|
|
@@ -110,8 +119,8 @@ var DEFAULT_SHUTDOWN_DRAIN_TIMEOUT_MS = 3e4;
|
|
|
110
119
|
|
|
111
120
|
// src/server/config/resolved-options.ts
|
|
112
121
|
function applyDefaults(opts) {
|
|
122
|
+
const connection = opts.connection;
|
|
113
123
|
const base = {
|
|
114
|
-
connection: opts.connection,
|
|
115
124
|
defaultJobOptions: { ...DEFAULT_JOB_OPTIONS, ...opts.defaultJobOptions ?? {} },
|
|
116
125
|
prefix: opts.prefix ?? "bull",
|
|
117
126
|
queueOptions: opts.queueOptions ?? {},
|
|
@@ -127,6 +136,11 @@ function applyDefaults(opts) {
|
|
|
127
136
|
connectionReadyTimeoutMs: opts.connectionReadyTimeoutMs ?? DEFAULT_CONNECTION_READY_TIMEOUT_MS
|
|
128
137
|
};
|
|
129
138
|
if (opts.telemetry !== void 0) base.telemetry = opts.telemetry;
|
|
139
|
+
Object.defineProperty(base, "connection", {
|
|
140
|
+
get: () => connection,
|
|
141
|
+
enumerable: false,
|
|
142
|
+
configurable: false
|
|
143
|
+
});
|
|
130
144
|
return Object.freeze(base);
|
|
131
145
|
}
|
|
132
146
|
|
|
@@ -156,34 +170,44 @@ function duplicateConnection(client) {
|
|
|
156
170
|
}
|
|
157
171
|
|
|
158
172
|
// src/server/services/connection-resolver.service.ts
|
|
173
|
+
var _client, _options;
|
|
159
174
|
var ConnectionResolver = class {
|
|
160
175
|
constructor(options) {
|
|
161
|
-
|
|
176
|
+
/**
|
|
177
|
+
* The resolved Redis client.
|
|
178
|
+
*
|
|
179
|
+
* An ECMAScript private field rather than a TypeScript `private` one, which
|
|
180
|
+
* is erased at runtime: an ioredis instance carries `options.password` as a
|
|
181
|
+
* plain field, so leaving this enumerable would let anything that serializes
|
|
182
|
+
* a service holding this resolver walk into the credentials.
|
|
183
|
+
*/
|
|
184
|
+
__privateAdd(this, _client);
|
|
185
|
+
/** The consumer's module options, which carry the Redis credentials. */
|
|
186
|
+
__privateAdd(this, _options);
|
|
187
|
+
__publicField(this, "mode");
|
|
188
|
+
__privateSet(this, _options, options);
|
|
162
189
|
}
|
|
163
|
-
options;
|
|
164
|
-
client;
|
|
165
|
-
mode;
|
|
166
190
|
/** Resolve and validate the connection. Call once during module bootstrap. */
|
|
167
191
|
async init() {
|
|
168
|
-
const cfg = this.
|
|
192
|
+
const cfg = __privateGet(this, _options).connection;
|
|
169
193
|
if ("client" in cfg) {
|
|
170
194
|
this.initModeA(cfg.client);
|
|
171
195
|
return;
|
|
172
196
|
}
|
|
173
197
|
this.mode = "mode-b-owned";
|
|
174
|
-
this
|
|
198
|
+
__privateSet(this, _client, "url" in cfg ? new Redis(cfg.url, { ...cfg.options ?? {}, lazyConnect: false }) : new Redis({ ...cfg.options, lazyConnect: false }));
|
|
175
199
|
await this.waitReady(
|
|
176
|
-
this.
|
|
200
|
+
__privateGet(this, _options).connectionReadyTimeoutMs ?? DEFAULT_CONNECTION_READY_TIMEOUT_MS
|
|
177
201
|
);
|
|
178
202
|
}
|
|
179
203
|
/** The resolved Queue-role client. */
|
|
180
204
|
getClient() {
|
|
181
|
-
if (!this
|
|
205
|
+
if (!__privateGet(this, _client)) {
|
|
182
206
|
throw new QueueException(QUEUE_ERROR_CODES.CONNECTION_INVALID, 500, {
|
|
183
207
|
reason: "not initialized"
|
|
184
208
|
});
|
|
185
209
|
}
|
|
186
|
-
return this
|
|
210
|
+
return __privateGet(this, _client);
|
|
187
211
|
}
|
|
188
212
|
/** The resolved connection mode. */
|
|
189
213
|
getMode() {
|
|
@@ -200,8 +224,8 @@ var ConnectionResolver = class {
|
|
|
200
224
|
}
|
|
201
225
|
/** Close the library-owned connection on shutdown; never touch a BYO client. */
|
|
202
226
|
async teardown() {
|
|
203
|
-
if (this.isOwned() && this
|
|
204
|
-
const client = this
|
|
227
|
+
if (this.isOwned() && __privateGet(this, _client)) {
|
|
228
|
+
const client = __privateGet(this, _client);
|
|
205
229
|
await client.quit().catch(() => {
|
|
206
230
|
client.disconnect();
|
|
207
231
|
});
|
|
@@ -210,7 +234,7 @@ var ConnectionResolver = class {
|
|
|
210
234
|
/** Validate and adopt a bring-your-own client for the Queue role. */
|
|
211
235
|
initModeA(client) {
|
|
212
236
|
this.mode = "mode-a-byo";
|
|
213
|
-
this
|
|
237
|
+
__privateSet(this, _client, client);
|
|
214
238
|
if (!isClientUsable(client)) {
|
|
215
239
|
throw new QueueException(QUEUE_ERROR_CODES.CONNECTION_INVALID, 500, { status: client.status });
|
|
216
240
|
}
|
|
@@ -248,6 +272,8 @@ var ConnectionResolver = class {
|
|
|
248
272
|
});
|
|
249
273
|
}
|
|
250
274
|
};
|
|
275
|
+
_client = new WeakMap();
|
|
276
|
+
_options = new WeakMap();
|
|
251
277
|
ConnectionResolver = __decorateClass([
|
|
252
278
|
Injectable(),
|
|
253
279
|
__decorateParam(0, Inject(BYMAX_QUEUE_OPTIONS))
|
|
@@ -296,15 +322,16 @@ var MAX_BULK_SIZE = 1e3;
|
|
|
296
322
|
function isOperationalError(err) {
|
|
297
323
|
return typeof err?.code === "string";
|
|
298
324
|
}
|
|
325
|
+
var _queues;
|
|
299
326
|
var QueueService = class {
|
|
300
327
|
constructor(connection, options) {
|
|
301
328
|
this.connection = connection;
|
|
302
329
|
this.options = options;
|
|
330
|
+
__publicField(this, "logger", new Logger(QueueService.name));
|
|
331
|
+
__privateAdd(this, _queues, /* @__PURE__ */ new Map());
|
|
303
332
|
}
|
|
304
333
|
connection;
|
|
305
334
|
options;
|
|
306
|
-
logger = new Logger(QueueService.name);
|
|
307
|
-
queues = /* @__PURE__ */ new Map();
|
|
308
335
|
/**
|
|
309
336
|
* Return the cached `Queue` for `queueName`, or create one with the module
|
|
310
337
|
* defaults applied. Subsequent calls with the same name return the same
|
|
@@ -314,7 +341,7 @@ var QueueService = class {
|
|
|
314
341
|
* @param overrides - Optional per-queue option overrides.
|
|
315
342
|
*/
|
|
316
343
|
getOrCreateQueue(queueName, overrides) {
|
|
317
|
-
const existing = this.
|
|
344
|
+
const existing = __privateGet(this, _queues).get(queueName);
|
|
318
345
|
if (existing) return existing;
|
|
319
346
|
const queue = new Queue(queueName, {
|
|
320
347
|
connection: this.connection.getClient(),
|
|
@@ -325,7 +352,7 @@ var QueueService = class {
|
|
|
325
352
|
...overrides
|
|
326
353
|
});
|
|
327
354
|
attachDefaultErrorListener(queue, this.logger, "Queue", queueName);
|
|
328
|
-
this.
|
|
355
|
+
__privateGet(this, _queues).set(queueName, queue);
|
|
329
356
|
return queue;
|
|
330
357
|
}
|
|
331
358
|
/** Resolve the raw cached queue (default generics) for internal delegation. */
|
|
@@ -529,7 +556,7 @@ var QueueService = class {
|
|
|
529
556
|
}
|
|
530
557
|
/** Return a read-only view of the cached queues. */
|
|
531
558
|
getCachedQueues() {
|
|
532
|
-
return this
|
|
559
|
+
return __privateGet(this, _queues);
|
|
533
560
|
}
|
|
534
561
|
/**
|
|
535
562
|
* Close every cached queue (swallowing per-queue errors) and clear the cache.
|
|
@@ -541,12 +568,13 @@ var QueueService = class {
|
|
|
541
568
|
* exists to prevent. `QueueLifecycle` is the only caller.
|
|
542
569
|
*/
|
|
543
570
|
async closeAll() {
|
|
544
|
-
for (const queue of this.
|
|
571
|
+
for (const queue of __privateGet(this, _queues).values()) {
|
|
545
572
|
await queue.close().catch(() => void 0);
|
|
546
573
|
}
|
|
547
|
-
this.
|
|
574
|
+
__privateGet(this, _queues).clear();
|
|
548
575
|
}
|
|
549
576
|
};
|
|
577
|
+
_queues = new WeakMap();
|
|
550
578
|
QueueService = __decorateClass([
|
|
551
579
|
Injectable(),
|
|
552
580
|
__decorateParam(0, Inject(ConnectionResolver)),
|
|
@@ -703,15 +731,16 @@ var MetricsService = class {
|
|
|
703
731
|
MetricsService = __decorateClass([
|
|
704
732
|
Injectable()
|
|
705
733
|
], MetricsService);
|
|
734
|
+
var _entries;
|
|
706
735
|
var WorkerRegistry = class {
|
|
707
736
|
constructor(connection, options) {
|
|
708
737
|
this.connection = connection;
|
|
709
738
|
this.options = options;
|
|
739
|
+
__publicField(this, "logger", new Logger(WorkerRegistry.name));
|
|
740
|
+
__privateAdd(this, _entries, /* @__PURE__ */ new Map());
|
|
710
741
|
}
|
|
711
742
|
connection;
|
|
712
743
|
options;
|
|
713
|
-
logger = new Logger(WorkerRegistry.name);
|
|
714
|
-
entries = /* @__PURE__ */ new Map();
|
|
715
744
|
/**
|
|
716
745
|
* Register an in-process worker with a handler function. The BullMQ `Worker`
|
|
717
746
|
* constructor is synchronous — no Redis handshake is performed at registration
|
|
@@ -742,7 +771,7 @@ var WorkerRegistry = class {
|
|
|
742
771
|
});
|
|
743
772
|
}
|
|
744
773
|
attachDefaultErrorListener(worker, this.logger, "Worker", queueName);
|
|
745
|
-
this.
|
|
774
|
+
__privateGet(this, _entries).set(queueName, { worker, connection: conn });
|
|
746
775
|
return worker;
|
|
747
776
|
}
|
|
748
777
|
/**
|
|
@@ -781,7 +810,7 @@ var WorkerRegistry = class {
|
|
|
781
810
|
});
|
|
782
811
|
}
|
|
783
812
|
attachDefaultErrorListener(worker, this.logger, "Worker", queueName);
|
|
784
|
-
this.
|
|
813
|
+
__privateGet(this, _entries).set(queueName, { worker, connection: conn });
|
|
785
814
|
return worker;
|
|
786
815
|
}
|
|
787
816
|
/**
|
|
@@ -792,11 +821,11 @@ var WorkerRegistry = class {
|
|
|
792
821
|
* @param queueName - The queue whose worker should be removed.
|
|
793
822
|
*/
|
|
794
823
|
async unregister(queueName) {
|
|
795
|
-
const entry = this.
|
|
824
|
+
const entry = __privateGet(this, _entries).get(queueName);
|
|
796
825
|
if (!entry) return;
|
|
797
826
|
await entry.worker.close();
|
|
798
827
|
await this.closeConnection(entry.connection);
|
|
799
|
-
this.
|
|
828
|
+
__privateGet(this, _entries).delete(queueName);
|
|
800
829
|
}
|
|
801
830
|
/**
|
|
802
831
|
* Returns the queue names of all currently registered workers.
|
|
@@ -804,7 +833,7 @@ var WorkerRegistry = class {
|
|
|
804
833
|
* @returns An immutable array of queue names.
|
|
805
834
|
*/
|
|
806
835
|
list() {
|
|
807
|
-
return Array.from(this.
|
|
836
|
+
return Array.from(__privateGet(this, _entries).keys());
|
|
808
837
|
}
|
|
809
838
|
/**
|
|
810
839
|
* Returns a read-only view of the registered workers keyed by queue name.
|
|
@@ -814,7 +843,7 @@ var WorkerRegistry = class {
|
|
|
814
843
|
*/
|
|
815
844
|
getAll() {
|
|
816
845
|
return new Map(
|
|
817
|
-
Array.from(this
|
|
846
|
+
Array.from(__privateGet(this, _entries), ([name, entry]) => [name, entry.worker])
|
|
818
847
|
);
|
|
819
848
|
}
|
|
820
849
|
/**
|
|
@@ -827,7 +856,7 @@ var WorkerRegistry = class {
|
|
|
827
856
|
*/
|
|
828
857
|
getConnections() {
|
|
829
858
|
return new Map(
|
|
830
|
-
Array.from(this
|
|
859
|
+
Array.from(__privateGet(this, _entries), ([name, entry]) => [name, entry.connection])
|
|
831
860
|
);
|
|
832
861
|
}
|
|
833
862
|
/**
|
|
@@ -850,7 +879,7 @@ var WorkerRegistry = class {
|
|
|
850
879
|
* @throws {QueueException} `DUPLICATE_PROCESSOR` if the queue is already registered.
|
|
851
880
|
*/
|
|
852
881
|
guardDuplicate(queueName) {
|
|
853
|
-
if (this.
|
|
882
|
+
if (__privateGet(this, _entries).has(queueName)) {
|
|
854
883
|
throw new QueueException(QUEUE_ERROR_CODES.DUPLICATE_PROCESSOR, 500, { queueName });
|
|
855
884
|
}
|
|
856
885
|
}
|
|
@@ -959,21 +988,23 @@ var WorkerRegistry = class {
|
|
|
959
988
|
return result;
|
|
960
989
|
}
|
|
961
990
|
};
|
|
991
|
+
_entries = new WeakMap();
|
|
962
992
|
WorkerRegistry = __decorateClass([
|
|
963
993
|
Injectable(),
|
|
964
994
|
__decorateParam(0, Inject(ConnectionResolver)),
|
|
965
995
|
__decorateParam(1, Inject(BYMAX_QUEUE_RESOLVED_OPTIONS))
|
|
966
996
|
], WorkerRegistry);
|
|
997
|
+
var _events, _connections;
|
|
967
998
|
var QueueEventsRegistry = class {
|
|
968
999
|
constructor(connection, options) {
|
|
969
1000
|
this.connection = connection;
|
|
970
1001
|
this.options = options;
|
|
1002
|
+
__publicField(this, "logger", new Logger(QueueEventsRegistry.name));
|
|
1003
|
+
__privateAdd(this, _events, /* @__PURE__ */ new Map());
|
|
1004
|
+
__privateAdd(this, _connections, /* @__PURE__ */ new Map());
|
|
971
1005
|
}
|
|
972
1006
|
connection;
|
|
973
1007
|
options;
|
|
974
|
-
logger = new Logger(QueueEventsRegistry.name);
|
|
975
|
-
events = /* @__PURE__ */ new Map();
|
|
976
|
-
connections = /* @__PURE__ */ new Map();
|
|
977
1008
|
/**
|
|
978
1009
|
* Returns the cached `QueueEvents` for `queueName`, creating one on first
|
|
979
1010
|
* call. Subsequent calls for the same name return the same instance without
|
|
@@ -983,7 +1014,7 @@ var QueueEventsRegistry = class {
|
|
|
983
1014
|
* @returns The `QueueEvents` instance for the queue.
|
|
984
1015
|
*/
|
|
985
1016
|
getOrCreate(queueName) {
|
|
986
|
-
const existing = this.
|
|
1017
|
+
const existing = __privateGet(this, _events).get(queueName);
|
|
987
1018
|
if (existing) return existing;
|
|
988
1019
|
const conn = duplicateConnection(this.connection.getClient());
|
|
989
1020
|
let qe;
|
|
@@ -994,8 +1025,8 @@ var QueueEventsRegistry = class {
|
|
|
994
1025
|
throw err;
|
|
995
1026
|
}
|
|
996
1027
|
attachDefaultErrorListener(qe, this.logger, "QueueEvents", queueName);
|
|
997
|
-
this.
|
|
998
|
-
this.
|
|
1028
|
+
__privateGet(this, _events).set(queueName, qe);
|
|
1029
|
+
__privateGet(this, _connections).set(queueName, conn);
|
|
999
1030
|
return qe;
|
|
1000
1031
|
}
|
|
1001
1032
|
/**
|
|
@@ -1004,7 +1035,7 @@ var QueueEventsRegistry = class {
|
|
|
1004
1035
|
* @returns An immutable array of queue names.
|
|
1005
1036
|
*/
|
|
1006
1037
|
list() {
|
|
1007
|
-
return Array.from(this.
|
|
1038
|
+
return Array.from(__privateGet(this, _events).keys());
|
|
1008
1039
|
}
|
|
1009
1040
|
/**
|
|
1010
1041
|
* Returns the live `QueueEvents` map. Intended for the shutdown orchestrator.
|
|
@@ -1012,7 +1043,7 @@ var QueueEventsRegistry = class {
|
|
|
1012
1043
|
* @returns A read-only view of the internal map.
|
|
1013
1044
|
*/
|
|
1014
1045
|
getAll() {
|
|
1015
|
-
return this
|
|
1046
|
+
return __privateGet(this, _events);
|
|
1016
1047
|
}
|
|
1017
1048
|
/**
|
|
1018
1049
|
* Returns a read-only view of the duplicated connections the library opened
|
|
@@ -1028,9 +1059,11 @@ var QueueEventsRegistry = class {
|
|
|
1028
1059
|
* @returns A read-only snapshot of queue name to duplicated connection.
|
|
1029
1060
|
*/
|
|
1030
1061
|
getConnections() {
|
|
1031
|
-
return new Map(this
|
|
1062
|
+
return new Map(__privateGet(this, _connections));
|
|
1032
1063
|
}
|
|
1033
1064
|
};
|
|
1065
|
+
_events = new WeakMap();
|
|
1066
|
+
_connections = new WeakMap();
|
|
1034
1067
|
QueueEventsRegistry = __decorateClass([
|
|
1035
1068
|
Injectable(),
|
|
1036
1069
|
__decorateParam(0, Inject(ConnectionResolver)),
|
|
@@ -1438,6 +1471,8 @@ var BymaxQueueModule = class extends ConfigurableModuleClass {
|
|
|
1438
1471
|
BymaxQueueModule = __decorateClass([
|
|
1439
1472
|
Module({})
|
|
1440
1473
|
], BymaxQueueModule);
|
|
1474
|
+
|
|
1475
|
+
// src/server/decorators/processor.decorator.ts
|
|
1441
1476
|
function Processor(queueName, options) {
|
|
1442
1477
|
return (target) => {
|
|
1443
1478
|
const concurrencyOmitted = options?.concurrency === void 0;
|
|
@@ -1453,6 +1488,8 @@ function Processor(queueName, options) {
|
|
|
1453
1488
|
Reflect.defineMetadata(PROCESSOR_METADATA_KEY, metadata, target);
|
|
1454
1489
|
};
|
|
1455
1490
|
}
|
|
1491
|
+
|
|
1492
|
+
// src/server/decorators/process.decorator.ts
|
|
1456
1493
|
function Process(jobName) {
|
|
1457
1494
|
return (target, propertyKey) => {
|
|
1458
1495
|
const existing = Reflect.getOwnMetadata(PROCESS_HANDLERS_METADATA_KEY, target.constructor) ?? [];
|
|
@@ -1460,6 +1497,8 @@ function Process(jobName) {
|
|
|
1460
1497
|
Reflect.defineMetadata(PROCESS_HANDLERS_METADATA_KEY, [...existing, entry], target.constructor);
|
|
1461
1498
|
};
|
|
1462
1499
|
}
|
|
1500
|
+
|
|
1501
|
+
// src/server/decorators/on-worker-event.decorator.ts
|
|
1463
1502
|
function OnWorkerEvent(eventName) {
|
|
1464
1503
|
return (target, propertyKey) => {
|
|
1465
1504
|
const existing = Reflect.getOwnMetadata(
|
|
@@ -1473,6 +1512,8 @@ function OnWorkerEvent(eventName) {
|
|
|
1473
1512
|
);
|
|
1474
1513
|
};
|
|
1475
1514
|
}
|
|
1515
|
+
|
|
1516
|
+
// src/server/decorators/on-queue-event.decorator.ts
|
|
1476
1517
|
function OnQueueEvent(eventName) {
|
|
1477
1518
|
return (target, propertyKey) => {
|
|
1478
1519
|
const existing = Reflect.getOwnMetadata(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bymax-one/nest-queue",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.8",
|
|
4
4
|
"description": "NestJS dynamic module wrapping BullMQ — typed jobs, flows, job schedulers, deduplication, OpenTelemetry, graceful shutdown",
|
|
5
5
|
"author": "Bymax One <support@bymax.one>",
|
|
6
6
|
"license": "MIT",
|