@monque/tsed 0.1.0 → 1.0.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@monque/tsed",
3
- "version": "0.1.0",
3
+ "version": "1.0.0",
4
4
  "description": "Ts.ED integration for Monque - A robust, type-safe MongoDB job queue for TypeScript",
5
5
  "author": "Maurice de Bruyn <debruyn.maurice@gmail.com>",
6
6
  "repository": {
@@ -68,11 +68,11 @@
68
68
  "lint": "biome check ."
69
69
  },
70
70
  "peerDependencies": {
71
- "@monque/core": "^1.1.2",
71
+ "@monque/core": "^1.2.0",
72
72
  "@tsed/core": "^8.24.1",
73
73
  "@tsed/di": "^8.24.1",
74
74
  "@tsed/mongoose": "^8.24.1",
75
- "mongodb": "^7.0.0"
75
+ "mongodb": "catalog:"
76
76
  },
77
77
  "peerDependenciesMeta": {
78
78
  "@tsed/mongoose": {
@@ -81,18 +81,18 @@
81
81
  },
82
82
  "devDependencies": {
83
83
  "@monque/core": "workspace:*",
84
- "@testcontainers/mongodb": "^11.11.0",
85
- "@total-typescript/ts-reset": "^0.6.1",
84
+ "@testcontainers/mongodb": "catalog:",
85
+ "@total-typescript/ts-reset": "catalog:",
86
86
  "@tsed/core": "^8.24.1",
87
87
  "@tsed/di": "^8.24.1",
88
88
  "@tsed/mongoose": "^8.24.1",
89
89
  "@tsed/platform-http": "^8.24.1",
90
90
  "@tsed/testcontainers-mongo": "^8.24.1",
91
- "@types/bun": "^1.3.7",
92
- "@vitest/coverage-v8": "^4.0.18",
93
- "mongodb": "^7.0.0",
91
+ "@types/node": "catalog:",
92
+ "@vitest/coverage-v8": "catalog:",
93
+ "mongodb": "catalog:",
94
94
  "testcontainers": "^11.11.0",
95
- "tsdown": "^0.20.1",
96
- "vitest": "^4.0.18"
95
+ "tsdown": "catalog:",
96
+ "vitest": "catalog:"
97
97
  }
98
98
  }
@@ -4,6 +4,8 @@
4
4
  * Defines the configuration interface and TsED module augmentation.
5
5
  */
6
6
 
7
+ import { MonqueError } from '@monque/core';
8
+
7
9
  import type { MonqueTsedConfig } from './types.js';
8
10
 
9
11
  // ─────────────────────────────────────────────────────────────────────────────
@@ -47,13 +49,13 @@ export function validateDatabaseConfig(config: MonqueTsedConfig): void {
47
49
  const strategies = [config.db, config.dbFactory, config.dbToken].filter(Boolean);
48
50
 
49
51
  if (strategies.length === 0) {
50
- throw new Error(
52
+ throw new MonqueError(
51
53
  "MonqueTsedConfig requires exactly one of 'db', 'dbFactory', or 'dbToken' to be set",
52
54
  );
53
55
  }
54
56
 
55
57
  if (strategies.length > 1) {
56
- throw new Error(
58
+ throw new MonqueError(
57
59
  "MonqueTsedConfig accepts only one of 'db', 'dbFactory', or 'dbToken' - multiple were provided",
58
60
  );
59
61
  }
@@ -24,12 +24,12 @@ import type { Db } from 'mongodb';
24
24
  * export class Server {}
25
25
  * ```
26
26
  */
27
- export interface MonqueTsedConfig extends Omit<MonqueOptions, 'db'> {
27
+ export interface MonqueTsedConfig extends MonqueOptions {
28
28
  /**
29
29
  * Enable or disable the Monque module.
30
30
  *
31
31
  * When disabled:
32
- * - Workers are not registered
32
+ * - Jobs are not registered
33
33
  * - Lifecycle hooks are no-ops
34
34
  * - MonqueService throws on access
35
35
  *
@@ -111,4 +111,37 @@ export interface MonqueTsedConfig extends Omit<MonqueOptions, 'db'> {
111
111
  * @default "default"
112
112
  */
113
113
  mongooseConnectionId?: string;
114
+
115
+ /**
116
+ * Disable job processing on this instance.
117
+ *
118
+ * When true, the module will initialize the database connection (allowing you to
119
+ * enqueue jobs via MonqueService) but will NOT register jobs or start the
120
+ * polling loop. Useful for "Producer-only" nodes like API servers that only
121
+ * enqueue jobs but don't process them.
122
+ *
123
+ * @example
124
+ * ```typescript
125
+ * // API Server (Producer-only)
126
+ * @Configuration({
127
+ * monque: {
128
+ * dbFactory: async () => client.db('myapp'),
129
+ * disableJobProcessing: true, // Only enqueue, don't process
130
+ * }
131
+ * })
132
+ * export class ApiServer {}
133
+ *
134
+ * // Job Server (Consumer)
135
+ * @Configuration({
136
+ * monque: {
137
+ * dbFactory: async () => client.db('myapp'),
138
+ * // disableJobProcessing defaults to false - processes jobs
139
+ * }
140
+ * })
141
+ * export class JobServer {}
142
+ * ```
143
+ *
144
+ * @default false
145
+ */
146
+ disableJobProcessing?: boolean;
114
147
  }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Symbol used to store decorator metadata on class constructors.
3
3
  *
4
- * Used by @WorkerController, @Worker, and @Cron decorators to attach
4
+ * Used by @JobController, @Job, and @Cron decorators to attach
5
5
  * metadata that is later collected by MonqueModule during initialization.
6
6
  *
7
7
  * @example
@@ -3,14 +3,14 @@
3
3
  *
4
4
  * These constants are used to categorize providers registered with Ts.ED's
5
5
  * dependency injection container, enabling MonqueModule to discover and
6
- * register workers automatically.
6
+ * register jobs automatically.
7
7
  *
8
8
  * Note: Using string constants with `as const` instead of enums per
9
9
  * Constitution guidelines.
10
10
  */
11
11
  export const ProviderTypes = {
12
- /** Provider type for @WorkerController decorated classes */
13
- WORKER_CONTROLLER: 'monque:worker-controller',
12
+ /** Provider type for @JobController decorated classes */
13
+ JOB_CONTROLLER: 'monque:job-controller',
14
14
  /** Provider type for cron job handlers */
15
15
  CRON: 'monque:cron',
16
16
  } as const;
@@ -1,7 +1,7 @@
1
1
  import { Store } from '@tsed/core';
2
2
 
3
3
  import { MONQUE } from '@/constants';
4
- import type { CronDecoratorOptions, CronMetadata, WorkerStore } from '@/decorators/types.js';
4
+ import type { CronDecoratorOptions, CronMetadata, JobStore } from '@/decorators/types.js';
5
5
 
6
6
  /**
7
7
  * Method decorator that registers a method as a scheduled cron job.
@@ -11,8 +11,8 @@ import type { CronDecoratorOptions, CronMetadata, WorkerStore } from '@/decorato
11
11
  *
12
12
  * @example
13
13
  * ```typescript
14
- * @WorkerController()
15
- * class ReportWorkers {
14
+ * @JobController()
15
+ * class ReportJobs {
16
16
  * @Cron("@daily", { timezone: "UTC" })
17
17
  * async generateDailyReport() {
18
18
  * // ...
@@ -41,9 +41,9 @@ export function Cron(pattern: string, options?: CronDecoratorOptions): MethodDec
41
41
  const store = Store.from(targetConstructor);
42
42
 
43
43
  // Get or initialize the MONQUE store
44
- const existing = store.get<Partial<WorkerStore>>(MONQUE) || {
44
+ const existing = store.get<Partial<JobStore>>(MONQUE) || {
45
45
  type: 'controller',
46
- workers: [],
46
+ jobs: [],
47
47
  cronJobs: [],
48
48
  };
49
49
 
@@ -1,10 +1,10 @@
1
1
  export { Cron } from './cron.js';
2
+ export { Job } from './job.js';
3
+ export { JobController } from './job-controller.js';
2
4
  export type {
3
5
  CronDecoratorOptions,
4
6
  CronMetadata,
5
- WorkerDecoratorOptions,
6
- WorkerMetadata,
7
- WorkerStore,
7
+ JobDecoratorOptions,
8
+ JobMetadata,
9
+ JobStore,
8
10
  } from './types.js';
9
- export { Worker } from './worker.js';
10
- export { WorkerController } from './worker-controller.js';
@@ -1,17 +1,17 @@
1
1
  /**
2
- * @WorkerController class decorator
2
+ * @JobController class decorator
3
3
  *
4
- * Marks a class as containing worker methods and registers it with the Ts.ED DI container.
5
- * Workers in the class will have their job names prefixed with the namespace.
4
+ * Marks a class as containing job methods and registers it with the Ts.ED DI container.
5
+ * Jobs in the class will have their job names prefixed with the namespace.
6
6
  *
7
7
  * @param namespace - Optional prefix for all job names in this controller.
8
8
  * When set, job names become "{namespace}.{name}".
9
9
  *
10
10
  * @example
11
11
  * ```typescript
12
- * @WorkerController("email")
13
- * export class EmailWorkers {
14
- * @Worker("send") // Registered as "email.send"
12
+ * @JobController("email")
13
+ * export class EmailJobs {
14
+ * @Job("send") // Registered as "email.send"
15
15
  * async send(job: Job<EmailPayload>) { }
16
16
  * }
17
17
  * ```
@@ -21,35 +21,35 @@ import { Injectable } from '@tsed/di';
21
21
 
22
22
  import { MONQUE, ProviderTypes } from '@/constants';
23
23
 
24
- import type { WorkerStore } from './types.js';
24
+ import type { JobStore } from './types.js';
25
25
 
26
26
  /**
27
- * Class decorator that registers a class as a worker controller.
27
+ * Class decorator that registers a class as a job controller.
28
28
  *
29
29
  * @param namespace - Optional namespace prefix for job names
30
30
  */
31
- export function WorkerController(namespace?: string): ClassDecorator {
31
+ export function JobController(namespace?: string): ClassDecorator {
32
32
  return useDecorators(
33
33
  // Register as injectable with custom provider type
34
34
  Injectable({
35
- type: ProviderTypes.WORKER_CONTROLLER,
35
+ type: ProviderTypes.JOB_CONTROLLER,
36
36
  }),
37
37
  // Apply custom decorator to store metadata
38
38
  (target: object) => {
39
39
  const store = Store.from(target);
40
40
 
41
41
  // Get existing store or create new one
42
- const existing = store.get<Partial<WorkerStore>>(MONQUE) || {};
42
+ const existing = store.get<Partial<JobStore>>(MONQUE) || {};
43
43
 
44
44
  // Merge with new metadata, only include namespace if defined
45
- const workerStore: WorkerStore = {
45
+ const jobStore: JobStore = {
46
46
  type: 'controller',
47
47
  ...(namespace !== undefined && { namespace }),
48
- workers: existing.workers || [],
48
+ jobs: existing.jobs || [],
49
49
  cronJobs: existing.cronJobs || [],
50
50
  };
51
51
 
52
- store.set(MONQUE, workerStore);
52
+ store.set(MONQUE, jobStore);
53
53
  },
54
54
  );
55
55
  }
@@ -1,17 +1,17 @@
1
1
  /**
2
- * @Worker method decorator
2
+ * @Job method decorator
3
3
  *
4
4
  * Registers a method as a job handler. The method will be called when a job
5
5
  * with the matching name is picked up for processing.
6
6
  *
7
7
  * @param name - Job name (combined with controller namespace if present).
8
- * @param options - Worker configuration options.
8
+ * @param options - Job configuration options.
9
9
  *
10
10
  * @example
11
11
  * ```typescript
12
- * @WorkerController("notifications")
13
- * export class NotificationWorkers {
14
- * @Worker("push", { concurrency: 10 })
12
+ * @JobController("notifications")
13
+ * export class NotificationJobs {
14
+ * @Job("push", { concurrency: 10 })
15
15
  * async sendPush(job: Job<PushPayload>) {
16
16
  * await pushService.send(job.data);
17
17
  * }
@@ -22,15 +22,15 @@ import { Store } from '@tsed/core';
22
22
 
23
23
  import { MONQUE } from '@/constants';
24
24
 
25
- import type { WorkerDecoratorOptions, WorkerMetadata, WorkerStore } from './types.js';
25
+ import type { JobDecoratorOptions, JobMetadata, JobStore } from './types.js';
26
26
 
27
27
  /**
28
28
  * Method decorator that registers a method as a job handler.
29
29
  *
30
30
  * @param name - The job name (will be prefixed with controller namespace if present)
31
- * @param options - Optional worker configuration (concurrency, replace, etc.)
31
+ * @param options - Optional job configuration (concurrency, replace, etc.)
32
32
  */
33
- export function Worker(name: string, options?: WorkerDecoratorOptions): MethodDecorator {
33
+ export function Job(name: string, options?: JobDecoratorOptions): MethodDecorator {
34
34
  return <T>(
35
35
  target: object,
36
36
  propertyKey: string | symbol,
@@ -38,7 +38,7 @@ export function Worker(name: string, options?: WorkerDecoratorOptions): MethodDe
38
38
  ): void => {
39
39
  const methodName = String(propertyKey);
40
40
 
41
- const workerMetadata: WorkerMetadata = {
41
+ const jobMetadata: JobMetadata = {
42
42
  name,
43
43
  method: methodName,
44
44
  opts: options || {},
@@ -49,18 +49,18 @@ export function Worker(name: string, options?: WorkerDecoratorOptions): MethodDe
49
49
  const store = Store.from(targetConstructor);
50
50
 
51
51
  // Get or initialize the MONQUE store
52
- const existing = store.get<Partial<WorkerStore>>(MONQUE) || {
52
+ const existing = store.get<Partial<JobStore>>(MONQUE) || {
53
53
  type: 'controller',
54
- workers: [],
54
+ jobs: [],
55
55
  cronJobs: [],
56
56
  };
57
57
 
58
- // Add this worker to the list
59
- const workers = [...(existing.workers || []), workerMetadata];
58
+ // Add this job to the list
59
+ const jobs = [...(existing.jobs || []), jobMetadata];
60
60
 
61
61
  store.set(MONQUE, {
62
62
  ...existing,
63
- workers,
63
+ jobs,
64
64
  });
65
65
  };
66
66
  }
@@ -5,27 +5,27 @@
5
5
  import type { WorkerOptions as CoreWorkerOptions, ScheduleOptions } from '@monque/core';
6
6
 
7
7
  // ─────────────────────────────────────────────────────────────────────────────
8
- // Worker Decorator Options
8
+ // Job Decorator Options
9
9
  // ─────────────────────────────────────────────────────────────────────────────
10
10
 
11
11
  /**
12
- * Options for the @Worker method decorator.
12
+ * Options for the @Job method decorator.
13
13
  *
14
14
  * Maps to @monque/core WorkerOptions. All standard Monque worker options
15
15
  * are exposed here for decorator-based configuration.
16
16
  */
17
- export interface WorkerDecoratorOptions extends CoreWorkerOptions {}
17
+ export interface JobDecoratorOptions extends CoreWorkerOptions {}
18
18
 
19
19
  // ─────────────────────────────────────────────────────────────────────────────
20
- // Worker Metadata
20
+ // Job Metadata
21
21
  // ─────────────────────────────────────────────────────────────────────────────
22
22
 
23
23
  /**
24
- * Metadata for a single @Worker decorated method.
24
+ * Metadata for a single @Job decorated method.
25
25
  *
26
- * Stored in the WorkerStore and used by MonqueModule to register workers.
26
+ * Stored in the JobStore and used by MonqueModule to register workers.
27
27
  */
28
- export interface WorkerMetadata {
28
+ export interface JobMetadata {
29
29
  /**
30
30
  * Job name (without namespace prefix).
31
31
  * Combined with controller namespace to form full job name.
@@ -38,9 +38,9 @@ export interface WorkerMetadata {
38
38
  method: string;
39
39
 
40
40
  /**
41
- * Worker options forwarded to Monque.register().
41
+ * Job options forwarded to Monque.register().
42
42
  */
43
- opts: WorkerDecoratorOptions;
43
+ opts: JobDecoratorOptions;
44
44
  }
45
45
 
46
46
  // ─────────────────────────────────────────────────────────────────────────────
@@ -66,7 +66,7 @@ export interface CronDecoratorOptions extends ScheduleOptions {
66
66
  /**
67
67
  * Metadata for a single @Cron decorated method.
68
68
  *
69
- * Stored in the WorkerStore and used by MonqueModule to schedule cron jobs.
69
+ * Stored in the JobStore and used by MonqueModule to schedule cron jobs.
70
70
  */
71
71
  export interface CronMetadata {
72
72
  /**
@@ -91,25 +91,25 @@ export interface CronMetadata {
91
91
  }
92
92
 
93
93
  // ─────────────────────────────────────────────────────────────────────────────
94
- // Worker Store
94
+ // Job Store
95
95
  // ─────────────────────────────────────────────────────────────────────────────
96
96
 
97
97
  /**
98
- * Complete metadata structure stored on @WorkerController classes.
98
+ * Complete metadata structure stored on @JobController classes.
99
99
  *
100
100
  * Accessed via `Store.from(Class).get(MONQUE)`.
101
101
  *
102
102
  * @example
103
103
  * ```typescript
104
- * const store = Store.from(EmailWorkers).get<WorkerStore>(MONQUE);
104
+ * const store = Store.from(EmailJobs).get<JobStore>(MONQUE);
105
105
  * console.log(store.namespace); // "email"
106
- * console.log(store.workers); // [{ name: "send", method: "sendEmail", opts: {} }]
106
+ * console.log(store.jobs); // [{ name: "send", method: "sendEmail", opts: {} }]
107
107
  * ```
108
108
  */
109
- export interface WorkerStore {
109
+ export interface JobStore {
110
110
  /**
111
111
  * Type identifier for the store.
112
- * Always "controller" for WorkerController.
112
+ * Always "controller" for JobController.
113
113
  */
114
114
  type: 'controller';
115
115
 
@@ -120,9 +120,9 @@ export interface WorkerStore {
120
120
  namespace?: string;
121
121
 
122
122
  /**
123
- * Worker method registrations from @Worker decorators.
123
+ * Job method registrations from @Job decorators.
124
124
  */
125
- workers: WorkerMetadata[];
125
+ jobs: JobMetadata[];
126
126
 
127
127
  /**
128
128
  * Cron job registrations from @Cron decorators.
package/src/index.ts CHANGED
@@ -3,18 +3,18 @@ export { MONQUE, type ProviderType, ProviderTypes } from './constants';
3
3
  export type {
4
4
  CronDecoratorOptions,
5
5
  CronMetadata,
6
- WorkerDecoratorOptions,
7
- WorkerMetadata,
8
- WorkerStore,
6
+ JobDecoratorOptions,
7
+ JobMetadata,
8
+ JobStore,
9
9
  } from './decorators';
10
- export { Cron, Worker, WorkerController } from './decorators';
10
+ export { Cron, Job, JobController } from './decorators';
11
11
  export { MonqueModule } from './monque-module.js';
12
12
  export { MonqueService } from './services';
13
13
  export {
14
14
  buildJobName,
15
- type CollectedWorkerMetadata,
16
- collectWorkerMetadata,
17
- getWorkerToken,
15
+ type CollectedJobMetadata,
16
+ collectJobMetadata,
17
+ getJobToken,
18
18
  type InjectorFn,
19
19
  resolveDatabase,
20
20
  } from './utils';
@@ -2,15 +2,17 @@
2
2
  * MonqueModule - Main Integration Module
3
3
  *
4
4
  * Orchestrates the integration between Monque and Ts.ED.
5
- * Handles lifecycle hooks, configuration resolution, and worker registration.
5
+ * Handles lifecycle hooks, configuration resolution, and job registration.
6
6
  */
7
7
 
8
8
  import {
9
9
  type Job,
10
10
  Monque,
11
+ MonqueError,
11
12
  type MonqueOptions,
12
13
  type ScheduleOptions,
13
14
  type WorkerOptions,
15
+ WorkerRegistrationError,
14
16
  } from '@monque/core';
15
17
  import {
16
18
  Configuration,
@@ -29,7 +31,7 @@ import {
29
31
  import { type MonqueTsedConfig, validateDatabaseConfig } from '@/config';
30
32
  import { ProviderTypes } from '@/constants';
31
33
  import { MonqueService } from '@/services';
32
- import { collectWorkerMetadata, resolveDatabase } from '@/utils';
34
+ import { collectJobMetadata, resolveDatabase } from '@/utils';
33
35
 
34
36
  @Module({
35
37
  imports: [MonqueService],
@@ -80,11 +82,13 @@ export class MonqueModule implements OnInit, OnDestroy {
80
82
  this.logger.info('Monque: Connecting to MongoDB...');
81
83
  await this.monque.initialize();
82
84
 
83
- await this.registerWorkers();
84
-
85
- await this.monque.start();
86
-
87
- this.logger.info('Monque: Started successfully');
85
+ if (config.disableJobProcessing) {
86
+ this.logger.info('Monque: Job processing is disabled for this instance');
87
+ } else {
88
+ await this.registerJobs();
89
+ await this.monque.start();
90
+ this.logger.info('Monque: Started successfully');
91
+ }
88
92
  } catch (error) {
89
93
  this.logger.error({
90
94
  event: 'MONQUE_INIT_ERROR',
@@ -107,22 +111,22 @@ export class MonqueModule implements OnInit, OnDestroy {
107
111
  }
108
112
 
109
113
  /**
110
- * Discover and register all workers from @WorkerController providers
114
+ * Discover and register all jobs from @JobController providers
111
115
  */
112
- protected async registerWorkers(): Promise<void> {
116
+ protected async registerJobs(): Promise<void> {
113
117
  if (!this.monque) {
114
- throw new Error('Monque instance not initialized');
118
+ throw new MonqueError('Monque instance not initialized');
115
119
  }
116
120
 
117
121
  const monque = this.monque;
118
- const workerControllers = this.injector.getProviders(ProviderTypes.WORKER_CONTROLLER);
122
+ const jobControllers = this.injector.getProviders(ProviderTypes.JOB_CONTROLLER);
119
123
  const registeredJobs = new Set<string>();
120
124
 
121
- this.logger.info(`Monque: Found ${workerControllers.length} worker controllers`);
125
+ this.logger.info(`Monque: Found ${jobControllers.length} job controllers`);
122
126
 
123
- for (const provider of workerControllers) {
127
+ for (const provider of jobControllers) {
124
128
  const useClass = provider.useClass;
125
- const workers = collectWorkerMetadata(useClass);
129
+ const jobs = collectJobMetadata(useClass);
126
130
  // Try to resolve singleton instance immediately
127
131
  const instance = this.injector.get(provider.token);
128
132
 
@@ -134,12 +138,13 @@ export class MonqueModule implements OnInit, OnDestroy {
134
138
  continue;
135
139
  }
136
140
 
137
- for (const worker of workers) {
138
- const { fullName, method, opts, isCron, cronPattern } = worker;
141
+ for (const job of jobs) {
142
+ const { fullName, method, opts, isCron, cronPattern } = job;
139
143
 
140
144
  if (registeredJobs.has(fullName)) {
141
- throw new Error(
145
+ throw new WorkerRegistrationError(
142
146
  `Monque: Duplicate job registration detected. Job "${fullName}" is already registered.`,
147
+ fullName,
143
148
  );
144
149
  }
145
150
 
@@ -188,7 +193,7 @@ export class MonqueModule implements OnInit, OnDestroy {
188
193
  monque.register(fullName, handler, opts as WorkerOptions);
189
194
  await monque.schedule(cronPattern, fullName, {}, opts as ScheduleOptions);
190
195
  } else {
191
- this.logger.debug(`Monque: Registering worker "${fullName}"`);
196
+ this.logger.debug(`Monque: Registering job "${fullName}"`);
192
197
  monque.register(fullName, handler, opts as WorkerOptions);
193
198
  }
194
199
  }
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Build the full job name by combining namespace and name.
3
3
  *
4
- * @param namespace - Optional namespace from @WorkerController
5
- * @param name - Job name from @Worker or @Cron
4
+ * @param namespace - Optional namespace from @JobController
5
+ * @param name - Job name from @Job or @Cron
6
6
  * @returns Full job name (e.g., "email.send" or just "send")
7
7
  *
8
8
  * @example