@bymax-one/nest-queue 1.0.7 → 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 CHANGED
@@ -7,6 +7,26 @@ 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
+
10
30
  ## [1.0.7] — 2026-08-04
11
31
 
12
32
  **Runtime change.** `dist/` differs from `1.0.6`: the four decorators no longer carry
@@ -369,6 +389,7 @@ v6 peer range.
369
389
 
370
390
  ---
371
391
 
392
+ [1.0.8]: https://github.com/bymaxone/nest-queue/compare/v1.0.7...v1.0.8
372
393
  [1.0.7]: https://github.com/bymaxone/nest-queue/compare/v1.0.6...v1.0.7
373
394
  [1.0.6]: https://github.com/bymaxone/nest-queue/releases/tag/v1.0.6
374
395
  [1.0.5]: https://github.com/bymaxone/nest-queue/releases/tag/v1.0.5
package/README.md CHANGED
@@ -711,6 +711,14 @@ message, or an exception payload. `QueueException.details` carries only scalar
711
711
  configuration values (`{ actualValue, expectedValue, limit, received }`), never a
712
712
  connection object and never `job.data`.
713
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
+
714
722
  ### Job data is opaque
715
723
 
716
724
  The library treats `job.data` as a payload to transport, never to inspect. It is never
@@ -755,16 +763,16 @@ key will do it twice at some point.
755
763
 
756
764
  ## 🛡️ Security Table
757
765
 
758
- | Layer | Implementation |
759
- | ----------------- | ---------------------------------------------------------------------------------------------------------- |
760
- | Credentials | Injected options only; never read from `process.env`, never logged, never placed in an exception |
761
- | Error payloads | `QueueException.details` restricted to scalar configuration values — no `job.data`, no connection object |
762
- | Job payloads | Treated as opaque; never deep-merged (prototype-pollution guard), never logged |
763
- | Namespacing | `prefix` applied uniformly to `Queue`, `Worker`, `QueueEvents` and `FlowProducer` |
764
- | Connection policy | `maxRetriesPerRequest: null` confined to duplicated consumer connections; producers keep fail-fast retries |
765
- | Delivery | At-least-once with a bounded drain; unacknowledged work is re-queued by BullMQ's stalled check |
766
- | Supply chain | `dependencies: {}`; SHA-pinned Actions, OSV-Scanner, TruffleHog, OpenSSF Scorecard; npm publish over OIDC |
767
- | 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 |
768
776
 
769
777
  > [!IMPORTANT]
770
778
  > **Delivery is at-least-once, and `prefix` is not an access boundary.** Handlers must be
@@ -6,7 +6,12 @@ var ioredis = require('ioredis');
6
6
  var bullmq = require('bullmq');
7
7
  var path = require('path');
8
8
 
9
+ var __defProp = Object.defineProperty;
9
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;
10
15
  var __decorateClass = (decorators, target, key, kind) => {
11
16
  var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
12
17
  for (var i = decorators.length - 1, decorator; i >= 0; i--)
@@ -15,6 +20,11 @@ var __decorateClass = (decorators, target, key, kind) => {
15
20
  return result;
16
21
  };
17
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);
18
28
 
19
29
  // src/shared/constants/error-codes.ts
20
30
  var QUEUE_ERROR_CODES = {
@@ -111,8 +121,8 @@ var DEFAULT_SHUTDOWN_DRAIN_TIMEOUT_MS = 3e4;
111
121
 
112
122
  // src/server/config/resolved-options.ts
113
123
  function applyDefaults(opts) {
124
+ const connection = opts.connection;
114
125
  const base = {
115
- connection: opts.connection,
116
126
  defaultJobOptions: { ...DEFAULT_JOB_OPTIONS, ...opts.defaultJobOptions ?? {} },
117
127
  prefix: opts.prefix ?? "bull",
118
128
  queueOptions: opts.queueOptions ?? {},
@@ -128,6 +138,11 @@ function applyDefaults(opts) {
128
138
  connectionReadyTimeoutMs: opts.connectionReadyTimeoutMs ?? DEFAULT_CONNECTION_READY_TIMEOUT_MS
129
139
  };
130
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
+ });
131
146
  return Object.freeze(base);
132
147
  }
133
148
 
@@ -157,34 +172,44 @@ function duplicateConnection(client) {
157
172
  }
158
173
 
159
174
  // src/server/services/connection-resolver.service.ts
175
+ var _client, _options;
160
176
  exports.ConnectionResolver = class ConnectionResolver {
161
177
  constructor(options) {
162
- this.options = options;
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);
163
191
  }
164
- options;
165
- client;
166
- mode;
167
192
  /** Resolve and validate the connection. Call once during module bootstrap. */
168
193
  async init() {
169
- const cfg = this.options.connection;
194
+ const cfg = __privateGet(this, _options).connection;
170
195
  if ("client" in cfg) {
171
196
  this.initModeA(cfg.client);
172
197
  return;
173
198
  }
174
199
  this.mode = "mode-b-owned";
175
- this.client = "url" in cfg ? new ioredis.Redis(cfg.url, { ...cfg.options ?? {}, lazyConnect: false }) : new ioredis.Redis({ ...cfg.options, lazyConnect: false });
200
+ __privateSet(this, _client, "url" in cfg ? new ioredis.Redis(cfg.url, { ...cfg.options ?? {}, lazyConnect: false }) : new ioredis.Redis({ ...cfg.options, lazyConnect: false }));
176
201
  await this.waitReady(
177
- this.options.connectionReadyTimeoutMs ?? DEFAULT_CONNECTION_READY_TIMEOUT_MS
202
+ __privateGet(this, _options).connectionReadyTimeoutMs ?? DEFAULT_CONNECTION_READY_TIMEOUT_MS
178
203
  );
179
204
  }
180
205
  /** The resolved Queue-role client. */
181
206
  getClient() {
182
- if (!this.client) {
207
+ if (!__privateGet(this, _client)) {
183
208
  throw new QueueException(QUEUE_ERROR_CODES.CONNECTION_INVALID, 500, {
184
209
  reason: "not initialized"
185
210
  });
186
211
  }
187
- return this.client;
212
+ return __privateGet(this, _client);
188
213
  }
189
214
  /** The resolved connection mode. */
190
215
  getMode() {
@@ -201,8 +226,8 @@ exports.ConnectionResolver = class ConnectionResolver {
201
226
  }
202
227
  /** Close the library-owned connection on shutdown; never touch a BYO client. */
203
228
  async teardown() {
204
- if (this.isOwned() && this.client) {
205
- const client = this.client;
229
+ if (this.isOwned() && __privateGet(this, _client)) {
230
+ const client = __privateGet(this, _client);
206
231
  await client.quit().catch(() => {
207
232
  client.disconnect();
208
233
  });
@@ -211,7 +236,7 @@ exports.ConnectionResolver = class ConnectionResolver {
211
236
  /** Validate and adopt a bring-your-own client for the Queue role. */
212
237
  initModeA(client) {
213
238
  this.mode = "mode-a-byo";
214
- this.client = client;
239
+ __privateSet(this, _client, client);
215
240
  if (!isClientUsable(client)) {
216
241
  throw new QueueException(QUEUE_ERROR_CODES.CONNECTION_INVALID, 500, { status: client.status });
217
242
  }
@@ -249,6 +274,8 @@ exports.ConnectionResolver = class ConnectionResolver {
249
274
  });
250
275
  }
251
276
  };
277
+ _client = new WeakMap();
278
+ _options = new WeakMap();
252
279
  exports.ConnectionResolver = __decorateClass([
253
280
  common.Injectable(),
254
281
  __decorateParam(0, common.Inject(BYMAX_QUEUE_OPTIONS))
@@ -297,15 +324,16 @@ var MAX_BULK_SIZE = 1e3;
297
324
  function isOperationalError(err) {
298
325
  return typeof err?.code === "string";
299
326
  }
327
+ var _queues;
300
328
  exports.QueueService = class QueueService {
301
329
  constructor(connection, options) {
302
330
  this.connection = connection;
303
331
  this.options = options;
332
+ __publicField(this, "logger", new common.Logger(exports.QueueService.name));
333
+ __privateAdd(this, _queues, /* @__PURE__ */ new Map());
304
334
  }
305
335
  connection;
306
336
  options;
307
- logger = new common.Logger(exports.QueueService.name);
308
- queues = /* @__PURE__ */ new Map();
309
337
  /**
310
338
  * Return the cached `Queue` for `queueName`, or create one with the module
311
339
  * defaults applied. Subsequent calls with the same name return the same
@@ -315,7 +343,7 @@ exports.QueueService = class QueueService {
315
343
  * @param overrides - Optional per-queue option overrides.
316
344
  */
317
345
  getOrCreateQueue(queueName, overrides) {
318
- const existing = this.queues.get(queueName);
346
+ const existing = __privateGet(this, _queues).get(queueName);
319
347
  if (existing) return existing;
320
348
  const queue = new bullmq.Queue(queueName, {
321
349
  connection: this.connection.getClient(),
@@ -326,7 +354,7 @@ exports.QueueService = class QueueService {
326
354
  ...overrides
327
355
  });
328
356
  attachDefaultErrorListener(queue, this.logger, "Queue", queueName);
329
- this.queues.set(queueName, queue);
357
+ __privateGet(this, _queues).set(queueName, queue);
330
358
  return queue;
331
359
  }
332
360
  /** Resolve the raw cached queue (default generics) for internal delegation. */
@@ -530,7 +558,7 @@ exports.QueueService = class QueueService {
530
558
  }
531
559
  /** Return a read-only view of the cached queues. */
532
560
  getCachedQueues() {
533
- return this.queues;
561
+ return __privateGet(this, _queues);
534
562
  }
535
563
  /**
536
564
  * Close every cached queue (swallowing per-queue errors) and clear the cache.
@@ -542,12 +570,13 @@ exports.QueueService = class QueueService {
542
570
  * exists to prevent. `QueueLifecycle` is the only caller.
543
571
  */
544
572
  async closeAll() {
545
- for (const queue of this.queues.values()) {
573
+ for (const queue of __privateGet(this, _queues).values()) {
546
574
  await queue.close().catch(() => void 0);
547
575
  }
548
- this.queues.clear();
576
+ __privateGet(this, _queues).clear();
549
577
  }
550
578
  };
579
+ _queues = new WeakMap();
551
580
  exports.QueueService = __decorateClass([
552
581
  common.Injectable(),
553
582
  __decorateParam(0, common.Inject(exports.ConnectionResolver)),
@@ -704,15 +733,16 @@ exports.MetricsService = class MetricsService {
704
733
  exports.MetricsService = __decorateClass([
705
734
  common.Injectable()
706
735
  ], exports.MetricsService);
736
+ var _entries;
707
737
  exports.WorkerRegistry = class WorkerRegistry {
708
738
  constructor(connection, options) {
709
739
  this.connection = connection;
710
740
  this.options = options;
741
+ __publicField(this, "logger", new common.Logger(exports.WorkerRegistry.name));
742
+ __privateAdd(this, _entries, /* @__PURE__ */ new Map());
711
743
  }
712
744
  connection;
713
745
  options;
714
- logger = new common.Logger(exports.WorkerRegistry.name);
715
- entries = /* @__PURE__ */ new Map();
716
746
  /**
717
747
  * Register an in-process worker with a handler function. The BullMQ `Worker`
718
748
  * constructor is synchronous — no Redis handshake is performed at registration
@@ -743,7 +773,7 @@ exports.WorkerRegistry = class WorkerRegistry {
743
773
  });
744
774
  }
745
775
  attachDefaultErrorListener(worker, this.logger, "Worker", queueName);
746
- this.entries.set(queueName, { worker, connection: conn });
776
+ __privateGet(this, _entries).set(queueName, { worker, connection: conn });
747
777
  return worker;
748
778
  }
749
779
  /**
@@ -782,7 +812,7 @@ exports.WorkerRegistry = class WorkerRegistry {
782
812
  });
783
813
  }
784
814
  attachDefaultErrorListener(worker, this.logger, "Worker", queueName);
785
- this.entries.set(queueName, { worker, connection: conn });
815
+ __privateGet(this, _entries).set(queueName, { worker, connection: conn });
786
816
  return worker;
787
817
  }
788
818
  /**
@@ -793,11 +823,11 @@ exports.WorkerRegistry = class WorkerRegistry {
793
823
  * @param queueName - The queue whose worker should be removed.
794
824
  */
795
825
  async unregister(queueName) {
796
- const entry = this.entries.get(queueName);
826
+ const entry = __privateGet(this, _entries).get(queueName);
797
827
  if (!entry) return;
798
828
  await entry.worker.close();
799
829
  await this.closeConnection(entry.connection);
800
- this.entries.delete(queueName);
830
+ __privateGet(this, _entries).delete(queueName);
801
831
  }
802
832
  /**
803
833
  * Returns the queue names of all currently registered workers.
@@ -805,7 +835,7 @@ exports.WorkerRegistry = class WorkerRegistry {
805
835
  * @returns An immutable array of queue names.
806
836
  */
807
837
  list() {
808
- return Array.from(this.entries.keys());
838
+ return Array.from(__privateGet(this, _entries).keys());
809
839
  }
810
840
  /**
811
841
  * Returns a read-only view of the registered workers keyed by queue name.
@@ -815,7 +845,7 @@ exports.WorkerRegistry = class WorkerRegistry {
815
845
  */
816
846
  getAll() {
817
847
  return new Map(
818
- Array.from(this.entries, ([name, entry]) => [name, entry.worker])
848
+ Array.from(__privateGet(this, _entries), ([name, entry]) => [name, entry.worker])
819
849
  );
820
850
  }
821
851
  /**
@@ -828,7 +858,7 @@ exports.WorkerRegistry = class WorkerRegistry {
828
858
  */
829
859
  getConnections() {
830
860
  return new Map(
831
- Array.from(this.entries, ([name, entry]) => [name, entry.connection])
861
+ Array.from(__privateGet(this, _entries), ([name, entry]) => [name, entry.connection])
832
862
  );
833
863
  }
834
864
  /**
@@ -851,7 +881,7 @@ exports.WorkerRegistry = class WorkerRegistry {
851
881
  * @throws {QueueException} `DUPLICATE_PROCESSOR` if the queue is already registered.
852
882
  */
853
883
  guardDuplicate(queueName) {
854
- if (this.entries.has(queueName)) {
884
+ if (__privateGet(this, _entries).has(queueName)) {
855
885
  throw new QueueException(QUEUE_ERROR_CODES.DUPLICATE_PROCESSOR, 500, { queueName });
856
886
  }
857
887
  }
@@ -960,21 +990,23 @@ exports.WorkerRegistry = class WorkerRegistry {
960
990
  return result;
961
991
  }
962
992
  };
993
+ _entries = new WeakMap();
963
994
  exports.WorkerRegistry = __decorateClass([
964
995
  common.Injectable(),
965
996
  __decorateParam(0, common.Inject(exports.ConnectionResolver)),
966
997
  __decorateParam(1, common.Inject(BYMAX_QUEUE_RESOLVED_OPTIONS))
967
998
  ], exports.WorkerRegistry);
999
+ var _events, _connections;
968
1000
  exports.QueueEventsRegistry = class QueueEventsRegistry {
969
1001
  constructor(connection, options) {
970
1002
  this.connection = connection;
971
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());
972
1007
  }
973
1008
  connection;
974
1009
  options;
975
- logger = new common.Logger(exports.QueueEventsRegistry.name);
976
- events = /* @__PURE__ */ new Map();
977
- connections = /* @__PURE__ */ new Map();
978
1010
  /**
979
1011
  * Returns the cached `QueueEvents` for `queueName`, creating one on first
980
1012
  * call. Subsequent calls for the same name return the same instance without
@@ -984,7 +1016,7 @@ exports.QueueEventsRegistry = class QueueEventsRegistry {
984
1016
  * @returns The `QueueEvents` instance for the queue.
985
1017
  */
986
1018
  getOrCreate(queueName) {
987
- const existing = this.events.get(queueName);
1019
+ const existing = __privateGet(this, _events).get(queueName);
988
1020
  if (existing) return existing;
989
1021
  const conn = duplicateConnection(this.connection.getClient());
990
1022
  let qe;
@@ -995,8 +1027,8 @@ exports.QueueEventsRegistry = class QueueEventsRegistry {
995
1027
  throw err;
996
1028
  }
997
1029
  attachDefaultErrorListener(qe, this.logger, "QueueEvents", queueName);
998
- this.events.set(queueName, qe);
999
- this.connections.set(queueName, conn);
1030
+ __privateGet(this, _events).set(queueName, qe);
1031
+ __privateGet(this, _connections).set(queueName, conn);
1000
1032
  return qe;
1001
1033
  }
1002
1034
  /**
@@ -1005,7 +1037,7 @@ exports.QueueEventsRegistry = class QueueEventsRegistry {
1005
1037
  * @returns An immutable array of queue names.
1006
1038
  */
1007
1039
  list() {
1008
- return Array.from(this.events.keys());
1040
+ return Array.from(__privateGet(this, _events).keys());
1009
1041
  }
1010
1042
  /**
1011
1043
  * Returns the live `QueueEvents` map. Intended for the shutdown orchestrator.
@@ -1013,7 +1045,7 @@ exports.QueueEventsRegistry = class QueueEventsRegistry {
1013
1045
  * @returns A read-only view of the internal map.
1014
1046
  */
1015
1047
  getAll() {
1016
- return this.events;
1048
+ return __privateGet(this, _events);
1017
1049
  }
1018
1050
  /**
1019
1051
  * Returns a read-only view of the duplicated connections the library opened
@@ -1029,9 +1061,11 @@ exports.QueueEventsRegistry = class QueueEventsRegistry {
1029
1061
  * @returns A read-only snapshot of queue name to duplicated connection.
1030
1062
  */
1031
1063
  getConnections() {
1032
- return new Map(this.connections);
1064
+ return new Map(__privateGet(this, _connections));
1033
1065
  }
1034
1066
  };
1067
+ _events = new WeakMap();
1068
+ _connections = new WeakMap();
1035
1069
  exports.QueueEventsRegistry = __decorateClass([
1036
1070
  common.Injectable(),
1037
1071
  __decorateParam(0, common.Inject(exports.ConnectionResolver)),
@@ -354,8 +354,7 @@ type JobSchedulerRepeatOptions = {
354
354
  * for `ready`, closing it on shutdown.
355
355
  */
356
356
  declare class ConnectionResolver {
357
- private readonly options;
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
@@ -354,8 +354,7 @@ type JobSchedulerRepeatOptions = {
354
354
  * for `ready`, closing it on shutdown.
355
355
  */
356
356
  declare class ConnectionResolver {
357
- private readonly options;
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
@@ -4,7 +4,12 @@ import { Redis } from 'ioredis';
4
4
  import { Worker, QueueEvents, Queue, FlowProducer } from 'bullmq';
5
5
  import { isAbsolute, extname } from 'path';
6
6
 
7
+ var __defProp = Object.defineProperty;
7
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;
8
13
  var __decorateClass = (decorators, target, key, kind) => {
9
14
  var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
10
15
  for (var i = decorators.length - 1, decorator; i >= 0; i--)
@@ -13,6 +18,11 @@ var __decorateClass = (decorators, target, key, kind) => {
13
18
  return result;
14
19
  };
15
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);
16
26
 
17
27
  // src/shared/constants/error-codes.ts
18
28
  var QUEUE_ERROR_CODES = {
@@ -109,8 +119,8 @@ var DEFAULT_SHUTDOWN_DRAIN_TIMEOUT_MS = 3e4;
109
119
 
110
120
  // src/server/config/resolved-options.ts
111
121
  function applyDefaults(opts) {
122
+ const connection = opts.connection;
112
123
  const base = {
113
- connection: opts.connection,
114
124
  defaultJobOptions: { ...DEFAULT_JOB_OPTIONS, ...opts.defaultJobOptions ?? {} },
115
125
  prefix: opts.prefix ?? "bull",
116
126
  queueOptions: opts.queueOptions ?? {},
@@ -126,6 +136,11 @@ function applyDefaults(opts) {
126
136
  connectionReadyTimeoutMs: opts.connectionReadyTimeoutMs ?? DEFAULT_CONNECTION_READY_TIMEOUT_MS
127
137
  };
128
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
+ });
129
144
  return Object.freeze(base);
130
145
  }
131
146
 
@@ -155,34 +170,44 @@ function duplicateConnection(client) {
155
170
  }
156
171
 
157
172
  // src/server/services/connection-resolver.service.ts
173
+ var _client, _options;
158
174
  var ConnectionResolver = class {
159
175
  constructor(options) {
160
- this.options = options;
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);
161
189
  }
162
- options;
163
- client;
164
- mode;
165
190
  /** Resolve and validate the connection. Call once during module bootstrap. */
166
191
  async init() {
167
- const cfg = this.options.connection;
192
+ const cfg = __privateGet(this, _options).connection;
168
193
  if ("client" in cfg) {
169
194
  this.initModeA(cfg.client);
170
195
  return;
171
196
  }
172
197
  this.mode = "mode-b-owned";
173
- this.client = "url" in cfg ? new Redis(cfg.url, { ...cfg.options ?? {}, lazyConnect: false }) : new Redis({ ...cfg.options, lazyConnect: false });
198
+ __privateSet(this, _client, "url" in cfg ? new Redis(cfg.url, { ...cfg.options ?? {}, lazyConnect: false }) : new Redis({ ...cfg.options, lazyConnect: false }));
174
199
  await this.waitReady(
175
- this.options.connectionReadyTimeoutMs ?? DEFAULT_CONNECTION_READY_TIMEOUT_MS
200
+ __privateGet(this, _options).connectionReadyTimeoutMs ?? DEFAULT_CONNECTION_READY_TIMEOUT_MS
176
201
  );
177
202
  }
178
203
  /** The resolved Queue-role client. */
179
204
  getClient() {
180
- if (!this.client) {
205
+ if (!__privateGet(this, _client)) {
181
206
  throw new QueueException(QUEUE_ERROR_CODES.CONNECTION_INVALID, 500, {
182
207
  reason: "not initialized"
183
208
  });
184
209
  }
185
- return this.client;
210
+ return __privateGet(this, _client);
186
211
  }
187
212
  /** The resolved connection mode. */
188
213
  getMode() {
@@ -199,8 +224,8 @@ var ConnectionResolver = class {
199
224
  }
200
225
  /** Close the library-owned connection on shutdown; never touch a BYO client. */
201
226
  async teardown() {
202
- if (this.isOwned() && this.client) {
203
- const client = this.client;
227
+ if (this.isOwned() && __privateGet(this, _client)) {
228
+ const client = __privateGet(this, _client);
204
229
  await client.quit().catch(() => {
205
230
  client.disconnect();
206
231
  });
@@ -209,7 +234,7 @@ var ConnectionResolver = class {
209
234
  /** Validate and adopt a bring-your-own client for the Queue role. */
210
235
  initModeA(client) {
211
236
  this.mode = "mode-a-byo";
212
- this.client = client;
237
+ __privateSet(this, _client, client);
213
238
  if (!isClientUsable(client)) {
214
239
  throw new QueueException(QUEUE_ERROR_CODES.CONNECTION_INVALID, 500, { status: client.status });
215
240
  }
@@ -247,6 +272,8 @@ var ConnectionResolver = class {
247
272
  });
248
273
  }
249
274
  };
275
+ _client = new WeakMap();
276
+ _options = new WeakMap();
250
277
  ConnectionResolver = __decorateClass([
251
278
  Injectable(),
252
279
  __decorateParam(0, Inject(BYMAX_QUEUE_OPTIONS))
@@ -295,15 +322,16 @@ var MAX_BULK_SIZE = 1e3;
295
322
  function isOperationalError(err) {
296
323
  return typeof err?.code === "string";
297
324
  }
325
+ var _queues;
298
326
  var QueueService = class {
299
327
  constructor(connection, options) {
300
328
  this.connection = connection;
301
329
  this.options = options;
330
+ __publicField(this, "logger", new Logger(QueueService.name));
331
+ __privateAdd(this, _queues, /* @__PURE__ */ new Map());
302
332
  }
303
333
  connection;
304
334
  options;
305
- logger = new Logger(QueueService.name);
306
- queues = /* @__PURE__ */ new Map();
307
335
  /**
308
336
  * Return the cached `Queue` for `queueName`, or create one with the module
309
337
  * defaults applied. Subsequent calls with the same name return the same
@@ -313,7 +341,7 @@ var QueueService = class {
313
341
  * @param overrides - Optional per-queue option overrides.
314
342
  */
315
343
  getOrCreateQueue(queueName, overrides) {
316
- const existing = this.queues.get(queueName);
344
+ const existing = __privateGet(this, _queues).get(queueName);
317
345
  if (existing) return existing;
318
346
  const queue = new Queue(queueName, {
319
347
  connection: this.connection.getClient(),
@@ -324,7 +352,7 @@ var QueueService = class {
324
352
  ...overrides
325
353
  });
326
354
  attachDefaultErrorListener(queue, this.logger, "Queue", queueName);
327
- this.queues.set(queueName, queue);
355
+ __privateGet(this, _queues).set(queueName, queue);
328
356
  return queue;
329
357
  }
330
358
  /** Resolve the raw cached queue (default generics) for internal delegation. */
@@ -528,7 +556,7 @@ var QueueService = class {
528
556
  }
529
557
  /** Return a read-only view of the cached queues. */
530
558
  getCachedQueues() {
531
- return this.queues;
559
+ return __privateGet(this, _queues);
532
560
  }
533
561
  /**
534
562
  * Close every cached queue (swallowing per-queue errors) and clear the cache.
@@ -540,12 +568,13 @@ var QueueService = class {
540
568
  * exists to prevent. `QueueLifecycle` is the only caller.
541
569
  */
542
570
  async closeAll() {
543
- for (const queue of this.queues.values()) {
571
+ for (const queue of __privateGet(this, _queues).values()) {
544
572
  await queue.close().catch(() => void 0);
545
573
  }
546
- this.queues.clear();
574
+ __privateGet(this, _queues).clear();
547
575
  }
548
576
  };
577
+ _queues = new WeakMap();
549
578
  QueueService = __decorateClass([
550
579
  Injectable(),
551
580
  __decorateParam(0, Inject(ConnectionResolver)),
@@ -702,15 +731,16 @@ var MetricsService = class {
702
731
  MetricsService = __decorateClass([
703
732
  Injectable()
704
733
  ], MetricsService);
734
+ var _entries;
705
735
  var WorkerRegistry = class {
706
736
  constructor(connection, options) {
707
737
  this.connection = connection;
708
738
  this.options = options;
739
+ __publicField(this, "logger", new Logger(WorkerRegistry.name));
740
+ __privateAdd(this, _entries, /* @__PURE__ */ new Map());
709
741
  }
710
742
  connection;
711
743
  options;
712
- logger = new Logger(WorkerRegistry.name);
713
- entries = /* @__PURE__ */ new Map();
714
744
  /**
715
745
  * Register an in-process worker with a handler function. The BullMQ `Worker`
716
746
  * constructor is synchronous — no Redis handshake is performed at registration
@@ -741,7 +771,7 @@ var WorkerRegistry = class {
741
771
  });
742
772
  }
743
773
  attachDefaultErrorListener(worker, this.logger, "Worker", queueName);
744
- this.entries.set(queueName, { worker, connection: conn });
774
+ __privateGet(this, _entries).set(queueName, { worker, connection: conn });
745
775
  return worker;
746
776
  }
747
777
  /**
@@ -780,7 +810,7 @@ var WorkerRegistry = class {
780
810
  });
781
811
  }
782
812
  attachDefaultErrorListener(worker, this.logger, "Worker", queueName);
783
- this.entries.set(queueName, { worker, connection: conn });
813
+ __privateGet(this, _entries).set(queueName, { worker, connection: conn });
784
814
  return worker;
785
815
  }
786
816
  /**
@@ -791,11 +821,11 @@ var WorkerRegistry = class {
791
821
  * @param queueName - The queue whose worker should be removed.
792
822
  */
793
823
  async unregister(queueName) {
794
- const entry = this.entries.get(queueName);
824
+ const entry = __privateGet(this, _entries).get(queueName);
795
825
  if (!entry) return;
796
826
  await entry.worker.close();
797
827
  await this.closeConnection(entry.connection);
798
- this.entries.delete(queueName);
828
+ __privateGet(this, _entries).delete(queueName);
799
829
  }
800
830
  /**
801
831
  * Returns the queue names of all currently registered workers.
@@ -803,7 +833,7 @@ var WorkerRegistry = class {
803
833
  * @returns An immutable array of queue names.
804
834
  */
805
835
  list() {
806
- return Array.from(this.entries.keys());
836
+ return Array.from(__privateGet(this, _entries).keys());
807
837
  }
808
838
  /**
809
839
  * Returns a read-only view of the registered workers keyed by queue name.
@@ -813,7 +843,7 @@ var WorkerRegistry = class {
813
843
  */
814
844
  getAll() {
815
845
  return new Map(
816
- Array.from(this.entries, ([name, entry]) => [name, entry.worker])
846
+ Array.from(__privateGet(this, _entries), ([name, entry]) => [name, entry.worker])
817
847
  );
818
848
  }
819
849
  /**
@@ -826,7 +856,7 @@ var WorkerRegistry = class {
826
856
  */
827
857
  getConnections() {
828
858
  return new Map(
829
- Array.from(this.entries, ([name, entry]) => [name, entry.connection])
859
+ Array.from(__privateGet(this, _entries), ([name, entry]) => [name, entry.connection])
830
860
  );
831
861
  }
832
862
  /**
@@ -849,7 +879,7 @@ var WorkerRegistry = class {
849
879
  * @throws {QueueException} `DUPLICATE_PROCESSOR` if the queue is already registered.
850
880
  */
851
881
  guardDuplicate(queueName) {
852
- if (this.entries.has(queueName)) {
882
+ if (__privateGet(this, _entries).has(queueName)) {
853
883
  throw new QueueException(QUEUE_ERROR_CODES.DUPLICATE_PROCESSOR, 500, { queueName });
854
884
  }
855
885
  }
@@ -958,21 +988,23 @@ var WorkerRegistry = class {
958
988
  return result;
959
989
  }
960
990
  };
991
+ _entries = new WeakMap();
961
992
  WorkerRegistry = __decorateClass([
962
993
  Injectable(),
963
994
  __decorateParam(0, Inject(ConnectionResolver)),
964
995
  __decorateParam(1, Inject(BYMAX_QUEUE_RESOLVED_OPTIONS))
965
996
  ], WorkerRegistry);
997
+ var _events, _connections;
966
998
  var QueueEventsRegistry = class {
967
999
  constructor(connection, options) {
968
1000
  this.connection = connection;
969
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());
970
1005
  }
971
1006
  connection;
972
1007
  options;
973
- logger = new Logger(QueueEventsRegistry.name);
974
- events = /* @__PURE__ */ new Map();
975
- connections = /* @__PURE__ */ new Map();
976
1008
  /**
977
1009
  * Returns the cached `QueueEvents` for `queueName`, creating one on first
978
1010
  * call. Subsequent calls for the same name return the same instance without
@@ -982,7 +1014,7 @@ var QueueEventsRegistry = class {
982
1014
  * @returns The `QueueEvents` instance for the queue.
983
1015
  */
984
1016
  getOrCreate(queueName) {
985
- const existing = this.events.get(queueName);
1017
+ const existing = __privateGet(this, _events).get(queueName);
986
1018
  if (existing) return existing;
987
1019
  const conn = duplicateConnection(this.connection.getClient());
988
1020
  let qe;
@@ -993,8 +1025,8 @@ var QueueEventsRegistry = class {
993
1025
  throw err;
994
1026
  }
995
1027
  attachDefaultErrorListener(qe, this.logger, "QueueEvents", queueName);
996
- this.events.set(queueName, qe);
997
- this.connections.set(queueName, conn);
1028
+ __privateGet(this, _events).set(queueName, qe);
1029
+ __privateGet(this, _connections).set(queueName, conn);
998
1030
  return qe;
999
1031
  }
1000
1032
  /**
@@ -1003,7 +1035,7 @@ var QueueEventsRegistry = class {
1003
1035
  * @returns An immutable array of queue names.
1004
1036
  */
1005
1037
  list() {
1006
- return Array.from(this.events.keys());
1038
+ return Array.from(__privateGet(this, _events).keys());
1007
1039
  }
1008
1040
  /**
1009
1041
  * Returns the live `QueueEvents` map. Intended for the shutdown orchestrator.
@@ -1011,7 +1043,7 @@ var QueueEventsRegistry = class {
1011
1043
  * @returns A read-only view of the internal map.
1012
1044
  */
1013
1045
  getAll() {
1014
- return this.events;
1046
+ return __privateGet(this, _events);
1015
1047
  }
1016
1048
  /**
1017
1049
  * Returns a read-only view of the duplicated connections the library opened
@@ -1027,9 +1059,11 @@ var QueueEventsRegistry = class {
1027
1059
  * @returns A read-only snapshot of queue name to duplicated connection.
1028
1060
  */
1029
1061
  getConnections() {
1030
- return new Map(this.connections);
1062
+ return new Map(__privateGet(this, _connections));
1031
1063
  }
1032
1064
  };
1065
+ _events = new WeakMap();
1066
+ _connections = new WeakMap();
1033
1067
  QueueEventsRegistry = __decorateClass([
1034
1068
  Injectable(),
1035
1069
  __decorateParam(0, Inject(ConnectionResolver)),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bymax-one/nest-queue",
3
- "version": "1.0.7",
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",