@boopkit/cap-background-jobs 0.1.0 → 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.
package/capability.json CHANGED
@@ -4,8 +4,8 @@
4
4
  "type": "application",
5
5
  "package": "@boopkit/cap-background-jobs",
6
6
  "requires": ["services"],
7
- "dependencies": ["amqplib@^0.10"],
8
- "devDependencies": ["@types/amqplib@^0.10"],
7
+ "dependencies": ["amqplib@^0.10", "minimist@^1.2"],
8
+ "devDependencies": ["@types/amqplib@^0.10", "@types/minimist@^1.2"],
9
9
  "env": [
10
10
  { "name": "CLOUDAMQP_URL", "description": "CloudAMQP connection URL (amqp://...)", "required": "boot" },
11
11
  { "name": "QUEUE_PREFIX", "description": "Queue name prefix for environment isolation (e.g. dev/mike)" },
@@ -13,7 +13,7 @@
13
13
  ],
14
14
  "scripts": {
15
15
  "worker": { "cmd": "ts-node src/worker.ts", "description": "Start the background job worker process" },
16
- "scheduler": { "cmd": "ts-node src/scheduler.ts", "description": "Publish trigger messages to job queues (run via Heroku Scheduler)" }
16
+ "scheduler": { "cmd": "ts-node src/scheduler.ts", "description": "Publish trigger messages to job queues supports --interval and --job filtering" }
17
17
  },
18
18
  "files": [
19
19
  "src/scheduler.ts",
@@ -32,7 +32,7 @@
32
32
  "Add CLOUDAMQP_URL to .env",
33
33
  "Set QUEUE_PREFIX for local dev isolation (e.g. dev/yourname)",
34
34
  "Add worker entry to Procfile: worker: npm run worker",
35
- "Configure Heroku Scheduler to run npm run scheduler on desired interval"
35
+ "Configure Heroku Scheduler to run 'npm run scheduler -- run-jobs --interval <tag>' on desired interval (e.g. 10-minutes, 60-minutes)"
36
36
  ],
37
37
  "verify": "npm run worker"
38
38
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@boopkit/cap-background-jobs",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Background job processing with RabbitMQ for boopkit-scaffolded projects",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -20,10 +20,12 @@
20
20
  "build": "tsc -p tsconfig.json"
21
21
  },
22
22
  "dependencies": {
23
- "amqplib": "^0.10.0"
23
+ "amqplib": "^0.10.0",
24
+ "minimist": "^1.2.8"
24
25
  },
25
26
  "devDependencies": {
26
27
  "@types/amqplib": "^0.10.0",
28
+ "@types/minimist": "^1.2.5",
27
29
  "typescript": "^5.9.3"
28
30
  },
29
31
  "license": "ISC",
@@ -33,7 +33,33 @@ Three separate OS processes share one MQ service:
33
33
  |---------|------------|------|-----------|
34
34
  | **Web** | `src/server.ts` | Publishes messages as a side effect of HTTP requests | Long-running |
35
35
  | **Worker** | `src/worker.ts` | Consumes messages via registered controllers | Long-running |
36
- | **Scheduler** | `src/scheduler.ts` | Publishes trigger messages, then exits | One-shot (cron) |
36
+ | **Scheduler** | `src/scheduler.ts` | Publishes trigger messages filtered by interval/job, then exits | One-shot (cron) |
37
+
38
+ ---
39
+
40
+ ## Scheduler CLI
41
+
42
+ The scheduler supports subcommands and options for selecting which jobs to trigger:
43
+
44
+ ```bash
45
+ # Publish all jobs tagged with the '10-minutes' interval
46
+ npm run scheduler -- run-jobs --interval 10-minutes
47
+
48
+ # Publish a specific job regardless of interval
49
+ npm run scheduler -- run-jobs --job example-job
50
+
51
+ # Combine filters
52
+ npm run scheduler -- run-jobs --interval 60-minutes --job reconciler
53
+
54
+ # List all registered jobs with their queue and interval
55
+ npm run scheduler -- list
56
+ ```
57
+
58
+ Each job in the `jobs` array has:
59
+ - `name` — human-readable identifier (for `--job` filtering)
60
+ - `queue` — target MQ queue name
61
+ - `interval` — tag for external scheduler filtering (e.g. `10-minutes`, `60-minutes`)
62
+ - `payload` — message body published to the queue
37
63
 
38
64
  ---
39
65
 
@@ -130,7 +156,9 @@ await MessageQueueService.listen('queue', handler, { prefetch: 10 });
130
156
 
131
157
  1. **CloudAMQP addon**: `heroku addons:create cloudamqp` — sets `CLOUDAMQP_URL` automatically
132
158
  2. **Procfile**: Add `worker: npm run worker`
133
- 3. **Heroku Scheduler**: Configure `npm run scheduler` on the desired interval
159
+ 3. **Heroku Scheduler**: Configure intervals, e.g.:
160
+ - Every 10 min: `npm run scheduler -- run-jobs --interval 10-minutes`
161
+ - Every hour: `npm run scheduler -- run-jobs --interval 60-minutes`
134
162
  4. **Scale worker**: `heroku ps:scale worker=1`
135
163
 
136
164
  ---
@@ -153,7 +181,7 @@ If drain exceeds 30s, the worker force-exits with code 1.
153
181
  | File | Purpose |
154
182
  |------|---------|
155
183
  | `src/worker.ts` | Worker entry point — boots services, registers consumers, handles shutdown |
156
- | `src/scheduler.ts` | Scheduler entry point — publishes trigger messages, exits |
184
+ | `src/scheduler.ts` | Scheduler entry point — CLI with interval/job filtering, publishes trigger messages, exits |
157
185
  | `src/services/message-queue.service.ts` | MQ service — publish, listen (auto/manual ACK), topology, retry/DLQ |
158
186
  | `src/controllers/example-job.controller.ts` | Example consumer — copyable template for new job handlers |
159
187
  | `tests/services/message-queue.service.test.ts` | MQ service unit tests |
@@ -1,12 +1,28 @@
1
1
  /** @boopkit/cap-background-jobs — Scheduler entry point for triggering background jobs. */
2
2
 
3
3
  import { loadServices } from '@services';
4
+ import minimist from 'minimist';
4
5
  import MessageQueueService from './services/message-queue.service';
5
6
 
6
- // Static job registration — add new {queue, payload} entries here
7
- const jobs = [
7
+ interface JobConfig {
8
+ /** Human-readable job name (used for --job filtering). */
9
+ name: string;
10
+ /** Target queue name to publish to. */
11
+ queue: string;
12
+ /** Interval tag for external scheduler filtering (e.g. '10-minutes', '60-minutes'). */
13
+ interval: string;
14
+ /** Message payload to publish. */
15
+ payload: { task: string; data?: Record<string, unknown> };
16
+ }
17
+
18
+ // Static job registration — add new entries here.
19
+ // The `interval` field tags each job so an external scheduler can run subsets:
20
+ // npm run scheduler -- run-jobs --interval 10-minutes
21
+ const jobs: JobConfig[] = [
8
22
  {
23
+ name: 'example-job',
9
24
  queue: 'example-job',
25
+ interval: '10-minutes',
10
26
  payload: {
11
27
  task: 'scheduled-run',
12
28
  data: {
@@ -17,10 +33,28 @@ const jobs = [
17
33
  },
18
34
  ];
19
35
 
20
- async function run() {
21
- await loadServices();
36
+ /**
37
+ * Publish trigger messages for jobs matching the given filters.
38
+ */
39
+ async function runJobs(
40
+ opts: { interval?: string; job?: string; await?: boolean },
41
+ ): Promise<void> {
42
+ let selected = jobs;
43
+
44
+ if (opts.interval) {
45
+ selected = selected.filter((j) => j.interval === opts.interval);
46
+ }
47
+ if (opts.job) {
48
+ selected = selected.filter((j) => j.name === opts.job);
49
+ }
22
50
 
23
- for (const job of jobs) {
51
+ console.log(
52
+ `[scheduler] ${selected.length} job(s) selected` +
53
+ (opts.interval ? ` for interval "${opts.interval}"` : '') +
54
+ (opts.job ? ` matching job "${opts.job}"` : ''),
55
+ );
56
+
57
+ for (const job of selected) {
24
58
  const ok = await MessageQueueService.publish(job.queue, job.payload);
25
59
  if (ok) {
26
60
  console.log(`[scheduler] published to "${job.queue}": ${job.payload.task}`);
@@ -28,12 +62,60 @@ async function run() {
28
62
  console.error(`[scheduler] failed to publish to "${job.queue}"`);
29
63
  }
30
64
  }
65
+ }
66
+
67
+ function printUsage(): void {
68
+ console.log(`Usage: npm run scheduler -- <command> [options]
69
+
70
+ Commands:
71
+ run-jobs Publish trigger messages to matching job queues
72
+ list List all registered jobs
73
+
74
+ Options:
75
+ --interval <tag> Filter jobs by interval tag (e.g. 10-minutes, 60-minutes)
76
+ --job <name> Filter jobs by name
77
+ `);
78
+ }
79
+
80
+ async function main(args: string[], opts: Record<string, unknown>): Promise<void> {
81
+ const command = (args[0] ?? '').toLowerCase();
82
+
83
+ if (!command) {
84
+ printUsage();
85
+ process.exit(0);
86
+ }
87
+
88
+ await loadServices();
89
+
90
+ switch (command) {
91
+ case 'run-jobs': {
92
+ await runJobs({
93
+ interval: opts.interval as string | undefined,
94
+ job: opts.job as string | undefined,
95
+ });
96
+ break;
97
+ }
98
+
99
+ case 'list': {
100
+ console.log('[scheduler] registered jobs:');
101
+ for (const job of jobs) {
102
+ console.log(` ${job.name} queue=${job.queue} interval=${job.interval}`);
103
+ }
104
+ break;
105
+ }
106
+
107
+ default:
108
+ console.error(`[scheduler] unknown command: ${command}`);
109
+ printUsage();
110
+ process.exit(1);
111
+ }
31
112
 
32
113
  await MessageQueueService.close();
33
114
  process.exit(0);
34
115
  }
35
116
 
36
- run().catch((err) => {
117
+ const argv = minimist(process.argv.slice(2));
118
+ main(argv._, argv).catch((err) => {
37
119
  console.error('[scheduler] failed:', err);
38
120
  process.exit(1);
39
121
  });
@@ -71,13 +71,27 @@ await MessageQueueService.publish('my-job', {
71
71
  Add an entry to the `jobs` array in `src/scheduler.ts`:
72
72
 
73
73
  ```typescript
74
- const jobs = [
75
- { queue: 'example-job', payload: { task: 'scheduled-run', data: { ... } } },
76
- { queue: 'my-job', payload: { task: 'nightly-sync', data: { ... } } },
74
+ const jobs: JobConfig[] = [
75
+ { name: 'example-job', queue: 'example-job', interval: '10-minutes', payload: { task: 'scheduled-run', data: { ... } } },
76
+ { name: 'nightly-sync', queue: 'my-job', interval: '60-minutes', payload: { task: 'nightly-sync', data: { ... } } },
77
77
  ];
78
78
  ```
79
79
 
80
- The scheduler publishes all jobs and exits. Configure Heroku Scheduler to run `npm run scheduler` at the desired interval.
80
+ Each job has an `interval` tag. Configure Heroku Scheduler to run different intervals:
81
+
82
+ ```bash
83
+ # Every 10 minutes
84
+ npm run scheduler -- run-jobs --interval 10-minutes
85
+
86
+ # Every hour
87
+ npm run scheduler -- run-jobs --interval 60-minutes
88
+
89
+ # Specific job only
90
+ npm run scheduler -- run-jobs --job nightly-sync
91
+
92
+ # List all registered jobs
93
+ npm run scheduler -- list
94
+ ```
81
95
 
82
96
  ## Auto-ACK vs manual-ACK
83
97