@bymax-one/nest-queue 1.0.3 → 1.0.5

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,110 @@ versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
8
  ---
9
9
 
10
+ ## [1.0.5] — 2026-08-01
11
+
12
+ Documentation only. `dist/` is byte-identical to `1.0.4` — verified by diffing a
13
+ fresh build against the published tarball — so there is no runtime change.
14
+
15
+ ### Fixed
16
+
17
+ - **The README's configuration examples did not compile in a strict project.**
18
+ Eight snippets passed `process.env.REDIS_URL` where a `string` is required:
19
+
20
+ ```
21
+ TS2322: Type 'string | undefined' is not assignable to type 'string'.
22
+ ```
23
+
24
+ `process.env.X` is `string | undefined` under `strict`, so anyone copying the
25
+ Quick Start into a strict TypeScript project — the configuration this library
26
+ itself uses and recommends — got a type error out of the box. Now shown with a
27
+ fallback, which is what the code has to do anyway:
28
+
29
+ ```ts
30
+ connection: {
31
+ url: process.env.REDIS_URL ?? 'redis://localhost:6379'
32
+ }
33
+ ```
34
+
35
+ Found by `pnpm check:published`, added in the same release, which compiles the
36
+ README's own snippets against the built package. Nobody had reported it, and
37
+ nothing before this would have.
38
+
39
+ ### Added
40
+
41
+ - **`pnpm check:published`** — a gate that verifies the published surface matches
42
+ the documentation, before a tag rather than after it. It scaffolds a throwaway
43
+ consumer, symlinks the package into its own `node_modules` so resolution runs
44
+ through the `exports` map into `dist/`, and then checks that the README's links
45
+ resolve, that its TypeScript snippets and the type tests compile against the
46
+ built package, and that every `v*.*.*` tag has a `## [x.y.z]` CHANGELOG section.
47
+
48
+ Each of those exists because its absence let a defect reach npm: the 404 link
49
+ corrected in `1.0.3`, the exported type that rejected the README's own snippet
50
+ corrected in `1.0.4`, and a deleted heading that would have turned release notes
51
+ into the generic fallback.
52
+
53
+ It runs in CI, in `release.yml`, and inside `prepublishOnly` — the last one
54
+ because the first publish of a package is manual by design, which is precisely
55
+ the path that bypasses the tag workflow.
56
+
57
+ ---
58
+
59
+ ## [1.0.4] — 2026-08-01
60
+
61
+ ### Fixed
62
+
63
+ - **`BymaxQueueModuleAsyncOptions.useFactory` rejected the factory this library
64
+ documents.** It was declared `(...args: unknown[]) => …`, and a parameter typed
65
+ `unknown` accepts no narrower parameter under `strictFunctionTypes`. So the
66
+ shape shown in the README and in `BymaxQueueModule`'s own `@example`:
67
+
68
+ ```ts
69
+ useFactory: (client: Redis) => ({ connection: { client } })
70
+ ```
71
+
72
+ failed to typecheck **against the exported interface**:
73
+
74
+ ```
75
+ TS2322: Type '(client: Redis) => …' is not assignable to
76
+ type '(...args: unknown[]) => …'
77
+ ```
78
+
79
+ Scope, precisely: `forRootAsync` is typed by NestJS's `ASYNC_OPTIONS_TYPE`, not
80
+ by this interface, so passing the object **inline** always compiled. Only a
81
+ consumer who annotated it — extracting the options into a
82
+ `const opts: BymaxQueueModuleAsyncOptions = { … }` — hit the error. The
83
+ interface was stricter than the module it describes.
84
+
85
+ It is now declared with **method syntax**. `strictFunctionTypes` makes
86
+ parameters contravariant for function-typed _properties_ but leaves method
87
+ parameters bivariant, so the interface accepts a factory that names its
88
+ injected types. This **widens** what is accepted: every factory that compiled
89
+ before still does, the variadic `(...args: unknown[])` form included. Nothing
90
+ needs changing in consumer code.
91
+
92
+ Method syntax rather than a looser parameter type, because the factory has to
93
+ stay **callable**: NestJS invokes it with the values `inject` resolves, and a
94
+ consumer may call it directly in a unit test. A `never[]` rest parameter would
95
+ have bought the same assignability and silently cost that —
96
+ `options.useFactory(client)` fails with `TS2345: Argument of type 'Redis' is
97
+ not assignable to parameter of type 'never'`.
98
+
99
+ Pinned by seven cases in `test/types/public-api.test-d.ts` — typed parameter,
100
+ async, several parameters, zero parameters, the pre-1.0.4 variadic form, and
101
+ two for callability — where `useFactory` was previously pinned by nothing,
102
+ which is why this went unnoticed. Red-checked against **both** rejected
103
+ signatures: the original property form fails the assignability cases, and
104
+ `never[]` fails the callability ones.
105
+
106
+ No runtime change, verified rather than asserted: diffed against the published
107
+ `1.0.3` tarball, all four emitted runtime files (`server` and `shared`, `.mjs`
108
+ and `.cjs`) are byte-identical, and only `dist/server/index.d.ts` / `.d.cts`
109
+ differ. The altered source declares types only, so it produces no Stryker
110
+ mutants and the 99.06% score stands.
111
+
112
+ ---
113
+
10
114
  ## [1.0.3] — 2026-08-01
11
115
 
12
116
  Documentation only. `dist/` is byte-identical to `1.0.2` — verified by unpacking
@@ -207,6 +311,9 @@ v6 peer range.
207
311
 
208
312
  ---
209
313
 
314
+ [1.0.5]: https://github.com/bymaxone/nest-queue/releases/tag/v1.0.5
315
+ [1.0.4]: https://github.com/bymaxone/nest-queue/releases/tag/v1.0.4
316
+ [1.0.3]: https://github.com/bymaxone/nest-queue/releases/tag/v1.0.3
210
317
  [1.0.2]: https://github.com/bymaxone/nest-queue/releases/tag/v1.0.2
211
318
  [1.0.1]: https://github.com/bymaxone/nest-queue/releases/tag/v1.0.1
212
319
  [1.0.0]: https://github.com/bymaxone/nest-queue/releases/tag/v1.0.0
package/README.md CHANGED
@@ -149,7 +149,7 @@ import { BymaxQueueModule } from '@bymax-one/nest-queue'
149
149
  @Module({
150
150
  imports: [
151
151
  BymaxQueueModule.forRoot({
152
- connection: { url: process.env.REDIS_URL },
152
+ connection: { url: process.env.REDIS_URL ?? 'redis://localhost:6379' },
153
153
  isGlobal: true,
154
154
  }),
155
155
  ],
@@ -259,7 +259,7 @@ Mode B lets the library own the Redis connections. Pass either a URL or raw iore
259
259
  ```typescript
260
260
  // URL form
261
261
  BymaxQueueModule.forRoot({
262
- connection: { url: process.env.REDIS_URL },
262
+ connection: { url: process.env.REDIS_URL ?? 'redis://localhost:6379' },
263
263
  isGlobal: true,
264
264
  })
265
265
 
@@ -267,9 +267,9 @@ BymaxQueueModule.forRoot({
267
267
  BymaxQueueModule.forRoot({
268
268
  connection: {
269
269
  options: {
270
- host: process.env.REDIS_HOST,
270
+ host: process.env.REDIS_HOST ?? 'localhost',
271
271
  port: Number(process.env.REDIS_PORT),
272
- password: process.env.REDIS_PASSWORD,
272
+ password: process.env.REDIS_PASSWORD ?? undefined,
273
273
  tls: {},
274
274
  },
275
275
  },
@@ -345,7 +345,7 @@ Enable flows in the module options and inject the service:
345
345
 
346
346
  ```typescript
347
347
  BymaxQueueModule.forRoot({
348
- connection: { url: process.env.REDIS_URL },
348
+ connection: { url: process.env.REDIS_URL ?? 'redis://localhost:6379' },
349
349
  flows: { enabled: true },
350
350
  })
351
351
 
@@ -370,7 +370,7 @@ Provide a per-queue snapshot with a configurable TTL cache:
370
370
 
371
371
  ```typescript
372
372
  BymaxQueueModule.forRoot({
373
- connection: { url: process.env.REDIS_URL },
373
+ connection: { url: process.env.REDIS_URL ?? 'redis://localhost:6379' },
374
374
  metrics: { enabled: true, cacheTtlMs: 5_000 },
375
375
  })
376
376
 
@@ -562,7 +562,7 @@ Pass a `bullmq-otel` telemetry instance (optional peer dep) to trace spans from
562
562
  import { BullMQOtel } from 'bullmq-otel'
563
563
 
564
564
  BymaxQueueModule.forRoot({
565
- connection: { url: process.env.REDIS_URL },
565
+ connection: { url: process.env.REDIS_URL ?? 'redis://localhost:6379' },
566
566
  telemetry: new BullMQOtel('my-service', tracer),
567
567
  })
568
568
  ```
@@ -602,7 +602,7 @@ Tune `drainTimeoutMs` when jobs may run longer than 30 s:
602
602
 
603
603
  ```typescript
604
604
  BymaxQueueModule.forRoot({
605
- connection: { url: process.env.REDIS_URL },
605
+ connection: { url: process.env.REDIS_URL ?? 'redis://localhost:6379' },
606
606
  shutdown: { drainTimeoutMs: 60_000 },
607
607
  })
608
608
  ```
@@ -97,8 +97,29 @@ interface BymaxQueueOptionsFactory {
97
97
  }
98
98
  /** Asynchronous configuration mirroring the NestJS async dynamic-module pattern. */
99
99
  interface BymaxQueueModuleAsyncOptions extends Pick<ModuleMetadata, 'imports'> {
100
- /** Factory returning the module options. */
101
- useFactory?: (...args: unknown[]) => Promise<BymaxQueueModuleOptions> | BymaxQueueModuleOptions;
100
+ /**
101
+ * Factory returning the module options. Its parameters are whatever `inject`
102
+ * resolves to, so declare them with the types you expect:
103
+ *
104
+ * ```ts
105
+ * useFactory: (client: Redis) => ({ connection: { client } })
106
+ * ```
107
+ *
108
+ * Declared with **method syntax** on purpose. `strictFunctionTypes` makes
109
+ * parameters contravariant for function-typed *properties* but leaves method
110
+ * parameters bivariant, so this accepts a factory that names its injected
111
+ * types — which a property typed `(...args: unknown[]) => …` rejects, since
112
+ * `unknown` is assignable from no narrower parameter. That rejection is what
113
+ * made the form above fail against this interface while compiling when passed
114
+ * inline to `forRootAsync`.
115
+ *
116
+ * Bivariance is the right trade here rather than a looser parameter type:
117
+ * NestJS resolves these arguments from `inject` at runtime, so no signature
118
+ * can make them statically safe, and method syntax keeps the factory
119
+ * **callable** — `options.useFactory(client)` typechecks, which a
120
+ * `never[]` rest parameter would have broken.
121
+ */
122
+ useFactory?(...args: unknown[]): Promise<BymaxQueueModuleOptions> | BymaxQueueModuleOptions;
102
123
  /** Class implementing the options factory. */
103
124
  useClass?: Type<BymaxQueueOptionsFactory>;
104
125
  /** Existing provider implementing the options factory. */
@@ -97,8 +97,29 @@ interface BymaxQueueOptionsFactory {
97
97
  }
98
98
  /** Asynchronous configuration mirroring the NestJS async dynamic-module pattern. */
99
99
  interface BymaxQueueModuleAsyncOptions extends Pick<ModuleMetadata, 'imports'> {
100
- /** Factory returning the module options. */
101
- useFactory?: (...args: unknown[]) => Promise<BymaxQueueModuleOptions> | BymaxQueueModuleOptions;
100
+ /**
101
+ * Factory returning the module options. Its parameters are whatever `inject`
102
+ * resolves to, so declare them with the types you expect:
103
+ *
104
+ * ```ts
105
+ * useFactory: (client: Redis) => ({ connection: { client } })
106
+ * ```
107
+ *
108
+ * Declared with **method syntax** on purpose. `strictFunctionTypes` makes
109
+ * parameters contravariant for function-typed *properties* but leaves method
110
+ * parameters bivariant, so this accepts a factory that names its injected
111
+ * types — which a property typed `(...args: unknown[]) => …` rejects, since
112
+ * `unknown` is assignable from no narrower parameter. That rejection is what
113
+ * made the form above fail against this interface while compiling when passed
114
+ * inline to `forRootAsync`.
115
+ *
116
+ * Bivariance is the right trade here rather than a looser parameter type:
117
+ * NestJS resolves these arguments from `inject` at runtime, so no signature
118
+ * can make them statically safe, and method syntax keeps the factory
119
+ * **callable** — `options.useFactory(client)` typechecks, which a
120
+ * `never[]` rest parameter would have broken.
121
+ */
122
+ useFactory?(...args: unknown[]): Promise<BymaxQueueModuleOptions> | BymaxQueueModuleOptions;
102
123
  /** Class implementing the options factory. */
103
124
  useClass?: Type<BymaxQueueOptionsFactory>;
104
125
  /** Existing provider implementing the options factory. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bymax-one/nest-queue",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
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",
@@ -131,23 +131,24 @@
131
131
  },
132
132
  "scripts": {
133
133
  "build": "pnpm clean && tsup",
134
+ "check:exports": "attw --pack .",
135
+ "check:published": "node scripts/check-published-surface.mjs",
136
+ "clean": "rm -rf dist coverage",
134
137
  "lint": "eslint src scripts test",
135
138
  "lint:fix": "eslint src scripts test --fix",
136
- "test": "jest",
137
- "test:cov": "jest --coverage",
138
- "test:watch": "jest --watch",
139
- "test:e2e": "jest --config jest.e2e.config.ts",
140
- "test:all": "pnpm test && pnpm test:e2e",
141
- "test:cov:all": "jest --config jest.coverage.config.ts --coverage",
142
- "test:types": "tsc --noEmit -p tsconfig.typetest.json",
143
139
  "mutation": "stryker run",
144
- "mutation:incremental": "stryker run --incremental",
145
140
  "mutation:dry-run": "stryker run --dryRunOnly",
146
- "typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.server.json",
141
+ "mutation:incremental": "stryker run --incremental",
142
+ "release": "pnpm publish --provenance",
147
143
  "size": "node scripts/check-size.mjs",
148
144
  "smoke": "node scripts/dogfood-smoke-test.mjs",
149
- "check:exports": "attw --pack .",
150
- "clean": "rm -rf dist coverage",
151
- "release": "pnpm publish --provenance"
145
+ "test": "jest",
146
+ "test:all": "pnpm test && pnpm test:e2e",
147
+ "test:cov": "jest --coverage",
148
+ "test:cov:all": "jest --config jest.coverage.config.ts --coverage",
149
+ "test:e2e": "jest --config jest.e2e.config.ts",
150
+ "test:types": "tsc --noEmit -p tsconfig.typetest.json",
151
+ "test:watch": "jest --watch",
152
+ "typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.server.json"
152
153
  }
153
154
  }