@vytches/ddd-messaging 0.29.3 → 0.31.0-alpha.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/dist/index.js CHANGED
@@ -1,7 +1,59 @@
1
- import { Logger as u } from "@vytches/ddd-logging";
2
- import { LibUtils as p, safeRun as d } from "@vytches/ddd-utils";
3
- var n = /* @__PURE__ */ ((i) => (i.PENDING = "PENDING", i.PROCESSING = "PROCESSING", i.PROCESSED = "PROCESSED", i.FAILED = "FAILED", i))(n || {}), g = /* @__PURE__ */ ((i) => (i.LOW = "low", i.NORMAL = "normal", i.HIGH = "high", i.CRITICAL = "critical", i))(g || {});
4
- class m {
1
+ import { LibUtils as y, safeRun as g } from "@vytches/ddd-utils";
2
+ import { internalLogger as c } from "@vytches/ddd-contracts/internal";
3
+ var n = /* @__PURE__ */ ((i) => (i.PENDING = "PENDING", i.PROCESSING = "PROCESSING", i.PROCESSED = "PROCESSED", i.FAILED = "FAILED", i))(n || {}), l = /* @__PURE__ */ ((i) => (i.LOW = "low", i.NORMAL = "normal", i.HIGH = "high", i.CRITICAL = "critical", i))(l || {});
4
+ const f = [
5
+ "critical",
6
+ "high",
7
+ "normal",
8
+ "low"
9
+ /* LOW */
10
+ ];
11
+ function R(i, e, t = f) {
12
+ const s = t.includes(i) ? t.indexOf(i) : t.length, a = t.includes(e) ? t.indexOf(e) : t.length;
13
+ return s - a;
14
+ }
15
+ class w {
16
+ /**
17
+ * Atomically claims a batch of unprocessed messages: reads PENDING
18
+ * messages *and* marks them `PROCESSING` in the same operation, so two
19
+ * concurrent callers (two processor instances, or two racing calls on one
20
+ * instance) never both receive the same message.
21
+ *
22
+ * **Optional, and non-atomic by default.** This method is declared with
23
+ * `?` — it is NOT part of the required abstract contract, so existing
24
+ * `IOutboxRepository` subclasses keep compiling unmodified (AC#5). The
25
+ * default implementation provided here is a convenience shim only: it
26
+ * calls {@link getUnprocessedMessages} and then `updateStatusBatch(...,
27
+ * PROCESSING)` as two separate steps. **That is NOT atomic** — it has the
28
+ * exact same read-then-mark race window as calling
29
+ * `getUnprocessedMessages` + `updateStatus` manually, and is safe only for
30
+ * a single-worker deployment. Override `claimBatch` in a concrete
31
+ * repository (e.g. backed by `SELECT ... FOR UPDATE SKIP LOCKED`, or a
32
+ * single atomic `UPDATE ... WHERE status='PENDING' LIMIT n RETURNING *`)
33
+ * to get real multi-worker/multi-processor safety.
34
+ *
35
+ * `OutboxProcessor` only calls this method when
36
+ * `OutboxProcessorOptions.useClaimBatch` is explicitly set to `true`
37
+ * (opt-in, default `false`) — see AC#1/AC#2. When enabled, the processor
38
+ * skips its own redundant `updateStatus(id, PROCESSING)` call for messages
39
+ * returned by `claimBatch`, since claiming already performed that mark.
40
+ *
41
+ * Handlers should be idempotent to side effects regardless — transient
42
+ * failures or duplicate-claim races (e.g. with the non-atomic default
43
+ * implementation) may still cause a handler to run more than once.
44
+ *
45
+ * @param limit - Maximum number of messages to claim.
46
+ * @param priorityOrder - See {@link getUnprocessedMessages}.
47
+ * @param messageTypes - See {@link getUnprocessedMessages}.
48
+ * @returns Messages that were claimed (already marked `PROCESSING`).
49
+ */
50
+ async claimBatch(e, t, s) {
51
+ const a = await this.getUnprocessedMessages(e, t, s);
52
+ return a.length === 0 ? a : (await this.updateStatusBatch(
53
+ a.map((r) => r.id),
54
+ n.PROCESSING
55
+ ), a.map((r) => ({ ...r, status: n.PROCESSING })));
56
+ }
5
57
  /**
6
58
  * Schedules a retry for a failed message at a specific future time.
7
59
  *
@@ -43,7 +95,7 @@ class m {
43
95
  return Promise.resolve(0);
44
96
  }
45
97
  }
46
- class h {
98
+ class d {
47
99
  /**
48
100
  * Creates a new outbox message
49
101
  * @param messageType Type of the message
@@ -52,17 +104,17 @@ class h {
52
104
  * @returns Outbox message
53
105
  */
54
106
  static createMessage(e, t, s) {
55
- const r = {
56
- id: p.getUUID(),
107
+ const a = {
108
+ id: y.getUUID(),
57
109
  messageType: e,
58
110
  payload: t,
59
111
  metadata: s?.metadata || {},
60
112
  status: n.PENDING,
61
113
  attempts: 0,
62
114
  createdAt: /* @__PURE__ */ new Date(),
63
- priority: s?.priority || g.NORMAL
115
+ priority: s?.priority || l.NORMAL
64
116
  };
65
- return s?.processAfter && (r.processAfter = s.processAfter), r;
117
+ return s?.processAfter && (a.processAfter = s.processAfter), a;
66
118
  }
67
119
  /**
68
120
  * Creates a delayed outbox message
@@ -72,11 +124,11 @@ class h {
72
124
  * @param options Additional options
73
125
  * @returns Outbox message scheduled for later processing
74
126
  */
75
- static createDelayedMessage(e, t, s, r) {
76
- const a = new Date(Date.now() + s);
77
- return h.createMessage(e, t, {
78
- ...r,
79
- processAfter: a
127
+ static createDelayedMessage(e, t, s, a) {
128
+ const r = new Date(Date.now() + s);
129
+ return d.createMessage(e, t, {
130
+ ...a,
131
+ processAfter: r
80
132
  });
81
133
  }
82
134
  /**
@@ -87,9 +139,9 @@ class h {
87
139
  * @returns High priority outbox message
88
140
  */
89
141
  static createHighPriorityMessage(e, t, s) {
90
- return h.createMessage(e, t, {
142
+ return d.createMessage(e, t, {
91
143
  ...s,
92
- priority: g.HIGH
144
+ priority: l.HIGH
93
145
  });
94
146
  }
95
147
  /**
@@ -105,52 +157,53 @@ class h {
105
157
  ...t?.metadata || {}
106
158
  }
107
159
  };
108
- return t?.priority && (s.priority = t.priority), t?.processAfter && (s.processAfter = t.processAfter), h.createMessage(
160
+ return t?.priority && (s.priority = t.priority), t?.processAfter && (s.processAfter = t.processAfter), d.createMessage(
109
161
  `integration_event:${e.eventName}`,
110
162
  e.payload,
111
163
  s
112
164
  );
113
165
  }
114
166
  }
115
- class v {
167
+ class P {
116
168
  constructor(e, t = {}) {
117
- this.handlers = /* @__PURE__ */ new Map(), this.middlewares = [], this.isRunning = !1, this.logger = u.create("OutboxProcessor");
169
+ this.handlers = /* @__PURE__ */ new Map(), this.middlewares = [], this.isRunning = !1, this.warnedMissingClaimBatch = !1;
118
170
  const s = t.batchSize ?? 10;
119
171
  if (s > 1e4)
120
172
  throw new RangeError(`OutboxProcessor: batchSize must be ≤ 10,000, got ${s}`);
121
173
  if (t.retryBackoff) {
122
- const { initial: o, multiplier: l, maxDelay: c } = t.retryBackoff;
123
- if (o <= 0 || l <= 0 || c <= 0)
174
+ const { initial: o, multiplier: h, maxDelay: u } = t.retryBackoff;
175
+ if (o <= 0 || h <= 0 || u <= 0)
124
176
  throw new RangeError(
125
- `OutboxProcessor: retryBackoff fields must be positive numbers (initial=${o}, multiplier=${l}, maxDelay=${c})`
177
+ `OutboxProcessor: retryBackoff fields must be positive numbers (initial=${o}, multiplier=${h}, maxDelay=${u})`
126
178
  );
127
179
  }
128
- const r = t.messageTimeout ?? 3e4, a = t.crashRecoveryThresholdMs ?? Math.max(3e5, r);
129
- if (a < r)
180
+ const a = t.messageTimeout ?? 3e4, r = t.crashRecoveryThresholdMs ?? Math.max(3e5, a);
181
+ if (r < a)
130
182
  throw new RangeError(
131
- `OutboxProcessor: crashRecoveryThresholdMs (${a}) must be ≥ messageTimeout (${r}) to avoid resetting in-flight messages`
183
+ `OutboxProcessor: crashRecoveryThresholdMs (${r}) must be ≥ messageTimeout (${a}) to avoid resetting in-flight messages`
132
184
  );
133
185
  this.repository = e, this.options = {
134
186
  batchSize: s,
135
187
  maxRetries: t.maxRetries ?? 3,
136
188
  processingInterval: t.processingInterval ?? 5e3,
137
189
  priorityOrder: t.priorityOrder ?? [
138
- g.CRITICAL,
139
- g.HIGH,
140
- g.NORMAL,
141
- g.LOW
190
+ l.CRITICAL,
191
+ l.HIGH,
192
+ l.NORMAL,
193
+ l.LOW
142
194
  ],
143
- messageTimeout: r,
195
+ messageTimeout: a,
144
196
  enableLogging: t.enableLogging ?? !1,
145
197
  retryBackoff: t.retryBackoff,
146
198
  messageTypes: t.messageTypes,
147
199
  crashRecoveryIntervalMs: t.crashRecoveryIntervalMs,
148
- crashRecoveryThresholdMs: a,
200
+ crashRecoveryThresholdMs: r,
149
201
  adaptiveRepoll: t.adaptiveRepoll ?? !1,
150
202
  adaptiveRepollMaxConsecutive: t.adaptiveRepollMaxConsecutive ?? 5,
151
203
  adaptiveRepollPauseMs: t.adaptiveRepollPauseMs ?? 50,
152
204
  startupJitterMs: t.startupJitterMs ?? 0,
153
- hooks: t.hooks
205
+ hooks: t.hooks,
206
+ useClaimBatch: t.useClaimBatch ?? !1
154
207
  };
155
208
  }
156
209
  /**
@@ -162,31 +215,54 @@ class v {
162
215
  try {
163
216
  t();
164
217
  } catch (s) {
165
- const r = s instanceof Error ? s.message : String(s);
166
- this.logger.error(`Outbox hook ${e} threw and was ignored: ${r}`);
218
+ c.error(
219
+ `OutboxProcessor: hook ${e} threw and was ignored`,
220
+ s instanceof Error ? s : void 0,
221
+ { hookName: e }
222
+ );
167
223
  }
168
224
  }
169
225
  /**
170
226
  * Registers a message handler for a specific message type
171
227
  */
172
228
  registerHandler(e, t) {
173
- this.handlers.set(e, t), this.logger.debug(`Registered handler for message type: ${e}`);
229
+ this.handlers.set(e, t);
230
+ }
231
+ /**
232
+ * Registers a catch-all handler invoked when no type-specific handler matches.
233
+ *
234
+ * NOTE: Registering a default handler removes the implicit type allowlist —
235
+ * all future message types, including ones added by mistake or injected via
236
+ * compromised storage, will be dispatched to this handler. Validate
237
+ * `message.messageType` inside the handler against a known-types set if your
238
+ * domain requires an explicit allowlist.
239
+ *
240
+ * NOTE: When `messageTypes` filter option is set, the default handler handles
241
+ * only types that pass through that filter. To handle all types, leave
242
+ * `messageTypes` unset.
243
+ *
244
+ * Second/subsequent call emits a warning and replaces the previous handler.
245
+ */
246
+ registerDefaultHandler(e) {
247
+ this.defaultHandler && c.warn(
248
+ "registerDefaultHandler: replacing existing default handler — previous handler discarded"
249
+ ), this.defaultHandler = e;
174
250
  }
175
251
  /**
176
252
  * Registers middleware for message processing pipeline
177
253
  */
178
254
  use(e) {
179
- this.middlewares.push(e), this.logger.debug("Registered middleware");
255
+ this.middlewares.push(e);
180
256
  }
181
257
  /**
182
258
  * Starts the processor with automatic interval processing
183
259
  */
184
260
  start() {
185
261
  if (this.isRunning) {
186
- this.logger.warn("Processor is already running");
262
+ c.warn("OutboxProcessor: processor is already running");
187
263
  return;
188
264
  }
189
- this.isRunning = !0, this.logger.info("Starting outbox processor");
265
+ this.isRunning = !0;
190
266
  const e = this.options.startupJitterMs > 0 ? Math.floor(Math.random() * this.options.startupJitterMs) : 0;
191
267
  this.scheduleProcessing(0, this.options.processingInterval + e), this.scheduleCrashRecovery();
192
268
  }
@@ -195,10 +271,10 @@ class v {
195
271
  */
196
272
  stop() {
197
273
  if (!this.isRunning) {
198
- this.logger.warn("Processor is not running");
274
+ c.warn("OutboxProcessor: processor is not running");
199
275
  return;
200
276
  }
201
- this.isRunning = !1, this.processingTimer && (clearTimeout(this.processingTimer), this.processingTimer = void 0), this.crashRecoveryTimer && (clearTimeout(this.crashRecoveryTimer), this.crashRecoveryTimer = void 0), this.logger.info("Stopped outbox processor");
277
+ this.isRunning = !1, this.processingTimer && (clearTimeout(this.processingTimer), this.processingTimer = void 0), this.crashRecoveryTimer && (clearTimeout(this.crashRecoveryTimer), this.crashRecoveryTimer = void 0);
202
278
  }
203
279
  /**
204
280
  * Processes a single batch of pending messages.
@@ -233,65 +309,82 @@ class v {
233
309
  const e = this.options.batchSize;
234
310
  if (!this.isRunning)
235
311
  return { processed: 0, batchSize: e };
236
- const [t, s] = await d(
237
- () => this.repository.getUnprocessedMessages(
312
+ const t = this.repository.claimBatch, s = this.options.useClaimBatch === !0 && typeof t == "function";
313
+ this.options.useClaimBatch && !s && !this.warnedMissingClaimBatch && (this.warnedMissingClaimBatch = !0, c.warn(
314
+ "OutboxProcessor: useClaimBatch is enabled but the repository does not implement claimBatch() — falling back to getUnprocessedMessages (no atomic-claim protection)"
315
+ ));
316
+ const [a, r] = await g(
317
+ () => s && t ? t.call(
318
+ this.repository,
319
+ e,
320
+ this.options.priorityOrder,
321
+ this.options.messageTypes
322
+ ) : this.repository.getUnprocessedMessages(
238
323
  e,
239
324
  this.options.priorityOrder,
240
325
  this.options.messageTypes
241
326
  )
242
327
  );
243
- if (t) {
244
- const c = t instanceof Error ? t.message : String(t);
245
- return this.logger.error(`Error retrieving messages: ${c}`), { processed: 0, batchSize: e };
246
- }
247
- const r = s;
248
- if (!r || !Array.isArray(r) || r.length === 0)
249
- return this.logger.debug("No pending messages to process"), { processed: 0, batchSize: e };
250
- this.logger.info(`Processing ${r.length} messages`);
251
- const a = Date.now(), l = (await Promise.allSettled(
252
- r.map((c) => this.processMessage(c))
328
+ if (a)
329
+ return c.error(
330
+ "OutboxProcessor: error retrieving messages",
331
+ a instanceof Error ? a : void 0
332
+ ), { processed: 0, batchSize: e };
333
+ const o = r;
334
+ if (!o || !Array.isArray(o) || o.length === 0)
335
+ return { processed: 0, batchSize: e };
336
+ const h = Date.now(), m = (await Promise.allSettled(
337
+ o.map((p) => this.processMessage(p, s))
253
338
  )).filter(
254
- (c) => c.status === "rejected" || c.status === "fulfilled" && c.value === !1
339
+ (p) => p.status === "rejected" || p.status === "fulfilled" && p.value === !1
255
340
  ).length;
256
- return this.logger.info(`Completed processing batch of ${r.length} messages`), this.safelyInvokeHook(
341
+ return this.safelyInvokeHook(
257
342
  "onBatchComplete",
258
343
  () => this.options.hooks?.onBatchComplete?.({
259
- processed: r.length,
260
- failed: l,
344
+ processed: o.length,
345
+ failed: m,
261
346
  batchSize: e,
262
- durationMs: Date.now() - a
347
+ durationMs: Date.now() - h
263
348
  })
264
- ), { processed: r.length, batchSize: e };
349
+ ), { processed: o.length, batchSize: e };
265
350
  }
266
351
  /**
267
352
  * Processes a single message through the middleware pipeline.
353
+ * @param alreadyClaimed When `true`, the message was already marked
354
+ * `PROCESSING` by `IOutboxRepository.claimBatch()` — the redundant
355
+ * `updateStatus(id, PROCESSING)` call is skipped.
268
356
  * @returns `true` if the message was processed successfully, `false` if it failed.
269
357
  */
270
- async processMessage(e) {
271
- const [t] = await d(async () => {
272
- await this.repository.updateStatus(e.id, n.PROCESSING), await this.buildPipeline(e)(e), await this.repository.updateStatus(e.id, n.PROCESSED), this.logger.debug(`Successfully processed message ${e.id}`);
358
+ async processMessage(e, t = !1) {
359
+ const [s] = await g(async () => {
360
+ t || await this.repository.updateStatus(e.id, n.PROCESSING), await this.buildPipeline(e)(e), await this.repository.updateStatus(e.id, n.PROCESSED);
273
361
  });
274
- return t ? (await this.handleMessageError(e, t), !1) : !0;
362
+ return s ? (await this.handleMessageError(e, s), !1) : !0;
275
363
  }
276
364
  /**
277
365
  * Builds the middleware pipeline for message processing
278
366
  */
279
367
  buildPipeline(e) {
280
- const t = this.handlers.get(e.messageType);
368
+ const t = this.handlers.get(e.messageType) ?? this.defaultHandler;
281
369
  if (!t)
282
370
  throw new Error(`No handler registered for message type: ${e.messageType}`);
283
- const s = async (r) => {
284
- await Promise.race([
285
- t.handle(r),
286
- new Promise(
287
- (a, o) => setTimeout(
288
- () => o(new Error("Message processing timeout")),
289
- this.options.messageTimeout
290
- )
291
- )
292
- ]);
371
+ const s = async (a) => {
372
+ let r;
373
+ try {
374
+ await Promise.race([
375
+ t.handle(a),
376
+ new Promise((o, h) => {
377
+ r = setTimeout(
378
+ () => h(new Error("Message processing timeout")),
379
+ this.options.messageTimeout
380
+ );
381
+ })
382
+ ]);
383
+ } finally {
384
+ r !== void 0 && clearTimeout(r);
385
+ }
293
386
  };
294
- return this.middlewares.reduceRight((r, a) => a(r), s);
387
+ return this.middlewares.reduceRight((a, r) => r(a), s);
295
388
  }
296
389
  /**
297
390
  * Handles errors during message processing
@@ -299,24 +392,30 @@ class v {
299
392
  async handleMessageError(e, t) {
300
393
  try {
301
394
  const s = await this.repository.incrementAttempt(e.id);
302
- if (this.logger.warn(`Message ${e.id} failed, attempt ${s}: ${t?.message}`), this.safelyInvokeHook(
395
+ if (c.warn(
396
+ `OutboxProcessor: message ${e.id} failed, attempt ${s}: ${y.sanitizeLogMessage(t?.message ?? String(t))}`
397
+ ), this.safelyInvokeHook(
303
398
  "onMessageFailed",
304
399
  () => this.options.hooks?.onMessageFailed?.(e, t, s)
305
400
  ), s >= this.options.maxRetries)
306
- await this.repository.updateStatus(e.id, n.FAILED, t), this.logger.error(`Message ${e.id} marked as failed after ${s} attempts`), this.safelyInvokeHook(
401
+ await this.repository.updateStatus(e.id, n.FAILED, t), c.error("OutboxProcessor: message permanently failed", t, {
402
+ messageId: e.id,
403
+ attempts: s
404
+ }), this.safelyInvokeHook(
307
405
  "onPermanentFailure",
308
406
  () => this.options.hooks?.onPermanentFailure?.(e, t)
309
407
  );
310
408
  else if (this.options.retryBackoff) {
311
- const { initial: r, multiplier: a, maxDelay: o } = this.options.retryBackoff, l = Math.min(r * Math.pow(a, s - 1), o), c = new Date(Date.now() + l);
312
- await this.repository.updateStatus(e.id, n.PENDING), await this.repository.scheduleRetry(e.id, c), this.logger.info(
313
- `Message ${e.id} scheduled for retry in ${l}ms (attempt ${s})`
314
- );
409
+ const { initial: a, multiplier: r, maxDelay: o } = this.options.retryBackoff, h = Math.min(a * Math.pow(r, s - 1), o), u = new Date(Date.now() + h);
410
+ await this.repository.updateStatus(e.id, n.PENDING), await this.repository.scheduleRetry(e.id, u);
315
411
  } else
316
- await this.repository.updateStatus(e.id, n.PENDING), this.logger.info(`Message ${e.id} scheduled for retry (attempt ${s + 1})`);
412
+ await this.repository.updateStatus(e.id, n.PENDING);
317
413
  } catch (s) {
318
- const r = s instanceof Error ? s.message : String(s);
319
- this.logger.error(`Error updating message status: ${r}`);
414
+ c.error(
415
+ "OutboxProcessor: error updating message status",
416
+ s instanceof Error ? s : void 0,
417
+ { messageId: e.id }
418
+ );
320
419
  }
321
420
  }
322
421
  /**
@@ -335,8 +434,8 @@ class v {
335
434
  this.isRunning && (this.processingTimer = setTimeout(async () => {
336
435
  const s = await this.processBatch();
337
436
  if (this.options.adaptiveRepoll && s.processed > 0 && s.processed >= s.batchSize) {
338
- const a = e + 1, o = a > this.options.adaptiveRepollMaxConsecutive ? this.options.adaptiveRepollPauseMs : 0;
339
- this.scheduleProcessing(a, o);
437
+ const r = e + 1, o = r > this.options.adaptiveRepollMaxConsecutive ? this.options.adaptiveRepollPauseMs : 0;
438
+ this.scheduleProcessing(r, o);
340
439
  } else
341
440
  this.scheduleProcessing(0, this.options.processingInterval);
342
441
  }, t));
@@ -356,19 +455,17 @@ class v {
356
455
  * logged, never thrown — crash recovery must not crash the processor.
357
456
  */
358
457
  async runCrashRecovery() {
359
- const e = new Date(Date.now() - this.options.crashRecoveryThresholdMs), [t, s] = await d(() => this.repository.resetStaleProcessing(e));
458
+ const e = new Date(Date.now() - this.options.crashRecoveryThresholdMs), [t, s] = await g(() => this.repository.resetStaleProcessing(e));
360
459
  if (t) {
361
- const r = t instanceof Error ? t.message : String(t);
362
- this.logger.error(`Crash recovery failed: ${r}`);
460
+ c.error(
461
+ "OutboxProcessor: crash recovery failed",
462
+ t instanceof Error ? t : void 0
463
+ );
363
464
  return;
364
465
  }
365
- s && s > 0 && this.logger.warn(`Crash recovery reset ${s} stale PROCESSING message(s) to PENDING`);
366
- }
367
- /**
368
- * Logs messages if logging is enabled
369
- */
370
- log(e) {
371
- this.options.enableLogging && this.logger.info(e);
466
+ s && s > 0 && c.warn(
467
+ `OutboxProcessor: crash recovery reset ${s} stale PROCESSING message(s) to PENDING`
468
+ );
372
469
  }
373
470
  /**
374
471
  * Gets processor statistics
@@ -380,11 +477,12 @@ class v {
380
477
  maxRetries: this.options.maxRetries,
381
478
  processingInterval: this.options.processingInterval,
382
479
  registeredHandlers: Array.from(this.handlers.keys()),
383
- middlewareCount: this.middlewares.length
480
+ middlewareCount: this.middlewares.length,
481
+ hasDefaultHandler: this.defaultHandler !== void 0
384
482
  };
385
483
  }
386
484
  }
387
- class M {
485
+ class I {
388
486
  constructor(e) {
389
487
  this.eventBus = e;
390
488
  }
@@ -400,10 +498,10 @@ class M {
400
498
  await this.eventBus.publish(s);
401
499
  }
402
500
  }
403
- class R {
501
+ class S {
404
502
  constructor(e, t = {}) {
405
- this.logger = u.create("OutboxService"), this.repository = e, this.options = {
406
- defaultPriority: t.defaultPriority ?? g.NORMAL,
503
+ this.repository = e, this.options = {
504
+ defaultPriority: t.defaultPriority ?? l.NORMAL,
407
505
  defaultMetadata: t.defaultMetadata ?? {},
408
506
  autoCleanup: t.autoCleanup ?? !1,
409
507
  cleanupAfterDays: t.cleanupAfterDays ?? 7,
@@ -414,38 +512,36 @@ class R {
414
512
  * Saves a message to the outbox
415
513
  */
416
514
  async saveMessage(e, t, s) {
417
- const r = h.createMessage(e, t, {
515
+ const a = d.createMessage(e, t, {
418
516
  priority: this.options.defaultPriority,
419
517
  metadata: { ...this.options.defaultMetadata, ...s?.metadata },
420
518
  ...s
421
- }), a = await this.repository.saveMessage(r);
422
- return this.logger.debug(`Saved message ${a} of type ${e}`), a;
519
+ });
520
+ return await this.repository.saveMessage(a);
423
521
  }
424
522
  /**
425
523
  * Saves multiple messages to the outbox in a batch
426
524
  */
427
525
  async saveMessages(e) {
428
526
  const t = e.map(
429
- ({ messageType: r, payload: a, options: o }) => h.createMessage(r, a, {
527
+ ({ messageType: a, payload: r, options: o }) => d.createMessage(a, r, {
430
528
  priority: this.options.defaultPriority,
431
529
  metadata: { ...this.options.defaultMetadata, ...o?.metadata },
432
530
  ...o
433
531
  })
434
- ), s = await this.repository.saveBatch(t);
435
- return this.logger.debug(`Saved batch of ${e.length} messages`), s;
532
+ );
533
+ return await this.repository.saveBatch(t);
436
534
  }
437
535
  /**
438
536
  * Schedules a message for delayed processing
439
537
  */
440
- async scheduleMessage(e, t, s, r) {
441
- const a = h.createDelayedMessage(e, t, s, {
538
+ async scheduleMessage(e, t, s, a) {
539
+ const r = d.createDelayedMessage(e, t, s, {
442
540
  priority: this.options.defaultPriority,
443
- metadata: { ...this.options.defaultMetadata, ...r?.metadata },
444
- ...r
445
- }), o = await this.repository.scheduleMessage(a, a.processAfter);
446
- return this.logger.debug(
447
- `Scheduled message ${o} of type ${e} for processing in ${s}ms`
448
- ), o;
541
+ metadata: { ...this.options.defaultMetadata, ...a?.metadata },
542
+ ...a
543
+ });
544
+ return await this.repository.scheduleMessage(r, r.processAfter);
449
545
  }
450
546
  /**
451
547
  * Saves a high priority message to the outbox
@@ -453,7 +549,7 @@ class R {
453
549
  async saveHighPriorityMessage(e, t, s) {
454
550
  return this.saveMessage(e, t, {
455
551
  ...s,
456
- priority: g.HIGH
552
+ priority: l.HIGH
457
553
  });
458
554
  }
459
555
  /**
@@ -462,14 +558,14 @@ class R {
462
558
  async saveCriticalMessage(e, t, s) {
463
559
  return this.saveMessage(e, t, {
464
560
  ...s,
465
- priority: g.CRITICAL
561
+ priority: l.CRITICAL
466
562
  });
467
563
  }
468
564
  /**
469
565
  * Converts a domain event to an outbox message and saves it
470
566
  */
471
567
  async saveDomainEvent(e, t) {
472
- const s = h.createFromIntegrationEvent(
568
+ const s = d.createFromIntegrationEvent(
473
569
  {
474
570
  eventName: e.eventName,
475
571
  payload: e,
@@ -484,8 +580,8 @@ class R {
484
580
  metadata: { ...this.options.defaultMetadata, ...t?.metadata },
485
581
  ...t
486
582
  }
487
- ), r = await this.repository.saveMessage(s);
488
- return this.logger.debug(`Saved domain event ${e.eventName} as message ${r}`), r;
583
+ );
584
+ return await this.repository.saveMessage(s);
489
585
  }
490
586
  /**
491
587
  * Gets a message by ID
@@ -503,25 +599,23 @@ class R {
503
599
  * Retries a failed message by resetting it to pending status
504
600
  */
505
601
  async retryMessage(e) {
506
- await this.repository.updateStatus(e, n.PENDING), this.logger.info(`Reset message ${e} to pending for retry`);
602
+ await this.repository.updateStatus(e, n.PENDING);
507
603
  }
508
604
  /**
509
605
  * Retries multiple failed messages
510
606
  */
511
607
  async retryMessages(e) {
512
- await this.repository.updateStatusBatch(e, n.PENDING), this.logger.info(`Reset ${e.length} messages to pending for retry`);
608
+ await this.repository.updateStatusBatch(e, n.PENDING);
513
609
  }
514
610
  /**
515
611
  * Cleans up old processed messages
516
612
  */
517
613
  async cleanup(e) {
518
614
  const t = e ?? this.options.cleanupAfterDays, s = /* @__PURE__ */ new Date();
519
- s.setDate(s.getDate() - t);
520
- const r = await this.repository.deleteByStatusAndAge(
615
+ return s.setDate(s.getDate() - t), await this.repository.deleteByStatusAndAge(
521
616
  s,
522
617
  n.PROCESSED
523
618
  );
524
- return this.logger.info(`Cleaned up ${r} processed messages older than ${t} days`), r;
525
619
  }
526
620
  /**
527
621
  * Gets outbox statistics
@@ -543,19 +637,14 @@ class R {
543
637
  // Would need separate query
544
638
  };
545
639
  }
546
- /**
547
- * Logs messages if logging is enabled
548
- */
549
- log(e) {
550
- this.options.enableLogging && this.logger.info(e);
551
- }
552
640
  }
553
641
  export {
554
- M as EventBusOutboxHandler,
555
- m as IOutboxRepository,
556
- g as MessagePriority,
642
+ I as EventBusOutboxHandler,
643
+ w as IOutboxRepository,
644
+ l as MessagePriority,
557
645
  n as MessageStatus,
558
- h as OutboxMessageFactory,
559
- v as OutboxProcessor,
560
- R as OutboxService
646
+ d as OutboxMessageFactory,
647
+ P as OutboxProcessor,
648
+ S as OutboxService,
649
+ R as comparePriority
561
650
  };
@@ -48,3 +48,22 @@ export interface IOutboxMessageHandler<T = unknown> {
48
48
  handle(message: IOutboxMessage<T>): Promise<void>;
49
49
  }
50
50
  export type OutboxMiddleware = (next: (message: IOutboxMessage) => Promise<void>) => (message: IOutboxMessage) => Promise<void>;
51
+ /**
52
+ * Compares two MessagePriority values for sorting purposes.
53
+ *
54
+ * Returns a negative number if `a` should be processed before `b`,
55
+ * positive if `b` before `a`, 0 if equal.
56
+ *
57
+ * @param order - Processing order array (first element = highest priority).
58
+ * Defaults to [CRITICAL, HIGH, NORMAL, LOW].
59
+ * Values absent from the array sort LAST (treated as lowest priority).
60
+ * Passing a partial array is safe — missing values are not promoted.
61
+ *
62
+ * @example
63
+ * // Sort messages by priority (highest first):
64
+ * messages.sort((a, b) => comparePriority(a.priority, b.priority));
65
+ *
66
+ * // Custom order:
67
+ * messages.sort((a, b) => comparePriority(a.priority, b.priority, ['high', 'critical', 'normal', 'low']));
68
+ */
69
+ export declare function comparePriority(a: MessagePriority, b: MessagePriority, order?: MessagePriority[]): number;