@bymax-one/nest-queue 1.2.0 → 1.2.2

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,47 @@ versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
8
  ---
9
9
 
10
+ ## [1.2.2] — 2026-09-04
11
+
12
+ **1.2.1 was tagged but never published**, so this release carries its documentation fix as well
13
+ as the one below. `dist/` changes here for the first time since 1.2.0 — JSDoc reaches the emitted
14
+ `.d.ts`, so a doc-block correction is a change to the published types.
15
+
16
+ ### Fixed
17
+
18
+ - **The `enqueue` deduplication example asserted a safety property it did not provide.** It showed
19
+ `` `${tenantId}:reindex:${term}` `` and stated that "one tenant's term cannot suppress another's".
20
+ Two variable fields joined by a delimiter either may contain do not produce a unique key: with
21
+ `` `${tenantId}:${term}` ``, the pairs `('a', 'b:c')` and `('a:b', 'c')` both yield `a:b:c`. The
22
+ guidance now percent-encodes every variable field, and the claim states what the construction
23
+ delivers. This is documentation a consumer copies into their own key-building code, which is why
24
+ the shape mattered more than whether `:reindex:` in particular was reachable.
25
+ - Carried from 1.2.1: **the `MetricsService` example in `README.md` called two methods that do not
26
+ exist**, `getMetrics(name)` and `getAll([names])`, where the service exposes `get(name)` and a
27
+ no-argument `getAll()`.
28
+
29
+ ### Internal
30
+
31
+ Not shipped, listed because the tag covers them: bullmq raised from 6.0.10 to 6.3.4 in the
32
+ workspace override; `fast-uri`, `qs` and `fastify` floors raised past published advisories; the
33
+ README-snippet gate now type-checks through receivers; `AGENTS.md` and its sync workflow added.
34
+
35
+ ## [1.2.1] — 2026-08-30
36
+
37
+ Documentation only. `dist/` is byte-identical to `1.2.0`; the published tarball was unpacked and
38
+ diffed against a fresh build to confirm it rather than assert it. The release exists because
39
+ `README.md` ships inside the package, so a fix that stays on `main` leaves the npm page — where
40
+ the example is actually read — still wrong.
41
+
42
+ ### Fixed
43
+
44
+ - **The `MetricsService` example in `README.md` called two methods that do not exist.** It showed
45
+ `getMetrics(name)` and `getAll([names])`; the service exposes `get(name)` and a no-argument
46
+ `getAll()` returning `readonly QueueMetrics[]` for every queue currently cached by
47
+ `QueueService`. Code copied from that example failed to compile, and the wrong `getAll` signature
48
+ also implied a per-call queue filter the API does not offer. Every other call in the README was
49
+ checked against the source in the same pass and is correct.
50
+
10
51
  ## [1.2.0] — 2026-08-10
11
52
 
12
53
  **Breaking, shipped as a minor by choice.** The peer ranges move to the next major of each
@@ -487,6 +528,8 @@ v6 peer range.
487
528
 
488
529
  ---
489
530
 
531
+ [1.2.2]: https://github.com/bymaxone/nest-queue/compare/v1.2.1...v1.2.2
532
+ [1.2.1]: https://github.com/bymaxone/nest-queue/compare/v1.2.0...v1.2.1
490
533
  [1.2.0]: https://github.com/bymaxone/nest-queue/compare/v1.0.11...v1.2.0
491
534
  [1.0.11]: https://github.com/bymaxone/nest-queue/compare/v1.0.10...v1.0.11
492
535
  [1.0.10]: https://github.com/bymaxone/nest-queue/compare/v1.0.9...v1.0.10
package/README.md CHANGED
@@ -400,8 +400,8 @@ BymaxQueueModule.forRoot({
400
400
  import { MetricsService } from '@bymax-one/nest-queue'
401
401
  import type { QueueMetrics } from '@bymax-one/nest-queue'
402
402
 
403
- const metrics: QueueMetrics = await this.metricsService.getMetrics('email')
404
- const all = await this.metricsService.getAll(['email', 'notifications'])
403
+ const metrics: QueueMetrics = await this.metricsService.get('email')
404
+ const all: readonly QueueMetrics[] = await this.metricsService.getAll()
405
405
  ```
406
406
 
407
407
  ---
@@ -392,7 +392,13 @@ exports.QueueService = class QueueService {
392
392
  * The deduplication `id` is a shared key across the queue, so a multi-tenant
393
393
  * producer MUST scope it by tenant: keyed on the value alone
394
394
  * (`` `reindex:${term}` ``), tenant A enqueuing a term suppresses tenant B's job
395
- * for the same term. Include the tenant (`` `${tenantId}:reindex:${term}` ``).
395
+ * for the same term.
396
+ *
397
+ * Scoping alone is not enough. Two variable fields joined by a delimiter that
398
+ * either may contain do not produce a unique key: with `` `${tenantId}:${term}` ``,
399
+ * the pairs `('a', 'b:c')` and `('a:b', 'c')` both yield `a:b:c`. Percent-encode
400
+ * every variable field so the delimiter cannot appear inside one:
401
+ * `` `${encodeURIComponent(tenantId)}:${encodeURIComponent(term)}` ``.
396
402
  *
397
403
  * Deduplication modes (`deduplication: { id, ttl?, extend?, replace?, keepLastIfActive? }`):
398
404
  * - Simple `{ id }` — collapse duplicates until the in-flight job completes or fails.
@@ -407,10 +413,11 @@ exports.QueueService = class QueueService {
407
413
  * @param data - Typed job payload.
408
414
  * @param options - Per-job BullMQ options (priority, delay, `jobId`, `deduplication`, ...).
409
415
  * @returns The created job.
410
- * @example Throttle: at most one reindex per term every 5 seconds. The dedup key
411
- * carries the tenant so one tenant's term cannot suppress another's.
416
+ * @example Throttle: at most one reindex per term every 5 seconds. Both variable
417
+ * fields are percent-encoded, so no other tenant/term pair can build this key.
418
+ * const dedupId = `${encodeURIComponent(tenantId)}:reindex:${encodeURIComponent(term)}`
412
419
  * await queueService.enqueue('search', 'reindex', { term }, {
413
- * deduplication: { id: `${tenantId}:reindex:${term}`, ttl: 5_000 },
420
+ * deduplication: { id: dedupId, ttl: 5_000 },
414
421
  * })
415
422
  */
416
423
  async enqueue(queueName, jobName, data, options) {
@@ -433,7 +433,13 @@ declare class QueueService {
433
433
  * The deduplication `id` is a shared key across the queue, so a multi-tenant
434
434
  * producer MUST scope it by tenant: keyed on the value alone
435
435
  * (`` `reindex:${term}` ``), tenant A enqueuing a term suppresses tenant B's job
436
- * for the same term. Include the tenant (`` `${tenantId}:reindex:${term}` ``).
436
+ * for the same term.
437
+ *
438
+ * Scoping alone is not enough. Two variable fields joined by a delimiter that
439
+ * either may contain do not produce a unique key: with `` `${tenantId}:${term}` ``,
440
+ * the pairs `('a', 'b:c')` and `('a:b', 'c')` both yield `a:b:c`. Percent-encode
441
+ * every variable field so the delimiter cannot appear inside one:
442
+ * `` `${encodeURIComponent(tenantId)}:${encodeURIComponent(term)}` ``.
437
443
  *
438
444
  * Deduplication modes (`deduplication: { id, ttl?, extend?, replace?, keepLastIfActive? }`):
439
445
  * - Simple `{ id }` — collapse duplicates until the in-flight job completes or fails.
@@ -448,10 +454,11 @@ declare class QueueService {
448
454
  * @param data - Typed job payload.
449
455
  * @param options - Per-job BullMQ options (priority, delay, `jobId`, `deduplication`, ...).
450
456
  * @returns The created job.
451
- * @example Throttle: at most one reindex per term every 5 seconds. The dedup key
452
- * carries the tenant so one tenant's term cannot suppress another's.
457
+ * @example Throttle: at most one reindex per term every 5 seconds. Both variable
458
+ * fields are percent-encoded, so no other tenant/term pair can build this key.
459
+ * const dedupId = `${encodeURIComponent(tenantId)}:reindex:${encodeURIComponent(term)}`
453
460
  * await queueService.enqueue('search', 'reindex', { term }, {
454
- * deduplication: { id: `${tenantId}:reindex:${term}`, ttl: 5_000 },
461
+ * deduplication: { id: dedupId, ttl: 5_000 },
455
462
  * })
456
463
  */
457
464
  enqueue<TData = unknown, TResult = unknown>(queueName: string, jobName: string, data: TData, options?: JobsOptions): Promise<TypedJob<TData, TResult>>;
@@ -433,7 +433,13 @@ declare class QueueService {
433
433
  * The deduplication `id` is a shared key across the queue, so a multi-tenant
434
434
  * producer MUST scope it by tenant: keyed on the value alone
435
435
  * (`` `reindex:${term}` ``), tenant A enqueuing a term suppresses tenant B's job
436
- * for the same term. Include the tenant (`` `${tenantId}:reindex:${term}` ``).
436
+ * for the same term.
437
+ *
438
+ * Scoping alone is not enough. Two variable fields joined by a delimiter that
439
+ * either may contain do not produce a unique key: with `` `${tenantId}:${term}` ``,
440
+ * the pairs `('a', 'b:c')` and `('a:b', 'c')` both yield `a:b:c`. Percent-encode
441
+ * every variable field so the delimiter cannot appear inside one:
442
+ * `` `${encodeURIComponent(tenantId)}:${encodeURIComponent(term)}` ``.
437
443
  *
438
444
  * Deduplication modes (`deduplication: { id, ttl?, extend?, replace?, keepLastIfActive? }`):
439
445
  * - Simple `{ id }` — collapse duplicates until the in-flight job completes or fails.
@@ -448,10 +454,11 @@ declare class QueueService {
448
454
  * @param data - Typed job payload.
449
455
  * @param options - Per-job BullMQ options (priority, delay, `jobId`, `deduplication`, ...).
450
456
  * @returns The created job.
451
- * @example Throttle: at most one reindex per term every 5 seconds. The dedup key
452
- * carries the tenant so one tenant's term cannot suppress another's.
457
+ * @example Throttle: at most one reindex per term every 5 seconds. Both variable
458
+ * fields are percent-encoded, so no other tenant/term pair can build this key.
459
+ * const dedupId = `${encodeURIComponent(tenantId)}:reindex:${encodeURIComponent(term)}`
453
460
  * await queueService.enqueue('search', 'reindex', { term }, {
454
- * deduplication: { id: `${tenantId}:reindex:${term}`, ttl: 5_000 },
461
+ * deduplication: { id: dedupId, ttl: 5_000 },
455
462
  * })
456
463
  */
457
464
  enqueue<TData = unknown, TResult = unknown>(queueName: string, jobName: string, data: TData, options?: JobsOptions): Promise<TypedJob<TData, TResult>>;
@@ -390,7 +390,13 @@ var QueueService = class {
390
390
  * The deduplication `id` is a shared key across the queue, so a multi-tenant
391
391
  * producer MUST scope it by tenant: keyed on the value alone
392
392
  * (`` `reindex:${term}` ``), tenant A enqueuing a term suppresses tenant B's job
393
- * for the same term. Include the tenant (`` `${tenantId}:reindex:${term}` ``).
393
+ * for the same term.
394
+ *
395
+ * Scoping alone is not enough. Two variable fields joined by a delimiter that
396
+ * either may contain do not produce a unique key: with `` `${tenantId}:${term}` ``,
397
+ * the pairs `('a', 'b:c')` and `('a:b', 'c')` both yield `a:b:c`. Percent-encode
398
+ * every variable field so the delimiter cannot appear inside one:
399
+ * `` `${encodeURIComponent(tenantId)}:${encodeURIComponent(term)}` ``.
394
400
  *
395
401
  * Deduplication modes (`deduplication: { id, ttl?, extend?, replace?, keepLastIfActive? }`):
396
402
  * - Simple `{ id }` — collapse duplicates until the in-flight job completes or fails.
@@ -405,10 +411,11 @@ var QueueService = class {
405
411
  * @param data - Typed job payload.
406
412
  * @param options - Per-job BullMQ options (priority, delay, `jobId`, `deduplication`, ...).
407
413
  * @returns The created job.
408
- * @example Throttle: at most one reindex per term every 5 seconds. The dedup key
409
- * carries the tenant so one tenant's term cannot suppress another's.
414
+ * @example Throttle: at most one reindex per term every 5 seconds. Both variable
415
+ * fields are percent-encoded, so no other tenant/term pair can build this key.
416
+ * const dedupId = `${encodeURIComponent(tenantId)}:reindex:${encodeURIComponent(term)}`
410
417
  * await queueService.enqueue('search', 'reindex', { term }, {
411
- * deduplication: { id: `${tenantId}:reindex:${term}`, ttl: 5_000 },
418
+ * deduplication: { id: dedupId, ttl: 5_000 },
412
419
  * })
413
420
  */
414
421
  async enqueue(queueName, jobName, data, options) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bymax-one/nest-queue",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
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",
@@ -100,8 +100,8 @@
100
100
  "@nestjs/common": "^11.1.28",
101
101
  "@nestjs/core": "^11.1.28",
102
102
  "@nestjs/testing": "^11.1.28",
103
- "@stryker-mutator/core": "^9.6.1",
104
- "@stryker-mutator/jest-runner": "^9.6.1",
103
+ "@stryker-mutator/core": "^10.0.0",
104
+ "@stryker-mutator/jest-runner": "^10.0.0",
105
105
  "@types/jest": "^30.0.0",
106
106
  "@types/node": "^26.2.0",
107
107
  "bullmq": "^6.0.0",