@bull-board/nestjs 8.0.2 → 8.1.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.
package/README.md CHANGED
@@ -144,10 +144,32 @@ export class FeatureModule {}
144
144
 
145
145
  The `forFeature` method registers the given queues to the bull-board instance.
146
146
  The following options are available.
147
- - `name` the queue name to register
147
+ - `name` the queue name to resolve from the Nest DI container.
148
+ - `queue` a queue instance to register directly, instead of resolving it by `name`.
148
149
  - `adapter` either `BullAdapter` or `BullMQAdapter` depending on which package you use.
149
150
  - `options` queue adapter options as found in the bull-board package, such as `readOnlyMode`, `description` etc.
150
151
 
152
+ Provide either `name` or `queue`.
153
+
154
+ ### Registering queue instances directly
155
+
156
+ `@nestjs/bullmq` generates the DI token for a queue from its `name` only, ignoring the
157
+ `prefix`. Two queues that share a name but use different prefixes therefore collapse onto a
158
+ single DI token, and a `name`-based lookup cannot tell them apart. Pass the queue instances
159
+ directly via `queue` to register them as distinct board entries:
160
+
161
+ ```typescript
162
+ @Module({
163
+ imports: [
164
+ BullBoardModule.forFeature(
165
+ { queue: emailsTenantA, adapter: BullMQAdapter, options: { prefix: 'tenant-a:' } },
166
+ { queue: emailsTenantB, adapter: BullMQAdapter, options: { prefix: 'tenant-b:' } },
167
+ ),
168
+ ],
169
+ })
170
+ export class FeatureModule {}
171
+ ```
172
+
151
173
  ## Using the bull-board instance in your controllers and/or services.
152
174
  The created bull-board instance is available via the `@InjectBullBoard()` decorator.
153
175
  For example in a controller:
@@ -24,8 +24,9 @@ let BullBoardFeatureModule = class BullBoardFeatureModule {
24
24
  this.board = board;
25
25
  }
26
26
  onModuleInit() {
27
+ var _a;
27
28
  for (const queueOption of this.queues) {
28
- const queue = this.moduleRef.get((0, bull_shared_1.getQueueToken)(queueOption.name), { strict: false });
29
+ const queue = (_a = queueOption.queue) !== null && _a !== void 0 ? _a : this.moduleRef.get((0, bull_shared_1.getQueueToken)(queueOption.name), { strict: false });
29
30
  const queueAdapter = new queueOption.adapter(queue, queueOption.options);
30
31
  this.board.addQueue(queueAdapter);
31
32
  }
@@ -16,13 +16,29 @@ export type BullBoardModuleAsyncOptions = {
16
16
  imports?: ModuleMetadata['imports'];
17
17
  inject?: Array<InjectionToken | OptionalFactoryDependency>;
18
18
  };
19
- export type BullBoardQueueOptions = {
20
- name: string;
19
+ type BullBoardQueueCommonOptions = {
21
20
  adapter: {
22
21
  new (queue: any, options?: Partial<QueueAdapterOptions>): BaseAdapter;
23
22
  };
24
23
  options?: Partial<QueueAdapterOptions>;
25
24
  };
25
+ export type BullBoardQueueOptions = BullBoardQueueCommonOptions & ({
26
+ /**
27
+ * The queue name to resolve from the Nest DI container (via `getQueueToken`).
28
+ */
29
+ name: string;
30
+ queue?: undefined;
31
+ } | {
32
+ /**
33
+ * A queue instance to register directly, bypassing the DI container lookup.
34
+ *
35
+ * Use this when the name-based lookup cannot disambiguate the queue — e.g. two
36
+ * queues sharing the same name but using different prefixes, which `@nestjs/bullmq`
37
+ * collapses onto a single DI token.
38
+ */
39
+ queue: unknown;
40
+ name?: string;
41
+ });
26
42
  export type BullBoardServerAdapter = IServerAdapter & {
27
43
  setBasePath(path: string): any;
28
44
  };
@@ -32,3 +48,4 @@ export type BullBoardFastifyAdapter = BullBoardServerAdapter & {
32
48
  export type BullBoardExpressAdapter = BullBoardServerAdapter & {
33
49
  getRouter(): any;
34
50
  };
51
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bull-board/nestjs",
3
- "version": "8.0.2",
3
+ "version": "8.1.0",
4
4
  "description": "A NestJS module for Bull-Board dashboard.",
5
5
  "keywords": [
6
6
  "bull",
@@ -28,9 +28,9 @@
28
28
  "test": "jest"
29
29
  },
30
30
  "devDependencies": {
31
- "@bull-board/api": "^8.0.2",
32
- "@bull-board/express": "^8.0.2",
33
- "@bull-board/fastify": "^8.0.2",
31
+ "@bull-board/api": "^8.1.0",
32
+ "@bull-board/express": "^8.1.0",
33
+ "@bull-board/fastify": "^8.1.0",
34
34
  "@bull-board/test-utils": "workspace:*",
35
35
  "@nestjs/bull-shared": "^11.0.4",
36
36
  "@nestjs/bullmq": "^11.0.4",
@@ -49,7 +49,7 @@
49
49
  "typescript": "^5.9.3"
50
50
  },
51
51
  "peerDependencies": {
52
- "@bull-board/api": "^8.0.2",
52
+ "@bull-board/api": "^8.1.0",
53
53
  "@nestjs/bull-shared": "^10.0.0 || ^11.0.0",
54
54
  "@nestjs/common": "^9.0.0 || ^10.0.0 || ^11.0.0",
55
55
  "@nestjs/core": "^9.0.0 || ^10.0.0 || ^11.0.0",
@@ -15,7 +15,9 @@ export class BullBoardFeatureModule implements OnModuleInit {
15
15
 
16
16
  onModuleInit(): any {
17
17
  for (const queueOption of this.queues) {
18
- const queue = this.moduleRef.get<Queue>(getQueueToken(queueOption.name), { strict: false });
18
+ const queue =
19
+ queueOption.queue ??
20
+ this.moduleRef.get<Queue>(getQueueToken(queueOption.name), { strict: false });
19
21
  const queueAdapter = new queueOption.adapter(queue, queueOption.options);
20
22
  this.board.addQueue(queueAdapter);
21
23
  }
@@ -22,12 +22,33 @@ export type BullBoardModuleAsyncOptions = {
22
22
  inject?: Array<InjectionToken | OptionalFactoryDependency>;
23
23
  };
24
24
 
25
- export type BullBoardQueueOptions = {
26
- name: string;
25
+ type BullBoardQueueCommonOptions = {
27
26
  adapter: { new (queue: any, options?: Partial<QueueAdapterOptions>): BaseAdapter };
28
27
  options?: Partial<QueueAdapterOptions>;
29
28
  };
30
29
 
30
+ export type BullBoardQueueOptions = BullBoardQueueCommonOptions &
31
+ (
32
+ | {
33
+ /**
34
+ * The queue name to resolve from the Nest DI container (via `getQueueToken`).
35
+ */
36
+ name: string;
37
+ queue?: undefined;
38
+ }
39
+ | {
40
+ /**
41
+ * A queue instance to register directly, bypassing the DI container lookup.
42
+ *
43
+ * Use this when the name-based lookup cannot disambiguate the queue — e.g. two
44
+ * queues sharing the same name but using different prefixes, which `@nestjs/bullmq`
45
+ * collapses onto a single DI token.
46
+ */
47
+ queue: unknown;
48
+ name?: string;
49
+ }
50
+ );
51
+
31
52
  //create our own types with the needed functions, so we don't need to include express/fastify libraries here.
32
53
  export type BullBoardServerAdapter = IServerAdapter & { setBasePath(path: string): any };
33
54
  export type BullBoardFastifyAdapter = BullBoardServerAdapter & { registerPlugin(): any };
@@ -0,0 +1,87 @@
1
+ import 'reflect-metadata';
2
+ import { BullMQAdapter } from '@bull-board/api/bullMQAdapter';
3
+ import { ExpressAdapter as BullBoardExpressAdapter } from '@bull-board/express';
4
+ import { uiFixtureBasePath } from '@bull-board/test-utils';
5
+ import { BullModule } from '@nestjs/bullmq';
6
+ import { INestApplication, Module } from '@nestjs/common';
7
+ import { NestFactory } from '@nestjs/core';
8
+ import { ExpressAdapter } from '@nestjs/platform-express';
9
+ import { Queue } from 'bullmq';
10
+ import request from 'supertest';
11
+ import { BullBoardModule } from '../src';
12
+
13
+ const connection = {
14
+ host: process.env.REDIS_HOST || 'localhost',
15
+ port: +(process.env.REDIS_PORT || 6379),
16
+ };
17
+
18
+ describe('forFeature with queue instances (same name, different prefix)', () => {
19
+ const queues: Queue[] = [];
20
+ let app: INestApplication | undefined;
21
+
22
+ afterEach(async () => {
23
+ if (app) {
24
+ await app.close();
25
+ app = undefined;
26
+ }
27
+ await Promise.all(queues.map((queue) => queue.close()));
28
+ queues.length = 0;
29
+ });
30
+
31
+ it('registers both queues as distinct board entries', async () => {
32
+ const emailsA = new Queue('emails', { connection, prefix: 'tenant-a' });
33
+ const emailsB = new Queue('emails', { connection, prefix: 'tenant-b' });
34
+ queues.push(emailsA, emailsB);
35
+
36
+ @Module({
37
+ imports: [
38
+ BullBoardModule.forRoot({
39
+ route: '/queues',
40
+ adapter: BullBoardExpressAdapter,
41
+ boardOptions: { uiBasePath: uiFixtureBasePath },
42
+ }),
43
+ BullBoardModule.forFeature(
44
+ { queue: emailsA, adapter: BullMQAdapter, options: { prefix: 'tenant-a:' } },
45
+ { queue: emailsB, adapter: BullMQAdapter, options: { prefix: 'tenant-b:' } }
46
+ ),
47
+ ],
48
+ })
49
+ class AppModule {}
50
+
51
+ app = await NestFactory.create(AppModule, new ExpressAdapter(), { logger: false });
52
+ await app.init();
53
+
54
+ const res = await request(app.getHttpServer()).get('/queues/api/queues').expect(200);
55
+ const names = (JSON.parse(res.text).queues as Array<{ name: string }>)
56
+ .map((queue) => queue.name)
57
+ .sort();
58
+
59
+ expect(names).toEqual(['tenant-a:emails', 'tenant-b:emails']);
60
+ });
61
+
62
+ it('still resolves a queue from the DI container when only a name is given', async () => {
63
+ @Module({
64
+ imports: [
65
+ BullModule.forRoot({ connection }),
66
+ BullModule.registerQueue({ name: 'reports' }),
67
+ BullBoardModule.forRoot({
68
+ route: '/queues',
69
+ adapter: BullBoardExpressAdapter,
70
+ boardOptions: { uiBasePath: uiFixtureBasePath },
71
+ }),
72
+ BullBoardModule.forFeature({ name: 'reports', adapter: BullMQAdapter }),
73
+ ],
74
+ })
75
+ class AppModule {}
76
+
77
+ app = await NestFactory.create(AppModule, new ExpressAdapter(), { logger: false });
78
+ await app.init();
79
+
80
+ const res = await request(app.getHttpServer()).get('/queues/api/queues').expect(200);
81
+ const names = (JSON.parse(res.text).queues as Array<{ name: string }>).map(
82
+ (queue) => queue.name
83
+ );
84
+
85
+ expect(names).toEqual(['reports']);
86
+ });
87
+ });