@amqp-contract/client 0.0.2 → 0.0.5

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
@@ -2,6 +2,8 @@
2
2
 
3
3
  Type-safe AMQP client for publishing messages using amqp-contract.
4
4
 
5
+ 📖 **[Full documentation →](https://btravers.github.io/amqp-contract/api/client)**
6
+
5
7
  ## Installation
6
8
 
7
9
  ```bash
@@ -11,16 +13,15 @@ pnpm add @amqp-contract/client amqplib
11
13
  ## Usage
12
14
 
13
15
  ```typescript
14
- import { createClient } from '@amqp-contract/client';
16
+ import { TypedAmqpClient } from '@amqp-contract/client';
15
17
  import { connect } from 'amqplib';
16
18
  import { contract } from './contract';
17
19
 
18
20
  // Connect to RabbitMQ
19
21
  const connection = await connect('amqp://localhost');
20
22
 
21
- // Create client from contract
22
- const client = createClient(contract);
23
- await client.connect(connection);
23
+ // Create client from contract (automatically connects)
24
+ const client = await TypedAmqpClient.create({ contract, connection });
24
25
 
25
26
  // Publish message with type safety
26
27
  await client.publish('orderCreated', {
@@ -34,19 +35,20 @@ await client.close();
34
35
 
35
36
  ## API
36
37
 
37
- ### `createClient(contract)`
38
+ ### `TypedAmqpClient.create(options)`
38
39
 
39
- Create a type-safe AMQP client from a contract.
40
+ Create a type-safe AMQP client from a contract. Automatically connects to RabbitMQ.
40
41
 
41
- ### `AmqpClient.connect(connection)`
42
+ **Parameters:**
42
43
 
43
- Connect to an AMQP broker and set up all exchanges, queues, and bindings defined in the contract.
44
+ - `options.contract` - Contract definition
45
+ - `options.connection` - amqplib Connection object
44
46
 
45
- ### `AmqpClient.publish(publisherName, message, options?)`
47
+ ### `TypedAmqpClient.publish(publisherName, message, options?)`
46
48
 
47
49
  Publish a message using a defined publisher. The message will be validated against the schema and type-checked at compile time.
48
50
 
49
- ### `AmqpClient.close()`
51
+ ### `TypedAmqpClient.close()`
50
52
 
51
53
  Close the channel and connection.
52
54
 
package/dist/index.cjs CHANGED
@@ -3,37 +3,26 @@
3
3
  /**
4
4
  * Type-safe AMQP client for publishing messages
5
5
  */
6
- var AmqpClient = class {
6
+ var TypedAmqpClient = class TypedAmqpClient {
7
7
  channel = null;
8
- connection = null;
9
- constructor(contract) {
8
+ constructor(contract, connection) {
10
9
  this.contract = contract;
10
+ this.connection = connection;
11
11
  }
12
12
  /**
13
- * Connect to AMQP broker
13
+ * Create a type-safe AMQP client from a contract
14
+ * The client will automatically connect to the AMQP broker
14
15
  */
15
- async connect(connection) {
16
- this.connection = connection;
17
- this.channel = await connection.createChannel();
18
- if (this.contract.exchanges && this.channel) for (const exchange of Object.values(this.contract.exchanges)) await this.channel.assertExchange(exchange.name, exchange.type, {
19
- durable: exchange.durable,
20
- autoDelete: exchange.autoDelete,
21
- internal: exchange.internal,
22
- arguments: exchange.arguments
23
- });
24
- if (this.contract.queues && this.channel) for (const queue of Object.values(this.contract.queues)) await this.channel.assertQueue(queue.name, {
25
- durable: queue.durable,
26
- exclusive: queue.exclusive,
27
- autoDelete: queue.autoDelete,
28
- arguments: queue.arguments
29
- });
30
- if (this.contract.bindings && this.channel) for (const binding of Object.values(this.contract.bindings)) await this.channel.bindQueue(binding.queue, binding.exchange, binding.routingKey ?? "", binding.arguments);
16
+ static async create(options) {
17
+ const client = new TypedAmqpClient(options.contract, options.connection);
18
+ await client.init();
19
+ return client;
31
20
  }
32
21
  /**
33
22
  * Publish a message using a defined publisher
34
23
  */
35
24
  async publish(publisherName, message, options) {
36
- if (!this.channel) throw new Error("Client not connected. Call connect() first.");
25
+ if (!this.channel) throw new Error("Client not initialized. Create the client using TypedAmqpClient.create() to establish a connection.");
37
26
  const publishers = this.contract.publishers;
38
27
  if (!publishers) throw new Error("No publishers defined in contract");
39
28
  const publisher = publishers[publisherName];
@@ -50,23 +39,29 @@ var AmqpClient = class {
50
39
  * Close the connection
51
40
  */
52
41
  async close() {
53
- if (this.channel) {
54
- await this.channel.close();
55
- this.channel = null;
56
- }
57
- if (this.connection) {
58
- await this.connection.close();
59
- this.connection = null;
60
- }
42
+ if (this.channel) await this.channel.close();
43
+ await this.connection.close();
44
+ }
45
+ /**
46
+ * Connect to AMQP broker
47
+ */
48
+ async init() {
49
+ this.channel = await this.connection.createChannel();
50
+ if (this.contract.exchanges) for (const exchange of Object.values(this.contract.exchanges)) await this.channel.assertExchange(exchange.name, exchange.type, {
51
+ durable: exchange.durable,
52
+ autoDelete: exchange.autoDelete,
53
+ internal: exchange.internal,
54
+ arguments: exchange.arguments
55
+ });
56
+ if (this.contract.queues) for (const queue of Object.values(this.contract.queues)) await this.channel.assertQueue(queue.name, {
57
+ durable: queue.durable,
58
+ exclusive: queue.exclusive,
59
+ autoDelete: queue.autoDelete,
60
+ arguments: queue.arguments
61
+ });
62
+ if (this.contract.bindings) for (const binding of Object.values(this.contract.bindings)) await this.channel.bindQueue(binding.queue, binding.exchange, binding.routingKey ?? "", binding.arguments);
61
63
  }
62
64
  };
63
- /**
64
- * Create a type-safe AMQP client from a contract
65
- */
66
- function createClient(contract) {
67
- return new AmqpClient(contract);
68
- }
69
65
 
70
66
  //#endregion
71
- exports.AmqpClient = AmqpClient;
72
- exports.createClient = createClient;
67
+ exports.TypedAmqpClient = TypedAmqpClient;
package/dist/index.d.cts CHANGED
@@ -3,6 +3,13 @@ import { ClientInferPublisherInput, ContractDefinition, InferPublisherNames } fr
3
3
 
4
4
  //#region src/client.d.ts
5
5
 
6
+ /**
7
+ * Options for creating a client
8
+ */
9
+ interface CreateClientOptions<TContract extends ContractDefinition> {
10
+ contract: TContract;
11
+ connection: ChannelModel;
12
+ }
6
13
  /**
7
14
  * Options for publishing a message
8
15
  */
@@ -13,15 +20,16 @@ interface PublishOptions {
13
20
  /**
14
21
  * Type-safe AMQP client for publishing messages
15
22
  */
16
- declare class AmqpClient<TContract extends ContractDefinition> {
23
+ declare class TypedAmqpClient<TContract extends ContractDefinition> {
17
24
  private readonly contract;
25
+ private readonly connection;
18
26
  private channel;
19
- private connection;
20
- constructor(contract: TContract);
27
+ private constructor();
21
28
  /**
22
- * Connect to AMQP broker
29
+ * Create a type-safe AMQP client from a contract
30
+ * The client will automatically connect to the AMQP broker
23
31
  */
24
- connect(connection: ChannelModel): Promise<void>;
32
+ static create<TContract extends ContractDefinition>(options: CreateClientOptions<TContract>): Promise<TypedAmqpClient<TContract>>;
25
33
  /**
26
34
  * Publish a message using a defined publisher
27
35
  */
@@ -30,11 +38,11 @@ declare class AmqpClient<TContract extends ContractDefinition> {
30
38
  * Close the connection
31
39
  */
32
40
  close(): Promise<void>;
41
+ /**
42
+ * Connect to AMQP broker
43
+ */
44
+ private init;
33
45
  }
34
- /**
35
- * Create a type-safe AMQP client from a contract
36
- */
37
- declare function createClient<TContract extends ContractDefinition>(contract: TContract): AmqpClient<TContract>;
38
46
  //#endregion
39
- export { AmqpClient, type PublishOptions, createClient };
47
+ export { type CreateClientOptions, type PublishOptions, TypedAmqpClient };
40
48
  //# sourceMappingURL=index.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../src/client.ts"],"sourcesContent":[],"mappings":";;;;;;;AAUA;AAQa,UARI,cAAA,CAQM;EAAmB,UAAA,CAAA,EAAA,MAAA;EAID,OAAA,CAAA,EAV7B,OAAA,CAAQ,OAUqB;;;;;AAkDtB,cAtDN,UAsDM,CAAA,kBAtDuB,kBAsDvB,CAAA,CAAA;EACoB,iBAAA,QAAA;EAAW,QAAA,OAAA;EAArC,QAAA,UAAA;EACC,WAAA,CAAA,QAAA,EApD2B,SAoD3B;EACT;;;EA8DW,OAAA,CAAA,UAAY,EA9GA,YA8GA,CAAA,EA9Ge,OA8Gf,CAAA,IAAA,CAAA;EAAmB;;;EAE5C,OAAA,CAAA,cApE2B,mBAoE3B,CApE+C,SAoE/C,CAAA,CAAA,CAAA,aAAA,EAnEgB,KAmEhB,EAAA,OAAA,EAlEU,yBAkEV,CAlEoC,SAkEpC,EAlE+C,KAkE/C,CAAA,EAAA,OAAA,CAAA,EAjEW,cAiEX,CAAA,EAhEE,OAgEF,CAAA,OAAA,CAAA;EAAU;;;WAjBI;;;;;iBAeD,+BAA+B,8BACnC,YACT,WAAW"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/client.ts"],"sourcesContent":[],"mappings":";;;;;;;AAUA;AAAuD,UAAtC,mBAAsC,CAAA,kBAAA,kBAAA,CAAA,CAAA;EAC3C,QAAA,EAAA,SAAA;EACE,UAAA,EAAA,YAAA;;AAMd;AAQA;;AAYwC,UApBvB,cAAA,CAoBuB;EACP,UAAA,CAAA,EAAA,MAAA;EAApB,OAAA,CAAA,EAnBD,OAAA,CAAQ,OAmBP;;;;;AAUiB,cAvBjB,eAuBiB,CAAA,kBAvBiB,kBAuBjB,CAAA,CAAA;EACX,iBAAA,QAAA;EACoB,iBAAA,UAAA;EAAW,QAAA,OAAA;EAArC,QAAA,WAAA,CAAA;EACC;;;;kCAd0B,6BAC3B,oBAAoB,aAC5B,QAAQ,gBAAgB;;;;wBASC,oBAAoB,2BAC/B,gBACN,0BAA0B,WAAW,kBACpC,iBACT;;;;WAiDY"}
package/dist/index.d.mts CHANGED
@@ -3,6 +3,13 @@ import { ClientInferPublisherInput, ContractDefinition, InferPublisherNames } fr
3
3
 
4
4
  //#region src/client.d.ts
5
5
 
6
+ /**
7
+ * Options for creating a client
8
+ */
9
+ interface CreateClientOptions<TContract extends ContractDefinition> {
10
+ contract: TContract;
11
+ connection: ChannelModel;
12
+ }
6
13
  /**
7
14
  * Options for publishing a message
8
15
  */
@@ -13,15 +20,16 @@ interface PublishOptions {
13
20
  /**
14
21
  * Type-safe AMQP client for publishing messages
15
22
  */
16
- declare class AmqpClient<TContract extends ContractDefinition> {
23
+ declare class TypedAmqpClient<TContract extends ContractDefinition> {
17
24
  private readonly contract;
25
+ private readonly connection;
18
26
  private channel;
19
- private connection;
20
- constructor(contract: TContract);
27
+ private constructor();
21
28
  /**
22
- * Connect to AMQP broker
29
+ * Create a type-safe AMQP client from a contract
30
+ * The client will automatically connect to the AMQP broker
23
31
  */
24
- connect(connection: ChannelModel): Promise<void>;
32
+ static create<TContract extends ContractDefinition>(options: CreateClientOptions<TContract>): Promise<TypedAmqpClient<TContract>>;
25
33
  /**
26
34
  * Publish a message using a defined publisher
27
35
  */
@@ -30,11 +38,11 @@ declare class AmqpClient<TContract extends ContractDefinition> {
30
38
  * Close the connection
31
39
  */
32
40
  close(): Promise<void>;
41
+ /**
42
+ * Connect to AMQP broker
43
+ */
44
+ private init;
33
45
  }
34
- /**
35
- * Create a type-safe AMQP client from a contract
36
- */
37
- declare function createClient<TContract extends ContractDefinition>(contract: TContract): AmqpClient<TContract>;
38
46
  //#endregion
39
- export { AmqpClient, type PublishOptions, createClient };
47
+ export { type CreateClientOptions, type PublishOptions, TypedAmqpClient };
40
48
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/client.ts"],"sourcesContent":[],"mappings":";;;;;;;AAUA;AAQa,UARI,cAAA,CAQM;EAAmB,UAAA,CAAA,EAAA,MAAA;EAID,OAAA,CAAA,EAV7B,OAAA,CAAQ,OAUqB;;;;;AAkDtB,cAtDN,UAsDM,CAAA,kBAtDuB,kBAsDvB,CAAA,CAAA;EACoB,iBAAA,QAAA;EAAW,QAAA,OAAA;EAArC,QAAA,UAAA;EACC,WAAA,CAAA,QAAA,EApD2B,SAoD3B;EACT;;;EA8DW,OAAA,CAAA,UAAY,EA9GA,YA8GA,CAAA,EA9Ge,OA8Gf,CAAA,IAAA,CAAA;EAAmB;;;EAE5C,OAAA,CAAA,cApE2B,mBAoE3B,CApE+C,SAoE/C,CAAA,CAAA,CAAA,aAAA,EAnEgB,KAmEhB,EAAA,OAAA,EAlEU,yBAkEV,CAlEoC,SAkEpC,EAlE+C,KAkE/C,CAAA,EAAA,OAAA,CAAA,EAjEW,cAiEX,CAAA,EAhEE,OAgEF,CAAA,OAAA,CAAA;EAAU;;;WAjBI;;;;;iBAeD,+BAA+B,8BACnC,YACT,WAAW"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/client.ts"],"sourcesContent":[],"mappings":";;;;;;;AAUA;AAAuD,UAAtC,mBAAsC,CAAA,kBAAA,kBAAA,CAAA,CAAA;EAC3C,QAAA,EAAA,SAAA;EACE,UAAA,EAAA,YAAA;;AAMd;AAQA;;AAYwC,UApBvB,cAAA,CAoBuB;EACP,UAAA,CAAA,EAAA,MAAA;EAApB,OAAA,CAAA,EAnBD,OAAA,CAAQ,OAmBP;;;;;AAUiB,cAvBjB,eAuBiB,CAAA,kBAvBiB,kBAuBjB,CAAA,CAAA;EACX,iBAAA,QAAA;EACoB,iBAAA,UAAA;EAAW,QAAA,OAAA;EAArC,QAAA,WAAA,CAAA;EACC;;;;kCAd0B,6BAC3B,oBAAoB,aAC5B,QAAQ,gBAAgB;;;;wBASC,oBAAoB,2BAC/B,gBACN,0BAA0B,WAAW,kBACpC,iBACT;;;;WAiDY"}
package/dist/index.mjs CHANGED
@@ -2,37 +2,26 @@
2
2
  /**
3
3
  * Type-safe AMQP client for publishing messages
4
4
  */
5
- var AmqpClient = class {
5
+ var TypedAmqpClient = class TypedAmqpClient {
6
6
  channel = null;
7
- connection = null;
8
- constructor(contract) {
7
+ constructor(contract, connection) {
9
8
  this.contract = contract;
9
+ this.connection = connection;
10
10
  }
11
11
  /**
12
- * Connect to AMQP broker
12
+ * Create a type-safe AMQP client from a contract
13
+ * The client will automatically connect to the AMQP broker
13
14
  */
14
- async connect(connection) {
15
- this.connection = connection;
16
- this.channel = await connection.createChannel();
17
- if (this.contract.exchanges && this.channel) for (const exchange of Object.values(this.contract.exchanges)) await this.channel.assertExchange(exchange.name, exchange.type, {
18
- durable: exchange.durable,
19
- autoDelete: exchange.autoDelete,
20
- internal: exchange.internal,
21
- arguments: exchange.arguments
22
- });
23
- if (this.contract.queues && this.channel) for (const queue of Object.values(this.contract.queues)) await this.channel.assertQueue(queue.name, {
24
- durable: queue.durable,
25
- exclusive: queue.exclusive,
26
- autoDelete: queue.autoDelete,
27
- arguments: queue.arguments
28
- });
29
- if (this.contract.bindings && this.channel) for (const binding of Object.values(this.contract.bindings)) await this.channel.bindQueue(binding.queue, binding.exchange, binding.routingKey ?? "", binding.arguments);
15
+ static async create(options) {
16
+ const client = new TypedAmqpClient(options.contract, options.connection);
17
+ await client.init();
18
+ return client;
30
19
  }
31
20
  /**
32
21
  * Publish a message using a defined publisher
33
22
  */
34
23
  async publish(publisherName, message, options) {
35
- if (!this.channel) throw new Error("Client not connected. Call connect() first.");
24
+ if (!this.channel) throw new Error("Client not initialized. Create the client using TypedAmqpClient.create() to establish a connection.");
36
25
  const publishers = this.contract.publishers;
37
26
  if (!publishers) throw new Error("No publishers defined in contract");
38
27
  const publisher = publishers[publisherName];
@@ -49,23 +38,30 @@ var AmqpClient = class {
49
38
  * Close the connection
50
39
  */
51
40
  async close() {
52
- if (this.channel) {
53
- await this.channel.close();
54
- this.channel = null;
55
- }
56
- if (this.connection) {
57
- await this.connection.close();
58
- this.connection = null;
59
- }
41
+ if (this.channel) await this.channel.close();
42
+ await this.connection.close();
43
+ }
44
+ /**
45
+ * Connect to AMQP broker
46
+ */
47
+ async init() {
48
+ this.channel = await this.connection.createChannel();
49
+ if (this.contract.exchanges) for (const exchange of Object.values(this.contract.exchanges)) await this.channel.assertExchange(exchange.name, exchange.type, {
50
+ durable: exchange.durable,
51
+ autoDelete: exchange.autoDelete,
52
+ internal: exchange.internal,
53
+ arguments: exchange.arguments
54
+ });
55
+ if (this.contract.queues) for (const queue of Object.values(this.contract.queues)) await this.channel.assertQueue(queue.name, {
56
+ durable: queue.durable,
57
+ exclusive: queue.exclusive,
58
+ autoDelete: queue.autoDelete,
59
+ arguments: queue.arguments
60
+ });
61
+ if (this.contract.bindings) for (const binding of Object.values(this.contract.bindings)) await this.channel.bindQueue(binding.queue, binding.exchange, binding.routingKey ?? "", binding.arguments);
60
62
  }
61
63
  };
62
- /**
63
- * Create a type-safe AMQP client from a contract
64
- */
65
- function createClient(contract) {
66
- return new AmqpClient(contract);
67
- }
68
64
 
69
65
  //#endregion
70
- export { AmqpClient, createClient };
66
+ export { TypedAmqpClient };
71
67
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["contract: TContract"],"sources":["../src/client.ts"],"sourcesContent":["import type { Channel, ChannelModel, Options } from \"amqplib\";\nimport type {\n ClientInferPublisherInput,\n ContractDefinition,\n InferPublisherNames,\n} from \"@amqp-contract/contract\";\n\n/**\n * Options for publishing a message\n */\nexport interface PublishOptions {\n routingKey?: string;\n options?: Options.Publish;\n}\n\n/**\n * Type-safe AMQP client for publishing messages\n */\nexport class AmqpClient<TContract extends ContractDefinition> {\n private channel: Channel | null = null;\n private connection: ChannelModel | null = null;\n\n constructor(private readonly contract: TContract) {}\n\n /**\n * Connect to AMQP broker\n */\n async connect(connection: ChannelModel): Promise<void> {\n this.connection = connection;\n this.channel = await connection.createChannel();\n\n // Setup exchanges\n if (this.contract.exchanges && this.channel) {\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 && this.channel) {\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 && this.channel) {\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 * Publish a message using a defined publisher\n */\n async publish<TName extends InferPublisherNames<TContract>>(\n publisherName: TName,\n message: ClientInferPublisherInput<TContract, TName>,\n options?: PublishOptions,\n ): Promise<boolean> {\n if (!this.channel) {\n throw new Error(\"Client not connected. Call connect() first.\");\n }\n\n const publishers = this.contract.publishers as Record<string, unknown>;\n if (!publishers) {\n throw new Error(\"No publishers defined in contract\");\n }\n\n const publisher = publishers[publisherName as string];\n if (!publisher || typeof publisher !== \"object\") {\n throw new Error(`Publisher \"${String(publisherName)}\" not found in contract`);\n }\n\n const publisherDef = publisher as {\n exchange: string;\n routingKey?: string;\n message: { \"~standard\": { validate: (value: unknown) => unknown } };\n };\n\n // Validate message using schema\n const validation = publisherDef.message[\"~standard\"].validate(message);\n if (\n typeof validation === \"object\" &&\n validation !== null &&\n \"issues\" in validation &&\n validation.issues\n ) {\n throw new Error(`Message validation failed: ${JSON.stringify(validation.issues)}`);\n }\n\n const validatedMessage =\n typeof validation === \"object\" && validation !== null && \"value\" in validation\n ? validation.value\n : message;\n\n // Publish message\n const routingKey = options?.routingKey ?? publisherDef.routingKey ?? \"\";\n const content = Buffer.from(JSON.stringify(validatedMessage));\n\n return this.channel.publish(publisherDef.exchange, routingKey, content, options?.options);\n }\n\n /**\n * Close the connection\n */\n async close(): Promise<void> {\n if (this.channel) {\n await this.channel.close();\n this.channel = null;\n }\n if (this.connection) {\n await (this.connection as unknown as { close(): Promise<void> }).close();\n this.connection = null;\n }\n }\n}\n\n/**\n * Create a type-safe AMQP client from a contract\n */\nexport function createClient<TContract extends ContractDefinition>(\n contract: TContract,\n): AmqpClient<TContract> {\n return new AmqpClient(contract);\n}\n"],"mappings":";;;;AAkBA,IAAa,aAAb,MAA8D;CAC5D,AAAQ,UAA0B;CAClC,AAAQ,aAAkC;CAE1C,YAAY,AAAiBA,UAAqB;EAArB;;;;;CAK7B,MAAM,QAAQ,YAAyC;AACrD,OAAK,aAAa;AAClB,OAAK,UAAU,MAAM,WAAW,eAAe;AAG/C,MAAI,KAAK,SAAS,aAAa,KAAK,QAClC,MAAK,MAAM,YAAY,OAAO,OAAO,KAAK,SAAS,UAAU,CAC3D,OAAM,KAAK,QAAQ,eAAe,SAAS,MAAM,SAAS,MAAM;GAC9D,SAAS,SAAS;GAClB,YAAY,SAAS;GACrB,UAAU,SAAS;GACnB,WAAW,SAAS;GACrB,CAAC;AAKN,MAAI,KAAK,SAAS,UAAU,KAAK,QAC/B,MAAK,MAAM,SAAS,OAAO,OAAO,KAAK,SAAS,OAAO,CACrD,OAAM,KAAK,QAAQ,YAAY,MAAM,MAAM;GACzC,SAAS,MAAM;GACf,WAAW,MAAM;GACjB,YAAY,MAAM;GAClB,WAAW,MAAM;GAClB,CAAC;AAKN,MAAI,KAAK,SAAS,YAAY,KAAK,QACjC,MAAK,MAAM,WAAW,OAAO,OAAO,KAAK,SAAS,SAAS,CACzD,OAAM,KAAK,QAAQ,UACjB,QAAQ,OACR,QAAQ,UACR,QAAQ,cAAc,IACtB,QAAQ,UACT;;;;;CAQP,MAAM,QACJ,eACA,SACA,SACkB;AAClB,MAAI,CAAC,KAAK,QACR,OAAM,IAAI,MAAM,8CAA8C;EAGhE,MAAM,aAAa,KAAK,SAAS;AACjC,MAAI,CAAC,WACH,OAAM,IAAI,MAAM,oCAAoC;EAGtD,MAAM,YAAY,WAAW;AAC7B,MAAI,CAAC,aAAa,OAAO,cAAc,SACrC,OAAM,IAAI,MAAM,cAAc,OAAO,cAAc,CAAC,yBAAyB;EAG/E,MAAM,eAAe;EAOrB,MAAM,aAAa,aAAa,QAAQ,aAAa,SAAS,QAAQ;AACtE,MACE,OAAO,eAAe,YACtB,eAAe,QACf,YAAY,cACZ,WAAW,OAEX,OAAM,IAAI,MAAM,8BAA8B,KAAK,UAAU,WAAW,OAAO,GAAG;EAGpF,MAAM,mBACJ,OAAO,eAAe,YAAY,eAAe,QAAQ,WAAW,aAChE,WAAW,QACX;EAGN,MAAM,aAAa,SAAS,cAAc,aAAa,cAAc;EACrE,MAAM,UAAU,OAAO,KAAK,KAAK,UAAU,iBAAiB,CAAC;AAE7D,SAAO,KAAK,QAAQ,QAAQ,aAAa,UAAU,YAAY,SAAS,SAAS,QAAQ;;;;;CAM3F,MAAM,QAAuB;AAC3B,MAAI,KAAK,SAAS;AAChB,SAAM,KAAK,QAAQ,OAAO;AAC1B,QAAK,UAAU;;AAEjB,MAAI,KAAK,YAAY;AACnB,SAAO,KAAK,WAAqD,OAAO;AACxE,QAAK,aAAa;;;;;;;AAQxB,SAAgB,aACd,UACuB;AACvB,QAAO,IAAI,WAAW,SAAS"}
1
+ {"version":3,"file":"index.mjs","names":["contract: TContract","connection: ChannelModel"],"sources":["../src/client.ts"],"sourcesContent":["import type { Channel, ChannelModel, Options } from \"amqplib\";\nimport type {\n ClientInferPublisherInput,\n ContractDefinition,\n InferPublisherNames,\n} from \"@amqp-contract/contract\";\n\n/**\n * Options for creating a client\n */\nexport interface CreateClientOptions<TContract extends ContractDefinition> {\n contract: TContract;\n connection: ChannelModel;\n}\n\n/**\n * Options for publishing a message\n */\nexport interface PublishOptions {\n routingKey?: string;\n options?: Options.Publish;\n}\n\n/**\n * Type-safe AMQP client for publishing messages\n */\nexport class TypedAmqpClient<TContract extends ContractDefinition> {\n private channel: Channel | null = null;\n\n private constructor(\n private readonly contract: TContract,\n private readonly connection: ChannelModel,\n ) {}\n\n /**\n * Create a type-safe AMQP client from a contract\n * The client will automatically connect to the AMQP broker\n */\n static async create<TContract extends ContractDefinition>(\n options: CreateClientOptions<TContract>,\n ): Promise<TypedAmqpClient<TContract>> {\n const client = new TypedAmqpClient(options.contract, options.connection);\n await client.init();\n return client;\n }\n\n /**\n * Publish a message using a defined publisher\n */\n async publish<TName extends InferPublisherNames<TContract>>(\n publisherName: TName,\n message: ClientInferPublisherInput<TContract, TName>,\n options?: PublishOptions,\n ): Promise<boolean> {\n if (!this.channel) {\n throw new Error(\n \"Client not initialized. Create the client using TypedAmqpClient.create() to establish a connection.\",\n );\n }\n\n const publishers = this.contract.publishers as Record<string, unknown>;\n if (!publishers) {\n throw new Error(\"No publishers defined in contract\");\n }\n\n const publisher = publishers[publisherName as string];\n if (!publisher || typeof publisher !== \"object\") {\n throw new Error(`Publisher \"${String(publisherName)}\" not found in contract`);\n }\n\n const publisherDef = publisher as {\n exchange: string;\n routingKey?: string;\n message: { \"~standard\": { validate: (value: unknown) => unknown } };\n };\n\n // Validate message using schema\n const validation = publisherDef.message[\"~standard\"].validate(message);\n if (\n typeof validation === \"object\" &&\n validation !== null &&\n \"issues\" in validation &&\n validation.issues\n ) {\n throw new Error(`Message validation failed: ${JSON.stringify(validation.issues)}`);\n }\n\n const validatedMessage =\n typeof validation === \"object\" && validation !== null && \"value\" in validation\n ? validation.value\n : message;\n\n // Publish message\n const routingKey = options?.routingKey ?? publisherDef.routingKey ?? \"\";\n const content = Buffer.from(JSON.stringify(validatedMessage));\n\n return this.channel.publish(publisherDef.exchange, routingKey, content, options?.options);\n }\n\n /**\n * Close the connection\n */\n async close(): Promise<void> {\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"],"mappings":";;;;AA0BA,IAAa,kBAAb,MAAa,gBAAsD;CACjE,AAAQ,UAA0B;CAElC,AAAQ,YACN,AAAiBA,UACjB,AAAiBC,YACjB;EAFiB;EACA;;;;;;CAOnB,aAAa,OACX,SACqC;EACrC,MAAM,SAAS,IAAI,gBAAgB,QAAQ,UAAU,QAAQ,WAAW;AACxE,QAAM,OAAO,MAAM;AACnB,SAAO;;;;;CAMT,MAAM,QACJ,eACA,SACA,SACkB;AAClB,MAAI,CAAC,KAAK,QACR,OAAM,IAAI,MACR,sGACD;EAGH,MAAM,aAAa,KAAK,SAAS;AACjC,MAAI,CAAC,WACH,OAAM,IAAI,MAAM,oCAAoC;EAGtD,MAAM,YAAY,WAAW;AAC7B,MAAI,CAAC,aAAa,OAAO,cAAc,SACrC,OAAM,IAAI,MAAM,cAAc,OAAO,cAAc,CAAC,yBAAyB;EAG/E,MAAM,eAAe;EAOrB,MAAM,aAAa,aAAa,QAAQ,aAAa,SAAS,QAAQ;AACtE,MACE,OAAO,eAAe,YACtB,eAAe,QACf,YAAY,cACZ,WAAW,OAEX,OAAM,IAAI,MAAM,8BAA8B,KAAK,UAAU,WAAW,OAAO,GAAG;EAGpF,MAAM,mBACJ,OAAO,eAAe,YAAY,eAAe,QAAQ,WAAW,aAChE,WAAW,QACX;EAGN,MAAM,aAAa,SAAS,cAAc,aAAa,cAAc;EACrE,MAAM,UAAU,OAAO,KAAK,KAAK,UAAU,iBAAiB,CAAC;AAE7D,SAAO,KAAK,QAAQ,QAAQ,aAAa,UAAU,YAAY,SAAS,SAAS,QAAQ;;;;;CAM3F,MAAM,QAAuB;AAC3B,MAAI,KAAK,QACP,OAAM,KAAK,QAAQ,OAAO;AAG5B,QAAM,KAAK,WAAW,OAAO;;;;;CAM/B,MAAc,OAAsB;AAClC,OAAK,UAAU,MAAM,KAAK,WAAW,eAAe;AAGpD,MAAI,KAAK,SAAS,UAChB,MAAK,MAAM,YAAY,OAAO,OAAO,KAAK,SAAS,UAAU,CAC3D,OAAM,KAAK,QAAQ,eAAe,SAAS,MAAM,SAAS,MAAM;GAC9D,SAAS,SAAS;GAClB,YAAY,SAAS;GACrB,UAAU,SAAS;GACnB,WAAW,SAAS;GACrB,CAAC;AAKN,MAAI,KAAK,SAAS,OAChB,MAAK,MAAM,SAAS,OAAO,OAAO,KAAK,SAAS,OAAO,CACrD,OAAM,KAAK,QAAQ,YAAY,MAAM,MAAM;GACzC,SAAS,MAAM;GACf,WAAW,MAAM;GACjB,YAAY,MAAM;GAClB,WAAW,MAAM;GAClB,CAAC;AAKN,MAAI,KAAK,SAAS,SAChB,MAAK,MAAM,WAAW,OAAO,OAAO,KAAK,SAAS,SAAS,CACzD,OAAM,KAAK,QAAQ,UACjB,QAAQ,OACR,QAAQ,UACR,QAAQ,cAAc,IACtB,QAAQ,UACT"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amqp-contract/client",
3
- "version": "0.0.2",
3
+ "version": "0.0.5",
4
4
  "description": "Client utilities for publishing 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.2"
44
+ "@amqp-contract/contract": "0.0.5"
45
45
  },
46
46
  "devDependencies": {
47
47
  "@types/amqplib": "0.10.8",
@@ -52,6 +52,7 @@
52
52
  "typescript": "5.9.3",
53
53
  "vitest": "4.0.16",
54
54
  "zod": "4.2.1",
55
+ "@amqp-contract/testing": "0.0.5",
55
56
  "@amqp-contract/tsconfig": "0.0.0"
56
57
  },
57
58
  "peerDependencies": {
@@ -60,8 +61,9 @@
60
61
  "scripts": {
61
62
  "build": "tsdown src/index.ts --format cjs,esm --dts --clean",
62
63
  "dev": "tsdown src/index.ts --format cjs,esm --dts --watch",
63
- "test": "vitest run",
64
- "test:watch": "vitest",
64
+ "test": "vitest run --project unit",
65
+ "test:integration": "vitest run --project integration",
66
+ "test:watch": "vitest --project unit",
65
67
  "typecheck": "tsc --noEmit"
66
68
  }
67
69
  }