@bymax-one/nest-queue 1.2.0 → 1.2.3

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,71 @@ versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
8
  ---
9
9
 
10
+ ## [1.2.3] — 2026-09-05
11
+
12
+ **Nothing that runs in this package changed.** A fresh build of the release commit was packed and
13
+ compared against the published 1.2.2 tarball: `dist/` is identical file for file, and `package.json`
14
+ differs only in the version string. The remaining difference is this CHANGELOG entry, which ships
15
+ because `CHANGELOG.md` does.
16
+
17
+ It exists because the estate releases on a cadence, so a library whose artifact is unchanged
18
+ publishes anyway and says so rather than implying a benefit it does not deliver. **There is no
19
+ reason to upgrade from 1.2.2**, and a consumer on `^1.2.0` gains nothing by resolving to it.
20
+
21
+ ### Internal
22
+
23
+ Not shipped — `files` is `dist`, `LICENSE`, `README.md`, `CHANGELOG.md`, and the only file touched
24
+ since 1.2.2 is `AGENTS.md`:
25
+
26
+ - The shared review-rules block was synchronised with its canonical copy, bringing in an invariant
27
+ for keys built from more than one field — the generalised form of the deduplication defect this
28
+ package corrected in 1.2.2.
29
+ - Two repository-specific review rules were corrected: one rested its case on a defect that had
30
+ since been fixed, so the claim had become false; the other required an authorship finding to
31
+ quote a resolvable commit id, a criterion the shared block superseded by placing author and
32
+ committer identity out of scope entirely.
33
+
34
+ ## [1.2.2] — 2026-09-04
35
+
36
+ **1.2.1 was tagged but never published**, so this release carries its documentation fix as well
37
+ as the one below. `dist/` changes here for the first time since 1.2.0 — JSDoc reaches the emitted
38
+ `.d.ts`, so a doc-block correction is a change to the published types.
39
+
40
+ ### Fixed
41
+
42
+ - **The `enqueue` deduplication example asserted a safety property it did not provide.** It showed
43
+ `` `${tenantId}:reindex:${term}` `` and stated that "one tenant's term cannot suppress another's".
44
+ Two variable fields joined by a delimiter either may contain do not produce a unique key: with
45
+ `` `${tenantId}:${term}` ``, the pairs `('a', 'b:c')` and `('a:b', 'c')` both yield `a:b:c`. The
46
+ guidance now percent-encodes every variable field, and the claim states what the construction
47
+ delivers. This is documentation a consumer copies into their own key-building code, which is why
48
+ the shape mattered more than whether `:reindex:` in particular was reachable.
49
+ - Carried from 1.2.1: **the `MetricsService` example in `README.md` called two methods that do not
50
+ exist**, `getMetrics(name)` and `getAll([names])`, where the service exposes `get(name)` and a
51
+ no-argument `getAll()`.
52
+
53
+ ### Internal
54
+
55
+ Not shipped, listed because the tag covers them: bullmq raised from 6.0.10 to 6.3.4 in the
56
+ workspace override; `fast-uri`, `qs` and `fastify` floors raised past published advisories; the
57
+ README-snippet gate now type-checks through receivers; `AGENTS.md` and its sync workflow added.
58
+
59
+ ## [1.2.1] — 2026-08-30
60
+
61
+ Documentation only. `dist/` is byte-identical to `1.2.0`; the published tarball was unpacked and
62
+ diffed against a fresh build to confirm it rather than assert it. The release exists because
63
+ `README.md` ships inside the package, so a fix that stays on `main` leaves the npm page — where
64
+ the example is actually read — still wrong.
65
+
66
+ ### Fixed
67
+
68
+ - **The `MetricsService` example in `README.md` called two methods that do not exist.** It showed
69
+ `getMetrics(name)` and `getAll([names])`; the service exposes `get(name)` and a no-argument
70
+ `getAll()` returning `readonly QueueMetrics[]` for every queue currently cached by
71
+ `QueueService`. Code copied from that example failed to compile, and the wrong `getAll` signature
72
+ also implied a per-call queue filter the API does not offer. Every other call in the README was
73
+ checked against the source in the same pass and is correct.
74
+
10
75
  ## [1.2.0] — 2026-08-10
11
76
 
12
77
  **Breaking, shipped as a minor by choice.** The peer ranges move to the next major of each
@@ -487,6 +552,9 @@ v6 peer range.
487
552
 
488
553
  ---
489
554
 
555
+ [1.2.3]: https://github.com/bymaxone/nest-queue/compare/v1.2.2...v1.2.3
556
+ [1.2.2]: https://github.com/bymaxone/nest-queue/compare/v1.2.1...v1.2.2
557
+ [1.2.1]: https://github.com/bymaxone/nest-queue/compare/v1.2.0...v1.2.1
490
558
  [1.2.0]: https://github.com/bymaxone/nest-queue/compare/v1.0.11...v1.2.0
491
559
  [1.0.11]: https://github.com/bymaxone/nest-queue/compare/v1.0.10...v1.0.11
492
560
  [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.3",
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",