@nest-batch/bullmq 0.2.0

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.
Files changed (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +333 -0
  3. package/dist/src/adapters/bullmq.adapter.d.ts +157 -0
  4. package/dist/src/adapters/bullmq.adapter.d.ts.map +1 -0
  5. package/dist/src/adapters/bullmq.adapter.js +252 -0
  6. package/dist/src/adapters/bullmq.adapter.js.map +1 -0
  7. package/dist/src/adapters/index.d.ts +12 -0
  8. package/dist/src/adapters/index.d.ts.map +1 -0
  9. package/dist/src/adapters/index.js +29 -0
  10. package/dist/src/adapters/index.js.map +1 -0
  11. package/dist/src/bullmq-execution-strategy.d.ts +59 -0
  12. package/dist/src/bullmq-execution-strategy.d.ts.map +1 -0
  13. package/dist/src/bullmq-execution-strategy.js +60 -0
  14. package/dist/src/bullmq-execution-strategy.js.map +1 -0
  15. package/dist/src/bullmq-runtime.service.d.ts +237 -0
  16. package/dist/src/bullmq-runtime.service.d.ts.map +1 -0
  17. package/dist/src/bullmq-runtime.service.js +441 -0
  18. package/dist/src/bullmq-runtime.service.js.map +1 -0
  19. package/dist/src/bullmq-schedule.service.d.ts +121 -0
  20. package/dist/src/bullmq-schedule.service.d.ts.map +1 -0
  21. package/dist/src/bullmq-schedule.service.js +232 -0
  22. package/dist/src/bullmq-schedule.service.js.map +1 -0
  23. package/dist/src/connection.d.ts +83 -0
  24. package/dist/src/connection.d.ts.map +1 -0
  25. package/dist/src/connection.js +72 -0
  26. package/dist/src/connection.js.map +1 -0
  27. package/dist/src/index.d.ts +29 -0
  28. package/dist/src/index.d.ts.map +1 -0
  29. package/dist/src/index.js +46 -0
  30. package/dist/src/index.js.map +1 -0
  31. package/dist/src/module-options.d.ts +68 -0
  32. package/dist/src/module-options.d.ts.map +1 -0
  33. package/dist/src/module-options.js +13 -0
  34. package/dist/src/module-options.js.map +1 -0
  35. package/package.json +71 -0
  36. package/src/adapters/bullmq.adapter.ts +346 -0
  37. package/src/adapters/index.ts +11 -0
  38. package/src/bullmq-execution-strategy.ts +81 -0
  39. package/src/bullmq-runtime.service.ts +540 -0
  40. package/src/bullmq-schedule.service.ts +271 -0
  41. package/src/connection.ts +97 -0
  42. package/src/index.ts +28 -0
  43. package/src/module-options.ts +74 -0
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Public API barrel for `@nest-batch/bullmq`.
3
+ *
4
+ * The host application should depend exclusively on this barrel:
5
+ * - `BullmqAdapter` is the new factory-pattern transport
6
+ * adapter (use with `NestBatchModule.forRoot({ adapters:
7
+ * { transport: BullmqAdapter.forRoot(...) } })`).
8
+ * - `BullMqExecutionStrategy` is the strategy class (also
9
+ * exported individually so callers can inject it directly for
10
+ * inspection / health checks).
11
+ * - `BULLMQ_MODULE_OPTIONS` is the DI token for the resolved
12
+ * module options bag.
13
+ * - the connection helpers are re-exported so callers can build
14
+ * a fully-resolved `BullMqResolvedConnection` from a partial
15
+ * `BullMqConnectionOptions` without importing the internal
16
+ * `connection.ts` file.
17
+ *
18
+ * The legacy `BullmqBatchModule` (with `forRoot` / `forRootAsync`
19
+ * static methods) has been replaced by `BullmqAdapter`. Internal
20
+ * modules (`./bullmq-execution-strategy`, `./module-options`,
21
+ * `./connection`, `./adapters/bullmq.module`) are implementation
22
+ * details and may move between releases.
23
+ */
24
+ export * from './connection';
25
+ export * from './module-options';
26
+ export * from './bullmq-execution-strategy';
27
+ export * from './bullmq-schedule.service';
28
+ export * from './adapters';
29
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,YAAY,CAAC"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Public API barrel for `@nest-batch/bullmq`.
3
+ *
4
+ * The host application should depend exclusively on this barrel:
5
+ * - `BullmqAdapter` is the new factory-pattern transport
6
+ * adapter (use with `NestBatchModule.forRoot({ adapters:
7
+ * { transport: BullmqAdapter.forRoot(...) } })`).
8
+ * - `BullMqExecutionStrategy` is the strategy class (also
9
+ * exported individually so callers can inject it directly for
10
+ * inspection / health checks).
11
+ * - `BULLMQ_MODULE_OPTIONS` is the DI token for the resolved
12
+ * module options bag.
13
+ * - the connection helpers are re-exported so callers can build
14
+ * a fully-resolved `BullMqResolvedConnection` from a partial
15
+ * `BullMqConnectionOptions` without importing the internal
16
+ * `connection.ts` file.
17
+ *
18
+ * The legacy `BullmqBatchModule` (with `forRoot` / `forRootAsync`
19
+ * static methods) has been replaced by `BullmqAdapter`. Internal
20
+ * modules (`./bullmq-execution-strategy`, `./module-options`,
21
+ * `./connection`, `./adapters/bullmq.module`) are implementation
22
+ * details and may move between releases.
23
+ */ "use strict";
24
+ Object.defineProperty(exports, "__esModule", {
25
+ value: true
26
+ });
27
+ _export_star(require("./connection"), exports);
28
+ _export_star(require("./module-options"), exports);
29
+ _export_star(require("./bullmq-execution-strategy"), exports);
30
+ _export_star(require("./bullmq-schedule.service"), exports);
31
+ _export_star(require("./adapters"), exports);
32
+ function _export_star(from, to) {
33
+ Object.keys(from).forEach(function(k) {
34
+ if (k !== "default" && !Object.prototype.hasOwnProperty.call(to, k)) {
35
+ Object.defineProperty(to, k, {
36
+ enumerable: true,
37
+ get: function() {
38
+ return from[k];
39
+ }
40
+ });
41
+ }
42
+ });
43
+ return from;
44
+ }
45
+
46
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/index.ts"],"sourcesContent":["/**\n * Public API barrel for `@nest-batch/bullmq`.\n *\n * The host application should depend exclusively on this barrel:\n * - `BullmqAdapter` is the new factory-pattern transport\n * adapter (use with `NestBatchModule.forRoot({ adapters:\n * { transport: BullmqAdapter.forRoot(...) } })`).\n * - `BullMqExecutionStrategy` is the strategy class (also\n * exported individually so callers can inject it directly for\n * inspection / health checks).\n * - `BULLMQ_MODULE_OPTIONS` is the DI token for the resolved\n * module options bag.\n * - the connection helpers are re-exported so callers can build\n * a fully-resolved `BullMqResolvedConnection` from a partial\n * `BullMqConnectionOptions` without importing the internal\n * `connection.ts` file.\n *\n * The legacy `BullmqBatchModule` (with `forRoot` / `forRootAsync`\n * static methods) has been replaced by `BullmqAdapter`. Internal\n * modules (`./bullmq-execution-strategy`, `./module-options`,\n * `./connection`, `./adapters/bullmq.module`) are implementation\n * details and may move between releases.\n */\nexport * from './connection';\nexport * from './module-options';\nexport * from './bullmq-execution-strategy';\nexport * from './bullmq-schedule.service';\nexport * from './adapters';\n"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;CAsBC;;;;qBACa;qBACA;qBACA;qBACA;qBACA"}
@@ -0,0 +1,68 @@
1
+ import type { BullMqConnectionOptions, BullMqResolvedConnection } from './connection';
2
+ /**
3
+ * Public options bag for `BullmqBatchModule.forRoot()` and
4
+ * `forRootAsync()`.
5
+ *
6
+ * The fields cover the connections the package needs to wire up:
7
+ * - `connection` — the BullMQ `Queue` / `Worker` / `QueueEvents`
8
+ * share. T17 stores it under `BULLMQ_MODULE_OPTIONS`; T18 splits
9
+ * the role-specific tuning (worker `maxRetriesPerRequest: null`
10
+ * + `enableReadyCheck: false`; producer `enableOfflineQueue:
11
+ * false`) onto this same connection record and derives the
12
+ * per-role client from it.
13
+ * - `autoStartWorker` — whether the module should also start a
14
+ * BullMQ `Worker` on `onApplicationBootstrap`. Defaults to
15
+ * `false` so a launcher-only deployment does not accidentally
16
+ * consume Redis. T18 wires the actual worker construction.
17
+ *
18
+ * The interface extends `BullMqConnectionOptions` via composition
19
+ * (not `extends`) so the field can be `undefined` at the top level
20
+ * (the module applies its own defaults via `resolveBullMqConnection`)
21
+ * and the resolved form (with defaults filled in) is what gets
22
+ * handed to the strategy.
23
+ */
24
+ export interface BullMqModuleOptions {
25
+ /**
26
+ * Redis connection settings shared by the BullMQ `Queue`,
27
+ * `Worker`, and `QueueEvents` clients this package builds.
28
+ * Optional — defaults are filled in by
29
+ * `resolveBullMqConnection()`.
30
+ */
31
+ connection?: BullMqConnectionOptions;
32
+ /**
33
+ * Whether the module should also spin up a BullMQ `Worker` on
34
+ * `OnApplicationBootstrap`. Default: `false` (launcher-only).
35
+ * Reserved for T18 — the skeleton in T17 does not implement
36
+ * worker lifecycle.
37
+ */
38
+ autoStartWorker?: boolean;
39
+ /**
40
+ * Reserved for future per-adapter extension. Adapter packages
41
+ * (e.g. a future `@nest-batch/mikro-orm` companion) can read
42
+ * the full options bag through this field for cross-cutting
43
+ * config.
44
+ */
45
+ readonly [key: string]: unknown;
46
+ }
47
+ /**
48
+ * Token under which the resolved module options are registered.
49
+ *
50
+ * The strategy injects the options via this token so it can build
51
+ * the per-role BullMQ connection clients. The token is a
52
+ * package-scoped `Symbol.for` key (mirroring
53
+ * `@nest-batch/core/MODULE_OPTIONS_TOKEN`) so it is unique across
54
+ * the host process even if multiple `@nest-batch/bullmq` versions
55
+ * are loaded.
56
+ */
57
+ export declare const BULLMQ_MODULE_OPTIONS: symbol;
58
+ /**
59
+ * Type alias for the fully-resolved options bag. Used by
60
+ * `BullmqBatchModule.forRoot()` to freeze the resolved value under
61
+ * `BULLMQ_MODULE_OPTIONS` and by the strategy to type its injected
62
+ * dependency.
63
+ */
64
+ export interface ResolvedBullMqModuleOptions {
65
+ readonly connection: BullMqResolvedConnection;
66
+ readonly autoStartWorker: boolean;
67
+ }
68
+ //# sourceMappingURL=module-options.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module-options.d.ts","sourceRoot":"","sources":["../../src/module-options.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AAEtF;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;;;OAKG;IACH,UAAU,CAAC,EAAE,uBAAuB,CAAC;IAErC;;;;;OAKG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAE1B;;;;;OAKG;IACH,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACjC;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,qBAAqB,EAAE,MAEnC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,UAAU,EAAE,wBAAwB,CAAC;IAC9C,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;CACnC"}
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "BULLMQ_MODULE_OPTIONS", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return BULLMQ_MODULE_OPTIONS;
9
+ }
10
+ });
11
+ const BULLMQ_MODULE_OPTIONS = Symbol.for('@nest-batch/bullmq/MODULE_OPTIONS');
12
+
13
+ //# sourceMappingURL=module-options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/module-options.ts"],"sourcesContent":["import type { BullMqConnectionOptions, BullMqResolvedConnection } from './connection';\n\n/**\n * Public options bag for `BullmqBatchModule.forRoot()` and\n * `forRootAsync()`.\n *\n * The fields cover the connections the package needs to wire up:\n * - `connection` — the BullMQ `Queue` / `Worker` / `QueueEvents`\n * share. T17 stores it under `BULLMQ_MODULE_OPTIONS`; T18 splits\n * the role-specific tuning (worker `maxRetriesPerRequest: null`\n * + `enableReadyCheck: false`; producer `enableOfflineQueue:\n * false`) onto this same connection record and derives the\n * per-role client from it.\n * - `autoStartWorker` — whether the module should also start a\n * BullMQ `Worker` on `onApplicationBootstrap`. Defaults to\n * `false` so a launcher-only deployment does not accidentally\n * consume Redis. T18 wires the actual worker construction.\n *\n * The interface extends `BullMqConnectionOptions` via composition\n * (not `extends`) so the field can be `undefined` at the top level\n * (the module applies its own defaults via `resolveBullMqConnection`)\n * and the resolved form (with defaults filled in) is what gets\n * handed to the strategy.\n */\nexport interface BullMqModuleOptions {\n /**\n * Redis connection settings shared by the BullMQ `Queue`,\n * `Worker`, and `QueueEvents` clients this package builds.\n * Optional — defaults are filled in by\n * `resolveBullMqConnection()`.\n */\n connection?: BullMqConnectionOptions;\n\n /**\n * Whether the module should also spin up a BullMQ `Worker` on\n * `OnApplicationBootstrap`. Default: `false` (launcher-only).\n * Reserved for T18 — the skeleton in T17 does not implement\n * worker lifecycle.\n */\n autoStartWorker?: boolean;\n\n /**\n * Reserved for future per-adapter extension. Adapter packages\n * (e.g. a future `@nest-batch/mikro-orm` companion) can read\n * the full options bag through this field for cross-cutting\n * config.\n */\n readonly [key: string]: unknown;\n}\n\n/**\n * Token under which the resolved module options are registered.\n *\n * The strategy injects the options via this token so it can build\n * the per-role BullMQ connection clients. The token is a\n * package-scoped `Symbol.for` key (mirroring\n * `@nest-batch/core/MODULE_OPTIONS_TOKEN`) so it is unique across\n * the host process even if multiple `@nest-batch/bullmq` versions\n * are loaded.\n */\nexport const BULLMQ_MODULE_OPTIONS: symbol = Symbol.for(\n '@nest-batch/bullmq/MODULE_OPTIONS',\n);\n\n/**\n * Type alias for the fully-resolved options bag. Used by\n * `BullmqBatchModule.forRoot()` to freeze the resolved value under\n * `BULLMQ_MODULE_OPTIONS` and by the strategy to type its injected\n * dependency.\n */\nexport interface ResolvedBullMqModuleOptions {\n readonly connection: BullMqResolvedConnection;\n readonly autoStartWorker: boolean;\n}\n"],"names":["BULLMQ_MODULE_OPTIONS","Symbol","for"],"mappings":";;;;+BA4DaA;;;eAAAA;;;AAAN,MAAMA,wBAAgCC,OAAOC,GAAG,CACrD"}
package/package.json ADDED
@@ -0,0 +1,71 @@
1
+ {
2
+ "name": "@nest-batch/bullmq",
3
+ "version": "0.2.0",
4
+ "description": "BullMQ runtime adapter for @nest-batch/core — runs batch jobs and partitioned steps as BullMQ queues and workers.",
5
+ "license": "MIT",
6
+ "author": "easdkr",
7
+ "homepage": "https://github.com/easdkr/nest-batch/tree/main/packages/bullmq#readme",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/easdkr/nest-batch.git",
11
+ "directory": "packages/bullmq"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/easdkr/nest-batch/issues"
15
+ },
16
+ "keywords": [
17
+ "nestjs",
18
+ "batch",
19
+ "bullmq",
20
+ "queue",
21
+ "worker"
22
+ ],
23
+ "main": "dist/src/index.js",
24
+ "types": "dist/src/index.d.ts",
25
+ "files": [
26
+ "dist/src",
27
+ "src",
28
+ "README.md"
29
+ ],
30
+ "publishConfig": {
31
+ "access": "public"
32
+ },
33
+ "peerDependencies": {
34
+ "@nestjs/common": "^10 || ^11",
35
+ "@nestjs/core": "^10 || ^11",
36
+ "bullmq": "^5.0.0",
37
+ "@nest-batch/core": "^0.2.0"
38
+ },
39
+ "peerDependenciesMeta": {
40
+ "@nest-batch/core": {
41
+ "optional": false
42
+ },
43
+ "@nestjs/common": {
44
+ "optional": false
45
+ },
46
+ "@nestjs/core": {
47
+ "optional": false
48
+ },
49
+ "bullmq": {
50
+ "optional": false
51
+ }
52
+ },
53
+ "devDependencies": {
54
+ "@nestjs/common": "^11.0.0",
55
+ "@nestjs/core": "^11.0.0",
56
+ "@swc/cli": "^0.7.0",
57
+ "@swc/core": "^1.10.7",
58
+ "@types/node": "^22.0.0",
59
+ "bullmq": "^5.0.0",
60
+ "reflect-metadata": "^0.2.2",
61
+ "typescript": "^5.5.0",
62
+ "vitest": "^2.0.0",
63
+ "@nest-batch/core": "0.2.0"
64
+ },
65
+ "scripts": {
66
+ "build": "swc src -d dist --config-file ../../.swcrc && tsc --emitDeclarationOnly -p tsconfig.build.json",
67
+ "test": "vitest run",
68
+ "test:watch": "vitest",
69
+ "typecheck": "tsc --noEmit"
70
+ }
71
+ }
@@ -0,0 +1,346 @@
1
+ import { Module, type DynamicModule, type Provider } from '@nestjs/common';
2
+ import { EXECUTION_STRATEGY, type BatchAdapter } from '@nest-batch/core';
3
+
4
+ import { BullMqExecutionStrategy } from '../bullmq-execution-strategy';
5
+ import { BullmqRuntimeService } from '../bullmq-runtime.service';
6
+ import { BullmqScheduleService } from '../bullmq-schedule.service';
7
+ import { resolveBullMqConnection } from '../connection';
8
+ import {
9
+ BULLMQ_MODULE_OPTIONS,
10
+ type BullMqModuleOptions,
11
+ type ResolvedBullMqModuleOptions,
12
+ } from '../module-options';
13
+
14
+ /**
15
+ * Empty Nest module class that owns the BullMQ transport's
16
+ * provider graph.
17
+ *
18
+ * Mirrors `InProcessModule` in `@nest-batch/core/src/adapters/
19
+ * in-process.adapter.ts`: the class has no body on purpose. It is
20
+ * purely a `DynamicModule` carrier — Nest's module system requires
21
+ * *some* class to identify the module, and the empty class is the
22
+ * minimum possible surface (no decorators, no lifecycle hooks, no
23
+ * metadata). All real behaviour lives on the providers.
24
+ */
25
+ @Module({})
26
+ export class BullmqModule {}
27
+
28
+ /**
29
+ * Sentinel token for the async-options factory chain.
30
+ *
31
+ * `forRootAsync` registers a `useFactory` provider under this token
32
+ * that runs the user's factory, then a second provider
33
+ * (`BULLMQ_MODULE_OPTIONS`) that depends on it and freezes the
34
+ * resolved options. A duplicate `provide` for
35
+ * `BULLMQ_MODULE_OPTIONS` would crash Nest's container, so the
36
+ * chain uses this private symbol as the intermediate step.
37
+ *
38
+ * Mirrors the `Symbol.for('@nest-batch/bullmq/OPTIONS_FACTORY')`
39
+ * used by the legacy `BullmqBatchModule.forRootAsync()` — the
40
+ * `Symbol.for` key is process-scoped and stable across module
41
+ * versions, so any host still wiring up the legacy class is not
42
+ * affected.
43
+ */
44
+ const OPTIONS_FACTORY: symbol = Symbol.for('@nest-batch/bullmq/OPTIONS_FACTORY');
45
+
46
+ /**
47
+ * The list of exports the BullMQ adapter's `DynamicModule` exposes
48
+ * to the host application.
49
+ *
50
+ * Centralised so the sync and async paths stay in lockstep — any
51
+ * future addition (e.g. a `BullmqScheduler` controller) only needs
52
+ * to be added here.
53
+ *
54
+ * The set mirrors the legacy `BullmqBatchModule.forRoot()` exports
55
+ * (the `forRootAsync()` legacy path was missing
56
+ * `BullmqRuntimeService` from `exports` — that omission is fixed
57
+ * here, both paths now export the same five entries).
58
+ *
59
+ * - `EXECUTION_STRATEGY` — the DI token, so host code (e.g. a
60
+ * `/healthz` endpoint) can resolve the strategy class via
61
+ * `moduleRef.get(EXECUTION_STRATEGY)`.
62
+ * - `BULLMQ_MODULE_OPTIONS` — the resolved connection / worker
63
+ * config bag, for inspection and (future) for per-role client
64
+ * builders.
65
+ * - `BullMqExecutionStrategy` — the concrete class, for type-
66
+ * strict consumers that prefer class injection.
67
+ * - `BullmqRuntimeService` — the runtime that owns the
68
+ * `Queue` / `Worker` / `QueueEvents` lifecycle.
69
+ * - `BullmqScheduleService` — the runtime that owns the
70
+ * `@BatchScheduled` cron-to-BullMQ translation.
71
+ */
72
+ const ADAPTER_EXPORTS: ReadonlyArray<symbol | typeof BullMqExecutionStrategy | typeof BullmqRuntimeService | typeof BullmqScheduleService> = [
73
+ EXECUTION_STRATEGY,
74
+ BULLMQ_MODULE_OPTIONS,
75
+ BullMqExecutionStrategy,
76
+ BullmqRuntimeService,
77
+ BullmqScheduleService,
78
+ ];
79
+
80
+ /**
81
+ * `BullmqAdapter` — the transport adapter for `@nest-batch/bullmq`
82
+ * used by the new factory-pattern
83
+ * `NestBatchModule.forRoot({ adapters: { transport, ... } })` API.
84
+ *
85
+ * Overrides the default `EXECUTION_STRATEGY` token with a BullMQ-
86
+ * backed `IExecutionStrategy` (`BullMqExecutionStrategy`) and wires
87
+ * the runtime services that own the BullMQ client lifecycle
88
+ * (`BullmqRuntimeService` for step enqueue + worker, plus
89
+ * `BullmqScheduleService` for `@BatchScheduled` cron entries).
90
+ *
91
+ * Two static methods:
92
+ *
93
+ * - `forRoot(options)` — synchronous configuration. The
94
+ * connection options are resolved up-front and frozen under
95
+ * the `BULLMQ_MODULE_OPTIONS` token. Use this when the Redis
96
+ * host is known at module composition time.
97
+ *
98
+ * - `forRootAsync({ imports, inject, useFactory })` — async
99
+ * configuration. The factory is registered as a sentinel
100
+ * provider; the `BULLMQ_MODULE_OPTIONS` provider depends on
101
+ * it. Use this when the connection comes from a config
102
+ * service or another async source.
103
+ *
104
+ * The two methods share the same provider list via the
105
+ * `buildStaticProviders` helper — the only difference is whether
106
+ * `BULLMQ_MODULE_OPTIONS` is a value provider (sync) or a factory
107
+ * provider that resolves the user's `useFactory` result (async).
108
+ *
109
+ * `globalProviders` is intentionally omitted. The recommended path
110
+ * is to expose host-visible providers via the module's own
111
+ * `exports` (see `ADAPTER_EXPORTS` above) — the `BatchAdapter`
112
+ * interface's `globalProviders` field is for runtime classes the
113
+ * adapter's own module needs but that core itself would also
114
+ * re-export. `JobLauncher` (registered by `NestBatchModule`, not
115
+ * by this adapter) injects the strategy by the `EXECUTION_STRATEGY`
116
+ * token, which is already in `exports`, so the resolution chain
117
+ * works without core having to know which adapter is active.
118
+ *
119
+ * @example
120
+ * ```ts
121
+ * // Synchronous wiring (connection known at module-build time)
122
+ * import { Module } from '@nestjs/common';
123
+ * import { NestBatchModule, InProcessAdapter } from '@nest-batch/core';
124
+ * import { MikroOrmAdapter } from '@nest-batch/mikro-orm';
125
+ * import { BullmqAdapter } from '@nest-batch/bullmq';
126
+ *
127
+ * @Module({
128
+ * imports: [
129
+ * NestBatchModule.forRoot({
130
+ * adapters: {
131
+ * persistence: MikroOrmAdapter,
132
+ * transport: BullmqAdapter.forRoot({
133
+ * connection: {
134
+ * host: process.env.REDIS_HOST,
135
+ * port: Number(process.env.REDIS_PORT),
136
+ * keyPrefix: 'nest-batch:',
137
+ * },
138
+ * autoStartWorker: true,
139
+ * }),
140
+ * },
141
+ * }),
142
+ * ],
143
+ * })
144
+ * class AppModule {}
145
+ * ```
146
+ *
147
+ * @example
148
+ * ```ts
149
+ * // Async wiring (connection sourced from ConfigService)
150
+ * BullmqAdapter.forRootAsync({
151
+ * imports: [ConfigModule],
152
+ * inject: [ConfigService],
153
+ * useFactory: (cfg: ConfigService) => ({
154
+ * connection: {
155
+ * host: cfg.get<string>('redis.host'),
156
+ * port: cfg.get<number>('redis.port'),
157
+ * password: cfg.get<string>('redis.password'),
158
+ * },
159
+ * }),
160
+ * });
161
+ * ```
162
+ */
163
+ export class BullmqAdapter {
164
+ /**
165
+ * Synchronous configuration.
166
+ *
167
+ * Resolves the connection options up-front (`resolveBullMqConnection`
168
+ * fills in defaults + freezes the bag) and emits a
169
+ * `BatchAdapter` whose `module` is a `global: true`
170
+ * `DynamicModule` registering the strategy class, the runtime
171
+ * services, the `EXECUTION_STRATEGY` binding, and the resolved
172
+ * options as a value provider under `BULLMQ_MODULE_OPTIONS`.
173
+ *
174
+ * No options object is required: the module accepts an empty
175
+ * `{}` and applies all defaults (host `127.0.0.1`, port `6379`,
176
+ * keyPrefix `nest-batch:`, no auth, no TLS, `autoStartWorker:
177
+ * false`).
178
+ *
179
+ * @param options - Connection + worker config. All fields optional.
180
+ * @returns A `BatchAdapter` with `name: 'bullmq'` and the
181
+ * `BullmqModule` dynamic module.
182
+ */
183
+ static forRoot(options: BullMqModuleOptions = {}): BatchAdapter {
184
+ const resolved: ResolvedBullMqModuleOptions = Object.freeze({
185
+ connection: resolveBullMqConnection(options.connection),
186
+ autoStartWorker: options.autoStartWorker ?? false,
187
+ });
188
+ return {
189
+ name: 'bullmq',
190
+ module: buildBullmqDynamicModule({
191
+ providers: buildStaticProviders(resolved),
192
+ }),
193
+ };
194
+ }
195
+
196
+ /**
197
+ * Async configuration — useful when the Redis connection comes
198
+ * from a config service or another async provider.
199
+ *
200
+ * The shape mirrors `NestBatchModule.forRootAsync`:
201
+ * - `imports` is forwarded to the resulting
202
+ * `DynamicModule.imports` (so `ConfigModule` is available
203
+ * when the factory runs).
204
+ * - `inject` lists the providers the factory needs in its
205
+ * argument list.
206
+ * - `useFactory` resolves the options bag at module-build
207
+ * time. The factory's return value is treated as
208
+ * `BullMqModuleOptions` and is fed through
209
+ * `resolveBullMqConnection` by the `BULLMQ_MODULE_OPTIONS`
210
+ * factory provider (defaults applied, bag frozen).
211
+ *
212
+ * Implementation note: the factory is registered under the
213
+ * package-private `OPTIONS_FACTORY` sentinel token; the
214
+ * `BULLMQ_MODULE_OPTIONS` provider depends on it. This is the
215
+ * same chain the legacy `BullmqBatchModule.forRootAsync` used
216
+ * — the dynamic module is built off the static provider list,
217
+ * with the static `BULLMQ_MODULE_OPTIONS` value provider
218
+ * replaced by the async factory pair.
219
+ *
220
+ * @param asyncOptions - `{ imports, inject, useFactory }` bag.
221
+ * `useFactory` is required; `imports` and `inject` are
222
+ * optional and default to `[]`.
223
+ * @returns A `BatchAdapter` with `name: 'bullmq'` and the
224
+ * `BullmqModule` dynamic module (with `imports` wired).
225
+ */
226
+ static forRootAsync(asyncOptions: {
227
+ imports?: DynamicModule['imports'];
228
+ inject?: readonly unknown[];
229
+ useFactory: (
230
+ ...args: unknown[]
231
+ ) => Promise<BullMqModuleOptions> | BullMqModuleOptions;
232
+ }): BatchAdapter {
233
+ const factoryProvider: Provider = {
234
+ provide: OPTIONS_FACTORY,
235
+ useFactory: asyncOptions.useFactory as (...args: unknown[]) => unknown,
236
+ inject: [...(asyncOptions.inject ?? [])] as Array<string | symbol | Function>,
237
+ };
238
+
239
+ const mergedOptionsProvider: Provider = {
240
+ provide: BULLMQ_MODULE_OPTIONS,
241
+ useFactory: (
242
+ fromFactory: BullMqModuleOptions | undefined,
243
+ ): ResolvedBullMqModuleOptions => {
244
+ return Object.freeze({
245
+ connection: resolveBullMqConnection(fromFactory?.connection),
246
+ autoStartWorker: fromFactory?.autoStartWorker ?? false,
247
+ });
248
+ },
249
+ inject: [OPTIONS_FACTORY],
250
+ };
251
+
252
+ // The static provider list is the same as `forRoot` except
253
+ // the value provider for `BULLMQ_MODULE_OPTIONS` is replaced
254
+ // with the async factory above (a duplicate `provide` would
255
+ // crash Nest's container). We seed `buildStaticProviders`
256
+ // with a placeholder resolved bag (its value is discarded
257
+ // — the async provider overrides the slot) so the function
258
+ // can be the single source of truth for the rest of the
259
+ // provider list.
260
+ const baseProviders = buildStaticProviders(
261
+ Object.freeze({
262
+ connection: resolveBullMqConnection(undefined),
263
+ autoStartWorker: false,
264
+ }),
265
+ );
266
+ const filtered = baseProviders.filter(
267
+ (p) =>
268
+ !(
269
+ typeof p === 'object' &&
270
+ p !== null &&
271
+ 'provide' in p &&
272
+ (p as { provide: unknown }).provide === BULLMQ_MODULE_OPTIONS
273
+ ),
274
+ );
275
+
276
+ return {
277
+ name: 'bullmq',
278
+ module: buildBullmqDynamicModule({
279
+ providers: [factoryProvider, mergedOptionsProvider, ...filtered],
280
+ imports: asyncOptions.imports,
281
+ }),
282
+ };
283
+ }
284
+ }
285
+
286
+ /**
287
+ * Build the static provider list shared by `forRoot()` and
288
+ * `forRootAsync()`.
289
+ *
290
+ * Centralised so the sync and async paths declare the same set of
291
+ * providers (and any future addition — e.g. a per-role client
292
+ * builder — only needs to be added here).
293
+ *
294
+ * The async path then filters the `BULLMQ_MODULE_OPTIONS` entry
295
+ * out of the returned array and replaces it with the factory
296
+ * pair. Everything else is shared.
297
+ */
298
+ function buildStaticProviders(
299
+ resolved: ResolvedBullMqModuleOptions,
300
+ ): Provider[] {
301
+ return [
302
+ BullMqExecutionStrategy,
303
+ BullmqRuntimeService,
304
+ BullmqScheduleService,
305
+ {
306
+ provide: EXECUTION_STRATEGY,
307
+ useExisting: BullMqExecutionStrategy,
308
+ },
309
+ {
310
+ provide: BULLMQ_MODULE_OPTIONS,
311
+ useValue: resolved,
312
+ },
313
+ ];
314
+ }
315
+
316
+ /**
317
+ * Build the `DynamicModule` payload for the BullMQ adapter.
318
+ *
319
+ * Extracted from the two factory methods so the provider /
320
+ * export / global-true shape lives in one place. NestJS's
321
+ * `validateExportedProvider` check rejects an `exports` entry
322
+ * that is not in `providers` (or imported), so adding to one
323
+ * without the other is a silent runtime failure — keeping the
324
+ * two arrays synchronised by construction is the safest
325
+ * pattern.
326
+ *
327
+ * `imports` is optional: `forRoot` does not need any (it has no
328
+ * async providers), `forRootAsync` forwards the user's
329
+ * `imports` (typically `ConfigModule`) so the factory's
330
+ * `inject` targets resolve.
331
+ */
332
+ function buildBullmqDynamicModule(args: {
333
+ providers: Provider[];
334
+ imports?: DynamicModule['imports'];
335
+ }): DynamicModule {
336
+ const module: DynamicModule = {
337
+ module: BullmqModule,
338
+ global: true,
339
+ providers: args.providers,
340
+ exports: [...ADAPTER_EXPORTS],
341
+ };
342
+ if (args.imports !== undefined) {
343
+ return { ...module, imports: [...args.imports] };
344
+ }
345
+ return module;
346
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Public surface for the BullMQ adapter factory.
3
+ *
4
+ * The `BullmqAdapter` is the only entry point the host should
5
+ * depend on for the new factory-pattern API. The internal module
6
+ * class (`BullmqModule`) is exported alongside it as a NestJS
7
+ * identifier — it is safe to reference from test code that needs
8
+ * to assert on the `module` field of the adapter value, but it
9
+ * has no runtime surface beyond the empty class body.
10
+ */
11
+ export * from './bullmq.adapter';