@amqp-contract/worker 0.17.0 → 0.19.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 +7 -15
- package/dist/index.d.cts +2 -22
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +2 -22
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -14
- package/dist/index.mjs.map +1 -1
- package/docs/index.md +60 -111
- package/package.json +6 -6
package/dist/index.cjs
CHANGED
|
@@ -7,19 +7,6 @@ let node_util = require("node:util");
|
|
|
7
7
|
|
|
8
8
|
//#region src/errors.ts
|
|
9
9
|
/**
|
|
10
|
-
* Error thrown when message validation fails
|
|
11
|
-
*/
|
|
12
|
-
var MessageValidationError = class extends Error {
|
|
13
|
-
constructor(consumerName, issues) {
|
|
14
|
-
super(`Message validation failed for consumer "${consumerName}"`);
|
|
15
|
-
this.consumerName = consumerName;
|
|
16
|
-
this.issues = issues;
|
|
17
|
-
this.name = "MessageValidationError";
|
|
18
|
-
const ErrorConstructor = Error;
|
|
19
|
-
if (typeof ErrorConstructor.captureStackTrace === "function") ErrorConstructor.captureStackTrace(this, this.constructor);
|
|
20
|
-
}
|
|
21
|
-
};
|
|
22
|
-
/**
|
|
23
10
|
* Retryable errors - transient failures that may succeed on retry
|
|
24
11
|
* Examples: network timeouts, rate limiting, temporary service unavailability
|
|
25
12
|
*
|
|
@@ -389,7 +376,7 @@ var TypedAmqpWorker = class TypedAmqpWorker {
|
|
|
389
376
|
const rawValidation = schema["~standard"].validate(data);
|
|
390
377
|
const validationPromise = rawValidation instanceof Promise ? rawValidation : Promise.resolve(rawValidation);
|
|
391
378
|
return _swan_io_boxed.Future.fromPromise(validationPromise).mapError((error) => new _amqp_contract_core.TechnicalError(`Error validating ${context.field}`, error)).mapOkToResult((result) => {
|
|
392
|
-
if (result.issues) return _swan_io_boxed.Result.Error(new _amqp_contract_core.TechnicalError(`${context.field} validation failed`, new MessageValidationError(context.consumerName, result.issues)));
|
|
379
|
+
if (result.issues) return _swan_io_boxed.Result.Error(new _amqp_contract_core.TechnicalError(`${context.field} validation failed`, new _amqp_contract_core.MessageValidationError(context.consumerName, result.issues)));
|
|
393
380
|
return _swan_io_boxed.Result.Ok(result.value);
|
|
394
381
|
}).tapError((error) => {
|
|
395
382
|
this.logger?.error(`${context.field} validation failed`, {
|
|
@@ -737,7 +724,12 @@ function defineHandlers(contract, handlers) {
|
|
|
737
724
|
}
|
|
738
725
|
|
|
739
726
|
//#endregion
|
|
740
|
-
exports
|
|
727
|
+
Object.defineProperty(exports, 'MessageValidationError', {
|
|
728
|
+
enumerable: true,
|
|
729
|
+
get: function () {
|
|
730
|
+
return _amqp_contract_core.MessageValidationError;
|
|
731
|
+
}
|
|
732
|
+
});
|
|
741
733
|
exports.NonRetryableError = NonRetryableError;
|
|
742
734
|
exports.RetryableError = RetryableError;
|
|
743
735
|
exports.TypedAmqpWorker = TypedAmqpWorker;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Logger, TechnicalError, TelemetryProvider } from "@amqp-contract/core";
|
|
1
|
+
import { Logger, MessageValidationError, TechnicalError, TelemetryProvider } from "@amqp-contract/core";
|
|
2
2
|
import * as amqp from "amqplib";
|
|
3
3
|
import { ConsumeMessage } from "amqplib";
|
|
4
4
|
import { TcpSocketConnectOpts } from "net";
|
|
@@ -49,14 +49,6 @@ interface AmqpConnectionManagerOptions {
|
|
|
49
49
|
}
|
|
50
50
|
//#endregion
|
|
51
51
|
//#region src/errors.d.ts
|
|
52
|
-
/**
|
|
53
|
-
* Error thrown when message validation fails
|
|
54
|
-
*/
|
|
55
|
-
declare class MessageValidationError extends Error {
|
|
56
|
-
readonly consumerName: string;
|
|
57
|
-
readonly issues: unknown;
|
|
58
|
-
constructor(consumerName: string, issues: unknown);
|
|
59
|
-
}
|
|
60
52
|
/**
|
|
61
53
|
* Retryable errors - transient failures that may succeed on retry
|
|
62
54
|
* Examples: network timeouts, rate limiting, temporary service unavailability
|
|
@@ -323,18 +315,6 @@ type WorkerInferConsumerHandlerEntry<TContract extends ContractDefinition, TName
|
|
|
323
315
|
* ```
|
|
324
316
|
*/
|
|
325
317
|
type WorkerInferConsumerHandlers<TContract extends ContractDefinition> = { [K in InferConsumerNames<TContract>]: WorkerInferConsumerHandlerEntry<TContract, K> };
|
|
326
|
-
/**
|
|
327
|
-
* @deprecated Use `WorkerInferConsumerHandler` instead. Will be removed in next major version.
|
|
328
|
-
*/
|
|
329
|
-
type WorkerInferSafeConsumerHandler<TContract extends ContractDefinition, TName extends InferConsumerNames<TContract>> = WorkerInferConsumerHandler<TContract, TName>;
|
|
330
|
-
/**
|
|
331
|
-
* @deprecated Use `WorkerInferConsumerHandlerEntry` instead. Will be removed in next major version.
|
|
332
|
-
*/
|
|
333
|
-
type WorkerInferSafeConsumerHandlerEntry<TContract extends ContractDefinition, TName extends InferConsumerNames<TContract>> = WorkerInferConsumerHandlerEntry<TContract, TName>;
|
|
334
|
-
/**
|
|
335
|
-
* @deprecated Use `WorkerInferConsumerHandlers` instead. Will be removed in next major version.
|
|
336
|
-
*/
|
|
337
|
-
type WorkerInferSafeConsumerHandlers<TContract extends ContractDefinition> = WorkerInferConsumerHandlers<TContract>;
|
|
338
318
|
//#endregion
|
|
339
319
|
//#region src/worker.d.ts
|
|
340
320
|
/**
|
|
@@ -680,5 +660,5 @@ declare function defineHandler<TContract extends ContractDefinition, TName exten
|
|
|
680
660
|
*/
|
|
681
661
|
declare function defineHandlers<TContract extends ContractDefinition>(contract: TContract, handlers: WorkerInferConsumerHandlers<TContract>): WorkerInferConsumerHandlers<TContract>;
|
|
682
662
|
//#endregion
|
|
683
|
-
export { type CreateWorkerOptions, type HandlerError, MessageValidationError, NonRetryableError, RetryableError, TypedAmqpWorker, type WorkerConsumedMessage, type WorkerInferConsumedMessage, type WorkerInferConsumerHandler, type WorkerInferConsumerHandlerEntry, type WorkerInferConsumerHandlers, type WorkerInferConsumerHeaders,
|
|
663
|
+
export { type CreateWorkerOptions, type HandlerError, MessageValidationError, NonRetryableError, RetryableError, TypedAmqpWorker, type WorkerConsumedMessage, type WorkerInferConsumedMessage, type WorkerInferConsumerHandler, type WorkerInferConsumerHandlerEntry, type WorkerInferConsumerHandlers, type WorkerInferConsumerHeaders, defineHandler, defineHandlers, isHandlerError, isNonRetryableError, isRetryableError, nonRetryable, retryable };
|
|
684
664
|
//# sourceMappingURL=index.d.cts.map
|
package/dist/index.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","names":["amqp","EventEmitter","TcpSocketConnectOpts","ConnectionOptions","ChannelWrapper","CreateChannelOpts","ConnectionUrl","Options","Connect","AmqpConnectionOptions","url","connectionOptions","ConnectListener","Connection","connection","arg","ConnectFailedListener","Error","err","Buffer","noDelay","timeout","keepAlive","keepAliveDelay","clientProperties","credentials","mechanism","username","password","response","AmqpConnectionManagerOptions","Promise","heartbeatIntervalInSeconds","reconnectTimeInSeconds","findServers","urls","callback","IAmqpConnectionManager","Function","ChannelModel","addListener","event","args","listener","reason","listeners","eventName","on","once","prependListener","prependOnceListener","removeListener","connect","options","reconnect","createChannel","close","isConnected","channelCount","AmqpConnectionManager","_channels","_currentUrl","_closed","_cancelRetriesHandler","_connectPromise","_currentConnection","_findServers","_urls","constructor","_connect","default"],"sources":["../../../node_modules/.pnpm/amqp-connection-manager@5.0.0_amqplib@0.10.9/node_modules/amqp-connection-manager/dist/types/AmqpConnectionManager.d.ts","../src/errors.ts","../src/types.ts","../src/worker.ts","../src/handlers.ts"],"x_google_ignoreList":[0],"mappings":";;;;;;;;;;KAKYM,aAAAA,YAAyBN,IAAAA,CAAKO,OAAAA,CAAQC,OAAAA;EAC9CE,GAAAA;EACAC,iBAAAA,GAAoBF,qBAAAA;AAAAA;AAAAA,KAcZA,qBAAAA,IAAyBN,iBAAAA,GAAoBD,oBAAAA;EACrDkB,OAAAA;EACAC,OAAAA;EACAC,SAAAA;EACAC,cAAAA;EACAC,gBAAAA;EACAC,WAAAA;IACIC,SAAAA;IACAC,QAAAA;IACAC,QAAAA;IACAC,QAAAA,QAAgBV,MAAAA;EAAAA;IAEhBO,SAAAA;IACAG,QAAAA,QAAgBV,MAAAA;EAAAA;AAAAA;AAAAA,UAGPW,4BAAAA;EAVbL;EAYAO,0BAAAA;EAVIL;;;;EAeJM,sBAAAA;EAVIJ;;;;AAGR;;;EAeIK,WAAAA,KAAgBE,QAAAA,GAAWD,IAAAA,EAAM7B,aAAAA,GAAgBA,aAAAA,+BAA4CyB,OAAAA,CAAQzB,aAAAA,GAAgBA,aAAAA;EAApEA;EAEjDK,iBAAAA,GAAoBF,qBAAAA;AAAAA
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":["amqp","EventEmitter","TcpSocketConnectOpts","ConnectionOptions","ChannelWrapper","CreateChannelOpts","ConnectionUrl","Options","Connect","AmqpConnectionOptions","url","connectionOptions","ConnectListener","Connection","connection","arg","ConnectFailedListener","Error","err","Buffer","noDelay","timeout","keepAlive","keepAliveDelay","clientProperties","credentials","mechanism","username","password","response","AmqpConnectionManagerOptions","Promise","heartbeatIntervalInSeconds","reconnectTimeInSeconds","findServers","urls","callback","IAmqpConnectionManager","Function","ChannelModel","addListener","event","args","listener","reason","listeners","eventName","on","once","prependListener","prependOnceListener","removeListener","connect","options","reconnect","createChannel","close","isConnected","channelCount","AmqpConnectionManager","_channels","_currentUrl","_closed","_cancelRetriesHandler","_connectPromise","_currentConnection","_findServers","_urls","constructor","_connect","default"],"sources":["../../../node_modules/.pnpm/amqp-connection-manager@5.0.0_amqplib@0.10.9/node_modules/amqp-connection-manager/dist/types/AmqpConnectionManager.d.ts","../src/errors.ts","../src/types.ts","../src/worker.ts","../src/handlers.ts"],"x_google_ignoreList":[0],"mappings":"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"}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Logger, TechnicalError, TelemetryProvider } from "@amqp-contract/core";
|
|
1
|
+
import { Logger, MessageValidationError, TechnicalError, TelemetryProvider } from "@amqp-contract/core";
|
|
2
2
|
import { ConsumerDefinition, ConsumerEntry, ContractDefinition, InferConsumerNames, MessageDefinition } from "@amqp-contract/contract";
|
|
3
3
|
import { Future, Result } from "@swan-io/boxed";
|
|
4
4
|
import * as amqp from "amqplib";
|
|
@@ -49,14 +49,6 @@ interface AmqpConnectionManagerOptions {
|
|
|
49
49
|
}
|
|
50
50
|
//#endregion
|
|
51
51
|
//#region src/errors.d.ts
|
|
52
|
-
/**
|
|
53
|
-
* Error thrown when message validation fails
|
|
54
|
-
*/
|
|
55
|
-
declare class MessageValidationError extends Error {
|
|
56
|
-
readonly consumerName: string;
|
|
57
|
-
readonly issues: unknown;
|
|
58
|
-
constructor(consumerName: string, issues: unknown);
|
|
59
|
-
}
|
|
60
52
|
/**
|
|
61
53
|
* Retryable errors - transient failures that may succeed on retry
|
|
62
54
|
* Examples: network timeouts, rate limiting, temporary service unavailability
|
|
@@ -323,18 +315,6 @@ type WorkerInferConsumerHandlerEntry<TContract extends ContractDefinition, TName
|
|
|
323
315
|
* ```
|
|
324
316
|
*/
|
|
325
317
|
type WorkerInferConsumerHandlers<TContract extends ContractDefinition> = { [K in InferConsumerNames<TContract>]: WorkerInferConsumerHandlerEntry<TContract, K> };
|
|
326
|
-
/**
|
|
327
|
-
* @deprecated Use `WorkerInferConsumerHandler` instead. Will be removed in next major version.
|
|
328
|
-
*/
|
|
329
|
-
type WorkerInferSafeConsumerHandler<TContract extends ContractDefinition, TName extends InferConsumerNames<TContract>> = WorkerInferConsumerHandler<TContract, TName>;
|
|
330
|
-
/**
|
|
331
|
-
* @deprecated Use `WorkerInferConsumerHandlerEntry` instead. Will be removed in next major version.
|
|
332
|
-
*/
|
|
333
|
-
type WorkerInferSafeConsumerHandlerEntry<TContract extends ContractDefinition, TName extends InferConsumerNames<TContract>> = WorkerInferConsumerHandlerEntry<TContract, TName>;
|
|
334
|
-
/**
|
|
335
|
-
* @deprecated Use `WorkerInferConsumerHandlers` instead. Will be removed in next major version.
|
|
336
|
-
*/
|
|
337
|
-
type WorkerInferSafeConsumerHandlers<TContract extends ContractDefinition> = WorkerInferConsumerHandlers<TContract>;
|
|
338
318
|
//#endregion
|
|
339
319
|
//#region src/worker.d.ts
|
|
340
320
|
/**
|
|
@@ -680,5 +660,5 @@ declare function defineHandler<TContract extends ContractDefinition, TName exten
|
|
|
680
660
|
*/
|
|
681
661
|
declare function defineHandlers<TContract extends ContractDefinition>(contract: TContract, handlers: WorkerInferConsumerHandlers<TContract>): WorkerInferConsumerHandlers<TContract>;
|
|
682
662
|
//#endregion
|
|
683
|
-
export { type CreateWorkerOptions, type HandlerError, MessageValidationError, NonRetryableError, RetryableError, TypedAmqpWorker, type WorkerConsumedMessage, type WorkerInferConsumedMessage, type WorkerInferConsumerHandler, type WorkerInferConsumerHandlerEntry, type WorkerInferConsumerHandlers, type WorkerInferConsumerHeaders,
|
|
663
|
+
export { type CreateWorkerOptions, type HandlerError, MessageValidationError, NonRetryableError, RetryableError, TypedAmqpWorker, type WorkerConsumedMessage, type WorkerInferConsumedMessage, type WorkerInferConsumerHandler, type WorkerInferConsumerHandlerEntry, type WorkerInferConsumerHandlers, type WorkerInferConsumerHeaders, defineHandler, defineHandlers, isHandlerError, isNonRetryableError, isRetryableError, nonRetryable, retryable };
|
|
684
664
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":["amqp","EventEmitter","TcpSocketConnectOpts","ConnectionOptions","ChannelWrapper","CreateChannelOpts","ConnectionUrl","Options","Connect","AmqpConnectionOptions","url","connectionOptions","ConnectListener","Connection","connection","arg","ConnectFailedListener","Error","err","Buffer","noDelay","timeout","keepAlive","keepAliveDelay","clientProperties","credentials","mechanism","username","password","response","AmqpConnectionManagerOptions","Promise","heartbeatIntervalInSeconds","reconnectTimeInSeconds","findServers","urls","callback","IAmqpConnectionManager","Function","ChannelModel","addListener","event","args","listener","reason","listeners","eventName","on","once","prependListener","prependOnceListener","removeListener","connect","options","reconnect","createChannel","close","isConnected","channelCount","AmqpConnectionManager","_channels","_currentUrl","_closed","_cancelRetriesHandler","_connectPromise","_currentConnection","_findServers","_urls","constructor","_connect","default"],"sources":["../../../node_modules/.pnpm/amqp-connection-manager@5.0.0_amqplib@0.10.9/node_modules/amqp-connection-manager/dist/types/AmqpConnectionManager.d.ts","../src/errors.ts","../src/types.ts","../src/worker.ts","../src/handlers.ts"],"x_google_ignoreList":[0],"mappings":";;;;;;;;;;KAKYM,aAAAA,YAAyBN,IAAAA,CAAKO,OAAAA,CAAQC,OAAAA;EAC9CE,GAAAA;EACAC,iBAAAA,GAAoBF,qBAAAA;AAAAA;AAAAA,KAcZA,qBAAAA,IAAyBN,iBAAAA,GAAoBD,oBAAAA;EACrDkB,OAAAA;EACAC,OAAAA;EACAC,SAAAA;EACAC,cAAAA;EACAC,gBAAAA;EACAC,WAAAA;IACIC,SAAAA;IACAC,QAAAA;IACAC,QAAAA;IACAC,QAAAA,QAAgBV,MAAAA;EAAAA;IAEhBO,SAAAA;IACAG,QAAAA,QAAgBV,MAAAA;EAAAA;AAAAA;AAAAA,UAGPW,4BAAAA;EAVbL;EAYAO,0BAAAA;EAVIL;;;;EAeJM,sBAAAA;EAVIJ;;;;AAGR;;;EAeIK,WAAAA,KAAgBE,QAAAA,GAAWD,IAAAA,EAAM7B,aAAAA,GAAgBA,aAAAA,+BAA4CyB,OAAAA,CAAQzB,aAAAA,GAAgBA,aAAAA;EAApEA;EAEjDK,iBAAAA,GAAoBF,qBAAAA;AAAAA
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":["amqp","EventEmitter","TcpSocketConnectOpts","ConnectionOptions","ChannelWrapper","CreateChannelOpts","ConnectionUrl","Options","Connect","AmqpConnectionOptions","url","connectionOptions","ConnectListener","Connection","connection","arg","ConnectFailedListener","Error","err","Buffer","noDelay","timeout","keepAlive","keepAliveDelay","clientProperties","credentials","mechanism","username","password","response","AmqpConnectionManagerOptions","Promise","heartbeatIntervalInSeconds","reconnectTimeInSeconds","findServers","urls","callback","IAmqpConnectionManager","Function","ChannelModel","addListener","event","args","listener","reason","listeners","eventName","on","once","prependListener","prependOnceListener","removeListener","connect","options","reconnect","createChannel","close","isConnected","channelCount","AmqpConnectionManager","_channels","_currentUrl","_closed","_cancelRetriesHandler","_connectPromise","_currentConnection","_findServers","_urls","constructor","_connect","default"],"sources":["../../../node_modules/.pnpm/amqp-connection-manager@5.0.0_amqplib@0.10.9/node_modules/amqp-connection-manager/dist/types/AmqpConnectionManager.d.ts","../src/errors.ts","../src/types.ts","../src/worker.ts","../src/handlers.ts"],"x_google_ignoreList":[0],"mappings":"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"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AmqpClient, TechnicalError, defaultTelemetryProvider, endSpanError, endSpanSuccess, recordConsumeMetric, startConsumeSpan } from "@amqp-contract/core";
|
|
1
|
+
import { AmqpClient, MessageValidationError, TechnicalError, defaultTelemetryProvider, endSpanError, endSpanSuccess, recordConsumeMetric, startConsumeSpan } from "@amqp-contract/core";
|
|
2
2
|
import { extractConsumer } from "@amqp-contract/contract";
|
|
3
3
|
import { Future, Result } from "@swan-io/boxed";
|
|
4
4
|
import { gunzip, inflate } from "node:zlib";
|
|
@@ -6,19 +6,6 @@ import { promisify } from "node:util";
|
|
|
6
6
|
|
|
7
7
|
//#region src/errors.ts
|
|
8
8
|
/**
|
|
9
|
-
* Error thrown when message validation fails
|
|
10
|
-
*/
|
|
11
|
-
var MessageValidationError = class extends Error {
|
|
12
|
-
constructor(consumerName, issues) {
|
|
13
|
-
super(`Message validation failed for consumer "${consumerName}"`);
|
|
14
|
-
this.consumerName = consumerName;
|
|
15
|
-
this.issues = issues;
|
|
16
|
-
this.name = "MessageValidationError";
|
|
17
|
-
const ErrorConstructor = Error;
|
|
18
|
-
if (typeof ErrorConstructor.captureStackTrace === "function") ErrorConstructor.captureStackTrace(this, this.constructor);
|
|
19
|
-
}
|
|
20
|
-
};
|
|
21
|
-
/**
|
|
22
9
|
* Retryable errors - transient failures that may succeed on retry
|
|
23
10
|
* Examples: network timeouts, rate limiting, temporary service unavailability
|
|
24
11
|
*
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/errors.ts","../src/decompression.ts","../src/worker.ts","../src/handlers.ts"],"sourcesContent":["/**\n * Error thrown when message validation fails\n */\nexport class MessageValidationError extends Error {\n constructor(\n public readonly consumerName: string,\n public readonly issues: unknown,\n ) {\n super(`Message validation failed for consumer \"${consumerName}\"`);\n this.name = \"MessageValidationError\";\n // Node.js specific stack trace capture\n const ErrorConstructor = Error as unknown as {\n captureStackTrace?: (target: object, constructor: Function) => void;\n };\n if (typeof ErrorConstructor.captureStackTrace === \"function\") {\n ErrorConstructor.captureStackTrace(this, this.constructor);\n }\n }\n}\n\n/**\n * Retryable errors - transient failures that may succeed on retry\n * Examples: network timeouts, rate limiting, temporary service unavailability\n *\n * Use this error type when the operation might succeed if retried.\n * The worker will apply exponential backoff and retry the message.\n */\nexport class RetryableError extends Error {\n constructor(\n message: string,\n public override readonly cause?: unknown,\n ) {\n super(message);\n this.name = \"RetryableError\";\n // Node.js specific stack trace capture\n const ErrorConstructor = Error as unknown as {\n captureStackTrace?: (target: object, constructor: Function) => void;\n };\n if (typeof ErrorConstructor.captureStackTrace === \"function\") {\n ErrorConstructor.captureStackTrace(this, this.constructor);\n }\n }\n}\n\n/**\n * Non-retryable errors - permanent failures that should not be retried\n * Examples: invalid data, business rule violations, permanent external failures\n *\n * Use this error type when retrying would not help - the message will be\n * immediately sent to the dead letter queue (DLQ) if configured.\n */\nexport class NonRetryableError extends Error {\n constructor(\n message: string,\n public override readonly cause?: unknown,\n ) {\n super(message);\n this.name = \"NonRetryableError\";\n // Node.js specific stack trace capture\n const ErrorConstructor = Error as unknown as {\n captureStackTrace?: (target: object, constructor: Function) => void;\n };\n if (typeof ErrorConstructor.captureStackTrace === \"function\") {\n ErrorConstructor.captureStackTrace(this, this.constructor);\n }\n }\n}\n\n/**\n * Union type representing all handler errors.\n * Use this type when defining handlers that explicitly signal error outcomes.\n */\nexport type HandlerError = RetryableError | NonRetryableError;\n\n// =============================================================================\n// Type Guards\n// =============================================================================\n\n/**\n * Type guard to check if an error is a RetryableError.\n *\n * Use this to check error types in catch blocks or error handlers.\n *\n * @param error - The error to check\n * @returns True if the error is a RetryableError\n *\n * @example\n * ```typescript\n * import { isRetryableError } from '@amqp-contract/worker';\n *\n * try {\n * await processMessage();\n * } catch (error) {\n * if (isRetryableError(error)) {\n * console.log('Will retry:', error.message);\n * } else {\n * console.log('Permanent failure:', error);\n * }\n * }\n * ```\n */\nexport function isRetryableError(error: unknown): error is RetryableError {\n return error instanceof RetryableError;\n}\n\n/**\n * Type guard to check if an error is a NonRetryableError.\n *\n * Use this to check error types in catch blocks or error handlers.\n *\n * @param error - The error to check\n * @returns True if the error is a NonRetryableError\n *\n * @example\n * ```typescript\n * import { isNonRetryableError } from '@amqp-contract/worker';\n *\n * try {\n * await processMessage();\n * } catch (error) {\n * if (isNonRetryableError(error)) {\n * console.log('Will not retry:', error.message);\n * }\n * }\n * ```\n */\nexport function isNonRetryableError(error: unknown): error is NonRetryableError {\n return error instanceof NonRetryableError;\n}\n\n/**\n * Type guard to check if an error is any HandlerError (RetryableError or NonRetryableError).\n *\n * @param error - The error to check\n * @returns True if the error is a HandlerError\n *\n * @example\n * ```typescript\n * import { isHandlerError } from '@amqp-contract/worker';\n *\n * function handleError(error: unknown) {\n * if (isHandlerError(error)) {\n * // error is RetryableError | NonRetryableError\n * console.log('Handler error:', error.name, error.message);\n * }\n * }\n * ```\n */\nexport function isHandlerError(error: unknown): error is HandlerError {\n return isRetryableError(error) || isNonRetryableError(error);\n}\n\n// =============================================================================\n// Factory Functions\n// =============================================================================\n\n/**\n * Create a RetryableError with less verbosity.\n *\n * This is a shorthand factory function for creating RetryableError instances.\n * Use it for cleaner error creation in handlers.\n *\n * @param message - Error message describing the failure\n * @param cause - Optional underlying error that caused this failure\n * @returns A new RetryableError instance\n *\n * @example\n * ```typescript\n * import { retryable } from '@amqp-contract/worker';\n * import { Future, Result } from '@swan-io/boxed';\n *\n * const handler = ({ payload }) =>\n * Future.fromPromise(processPayment(payload))\n * .mapOk(() => undefined)\n * .mapError((e) => retryable('Payment service unavailable', e));\n *\n * // Equivalent to:\n * // .mapError((e) => new RetryableError('Payment service unavailable', e));\n * ```\n */\nexport function retryable(message: string, cause?: unknown): RetryableError {\n return new RetryableError(message, cause);\n}\n\n/**\n * Create a NonRetryableError with less verbosity.\n *\n * This is a shorthand factory function for creating NonRetryableError instances.\n * Use it for cleaner error creation in handlers.\n *\n * @param message - Error message describing the failure\n * @param cause - Optional underlying error that caused this failure\n * @returns A new NonRetryableError instance\n *\n * @example\n * ```typescript\n * import { nonRetryable } from '@amqp-contract/worker';\n * import { Future, Result } from '@swan-io/boxed';\n *\n * const handler = ({ payload }) => {\n * if (!isValidPayload(payload)) {\n * return Future.value(Result.Error(nonRetryable('Invalid payload format')));\n * }\n * return Future.value(Result.Ok(undefined));\n * };\n *\n * // Equivalent to:\n * // return Future.value(Result.Error(new NonRetryableError('Invalid payload format')));\n * ```\n */\nexport function nonRetryable(message: string, cause?: unknown): NonRetryableError {\n return new NonRetryableError(message, cause);\n}\n","import { Future, Result } from \"@swan-io/boxed\";\nimport { gunzip, inflate } from \"node:zlib\";\nimport { TechnicalError } from \"@amqp-contract/core\";\nimport { promisify } from \"node:util\";\n\nconst gunzipAsync = promisify(gunzip);\nconst inflateAsync = promisify(inflate);\n\n/**\n * Supported content encodings for message decompression.\n */\nconst SUPPORTED_ENCODINGS = [\"gzip\", \"deflate\"] as const;\n\n/**\n * Type for supported content encodings.\n */\ntype SupportedEncoding = (typeof SUPPORTED_ENCODINGS)[number];\n\n/**\n * Type guard to check if a string is a supported encoding.\n */\nfunction isSupportedEncoding(encoding: string): encoding is SupportedEncoding {\n return SUPPORTED_ENCODINGS.includes(encoding.toLowerCase() as SupportedEncoding);\n}\n\n/**\n * Decompress a buffer based on the content-encoding header.\n *\n * @param buffer - The buffer to decompress\n * @param contentEncoding - The content-encoding header value (e.g., 'gzip', 'deflate')\n * @returns A Future with the decompressed buffer or a TechnicalError\n *\n * @internal\n */\nexport function decompressBuffer(\n buffer: Buffer,\n contentEncoding: string | undefined,\n): Future<Result<Buffer, TechnicalError>> {\n if (!contentEncoding) {\n return Future.value(Result.Ok(buffer));\n }\n\n const normalizedEncoding = contentEncoding.toLowerCase();\n\n if (!isSupportedEncoding(normalizedEncoding)) {\n return Future.value(\n Result.Error(\n new TechnicalError(\n `Unsupported content-encoding: \"${contentEncoding}\". ` +\n `Supported encodings are: ${SUPPORTED_ENCODINGS.join(\", \")}. ` +\n `Please check your publisher configuration.`,\n ),\n ),\n );\n }\n\n switch (normalizedEncoding) {\n case \"gzip\":\n return Future.fromPromise(gunzipAsync(buffer)).mapError(\n (error) => new TechnicalError(\"Failed to decompress gzip\", error),\n );\n case \"deflate\":\n return Future.fromPromise(inflateAsync(buffer)).mapError(\n (error) => new TechnicalError(\"Failed to decompress deflate\", error),\n );\n }\n}\n","import {\n AmqpClient,\n type Logger,\n TechnicalError,\n type TelemetryProvider,\n defaultTelemetryProvider,\n endSpanError,\n endSpanSuccess,\n recordConsumeMetric,\n startConsumeSpan,\n} from \"@amqp-contract/core\";\nimport type { AmqpConnectionManagerOptions, ConnectionUrl } from \"amqp-connection-manager\";\nimport type { Channel, ConsumeMessage } from \"amqplib\";\nimport {\n type ConsumerDefinition,\n type ContractDefinition,\n type InferConsumerNames,\n type ResolvedRetryOptions,\n type ResolvedTtlBackoffRetryOptions,\n extractConsumer,\n} from \"@amqp-contract/contract\";\nimport { Future, Result } from \"@swan-io/boxed\";\nimport { MessageValidationError, NonRetryableError } from \"./errors.js\";\nimport type {\n WorkerInferConsumedMessage,\n WorkerInferConsumerHandler,\n WorkerInferConsumerHandlers,\n} from \"./types.js\";\nimport type { HandlerError } from \"./errors.js\";\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport { decompressBuffer } from \"./decompression.js\";\n\n/**\n * Internal type for consumer options extracted from handler tuples.\n * Not exported - options are specified inline in the handler tuple types.\n *\n * Note: Retry configuration is now defined at the queue level in the contract,\n * not at the handler level. See `QueueDefinition.retry` for configuration options.\n */\ntype ConsumerOptions = {\n /** Number of messages to prefetch */\n prefetch?: number;\n};\n\n/**\n * Retry configuration from the contract with all values resolved.\n * This is a discriminated union on `mode` - TTL-backoff has the config fields,\n * quorum-native does not.\n */\ntype ResolvedRetryConfig = ResolvedRetryOptions;\n\n/**\n * Type guard to check if a handler entry is a tuple format [handler, options].\n */\nfunction isHandlerTuple(entry: unknown): entry is [unknown, ConsumerOptions] {\n return Array.isArray(entry) && entry.length === 2;\n}\n\n/**\n * Options for creating a type-safe AMQP worker.\n *\n * @typeParam TContract - The contract definition type\n *\n * @example\n * ```typescript\n * const options: CreateWorkerOptions<typeof contract> = {\n * contract: myContract,\n * handlers: {\n * // Simple handler\n * processOrder: ({ payload }) => {\n * console.log('Processing order:', payload.orderId);\n * return Future.value(Result.Ok(undefined));\n * },\n * // Handler with prefetch configuration\n * processPayment: [\n * ({ payload }) => {\n * console.log('Processing payment:', payload.paymentId);\n * return Future.value(Result.Ok(undefined));\n * },\n * { prefetch: 10 }\n * ]\n * },\n * urls: ['amqp://localhost'],\n * connectionOptions: {\n * heartbeatIntervalInSeconds: 30\n * },\n * logger: myLogger\n * };\n * ```\n *\n * Note: Retry configuration is defined at the queue level in the contract,\n * not at the handler level. See `QueueDefinition.retry` for configuration options.\n */\nexport type CreateWorkerOptions<TContract extends ContractDefinition> = {\n /** The AMQP contract definition specifying consumers and their message schemas */\n contract: TContract;\n /**\n * Handlers for each consumer defined in the contract.\n * Handlers must return `Future<Result<void, HandlerError>>` for explicit error handling.\n * Use defineHandler() to create handlers.\n */\n handlers: WorkerInferConsumerHandlers<TContract>;\n /** AMQP broker URL(s). Multiple URLs provide failover support */\n urls: ConnectionUrl[];\n /** Optional connection configuration (heartbeat, reconnect settings, etc.) */\n connectionOptions?: AmqpConnectionManagerOptions | undefined;\n /** Optional logger for logging message consumption and errors */\n logger?: Logger | undefined;\n /**\n * Optional telemetry provider for tracing and metrics.\n * If not provided, uses the default provider which attempts to load OpenTelemetry.\n * OpenTelemetry instrumentation is automatically enabled if @opentelemetry/api is installed.\n */\n telemetry?: TelemetryProvider | undefined;\n};\n\n/**\n * Type-safe AMQP worker for consuming messages from RabbitMQ.\n *\n * This class provides automatic message validation, connection management,\n * and error handling for consuming messages based on a contract definition.\n *\n * @typeParam TContract - The contract definition type\n *\n * @example\n * ```typescript\n * import { TypedAmqpWorker } from '@amqp-contract/worker';\n * import { defineQueue, defineMessage, defineContract, defineConsumer } from '@amqp-contract/contract';\n * import { z } from 'zod';\n *\n * const orderQueue = defineQueue('order-processing', { durable: true });\n * const orderMessage = defineMessage(z.object({\n * orderId: z.string(),\n * amount: z.number()\n * }));\n *\n * const contract = defineContract({\n * consumers: {\n * processOrder: defineConsumer(orderQueue, orderMessage)\n * }\n * });\n *\n * const worker = await TypedAmqpWorker.create({\n * contract,\n * handlers: {\n * processOrder: async (message) => {\n * console.log('Processing order', message.orderId);\n * // Process the order...\n * }\n * },\n * urls: ['amqp://localhost']\n * }).resultToPromise();\n *\n * // Close when done\n * await worker.close().resultToPromise();\n * ```\n */\nexport class TypedAmqpWorker<TContract extends ContractDefinition> {\n /**\n * Internal handler storage - handlers returning `Future<Result>`.\n */\n private readonly actualHandlers: Partial<\n Record<\n InferConsumerNames<TContract>,\n WorkerInferConsumerHandler<TContract, InferConsumerNames<TContract>>\n >\n >;\n private readonly consumerOptions: Partial<Record<InferConsumerNames<TContract>, ConsumerOptions>>;\n private readonly consumerTags: Set<string> = new Set();\n private readonly telemetry: TelemetryProvider;\n\n private constructor(\n private readonly contract: TContract,\n private readonly amqpClient: AmqpClient,\n handlers: WorkerInferConsumerHandlers<TContract>,\n private readonly logger?: Logger,\n telemetry?: TelemetryProvider,\n ) {\n this.telemetry = telemetry ?? defaultTelemetryProvider;\n\n // Extract handlers and options from the handlers object\n this.actualHandlers = {};\n this.consumerOptions = {};\n\n // Cast handlers to a generic record for iteration\n const handlersRecord = handlers as Record<string, unknown>;\n\n for (const consumerName of Object.keys(handlersRecord)) {\n const handlerEntry = handlersRecord[consumerName];\n const typedConsumerName = consumerName as InferConsumerNames<TContract>;\n\n if (isHandlerTuple(handlerEntry)) {\n // Tuple format: [handler, options]\n const [handler, options] = handlerEntry;\n this.actualHandlers[typedConsumerName] = handler as WorkerInferConsumerHandler<\n TContract,\n InferConsumerNames<TContract>\n >;\n this.consumerOptions[typedConsumerName] = options;\n } else {\n // Direct function format\n this.actualHandlers[typedConsumerName] = handlerEntry as WorkerInferConsumerHandler<\n TContract,\n InferConsumerNames<TContract>\n >;\n }\n }\n }\n\n /**\n * Create a type-safe AMQP worker from a contract.\n *\n * Connection management (including automatic reconnection) is handled internally\n * by amqp-connection-manager via the {@link AmqpClient}. The worker will set up\n * consumers for all contract-defined handlers asynchronously in the background\n * once the underlying connection and channels are ready.\n *\n * Connections are automatically shared across clients and workers with the same\n * URLs and connection options, following RabbitMQ best practices.\n *\n * @param options - Configuration options for the worker\n * @returns A Future that resolves to a Result containing the worker or an error\n *\n * @example\n * ```typescript\n * const worker = await TypedAmqpWorker.create({\n * contract: myContract,\n * handlers: {\n * processOrder: async ({ payload }) => console.log('Order:', payload.orderId)\n * },\n * urls: ['amqp://localhost']\n * }).resultToPromise();\n * ```\n */\n static create<TContract extends ContractDefinition>({\n contract,\n handlers,\n urls,\n connectionOptions,\n logger,\n telemetry,\n }: CreateWorkerOptions<TContract>): Future<Result<TypedAmqpWorker<TContract>, TechnicalError>> {\n const worker = new TypedAmqpWorker(\n contract,\n new AmqpClient(contract, {\n urls,\n connectionOptions,\n }),\n handlers,\n logger,\n telemetry,\n );\n\n // Note: Wait queues are now created by the core package in setupAmqpTopology\n // when the queue's retry mode is \"ttl-backoff\"\n return worker\n .waitForConnectionReady()\n .flatMapOk(() => worker.consumeAll())\n .mapOk(() => worker);\n }\n\n /**\n * Close the AMQP channel and connection.\n *\n * This gracefully closes the connection to the AMQP broker,\n * stopping all message consumption and cleaning up resources.\n *\n * @returns A Future that resolves to a Result indicating success or failure\n *\n * @example\n * ```typescript\n * const closeResult = await worker.close().resultToPromise();\n * if (closeResult.isOk()) {\n * console.log('Worker closed successfully');\n * }\n * ```\n */\n close(): Future<Result<void, TechnicalError>> {\n return Future.all(\n Array.from(this.consumerTags).map((consumerTag) =>\n this.amqpClient.cancel(consumerTag).mapErrorToResult((error) => {\n this.logger?.warn(\"Failed to cancel consumer during close\", { consumerTag, error });\n return Result.Ok(undefined);\n }),\n ),\n )\n .map(Result.all)\n .tapOk(() => {\n // Clear consumer tags after successful cancellation\n this.consumerTags.clear();\n })\n .flatMapOk(() => this.amqpClient.close())\n .mapOk(() => undefined);\n }\n\n /**\n * Get the retry configuration for a consumer's queue.\n * Defaults are applied in the contract's defineQueue, so we just return the config.\n */\n private getRetryConfigForConsumer(consumer: ConsumerDefinition): ResolvedRetryConfig {\n return consumer.queue.retry;\n }\n\n /**\n * Start consuming messages for all consumers.\n * TypeScript guarantees consumers exist (handlers require matching consumers).\n */\n private consumeAll(): Future<Result<void, TechnicalError>> {\n const consumers = this.contract.consumers!;\n const consumerNames = Object.keys(consumers) as InferConsumerNames<TContract>[];\n\n // Calculate max prefetch (AMQP 0.9.1 prefetch is per-channel)\n const maxPrefetch = consumerNames.reduce((max, name) => {\n const prefetch = this.consumerOptions[name]?.prefetch;\n return prefetch ? Math.max(max, prefetch) : max;\n }, 0);\n\n if (maxPrefetch > 0) {\n this.amqpClient.addSetup(async (channel: Channel) => {\n await channel.prefetch(maxPrefetch);\n });\n }\n\n return Future.all(consumerNames.map((name) => this.consume(name)))\n .map(Result.all)\n .mapOk(() => undefined);\n }\n\n private waitForConnectionReady(): Future<Result<void, TechnicalError>> {\n return this.amqpClient.waitForConnect();\n }\n\n /**\n * Start consuming messages for a specific consumer.\n * TypeScript guarantees consumer and handler exist for valid consumer names.\n */\n private consume<TName extends InferConsumerNames<TContract>>(\n consumerName: TName,\n ): Future<Result<void, TechnicalError>> {\n // Non-null assertions safe: TypeScript guarantees these exist for valid TName\n const consumerEntry = this.contract.consumers![consumerName as string]!;\n const consumer = extractConsumer(consumerEntry);\n const handler = this.actualHandlers[consumerName]!;\n\n return this.consumeSingle(\n consumerName,\n consumer,\n handler as Parameters<typeof this.consumeSingle<TName>>[2],\n );\n }\n\n /**\n * Validate data against a Standard Schema and handle errors.\n */\n private validateSchema(\n schema: StandardSchemaV1,\n data: unknown,\n context: { consumerName: string; queueName: string; field: string },\n msg: ConsumeMessage,\n ): Future<Result<unknown, TechnicalError>> {\n const rawValidation = schema[\"~standard\"].validate(data);\n const validationPromise =\n rawValidation instanceof Promise ? rawValidation : Promise.resolve(rawValidation);\n\n return Future.fromPromise(validationPromise)\n .mapError((error) => new TechnicalError(`Error validating ${context.field}`, error))\n .mapOkToResult((result) => {\n if (result.issues) {\n return Result.Error(\n new TechnicalError(\n `${context.field} validation failed`,\n new MessageValidationError(context.consumerName, result.issues),\n ),\n );\n }\n return Result.Ok(result.value);\n })\n .tapError((error) => {\n this.logger?.error(`${context.field} validation failed`, {\n consumerName: context.consumerName,\n queueName: context.queueName,\n error,\n });\n this.amqpClient.nack(msg, false, false);\n });\n }\n\n /**\n * Parse and validate a message from AMQP.\n * @returns Ok with validated message (payload + headers), or Error (message already nacked)\n */\n private parseAndValidateMessage<TName extends InferConsumerNames<TContract>>(\n msg: ConsumeMessage,\n consumer: ConsumerDefinition,\n consumerName: TName,\n ): Future<Result<WorkerInferConsumedMessage<TContract, TName>, TechnicalError>> {\n const context = {\n consumerName: String(consumerName),\n queueName: consumer.queue.name,\n };\n\n const nackAndError = (message: string, error?: unknown): TechnicalError => {\n this.logger?.error(message, { ...context, error });\n this.amqpClient.nack(msg, false, false);\n return new TechnicalError(message, error);\n };\n\n // Decompress → Parse JSON → Validate payload\n const parsePayload = decompressBuffer(msg.content, msg.properties.contentEncoding)\n .tapError((error) => {\n this.logger?.error(\"Failed to decompress message\", { ...context, error });\n this.amqpClient.nack(msg, false, false);\n })\n .mapOkToResult((buffer) =>\n Result.fromExecution(() => JSON.parse(buffer.toString()) as unknown).mapError((error) =>\n nackAndError(\"Failed to parse JSON\", error),\n ),\n )\n .flatMapOk((parsed) =>\n this.validateSchema(\n consumer.message.payload as StandardSchemaV1,\n parsed,\n { ...context, field: \"payload\" },\n msg,\n ),\n );\n\n // Validate headers (if schema defined)\n const parseHeaders = consumer.message.headers\n ? this.validateSchema(\n consumer.message.headers as StandardSchemaV1,\n msg.properties.headers ?? {},\n { ...context, field: \"headers\" },\n msg,\n )\n : Future.value(Result.Ok<unknown, TechnicalError>(undefined));\n\n return Future.allFromDict({ payload: parsePayload, headers: parseHeaders }).map(\n Result.allFromDict,\n ) as Future<Result<WorkerInferConsumedMessage<TContract, TName>, TechnicalError>>;\n }\n\n /**\n * Consume messages one at a time\n */\n private consumeSingle<TName extends InferConsumerNames<TContract>>(\n consumerName: TName,\n consumer: ConsumerDefinition,\n handler: (\n message: WorkerInferConsumedMessage<TContract, TName>,\n rawMessage: ConsumeMessage,\n ) => Future<Result<void, HandlerError>>,\n ): Future<Result<void, TechnicalError>> {\n const queueName = consumer.queue.name;\n\n // Start consuming\n return this.amqpClient\n .consume(queueName, async (msg) => {\n // Handle null messages (consumer cancellation)\n if (msg === null) {\n this.logger?.warn(\"Consumer cancelled by server\", {\n consumerName: String(consumerName),\n queueName,\n });\n return;\n }\n\n const startTime = Date.now();\n const span = startConsumeSpan(this.telemetry, queueName, String(consumerName), {\n \"messaging.rabbitmq.message.delivery_tag\": msg.fields.deliveryTag,\n });\n\n // Parse and validate message\n await this.parseAndValidateMessage(msg, consumer, consumerName)\n .flatMapOk((validatedMessage) =>\n handler(validatedMessage, msg)\n .flatMapOk(() => {\n this.logger?.info(\"Message consumed successfully\", {\n consumerName: String(consumerName),\n queueName,\n });\n // Acknowledge message on success\n this.amqpClient.ack(msg);\n\n // Record telemetry success\n const durationMs = Date.now() - startTime;\n endSpanSuccess(span);\n recordConsumeMetric(\n this.telemetry,\n queueName,\n String(consumerName),\n true,\n durationMs,\n );\n\n return Future.value(Result.Ok<void, HandlerError>(undefined));\n })\n .flatMapError((handlerError: HandlerError) => {\n // Handler returned an error\n this.logger?.error(\"Error processing message\", {\n consumerName: String(consumerName),\n queueName,\n errorType: handlerError.name,\n error: handlerError.message,\n });\n\n // Record telemetry failure\n const durationMs = Date.now() - startTime;\n endSpanError(span, handlerError);\n recordConsumeMetric(\n this.telemetry,\n queueName,\n String(consumerName),\n false,\n durationMs,\n );\n\n // Handle the error using retry mechanism\n return this.handleError(handlerError, msg, String(consumerName), consumer);\n }),\n )\n .tapError(() => {\n // Record telemetry failure for validation errors\n // Note: The actual validation error is logged in parseAndValidateMessage,\n // here we just record that validation failed for telemetry purposes\n const durationMs = Date.now() - startTime;\n endSpanError(span, new Error(\"Message validation failed\"));\n recordConsumeMetric(this.telemetry, queueName, String(consumerName), false, durationMs);\n })\n .toPromise();\n })\n .tapOk((consumerTag) => {\n // Store consumer tag for later cancellation\n this.consumerTags.add(consumerTag);\n })\n .mapError(\n (error) =>\n new TechnicalError(`Failed to start consuming for \"${String(consumerName)}\"`, error),\n )\n .mapOk(() => undefined);\n }\n\n /**\n * Handle error in message processing with retry logic.\n *\n * Flow depends on retry mode:\n *\n * **quorum-native mode:**\n * 1. If NonRetryableError -> send directly to DLQ (no retry)\n * 2. Otherwise -> nack with requeue=true (RabbitMQ handles delivery count)\n *\n * **ttl-backoff mode:**\n * 1. If NonRetryableError -> send directly to DLQ (no retry)\n * 2. If max retries exceeded -> send to DLQ\n * 3. Otherwise -> publish to wait queue with TTL for retry\n *\n * **Legacy mode (no retry config):**\n * 1. nack with requeue=true (immediate requeue)\n */\n private handleError(\n error: Error,\n msg: ConsumeMessage,\n consumerName: string,\n consumer: ConsumerDefinition,\n ): Future<Result<void, TechnicalError>> {\n // NonRetryableError -> send directly to DLQ without retrying\n if (error instanceof NonRetryableError) {\n this.logger?.error(\"Non-retryable error, sending to DLQ immediately\", {\n consumerName,\n errorType: error.name,\n error: error.message,\n });\n this.sendToDLQ(msg, consumer);\n return Future.value(Result.Ok(undefined));\n }\n\n // Get retry config from the queue definition in the contract\n const config = this.getRetryConfigForConsumer(consumer);\n\n // Quorum-native mode: let RabbitMQ handle retry via x-delivery-count\n if (config.mode === \"quorum-native\") {\n return this.handleErrorQuorumNative(error, msg, consumerName, consumer);\n }\n\n // TTL-backoff mode: use wait queue with exponential backoff\n return this.handleErrorTtlBackoff(error, msg, consumerName, consumer, config);\n }\n\n /**\n * Handle error using quorum queue's native delivery limit feature.\n *\n * Simply requeues the message with nack(requeue=true). RabbitMQ automatically:\n * - Increments x-delivery-count header\n * - Dead-letters the message when count exceeds x-delivery-limit\n *\n * This is simpler than TTL-based retry but provides immediate retries only.\n */\n private handleErrorQuorumNative(\n error: Error,\n msg: ConsumeMessage,\n consumerName: string,\n consumer: ConsumerDefinition,\n ): Future<Result<void, TechnicalError>> {\n const queue = consumer.queue;\n const queueName = queue.name;\n // x-delivery-count is incremented on each delivery attempt\n // When x-delivery-count equals x-delivery-limit, message is dead-lettered on next attempt\n const deliveryCount = (msg.properties.headers?.[\"x-delivery-count\"] as number) ?? 0;\n // This function is only called for quorum-native mode, which requires quorum queues\n const deliveryLimit = queue.type === \"quorum\" ? queue.deliveryLimit : undefined;\n\n // After this requeue, RabbitMQ will increment deliveryCount\n // Message is dead-lettered when deliveryCount reaches deliveryLimit\n // So if deliveryCount == deliveryLimit - 1, the next failure will dead-letter the message\n const attemptsBeforeDeadLetter =\n deliveryLimit !== undefined ? Math.max(0, deliveryLimit - deliveryCount - 1) : \"unknown\";\n\n // Log warning if this is the last attempt before dead-lettering\n if (deliveryLimit !== undefined && deliveryCount >= deliveryLimit - 1) {\n this.logger?.warn(\"Message at final delivery attempt (quorum-native mode)\", {\n consumerName,\n queueName,\n deliveryCount,\n deliveryLimit,\n willDeadLetterOnNextFailure: deliveryCount === deliveryLimit - 1,\n alreadyExceededLimit: deliveryCount >= deliveryLimit,\n error: error.message,\n });\n } else {\n this.logger?.warn(\"Retrying message (quorum-native mode)\", {\n consumerName,\n queueName,\n deliveryCount,\n deliveryLimit,\n attemptsBeforeDeadLetter,\n error: error.message,\n });\n }\n\n // Requeue the message - RabbitMQ tracks delivery count and handles dead-lettering\n this.amqpClient.nack(msg, false, true);\n return Future.value(Result.Ok(undefined));\n }\n\n /**\n * Handle error using TTL + wait queue pattern for exponential backoff.\n */\n private handleErrorTtlBackoff(\n error: Error,\n msg: ConsumeMessage,\n consumerName: string,\n consumer: ConsumerDefinition,\n config: ResolvedTtlBackoffRetryOptions,\n ): Future<Result<void, TechnicalError>> {\n // Get retry count from headers\n const retryCount = (msg.properties.headers?.[\"x-retry-count\"] as number) ?? 0;\n\n // Max retries exceeded -> DLQ\n if (retryCount >= config.maxRetries) {\n this.logger?.error(\"Max retries exceeded, sending to DLQ\", {\n consumerName,\n retryCount,\n maxRetries: config.maxRetries,\n error: error.message,\n });\n this.sendToDLQ(msg, consumer);\n return Future.value(Result.Ok(undefined));\n }\n\n // Retry with exponential backoff\n const delayMs = this.calculateRetryDelay(retryCount, config);\n this.logger?.warn(\"Retrying message (ttl-backoff mode)\", {\n consumerName,\n retryCount: retryCount + 1,\n delayMs,\n error: error.message,\n });\n\n return this.publishForRetry(msg, consumer, retryCount + 1, delayMs, error);\n }\n\n /**\n * Calculate retry delay with exponential backoff and optional jitter.\n */\n private calculateRetryDelay(retryCount: number, config: ResolvedTtlBackoffRetryOptions): number {\n const { initialDelayMs, maxDelayMs, backoffMultiplier, jitter } = config;\n\n let delay = Math.min(initialDelayMs * Math.pow(backoffMultiplier, retryCount), maxDelayMs);\n\n if (jitter) {\n // Add jitter: random value between 50% and 100% of calculated delay\n delay = delay * (0.5 + Math.random() * 0.5);\n }\n\n return Math.floor(delay);\n }\n\n /**\n * Parse message content for republishing.\n * Prevents double JSON serialization by converting Buffer to object when possible.\n */\n private parseMessageContentForRetry(msg: ConsumeMessage, queueName: string): Buffer | unknown {\n let content: Buffer | unknown = msg.content;\n\n // If message is not compressed (no contentEncoding), parse it to get the original object\n if (!msg.properties.contentEncoding) {\n try {\n content = JSON.parse(msg.content.toString());\n } catch (err) {\n this.logger?.warn(\"Failed to parse message for retry, using original buffer\", {\n queueName,\n error: err,\n });\n }\n }\n\n return content;\n }\n\n /**\n * Publish message to wait queue for retry after TTL expires.\n *\n * ┌─────────────────────────────────────────────────────────────────┐\n * │ Retry Flow (Native RabbitMQ TTL + DLX Pattern) │\n * ├─────────────────────────────────────────────────────────────────┤\n * │ │\n * │ 1. Handler throws any Error │\n * │ ↓ │\n * │ 2. Worker publishes to DLX with routing key: {queue}-wait │\n * │ ↓ │\n * │ 3. DLX routes to wait queue: {queue}-wait │\n * │ (with expiration: calculated backoff delay) │\n * │ ↓ │\n * │ 4. Message waits in queue until TTL expires │\n * │ ↓ │\n * │ 5. Expired message dead-lettered to DLX │\n * │ (with routing key: {queue}) │\n * │ ↓ │\n * │ 6. DLX routes back to main queue → RETRY │\n * │ ↓ │\n * │ 7. If retries exhausted: nack without requeue → DLQ │\n * │ │\n * └─────────────────────────────────────────────────────────────────┘\n */\n private publishForRetry(\n msg: ConsumeMessage,\n consumer: ConsumerDefinition,\n newRetryCount: number,\n delayMs: number,\n error: Error,\n ): Future<Result<void, TechnicalError>> {\n const queueName = consumer.queue.name;\n const deadLetter = consumer.queue.deadLetter;\n\n if (!deadLetter) {\n this.logger?.warn(\n \"Cannot retry: queue does not have DLX configured, falling back to nack with requeue\",\n {\n queueName,\n },\n );\n this.amqpClient.nack(msg, false, true);\n return Future.value(Result.Ok(undefined));\n }\n\n const dlxName = deadLetter.exchange.name;\n const waitRoutingKey = `${queueName}-wait`;\n\n // Acknowledge original message\n this.amqpClient.ack(msg);\n\n const content = this.parseMessageContentForRetry(msg, queueName);\n\n // Publish to DLX with wait routing key\n return this.amqpClient\n .publish(dlxName, waitRoutingKey, content, {\n ...msg.properties,\n expiration: delayMs.toString(), // Per-message TTL\n headers: {\n ...msg.properties.headers,\n \"x-retry-count\": newRetryCount,\n \"x-last-error\": error.message,\n \"x-first-failure-timestamp\":\n msg.properties.headers?.[\"x-first-failure-timestamp\"] ?? Date.now(),\n },\n })\n .mapOkToResult((published) => {\n if (!published) {\n this.logger?.error(\"Failed to publish message for retry (write buffer full)\", {\n queueName,\n waitRoutingKey,\n retryCount: newRetryCount,\n });\n return Result.Error(\n new TechnicalError(\"Failed to publish message for retry (write buffer full)\"),\n );\n }\n\n this.logger?.info(\"Message published for retry\", {\n queueName,\n waitRoutingKey,\n retryCount: newRetryCount,\n delayMs,\n });\n return Result.Ok(undefined);\n });\n }\n\n /**\n * Send message to dead letter queue.\n * Nacks the message without requeue, relying on DLX configuration.\n */\n private sendToDLQ(msg: ConsumeMessage, consumer: ConsumerDefinition): void {\n const queueName = consumer.queue.name;\n const hasDeadLetter = consumer.queue.deadLetter !== undefined;\n\n if (!hasDeadLetter) {\n this.logger?.warn(\"Queue does not have DLX configured - message will be lost on nack\", {\n queueName,\n });\n }\n\n this.logger?.info(\"Sending message to DLQ\", {\n queueName,\n deliveryTag: msg.fields.deliveryTag,\n });\n\n // Nack without requeue - relies on DLX configuration\n this.amqpClient.nack(msg, false, false);\n }\n}\n","import type { ContractDefinition, InferConsumerNames } from \"@amqp-contract/contract\";\nimport type {\n WorkerInferConsumerHandler,\n WorkerInferConsumerHandlerEntry,\n WorkerInferConsumerHandlers,\n} from \"./types.js\";\n\n// =============================================================================\n// Helper Functions\n// =============================================================================\n\n/**\n * Validate that a consumer exists in the contract\n */\nfunction validateConsumerExists<TContract extends ContractDefinition>(\n contract: TContract,\n consumerName: string,\n): void {\n const consumers = contract.consumers;\n\n if (!consumers || !(consumerName in consumers)) {\n const availableConsumers = consumers ? Object.keys(consumers) : [];\n const available = availableConsumers.length > 0 ? availableConsumers.join(\", \") : \"none\";\n throw new Error(\n `Consumer \"${consumerName}\" not found in contract. Available consumers: ${available}`,\n );\n }\n}\n\n/**\n * Validate that all handlers reference valid consumers\n */\nfunction validateHandlers<TContract extends ContractDefinition>(\n contract: TContract,\n handlers: Record<string, unknown>,\n): void {\n const consumers = contract.consumers;\n const availableConsumers = Object.keys(consumers ?? {});\n const availableConsumerNames =\n availableConsumers.length > 0 ? availableConsumers.join(\", \") : \"none\";\n\n for (const handlerName of Object.keys(handlers)) {\n if (!consumers || !(handlerName in consumers)) {\n throw new Error(\n `Consumer \"${handlerName}\" not found in contract. Available consumers: ${availableConsumerNames}`,\n );\n }\n }\n}\n\n// =============================================================================\n// Handler Definitions\n// =============================================================================\n\n/**\n * Define a type-safe handler for a specific consumer in a contract.\n *\n * **Recommended:** This function creates handlers that return `Future<Result<void, HandlerError>>`,\n * providing explicit error handling and better control over retry behavior.\n *\n * Supports two patterns:\n * 1. Simple handler: just the function\n * 2. Handler with options: [handler, { prefetch: 10 }]\n *\n * @template TContract - The contract definition type\n * @template TName - The consumer name from the contract\n * @param contract - The contract definition containing the consumer\n * @param consumerName - The name of the consumer from the contract\n * @param handler - The handler function that returns `Future<Result<void, HandlerError>>`\n * @param options - Optional consumer options (prefetch)\n * @returns A type-safe handler that can be used with TypedAmqpWorker\n *\n * @example\n * ```typescript\n * import { defineHandler, RetryableError, NonRetryableError } from '@amqp-contract/worker';\n * import { Future, Result } from '@swan-io/boxed';\n * import { orderContract } from './contract';\n *\n * // Simple handler with explicit error handling using mapError\n * const processOrderHandler = defineHandler(\n * orderContract,\n * 'processOrder',\n * ({ payload }) =>\n * Future.fromPromise(processPayment(payload))\n * .mapOk(() => undefined)\n * .mapError((error) => new RetryableError('Payment failed', error))\n * );\n *\n * // Handler with validation (non-retryable error)\n * const validateOrderHandler = defineHandler(\n * orderContract,\n * 'validateOrder',\n * ({ payload }) => {\n * if (payload.amount < 1) {\n * // Won't be retried - goes directly to DLQ\n * return Future.value(Result.Error(new NonRetryableError('Invalid order amount')));\n * }\n * return Future.value(Result.Ok(undefined));\n * }\n * );\n * ```\n */\nexport function defineHandler<\n TContract extends ContractDefinition,\n TName extends InferConsumerNames<TContract>,\n>(\n contract: TContract,\n consumerName: TName,\n handler: WorkerInferConsumerHandler<TContract, TName>,\n): WorkerInferConsumerHandlerEntry<TContract, TName>;\nexport function defineHandler<\n TContract extends ContractDefinition,\n TName extends InferConsumerNames<TContract>,\n>(\n contract: TContract,\n consumerName: TName,\n handler: WorkerInferConsumerHandler<TContract, TName>,\n options: { prefetch?: number },\n): WorkerInferConsumerHandlerEntry<TContract, TName>;\nexport function defineHandler<\n TContract extends ContractDefinition,\n TName extends InferConsumerNames<TContract>,\n>(\n contract: TContract,\n consumerName: TName,\n handler: WorkerInferConsumerHandler<TContract, TName>,\n options?: { prefetch?: number },\n): WorkerInferConsumerHandlerEntry<TContract, TName> {\n validateConsumerExists(contract, String(consumerName));\n\n if (options) {\n return [handler, options];\n }\n return handler;\n}\n\n/**\n * Define multiple type-safe handlers for consumers in a contract.\n *\n * **Recommended:** This function creates handlers that return `Future<Result<void, HandlerError>>`,\n * providing explicit error handling and better control over retry behavior.\n *\n * @template TContract - The contract definition type\n * @param contract - The contract definition containing the consumers\n * @param handlers - An object with handler functions for each consumer\n * @returns A type-safe handlers object that can be used with TypedAmqpWorker\n *\n * @example\n * ```typescript\n * import { defineHandlers, RetryableError } from '@amqp-contract/worker';\n * import { Future } from '@swan-io/boxed';\n * import { orderContract } from './contract';\n *\n * const handlers = defineHandlers(orderContract, {\n * processOrder: ({ payload }) =>\n * Future.fromPromise(processPayment(payload))\n * .mapOk(() => undefined)\n * .mapError((error) => new RetryableError('Payment failed', error)),\n * notifyOrder: ({ payload }) =>\n * Future.fromPromise(sendNotification(payload))\n * .mapOk(() => undefined)\n * .mapError((error) => new RetryableError('Notification failed', error)),\n * });\n * ```\n */\nexport function defineHandlers<TContract extends ContractDefinition>(\n contract: TContract,\n handlers: WorkerInferConsumerHandlers<TContract>,\n): WorkerInferConsumerHandlers<TContract> {\n validateHandlers(contract, handlers as unknown as Record<string, unknown>);\n return handlers;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/errors.ts","../src/decompression.ts","../src/worker.ts","../src/handlers.ts"],"sourcesContent":["export { MessageValidationError } from \"@amqp-contract/core\";\n\n/**\n * Retryable errors - transient failures that may succeed on retry\n * Examples: network timeouts, rate limiting, temporary service unavailability\n *\n * Use this error type when the operation might succeed if retried.\n * The worker will apply exponential backoff and retry the message.\n */\nexport class RetryableError extends Error {\n constructor(\n message: string,\n public override readonly cause?: unknown,\n ) {\n super(message);\n this.name = \"RetryableError\";\n // Node.js specific stack trace capture\n const ErrorConstructor = Error as unknown as {\n captureStackTrace?: (target: object, constructor: Function) => void;\n };\n if (typeof ErrorConstructor.captureStackTrace === \"function\") {\n ErrorConstructor.captureStackTrace(this, this.constructor);\n }\n }\n}\n\n/**\n * Non-retryable errors - permanent failures that should not be retried\n * Examples: invalid data, business rule violations, permanent external failures\n *\n * Use this error type when retrying would not help - the message will be\n * immediately sent to the dead letter queue (DLQ) if configured.\n */\nexport class NonRetryableError extends Error {\n constructor(\n message: string,\n public override readonly cause?: unknown,\n ) {\n super(message);\n this.name = \"NonRetryableError\";\n // Node.js specific stack trace capture\n const ErrorConstructor = Error as unknown as {\n captureStackTrace?: (target: object, constructor: Function) => void;\n };\n if (typeof ErrorConstructor.captureStackTrace === \"function\") {\n ErrorConstructor.captureStackTrace(this, this.constructor);\n }\n }\n}\n\n/**\n * Union type representing all handler errors.\n * Use this type when defining handlers that explicitly signal error outcomes.\n */\nexport type HandlerError = RetryableError | NonRetryableError;\n\n// =============================================================================\n// Type Guards\n// =============================================================================\n\n/**\n * Type guard to check if an error is a RetryableError.\n *\n * Use this to check error types in catch blocks or error handlers.\n *\n * @param error - The error to check\n * @returns True if the error is a RetryableError\n *\n * @example\n * ```typescript\n * import { isRetryableError } from '@amqp-contract/worker';\n *\n * try {\n * await processMessage();\n * } catch (error) {\n * if (isRetryableError(error)) {\n * console.log('Will retry:', error.message);\n * } else {\n * console.log('Permanent failure:', error);\n * }\n * }\n * ```\n */\nexport function isRetryableError(error: unknown): error is RetryableError {\n return error instanceof RetryableError;\n}\n\n/**\n * Type guard to check if an error is a NonRetryableError.\n *\n * Use this to check error types in catch blocks or error handlers.\n *\n * @param error - The error to check\n * @returns True if the error is a NonRetryableError\n *\n * @example\n * ```typescript\n * import { isNonRetryableError } from '@amqp-contract/worker';\n *\n * try {\n * await processMessage();\n * } catch (error) {\n * if (isNonRetryableError(error)) {\n * console.log('Will not retry:', error.message);\n * }\n * }\n * ```\n */\nexport function isNonRetryableError(error: unknown): error is NonRetryableError {\n return error instanceof NonRetryableError;\n}\n\n/**\n * Type guard to check if an error is any HandlerError (RetryableError or NonRetryableError).\n *\n * @param error - The error to check\n * @returns True if the error is a HandlerError\n *\n * @example\n * ```typescript\n * import { isHandlerError } from '@amqp-contract/worker';\n *\n * function handleError(error: unknown) {\n * if (isHandlerError(error)) {\n * // error is RetryableError | NonRetryableError\n * console.log('Handler error:', error.name, error.message);\n * }\n * }\n * ```\n */\nexport function isHandlerError(error: unknown): error is HandlerError {\n return isRetryableError(error) || isNonRetryableError(error);\n}\n\n// =============================================================================\n// Factory Functions\n// =============================================================================\n\n/**\n * Create a RetryableError with less verbosity.\n *\n * This is a shorthand factory function for creating RetryableError instances.\n * Use it for cleaner error creation in handlers.\n *\n * @param message - Error message describing the failure\n * @param cause - Optional underlying error that caused this failure\n * @returns A new RetryableError instance\n *\n * @example\n * ```typescript\n * import { retryable } from '@amqp-contract/worker';\n * import { Future, Result } from '@swan-io/boxed';\n *\n * const handler = ({ payload }) =>\n * Future.fromPromise(processPayment(payload))\n * .mapOk(() => undefined)\n * .mapError((e) => retryable('Payment service unavailable', e));\n *\n * // Equivalent to:\n * // .mapError((e) => new RetryableError('Payment service unavailable', e));\n * ```\n */\nexport function retryable(message: string, cause?: unknown): RetryableError {\n return new RetryableError(message, cause);\n}\n\n/**\n * Create a NonRetryableError with less verbosity.\n *\n * This is a shorthand factory function for creating NonRetryableError instances.\n * Use it for cleaner error creation in handlers.\n *\n * @param message - Error message describing the failure\n * @param cause - Optional underlying error that caused this failure\n * @returns A new NonRetryableError instance\n *\n * @example\n * ```typescript\n * import { nonRetryable } from '@amqp-contract/worker';\n * import { Future, Result } from '@swan-io/boxed';\n *\n * const handler = ({ payload }) => {\n * if (!isValidPayload(payload)) {\n * return Future.value(Result.Error(nonRetryable('Invalid payload format')));\n * }\n * return Future.value(Result.Ok(undefined));\n * };\n *\n * // Equivalent to:\n * // return Future.value(Result.Error(new NonRetryableError('Invalid payload format')));\n * ```\n */\nexport function nonRetryable(message: string, cause?: unknown): NonRetryableError {\n return new NonRetryableError(message, cause);\n}\n","import { Future, Result } from \"@swan-io/boxed\";\nimport { gunzip, inflate } from \"node:zlib\";\nimport { TechnicalError } from \"@amqp-contract/core\";\nimport { promisify } from \"node:util\";\n\nconst gunzipAsync = promisify(gunzip);\nconst inflateAsync = promisify(inflate);\n\n/**\n * Supported content encodings for message decompression.\n */\nconst SUPPORTED_ENCODINGS = [\"gzip\", \"deflate\"] as const;\n\n/**\n * Type for supported content encodings.\n */\ntype SupportedEncoding = (typeof SUPPORTED_ENCODINGS)[number];\n\n/**\n * Type guard to check if a string is a supported encoding.\n */\nfunction isSupportedEncoding(encoding: string): encoding is SupportedEncoding {\n return SUPPORTED_ENCODINGS.includes(encoding.toLowerCase() as SupportedEncoding);\n}\n\n/**\n * Decompress a buffer based on the content-encoding header.\n *\n * @param buffer - The buffer to decompress\n * @param contentEncoding - The content-encoding header value (e.g., 'gzip', 'deflate')\n * @returns A Future with the decompressed buffer or a TechnicalError\n *\n * @internal\n */\nexport function decompressBuffer(\n buffer: Buffer,\n contentEncoding: string | undefined,\n): Future<Result<Buffer, TechnicalError>> {\n if (!contentEncoding) {\n return Future.value(Result.Ok(buffer));\n }\n\n const normalizedEncoding = contentEncoding.toLowerCase();\n\n if (!isSupportedEncoding(normalizedEncoding)) {\n return Future.value(\n Result.Error(\n new TechnicalError(\n `Unsupported content-encoding: \"${contentEncoding}\". ` +\n `Supported encodings are: ${SUPPORTED_ENCODINGS.join(\", \")}. ` +\n `Please check your publisher configuration.`,\n ),\n ),\n );\n }\n\n switch (normalizedEncoding) {\n case \"gzip\":\n return Future.fromPromise(gunzipAsync(buffer)).mapError(\n (error) => new TechnicalError(\"Failed to decompress gzip\", error),\n );\n case \"deflate\":\n return Future.fromPromise(inflateAsync(buffer)).mapError(\n (error) => new TechnicalError(\"Failed to decompress deflate\", error),\n );\n }\n}\n","import {\n AmqpClient,\n type Logger,\n TechnicalError,\n type TelemetryProvider,\n defaultTelemetryProvider,\n endSpanError,\n endSpanSuccess,\n recordConsumeMetric,\n startConsumeSpan,\n} from \"@amqp-contract/core\";\nimport type { AmqpConnectionManagerOptions, ConnectionUrl } from \"amqp-connection-manager\";\nimport type { Channel, ConsumeMessage } from \"amqplib\";\nimport {\n type ConsumerDefinition,\n type ContractDefinition,\n type InferConsumerNames,\n type ResolvedRetryOptions,\n type ResolvedTtlBackoffRetryOptions,\n extractConsumer,\n} from \"@amqp-contract/contract\";\nimport { Future, Result } from \"@swan-io/boxed\";\nimport { MessageValidationError, NonRetryableError } from \"./errors.js\";\nimport type {\n WorkerInferConsumedMessage,\n WorkerInferConsumerHandler,\n WorkerInferConsumerHandlers,\n} from \"./types.js\";\nimport type { HandlerError } from \"./errors.js\";\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport { decompressBuffer } from \"./decompression.js\";\n\n/**\n * Internal type for consumer options extracted from handler tuples.\n * Not exported - options are specified inline in the handler tuple types.\n *\n * Note: Retry configuration is now defined at the queue level in the contract,\n * not at the handler level. See `QueueDefinition.retry` for configuration options.\n */\ntype ConsumerOptions = {\n /** Number of messages to prefetch */\n prefetch?: number;\n};\n\n/**\n * Retry configuration from the contract with all values resolved.\n * This is a discriminated union on `mode` - TTL-backoff has the config fields,\n * quorum-native does not.\n */\ntype ResolvedRetryConfig = ResolvedRetryOptions;\n\n/**\n * Type guard to check if a handler entry is a tuple format [handler, options].\n */\nfunction isHandlerTuple(entry: unknown): entry is [unknown, ConsumerOptions] {\n return Array.isArray(entry) && entry.length === 2;\n}\n\n/**\n * Options for creating a type-safe AMQP worker.\n *\n * @typeParam TContract - The contract definition type\n *\n * @example\n * ```typescript\n * const options: CreateWorkerOptions<typeof contract> = {\n * contract: myContract,\n * handlers: {\n * // Simple handler\n * processOrder: ({ payload }) => {\n * console.log('Processing order:', payload.orderId);\n * return Future.value(Result.Ok(undefined));\n * },\n * // Handler with prefetch configuration\n * processPayment: [\n * ({ payload }) => {\n * console.log('Processing payment:', payload.paymentId);\n * return Future.value(Result.Ok(undefined));\n * },\n * { prefetch: 10 }\n * ]\n * },\n * urls: ['amqp://localhost'],\n * connectionOptions: {\n * heartbeatIntervalInSeconds: 30\n * },\n * logger: myLogger\n * };\n * ```\n *\n * Note: Retry configuration is defined at the queue level in the contract,\n * not at the handler level. See `QueueDefinition.retry` for configuration options.\n */\nexport type CreateWorkerOptions<TContract extends ContractDefinition> = {\n /** The AMQP contract definition specifying consumers and their message schemas */\n contract: TContract;\n /**\n * Handlers for each consumer defined in the contract.\n * Handlers must return `Future<Result<void, HandlerError>>` for explicit error handling.\n * Use defineHandler() to create handlers.\n */\n handlers: WorkerInferConsumerHandlers<TContract>;\n /** AMQP broker URL(s). Multiple URLs provide failover support */\n urls: ConnectionUrl[];\n /** Optional connection configuration (heartbeat, reconnect settings, etc.) */\n connectionOptions?: AmqpConnectionManagerOptions | undefined;\n /** Optional logger for logging message consumption and errors */\n logger?: Logger | undefined;\n /**\n * Optional telemetry provider for tracing and metrics.\n * If not provided, uses the default provider which attempts to load OpenTelemetry.\n * OpenTelemetry instrumentation is automatically enabled if @opentelemetry/api is installed.\n */\n telemetry?: TelemetryProvider | undefined;\n};\n\n/**\n * Type-safe AMQP worker for consuming messages from RabbitMQ.\n *\n * This class provides automatic message validation, connection management,\n * and error handling for consuming messages based on a contract definition.\n *\n * @typeParam TContract - The contract definition type\n *\n * @example\n * ```typescript\n * import { TypedAmqpWorker } from '@amqp-contract/worker';\n * import { defineQueue, defineMessage, defineContract, defineConsumer } from '@amqp-contract/contract';\n * import { z } from 'zod';\n *\n * const orderQueue = defineQueue('order-processing', { durable: true });\n * const orderMessage = defineMessage(z.object({\n * orderId: z.string(),\n * amount: z.number()\n * }));\n *\n * const contract = defineContract({\n * consumers: {\n * processOrder: defineConsumer(orderQueue, orderMessage)\n * }\n * });\n *\n * const worker = await TypedAmqpWorker.create({\n * contract,\n * handlers: {\n * processOrder: async (message) => {\n * console.log('Processing order', message.orderId);\n * // Process the order...\n * }\n * },\n * urls: ['amqp://localhost']\n * }).resultToPromise();\n *\n * // Close when done\n * await worker.close().resultToPromise();\n * ```\n */\nexport class TypedAmqpWorker<TContract extends ContractDefinition> {\n /**\n * Internal handler storage - handlers returning `Future<Result>`.\n */\n private readonly actualHandlers: Partial<\n Record<\n InferConsumerNames<TContract>,\n WorkerInferConsumerHandler<TContract, InferConsumerNames<TContract>>\n >\n >;\n private readonly consumerOptions: Partial<Record<InferConsumerNames<TContract>, ConsumerOptions>>;\n private readonly consumerTags: Set<string> = new Set();\n private readonly telemetry: TelemetryProvider;\n\n private constructor(\n private readonly contract: TContract,\n private readonly amqpClient: AmqpClient,\n handlers: WorkerInferConsumerHandlers<TContract>,\n private readonly logger?: Logger,\n telemetry?: TelemetryProvider,\n ) {\n this.telemetry = telemetry ?? defaultTelemetryProvider;\n\n // Extract handlers and options from the handlers object\n this.actualHandlers = {};\n this.consumerOptions = {};\n\n // Cast handlers to a generic record for iteration\n const handlersRecord = handlers as Record<string, unknown>;\n\n for (const consumerName of Object.keys(handlersRecord)) {\n const handlerEntry = handlersRecord[consumerName];\n const typedConsumerName = consumerName as InferConsumerNames<TContract>;\n\n if (isHandlerTuple(handlerEntry)) {\n // Tuple format: [handler, options]\n const [handler, options] = handlerEntry;\n this.actualHandlers[typedConsumerName] = handler as WorkerInferConsumerHandler<\n TContract,\n InferConsumerNames<TContract>\n >;\n this.consumerOptions[typedConsumerName] = options;\n } else {\n // Direct function format\n this.actualHandlers[typedConsumerName] = handlerEntry as WorkerInferConsumerHandler<\n TContract,\n InferConsumerNames<TContract>\n >;\n }\n }\n }\n\n /**\n * Create a type-safe AMQP worker from a contract.\n *\n * Connection management (including automatic reconnection) is handled internally\n * by amqp-connection-manager via the {@link AmqpClient}. The worker will set up\n * consumers for all contract-defined handlers asynchronously in the background\n * once the underlying connection and channels are ready.\n *\n * Connections are automatically shared across clients and workers with the same\n * URLs and connection options, following RabbitMQ best practices.\n *\n * @param options - Configuration options for the worker\n * @returns A Future that resolves to a Result containing the worker or an error\n *\n * @example\n * ```typescript\n * const worker = await TypedAmqpWorker.create({\n * contract: myContract,\n * handlers: {\n * processOrder: async ({ payload }) => console.log('Order:', payload.orderId)\n * },\n * urls: ['amqp://localhost']\n * }).resultToPromise();\n * ```\n */\n static create<TContract extends ContractDefinition>({\n contract,\n handlers,\n urls,\n connectionOptions,\n logger,\n telemetry,\n }: CreateWorkerOptions<TContract>): Future<Result<TypedAmqpWorker<TContract>, TechnicalError>> {\n const worker = new TypedAmqpWorker(\n contract,\n new AmqpClient(contract, {\n urls,\n connectionOptions,\n }),\n handlers,\n logger,\n telemetry,\n );\n\n // Note: Wait queues are now created by the core package in setupAmqpTopology\n // when the queue's retry mode is \"ttl-backoff\"\n return worker\n .waitForConnectionReady()\n .flatMapOk(() => worker.consumeAll())\n .mapOk(() => worker);\n }\n\n /**\n * Close the AMQP channel and connection.\n *\n * This gracefully closes the connection to the AMQP broker,\n * stopping all message consumption and cleaning up resources.\n *\n * @returns A Future that resolves to a Result indicating success or failure\n *\n * @example\n * ```typescript\n * const closeResult = await worker.close().resultToPromise();\n * if (closeResult.isOk()) {\n * console.log('Worker closed successfully');\n * }\n * ```\n */\n close(): Future<Result<void, TechnicalError>> {\n return Future.all(\n Array.from(this.consumerTags).map((consumerTag) =>\n this.amqpClient.cancel(consumerTag).mapErrorToResult((error) => {\n this.logger?.warn(\"Failed to cancel consumer during close\", { consumerTag, error });\n return Result.Ok(undefined);\n }),\n ),\n )\n .map(Result.all)\n .tapOk(() => {\n // Clear consumer tags after successful cancellation\n this.consumerTags.clear();\n })\n .flatMapOk(() => this.amqpClient.close())\n .mapOk(() => undefined);\n }\n\n /**\n * Get the retry configuration for a consumer's queue.\n * Defaults are applied in the contract's defineQueue, so we just return the config.\n */\n private getRetryConfigForConsumer(consumer: ConsumerDefinition): ResolvedRetryConfig {\n return consumer.queue.retry;\n }\n\n /**\n * Start consuming messages for all consumers.\n * TypeScript guarantees consumers exist (handlers require matching consumers).\n */\n private consumeAll(): Future<Result<void, TechnicalError>> {\n // Non-null assertion safe: TypeScript guarantees consumers exist (handlers require matching consumers)\n const consumers = this.contract.consumers!;\n const consumerNames = Object.keys(consumers) as InferConsumerNames<TContract>[];\n\n // Calculate max prefetch (AMQP 0.9.1 prefetch is per-channel)\n const maxPrefetch = consumerNames.reduce((max, name) => {\n const prefetch = this.consumerOptions[name]?.prefetch;\n return prefetch ? Math.max(max, prefetch) : max;\n }, 0);\n\n if (maxPrefetch > 0) {\n this.amqpClient.addSetup(async (channel: Channel) => {\n await channel.prefetch(maxPrefetch);\n });\n }\n\n return Future.all(consumerNames.map((name) => this.consume(name)))\n .map(Result.all)\n .mapOk(() => undefined);\n }\n\n private waitForConnectionReady(): Future<Result<void, TechnicalError>> {\n return this.amqpClient.waitForConnect();\n }\n\n /**\n * Start consuming messages for a specific consumer.\n * TypeScript guarantees consumer and handler exist for valid consumer names.\n */\n private consume<TName extends InferConsumerNames<TContract>>(\n consumerName: TName,\n ): Future<Result<void, TechnicalError>> {\n // Non-null assertions safe: TypeScript guarantees these exist for valid TName\n const consumerEntry = this.contract.consumers![consumerName as string]!;\n const consumer = extractConsumer(consumerEntry);\n // Non-null assertion safe: constructor validates handlers match consumer names\n const handler = this.actualHandlers[consumerName]!;\n\n return this.consumeSingle(\n consumerName,\n consumer,\n handler as Parameters<typeof this.consumeSingle<TName>>[2],\n );\n }\n\n /**\n * Validate data against a Standard Schema and handle errors.\n */\n private validateSchema(\n schema: StandardSchemaV1,\n data: unknown,\n context: { consumerName: string; queueName: string; field: string },\n msg: ConsumeMessage,\n ): Future<Result<unknown, TechnicalError>> {\n const rawValidation = schema[\"~standard\"].validate(data);\n const validationPromise =\n rawValidation instanceof Promise ? rawValidation : Promise.resolve(rawValidation);\n\n return Future.fromPromise(validationPromise)\n .mapError((error) => new TechnicalError(`Error validating ${context.field}`, error))\n .mapOkToResult((result) => {\n if (result.issues) {\n return Result.Error(\n new TechnicalError(\n `${context.field} validation failed`,\n new MessageValidationError(context.consumerName, result.issues),\n ),\n );\n }\n return Result.Ok(result.value);\n })\n .tapError((error) => {\n this.logger?.error(`${context.field} validation failed`, {\n consumerName: context.consumerName,\n queueName: context.queueName,\n error,\n });\n this.amqpClient.nack(msg, false, false);\n });\n }\n\n /**\n * Parse and validate a message from AMQP.\n * @returns Ok with validated message (payload + headers), or Error (message already nacked)\n */\n private parseAndValidateMessage<TName extends InferConsumerNames<TContract>>(\n msg: ConsumeMessage,\n consumer: ConsumerDefinition,\n consumerName: TName,\n ): Future<Result<WorkerInferConsumedMessage<TContract, TName>, TechnicalError>> {\n const context = {\n consumerName: String(consumerName),\n queueName: consumer.queue.name,\n };\n\n const nackAndError = (message: string, error?: unknown): TechnicalError => {\n this.logger?.error(message, { ...context, error });\n this.amqpClient.nack(msg, false, false);\n return new TechnicalError(message, error);\n };\n\n // Decompress → Parse JSON → Validate payload\n const parsePayload = decompressBuffer(msg.content, msg.properties.contentEncoding)\n .tapError((error) => {\n this.logger?.error(\"Failed to decompress message\", { ...context, error });\n this.amqpClient.nack(msg, false, false);\n })\n .mapOkToResult((buffer) =>\n Result.fromExecution(() => JSON.parse(buffer.toString()) as unknown).mapError((error) =>\n nackAndError(\"Failed to parse JSON\", error),\n ),\n )\n .flatMapOk((parsed) =>\n this.validateSchema(\n consumer.message.payload as StandardSchemaV1,\n parsed,\n { ...context, field: \"payload\" },\n msg,\n ),\n );\n\n // Validate headers (if schema defined)\n const parseHeaders = consumer.message.headers\n ? this.validateSchema(\n consumer.message.headers as StandardSchemaV1,\n msg.properties.headers ?? {},\n { ...context, field: \"headers\" },\n msg,\n )\n : Future.value(Result.Ok<unknown, TechnicalError>(undefined));\n\n return Future.allFromDict({ payload: parsePayload, headers: parseHeaders }).map(\n Result.allFromDict,\n ) as Future<Result<WorkerInferConsumedMessage<TContract, TName>, TechnicalError>>;\n }\n\n /**\n * Consume messages one at a time\n */\n private consumeSingle<TName extends InferConsumerNames<TContract>>(\n consumerName: TName,\n consumer: ConsumerDefinition,\n handler: (\n message: WorkerInferConsumedMessage<TContract, TName>,\n rawMessage: ConsumeMessage,\n ) => Future<Result<void, HandlerError>>,\n ): Future<Result<void, TechnicalError>> {\n const queueName = consumer.queue.name;\n\n // Start consuming\n return this.amqpClient\n .consume(queueName, async (msg) => {\n // Handle null messages (consumer cancellation)\n if (msg === null) {\n this.logger?.warn(\"Consumer cancelled by server\", {\n consumerName: String(consumerName),\n queueName,\n });\n return;\n }\n\n const startTime = Date.now();\n const span = startConsumeSpan(this.telemetry, queueName, String(consumerName), {\n \"messaging.rabbitmq.message.delivery_tag\": msg.fields.deliveryTag,\n });\n\n // Parse and validate message\n await this.parseAndValidateMessage(msg, consumer, consumerName)\n .flatMapOk((validatedMessage) =>\n handler(validatedMessage, msg)\n .flatMapOk(() => {\n this.logger?.info(\"Message consumed successfully\", {\n consumerName: String(consumerName),\n queueName,\n });\n // Acknowledge message on success\n this.amqpClient.ack(msg);\n\n // Record telemetry success\n const durationMs = Date.now() - startTime;\n endSpanSuccess(span);\n recordConsumeMetric(\n this.telemetry,\n queueName,\n String(consumerName),\n true,\n durationMs,\n );\n\n return Future.value(Result.Ok<void, HandlerError>(undefined));\n })\n .flatMapError((handlerError: HandlerError) => {\n // Handler returned an error\n this.logger?.error(\"Error processing message\", {\n consumerName: String(consumerName),\n queueName,\n errorType: handlerError.name,\n error: handlerError.message,\n });\n\n // Record telemetry failure\n const durationMs = Date.now() - startTime;\n endSpanError(span, handlerError);\n recordConsumeMetric(\n this.telemetry,\n queueName,\n String(consumerName),\n false,\n durationMs,\n );\n\n // Handle the error using retry mechanism\n return this.handleError(handlerError, msg, String(consumerName), consumer);\n }),\n )\n .tapError(() => {\n // Record telemetry failure for validation errors\n // Note: The actual validation error is logged in parseAndValidateMessage,\n // here we just record that validation failed for telemetry purposes\n const durationMs = Date.now() - startTime;\n endSpanError(span, new Error(\"Message validation failed\"));\n recordConsumeMetric(this.telemetry, queueName, String(consumerName), false, durationMs);\n })\n .toPromise();\n })\n .tapOk((consumerTag) => {\n // Store consumer tag for later cancellation\n this.consumerTags.add(consumerTag);\n })\n .mapError(\n (error) =>\n new TechnicalError(`Failed to start consuming for \"${String(consumerName)}\"`, error),\n )\n .mapOk(() => undefined);\n }\n\n /**\n * Handle error in message processing with retry logic.\n *\n * Flow depends on retry mode:\n *\n * **quorum-native mode:**\n * 1. If NonRetryableError -> send directly to DLQ (no retry)\n * 2. Otherwise -> nack with requeue=true (RabbitMQ handles delivery count)\n *\n * **ttl-backoff mode:**\n * 1. If NonRetryableError -> send directly to DLQ (no retry)\n * 2. If max retries exceeded -> send to DLQ\n * 3. Otherwise -> publish to wait queue with TTL for retry\n *\n * **Legacy mode (no retry config):**\n * 1. nack with requeue=true (immediate requeue)\n */\n private handleError(\n error: Error,\n msg: ConsumeMessage,\n consumerName: string,\n consumer: ConsumerDefinition,\n ): Future<Result<void, TechnicalError>> {\n // NonRetryableError -> send directly to DLQ without retrying\n if (error instanceof NonRetryableError) {\n this.logger?.error(\"Non-retryable error, sending to DLQ immediately\", {\n consumerName,\n errorType: error.name,\n error: error.message,\n });\n this.sendToDLQ(msg, consumer);\n return Future.value(Result.Ok(undefined));\n }\n\n // Get retry config from the queue definition in the contract\n const config = this.getRetryConfigForConsumer(consumer);\n\n // Quorum-native mode: let RabbitMQ handle retry via x-delivery-count\n if (config.mode === \"quorum-native\") {\n return this.handleErrorQuorumNative(error, msg, consumerName, consumer);\n }\n\n // TTL-backoff mode: use wait queue with exponential backoff\n return this.handleErrorTtlBackoff(error, msg, consumerName, consumer, config);\n }\n\n /**\n * Handle error using quorum queue's native delivery limit feature.\n *\n * Simply requeues the message with nack(requeue=true). RabbitMQ automatically:\n * - Increments x-delivery-count header\n * - Dead-letters the message when count exceeds x-delivery-limit\n *\n * This is simpler than TTL-based retry but provides immediate retries only.\n */\n private handleErrorQuorumNative(\n error: Error,\n msg: ConsumeMessage,\n consumerName: string,\n consumer: ConsumerDefinition,\n ): Future<Result<void, TechnicalError>> {\n const queue = consumer.queue;\n const queueName = queue.name;\n // x-delivery-count is incremented on each delivery attempt\n // When x-delivery-count equals x-delivery-limit, message is dead-lettered on next attempt\n const deliveryCount = (msg.properties.headers?.[\"x-delivery-count\"] as number) ?? 0;\n // This function is only called for quorum-native mode, which requires quorum queues\n const deliveryLimit = queue.type === \"quorum\" ? queue.deliveryLimit : undefined;\n\n // After this requeue, RabbitMQ will increment deliveryCount\n // Message is dead-lettered when deliveryCount reaches deliveryLimit\n // So if deliveryCount == deliveryLimit - 1, the next failure will dead-letter the message\n const attemptsBeforeDeadLetter =\n deliveryLimit !== undefined ? Math.max(0, deliveryLimit - deliveryCount - 1) : \"unknown\";\n\n // Log warning if this is the last attempt before dead-lettering\n if (deliveryLimit !== undefined && deliveryCount >= deliveryLimit - 1) {\n this.logger?.warn(\"Message at final delivery attempt (quorum-native mode)\", {\n consumerName,\n queueName,\n deliveryCount,\n deliveryLimit,\n willDeadLetterOnNextFailure: deliveryCount === deliveryLimit - 1,\n alreadyExceededLimit: deliveryCount >= deliveryLimit,\n error: error.message,\n });\n } else {\n this.logger?.warn(\"Retrying message (quorum-native mode)\", {\n consumerName,\n queueName,\n deliveryCount,\n deliveryLimit,\n attemptsBeforeDeadLetter,\n error: error.message,\n });\n }\n\n // Requeue the message - RabbitMQ tracks delivery count and handles dead-lettering\n this.amqpClient.nack(msg, false, true);\n return Future.value(Result.Ok(undefined));\n }\n\n /**\n * Handle error using TTL + wait queue pattern for exponential backoff.\n */\n private handleErrorTtlBackoff(\n error: Error,\n msg: ConsumeMessage,\n consumerName: string,\n consumer: ConsumerDefinition,\n config: ResolvedTtlBackoffRetryOptions,\n ): Future<Result<void, TechnicalError>> {\n // Get retry count from headers\n const retryCount = (msg.properties.headers?.[\"x-retry-count\"] as number) ?? 0;\n\n // Max retries exceeded -> DLQ\n if (retryCount >= config.maxRetries) {\n this.logger?.error(\"Max retries exceeded, sending to DLQ\", {\n consumerName,\n retryCount,\n maxRetries: config.maxRetries,\n error: error.message,\n });\n this.sendToDLQ(msg, consumer);\n return Future.value(Result.Ok(undefined));\n }\n\n // Retry with exponential backoff\n const delayMs = this.calculateRetryDelay(retryCount, config);\n this.logger?.warn(\"Retrying message (ttl-backoff mode)\", {\n consumerName,\n retryCount: retryCount + 1,\n delayMs,\n error: error.message,\n });\n\n return this.publishForRetry(msg, consumer, retryCount + 1, delayMs, error);\n }\n\n /**\n * Calculate retry delay with exponential backoff and optional jitter.\n */\n private calculateRetryDelay(retryCount: number, config: ResolvedTtlBackoffRetryOptions): number {\n const { initialDelayMs, maxDelayMs, backoffMultiplier, jitter } = config;\n\n let delay = Math.min(initialDelayMs * Math.pow(backoffMultiplier, retryCount), maxDelayMs);\n\n if (jitter) {\n // Add jitter: random value between 50% and 100% of calculated delay\n delay = delay * (0.5 + Math.random() * 0.5);\n }\n\n return Math.floor(delay);\n }\n\n /**\n * Parse message content for republishing.\n * Prevents double JSON serialization by converting Buffer to object when possible.\n */\n private parseMessageContentForRetry(msg: ConsumeMessage, queueName: string): Buffer | unknown {\n let content: Buffer | unknown = msg.content;\n\n // If message is not compressed (no contentEncoding), parse it to get the original object\n if (!msg.properties.contentEncoding) {\n try {\n content = JSON.parse(msg.content.toString());\n } catch (err) {\n this.logger?.warn(\"Failed to parse message for retry, using original buffer\", {\n queueName,\n error: err,\n });\n }\n }\n\n return content;\n }\n\n /**\n * Publish message to wait queue for retry after TTL expires.\n *\n * ┌─────────────────────────────────────────────────────────────────┐\n * │ Retry Flow (Native RabbitMQ TTL + DLX Pattern) │\n * ├─────────────────────────────────────────────────────────────────┤\n * │ │\n * │ 1. Handler throws any Error │\n * │ ↓ │\n * │ 2. Worker publishes to DLX with routing key: {queue}-wait │\n * │ ↓ │\n * │ 3. DLX routes to wait queue: {queue}-wait │\n * │ (with expiration: calculated backoff delay) │\n * │ ↓ │\n * │ 4. Message waits in queue until TTL expires │\n * │ ↓ │\n * │ 5. Expired message dead-lettered to DLX │\n * │ (with routing key: {queue}) │\n * │ ↓ │\n * │ 6. DLX routes back to main queue → RETRY │\n * │ ↓ │\n * │ 7. If retries exhausted: nack without requeue → DLQ │\n * │ │\n * └─────────────────────────────────────────────────────────────────┘\n */\n private publishForRetry(\n msg: ConsumeMessage,\n consumer: ConsumerDefinition,\n newRetryCount: number,\n delayMs: number,\n error: Error,\n ): Future<Result<void, TechnicalError>> {\n const queueName = consumer.queue.name;\n const deadLetter = consumer.queue.deadLetter;\n\n if (!deadLetter) {\n this.logger?.warn(\n \"Cannot retry: queue does not have DLX configured, falling back to nack with requeue\",\n {\n queueName,\n },\n );\n this.amqpClient.nack(msg, false, true);\n return Future.value(Result.Ok(undefined));\n }\n\n const dlxName = deadLetter.exchange.name;\n const waitRoutingKey = `${queueName}-wait`;\n\n // Acknowledge original message\n this.amqpClient.ack(msg);\n\n const content = this.parseMessageContentForRetry(msg, queueName);\n\n // Publish to DLX with wait routing key\n return this.amqpClient\n .publish(dlxName, waitRoutingKey, content, {\n ...msg.properties,\n expiration: delayMs.toString(), // Per-message TTL\n headers: {\n ...msg.properties.headers,\n \"x-retry-count\": newRetryCount,\n \"x-last-error\": error.message,\n \"x-first-failure-timestamp\":\n msg.properties.headers?.[\"x-first-failure-timestamp\"] ?? Date.now(),\n },\n })\n .mapOkToResult((published) => {\n if (!published) {\n this.logger?.error(\"Failed to publish message for retry (write buffer full)\", {\n queueName,\n waitRoutingKey,\n retryCount: newRetryCount,\n });\n return Result.Error(\n new TechnicalError(\"Failed to publish message for retry (write buffer full)\"),\n );\n }\n\n this.logger?.info(\"Message published for retry\", {\n queueName,\n waitRoutingKey,\n retryCount: newRetryCount,\n delayMs,\n });\n return Result.Ok(undefined);\n });\n }\n\n /**\n * Send message to dead letter queue.\n * Nacks the message without requeue, relying on DLX configuration.\n */\n private sendToDLQ(msg: ConsumeMessage, consumer: ConsumerDefinition): void {\n const queueName = consumer.queue.name;\n const hasDeadLetter = consumer.queue.deadLetter !== undefined;\n\n if (!hasDeadLetter) {\n this.logger?.warn(\"Queue does not have DLX configured - message will be lost on nack\", {\n queueName,\n });\n }\n\n this.logger?.info(\"Sending message to DLQ\", {\n queueName,\n deliveryTag: msg.fields.deliveryTag,\n });\n\n // Nack without requeue - relies on DLX configuration\n this.amqpClient.nack(msg, false, false);\n }\n}\n","import type { ContractDefinition, InferConsumerNames } from \"@amqp-contract/contract\";\nimport type {\n WorkerInferConsumerHandler,\n WorkerInferConsumerHandlerEntry,\n WorkerInferConsumerHandlers,\n} from \"./types.js\";\n\n// =============================================================================\n// Helper Functions\n// =============================================================================\n\n/**\n * Validate that a consumer exists in the contract\n */\nfunction validateConsumerExists<TContract extends ContractDefinition>(\n contract: TContract,\n consumerName: string,\n): void {\n const consumers = contract.consumers;\n\n if (!consumers || !(consumerName in consumers)) {\n const availableConsumers = consumers ? Object.keys(consumers) : [];\n const available = availableConsumers.length > 0 ? availableConsumers.join(\", \") : \"none\";\n throw new Error(\n `Consumer \"${consumerName}\" not found in contract. Available consumers: ${available}`,\n );\n }\n}\n\n/**\n * Validate that all handlers reference valid consumers\n */\nfunction validateHandlers<TContract extends ContractDefinition>(\n contract: TContract,\n handlers: object,\n): void {\n const consumers = contract.consumers;\n const availableConsumers = Object.keys(consumers ?? {});\n const availableConsumerNames =\n availableConsumers.length > 0 ? availableConsumers.join(\", \") : \"none\";\n\n for (const handlerName of Object.keys(handlers)) {\n if (!consumers || !(handlerName in consumers)) {\n throw new Error(\n `Consumer \"${handlerName}\" not found in contract. Available consumers: ${availableConsumerNames}`,\n );\n }\n }\n}\n\n// =============================================================================\n// Handler Definitions\n// =============================================================================\n\n/**\n * Define a type-safe handler for a specific consumer in a contract.\n *\n * **Recommended:** This function creates handlers that return `Future<Result<void, HandlerError>>`,\n * providing explicit error handling and better control over retry behavior.\n *\n * Supports two patterns:\n * 1. Simple handler: just the function\n * 2. Handler with options: [handler, { prefetch: 10 }]\n *\n * @template TContract - The contract definition type\n * @template TName - The consumer name from the contract\n * @param contract - The contract definition containing the consumer\n * @param consumerName - The name of the consumer from the contract\n * @param handler - The handler function that returns `Future<Result<void, HandlerError>>`\n * @param options - Optional consumer options (prefetch)\n * @returns A type-safe handler that can be used with TypedAmqpWorker\n *\n * @example\n * ```typescript\n * import { defineHandler, RetryableError, NonRetryableError } from '@amqp-contract/worker';\n * import { Future, Result } from '@swan-io/boxed';\n * import { orderContract } from './contract';\n *\n * // Simple handler with explicit error handling using mapError\n * const processOrderHandler = defineHandler(\n * orderContract,\n * 'processOrder',\n * ({ payload }) =>\n * Future.fromPromise(processPayment(payload))\n * .mapOk(() => undefined)\n * .mapError((error) => new RetryableError('Payment failed', error))\n * );\n *\n * // Handler with validation (non-retryable error)\n * const validateOrderHandler = defineHandler(\n * orderContract,\n * 'validateOrder',\n * ({ payload }) => {\n * if (payload.amount < 1) {\n * // Won't be retried - goes directly to DLQ\n * return Future.value(Result.Error(new NonRetryableError('Invalid order amount')));\n * }\n * return Future.value(Result.Ok(undefined));\n * }\n * );\n * ```\n */\nexport function defineHandler<\n TContract extends ContractDefinition,\n TName extends InferConsumerNames<TContract>,\n>(\n contract: TContract,\n consumerName: TName,\n handler: WorkerInferConsumerHandler<TContract, TName>,\n): WorkerInferConsumerHandlerEntry<TContract, TName>;\nexport function defineHandler<\n TContract extends ContractDefinition,\n TName extends InferConsumerNames<TContract>,\n>(\n contract: TContract,\n consumerName: TName,\n handler: WorkerInferConsumerHandler<TContract, TName>,\n options: { prefetch?: number },\n): WorkerInferConsumerHandlerEntry<TContract, TName>;\nexport function defineHandler<\n TContract extends ContractDefinition,\n TName extends InferConsumerNames<TContract>,\n>(\n contract: TContract,\n consumerName: TName,\n handler: WorkerInferConsumerHandler<TContract, TName>,\n options?: { prefetch?: number },\n): WorkerInferConsumerHandlerEntry<TContract, TName> {\n validateConsumerExists(contract, String(consumerName));\n\n if (options) {\n return [handler, options];\n }\n return handler;\n}\n\n/**\n * Define multiple type-safe handlers for consumers in a contract.\n *\n * **Recommended:** This function creates handlers that return `Future<Result<void, HandlerError>>`,\n * providing explicit error handling and better control over retry behavior.\n *\n * @template TContract - The contract definition type\n * @param contract - The contract definition containing the consumers\n * @param handlers - An object with handler functions for each consumer\n * @returns A type-safe handlers object that can be used with TypedAmqpWorker\n *\n * @example\n * ```typescript\n * import { defineHandlers, RetryableError } from '@amqp-contract/worker';\n * import { Future } from '@swan-io/boxed';\n * import { orderContract } from './contract';\n *\n * const handlers = defineHandlers(orderContract, {\n * processOrder: ({ payload }) =>\n * Future.fromPromise(processPayment(payload))\n * .mapOk(() => undefined)\n * .mapError((error) => new RetryableError('Payment failed', error)),\n * notifyOrder: ({ payload }) =>\n * Future.fromPromise(sendNotification(payload))\n * .mapOk(() => undefined)\n * .mapError((error) => new RetryableError('Notification failed', error)),\n * });\n * ```\n */\nexport function defineHandlers<TContract extends ContractDefinition>(\n contract: TContract,\n handlers: WorkerInferConsumerHandlers<TContract>,\n): WorkerInferConsumerHandlers<TContract> {\n validateHandlers(contract, handlers);\n return handlers;\n}\n"],"mappings":"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"}
|
package/docs/index.md
CHANGED
|
@@ -8,9 +8,20 @@
|
|
|
8
8
|
|
|
9
9
|
### MessageValidationError
|
|
10
10
|
|
|
11
|
-
Defined in:
|
|
11
|
+
Defined in: packages/core/dist/index.d.mts:83
|
|
12
12
|
|
|
13
|
-
Error thrown when message validation fails
|
|
13
|
+
Error thrown when message validation fails (payload or headers).
|
|
14
|
+
|
|
15
|
+
Used by both the client (publish-time payload validation) and the worker
|
|
16
|
+
(consume-time payload and headers validation).
|
|
17
|
+
|
|
18
|
+
#### Param
|
|
19
|
+
|
|
20
|
+
The name of the publisher or consumer that triggered the validation
|
|
21
|
+
|
|
22
|
+
#### Param
|
|
23
|
+
|
|
24
|
+
The validation issues from the Standard Schema validation
|
|
14
25
|
|
|
15
26
|
#### Extends
|
|
16
27
|
|
|
@@ -21,16 +32,16 @@ Error thrown when message validation fails
|
|
|
21
32
|
##### Constructor
|
|
22
33
|
|
|
23
34
|
```ts
|
|
24
|
-
new MessageValidationError(
|
|
35
|
+
new MessageValidationError(source, issues): MessageValidationError;
|
|
25
36
|
```
|
|
26
37
|
|
|
27
|
-
Defined in:
|
|
38
|
+
Defined in: packages/core/dist/index.d.mts:86
|
|
28
39
|
|
|
29
40
|
###### Parameters
|
|
30
41
|
|
|
31
42
|
| Parameter | Type |
|
|
32
43
|
| ------ | ------ |
|
|
33
|
-
| `
|
|
44
|
+
| `source` | `string` |
|
|
34
45
|
| `issues` | `unknown` |
|
|
35
46
|
|
|
36
47
|
###### Returns
|
|
@@ -48,12 +59,12 @@ Error.constructor
|
|
|
48
59
|
| Property | Modifier | Type | Description | Inherited from | Defined in |
|
|
49
60
|
| ------ | ------ | ------ | ------ | ------ | ------ |
|
|
50
61
|
| <a id="cause"></a> `cause?` | `public` | `unknown` | - | `Error.cause` | node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es2022.error.d.ts:26 |
|
|
51
|
-
| <a id="
|
|
52
|
-
| <a id="issues"></a> `issues` | `readonly` | `unknown` | - | - | [packages/worker/src/errors.ts:7](https://github.com/btravers/amqp-contract/blob/5f68ac0e7a01403c51ddebd327d65b70384c2e2f/packages/worker/src/errors.ts#L7) |
|
|
62
|
+
| <a id="issues"></a> `issues` | `readonly` | `unknown` | - | - | packages/core/dist/index.d.mts:85 |
|
|
53
63
|
| <a id="message"></a> `message` | `public` | `string` | - | `Error.message` | node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es5.d.ts:1077 |
|
|
54
64
|
| <a id="name"></a> `name` | `public` | `string` | - | `Error.name` | node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es5.d.ts:1076 |
|
|
65
|
+
| <a id="source"></a> `source` | `readonly` | `string` | - | - | packages/core/dist/index.d.mts:84 |
|
|
55
66
|
| <a id="stack"></a> `stack?` | `public` | `string` | - | `Error.stack` | node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es5.d.ts:1078 |
|
|
56
|
-
| <a id="stacktracelimit"></a> `stackTraceLimit` | `static` | `number` | The `Error.stackTraceLimit` property specifies the number of stack frames collected by a stack trace (whether generated by `new Error().stack` or `Error.captureStackTrace(obj)`). The default value is `10` but may be set to any valid JavaScript number. Changes will affect any stack trace captured _after_ the value has been changed. If set to a non-number value, or set to a negative number, stack traces will not capture any frames. | `Error.stackTraceLimit` | node\_modules/.pnpm/@types+node@25.2.
|
|
67
|
+
| <a id="stacktracelimit"></a> `stackTraceLimit` | `static` | `number` | The `Error.stackTraceLimit` property specifies the number of stack frames collected by a stack trace (whether generated by `new Error().stack` or `Error.captureStackTrace(obj)`). The default value is `10` but may be set to any valid JavaScript number. Changes will affect any stack trace captured _after_ the value has been changed. If set to a non-number value, or set to a negative number, stack traces will not capture any frames. | `Error.stackTraceLimit` | node\_modules/.pnpm/@types+node@25.2.3/node\_modules/@types/node/globals.d.ts:67 |
|
|
57
68
|
|
|
58
69
|
#### Methods
|
|
59
70
|
|
|
@@ -63,7 +74,7 @@ Error.constructor
|
|
|
63
74
|
static captureStackTrace(targetObject, constructorOpt?): void;
|
|
64
75
|
```
|
|
65
76
|
|
|
66
|
-
Defined in: node\_modules/.pnpm/@types+node@25.2.
|
|
77
|
+
Defined in: node\_modules/.pnpm/@types+node@25.2.3/node\_modules/@types/node/globals.d.ts:51
|
|
67
78
|
|
|
68
79
|
Creates a `.stack` property on `targetObject`, which when accessed returns
|
|
69
80
|
a string representing the location in the code at which
|
|
@@ -132,7 +143,7 @@ Error.captureStackTrace
|
|
|
132
143
|
static prepareStackTrace(err, stackTraces): any;
|
|
133
144
|
```
|
|
134
145
|
|
|
135
|
-
Defined in: node\_modules/.pnpm/@types+node@25.2.
|
|
146
|
+
Defined in: node\_modules/.pnpm/@types+node@25.2.3/node\_modules/@types/node/globals.d.ts:55
|
|
136
147
|
|
|
137
148
|
###### Parameters
|
|
138
149
|
|
|
@@ -159,7 +170,7 @@ Error.prepareStackTrace
|
|
|
159
170
|
|
|
160
171
|
### NonRetryableError
|
|
161
172
|
|
|
162
|
-
Defined in: [packages/worker/src/errors.ts:
|
|
173
|
+
Defined in: [packages/worker/src/errors.ts:34](https://github.com/btravers/amqp-contract/blob/a5ff0e58db355e3410ea3b765884871c3b44c87d/packages/worker/src/errors.ts#L34)
|
|
163
174
|
|
|
164
175
|
Non-retryable errors - permanent failures that should not be retried
|
|
165
176
|
Examples: invalid data, business rule violations, permanent external failures
|
|
@@ -179,7 +190,7 @@ immediately sent to the dead letter queue (DLQ) if configured.
|
|
|
179
190
|
new NonRetryableError(message, cause?): NonRetryableError;
|
|
180
191
|
```
|
|
181
192
|
|
|
182
|
-
Defined in: [packages/worker/src/errors.ts:
|
|
193
|
+
Defined in: [packages/worker/src/errors.ts:35](https://github.com/btravers/amqp-contract/blob/a5ff0e58db355e3410ea3b765884871c3b44c87d/packages/worker/src/errors.ts#L35)
|
|
183
194
|
|
|
184
195
|
###### Parameters
|
|
185
196
|
|
|
@@ -202,11 +213,11 @@ Error.constructor
|
|
|
202
213
|
|
|
203
214
|
| Property | Modifier | Type | Description | Inherited from | Defined in |
|
|
204
215
|
| ------ | ------ | ------ | ------ | ------ | ------ |
|
|
205
|
-
| <a id="cause-1"></a> `cause?` | `readonly` | `unknown` | - | `Error.cause` | [packages/worker/src/errors.ts:
|
|
216
|
+
| <a id="cause-1"></a> `cause?` | `readonly` | `unknown` | - | `Error.cause` | [packages/worker/src/errors.ts:37](https://github.com/btravers/amqp-contract/blob/a5ff0e58db355e3410ea3b765884871c3b44c87d/packages/worker/src/errors.ts#L37) |
|
|
206
217
|
| <a id="message-1"></a> `message` | `public` | `string` | - | `Error.message` | node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es5.d.ts:1077 |
|
|
207
218
|
| <a id="name-1"></a> `name` | `public` | `string` | - | `Error.name` | node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es5.d.ts:1076 |
|
|
208
219
|
| <a id="stack-1"></a> `stack?` | `public` | `string` | - | `Error.stack` | node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es5.d.ts:1078 |
|
|
209
|
-
| <a id="stacktracelimit-1"></a> `stackTraceLimit` | `static` | `number` | The `Error.stackTraceLimit` property specifies the number of stack frames collected by a stack trace (whether generated by `new Error().stack` or `Error.captureStackTrace(obj)`). The default value is `10` but may be set to any valid JavaScript number. Changes will affect any stack trace captured _after_ the value has been changed. If set to a non-number value, or set to a negative number, stack traces will not capture any frames. | `Error.stackTraceLimit` | node\_modules/.pnpm/@types+node@25.2.
|
|
220
|
+
| <a id="stacktracelimit-1"></a> `stackTraceLimit` | `static` | `number` | The `Error.stackTraceLimit` property specifies the number of stack frames collected by a stack trace (whether generated by `new Error().stack` or `Error.captureStackTrace(obj)`). The default value is `10` but may be set to any valid JavaScript number. Changes will affect any stack trace captured _after_ the value has been changed. If set to a non-number value, or set to a negative number, stack traces will not capture any frames. | `Error.stackTraceLimit` | node\_modules/.pnpm/@types+node@25.2.3/node\_modules/@types/node/globals.d.ts:67 |
|
|
210
221
|
|
|
211
222
|
#### Methods
|
|
212
223
|
|
|
@@ -216,7 +227,7 @@ Error.constructor
|
|
|
216
227
|
static captureStackTrace(targetObject, constructorOpt?): void;
|
|
217
228
|
```
|
|
218
229
|
|
|
219
|
-
Defined in: node\_modules/.pnpm/@types+node@25.2.
|
|
230
|
+
Defined in: node\_modules/.pnpm/@types+node@25.2.3/node\_modules/@types/node/globals.d.ts:51
|
|
220
231
|
|
|
221
232
|
Creates a `.stack` property on `targetObject`, which when accessed returns
|
|
222
233
|
a string representing the location in the code at which
|
|
@@ -285,7 +296,7 @@ Error.captureStackTrace
|
|
|
285
296
|
static prepareStackTrace(err, stackTraces): any;
|
|
286
297
|
```
|
|
287
298
|
|
|
288
|
-
Defined in: node\_modules/.pnpm/@types+node@25.2.
|
|
299
|
+
Defined in: node\_modules/.pnpm/@types+node@25.2.3/node\_modules/@types/node/globals.d.ts:55
|
|
289
300
|
|
|
290
301
|
###### Parameters
|
|
291
302
|
|
|
@@ -312,7 +323,7 @@ Error.prepareStackTrace
|
|
|
312
323
|
|
|
313
324
|
### RetryableError
|
|
314
325
|
|
|
315
|
-
Defined in: [packages/worker/src/errors.ts:
|
|
326
|
+
Defined in: [packages/worker/src/errors.ts:10](https://github.com/btravers/amqp-contract/blob/a5ff0e58db355e3410ea3b765884871c3b44c87d/packages/worker/src/errors.ts#L10)
|
|
316
327
|
|
|
317
328
|
Retryable errors - transient failures that may succeed on retry
|
|
318
329
|
Examples: network timeouts, rate limiting, temporary service unavailability
|
|
@@ -332,7 +343,7 @@ The worker will apply exponential backoff and retry the message.
|
|
|
332
343
|
new RetryableError(message, cause?): RetryableError;
|
|
333
344
|
```
|
|
334
345
|
|
|
335
|
-
Defined in: [packages/worker/src/errors.ts:
|
|
346
|
+
Defined in: [packages/worker/src/errors.ts:11](https://github.com/btravers/amqp-contract/blob/a5ff0e58db355e3410ea3b765884871c3b44c87d/packages/worker/src/errors.ts#L11)
|
|
336
347
|
|
|
337
348
|
###### Parameters
|
|
338
349
|
|
|
@@ -355,11 +366,11 @@ Error.constructor
|
|
|
355
366
|
|
|
356
367
|
| Property | Modifier | Type | Description | Inherited from | Defined in |
|
|
357
368
|
| ------ | ------ | ------ | ------ | ------ | ------ |
|
|
358
|
-
| <a id="cause-2"></a> `cause?` | `readonly` | `unknown` | - | `Error.cause` | [packages/worker/src/errors.ts:
|
|
369
|
+
| <a id="cause-2"></a> `cause?` | `readonly` | `unknown` | - | `Error.cause` | [packages/worker/src/errors.ts:13](https://github.com/btravers/amqp-contract/blob/a5ff0e58db355e3410ea3b765884871c3b44c87d/packages/worker/src/errors.ts#L13) |
|
|
359
370
|
| <a id="message-2"></a> `message` | `public` | `string` | - | `Error.message` | node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es5.d.ts:1077 |
|
|
360
371
|
| <a id="name-2"></a> `name` | `public` | `string` | - | `Error.name` | node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es5.d.ts:1076 |
|
|
361
372
|
| <a id="stack-2"></a> `stack?` | `public` | `string` | - | `Error.stack` | node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es5.d.ts:1078 |
|
|
362
|
-
| <a id="stacktracelimit-2"></a> `stackTraceLimit` | `static` | `number` | The `Error.stackTraceLimit` property specifies the number of stack frames collected by a stack trace (whether generated by `new Error().stack` or `Error.captureStackTrace(obj)`). The default value is `10` but may be set to any valid JavaScript number. Changes will affect any stack trace captured _after_ the value has been changed. If set to a non-number value, or set to a negative number, stack traces will not capture any frames. | `Error.stackTraceLimit` | node\_modules/.pnpm/@types+node@25.2.
|
|
373
|
+
| <a id="stacktracelimit-2"></a> `stackTraceLimit` | `static` | `number` | The `Error.stackTraceLimit` property specifies the number of stack frames collected by a stack trace (whether generated by `new Error().stack` or `Error.captureStackTrace(obj)`). The default value is `10` but may be set to any valid JavaScript number. Changes will affect any stack trace captured _after_ the value has been changed. If set to a non-number value, or set to a negative number, stack traces will not capture any frames. | `Error.stackTraceLimit` | node\_modules/.pnpm/@types+node@25.2.3/node\_modules/@types/node/globals.d.ts:67 |
|
|
363
374
|
|
|
364
375
|
#### Methods
|
|
365
376
|
|
|
@@ -369,7 +380,7 @@ Error.constructor
|
|
|
369
380
|
static captureStackTrace(targetObject, constructorOpt?): void;
|
|
370
381
|
```
|
|
371
382
|
|
|
372
|
-
Defined in: node\_modules/.pnpm/@types+node@25.2.
|
|
383
|
+
Defined in: node\_modules/.pnpm/@types+node@25.2.3/node\_modules/@types/node/globals.d.ts:51
|
|
373
384
|
|
|
374
385
|
Creates a `.stack` property on `targetObject`, which when accessed returns
|
|
375
386
|
a string representing the location in the code at which
|
|
@@ -438,7 +449,7 @@ Error.captureStackTrace
|
|
|
438
449
|
static prepareStackTrace(err, stackTraces): any;
|
|
439
450
|
```
|
|
440
451
|
|
|
441
|
-
Defined in: node\_modules/.pnpm/@types+node@25.2.
|
|
452
|
+
Defined in: node\_modules/.pnpm/@types+node@25.2.3/node\_modules/@types/node/globals.d.ts:55
|
|
442
453
|
|
|
443
454
|
###### Parameters
|
|
444
455
|
|
|
@@ -465,7 +476,7 @@ Error.prepareStackTrace
|
|
|
465
476
|
|
|
466
477
|
### TypedAmqpWorker
|
|
467
478
|
|
|
468
|
-
Defined in: [packages/worker/src/worker.ts:158](https://github.com/btravers/amqp-contract/blob/
|
|
479
|
+
Defined in: [packages/worker/src/worker.ts:158](https://github.com/btravers/amqp-contract/blob/a5ff0e58db355e3410ea3b765884871c3b44c87d/packages/worker/src/worker.ts#L158)
|
|
469
480
|
|
|
470
481
|
Type-safe AMQP worker for consuming messages from RabbitMQ.
|
|
471
482
|
|
|
@@ -520,7 +531,7 @@ await worker.close().resultToPromise();
|
|
|
520
531
|
close(): Future<Result<void, TechnicalError>>;
|
|
521
532
|
```
|
|
522
533
|
|
|
523
|
-
Defined in: [packages/worker/src/worker.ts:278](https://github.com/btravers/amqp-contract/blob/
|
|
534
|
+
Defined in: [packages/worker/src/worker.ts:278](https://github.com/btravers/amqp-contract/blob/a5ff0e58db355e3410ea3b765884871c3b44c87d/packages/worker/src/worker.ts#L278)
|
|
524
535
|
|
|
525
536
|
Close the AMQP channel and connection.
|
|
526
537
|
|
|
@@ -548,7 +559,7 @@ if (closeResult.isOk()) {
|
|
|
548
559
|
static create<TContract>(options): Future<Result<TypedAmqpWorker<TContract>, TechnicalError>>;
|
|
549
560
|
```
|
|
550
561
|
|
|
551
|
-
Defined in: [packages/worker/src/worker.ts:235](https://github.com/btravers/amqp-contract/blob/
|
|
562
|
+
Defined in: [packages/worker/src/worker.ts:235](https://github.com/btravers/amqp-contract/blob/a5ff0e58db355e3410ea3b765884871c3b44c87d/packages/worker/src/worker.ts#L235)
|
|
552
563
|
|
|
553
564
|
Create a type-safe AMQP worker from a contract.
|
|
554
565
|
|
|
@@ -598,7 +609,7 @@ const worker = await TypedAmqpWorker.create({
|
|
|
598
609
|
type CreateWorkerOptions<TContract> = object;
|
|
599
610
|
```
|
|
600
611
|
|
|
601
|
-
Defined in: [packages/worker/src/worker.ts:94](https://github.com/btravers/amqp-contract/blob/
|
|
612
|
+
Defined in: [packages/worker/src/worker.ts:94](https://github.com/btravers/amqp-contract/blob/a5ff0e58db355e3410ea3b765884871c3b44c87d/packages/worker/src/worker.ts#L94)
|
|
602
613
|
|
|
603
614
|
Options for creating a type-safe AMQP worker.
|
|
604
615
|
|
|
@@ -643,12 +654,12 @@ not at the handler level. See `QueueDefinition.retry` for configuration options.
|
|
|
643
654
|
|
|
644
655
|
| Property | Type | Description | Defined in |
|
|
645
656
|
| ------ | ------ | ------ | ------ |
|
|
646
|
-
| <a id="connectionoptions"></a> `connectionOptions?` | `AmqpConnectionManagerOptions` | Optional connection configuration (heartbeat, reconnect settings, etc.) | [packages/worker/src/worker.ts:106](https://github.com/btravers/amqp-contract/blob/
|
|
647
|
-
| <a id="contract"></a> `contract` | `TContract` | The AMQP contract definition specifying consumers and their message schemas | [packages/worker/src/worker.ts:96](https://github.com/btravers/amqp-contract/blob/
|
|
648
|
-
| <a id="handlers"></a> `handlers` | [`WorkerInferConsumerHandlers`](#workerinferconsumerhandlers)<`TContract`> | Handlers for each consumer defined in the contract. Handlers must return `Future<Result<void, HandlerError>>` for explicit error handling. Use defineHandler() to create handlers. | [packages/worker/src/worker.ts:102](https://github.com/btravers/amqp-contract/blob/
|
|
649
|
-
| <a id="logger"></a> `logger?` | `Logger` | Optional logger for logging message consumption and errors | [packages/worker/src/worker.ts:108](https://github.com/btravers/amqp-contract/blob/
|
|
650
|
-
| <a id="telemetry"></a> `telemetry?` | `TelemetryProvider` | Optional telemetry provider for tracing and metrics. If not provided, uses the default provider which attempts to load OpenTelemetry. OpenTelemetry instrumentation is automatically enabled if @opentelemetry/api is installed. | [packages/worker/src/worker.ts:114](https://github.com/btravers/amqp-contract/blob/
|
|
651
|
-
| <a id="urls"></a> `urls` | `ConnectionUrl`[] | AMQP broker URL(s). Multiple URLs provide failover support | [packages/worker/src/worker.ts:104](https://github.com/btravers/amqp-contract/blob/
|
|
657
|
+
| <a id="connectionoptions"></a> `connectionOptions?` | `AmqpConnectionManagerOptions` | Optional connection configuration (heartbeat, reconnect settings, etc.) | [packages/worker/src/worker.ts:106](https://github.com/btravers/amqp-contract/blob/a5ff0e58db355e3410ea3b765884871c3b44c87d/packages/worker/src/worker.ts#L106) |
|
|
658
|
+
| <a id="contract"></a> `contract` | `TContract` | The AMQP contract definition specifying consumers and their message schemas | [packages/worker/src/worker.ts:96](https://github.com/btravers/amqp-contract/blob/a5ff0e58db355e3410ea3b765884871c3b44c87d/packages/worker/src/worker.ts#L96) |
|
|
659
|
+
| <a id="handlers"></a> `handlers` | [`WorkerInferConsumerHandlers`](#workerinferconsumerhandlers)<`TContract`> | Handlers for each consumer defined in the contract. Handlers must return `Future<Result<void, HandlerError>>` for explicit error handling. Use defineHandler() to create handlers. | [packages/worker/src/worker.ts:102](https://github.com/btravers/amqp-contract/blob/a5ff0e58db355e3410ea3b765884871c3b44c87d/packages/worker/src/worker.ts#L102) |
|
|
660
|
+
| <a id="logger"></a> `logger?` | `Logger` | Optional logger for logging message consumption and errors | [packages/worker/src/worker.ts:108](https://github.com/btravers/amqp-contract/blob/a5ff0e58db355e3410ea3b765884871c3b44c87d/packages/worker/src/worker.ts#L108) |
|
|
661
|
+
| <a id="telemetry"></a> `telemetry?` | `TelemetryProvider` | Optional telemetry provider for tracing and metrics. If not provided, uses the default provider which attempts to load OpenTelemetry. OpenTelemetry instrumentation is automatically enabled if @opentelemetry/api is installed. | [packages/worker/src/worker.ts:114](https://github.com/btravers/amqp-contract/blob/a5ff0e58db355e3410ea3b765884871c3b44c87d/packages/worker/src/worker.ts#L114) |
|
|
662
|
+
| <a id="urls"></a> `urls` | `ConnectionUrl`[] | AMQP broker URL(s). Multiple URLs provide failover support | [packages/worker/src/worker.ts:104](https://github.com/btravers/amqp-contract/blob/a5ff0e58db355e3410ea3b765884871c3b44c87d/packages/worker/src/worker.ts#L104) |
|
|
652
663
|
|
|
653
664
|
***
|
|
654
665
|
|
|
@@ -660,7 +671,7 @@ type HandlerError =
|
|
|
660
671
|
| NonRetryableError;
|
|
661
672
|
```
|
|
662
673
|
|
|
663
|
-
Defined in: [packages/worker/src/errors.ts:
|
|
674
|
+
Defined in: [packages/worker/src/errors.ts:55](https://github.com/btravers/amqp-contract/blob/a5ff0e58db355e3410ea3b765884871c3b44c87d/packages/worker/src/errors.ts#L55)
|
|
664
675
|
|
|
665
676
|
Union type representing all handler errors.
|
|
666
677
|
Use this type when defining handlers that explicitly signal error outcomes.
|
|
@@ -673,7 +684,7 @@ Use this type when defining handlers that explicitly signal error outcomes.
|
|
|
673
684
|
type WorkerConsumedMessage<TPayload, THeaders> = object;
|
|
674
685
|
```
|
|
675
686
|
|
|
676
|
-
Defined in: [packages/worker/src/types.ts:104](https://github.com/btravers/amqp-contract/blob/
|
|
687
|
+
Defined in: [packages/worker/src/types.ts:104](https://github.com/btravers/amqp-contract/blob/a5ff0e58db355e3410ea3b765884871c3b44c87d/packages/worker/src/types.ts#L104)
|
|
677
688
|
|
|
678
689
|
A consumed message containing parsed payload and headers.
|
|
679
690
|
|
|
@@ -703,8 +714,8 @@ const handler = defineHandler(contract, 'processOrder', (message, rawMessage) =>
|
|
|
703
714
|
|
|
704
715
|
| Property | Type | Description | Defined in |
|
|
705
716
|
| ------ | ------ | ------ | ------ |
|
|
706
|
-
| <a id="headers"></a> `headers` | `THeaders` *extends* `undefined` ? `undefined` : `THeaders` | The validated message headers (present only when headers schema is defined) | [packages/worker/src/types.ts:108](https://github.com/btravers/amqp-contract/blob/
|
|
707
|
-
| <a id="payload"></a> `payload` | `TPayload` | The validated message payload | [packages/worker/src/types.ts:106](https://github.com/btravers/amqp-contract/blob/
|
|
717
|
+
| <a id="headers"></a> `headers` | `THeaders` *extends* `undefined` ? `undefined` : `THeaders` | The validated message headers (present only when headers schema is defined) | [packages/worker/src/types.ts:108](https://github.com/btravers/amqp-contract/blob/a5ff0e58db355e3410ea3b765884871c3b44c87d/packages/worker/src/types.ts#L108) |
|
|
718
|
+
| <a id="payload"></a> `payload` | `TPayload` | The validated message payload | [packages/worker/src/types.ts:106](https://github.com/btravers/amqp-contract/blob/a5ff0e58db355e3410ea3b765884871c3b44c87d/packages/worker/src/types.ts#L106) |
|
|
708
719
|
|
|
709
720
|
***
|
|
710
721
|
|
|
@@ -714,7 +725,7 @@ const handler = defineHandler(contract, 'processOrder', (message, rawMessage) =>
|
|
|
714
725
|
type WorkerInferConsumedMessage<TContract, TName> = WorkerConsumedMessage<WorkerInferConsumerPayload<TContract, TName>, WorkerInferConsumerHeaders<TContract, TName>>;
|
|
715
726
|
```
|
|
716
727
|
|
|
717
|
-
Defined in: [packages/worker/src/types.ts:115](https://github.com/btravers/amqp-contract/blob/
|
|
728
|
+
Defined in: [packages/worker/src/types.ts:115](https://github.com/btravers/amqp-contract/blob/a5ff0e58db355e3410ea3b765884871c3b44c87d/packages/worker/src/types.ts#L115)
|
|
718
729
|
|
|
719
730
|
Infer the full consumed message type for a specific consumer.
|
|
720
731
|
Includes both payload and headers (if defined).
|
|
@@ -734,7 +745,7 @@ Includes both payload and headers (if defined).
|
|
|
734
745
|
type WorkerInferConsumerHandler<TContract, TName> = (message, rawMessage) => Future<Result<void, HandlerError>>;
|
|
735
746
|
```
|
|
736
747
|
|
|
737
|
-
Defined in: [packages/worker/src/types.ts:154](https://github.com/btravers/amqp-contract/blob/
|
|
748
|
+
Defined in: [packages/worker/src/types.ts:154](https://github.com/btravers/amqp-contract/blob/a5ff0e58db355e3410ea3b765884871c3b44c87d/packages/worker/src/types.ts#L154)
|
|
738
749
|
|
|
739
750
|
Consumer handler type for a specific consumer.
|
|
740
751
|
Returns a `Future<Result<void, HandlerError>>` for explicit error handling.
|
|
@@ -787,7 +798,7 @@ type WorkerInferConsumerHandlerEntry<TContract, TName> =
|
|
|
787
798
|
}];
|
|
788
799
|
```
|
|
789
800
|
|
|
790
|
-
Defined in: [packages/worker/src/types.ts:172](https://github.com/btravers/amqp-contract/blob/
|
|
801
|
+
Defined in: [packages/worker/src/types.ts:172](https://github.com/btravers/amqp-contract/blob/a5ff0e58db355e3410ea3b765884871c3b44c87d/packages/worker/src/types.ts#L172)
|
|
791
802
|
|
|
792
803
|
Handler entry for a consumer - either a function or a tuple of [handler, options].
|
|
793
804
|
|
|
@@ -813,7 +824,7 @@ not at the handler level. See `QueueDefinition.retry` for configuration options.
|
|
|
813
824
|
type WorkerInferConsumerHandlers<TContract> = { [K in InferConsumerNames<TContract>]: WorkerInferConsumerHandlerEntry<TContract, K> };
|
|
814
825
|
```
|
|
815
826
|
|
|
816
|
-
Defined in: [packages/worker/src/types.ts:193](https://github.com/btravers/amqp-contract/blob/
|
|
827
|
+
Defined in: [packages/worker/src/types.ts:193](https://github.com/btravers/amqp-contract/blob/a5ff0e58db355e3410ea3b765884871c3b44c87d/packages/worker/src/types.ts#L193)
|
|
817
828
|
|
|
818
829
|
Consumer handlers for a contract.
|
|
819
830
|
All handlers return `Future<Result<void, HandlerError>>` for explicit error control.
|
|
@@ -843,7 +854,7 @@ const handlers: WorkerInferConsumerHandlers<typeof contract> = {
|
|
|
843
854
|
type WorkerInferConsumerHeaders<TContract, TName> = ConsumerInferHeadersInput<InferConsumer<TContract, TName>>;
|
|
844
855
|
```
|
|
845
856
|
|
|
846
|
-
Defined in: [packages/worker/src/types.ts:79](https://github.com/btravers/amqp-contract/blob/
|
|
857
|
+
Defined in: [packages/worker/src/types.ts:79](https://github.com/btravers/amqp-contract/blob/a5ff0e58db355e3410ea3b765884871c3b44c87d/packages/worker/src/types.ts#L79)
|
|
847
858
|
|
|
848
859
|
Infer the headers type for a specific consumer
|
|
849
860
|
Returns undefined if no headers schema is defined
|
|
@@ -855,68 +866,6 @@ Returns undefined if no headers schema is defined
|
|
|
855
866
|
| `TContract` *extends* `ContractDefinition` |
|
|
856
867
|
| `TName` *extends* `InferConsumerNames`<`TContract`> |
|
|
857
868
|
|
|
858
|
-
***
|
|
859
|
-
|
|
860
|
-
### ~~WorkerInferSafeConsumerHandler~~
|
|
861
|
-
|
|
862
|
-
```ts
|
|
863
|
-
type WorkerInferSafeConsumerHandler<TContract, TName> = WorkerInferConsumerHandler<TContract, TName>;
|
|
864
|
-
```
|
|
865
|
-
|
|
866
|
-
Defined in: [packages/worker/src/types.ts:205](https://github.com/btravers/amqp-contract/blob/5f68ac0e7a01403c51ddebd327d65b70384c2e2f/packages/worker/src/types.ts#L205)
|
|
867
|
-
|
|
868
|
-
#### Type Parameters
|
|
869
|
-
|
|
870
|
-
| Type Parameter |
|
|
871
|
-
| ------ |
|
|
872
|
-
| `TContract` *extends* `ContractDefinition` |
|
|
873
|
-
| `TName` *extends* `InferConsumerNames`<`TContract`> |
|
|
874
|
-
|
|
875
|
-
#### Deprecated
|
|
876
|
-
|
|
877
|
-
Use `WorkerInferConsumerHandler` instead. Will be removed in next major version.
|
|
878
|
-
|
|
879
|
-
***
|
|
880
|
-
|
|
881
|
-
### ~~WorkerInferSafeConsumerHandlerEntry~~
|
|
882
|
-
|
|
883
|
-
```ts
|
|
884
|
-
type WorkerInferSafeConsumerHandlerEntry<TContract, TName> = WorkerInferConsumerHandlerEntry<TContract, TName>;
|
|
885
|
-
```
|
|
886
|
-
|
|
887
|
-
Defined in: [packages/worker/src/types.ts:213](https://github.com/btravers/amqp-contract/blob/5f68ac0e7a01403c51ddebd327d65b70384c2e2f/packages/worker/src/types.ts#L213)
|
|
888
|
-
|
|
889
|
-
#### Type Parameters
|
|
890
|
-
|
|
891
|
-
| Type Parameter |
|
|
892
|
-
| ------ |
|
|
893
|
-
| `TContract` *extends* `ContractDefinition` |
|
|
894
|
-
| `TName` *extends* `InferConsumerNames`<`TContract`> |
|
|
895
|
-
|
|
896
|
-
#### Deprecated
|
|
897
|
-
|
|
898
|
-
Use `WorkerInferConsumerHandlerEntry` instead. Will be removed in next major version.
|
|
899
|
-
|
|
900
|
-
***
|
|
901
|
-
|
|
902
|
-
### ~~WorkerInferSafeConsumerHandlers~~
|
|
903
|
-
|
|
904
|
-
```ts
|
|
905
|
-
type WorkerInferSafeConsumerHandlers<TContract> = WorkerInferConsumerHandlers<TContract>;
|
|
906
|
-
```
|
|
907
|
-
|
|
908
|
-
Defined in: [packages/worker/src/types.ts:221](https://github.com/btravers/amqp-contract/blob/5f68ac0e7a01403c51ddebd327d65b70384c2e2f/packages/worker/src/types.ts#L221)
|
|
909
|
-
|
|
910
|
-
#### Type Parameters
|
|
911
|
-
|
|
912
|
-
| Type Parameter |
|
|
913
|
-
| ------ |
|
|
914
|
-
| `TContract` *extends* `ContractDefinition` |
|
|
915
|
-
|
|
916
|
-
#### Deprecated
|
|
917
|
-
|
|
918
|
-
Use `WorkerInferConsumerHandlers` instead. Will be removed in next major version.
|
|
919
|
-
|
|
920
869
|
## Functions
|
|
921
870
|
|
|
922
871
|
### defineHandler()
|
|
@@ -930,7 +879,7 @@ function defineHandler<TContract, TName>(
|
|
|
930
879
|
handler): WorkerInferConsumerHandlerEntry<TContract, TName>;
|
|
931
880
|
```
|
|
932
881
|
|
|
933
|
-
Defined in: [packages/worker/src/handlers.ts:103](https://github.com/btravers/amqp-contract/blob/
|
|
882
|
+
Defined in: [packages/worker/src/handlers.ts:103](https://github.com/btravers/amqp-contract/blob/a5ff0e58db355e3410ea3b765884871c3b44c87d/packages/worker/src/handlers.ts#L103)
|
|
934
883
|
|
|
935
884
|
Define a type-safe handler for a specific consumer in a contract.
|
|
936
885
|
|
|
@@ -1003,7 +952,7 @@ function defineHandler<TContract, TName>(
|
|
|
1003
952
|
options): WorkerInferConsumerHandlerEntry<TContract, TName>;
|
|
1004
953
|
```
|
|
1005
954
|
|
|
1006
|
-
Defined in: [packages/worker/src/handlers.ts:111](https://github.com/btravers/amqp-contract/blob/
|
|
955
|
+
Defined in: [packages/worker/src/handlers.ts:111](https://github.com/btravers/amqp-contract/blob/a5ff0e58db355e3410ea3b765884871c3b44c87d/packages/worker/src/handlers.ts#L111)
|
|
1007
956
|
|
|
1008
957
|
Define a type-safe handler for a specific consumer in a contract.
|
|
1009
958
|
|
|
@@ -1076,7 +1025,7 @@ const validateOrderHandler = defineHandler(
|
|
|
1076
1025
|
function defineHandlers<TContract>(contract, handlers): WorkerInferConsumerHandlers<TContract>;
|
|
1077
1026
|
```
|
|
1078
1027
|
|
|
1079
|
-
Defined in: [packages/worker/src/handlers.ts:166](https://github.com/btravers/amqp-contract/blob/
|
|
1028
|
+
Defined in: [packages/worker/src/handlers.ts:166](https://github.com/btravers/amqp-contract/blob/a5ff0e58db355e3410ea3b765884871c3b44c87d/packages/worker/src/handlers.ts#L166)
|
|
1080
1029
|
|
|
1081
1030
|
Define multiple type-safe handlers for consumers in a contract.
|
|
1082
1031
|
|
|
@@ -1129,7 +1078,7 @@ const handlers = defineHandlers(orderContract, {
|
|
|
1129
1078
|
function isHandlerError(error): error is HandlerError;
|
|
1130
1079
|
```
|
|
1131
1080
|
|
|
1132
|
-
Defined in: [packages/worker/src/errors.ts:
|
|
1081
|
+
Defined in: [packages/worker/src/errors.ts:131](https://github.com/btravers/amqp-contract/blob/a5ff0e58db355e3410ea3b765884871c3b44c87d/packages/worker/src/errors.ts#L131)
|
|
1133
1082
|
|
|
1134
1083
|
Type guard to check if an error is any HandlerError (RetryableError or NonRetryableError).
|
|
1135
1084
|
|
|
@@ -1166,7 +1115,7 @@ function handleError(error: unknown) {
|
|
|
1166
1115
|
function isNonRetryableError(error): error is NonRetryableError;
|
|
1167
1116
|
```
|
|
1168
1117
|
|
|
1169
|
-
Defined in: [packages/worker/src/errors.ts:
|
|
1118
|
+
Defined in: [packages/worker/src/errors.ts:109](https://github.com/btravers/amqp-contract/blob/a5ff0e58db355e3410ea3b765884871c3b44c87d/packages/worker/src/errors.ts#L109)
|
|
1170
1119
|
|
|
1171
1120
|
Type guard to check if an error is a NonRetryableError.
|
|
1172
1121
|
|
|
@@ -1206,7 +1155,7 @@ try {
|
|
|
1206
1155
|
function isRetryableError(error): error is RetryableError;
|
|
1207
1156
|
```
|
|
1208
1157
|
|
|
1209
|
-
Defined in: [packages/worker/src/errors.ts:
|
|
1158
|
+
Defined in: [packages/worker/src/errors.ts:84](https://github.com/btravers/amqp-contract/blob/a5ff0e58db355e3410ea3b765884871c3b44c87d/packages/worker/src/errors.ts#L84)
|
|
1210
1159
|
|
|
1211
1160
|
Type guard to check if an error is a RetryableError.
|
|
1212
1161
|
|
|
@@ -1248,7 +1197,7 @@ try {
|
|
|
1248
1197
|
function nonRetryable(message, cause?): NonRetryableError;
|
|
1249
1198
|
```
|
|
1250
1199
|
|
|
1251
|
-
Defined in: [packages/worker/src/errors.ts:
|
|
1200
|
+
Defined in: [packages/worker/src/errors.ts:193](https://github.com/btravers/amqp-contract/blob/a5ff0e58db355e3410ea3b765884871c3b44c87d/packages/worker/src/errors.ts#L193)
|
|
1252
1201
|
|
|
1253
1202
|
Create a NonRetryableError with less verbosity.
|
|
1254
1203
|
|
|
@@ -1293,7 +1242,7 @@ const handler = ({ payload }) => {
|
|
|
1293
1242
|
function retryable(message, cause?): RetryableError;
|
|
1294
1243
|
```
|
|
1295
1244
|
|
|
1296
|
-
Defined in: [packages/worker/src/errors.ts:
|
|
1245
|
+
Defined in: [packages/worker/src/errors.ts:163](https://github.com/btravers/amqp-contract/blob/a5ff0e58db355e3410ea3b765884871c3b44c87d/packages/worker/src/errors.ts#L163)
|
|
1297
1246
|
|
|
1298
1247
|
Create a RetryableError with less verbosity.
|
|
1299
1248
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@amqp-contract/worker",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"description": "Worker utilities for consuming messages using amqp-contract",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"amqp",
|
|
@@ -52,22 +52,22 @@
|
|
|
52
52
|
"dependencies": {
|
|
53
53
|
"@standard-schema/spec": "1.1.0",
|
|
54
54
|
"@swan-io/boxed": "3.2.1",
|
|
55
|
-
"@amqp-contract/contract": "0.
|
|
56
|
-
"@amqp-contract/core": "0.
|
|
55
|
+
"@amqp-contract/contract": "0.19.0",
|
|
56
|
+
"@amqp-contract/core": "0.19.0"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
59
|
"@types/amqplib": "0.10.8",
|
|
60
|
-
"@types/node": "25.2.
|
|
60
|
+
"@types/node": "25.2.3",
|
|
61
61
|
"@vitest/coverage-v8": "4.0.18",
|
|
62
62
|
"amqp-connection-manager": "5.0.0",
|
|
63
63
|
"amqplib": "0.10.9",
|
|
64
64
|
"tsdown": "0.20.3",
|
|
65
65
|
"typedoc": "0.28.16",
|
|
66
|
-
"typedoc-plugin-markdown": "4.
|
|
66
|
+
"typedoc-plugin-markdown": "4.10.0",
|
|
67
67
|
"typescript": "5.9.3",
|
|
68
68
|
"vitest": "4.0.18",
|
|
69
69
|
"zod": "4.3.6",
|
|
70
|
-
"@amqp-contract/testing": "0.
|
|
70
|
+
"@amqp-contract/testing": "0.19.0",
|
|
71
71
|
"@amqp-contract/tsconfig": "0.1.0",
|
|
72
72
|
"@amqp-contract/typedoc": "0.1.0"
|
|
73
73
|
},
|