@amqp-contract/worker 0.1.3 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -39,6 +39,10 @@ const worker = await TypedAmqpWorker.create({
39
39
  // await worker.close();
40
40
  ```
41
41
 
42
+ ## Defining Handlers Externally
43
+
44
+ You can define handlers outside of the worker creation using `defineHandler` and `defineHandlers` for better code organization. See the [Worker API documentation](https://btravers.github.io/amqp-contract/api/worker) for details.
45
+
42
46
  ## Error Handling
43
47
 
44
48
  Worker handlers use standard Promise-based async/await pattern:
@@ -70,61 +74,7 @@ These errors are logged but **handlers don't need to use them** - just throw sta
70
74
 
71
75
  ## API
72
76
 
73
- ### `TypedAmqpWorker.create(options)`
74
-
75
- Create a type-safe AMQP worker from a contract with message handlers. Automatically connects and starts consuming all messages.
76
-
77
- **Parameters:**
78
-
79
- - `options.contract` - Contract definition
80
- - `options.handlers` - Object with async handler functions for each consumer
81
- - `options.connection` - AMQP connection URL (string) or connection options (Options.Connect)
82
-
83
- **Returns:** `Promise<TypedAmqpWorker>`
84
-
85
- **Example:**
86
-
87
- ```typescript
88
- const worker = await TypedAmqpWorker.create({
89
- contract,
90
- handlers: {
91
- // Each handler receives type-checked message
92
- processOrder: async (message) => {
93
- // message.orderId is type-checked
94
- console.log(message.orderId);
95
- },
96
- processPayment: async (message) => {
97
- // Different message type for this consumer
98
- await handlePayment(message);
99
- },
100
- },
101
- connection: {
102
- hostname: 'localhost',
103
- port: 5672,
104
- username: 'guest',
105
- password: 'guest',
106
- },
107
- });
108
- ```
109
-
110
- ### Handler Signature
111
-
112
- ```typescript
113
- type Handler<T> = (message: T) => Promise<void>
114
- ```
115
-
116
- Handlers are simple async functions that:
117
-
118
- - Receive type-checked message as parameter
119
- - Return `Promise<void>`
120
- - Can throw exceptions (message will be requeued)
121
- - Message is acknowledged automatically on success
122
-
123
- ### `TypedAmqpWorker.close()`
124
-
125
- Stop consuming and close the channel and connection.
126
-
127
- **Returns:** `Promise<void>`
77
+ See the [Worker API documentation](https://btravers.github.io/amqp-contract/api/worker) for complete API reference.
128
78
 
129
79
  ## License
130
80
 
package/dist/index.cjs CHANGED
@@ -1,4 +1,5 @@
1
1
  let amqplib = require("amqplib");
2
+ let _amqp_contract_core = require("@amqp-contract/core");
2
3
  let _swan_io_boxed = require("@swan-io/boxed");
3
4
 
4
5
  //#region src/errors.ts
@@ -80,22 +81,7 @@ var TypedAmqpWorker = class TypedAmqpWorker {
80
81
  async init() {
81
82
  this.connection = await (0, amqplib.connect)(this.connectionOptions);
82
83
  this.channel = await this.connection.createChannel();
83
- if (this.contract.exchanges) for (const exchange of Object.values(this.contract.exchanges)) await this.channel.assertExchange(exchange.name, exchange.type, {
84
- durable: exchange.durable,
85
- autoDelete: exchange.autoDelete,
86
- internal: exchange.internal,
87
- arguments: exchange.arguments
88
- });
89
- if (this.contract.queues) for (const queue of Object.values(this.contract.queues)) await this.channel.assertQueue(queue.name, {
90
- durable: queue.durable,
91
- exclusive: queue.exclusive,
92
- autoDelete: queue.autoDelete,
93
- arguments: queue.arguments
94
- });
95
- if (this.contract.bindings) {
96
- for (const binding of Object.values(this.contract.bindings)) if (binding.type === "queue") await this.channel.bindQueue(binding.queue, binding.exchange, binding.routingKey ?? "", binding.arguments);
97
- else if (binding.type === "exchange") await this.channel.bindExchange(binding.destination, binding.source, binding.routingKey ?? "", binding.arguments);
98
- }
84
+ await (0, _amqp_contract_core.setupInfra)(this.channel, this.contract);
99
85
  }
100
86
  /**
101
87
  * Start consuming messages for all consumers
@@ -122,7 +108,7 @@ var TypedAmqpWorker = class TypedAmqpWorker {
122
108
  const handler = this.handlers[consumerName];
123
109
  if (!handler) throw new Error(`Handler for "${String(consumerName)}" not provided`);
124
110
  if (consumerDef.prefetch !== void 0) await this.channel.prefetch(consumerDef.prefetch);
125
- const result = await this.channel.consume(consumerDef.queue, async (msg) => {
111
+ const result = await this.channel.consume(consumerDef.queue.name, async (msg) => {
126
112
  if (!msg) return;
127
113
  const parseResult = _swan_io_boxed.Result.fromExecution(() => JSON.parse(msg.content.toString()));
128
114
  if (parseResult.isError()) {
@@ -131,7 +117,7 @@ var TypedAmqpWorker = class TypedAmqpWorker {
131
117
  return;
132
118
  }
133
119
  const content = parseResult.value;
134
- const rawValidation = consumerDef.message["~standard"].validate(content);
120
+ const rawValidation = consumerDef.message.payload["~standard"].validate(content);
135
121
  const resolvedValidation = rawValidation instanceof Promise ? await rawValidation : rawValidation;
136
122
  const validationResult = typeof resolvedValidation === "object" && resolvedValidation !== null && "issues" in resolvedValidation && resolvedValidation.issues ? _swan_io_boxed.Result.Error(new MessageValidationError(String(consumerName), resolvedValidation.issues)) : _swan_io_boxed.Result.Ok(typeof resolvedValidation === "object" && resolvedValidation !== null && "value" in resolvedValidation ? resolvedValidation.value : content);
137
123
  if (validationResult.isError()) {
@@ -160,7 +146,155 @@ var TypedAmqpWorker = class TypedAmqpWorker {
160
146
  }
161
147
  };
162
148
 
149
+ //#endregion
150
+ //#region src/handlers.ts
151
+ /**
152
+ * Define a type-safe handler for a specific consumer in a contract.
153
+ *
154
+ * This utility allows you to define handlers outside of the worker creation,
155
+ * providing better code organization and reusability.
156
+ *
157
+ * @template TContract - The contract definition type
158
+ * @template TName - The consumer name from the contract
159
+ * @param contract - The contract definition containing the consumer
160
+ * @param consumerName - The name of the consumer from the contract
161
+ * @param handler - The async handler function that processes messages
162
+ * @returns A type-safe handler that can be used with TypedAmqpWorker
163
+ *
164
+ * @example
165
+ * ```typescript
166
+ * import { defineHandler } from '@amqp-contract/worker';
167
+ * import { orderContract } from './contract';
168
+ *
169
+ * // Define handler outside of worker creation
170
+ * const processOrderHandler = defineHandler(
171
+ * orderContract,
172
+ * 'processOrder',
173
+ * async (message) => {
174
+ * // message is fully typed based on the contract
175
+ * console.log('Processing order:', message.orderId);
176
+ * await processPayment(message);
177
+ * }
178
+ * );
179
+ *
180
+ * // Use the handler in worker
181
+ * const worker = await TypedAmqpWorker.create({
182
+ * contract: orderContract,
183
+ * handlers: {
184
+ * processOrder: processOrderHandler,
185
+ * },
186
+ * connection: 'amqp://localhost',
187
+ * });
188
+ * ```
189
+ *
190
+ * @example
191
+ * ```typescript
192
+ * // Define multiple handlers
193
+ * const processOrderHandler = defineHandler(
194
+ * orderContract,
195
+ * 'processOrder',
196
+ * async (message) => {
197
+ * await processOrder(message);
198
+ * }
199
+ * );
200
+ *
201
+ * const notifyOrderHandler = defineHandler(
202
+ * orderContract,
203
+ * 'notifyOrder',
204
+ * async (message) => {
205
+ * await sendNotification(message);
206
+ * }
207
+ * );
208
+ *
209
+ * // Compose handlers
210
+ * const worker = await TypedAmqpWorker.create({
211
+ * contract: orderContract,
212
+ * handlers: {
213
+ * processOrder: processOrderHandler,
214
+ * notifyOrder: notifyOrderHandler,
215
+ * },
216
+ * connection: 'amqp://localhost',
217
+ * });
218
+ * ```
219
+ */
220
+ function defineHandler(contract, consumerName, handler) {
221
+ const consumers = contract.consumers;
222
+ if (!consumers || !(consumerName in consumers)) {
223
+ const availableConsumers = consumers ? Object.keys(consumers) : [];
224
+ const available = availableConsumers.length > 0 ? availableConsumers.join(", ") : "none";
225
+ throw new Error(`Consumer "${String(consumerName)}" not found in contract. Available consumers: ${available}`);
226
+ }
227
+ return handler;
228
+ }
229
+ /**
230
+ * Define multiple type-safe handlers for consumers in a contract.
231
+ *
232
+ * This utility allows you to define all handlers at once outside of the worker creation,
233
+ * ensuring type safety and providing better code organization.
234
+ *
235
+ * @template TContract - The contract definition type
236
+ * @param contract - The contract definition containing the consumers
237
+ * @param handlers - An object with async handler functions for each consumer
238
+ * @returns A type-safe handlers object that can be used with TypedAmqpWorker
239
+ *
240
+ * @example
241
+ * ```typescript
242
+ * import { defineHandlers } from '@amqp-contract/worker';
243
+ * import { orderContract } from './contract';
244
+ *
245
+ * // Define all handlers at once
246
+ * const handlers = defineHandlers(orderContract, {
247
+ * processOrder: async (message) => {
248
+ * // message is fully typed based on the contract
249
+ * console.log('Processing order:', message.orderId);
250
+ * await processPayment(message);
251
+ * },
252
+ * notifyOrder: async (message) => {
253
+ * await sendNotification(message);
254
+ * },
255
+ * shipOrder: async (message) => {
256
+ * await prepareShipment(message);
257
+ * },
258
+ * });
259
+ *
260
+ * // Use the handlers in worker
261
+ * const worker = await TypedAmqpWorker.create({
262
+ * contract: orderContract,
263
+ * handlers,
264
+ * connection: 'amqp://localhost',
265
+ * });
266
+ * ```
267
+ *
268
+ * @example
269
+ * ```typescript
270
+ * // Separate handler definitions for better organization
271
+ * async function handleProcessOrder(message: WorkerInferConsumerInput<typeof orderContract, 'processOrder'>) {
272
+ * await processOrder(message);
273
+ * }
274
+ *
275
+ * async function handleNotifyOrder(message: WorkerInferConsumerInput<typeof orderContract, 'notifyOrder'>) {
276
+ * await sendNotification(message);
277
+ * }
278
+ *
279
+ * const handlers = defineHandlers(orderContract, {
280
+ * processOrder: handleProcessOrder,
281
+ * notifyOrder: handleNotifyOrder,
282
+ * });
283
+ * ```
284
+ */
285
+ function defineHandlers(contract, handlers) {
286
+ const consumers = contract.consumers;
287
+ const availableConsumers = consumers ? Object.keys(consumers) : [];
288
+ for (const handlerName of Object.keys(handlers)) if (!consumers || !(handlerName in consumers)) {
289
+ const available = availableConsumers.length > 0 ? availableConsumers.join(", ") : "none";
290
+ throw new Error(`Consumer "${handlerName}" not found in contract. Available consumers: ${available}`);
291
+ }
292
+ return handlers;
293
+ }
294
+
163
295
  //#endregion
164
296
  exports.MessageValidationError = MessageValidationError;
165
297
  exports.TechnicalError = TechnicalError;
166
- exports.TypedAmqpWorker = TypedAmqpWorker;
298
+ exports.TypedAmqpWorker = TypedAmqpWorker;
299
+ exports.defineHandler = defineHandler;
300
+ exports.defineHandlers = defineHandlers;
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Options } from "amqplib";
2
- import { ContractDefinition, WorkerInferConsumerHandlers } from "@amqp-contract/contract";
2
+ import { ContractDefinition, InferConsumerNames, WorkerInferConsumerHandler, WorkerInferConsumerHandlers } from "@amqp-contract/contract";
3
3
 
4
4
  //#region src/worker.d.ts
5
5
 
@@ -73,5 +73,134 @@ declare class MessageValidationError extends WorkerError {
73
73
  constructor(consumerName: string, issues: unknown);
74
74
  }
75
75
  //#endregion
76
- export { type CreateWorkerOptions, MessageValidationError, TechnicalError, TypedAmqpWorker };
76
+ //#region src/handlers.d.ts
77
+ /**
78
+ * Define a type-safe handler for a specific consumer in a contract.
79
+ *
80
+ * This utility allows you to define handlers outside of the worker creation,
81
+ * providing better code organization and reusability.
82
+ *
83
+ * @template TContract - The contract definition type
84
+ * @template TName - The consumer name from the contract
85
+ * @param contract - The contract definition containing the consumer
86
+ * @param consumerName - The name of the consumer from the contract
87
+ * @param handler - The async handler function that processes messages
88
+ * @returns A type-safe handler that can be used with TypedAmqpWorker
89
+ *
90
+ * @example
91
+ * ```typescript
92
+ * import { defineHandler } from '@amqp-contract/worker';
93
+ * import { orderContract } from './contract';
94
+ *
95
+ * // Define handler outside of worker creation
96
+ * const processOrderHandler = defineHandler(
97
+ * orderContract,
98
+ * 'processOrder',
99
+ * async (message) => {
100
+ * // message is fully typed based on the contract
101
+ * console.log('Processing order:', message.orderId);
102
+ * await processPayment(message);
103
+ * }
104
+ * );
105
+ *
106
+ * // Use the handler in worker
107
+ * const worker = await TypedAmqpWorker.create({
108
+ * contract: orderContract,
109
+ * handlers: {
110
+ * processOrder: processOrderHandler,
111
+ * },
112
+ * connection: 'amqp://localhost',
113
+ * });
114
+ * ```
115
+ *
116
+ * @example
117
+ * ```typescript
118
+ * // Define multiple handlers
119
+ * const processOrderHandler = defineHandler(
120
+ * orderContract,
121
+ * 'processOrder',
122
+ * async (message) => {
123
+ * await processOrder(message);
124
+ * }
125
+ * );
126
+ *
127
+ * const notifyOrderHandler = defineHandler(
128
+ * orderContract,
129
+ * 'notifyOrder',
130
+ * async (message) => {
131
+ * await sendNotification(message);
132
+ * }
133
+ * );
134
+ *
135
+ * // Compose handlers
136
+ * const worker = await TypedAmqpWorker.create({
137
+ * contract: orderContract,
138
+ * handlers: {
139
+ * processOrder: processOrderHandler,
140
+ * notifyOrder: notifyOrderHandler,
141
+ * },
142
+ * connection: 'amqp://localhost',
143
+ * });
144
+ * ```
145
+ */
146
+ declare function defineHandler<TContract extends ContractDefinition, TName extends InferConsumerNames<TContract>>(contract: TContract, consumerName: TName, handler: WorkerInferConsumerHandler<TContract, TName>): WorkerInferConsumerHandler<TContract, TName>;
147
+ /**
148
+ * Define multiple type-safe handlers for consumers in a contract.
149
+ *
150
+ * This utility allows you to define all handlers at once outside of the worker creation,
151
+ * ensuring type safety and providing better code organization.
152
+ *
153
+ * @template TContract - The contract definition type
154
+ * @param contract - The contract definition containing the consumers
155
+ * @param handlers - An object with async handler functions for each consumer
156
+ * @returns A type-safe handlers object that can be used with TypedAmqpWorker
157
+ *
158
+ * @example
159
+ * ```typescript
160
+ * import { defineHandlers } from '@amqp-contract/worker';
161
+ * import { orderContract } from './contract';
162
+ *
163
+ * // Define all handlers at once
164
+ * const handlers = defineHandlers(orderContract, {
165
+ * processOrder: async (message) => {
166
+ * // message is fully typed based on the contract
167
+ * console.log('Processing order:', message.orderId);
168
+ * await processPayment(message);
169
+ * },
170
+ * notifyOrder: async (message) => {
171
+ * await sendNotification(message);
172
+ * },
173
+ * shipOrder: async (message) => {
174
+ * await prepareShipment(message);
175
+ * },
176
+ * });
177
+ *
178
+ * // Use the handlers in worker
179
+ * const worker = await TypedAmqpWorker.create({
180
+ * contract: orderContract,
181
+ * handlers,
182
+ * connection: 'amqp://localhost',
183
+ * });
184
+ * ```
185
+ *
186
+ * @example
187
+ * ```typescript
188
+ * // Separate handler definitions for better organization
189
+ * async function handleProcessOrder(message: WorkerInferConsumerInput<typeof orderContract, 'processOrder'>) {
190
+ * await processOrder(message);
191
+ * }
192
+ *
193
+ * async function handleNotifyOrder(message: WorkerInferConsumerInput<typeof orderContract, 'notifyOrder'>) {
194
+ * await sendNotification(message);
195
+ * }
196
+ *
197
+ * const handlers = defineHandlers(orderContract, {
198
+ * processOrder: handleProcessOrder,
199
+ * notifyOrder: handleNotifyOrder,
200
+ * });
201
+ * ```
202
+ */
203
+ declare function defineHandlers<TContract extends ContractDefinition>(contract: TContract, handlers: WorkerInferConsumerHandlers<TContract>): WorkerInferConsumerHandlers<TContract>;
204
+ //#endregion
205
+ export { type CreateWorkerOptions, MessageValidationError, TechnicalError, TypedAmqpWorker, defineHandler, defineHandlers };
77
206
  //# sourceMappingURL=index.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../src/worker.ts","../src/errors.ts"],"sourcesContent":[],"mappings":";;;;;;;AAcA;AAAuD,UAAtC,mBAAsC,CAAA,kBAAA,kBAAA,CAAA,CAAA;EAC3C,QAAA,EAAA,SAAA;EAC4B,QAAA,EAA5B,2BAA4B,CAAA,SAAA,CAAA;EAA5B,UAAA,EAAA,MAAA,GACW,OAAA,CAAQ,OADnB;;;AAOZ;;AAewC,cAf3B,eAe2B,CAAA,kBAfO,kBAeP,CAAA,CAAA;EACP,iBAAA,QAAA;EAApB,iBAAA,QAAA;EACgB,iBAAA,iBAAA;EAAhB,QAAA,OAAA;EAAR,QAAA,UAAA;EAUY,QAAA,YAAA;EAAO,QAAA,WAAA,CAAA;;;;;EC7BX,OAAA,MAAA,CAAA,kBDiB2B,kBCjBO,CAAA,CAAA,OAAA,EDkBlC,mBClBkC,CDkBd,SClBc,CAAA,CAAA,EDmB1C,OCnB0C,CDmBlC,eCnBkC,CDmBlB,SCnBkB,CAAA,CAAA;EAalC;;;WDgBI;;;;;;;;;;;;;;;;;;;;;;;uBC/CF,WAAA,SAAoB,KAAA;EDWlB,UAAA,WAAA,CAAmB,OAAA,EAAA,MAAA;;;;;;AAGE,cCIzB,cAAA,SAAuB,WAAA,CDJE;EAMzB,SAAA,KAAA,CAAA,EAAA,OAAe,GAAA,SAAA;EAAmB,WAAA,CAAA,OAAA,EAAA,MAAA,EAAA,KAAA,CAAA,EAAA,OAAA,GAAA,SAAA;;;;;AAiBlC,cCNA,sBAAA,SAA+B,WAAA,CDM/B;EAAR,SAAA,YAAA,EAAA,MAAA;EAUY,SAAA,MAAA,EAAA,OAAA;EAAO,WAAA,CAAA,YAAA,EAAA,MAAA,EAAA,MAAA,EAAA,OAAA"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/worker.ts","../src/errors.ts","../src/handlers.ts"],"sourcesContent":[],"mappings":";;;;;;;AAeA;AAAuD,UAAtC,mBAAsC,CAAA,kBAAA,kBAAA,CAAA,CAAA;EAC3C,QAAA,EAAA,SAAA;EAC4B,QAAA,EAA5B,2BAA4B,CAAA,SAAA,CAAA;EAA5B,UAAA,EAAA,MAAA,GACW,OAAA,CAAQ,OADnB;;;AAOZ;;AAewC,cAf3B,eAe2B,CAAA,kBAfO,kBAeP,CAAA,CAAA;EACP,iBAAA,QAAA;EAApB,iBAAA,QAAA;EACgB,iBAAA,iBAAA;EAAhB,QAAA,OAAA;EAAR,QAAA,UAAA;EAUY,QAAA,YAAA;EAAO,QAAA,WAAA,CAAA;;;;;EC9BX,OAAA,MAAA,CAAA,kBDkB2B,kBClBO,CAAA,CAAA,OAAA,EDmBlC,mBCnBkC,CDmBd,SCnBc,CAAA,CAAA,EDoB1C,OCpB0C,CDoBlC,eCpBkC,CDoBlB,SCpBkB,CAAA,CAAA;EAalC;;;WDiBI;EEyBD;;;EAEA,QAAA,IAAA;EAEJ;;;EAEqC,QAAA,UAAA;EAAtC;;;EACR,QAAA,OAAA;EAA0B;AAuE7B;;EACY,QAAA,aAAA;;;;;;;uBDxJG,WAAA,SAAoB,KAAA;EDYlB,UAAA,WAAA,CAAmB,OAAA,EAAA,MAAA;;;;;;AAGE,cCGzB,cAAA,SAAuB,WAAA,CDHE;EAMzB,SAAA,KAAA,CAAA,EAAA,OAAe,GAAA,SAAA;EAAmB,WAAA,CAAA,OAAA,EAAA,MAAA,EAAA,KAAA,CAAA,EAAA,OAAA,GAAA,SAAA;;;;;AAiBlC,cCPA,sBAAA,SAA+B,WAAA,CDO/B;EAAR,SAAA,YAAA,EAAA,MAAA;EAUY,SAAA,MAAA,EAAA,OAAA;EAAO,WAAA,CAAA,YAAA,EAAA,MAAA,EAAA,MAAA,EAAA,OAAA;;;;;;;AApCxB;;;;;;;AASA;;;;;;;;;;;;;;ACHA;AAaA;;;;AC0CA;;;;;;;;;;;;;AA8EA;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA9EgB,gCACI,kCACJ,mBAAmB,sBAEvB,yBACI,gBACL,2BAA2B,WAAW,SAC9C,2BAA2B,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAuEzB,iCAAiC,8BACrC,qBACA,4BAA4B,aACrC,4BAA4B"}
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Options } from "amqplib";
2
- import { ContractDefinition, WorkerInferConsumerHandlers } from "@amqp-contract/contract";
2
+ import { ContractDefinition, InferConsumerNames, WorkerInferConsumerHandler, WorkerInferConsumerHandlers } from "@amqp-contract/contract";
3
3
 
4
4
  //#region src/worker.d.ts
5
5
 
@@ -73,5 +73,134 @@ declare class MessageValidationError extends WorkerError {
73
73
  constructor(consumerName: string, issues: unknown);
74
74
  }
75
75
  //#endregion
76
- export { type CreateWorkerOptions, MessageValidationError, TechnicalError, TypedAmqpWorker };
76
+ //#region src/handlers.d.ts
77
+ /**
78
+ * Define a type-safe handler for a specific consumer in a contract.
79
+ *
80
+ * This utility allows you to define handlers outside of the worker creation,
81
+ * providing better code organization and reusability.
82
+ *
83
+ * @template TContract - The contract definition type
84
+ * @template TName - The consumer name from the contract
85
+ * @param contract - The contract definition containing the consumer
86
+ * @param consumerName - The name of the consumer from the contract
87
+ * @param handler - The async handler function that processes messages
88
+ * @returns A type-safe handler that can be used with TypedAmqpWorker
89
+ *
90
+ * @example
91
+ * ```typescript
92
+ * import { defineHandler } from '@amqp-contract/worker';
93
+ * import { orderContract } from './contract';
94
+ *
95
+ * // Define handler outside of worker creation
96
+ * const processOrderHandler = defineHandler(
97
+ * orderContract,
98
+ * 'processOrder',
99
+ * async (message) => {
100
+ * // message is fully typed based on the contract
101
+ * console.log('Processing order:', message.orderId);
102
+ * await processPayment(message);
103
+ * }
104
+ * );
105
+ *
106
+ * // Use the handler in worker
107
+ * const worker = await TypedAmqpWorker.create({
108
+ * contract: orderContract,
109
+ * handlers: {
110
+ * processOrder: processOrderHandler,
111
+ * },
112
+ * connection: 'amqp://localhost',
113
+ * });
114
+ * ```
115
+ *
116
+ * @example
117
+ * ```typescript
118
+ * // Define multiple handlers
119
+ * const processOrderHandler = defineHandler(
120
+ * orderContract,
121
+ * 'processOrder',
122
+ * async (message) => {
123
+ * await processOrder(message);
124
+ * }
125
+ * );
126
+ *
127
+ * const notifyOrderHandler = defineHandler(
128
+ * orderContract,
129
+ * 'notifyOrder',
130
+ * async (message) => {
131
+ * await sendNotification(message);
132
+ * }
133
+ * );
134
+ *
135
+ * // Compose handlers
136
+ * const worker = await TypedAmqpWorker.create({
137
+ * contract: orderContract,
138
+ * handlers: {
139
+ * processOrder: processOrderHandler,
140
+ * notifyOrder: notifyOrderHandler,
141
+ * },
142
+ * connection: 'amqp://localhost',
143
+ * });
144
+ * ```
145
+ */
146
+ declare function defineHandler<TContract extends ContractDefinition, TName extends InferConsumerNames<TContract>>(contract: TContract, consumerName: TName, handler: WorkerInferConsumerHandler<TContract, TName>): WorkerInferConsumerHandler<TContract, TName>;
147
+ /**
148
+ * Define multiple type-safe handlers for consumers in a contract.
149
+ *
150
+ * This utility allows you to define all handlers at once outside of the worker creation,
151
+ * ensuring type safety and providing better code organization.
152
+ *
153
+ * @template TContract - The contract definition type
154
+ * @param contract - The contract definition containing the consumers
155
+ * @param handlers - An object with async handler functions for each consumer
156
+ * @returns A type-safe handlers object that can be used with TypedAmqpWorker
157
+ *
158
+ * @example
159
+ * ```typescript
160
+ * import { defineHandlers } from '@amqp-contract/worker';
161
+ * import { orderContract } from './contract';
162
+ *
163
+ * // Define all handlers at once
164
+ * const handlers = defineHandlers(orderContract, {
165
+ * processOrder: async (message) => {
166
+ * // message is fully typed based on the contract
167
+ * console.log('Processing order:', message.orderId);
168
+ * await processPayment(message);
169
+ * },
170
+ * notifyOrder: async (message) => {
171
+ * await sendNotification(message);
172
+ * },
173
+ * shipOrder: async (message) => {
174
+ * await prepareShipment(message);
175
+ * },
176
+ * });
177
+ *
178
+ * // Use the handlers in worker
179
+ * const worker = await TypedAmqpWorker.create({
180
+ * contract: orderContract,
181
+ * handlers,
182
+ * connection: 'amqp://localhost',
183
+ * });
184
+ * ```
185
+ *
186
+ * @example
187
+ * ```typescript
188
+ * // Separate handler definitions for better organization
189
+ * async function handleProcessOrder(message: WorkerInferConsumerInput<typeof orderContract, 'processOrder'>) {
190
+ * await processOrder(message);
191
+ * }
192
+ *
193
+ * async function handleNotifyOrder(message: WorkerInferConsumerInput<typeof orderContract, 'notifyOrder'>) {
194
+ * await sendNotification(message);
195
+ * }
196
+ *
197
+ * const handlers = defineHandlers(orderContract, {
198
+ * processOrder: handleProcessOrder,
199
+ * notifyOrder: handleNotifyOrder,
200
+ * });
201
+ * ```
202
+ */
203
+ declare function defineHandlers<TContract extends ContractDefinition>(contract: TContract, handlers: WorkerInferConsumerHandlers<TContract>): WorkerInferConsumerHandlers<TContract>;
204
+ //#endregion
205
+ export { type CreateWorkerOptions, MessageValidationError, TechnicalError, TypedAmqpWorker, defineHandler, defineHandlers };
77
206
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/worker.ts","../src/errors.ts"],"sourcesContent":[],"mappings":";;;;;;;AAcA;AAAuD,UAAtC,mBAAsC,CAAA,kBAAA,kBAAA,CAAA,CAAA;EAC3C,QAAA,EAAA,SAAA;EAC4B,QAAA,EAA5B,2BAA4B,CAAA,SAAA,CAAA;EAA5B,UAAA,EAAA,MAAA,GACW,OAAA,CAAQ,OADnB;;;AAOZ;;AAewC,cAf3B,eAe2B,CAAA,kBAfO,kBAeP,CAAA,CAAA;EACP,iBAAA,QAAA;EAApB,iBAAA,QAAA;EACgB,iBAAA,iBAAA;EAAhB,QAAA,OAAA;EAAR,QAAA,UAAA;EAUY,QAAA,YAAA;EAAO,QAAA,WAAA,CAAA;;;;;EC7BX,OAAA,MAAA,CAAA,kBDiB2B,kBCjBO,CAAA,CAAA,OAAA,EDkBlC,mBClBkC,CDkBd,SClBc,CAAA,CAAA,EDmB1C,OCnB0C,CDmBlC,eCnBkC,CDmBlB,SCnBkB,CAAA,CAAA;EAalC;;;WDgBI;;;;;;;;;;;;;;;;;;;;;;;uBC/CF,WAAA,SAAoB,KAAA;EDWlB,UAAA,WAAA,CAAmB,OAAA,EAAA,MAAA;;;;;;AAGE,cCIzB,cAAA,SAAuB,WAAA,CDJE;EAMzB,SAAA,KAAA,CAAA,EAAA,OAAe,GAAA,SAAA;EAAmB,WAAA,CAAA,OAAA,EAAA,MAAA,EAAA,KAAA,CAAA,EAAA,OAAA,GAAA,SAAA;;;;;AAiBlC,cCNA,sBAAA,SAA+B,WAAA,CDM/B;EAAR,SAAA,YAAA,EAAA,MAAA;EAUY,SAAA,MAAA,EAAA,OAAA;EAAO,WAAA,CAAA,YAAA,EAAA,MAAA,EAAA,MAAA,EAAA,OAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/worker.ts","../src/errors.ts","../src/handlers.ts"],"sourcesContent":[],"mappings":";;;;;;;AAeA;AAAuD,UAAtC,mBAAsC,CAAA,kBAAA,kBAAA,CAAA,CAAA;EAC3C,QAAA,EAAA,SAAA;EAC4B,QAAA,EAA5B,2BAA4B,CAAA,SAAA,CAAA;EAA5B,UAAA,EAAA,MAAA,GACW,OAAA,CAAQ,OADnB;;;AAOZ;;AAewC,cAf3B,eAe2B,CAAA,kBAfO,kBAeP,CAAA,CAAA;EACP,iBAAA,QAAA;EAApB,iBAAA,QAAA;EACgB,iBAAA,iBAAA;EAAhB,QAAA,OAAA;EAAR,QAAA,UAAA;EAUY,QAAA,YAAA;EAAO,QAAA,WAAA,CAAA;;;;;EC9BX,OAAA,MAAA,CAAA,kBDkB2B,kBClBO,CAAA,CAAA,OAAA,EDmBlC,mBCnBkC,CDmBd,SCnBc,CAAA,CAAA,EDoB1C,OCpB0C,CDoBlC,eCpBkC,CDoBlB,SCpBkB,CAAA,CAAA;EAalC;;;WDiBI;EEyBD;;;EAEA,QAAA,IAAA;EAEJ;;;EAEqC,QAAA,UAAA;EAAtC;;;EACR,QAAA,OAAA;EAA0B;AAuE7B;;EACY,QAAA,aAAA;;;;;;;uBDxJG,WAAA,SAAoB,KAAA;EDYlB,UAAA,WAAA,CAAmB,OAAA,EAAA,MAAA;;;;;;AAGE,cCGzB,cAAA,SAAuB,WAAA,CDHE;EAMzB,SAAA,KAAA,CAAA,EAAA,OAAe,GAAA,SAAA;EAAmB,WAAA,CAAA,OAAA,EAAA,MAAA,EAAA,KAAA,CAAA,EAAA,OAAA,GAAA,SAAA;;;;;AAiBlC,cCPA,sBAAA,SAA+B,WAAA,CDO/B;EAAR,SAAA,YAAA,EAAA,MAAA;EAUY,SAAA,MAAA,EAAA,OAAA;EAAO,WAAA,CAAA,YAAA,EAAA,MAAA,EAAA,MAAA,EAAA,OAAA;;;;;;;AApCxB;;;;;;;AASA;;;;;;;;;;;;;;ACHA;AAaA;;;;AC0CA;;;;;;;;;;;;;AA8EA;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA9EgB,gCACI,kCACJ,mBAAmB,sBAEvB,yBACI,gBACL,2BAA2B,WAAW,SAC9C,2BAA2B,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAuEzB,iCAAiC,8BACrC,qBACA,4BAA4B,aACrC,4BAA4B"}
package/dist/index.mjs CHANGED
@@ -1,4 +1,5 @@
1
1
  import { connect } from "amqplib";
2
+ import { setupInfra } from "@amqp-contract/core";
2
3
  import { Result } from "@swan-io/boxed";
3
4
 
4
5
  //#region src/errors.ts
@@ -80,22 +81,7 @@ var TypedAmqpWorker = class TypedAmqpWorker {
80
81
  async init() {
81
82
  this.connection = await connect(this.connectionOptions);
82
83
  this.channel = await this.connection.createChannel();
83
- if (this.contract.exchanges) for (const exchange of Object.values(this.contract.exchanges)) await this.channel.assertExchange(exchange.name, exchange.type, {
84
- durable: exchange.durable,
85
- autoDelete: exchange.autoDelete,
86
- internal: exchange.internal,
87
- arguments: exchange.arguments
88
- });
89
- if (this.contract.queues) for (const queue of Object.values(this.contract.queues)) await this.channel.assertQueue(queue.name, {
90
- durable: queue.durable,
91
- exclusive: queue.exclusive,
92
- autoDelete: queue.autoDelete,
93
- arguments: queue.arguments
94
- });
95
- if (this.contract.bindings) {
96
- for (const binding of Object.values(this.contract.bindings)) if (binding.type === "queue") await this.channel.bindQueue(binding.queue, binding.exchange, binding.routingKey ?? "", binding.arguments);
97
- else if (binding.type === "exchange") await this.channel.bindExchange(binding.destination, binding.source, binding.routingKey ?? "", binding.arguments);
98
- }
84
+ await setupInfra(this.channel, this.contract);
99
85
  }
100
86
  /**
101
87
  * Start consuming messages for all consumers
@@ -122,7 +108,7 @@ var TypedAmqpWorker = class TypedAmqpWorker {
122
108
  const handler = this.handlers[consumerName];
123
109
  if (!handler) throw new Error(`Handler for "${String(consumerName)}" not provided`);
124
110
  if (consumerDef.prefetch !== void 0) await this.channel.prefetch(consumerDef.prefetch);
125
- const result = await this.channel.consume(consumerDef.queue, async (msg) => {
111
+ const result = await this.channel.consume(consumerDef.queue.name, async (msg) => {
126
112
  if (!msg) return;
127
113
  const parseResult = Result.fromExecution(() => JSON.parse(msg.content.toString()));
128
114
  if (parseResult.isError()) {
@@ -131,7 +117,7 @@ var TypedAmqpWorker = class TypedAmqpWorker {
131
117
  return;
132
118
  }
133
119
  const content = parseResult.value;
134
- const rawValidation = consumerDef.message["~standard"].validate(content);
120
+ const rawValidation = consumerDef.message.payload["~standard"].validate(content);
135
121
  const resolvedValidation = rawValidation instanceof Promise ? await rawValidation : rawValidation;
136
122
  const validationResult = typeof resolvedValidation === "object" && resolvedValidation !== null && "issues" in resolvedValidation && resolvedValidation.issues ? Result.Error(new MessageValidationError(String(consumerName), resolvedValidation.issues)) : Result.Ok(typeof resolvedValidation === "object" && resolvedValidation !== null && "value" in resolvedValidation ? resolvedValidation.value : content);
137
123
  if (validationResult.isError()) {
@@ -161,5 +147,151 @@ var TypedAmqpWorker = class TypedAmqpWorker {
161
147
  };
162
148
 
163
149
  //#endregion
164
- export { MessageValidationError, TechnicalError, TypedAmqpWorker };
150
+ //#region src/handlers.ts
151
+ /**
152
+ * Define a type-safe handler for a specific consumer in a contract.
153
+ *
154
+ * This utility allows you to define handlers outside of the worker creation,
155
+ * providing better code organization and reusability.
156
+ *
157
+ * @template TContract - The contract definition type
158
+ * @template TName - The consumer name from the contract
159
+ * @param contract - The contract definition containing the consumer
160
+ * @param consumerName - The name of the consumer from the contract
161
+ * @param handler - The async handler function that processes messages
162
+ * @returns A type-safe handler that can be used with TypedAmqpWorker
163
+ *
164
+ * @example
165
+ * ```typescript
166
+ * import { defineHandler } from '@amqp-contract/worker';
167
+ * import { orderContract } from './contract';
168
+ *
169
+ * // Define handler outside of worker creation
170
+ * const processOrderHandler = defineHandler(
171
+ * orderContract,
172
+ * 'processOrder',
173
+ * async (message) => {
174
+ * // message is fully typed based on the contract
175
+ * console.log('Processing order:', message.orderId);
176
+ * await processPayment(message);
177
+ * }
178
+ * );
179
+ *
180
+ * // Use the handler in worker
181
+ * const worker = await TypedAmqpWorker.create({
182
+ * contract: orderContract,
183
+ * handlers: {
184
+ * processOrder: processOrderHandler,
185
+ * },
186
+ * connection: 'amqp://localhost',
187
+ * });
188
+ * ```
189
+ *
190
+ * @example
191
+ * ```typescript
192
+ * // Define multiple handlers
193
+ * const processOrderHandler = defineHandler(
194
+ * orderContract,
195
+ * 'processOrder',
196
+ * async (message) => {
197
+ * await processOrder(message);
198
+ * }
199
+ * );
200
+ *
201
+ * const notifyOrderHandler = defineHandler(
202
+ * orderContract,
203
+ * 'notifyOrder',
204
+ * async (message) => {
205
+ * await sendNotification(message);
206
+ * }
207
+ * );
208
+ *
209
+ * // Compose handlers
210
+ * const worker = await TypedAmqpWorker.create({
211
+ * contract: orderContract,
212
+ * handlers: {
213
+ * processOrder: processOrderHandler,
214
+ * notifyOrder: notifyOrderHandler,
215
+ * },
216
+ * connection: 'amqp://localhost',
217
+ * });
218
+ * ```
219
+ */
220
+ function defineHandler(contract, consumerName, handler) {
221
+ const consumers = contract.consumers;
222
+ if (!consumers || !(consumerName in consumers)) {
223
+ const availableConsumers = consumers ? Object.keys(consumers) : [];
224
+ const available = availableConsumers.length > 0 ? availableConsumers.join(", ") : "none";
225
+ throw new Error(`Consumer "${String(consumerName)}" not found in contract. Available consumers: ${available}`);
226
+ }
227
+ return handler;
228
+ }
229
+ /**
230
+ * Define multiple type-safe handlers for consumers in a contract.
231
+ *
232
+ * This utility allows you to define all handlers at once outside of the worker creation,
233
+ * ensuring type safety and providing better code organization.
234
+ *
235
+ * @template TContract - The contract definition type
236
+ * @param contract - The contract definition containing the consumers
237
+ * @param handlers - An object with async handler functions for each consumer
238
+ * @returns A type-safe handlers object that can be used with TypedAmqpWorker
239
+ *
240
+ * @example
241
+ * ```typescript
242
+ * import { defineHandlers } from '@amqp-contract/worker';
243
+ * import { orderContract } from './contract';
244
+ *
245
+ * // Define all handlers at once
246
+ * const handlers = defineHandlers(orderContract, {
247
+ * processOrder: async (message) => {
248
+ * // message is fully typed based on the contract
249
+ * console.log('Processing order:', message.orderId);
250
+ * await processPayment(message);
251
+ * },
252
+ * notifyOrder: async (message) => {
253
+ * await sendNotification(message);
254
+ * },
255
+ * shipOrder: async (message) => {
256
+ * await prepareShipment(message);
257
+ * },
258
+ * });
259
+ *
260
+ * // Use the handlers in worker
261
+ * const worker = await TypedAmqpWorker.create({
262
+ * contract: orderContract,
263
+ * handlers,
264
+ * connection: 'amqp://localhost',
265
+ * });
266
+ * ```
267
+ *
268
+ * @example
269
+ * ```typescript
270
+ * // Separate handler definitions for better organization
271
+ * async function handleProcessOrder(message: WorkerInferConsumerInput<typeof orderContract, 'processOrder'>) {
272
+ * await processOrder(message);
273
+ * }
274
+ *
275
+ * async function handleNotifyOrder(message: WorkerInferConsumerInput<typeof orderContract, 'notifyOrder'>) {
276
+ * await sendNotification(message);
277
+ * }
278
+ *
279
+ * const handlers = defineHandlers(orderContract, {
280
+ * processOrder: handleProcessOrder,
281
+ * notifyOrder: handleNotifyOrder,
282
+ * });
283
+ * ```
284
+ */
285
+ function defineHandlers(contract, handlers) {
286
+ const consumers = contract.consumers;
287
+ const availableConsumers = consumers ? Object.keys(consumers) : [];
288
+ for (const handlerName of Object.keys(handlers)) if (!consumers || !(handlerName in consumers)) {
289
+ const available = availableConsumers.length > 0 ? availableConsumers.join(", ") : "none";
290
+ throw new Error(`Consumer "${handlerName}" not found in contract. Available consumers: ${available}`);
291
+ }
292
+ return handlers;
293
+ }
294
+
295
+ //#endregion
296
+ export { MessageValidationError, TechnicalError, TypedAmqpWorker, defineHandler, defineHandlers };
165
297
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["cause?: unknown","consumerName: string","issues: unknown","contract: TContract","handlers: WorkerInferConsumerHandlers<TContract>","connectionOptions: string | Options.Connect","validationResult: Result<unknown, MessageValidationError>"],"sources":["../src/errors.ts","../src/worker.ts"],"sourcesContent":["/**\n * Base error class for worker errors\n */\nabstract class WorkerError extends Error {\n protected constructor(message: string) {\n super(message);\n this.name = \"WorkerError\";\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 * Error for technical/runtime failures in worker operations\n * This includes validation failures, parsing failures, and processing failures\n */\nexport class TechnicalError extends WorkerError {\n constructor(\n message: string,\n public override readonly cause?: unknown,\n ) {\n super(message);\n this.name = \"TechnicalError\";\n }\n}\n\n/**\n * Error thrown when message validation fails\n */\nexport class MessageValidationError extends WorkerError {\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 }\n}\n","import { connect } from \"amqplib\";\nimport type { Channel, ChannelModel, ConsumeMessage, Options } from \"amqplib\";\nimport type {\n ContractDefinition,\n InferConsumerNames,\n WorkerInferConsumerHandlers,\n WorkerInferConsumerInput,\n} from \"@amqp-contract/contract\";\nimport { Result } from \"@swan-io/boxed\";\nimport { MessageValidationError, TechnicalError } from \"./errors.js\";\n\n/**\n * Options for creating a worker\n */\nexport interface CreateWorkerOptions<TContract extends ContractDefinition> {\n contract: TContract;\n handlers: WorkerInferConsumerHandlers<TContract>;\n connection: string | Options.Connect;\n}\n\n/**\n * Type-safe AMQP worker for consuming messages\n */\nexport class TypedAmqpWorker<TContract extends ContractDefinition> {\n private channel: Channel | null = null;\n private connection: ChannelModel | null = null;\n private consumerTags: string[] = [];\n\n private constructor(\n private readonly contract: TContract,\n private readonly handlers: WorkerInferConsumerHandlers<TContract>,\n private readonly connectionOptions: string | Options.Connect,\n ) {}\n\n /**\n * Create a type-safe AMQP worker from a contract\n * The worker will automatically connect and start consuming all messages\n */\n static async create<TContract extends ContractDefinition>(\n options: CreateWorkerOptions<TContract>,\n ): Promise<TypedAmqpWorker<TContract>> {\n const worker = new TypedAmqpWorker(options.contract, options.handlers, options.connection);\n await worker.init();\n await worker.consumeAll();\n return worker;\n }\n\n /**\n * Close the connection\n */\n async close(): Promise<void> {\n await this.stopConsuming();\n\n if (this.channel) {\n await this.channel.close();\n this.channel = null;\n }\n\n if (this.connection) {\n await this.connection.close();\n this.connection = null;\n }\n }\n\n /**\n * Connect to AMQP broker\n */\n private async init(): Promise<void> {\n this.connection = await connect(this.connectionOptions);\n this.channel = await this.connection.createChannel();\n\n // Setup exchanges\n if (this.contract.exchanges) {\n for (const exchange of Object.values(this.contract.exchanges)) {\n await this.channel.assertExchange(exchange.name, exchange.type, {\n durable: exchange.durable,\n autoDelete: exchange.autoDelete,\n internal: exchange.internal,\n arguments: exchange.arguments,\n });\n }\n }\n\n // Setup queues\n if (this.contract.queues) {\n for (const queue of Object.values(this.contract.queues)) {\n await this.channel.assertQueue(queue.name, {\n durable: queue.durable,\n exclusive: queue.exclusive,\n autoDelete: queue.autoDelete,\n arguments: queue.arguments,\n });\n }\n }\n\n // Setup bindings\n if (this.contract.bindings) {\n for (const binding of Object.values(this.contract.bindings)) {\n if (binding.type === \"queue\") {\n await this.channel.bindQueue(\n binding.queue,\n binding.exchange,\n binding.routingKey ?? \"\",\n binding.arguments,\n );\n } else if (binding.type === \"exchange\") {\n await this.channel.bindExchange(\n binding.destination,\n binding.source,\n binding.routingKey ?? \"\",\n binding.arguments,\n );\n }\n }\n }\n }\n\n /**\n * Start consuming messages for all consumers\n */\n private async consumeAll(): Promise<void> {\n if (!this.contract.consumers) {\n throw new Error(\"No consumers defined in contract\");\n }\n\n const consumerNames = Object.keys(this.contract.consumers) as InferConsumerNames<TContract>[];\n\n for (const consumerName of consumerNames) {\n await this.consume(consumerName);\n }\n }\n\n /**\n * Start consuming messages for a specific consumer\n */\n private async consume<TName extends InferConsumerNames<TContract>>(\n consumerName: TName,\n ): Promise<void> {\n if (!this.channel) {\n throw new Error(\n \"Worker not initialized. Use TypedAmqpWorker.create() to obtain an initialized worker instance.\",\n );\n }\n\n const consumers = this.contract.consumers as Record<string, unknown>;\n if (!consumers) {\n throw new Error(\"No consumers defined in contract\");\n }\n\n const consumer = consumers[consumerName as string];\n if (!consumer || typeof consumer !== \"object\") {\n const availableConsumers = Object.keys(consumers);\n const available = availableConsumers.length > 0 ? availableConsumers.join(\", \") : \"none\";\n throw new Error(\n `Consumer not found: \"${String(consumerName)}\". Available consumers: ${available}`,\n );\n }\n\n const consumerDef = consumer as {\n queue: string;\n message: { \"~standard\": { validate: (value: unknown) => unknown } };\n prefetch?: number;\n noAck?: boolean;\n };\n\n const handler = this.handlers[consumerName];\n if (!handler) {\n throw new Error(`Handler for \"${String(consumerName)}\" not provided`);\n }\n\n // Set prefetch if specified\n if (consumerDef.prefetch !== undefined) {\n await this.channel.prefetch(consumerDef.prefetch);\n }\n\n // Start consuming\n const result = await this.channel.consume(\n consumerDef.queue,\n async (msg: ConsumeMessage | null) => {\n if (!msg) {\n return;\n }\n\n // Parse message\n const parseResult = Result.fromExecution(() => JSON.parse(msg.content.toString()));\n\n if (parseResult.isError()) {\n console.error(\n new TechnicalError(\n `Error parsing message for consumer \"${String(consumerName)}\"`,\n parseResult.error,\n ),\n );\n // Reject message with no requeue (malformed JSON)\n this.channel?.nack(msg, false, false);\n return;\n }\n\n const content = parseResult.value;\n\n // Validate message using schema (supports sync and async validators)\n const rawValidation = consumerDef.message[\"~standard\"].validate(content);\n const resolvedValidation =\n rawValidation instanceof Promise ? await rawValidation : rawValidation;\n const validationResult: Result<unknown, MessageValidationError> =\n typeof resolvedValidation === \"object\" &&\n resolvedValidation !== null &&\n \"issues\" in resolvedValidation &&\n resolvedValidation.issues\n ? Result.Error(\n new MessageValidationError(String(consumerName), resolvedValidation.issues),\n )\n : Result.Ok(\n typeof resolvedValidation === \"object\" &&\n resolvedValidation !== null &&\n \"value\" in resolvedValidation\n ? resolvedValidation.value\n : content,\n );\n\n if (validationResult.isError()) {\n console.error(validationResult.error);\n // Reject message with no requeue (validation failed)\n this.channel?.nack(msg, false, false);\n return;\n }\n\n const validatedMessage = validationResult.value as WorkerInferConsumerInput<\n TContract,\n TName\n >;\n\n // Call handler and wait for Promise to resolve\n try {\n await handler(validatedMessage);\n\n // Acknowledge message if not in noAck mode\n if (!consumerDef.noAck) {\n this.channel?.ack(msg);\n }\n } catch (error) {\n console.error(\n new TechnicalError(\n `Error processing message for consumer \"${String(consumerName)}\"`,\n error,\n ),\n );\n // Reject message and requeue (handler failed)\n this.channel?.nack(msg, false, true);\n }\n },\n {\n noAck: consumerDef.noAck ?? false,\n },\n );\n\n this.consumerTags.push(result.consumerTag);\n }\n\n /**\n * Stop consuming messages\n */\n private async stopConsuming(): Promise<void> {\n if (!this.channel) {\n return;\n }\n\n for (const tag of this.consumerTags) {\n await this.channel.cancel(tag);\n }\n\n this.consumerTags = [];\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["cause?: unknown","consumerName: string","issues: unknown","contract: TContract","handlers: WorkerInferConsumerHandlers<TContract>","connectionOptions: string | Options.Connect","validationResult: Result<unknown, MessageValidationError>"],"sources":["../src/errors.ts","../src/worker.ts","../src/handlers.ts"],"sourcesContent":["/**\n * Base error class for worker errors\n */\nabstract class WorkerError extends Error {\n protected constructor(message: string) {\n super(message);\n this.name = \"WorkerError\";\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 * Error for technical/runtime failures in worker operations\n * This includes validation failures, parsing failures, and processing failures\n */\nexport class TechnicalError extends WorkerError {\n constructor(\n message: string,\n public override readonly cause?: unknown,\n ) {\n super(message);\n this.name = \"TechnicalError\";\n }\n}\n\n/**\n * Error thrown when message validation fails\n */\nexport class MessageValidationError extends WorkerError {\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 }\n}\n","import { connect } from \"amqplib\";\nimport type { Channel, ChannelModel, ConsumeMessage, Options } from \"amqplib\";\nimport type {\n ContractDefinition,\n InferConsumerNames,\n WorkerInferConsumerHandlers,\n WorkerInferConsumerInput,\n} from \"@amqp-contract/contract\";\nimport { setupInfra } from \"@amqp-contract/core\";\nimport { Result } from \"@swan-io/boxed\";\nimport { MessageValidationError, TechnicalError } from \"./errors.js\";\n\n/**\n * Options for creating a worker\n */\nexport interface CreateWorkerOptions<TContract extends ContractDefinition> {\n contract: TContract;\n handlers: WorkerInferConsumerHandlers<TContract>;\n connection: string | Options.Connect;\n}\n\n/**\n * Type-safe AMQP worker for consuming messages\n */\nexport class TypedAmqpWorker<TContract extends ContractDefinition> {\n private channel: Channel | null = null;\n private connection: ChannelModel | null = null;\n private consumerTags: string[] = [];\n\n private constructor(\n private readonly contract: TContract,\n private readonly handlers: WorkerInferConsumerHandlers<TContract>,\n private readonly connectionOptions: string | Options.Connect,\n ) {}\n\n /**\n * Create a type-safe AMQP worker from a contract\n * The worker will automatically connect and start consuming all messages\n */\n static async create<TContract extends ContractDefinition>(\n options: CreateWorkerOptions<TContract>,\n ): Promise<TypedAmqpWorker<TContract>> {\n const worker = new TypedAmqpWorker(options.contract, options.handlers, options.connection);\n await worker.init();\n await worker.consumeAll();\n return worker;\n }\n\n /**\n * Close the connection\n */\n async close(): Promise<void> {\n await this.stopConsuming();\n\n if (this.channel) {\n await this.channel.close();\n this.channel = null;\n }\n\n if (this.connection) {\n await this.connection.close();\n this.connection = null;\n }\n }\n\n /**\n * Connect to AMQP broker\n */\n private async init(): Promise<void> {\n this.connection = await connect(this.connectionOptions);\n this.channel = await this.connection.createChannel();\n\n // Setup exchanges, queues, and bindings\n await setupInfra(this.channel, this.contract);\n }\n\n /**\n * Start consuming messages for all consumers\n */\n private async consumeAll(): Promise<void> {\n if (!this.contract.consumers) {\n throw new Error(\"No consumers defined in contract\");\n }\n\n const consumerNames = Object.keys(this.contract.consumers) as InferConsumerNames<TContract>[];\n\n for (const consumerName of consumerNames) {\n await this.consume(consumerName);\n }\n }\n\n /**\n * Start consuming messages for a specific consumer\n */\n private async consume<TName extends InferConsumerNames<TContract>>(\n consumerName: TName,\n ): Promise<void> {\n if (!this.channel) {\n throw new Error(\n \"Worker not initialized. Use TypedAmqpWorker.create() to obtain an initialized worker instance.\",\n );\n }\n\n const consumers = this.contract.consumers as Record<string, unknown>;\n if (!consumers) {\n throw new Error(\"No consumers defined in contract\");\n }\n\n const consumer = consumers[consumerName as string];\n if (!consumer || typeof consumer !== \"object\") {\n const availableConsumers = Object.keys(consumers);\n const available = availableConsumers.length > 0 ? availableConsumers.join(\", \") : \"none\";\n throw new Error(\n `Consumer not found: \"${String(consumerName)}\". Available consumers: ${available}`,\n );\n }\n\n const consumerDef = consumer as {\n queue: { name: string };\n message: { payload: { \"~standard\": { validate: (value: unknown) => unknown } } };\n prefetch?: number;\n noAck?: boolean;\n };\n\n const handler = this.handlers[consumerName];\n if (!handler) {\n throw new Error(`Handler for \"${String(consumerName)}\" not provided`);\n }\n\n // Set prefetch if specified\n if (consumerDef.prefetch !== undefined) {\n await this.channel.prefetch(consumerDef.prefetch);\n }\n\n // Start consuming\n const result = await this.channel.consume(\n consumerDef.queue.name,\n async (msg: ConsumeMessage | null) => {\n if (!msg) {\n return;\n }\n\n // Parse message\n const parseResult = Result.fromExecution(() => JSON.parse(msg.content.toString()));\n\n if (parseResult.isError()) {\n console.error(\n new TechnicalError(\n `Error parsing message for consumer \"${String(consumerName)}\"`,\n parseResult.error,\n ),\n );\n // Reject message with no requeue (malformed JSON)\n this.channel?.nack(msg, false, false);\n return;\n }\n\n const content = parseResult.value;\n\n // Validate message using schema (supports sync and async validators)\n const rawValidation = consumerDef.message.payload[\"~standard\"].validate(content);\n const resolvedValidation =\n rawValidation instanceof Promise ? await rawValidation : rawValidation;\n const validationResult: Result<unknown, MessageValidationError> =\n typeof resolvedValidation === \"object\" &&\n resolvedValidation !== null &&\n \"issues\" in resolvedValidation &&\n resolvedValidation.issues\n ? Result.Error(\n new MessageValidationError(String(consumerName), resolvedValidation.issues),\n )\n : Result.Ok(\n typeof resolvedValidation === \"object\" &&\n resolvedValidation !== null &&\n \"value\" in resolvedValidation\n ? resolvedValidation.value\n : content,\n );\n\n if (validationResult.isError()) {\n console.error(validationResult.error);\n // Reject message with no requeue (validation failed)\n this.channel?.nack(msg, false, false);\n return;\n }\n\n const validatedMessage = validationResult.value as WorkerInferConsumerInput<\n TContract,\n TName\n >;\n\n // Call handler and wait for Promise to resolve\n try {\n await handler(validatedMessage);\n\n // Acknowledge message if not in noAck mode\n if (!consumerDef.noAck) {\n this.channel?.ack(msg);\n }\n } catch (error) {\n console.error(\n new TechnicalError(\n `Error processing message for consumer \"${String(consumerName)}\"`,\n error,\n ),\n );\n // Reject message and requeue (handler failed)\n this.channel?.nack(msg, false, true);\n }\n },\n {\n noAck: consumerDef.noAck ?? false,\n },\n );\n\n this.consumerTags.push(result.consumerTag);\n }\n\n /**\n * Stop consuming messages\n */\n private async stopConsuming(): Promise<void> {\n if (!this.channel) {\n return;\n }\n\n for (const tag of this.consumerTags) {\n await this.channel.cancel(tag);\n }\n\n this.consumerTags = [];\n }\n}\n","import type {\n ContractDefinition,\n InferConsumerNames,\n WorkerInferConsumerHandler,\n WorkerInferConsumerHandlers,\n} from \"@amqp-contract/contract\";\n\n/**\n * Define a type-safe handler for a specific consumer in a contract.\n *\n * This utility allows you to define handlers outside of the worker creation,\n * providing better code organization and reusability.\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 async handler function that processes messages\n * @returns A type-safe handler that can be used with TypedAmqpWorker\n *\n * @example\n * ```typescript\n * import { defineHandler } from '@amqp-contract/worker';\n * import { orderContract } from './contract';\n *\n * // Define handler outside of worker creation\n * const processOrderHandler = defineHandler(\n * orderContract,\n * 'processOrder',\n * async (message) => {\n * // message is fully typed based on the contract\n * console.log('Processing order:', message.orderId);\n * await processPayment(message);\n * }\n * );\n *\n * // Use the handler in worker\n * const worker = await TypedAmqpWorker.create({\n * contract: orderContract,\n * handlers: {\n * processOrder: processOrderHandler,\n * },\n * connection: 'amqp://localhost',\n * });\n * ```\n *\n * @example\n * ```typescript\n * // Define multiple handlers\n * const processOrderHandler = defineHandler(\n * orderContract,\n * 'processOrder',\n * async (message) => {\n * await processOrder(message);\n * }\n * );\n *\n * const notifyOrderHandler = defineHandler(\n * orderContract,\n * 'notifyOrder',\n * async (message) => {\n * await sendNotification(message);\n * }\n * );\n *\n * // Compose handlers\n * const worker = await TypedAmqpWorker.create({\n * contract: orderContract,\n * handlers: {\n * processOrder: processOrderHandler,\n * notifyOrder: notifyOrderHandler,\n * },\n * connection: 'amqp://localhost',\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): WorkerInferConsumerHandler<TContract, TName> {\n // Validate that the consumer exists in the contract\n const consumers = contract.consumers as Record<string, unknown> | undefined;\n if (!consumers || !((consumerName as string) in consumers)) {\n const availableConsumers = consumers ? Object.keys(consumers) : [];\n const available = availableConsumers.length > 0 ? availableConsumers.join(\", \") : \"none\";\n throw new Error(\n `Consumer \"${String(consumerName)}\" not found in contract. Available consumers: ${available}`,\n );\n }\n\n // Return the handler as-is, with type checking enforced\n return handler;\n}\n\n/**\n * Define multiple type-safe handlers for consumers in a contract.\n *\n * This utility allows you to define all handlers at once outside of the worker creation,\n * ensuring type safety and providing better code organization.\n *\n * @template TContract - The contract definition type\n * @param contract - The contract definition containing the consumers\n * @param handlers - An object with async 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 } from '@amqp-contract/worker';\n * import { orderContract } from './contract';\n *\n * // Define all handlers at once\n * const handlers = defineHandlers(orderContract, {\n * processOrder: async (message) => {\n * // message is fully typed based on the contract\n * console.log('Processing order:', message.orderId);\n * await processPayment(message);\n * },\n * notifyOrder: async (message) => {\n * await sendNotification(message);\n * },\n * shipOrder: async (message) => {\n * await prepareShipment(message);\n * },\n * });\n *\n * // Use the handlers in worker\n * const worker = await TypedAmqpWorker.create({\n * contract: orderContract,\n * handlers,\n * connection: 'amqp://localhost',\n * });\n * ```\n *\n * @example\n * ```typescript\n * // Separate handler definitions for better organization\n * async function handleProcessOrder(message: WorkerInferConsumerInput<typeof orderContract, 'processOrder'>) {\n * await processOrder(message);\n * }\n *\n * async function handleNotifyOrder(message: WorkerInferConsumerInput<typeof orderContract, 'notifyOrder'>) {\n * await sendNotification(message);\n * }\n *\n * const handlers = defineHandlers(orderContract, {\n * processOrder: handleProcessOrder,\n * notifyOrder: handleNotifyOrder,\n * });\n * ```\n */\nexport function defineHandlers<TContract extends ContractDefinition>(\n contract: TContract,\n handlers: WorkerInferConsumerHandlers<TContract>,\n): WorkerInferConsumerHandlers<TContract> {\n // Validate that all consumers in handlers exist in the contract\n const consumers = contract.consumers as Record<string, unknown> | undefined;\n const availableConsumers = consumers ? Object.keys(consumers) : [];\n\n for (const handlerName of Object.keys(handlers)) {\n if (!consumers || !(handlerName in consumers)) {\n const available = availableConsumers.length > 0 ? availableConsumers.join(\", \") : \"none\";\n throw new Error(\n `Consumer \"${handlerName}\" not found in contract. Available consumers: ${available}`,\n );\n }\n }\n\n // Return the handlers as-is, with type checking enforced\n return handlers;\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amqp-contract/worker",
3
- "version": "0.1.3",
3
+ "version": "0.2.0",
4
4
  "description": "Worker utilities for consuming messages using amqp-contract",
5
5
  "keywords": [
6
6
  "amqp",
@@ -42,7 +42,8 @@
42
42
  ],
43
43
  "dependencies": {
44
44
  "@swan-io/boxed": "3.2.1",
45
- "@amqp-contract/contract": "0.1.3"
45
+ "@amqp-contract/contract": "0.2.0",
46
+ "@amqp-contract/core": "0.2.0"
46
47
  },
47
48
  "devDependencies": {
48
49
  "@types/amqplib": "0.10.8",
@@ -53,8 +54,8 @@
53
54
  "typescript": "5.9.3",
54
55
  "vitest": "4.0.16",
55
56
  "zod": "4.2.1",
56
- "@amqp-contract/client": "0.1.3",
57
- "@amqp-contract/testing": "0.1.3",
57
+ "@amqp-contract/client": "0.2.0",
58
+ "@amqp-contract/testing": "0.2.0",
58
59
  "@amqp-contract/tsconfig": "0.0.0"
59
60
  },
60
61
  "peerDependencies": {