@amqp-contract/worker 0.0.5 → 0.0.6

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
@@ -7,19 +7,15 @@ Type-safe AMQP worker for consuming messages using amqp-contract.
7
7
  ## Installation
8
8
 
9
9
  ```bash
10
- pnpm add @amqp-contract/worker amqplib
10
+ pnpm add @amqp-contract/worker
11
11
  ```
12
12
 
13
13
  ## Usage
14
14
 
15
15
  ```typescript
16
16
  import { TypedAmqpWorker } from '@amqp-contract/worker';
17
- import { connect } from 'amqplib';
18
17
  import { contract } from './contract';
19
18
 
20
- // Connect to RabbitMQ
21
- const connection = await connect('amqp://localhost');
22
-
23
19
  // Create worker from contract with handlers (automatically connects and starts consuming)
24
20
  const worker = await TypedAmqpWorker.create({
25
21
  contract,
@@ -29,7 +25,7 @@ const worker = await TypedAmqpWorker.create({
29
25
  // Your business logic here
30
26
  },
31
27
  },
32
- connection,
28
+ connection: 'amqp://localhost',
33
29
  });
34
30
 
35
31
  // Worker is already consuming messages
@@ -48,7 +44,7 @@ Create a type-safe AMQP worker from a contract with message handlers. Automatica
48
44
 
49
45
  - `options.contract` - Contract definition
50
46
  - `options.handlers` - Object with handler functions for each consumer
51
- - `options.connection` - amqplib Connection object
47
+ - `options.connection` - AMQP connection URL (string) or connection options (Options.Connect)
52
48
 
53
49
  ### `TypedAmqpWorker.close()`
54
50
 
package/dist/index.cjs CHANGED
@@ -1,3 +1,4 @@
1
+ let amqplib = require("amqplib");
1
2
 
2
3
  //#region src/worker.ts
3
4
  /**
@@ -5,11 +6,12 @@
5
6
  */
6
7
  var TypedAmqpWorker = class TypedAmqpWorker {
7
8
  channel = null;
9
+ connection = null;
8
10
  consumerTags = [];
9
- constructor(contract, handlers, connection) {
11
+ constructor(contract, handlers, connectionOptions) {
10
12
  this.contract = contract;
11
13
  this.handlers = handlers;
12
- this.connection = connection;
14
+ this.connectionOptions = connectionOptions;
13
15
  }
14
16
  /**
15
17
  * Create a type-safe AMQP worker from a contract
@@ -26,13 +28,20 @@ var TypedAmqpWorker = class TypedAmqpWorker {
26
28
  */
27
29
  async close() {
28
30
  await this.stopConsuming();
29
- if (this.channel) await this.channel.close();
30
- await this.connection.close();
31
+ if (this.channel) {
32
+ await this.channel.close();
33
+ this.channel = null;
34
+ }
35
+ if (this.connection) {
36
+ await this.connection.close();
37
+ this.connection = null;
38
+ }
31
39
  }
32
40
  /**
33
41
  * Connect to AMQP broker
34
42
  */
35
43
  async init() {
44
+ this.connection = await (0, amqplib.connect)(this.connectionOptions);
36
45
  this.channel = await this.connection.createChannel();
37
46
  if (this.contract.exchanges) for (const exchange of Object.values(this.contract.exchanges)) await this.channel.assertExchange(exchange.name, exchange.type, {
38
47
  durable: exchange.durable,
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { ChannelModel } from "amqplib";
1
+ import { Options } from "amqplib";
2
2
  import { ContractDefinition, WorkerInferConsumerHandlers } from "@amqp-contract/contract";
3
3
 
4
4
  //#region src/worker.d.ts
@@ -9,7 +9,7 @@ import { ContractDefinition, WorkerInferConsumerHandlers } from "@amqp-contract/
9
9
  interface CreateWorkerOptions<TContract extends ContractDefinition> {
10
10
  contract: TContract;
11
11
  handlers: WorkerInferConsumerHandlers<TContract>;
12
- connection: ChannelModel;
12
+ connection: string | Options.Connect;
13
13
  }
14
14
  /**
15
15
  * Type-safe AMQP worker for consuming messages
@@ -17,8 +17,9 @@ interface CreateWorkerOptions<TContract extends ContractDefinition> {
17
17
  declare class TypedAmqpWorker<TContract extends ContractDefinition> {
18
18
  private readonly contract;
19
19
  private readonly handlers;
20
- private readonly connection;
20
+ private readonly connectionOptions;
21
21
  private channel;
22
+ private connection;
22
23
  private consumerTags;
23
24
  private constructor();
24
25
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../src/worker.ts"],"sourcesContent":[],"mappings":";;;;;;;AAUA;AAAuD,UAAtC,mBAAsC,CAAA,kBAAA,kBAAA,CAAA,CAAA;EAC3C,QAAA,EAAA,SAAA;EAC4B,QAAA,EAA5B,2BAA4B,CAAA,SAAA,CAAA;EAA5B,UAAA,EACE,YADF;;;AAOZ;;AAcwC,cAd3B,eAc2B,CAAA,kBAdO,kBAcP,CAAA,CAAA;EACP,iBAAA,QAAA;EAApB,iBAAA,QAAA;EACgB,iBAAA,UAAA;EAAhB,QAAA,OAAA;EAAR,QAAA,YAAA;EAUY,QAAA,WAAA,CAAA;EAAO;;;;kCAZgB,6BAC3B,oBAAoB,aAC5B,QAAQ,gBAAgB;;;;WAUZ"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/worker.ts"],"sourcesContent":[],"mappings":";;;;;;;AAWA;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;;;;;kCAZgB,6BAC3B,oBAAoB,aAC5B,QAAQ,gBAAgB;;;;WAUZ"}
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { ChannelModel } from "amqplib";
1
+ import { Options } from "amqplib";
2
2
  import { ContractDefinition, WorkerInferConsumerHandlers } from "@amqp-contract/contract";
3
3
 
4
4
  //#region src/worker.d.ts
@@ -9,7 +9,7 @@ import { ContractDefinition, WorkerInferConsumerHandlers } from "@amqp-contract/
9
9
  interface CreateWorkerOptions<TContract extends ContractDefinition> {
10
10
  contract: TContract;
11
11
  handlers: WorkerInferConsumerHandlers<TContract>;
12
- connection: ChannelModel;
12
+ connection: string | Options.Connect;
13
13
  }
14
14
  /**
15
15
  * Type-safe AMQP worker for consuming messages
@@ -17,8 +17,9 @@ interface CreateWorkerOptions<TContract extends ContractDefinition> {
17
17
  declare class TypedAmqpWorker<TContract extends ContractDefinition> {
18
18
  private readonly contract;
19
19
  private readonly handlers;
20
- private readonly connection;
20
+ private readonly connectionOptions;
21
21
  private channel;
22
+ private connection;
22
23
  private consumerTags;
23
24
  private constructor();
24
25
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/worker.ts"],"sourcesContent":[],"mappings":";;;;;;;AAUA;AAAuD,UAAtC,mBAAsC,CAAA,kBAAA,kBAAA,CAAA,CAAA;EAC3C,QAAA,EAAA,SAAA;EAC4B,QAAA,EAA5B,2BAA4B,CAAA,SAAA,CAAA;EAA5B,UAAA,EACE,YADF;;;AAOZ;;AAcwC,cAd3B,eAc2B,CAAA,kBAdO,kBAcP,CAAA,CAAA;EACP,iBAAA,QAAA;EAApB,iBAAA,QAAA;EACgB,iBAAA,UAAA;EAAhB,QAAA,OAAA;EAAR,QAAA,YAAA;EAUY,QAAA,WAAA,CAAA;EAAO;;;;kCAZgB,6BAC3B,oBAAoB,aAC5B,QAAQ,gBAAgB;;;;WAUZ"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/worker.ts"],"sourcesContent":[],"mappings":";;;;;;;AAWA;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;;;;;kCAZgB,6BAC3B,oBAAoB,aAC5B,QAAQ,gBAAgB;;;;WAUZ"}
package/dist/index.mjs CHANGED
@@ -1,14 +1,17 @@
1
+ import { connect } from "amqplib";
2
+
1
3
  //#region src/worker.ts
2
4
  /**
3
5
  * Type-safe AMQP worker for consuming messages
4
6
  */
5
7
  var TypedAmqpWorker = class TypedAmqpWorker {
6
8
  channel = null;
9
+ connection = null;
7
10
  consumerTags = [];
8
- constructor(contract, handlers, connection) {
11
+ constructor(contract, handlers, connectionOptions) {
9
12
  this.contract = contract;
10
13
  this.handlers = handlers;
11
- this.connection = connection;
14
+ this.connectionOptions = connectionOptions;
12
15
  }
13
16
  /**
14
17
  * Create a type-safe AMQP worker from a contract
@@ -25,13 +28,20 @@ var TypedAmqpWorker = class TypedAmqpWorker {
25
28
  */
26
29
  async close() {
27
30
  await this.stopConsuming();
28
- if (this.channel) await this.channel.close();
29
- await this.connection.close();
31
+ if (this.channel) {
32
+ await this.channel.close();
33
+ this.channel = null;
34
+ }
35
+ if (this.connection) {
36
+ await this.connection.close();
37
+ this.connection = null;
38
+ }
30
39
  }
31
40
  /**
32
41
  * Connect to AMQP broker
33
42
  */
34
43
  async init() {
44
+ this.connection = await connect(this.connectionOptions);
35
45
  this.channel = await this.connection.createChannel();
36
46
  if (this.contract.exchanges) for (const exchange of Object.values(this.contract.exchanges)) await this.channel.assertExchange(exchange.name, exchange.type, {
37
47
  durable: exchange.durable,
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["contract: TContract","handlers: WorkerInferConsumerHandlers<TContract>","connection: ChannelModel"],"sources":["../src/worker.ts"],"sourcesContent":["import type { Channel, ChannelModel, ConsumeMessage } from \"amqplib\";\nimport type {\n ContractDefinition,\n InferConsumerNames,\n WorkerInferConsumerHandlers,\n} from \"@amqp-contract/contract\";\n\n/**\n * Options for creating a worker\n */\nexport interface CreateWorkerOptions<TContract extends ContractDefinition> {\n contract: TContract;\n handlers: WorkerInferConsumerHandlers<TContract>;\n connection: ChannelModel;\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 consumerTags: string[] = [];\n\n private constructor(\n private readonly contract: TContract,\n private readonly handlers: WorkerInferConsumerHandlers<TContract>,\n private readonly connection: ChannelModel,\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 }\n\n await this.connection.close();\n }\n\n /**\n * Connect to AMQP broker\n */\n private async init(): Promise<void> {\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 await this.channel.bindQueue(\n binding.queue,\n binding.exchange,\n binding.routingKey ?? \"\",\n binding.arguments,\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 throw new Error(`Consumer \"${String(consumerName)}\" not found in contract`);\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 try {\n // Parse message\n const content = JSON.parse(msg.content.toString());\n\n // Validate message using schema\n const validation = consumerDef.message[\"~standard\"].validate(content);\n if (\n typeof validation === \"object\" &&\n validation !== null &&\n \"issues\" in validation &&\n validation.issues\n ) {\n console.error(\"Message validation failed:\", validation.issues);\n // Reject message with no requeue\n this.channel?.nack(msg, false, false);\n return;\n }\n\n const validatedMessage =\n typeof validation === \"object\" && validation !== null && \"value\" in validation\n ? validation.value\n : content;\n\n // Call handler\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(\"Error processing message:\", error);\n // Reject message and requeue\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":["contract: TContract","handlers: WorkerInferConsumerHandlers<TContract>","connectionOptions: string | Options.Connect"],"sources":["../src/worker.ts"],"sourcesContent":["import { connect } from \"amqplib\";\nimport type { Channel, ChannelModel, ConsumeMessage, Options } from \"amqplib\";\nimport type {\n ContractDefinition,\n InferConsumerNames,\n WorkerInferConsumerHandlers,\n} from \"@amqp-contract/contract\";\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 await this.channel.bindQueue(\n binding.queue,\n binding.exchange,\n binding.routingKey ?? \"\",\n binding.arguments,\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 throw new Error(`Consumer \"${String(consumerName)}\" not found in contract`);\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 try {\n // Parse message\n const content = JSON.parse(msg.content.toString());\n\n // Validate message using schema\n const validation = consumerDef.message[\"~standard\"].validate(content);\n if (\n typeof validation === \"object\" &&\n validation !== null &&\n \"issues\" in validation &&\n validation.issues\n ) {\n console.error(\"Message validation failed:\", validation.issues);\n // Reject message with no requeue\n this.channel?.nack(msg, false, false);\n return;\n }\n\n const validatedMessage =\n typeof validation === \"object\" && validation !== null && \"value\" in validation\n ? validation.value\n : content;\n\n // Call handler\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(\"Error processing message:\", error);\n // Reject message and requeue\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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amqp-contract/worker",
3
- "version": "0.0.5",
3
+ "version": "0.0.6",
4
4
  "description": "Worker utilities for consuming messages using amqp-contract",
5
5
  "keywords": [
6
6
  "amqp",
@@ -41,7 +41,7 @@
41
41
  "dist"
42
42
  ],
43
43
  "dependencies": {
44
- "@amqp-contract/contract": "0.0.5"
44
+ "@amqp-contract/contract": "0.0.6"
45
45
  },
46
46
  "devDependencies": {
47
47
  "@types/amqplib": "0.10.8",
@@ -52,8 +52,8 @@
52
52
  "typescript": "5.9.3",
53
53
  "vitest": "4.0.16",
54
54
  "zod": "4.2.1",
55
- "@amqp-contract/client": "0.0.5",
56
- "@amqp-contract/testing": "0.0.5",
55
+ "@amqp-contract/client": "0.0.6",
56
+ "@amqp-contract/testing": "0.0.6",
57
57
  "@amqp-contract/tsconfig": "0.0.0"
58
58
  },
59
59
  "peerDependencies": {