@basaltkit/queue 1.0.0 → 1.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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 Machize Contributors
3
+ Copyright (c) 2026 Basalt Contributors
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/dist/index.d.ts CHANGED
@@ -3,6 +3,11 @@ import { DurationInput, BasaltError } from '@basaltkit/core';
3
3
  import { ConnectionOptions } from 'bullmq';
4
4
  import { EventBus, BasaltEvent } from '@basaltkit/events';
5
5
 
6
+ /** Driver-neutral retention: `true`/`false`, a count, or `{ ageMs, count }`. */
7
+ type RetentionOption = boolean | number | {
8
+ ageMs?: number;
9
+ count?: number;
10
+ };
6
11
  interface AddJobOptions {
7
12
  attempts: number;
8
13
  backoff?: {
@@ -11,6 +16,10 @@ interface AddJobOptions {
11
16
  } | undefined;
12
17
  delayMs?: number | undefined;
13
18
  priority?: number | undefined;
19
+ /** Retention for completed jobs. Undefined → the driver's default. */
20
+ removeOnComplete?: RetentionOption | undefined;
21
+ /** Retention for failed jobs. Undefined → the driver's default. */
22
+ removeOnFail?: RetentionOption | undefined;
14
23
  }
15
24
  type JobExecutor = (jobName: string, data: unknown) => Promise<void>;
16
25
  /**
@@ -96,12 +105,26 @@ interface JobBackoff {
96
105
  type: 'exponential' | 'fixed';
97
106
  delay: DurationInput;
98
107
  }
108
+ /**
109
+ * Redis retention for finished jobs (BullMQ driver): `true` removes it as soon as
110
+ * it finishes, `false` keeps it forever, a number keeps that many most-recent, and
111
+ * `{ age, count }` keeps by age and/or count. Defaults: completed `{ count: 1000 }`,
112
+ * failed `false` (keep all). The sync driver ignores it (it stores nothing).
113
+ */
114
+ type JobRetention = boolean | number | {
115
+ age?: DurationInput;
116
+ count?: number;
117
+ };
99
118
  interface JobDefinition<T = unknown> {
100
119
  readonly name: string;
101
120
  readonly schema?: JobSchema<T> | undefined;
102
121
  readonly queue: string;
103
122
  readonly attempts: number;
104
123
  readonly backoff?: JobBackoff | undefined;
124
+ /** Retention for completed jobs. Overrides the queuePlugin default. */
125
+ readonly removeOnComplete?: JobRetention | undefined;
126
+ /** Retention for failed jobs. Overrides the queuePlugin default. */
127
+ readonly removeOnFail?: JobRetention | undefined;
105
128
  handle(payload: T): void | Promise<void>;
106
129
  /** Enqueues the job — available after registration in a QueueManager. */
107
130
  dispatch(payload: T, options?: DispatchOptions): Promise<void>;
@@ -128,6 +151,8 @@ declare function defineJob<T = unknown>(config: {
128
151
  queue?: string;
129
152
  attempts?: number;
130
153
  backoff?: JobBackoff;
154
+ removeOnComplete?: JobRetention;
155
+ removeOnFail?: JobRetention;
131
156
  handle(payload: T): void | Promise<void>;
132
157
  }): JobDefinition<T>;
133
158
 
@@ -151,6 +176,10 @@ interface QueueManagerOptions {
151
176
  onUnsupported?: UnsupportedPolicy;
152
177
  /** Sink for 'warn' diagnostics. Default console.warn. */
153
178
  warn?: (message: string) => void;
179
+ /** Default retention for completed jobs (a job can override). Driver default: keep 1000. */
180
+ removeOnComplete?: JobRetention;
181
+ /** Default retention for failed jobs (a job can override). Driver default: keep all. */
182
+ removeOnFail?: JobRetention;
154
183
  }
155
184
  declare class QueueManager implements JobDispatcher {
156
185
  private readonly driver;
@@ -158,6 +187,8 @@ declare class QueueManager implements JobDispatcher {
158
187
  private readonly onUnsupported;
159
188
  private readonly warn;
160
189
  private readonly warned;
190
+ private readonly defaultRemoveOnComplete;
191
+ private readonly defaultRemoveOnFail;
161
192
  constructor(driver: QueueDriver, options?: QueueManagerOptions);
162
193
  /**
163
194
  * Checks the dispatch's options against the driver's declared capabilities.
@@ -236,7 +267,18 @@ interface QueuePluginOptions {
236
267
  * 'throw' in production for a hard guarantee, 'ignore' for the old behavior.
237
268
  */
238
269
  onUnsupported?: UnsupportedPolicy;
270
+ /**
271
+ * Default retention for completed jobs in Redis (BullMQ). `true` removes on
272
+ * finish, a number keeps that many, `{ age: '7d', count: 500 }` caps both.
273
+ * Default: keep the last 1000. A job can override via `defineJob`.
274
+ */
275
+ removeOnComplete?: JobRetention;
276
+ /**
277
+ * Default retention for failed jobs. Default `false` (keep all, for inspection
278
+ * and retries) — set e.g. `{ age: '14d' }` so failures don't grow unbounded.
279
+ */
280
+ removeOnFail?: JobRetention;
239
281
  }
240
282
  declare function queuePlugin(options?: QueuePluginOptions): _basaltkit_core.BasaltPlugin<unknown>;
241
283
 
242
- export { type AddJobOptions, type BullmqDriverOptions, BullmqQueueDriver, type DispatchOptions, type DriverCapabilities, type JobBackoff, type JobDefinition, type JobExecutor, JobNotRegisteredError, type JobSchema, JobValidationError, QUEUE, type QueueDriver, QueueManager, type QueueManagerOptions, type QueuePluginOptions, type QueuedListenerOptions, SyncQueueDriver, UnknownJobError, UnsupportedJobOptionError, type UnsupportedPolicy, defineJob, queuePlugin, queuedOn };
284
+ export { type AddJobOptions, type BullmqDriverOptions, BullmqQueueDriver, type DispatchOptions, type DriverCapabilities, type JobBackoff, type JobDefinition, type JobExecutor, JobNotRegisteredError, type JobRetention, type JobSchema, JobValidationError, QUEUE, type QueueDriver, QueueManager, type QueueManagerOptions, type QueuePluginOptions, type QueuedListenerOptions, SyncQueueDriver, UnknownJobError, UnsupportedJobOptionError, type UnsupportedPolicy, defineJob, queuePlugin, queuedOn };
package/dist/index.js CHANGED
@@ -3,6 +3,14 @@ import { createToken, definePlugin } from "@basaltkit/core";
3
3
 
4
4
  // src/drivers/bullmq.ts
5
5
  import { Queue, Worker } from "bullmq";
6
+ function toBullRetention(retention, fallback) {
7
+ if (retention === void 0) return fallback;
8
+ if (typeof retention === "boolean" || typeof retention === "number") return retention;
9
+ const out = {};
10
+ if (retention.ageMs !== void 0) out.age = Math.max(1, Math.round(retention.ageMs / 1e3));
11
+ if (retention.count !== void 0) out.count = retention.count;
12
+ return out;
13
+ }
6
14
  var BullmqQueueDriver = class {
7
15
  name = "bullmq";
8
16
  capabilities = { delayed: true, priority: true, retries: true, backoff: true };
@@ -22,8 +30,8 @@ var BullmqQueueDriver = class {
22
30
  ...options.backoff ? { backoff: { type: options.backoff.type, delay: options.backoff.delayMs } } : {},
23
31
  ...options.delayMs !== void 0 ? { delay: options.delayMs } : {},
24
32
  ...options.priority !== void 0 ? { priority: options.priority } : {},
25
- removeOnComplete: { count: 1e3 },
26
- removeOnFail: false
33
+ removeOnComplete: toBullRetention(options.removeOnComplete, { count: 1e3 }),
34
+ removeOnFail: toBullRetention(options.removeOnFail, false)
27
35
  });
28
36
  }
29
37
  startWorker(queue, options = {}) {
@@ -128,6 +136,8 @@ function defineJob(config) {
128
136
  queue: config.queue ?? "default",
129
137
  attempts: config.attempts ?? 1,
130
138
  backoff: config.backoff,
139
+ removeOnComplete: config.removeOnComplete,
140
+ removeOnFail: config.removeOnFail,
131
141
  handle: config.handle,
132
142
  async dispatch(payload, options) {
133
143
  if (!dispatcher) throw new JobNotRegisteredError(config.name);
@@ -150,6 +160,14 @@ function validatePayload(job, payload) {
150
160
  }
151
161
 
152
162
  // src/manager.ts
163
+ function resolveRetention(retention) {
164
+ if (retention === void 0) return void 0;
165
+ if (typeof retention === "boolean" || typeof retention === "number") return retention;
166
+ const out = {};
167
+ if (retention.age !== void 0) out.ageMs = parseDuration(retention.age);
168
+ if (retention.count !== void 0) out.count = retention.count;
169
+ return out;
170
+ }
153
171
  var UnknownJobError = class extends BasaltError2 {
154
172
  constructor(job) {
155
173
  super(
@@ -187,6 +205,8 @@ var QueueManager = class {
187
205
  this.driver = driver;
188
206
  this.onUnsupported = options.onUnsupported ?? "warn";
189
207
  this.warn = options.warn ?? ((message) => console.warn(message));
208
+ this.defaultRemoveOnComplete = options.removeOnComplete;
209
+ this.defaultRemoveOnFail = options.removeOnFail;
190
210
  driver.setExecutor((jobName, data) => this.execute(jobName, data));
191
211
  }
192
212
  driver;
@@ -194,6 +214,8 @@ var QueueManager = class {
194
214
  onUnsupported;
195
215
  warn;
196
216
  warned = /* @__PURE__ */ new Set();
217
+ defaultRemoveOnComplete;
218
+ defaultRemoveOnFail;
197
219
  /**
198
220
  * Checks the dispatch's options against the driver's declared capabilities.
199
221
  * A driver that omits `capabilities` is assumed fully capable (back-compat).
@@ -228,7 +250,10 @@ var QueueManager = class {
228
250
  attempts: job.attempts,
229
251
  backoff: job.backoff ? { type: job.backoff.type, delayMs: parseDuration(job.backoff.delay) } : void 0,
230
252
  delayMs: options.delay === void 0 ? void 0 : parseDuration(options.delay),
231
- priority: options.priority
253
+ priority: options.priority,
254
+ // Per-job overrides the queuePlugin default; undefined leaves the driver default.
255
+ removeOnComplete: resolveRetention(job.removeOnComplete ?? this.defaultRemoveOnComplete),
256
+ removeOnFail: resolveRetention(job.removeOnFail ?? this.defaultRemoveOnFail)
232
257
  };
233
258
  this.assertSupported(job.name, addOptions);
234
259
  await this.driver.add(job.queue, job.name, envelope, addOptions);
@@ -287,10 +312,11 @@ function queuePlugin(options = {}) {
287
312
  register({ container }) {
288
313
  container.singleton(QUEUE, () => {
289
314
  const driver = options.driver ?? (options.connection ? new BullmqQueueDriver({ connection: options.connection }) : new SyncQueueDriver());
290
- const manager = new QueueManager(
291
- driver,
292
- options.onUnsupported !== void 0 ? { onUnsupported: options.onUnsupported } : {}
293
- );
315
+ const manager = new QueueManager(driver, {
316
+ ...options.onUnsupported !== void 0 ? { onUnsupported: options.onUnsupported } : {},
317
+ ...options.removeOnComplete !== void 0 ? { removeOnComplete: options.removeOnComplete } : {},
318
+ ...options.removeOnFail !== void 0 ? { removeOnFail: options.removeOnFail } : {}
319
+ });
294
320
  for (const job of options.jobs ?? []) manager.register(job);
295
321
  return manager;
296
322
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@basaltkit/queue",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Basalt queues on top of BullMQ: declarative jobs with Zod payloads, context propagation (tenant/requestId) to workers and a sync driver for tests.",
5
5
  "license": "MIT",
6
6
  "type": "module",