@amqp-contract/worker 0.11.0 → 0.12.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.cjs CHANGED
@@ -5,36 +5,16 @@ let node_util = require("node:util");
5
5
 
6
6
  //#region src/errors.ts
7
7
  /**
8
- * Base error class for worker errors
9
- */
10
- var WorkerError = class extends Error {
11
- constructor(message) {
12
- super(message);
13
- this.name = "WorkerError";
14
- const ErrorConstructor = Error;
15
- if (typeof ErrorConstructor.captureStackTrace === "function") ErrorConstructor.captureStackTrace(this, this.constructor);
16
- }
17
- };
18
- /**
19
- * Error for technical/runtime failures in worker operations
20
- * This includes validation failures, parsing failures, and processing failures
21
- */
22
- var TechnicalError = class extends WorkerError {
23
- constructor(message, cause) {
24
- super(message);
25
- this.cause = cause;
26
- this.name = "TechnicalError";
27
- }
28
- };
29
- /**
30
8
  * Error thrown when message validation fails
31
9
  */
32
- var MessageValidationError = class extends WorkerError {
10
+ var MessageValidationError = class extends Error {
33
11
  constructor(consumerName, issues) {
34
12
  super(`Message validation failed for consumer "${consumerName}"`);
35
13
  this.consumerName = consumerName;
36
14
  this.issues = issues;
37
15
  this.name = "MessageValidationError";
16
+ const ErrorConstructor = Error;
17
+ if (typeof ErrorConstructor.captureStackTrace === "function") ErrorConstructor.captureStackTrace(this, this.constructor);
38
18
  }
39
19
  };
40
20
  /**
@@ -44,11 +24,13 @@ var MessageValidationError = class extends WorkerError {
44
24
  * Use this error type when the operation might succeed if retried.
45
25
  * The worker will apply exponential backoff and retry the message.
46
26
  */
47
- var RetryableError = class extends WorkerError {
27
+ var RetryableError = class extends Error {
48
28
  constructor(message, cause) {
49
29
  super(message);
50
30
  this.cause = cause;
51
31
  this.name = "RetryableError";
32
+ const ErrorConstructor = Error;
33
+ if (typeof ErrorConstructor.captureStackTrace === "function") ErrorConstructor.captureStackTrace(this, this.constructor);
52
34
  }
53
35
  };
54
36
  /**
@@ -58,11 +40,13 @@ var RetryableError = class extends WorkerError {
58
40
  * Use this error type when retrying would not help - the message will be
59
41
  * immediately sent to the dead letter queue (DLQ) if configured.
60
42
  */
61
- var NonRetryableError = class extends WorkerError {
43
+ var NonRetryableError = class extends Error {
62
44
  constructor(message, cause) {
63
45
  super(message);
64
46
  this.cause = cause;
65
47
  this.name = "NonRetryableError";
48
+ const ErrorConstructor = Error;
49
+ if (typeof ErrorConstructor.captureStackTrace === "function") ErrorConstructor.captureStackTrace(this, this.constructor);
66
50
  }
67
51
  };
68
52
 
@@ -71,21 +55,31 @@ var NonRetryableError = class extends WorkerError {
71
55
  const gunzipAsync = (0, node_util.promisify)(node_zlib.gunzip);
72
56
  const inflateAsync = (0, node_util.promisify)(node_zlib.inflate);
73
57
  /**
58
+ * Supported content encodings for message decompression.
59
+ */
60
+ const SUPPORTED_ENCODINGS = ["gzip", "deflate"];
61
+ /**
62
+ * Type guard to check if a string is a supported encoding.
63
+ */
64
+ function isSupportedEncoding(encoding) {
65
+ return SUPPORTED_ENCODINGS.includes(encoding.toLowerCase());
66
+ }
67
+ /**
74
68
  * Decompress a buffer based on the content-encoding header.
75
69
  *
76
70
  * @param buffer - The buffer to decompress
77
71
  * @param contentEncoding - The content-encoding header value (e.g., 'gzip', 'deflate')
78
- * @returns A promise that resolves to the decompressed buffer
79
- * @throws Error if decompression fails or if the encoding is unsupported
72
+ * @returns A Future with the decompressed buffer or a TechnicalError
80
73
  *
81
74
  * @internal
82
75
  */
83
- async function decompressBuffer(buffer, contentEncoding) {
84
- if (!contentEncoding) return buffer;
85
- switch (contentEncoding.toLowerCase()) {
86
- case "gzip": return gunzipAsync(buffer);
87
- case "deflate": return inflateAsync(buffer);
88
- default: throw new Error(`Unsupported content-encoding: ${contentEncoding}`);
76
+ function decompressBuffer(buffer, contentEncoding) {
77
+ if (!contentEncoding) return _swan_io_boxed.Future.value(_swan_io_boxed.Result.Ok(buffer));
78
+ const normalizedEncoding = contentEncoding.toLowerCase();
79
+ if (!isSupportedEncoding(normalizedEncoding)) return _swan_io_boxed.Future.value(_swan_io_boxed.Result.Error(new _amqp_contract_core.TechnicalError(`Unsupported content-encoding: "${contentEncoding}". Supported encodings are: ${SUPPORTED_ENCODINGS.join(", ")}. Please check your publisher configuration.`)));
80
+ switch (normalizedEncoding) {
81
+ case "gzip": return _swan_io_boxed.Future.fromPromise(gunzipAsync(buffer)).mapError((error) => new _amqp_contract_core.TechnicalError("Failed to decompress gzip", error));
82
+ case "deflate": return _swan_io_boxed.Future.fromPromise(inflateAsync(buffer)).mapError((error) => new _amqp_contract_core.TechnicalError("Failed to decompress deflate", error));
89
83
  }
90
84
  }
91
85
 
@@ -98,17 +92,6 @@ function isHandlerTuple(entry) {
98
92
  return Array.isArray(entry) && entry.length === 2;
99
93
  }
100
94
  /**
101
- * Type guard to check if a value is a Standard Schema v1 compliant schema.
102
- */
103
- function isStandardSchema(value) {
104
- if (typeof value !== "object" || value === null) return false;
105
- if (!("~standard" in value)) return false;
106
- const standard = value["~standard"];
107
- if (typeof standard !== "object" || standard === null) return false;
108
- if (!("validate" in standard)) return false;
109
- return typeof standard.validate === "function";
110
- }
111
- /**
112
95
  * Type-safe AMQP worker for consuming messages from RabbitMQ.
113
96
  *
114
97
  * This class provides automatic message validation, connection management,
@@ -204,7 +187,7 @@ var TypedAmqpWorker = class TypedAmqpWorker {
204
187
  urls,
205
188
  connectionOptions
206
189
  }), handlers, logger, telemetry);
207
- return worker.waitForConnectionReady().flatMapOk(() => worker.validateRetryConfiguration()).flatMapOk(() => worker.consumeAll()).mapOk(() => worker);
190
+ return worker.waitForConnectionReady().flatMapOk(() => worker.consumeAll()).mapOk(() => worker);
208
191
  }
209
192
  /**
210
193
  * Close the AMQP channel and connection.
@@ -223,7 +206,7 @@ var TypedAmqpWorker = class TypedAmqpWorker {
223
206
  * ```
224
207
  */
225
208
  close() {
226
- return _swan_io_boxed.Future.all(Array.from(this.consumerTags).map((consumerTag) => _swan_io_boxed.Future.fromPromise(this.amqpClient.channel.cancel(consumerTag)).mapErrorToResult((error) => {
209
+ return _swan_io_boxed.Future.all(Array.from(this.consumerTags).map((consumerTag) => this.amqpClient.cancel(consumerTag).mapErrorToResult((error) => {
227
210
  this.logger?.warn("Failed to cancel consumer during close", {
228
211
  consumerTag,
229
212
  error
@@ -231,213 +214,103 @@ var TypedAmqpWorker = class TypedAmqpWorker {
231
214
  return _swan_io_boxed.Result.Ok(void 0);
232
215
  }))).map(_swan_io_boxed.Result.all).tapOk(() => {
233
216
  this.consumerTags.clear();
234
- }).flatMapOk(() => _swan_io_boxed.Future.fromPromise(this.amqpClient.close())).mapError((error) => new TechnicalError("Failed to close AMQP connection", error)).mapOk(() => void 0);
217
+ }).flatMapOk(() => this.amqpClient.close()).mapOk(() => void 0);
235
218
  }
236
219
  /**
237
- * Validate retry configuration for all consumers.
238
- *
239
- * For quorum-native mode, validates that the queue is properly configured.
240
- * For TTL-backoff mode, wait queues are created by setupAmqpTopology in the core package.
241
- */
242
- validateRetryConfiguration() {
243
- if (!this.contract.consumers) return _swan_io_boxed.Future.value(_swan_io_boxed.Result.Ok(void 0));
244
- for (const consumerName of Object.keys(this.contract.consumers)) {
245
- const consumer = this.contract.consumers[consumerName];
246
- if (!consumer) continue;
247
- const queue = consumer.queue;
248
- if ((queue.retry?.mode ?? "ttl-backoff") === "quorum-native") {
249
- const validationError = this.validateQuorumNativeConfigForConsumer(String(consumerName), consumer);
250
- if (validationError) return _swan_io_boxed.Future.value(_swan_io_boxed.Result.Error(validationError));
251
- this.logger?.info("Using quorum-native retry mode", {
252
- consumerName: String(consumerName),
253
- queueName: queue.name
254
- });
255
- } else if (queue.deadLetter) this.logger?.info("Using TTL-backoff retry mode", {
256
- consumerName: String(consumerName),
257
- queueName: queue.name
258
- });
259
- else this.logger?.warn("Queue has no deadLetter configured - retries will use nack with requeue", {
260
- consumerName: String(consumerName),
261
- queueName: queue.name
262
- });
263
- }
264
- return _swan_io_boxed.Future.value(_swan_io_boxed.Result.Ok(void 0));
265
- }
266
- /**
267
- * Get the resolved retry configuration for a consumer's queue.
268
- * Reads retry config from the queue definition in the contract.
220
+ * Get the retry configuration for a consumer's queue.
221
+ * Defaults are applied in the contract's defineQueue, so we just return the config.
269
222
  */
270
223
  getRetryConfigForConsumer(consumer) {
271
- const retryOptions = consumer.queue.retry;
272
- if (retryOptions?.mode === "quorum-native") return {
273
- mode: "quorum-native",
274
- maxRetries: 0,
275
- initialDelayMs: 0,
276
- maxDelayMs: 0,
277
- backoffMultiplier: 0,
278
- jitter: false
279
- };
280
- if (retryOptions?.mode === "ttl-backoff") return {
281
- mode: "ttl-backoff",
282
- maxRetries: retryOptions.maxRetries ?? 3,
283
- initialDelayMs: retryOptions.initialDelayMs ?? 1e3,
284
- maxDelayMs: retryOptions.maxDelayMs ?? 3e4,
285
- backoffMultiplier: retryOptions.backoffMultiplier ?? 2,
286
- jitter: retryOptions.jitter ?? true
287
- };
288
- return {
289
- mode: "ttl-backoff",
290
- maxRetries: 3,
291
- initialDelayMs: 1e3,
292
- maxDelayMs: 3e4,
293
- backoffMultiplier: 2,
294
- jitter: true
295
- };
224
+ return consumer.queue.retry;
296
225
  }
297
226
  /**
298
- * Validate that quorum-native retry mode is properly configured for a specific consumer.
299
- *
300
- * Requirements for quorum-native mode:
301
- * - Consumer queue must be a quorum queue
302
- * - Consumer queue must have deliveryLimit configured
303
- * - Consumer queue should have DLX configured (warning if not)
304
- *
305
- * @returns TechnicalError if validation fails, null if valid
306
- */
307
- validateQuorumNativeConfigForConsumer(consumerName, consumer) {
308
- const queue = consumer.queue;
309
- if (queue.type !== "quorum") return new TechnicalError(`Consumer "${consumerName}" uses queue "${queue.name}" with type "${queue.type}". Quorum-native retry mode requires quorum queues.`);
310
- if (queue.deliveryLimit === void 0) return new TechnicalError(`Consumer "${consumerName}" uses queue "${queue.name}" without deliveryLimit configured. Quorum-native retry mode requires deliveryLimit to be set on the queue definition.`);
311
- if (!queue.deadLetter) this.logger?.warn(`Consumer "${consumerName}" uses queue "${queue.name}" without deadLetter configured. Messages exceeding deliveryLimit will be dropped instead of dead-lettered.`);
312
- return null;
313
- }
314
- /**
315
- * Start consuming messages for all consumers
227
+ * Start consuming messages for all consumers.
228
+ * TypeScript guarantees consumers exist (handlers require matching consumers).
316
229
  */
317
230
  consumeAll() {
318
- if (!this.contract.consumers) return _swan_io_boxed.Future.value(_swan_io_boxed.Result.Error(new TechnicalError("No consumers defined in contract")));
319
- const consumerNames = Object.keys(this.contract.consumers);
320
- let maxPrefetch = 0;
321
- for (const consumerName of consumerNames) {
322
- const options = this.consumerOptions[consumerName];
323
- if (options?.prefetch !== void 0) {
324
- if (options.prefetch <= 0 || !Number.isInteger(options.prefetch)) return _swan_io_boxed.Future.value(_swan_io_boxed.Result.Error(new TechnicalError(`Invalid prefetch value for "${String(consumerName)}": must be a positive integer`)));
325
- maxPrefetch = Math.max(maxPrefetch, options.prefetch);
326
- }
327
- }
328
- if (maxPrefetch > 0) this.amqpClient.channel.addSetup(async (channel) => {
231
+ const consumers = this.contract.consumers;
232
+ const consumerNames = Object.keys(consumers);
233
+ const maxPrefetch = consumerNames.reduce((max, name) => {
234
+ const prefetch = this.consumerOptions[name]?.prefetch;
235
+ return prefetch ? Math.max(max, prefetch) : max;
236
+ }, 0);
237
+ if (maxPrefetch > 0) this.amqpClient.addSetup(async (channel) => {
329
238
  await channel.prefetch(maxPrefetch);
330
239
  });
331
- return _swan_io_boxed.Future.all(consumerNames.map((consumerName) => this.consume(consumerName))).map(_swan_io_boxed.Result.all).mapOk(() => void 0);
240
+ return _swan_io_boxed.Future.all(consumerNames.map((name) => this.consume(name))).map(_swan_io_boxed.Result.all).mapOk(() => void 0);
332
241
  }
333
242
  waitForConnectionReady() {
334
- return _swan_io_boxed.Future.fromPromise(this.amqpClient.channel.waitForConnect()).mapError((error) => new TechnicalError("Failed to wait for connection ready", error));
243
+ return this.amqpClient.waitForConnect();
335
244
  }
336
245
  /**
337
- * Start consuming messages for a specific consumer
246
+ * Start consuming messages for a specific consumer.
247
+ * TypeScript guarantees consumer and handler exist for valid consumer names.
338
248
  */
339
249
  consume(consumerName) {
340
- const consumers = this.contract.consumers;
341
- if (!consumers) return _swan_io_boxed.Future.value(_swan_io_boxed.Result.Error(new TechnicalError("No consumers defined in contract")));
342
- const consumer = consumers[consumerName];
343
- if (!consumer) {
344
- const availableConsumers = Object.keys(consumers);
345
- const available = availableConsumers.length > 0 ? availableConsumers.join(", ") : "none";
346
- return _swan_io_boxed.Future.value(_swan_io_boxed.Result.Error(new TechnicalError(`Consumer not found: "${String(consumerName)}". Available consumers: ${available}`)));
347
- }
250
+ const consumer = this.contract.consumers[consumerName];
348
251
  const handler = this.actualHandlers[consumerName];
349
- if (!handler) return _swan_io_boxed.Future.value(_swan_io_boxed.Result.Error(new TechnicalError(`Handler for "${String(consumerName)}" not provided`)));
350
252
  return this.consumeSingle(consumerName, consumer, handler);
351
253
  }
352
254
  /**
353
- * Parse and validate a message from AMQP
354
- * @returns `Future<Result<consumed message, void>>` - Ok with validated consumed message (payload + headers), or Error (already handled with nack)
255
+ * Validate data against a Standard Schema and handle errors.
355
256
  */
356
- parseAndValidateMessage(msg, consumer, consumerName) {
357
- const decompressMessage = _swan_io_boxed.Future.fromPromise(decompressBuffer(msg.content, msg.properties.contentEncoding)).tapError((error) => {
358
- this.logger?.error("Error decompressing message", {
359
- consumerName: String(consumerName),
360
- queueName: consumer.queue.name,
361
- contentEncoding: msg.properties.contentEncoding,
257
+ validateSchema(schema, data, context, msg) {
258
+ const rawValidation = schema["~standard"].validate(data);
259
+ const validationPromise = rawValidation instanceof Promise ? rawValidation : Promise.resolve(rawValidation);
260
+ return _swan_io_boxed.Future.fromPromise(validationPromise).mapError((error) => new _amqp_contract_core.TechnicalError(`Error validating ${context.field}`, error)).mapOkToResult((result) => {
261
+ if (result.issues) return _swan_io_boxed.Result.Error(new _amqp_contract_core.TechnicalError(`${context.field} validation failed`, new MessageValidationError(context.consumerName, result.issues)));
262
+ return _swan_io_boxed.Result.Ok(result.value);
263
+ }).tapError((error) => {
264
+ this.logger?.error(`${context.field} validation failed`, {
265
+ consumerName: context.consumerName,
266
+ queueName: context.queueName,
362
267
  error
363
268
  });
364
- this.amqpClient.channel.nack(msg, false, false);
365
- }).mapError(() => void 0);
366
- const parseMessage = (buffer) => {
367
- const parseResult = _swan_io_boxed.Result.fromExecution(() => JSON.parse(buffer.toString()));
368
- if (parseResult.isError()) {
369
- this.logger?.error("Error parsing message", {
370
- consumerName: String(consumerName),
371
- queueName: consumer.queue.name,
372
- error: parseResult.error
373
- });
374
- this.amqpClient.channel.nack(msg, false, false);
375
- return _swan_io_boxed.Future.value(_swan_io_boxed.Result.Error(void 0));
376
- }
377
- return _swan_io_boxed.Future.value(_swan_io_boxed.Result.Ok(parseResult.value));
378
- };
379
- const validatePayload = (parsedMessage) => {
380
- const rawValidation = consumer.message.payload["~standard"].validate(parsedMessage);
381
- return _swan_io_boxed.Future.fromPromise(rawValidation instanceof Promise ? rawValidation : Promise.resolve(rawValidation)).mapError(() => void 0).mapOkToResult((validationResult) => {
382
- if (validationResult.issues) {
383
- const error = new MessageValidationError(String(consumerName), validationResult.issues);
384
- this.logger?.error("Message payload validation failed", {
385
- consumerName: String(consumerName),
386
- queueName: consumer.queue.name,
387
- error
388
- });
389
- this.amqpClient.channel.nack(msg, false, false);
390
- return _swan_io_boxed.Result.Error(void 0);
391
- }
392
- return _swan_io_boxed.Result.Ok(validationResult.value);
393
- });
269
+ this.amqpClient.nack(msg, false, false);
270
+ });
271
+ }
272
+ /**
273
+ * Parse and validate a message from AMQP.
274
+ * @returns Ok with validated message (payload + headers), or Error (message already nacked)
275
+ */
276
+ parseAndValidateMessage(msg, consumer, consumerName) {
277
+ const context = {
278
+ consumerName: String(consumerName),
279
+ queueName: consumer.queue.name
394
280
  };
395
- const validateHeaders = () => {
396
- const headersSchema = consumer.message.headers;
397
- if (!headersSchema) return _swan_io_boxed.Future.value(_swan_io_boxed.Result.Ok(void 0));
398
- if (!isStandardSchema(headersSchema)) {
399
- const error = new MessageValidationError(String(consumerName), "Invalid headers schema: not a Standard Schema v1 compliant schema");
400
- this.logger?.error("Message headers validation failed", {
401
- consumerName: String(consumerName),
402
- queueName: consumer.queue.name,
403
- error
404
- });
405
- this.amqpClient.channel.nack(msg, false, false);
406
- return _swan_io_boxed.Future.value(_swan_io_boxed.Result.Error(void 0));
407
- }
408
- const validSchema = headersSchema;
409
- const rawHeaders = msg.properties.headers ?? {};
410
- const rawValidation = validSchema["~standard"].validate(rawHeaders);
411
- return _swan_io_boxed.Future.fromPromise(rawValidation instanceof Promise ? rawValidation : Promise.resolve(rawValidation)).mapError(() => void 0).mapOkToResult((validationResult) => {
412
- if (validationResult.issues) {
413
- const error = new MessageValidationError(String(consumerName), validationResult.issues);
414
- this.logger?.error("Message headers validation failed", {
415
- consumerName: String(consumerName),
416
- queueName: consumer.queue.name,
417
- error
418
- });
419
- this.amqpClient.channel.nack(msg, false, false);
420
- return _swan_io_boxed.Result.Error(void 0);
421
- }
422
- return _swan_io_boxed.Result.Ok(validationResult.value);
281
+ const nackAndError = (message, error) => {
282
+ this.logger?.error(message, {
283
+ ...context,
284
+ error
423
285
  });
286
+ this.amqpClient.nack(msg, false, false);
287
+ return new _amqp_contract_core.TechnicalError(message, error);
424
288
  };
425
- const buildConsumedMessage = (validatedPayload) => {
426
- return validateHeaders().mapOk((validatedHeaders) => {
427
- return {
428
- payload: validatedPayload,
429
- headers: validatedHeaders
430
- };
289
+ const parsePayload = decompressBuffer(msg.content, msg.properties.contentEncoding).tapError((error) => {
290
+ this.logger?.error("Failed to decompress message", {
291
+ ...context,
292
+ error
431
293
  });
432
- };
433
- return decompressMessage.flatMapOk(parseMessage).flatMapOk(validatePayload).flatMapOk(buildConsumedMessage);
294
+ this.amqpClient.nack(msg, false, false);
295
+ }).mapOkToResult((buffer) => _swan_io_boxed.Result.fromExecution(() => JSON.parse(buffer.toString())).mapError((error) => nackAndError("Failed to parse JSON", error))).flatMapOk((parsed) => this.validateSchema(consumer.message.payload, parsed, {
296
+ ...context,
297
+ field: "payload"
298
+ }, msg));
299
+ const parseHeaders = consumer.message.headers ? this.validateSchema(consumer.message.headers, msg.properties.headers ?? {}, {
300
+ ...context,
301
+ field: "headers"
302
+ }, msg) : _swan_io_boxed.Future.value(_swan_io_boxed.Result.Ok(void 0));
303
+ return _swan_io_boxed.Future.allFromDict({
304
+ payload: parsePayload,
305
+ headers: parseHeaders
306
+ }).map(_swan_io_boxed.Result.allFromDict);
434
307
  }
435
308
  /**
436
309
  * Consume messages one at a time
437
310
  */
438
311
  consumeSingle(consumerName, consumer, handler) {
439
312
  const queueName = consumer.queue.name;
440
- return _swan_io_boxed.Future.fromPromise(this.amqpClient.channel.consume(queueName, async (msg) => {
313
+ return this.amqpClient.consume(queueName, async (msg) => {
441
314
  if (msg === null) {
442
315
  this.logger?.warn("Consumer cancelled by server", {
443
316
  consumerName: String(consumerName),
@@ -452,7 +325,7 @@ var TypedAmqpWorker = class TypedAmqpWorker {
452
325
  consumerName: String(consumerName),
453
326
  queueName
454
327
  });
455
- this.amqpClient.channel.ack(msg);
328
+ this.amqpClient.ack(msg);
456
329
  const durationMs = Date.now() - startTime;
457
330
  (0, _amqp_contract_core.endSpanSuccess)(span);
458
331
  (0, _amqp_contract_core.recordConsumeMetric)(this.telemetry, queueName, String(consumerName), true, durationMs);
@@ -473,9 +346,9 @@ var TypedAmqpWorker = class TypedAmqpWorker {
473
346
  (0, _amqp_contract_core.endSpanError)(span, /* @__PURE__ */ new Error("Message validation failed"));
474
347
  (0, _amqp_contract_core.recordConsumeMetric)(this.telemetry, queueName, String(consumerName), false, durationMs);
475
348
  }).toPromise();
476
- })).tapOk((reply) => {
477
- this.consumerTags.add(reply.consumerTag);
478
- }).mapError((error) => new TechnicalError(`Failed to start consuming for "${String(consumerName)}"`, error)).mapOk(() => void 0);
349
+ }).tapOk((consumerTag) => {
350
+ this.consumerTags.add(consumerTag);
351
+ }).mapError((error) => new _amqp_contract_core.TechnicalError(`Failed to start consuming for "${String(consumerName)}"`, error)).mapOk(() => void 0);
479
352
  }
480
353
  /**
481
354
  * Handle error in message processing with retry logic.
@@ -540,7 +413,7 @@ var TypedAmqpWorker = class TypedAmqpWorker {
540
413
  attemptsBeforeDeadLetter,
541
414
  error: error.message
542
415
  });
543
- this.amqpClient.channel.nack(msg, false, true);
416
+ this.amqpClient.nack(msg, false, true);
544
417
  return _swan_io_boxed.Future.value(_swan_io_boxed.Result.Ok(void 0));
545
418
  }
546
419
  /**
@@ -622,14 +495,14 @@ var TypedAmqpWorker = class TypedAmqpWorker {
622
495
  const deadLetter = consumer.queue.deadLetter;
623
496
  if (!deadLetter) {
624
497
  this.logger?.warn("Cannot retry: queue does not have DLX configured, falling back to nack with requeue", { queueName });
625
- this.amqpClient.channel.nack(msg, false, true);
498
+ this.amqpClient.nack(msg, false, true);
626
499
  return _swan_io_boxed.Future.value(_swan_io_boxed.Result.Ok(void 0));
627
500
  }
628
501
  const dlxName = deadLetter.exchange.name;
629
502
  const waitRoutingKey = `${queueName}-wait`;
630
- this.amqpClient.channel.ack(msg);
503
+ this.amqpClient.ack(msg);
631
504
  const content = this.parseMessageContentForRetry(msg, queueName);
632
- return _swan_io_boxed.Future.fromPromise(this.amqpClient.channel.publish(dlxName, waitRoutingKey, content, {
505
+ return this.amqpClient.publish(dlxName, waitRoutingKey, content, {
633
506
  ...msg.properties,
634
507
  expiration: delayMs.toString(),
635
508
  headers: {
@@ -638,14 +511,14 @@ var TypedAmqpWorker = class TypedAmqpWorker {
638
511
  "x-last-error": error.message,
639
512
  "x-first-failure-timestamp": msg.properties.headers?.["x-first-failure-timestamp"] ?? Date.now()
640
513
  }
641
- })).mapError((error$1) => new TechnicalError("Failed to publish message for retry", error$1)).mapOkToResult((published) => {
514
+ }).mapOkToResult((published) => {
642
515
  if (!published) {
643
516
  this.logger?.error("Failed to publish message for retry (write buffer full)", {
644
517
  queueName,
645
518
  waitRoutingKey,
646
519
  retryCount: newRetryCount
647
520
  });
648
- return _swan_io_boxed.Result.Error(new TechnicalError("Failed to publish message for retry (write buffer full)"));
521
+ return _swan_io_boxed.Result.Error(new _amqp_contract_core.TechnicalError("Failed to publish message for retry (write buffer full)"));
649
522
  }
650
523
  this.logger?.info("Message published for retry", {
651
524
  queueName,
@@ -667,7 +540,7 @@ var TypedAmqpWorker = class TypedAmqpWorker {
667
540
  queueName,
668
541
  deliveryTag: msg.fields.deliveryTag
669
542
  });
670
- this.amqpClient.channel.nack(msg, false, false);
543
+ this.amqpClient.nack(msg, false, false);
671
544
  }
672
545
  };
673
546
 
@@ -736,7 +609,6 @@ function defineHandlers(contract, handlers) {
736
609
  exports.MessageValidationError = MessageValidationError;
737
610
  exports.NonRetryableError = NonRetryableError;
738
611
  exports.RetryableError = RetryableError;
739
- exports.TechnicalError = TechnicalError;
740
612
  exports.TypedAmqpWorker = TypedAmqpWorker;
741
613
  exports.defineHandler = defineHandler;
742
614
  exports.defineHandlers = defineHandlers;
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { Logger, TelemetryProvider } from "@amqp-contract/core";
1
+ import { Logger, TechnicalError, TelemetryProvider } from "@amqp-contract/core";
2
2
  import { AmqpConnectionManagerOptions, ConnectionUrl } from "amqp-connection-manager";
3
3
  import { ConsumerDefinition, ContractDefinition, InferConsumerNames, MessageDefinition } from "@amqp-contract/contract";
4
4
  import { Future, Result } from "@swan-io/boxed";
@@ -6,24 +6,10 @@ import { ConsumeMessage } from "amqplib";
6
6
  import { StandardSchemaV1 } from "@standard-schema/spec";
7
7
 
8
8
  //#region src/errors.d.ts
9
- /**
10
- * Base error class for worker errors
11
- */
12
- declare abstract class WorkerError extends Error {
13
- protected constructor(message: string);
14
- }
15
- /**
16
- * Error for technical/runtime failures in worker operations
17
- * This includes validation failures, parsing failures, and processing failures
18
- */
19
- declare class TechnicalError extends WorkerError {
20
- readonly cause?: unknown | undefined;
21
- constructor(message: string, cause?: unknown | undefined);
22
- }
23
9
  /**
24
10
  * Error thrown when message validation fails
25
11
  */
26
- declare class MessageValidationError extends WorkerError {
12
+ declare class MessageValidationError extends Error {
27
13
  readonly consumerName: string;
28
14
  readonly issues: unknown;
29
15
  constructor(consumerName: string, issues: unknown);
@@ -35,7 +21,7 @@ declare class MessageValidationError extends WorkerError {
35
21
  * Use this error type when the operation might succeed if retried.
36
22
  * The worker will apply exponential backoff and retry the message.
37
23
  */
38
- declare class RetryableError extends WorkerError {
24
+ declare class RetryableError extends Error {
39
25
  readonly cause?: unknown | undefined;
40
26
  constructor(message: string, cause?: unknown | undefined);
41
27
  }
@@ -46,7 +32,7 @@ declare class RetryableError extends WorkerError {
46
32
  * Use this error type when retrying would not help - the message will be
47
33
  * immediately sent to the dead letter queue (DLQ) if configured.
48
34
  */
49
- declare class NonRetryableError extends WorkerError {
35
+ declare class NonRetryableError extends Error {
50
36
  readonly cause?: unknown | undefined;
51
37
  constructor(message: string, cause?: unknown | undefined);
52
38
  }
@@ -320,40 +306,28 @@ declare class TypedAmqpWorker<TContract extends ContractDefinition> {
320
306
  */
321
307
  close(): Future<Result<void, TechnicalError>>;
322
308
  /**
323
- * Validate retry configuration for all consumers.
324
- *
325
- * For quorum-native mode, validates that the queue is properly configured.
326
- * For TTL-backoff mode, wait queues are created by setupAmqpTopology in the core package.
327
- */
328
- private validateRetryConfiguration;
329
- /**
330
- * Get the resolved retry configuration for a consumer's queue.
331
- * Reads retry config from the queue definition in the contract.
309
+ * Get the retry configuration for a consumer's queue.
310
+ * Defaults are applied in the contract's defineQueue, so we just return the config.
332
311
  */
333
312
  private getRetryConfigForConsumer;
334
313
  /**
335
- * Validate that quorum-native retry mode is properly configured for a specific consumer.
336
- *
337
- * Requirements for quorum-native mode:
338
- * - Consumer queue must be a quorum queue
339
- * - Consumer queue must have deliveryLimit configured
340
- * - Consumer queue should have DLX configured (warning if not)
341
- *
342
- * @returns TechnicalError if validation fails, null if valid
343
- */
344
- private validateQuorumNativeConfigForConsumer;
345
- /**
346
- * Start consuming messages for all consumers
314
+ * Start consuming messages for all consumers.
315
+ * TypeScript guarantees consumers exist (handlers require matching consumers).
347
316
  */
348
317
  private consumeAll;
349
318
  private waitForConnectionReady;
350
319
  /**
351
- * Start consuming messages for a specific consumer
320
+ * Start consuming messages for a specific consumer.
321
+ * TypeScript guarantees consumer and handler exist for valid consumer names.
352
322
  */
353
323
  private consume;
354
324
  /**
355
- * Parse and validate a message from AMQP
356
- * @returns `Future<Result<consumed message, void>>` - Ok with validated consumed message (payload + headers), or Error (already handled with nack)
325
+ * Validate data against a Standard Schema and handle errors.
326
+ */
327
+ private validateSchema;
328
+ /**
329
+ * Parse and validate a message from AMQP.
330
+ * @returns Ok with validated message (payload + headers), or Error (message already nacked)
357
331
  */
358
332
  private parseAndValidateMessage;
359
333
  /**
@@ -518,5 +492,5 @@ declare function defineHandler<TContract extends ContractDefinition, TName exten
518
492
  */
519
493
  declare function defineHandlers<TContract extends ContractDefinition>(contract: TContract, handlers: WorkerInferSafeConsumerHandlers<TContract>): WorkerInferSafeConsumerHandlers<TContract>;
520
494
  //#endregion
521
- export { type CreateWorkerOptions, type HandlerError, MessageValidationError, NonRetryableError, RetryableError, TechnicalError, TypedAmqpWorker, type WorkerConsumedMessage, type WorkerInferConsumedMessage, type WorkerInferConsumerHeaders, type WorkerInferSafeConsumerHandler, type WorkerInferSafeConsumerHandlerEntry, type WorkerInferSafeConsumerHandlers, defineHandler, defineHandlers };
495
+ export { type CreateWorkerOptions, type HandlerError, MessageValidationError, NonRetryableError, RetryableError, TypedAmqpWorker, type WorkerConsumedMessage, type WorkerInferConsumedMessage, type WorkerInferConsumerHeaders, type WorkerInferSafeConsumerHandler, type WorkerInferSafeConsumerHandlerEntry, type WorkerInferSafeConsumerHandlers, defineHandler, defineHandlers };
522
496
  //# sourceMappingURL=index.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../src/errors.ts","../src/types.ts","../src/worker.ts","../src/handlers.ts"],"sourcesContent":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/errors.ts","../src/types.ts","../src/worker.ts","../src/handlers.ts"],"sourcesContent":[],"mappings":"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FjDA,CAAA,kBEkDI,kBFlDJ,EAAA,cEmDA,kBFnDA,CEmDmB,SFnDnB,CAAA,CAAA,CAAA,QAAA,EEqDJ,SFrDI,EAAA,YAAA,EEsDA,KFtDA,EAAA,OAAA,EEuDL,8BFvDK,CEuD0B,SFvD1B,EEuDqC,KFvDrC,CAAA,CAAA,EEwDb,mCFxDa,CEwDuB,SFxDvB,EEwDkC,KFxDlC,CAAA;AAC4B,iBEwD5B,aFxD4B,CAAA,kBEyDxB,kBFzDwB,EAAA,cE0D5B,kBF1D4B,CE0DT,SF1DS,CAAA,CAAA,CAAA,QAAA,EE4DhC,SF5DgC,EAAA,YAAA,EE6D5B,KF7D4B,EAAA,OAAA,EE8DjC,8BF9DiC,CE8DF,SF9DE,EE8DS,KF9DT,CAAA,EAAA,OAAA,EAAA;EAAW,QAAA,CAAA,EAAA,MAAA;CAAzB,CAAA,EEgE3B,mCFhE2B,CEgES,SFhET,EEgEoB,KFhEpB,CAAA;;;AAM9B;;;;;;;;;AAyBA;;;;;AAWA;;;;;;;;;;;;AAoCA;AACoB,iBEgCJ,cFhCI,CAAA,kBEgC6B,kBFhC7B,CAAA,CAAA,QAAA,EEiCR,SFjCQ,EAAA,QAAA,EEkCR,+BFlCQ,CEkCwB,SFlCxB,CAAA,CAAA,EEmCjB,+BFnCiB,CEmCe,SFnCf,CAAA"}