@amqp-contract/client 0.1.4 → 0.2.1
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 +11 -1
- package/dist/index.cjs +31 -55
- package/dist/index.d.cts +44 -25
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +44 -25
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +32 -56
- package/dist/index.mjs.map +1 -1
- package/docs/index.md +447 -0
- package/package.json +13 -8
package/README.md
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
# @amqp-contract/client
|
|
2
2
|
|
|
3
|
-
Type-safe AMQP client for publishing messages using amqp-contract with explicit error handling via `Result` types
|
|
3
|
+
**Type-safe AMQP client for publishing messages using amqp-contract with explicit error handling via `Result` types.**
|
|
4
|
+
|
|
5
|
+
[](https://github.com/btravers/amqp-contract/actions/workflows/ci.yml)
|
|
6
|
+
[](https://www.npmjs.com/package/@amqp-contract/client)
|
|
7
|
+
[](https://www.npmjs.com/package/@amqp-contract/client)
|
|
8
|
+
[](https://www.typescriptlang.org/)
|
|
9
|
+
[](https://opensource.org/licenses/MIT)
|
|
4
10
|
|
|
5
11
|
📖 **[Full documentation →](https://btravers.github.io/amqp-contract/api/client)**
|
|
6
12
|
|
|
@@ -107,6 +113,10 @@ Close the channel and connection.
|
|
|
107
113
|
|
|
108
114
|
**Returns:** `Promise<void>`
|
|
109
115
|
|
|
116
|
+
## Documentation
|
|
117
|
+
|
|
118
|
+
📖 **[Read the full documentation →](https://btravers.github.io/amqp-contract)**
|
|
119
|
+
|
|
110
120
|
## License
|
|
111
121
|
|
|
112
122
|
MIT
|
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
let amqplib = require("amqplib");
|
|
2
1
|
let _swan_io_boxed = require("@swan-io/boxed");
|
|
2
|
+
let _amqp_contract_core = require("@amqp-contract/core");
|
|
3
3
|
|
|
4
4
|
//#region src/errors.ts
|
|
5
5
|
/**
|
|
@@ -42,76 +42,52 @@ var MessageValidationError = class extends ClientError {
|
|
|
42
42
|
* Type-safe AMQP client for publishing messages
|
|
43
43
|
*/
|
|
44
44
|
var TypedAmqpClient = class TypedAmqpClient {
|
|
45
|
-
|
|
46
|
-
connection = null;
|
|
47
|
-
constructor(contract, connectionOptions) {
|
|
45
|
+
constructor(contract, amqpClient) {
|
|
48
46
|
this.contract = contract;
|
|
49
|
-
this.
|
|
47
|
+
this.amqpClient = amqpClient;
|
|
50
48
|
}
|
|
51
49
|
/**
|
|
52
|
-
* Create a type-safe AMQP client from a contract
|
|
53
|
-
*
|
|
50
|
+
* Create a type-safe AMQP client from a contract.
|
|
51
|
+
*
|
|
52
|
+
* Connection management (including automatic reconnection) is handled internally
|
|
53
|
+
* by amqp-connection-manager via the {@link AmqpClient}. The client establishes
|
|
54
|
+
* infrastructure asynchronously in the background once the connection is ready.
|
|
54
55
|
*/
|
|
55
|
-
static
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
static create({ contract, urls, connectionOptions }) {
|
|
57
|
+
return new TypedAmqpClient(contract, new _amqp_contract_core.AmqpClient(contract, {
|
|
58
|
+
urls,
|
|
59
|
+
connectionOptions
|
|
60
|
+
}));
|
|
59
61
|
}
|
|
60
62
|
/**
|
|
61
63
|
* Publish a message using a defined publisher
|
|
62
64
|
* Returns Result.Ok(true) on success, or Result.Error with specific error on failure
|
|
63
65
|
*/
|
|
64
66
|
publish(publisherName, message, options) {
|
|
65
|
-
if (!this.channel) throw new Error("Client not initialized. Create the client using TypedAmqpClient.create() to establish a connection.");
|
|
66
67
|
const publishers = this.contract.publishers;
|
|
67
|
-
if (!publishers)
|
|
68
|
+
if (!publishers) return _swan_io_boxed.Future.value(_swan_io_boxed.Result.Error(new TechnicalError("No publishers defined in contract")));
|
|
68
69
|
const publisher = publishers[publisherName];
|
|
69
|
-
if (!publisher
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
70
|
+
if (!publisher) return _swan_io_boxed.Future.value(_swan_io_boxed.Result.Error(new TechnicalError(`Publisher "${String(publisherName)}" not found in contract`)));
|
|
71
|
+
const validateMessage = () => {
|
|
72
|
+
const validationResult = publisher.message.payload["~standard"].validate(message);
|
|
73
|
+
return _swan_io_boxed.Future.fromPromise(validationResult instanceof Promise ? validationResult : Promise.resolve(validationResult)).mapError((error) => new TechnicalError(`Validation failed`, error)).mapOkToResult((validation) => {
|
|
74
|
+
if (validation.issues) return _swan_io_boxed.Result.Error(new MessageValidationError(String(publisherName), validation.issues));
|
|
75
|
+
return _swan_io_boxed.Result.Ok(validation.value);
|
|
76
|
+
});
|
|
77
|
+
};
|
|
78
|
+
const publishMessage = (validatedMessage) => {
|
|
79
|
+
return _swan_io_boxed.Future.fromPromise(this.amqpClient.channel.publish(publisher.exchange.name, publisher.routingKey ?? "", validatedMessage, options)).mapError((error) => new TechnicalError(`Failed to publish message`, error)).mapOkToResult((published) => {
|
|
80
|
+
if (!published) return _swan_io_boxed.Result.Error(new TechnicalError(`Failed to publish message for publisher "${String(publisherName)}": Channel rejected the message (buffer full or other channel issue)`));
|
|
81
|
+
return _swan_io_boxed.Result.Ok(published);
|
|
82
|
+
});
|
|
83
|
+
};
|
|
84
|
+
return validateMessage().flatMapOk((validatedMessage) => publishMessage(validatedMessage));
|
|
79
85
|
}
|
|
80
86
|
/**
|
|
81
87
|
* Close the channel and connection
|
|
82
88
|
*/
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
await this.channel.close();
|
|
86
|
-
this.channel = null;
|
|
87
|
-
}
|
|
88
|
-
if (this.connection) {
|
|
89
|
-
await this.connection.close();
|
|
90
|
-
this.connection = null;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
/**
|
|
94
|
-
* Connect to AMQP broker
|
|
95
|
-
*/
|
|
96
|
-
async init() {
|
|
97
|
-
this.connection = await (0, amqplib.connect)(this.connectionOptions);
|
|
98
|
-
this.channel = await this.connection.createChannel();
|
|
99
|
-
if (this.contract.exchanges) for (const exchange of Object.values(this.contract.exchanges)) await this.channel.assertExchange(exchange.name, exchange.type, {
|
|
100
|
-
durable: exchange.durable,
|
|
101
|
-
autoDelete: exchange.autoDelete,
|
|
102
|
-
internal: exchange.internal,
|
|
103
|
-
arguments: exchange.arguments
|
|
104
|
-
});
|
|
105
|
-
if (this.contract.queues) for (const queue of Object.values(this.contract.queues)) await this.channel.assertQueue(queue.name, {
|
|
106
|
-
durable: queue.durable,
|
|
107
|
-
exclusive: queue.exclusive,
|
|
108
|
-
autoDelete: queue.autoDelete,
|
|
109
|
-
arguments: queue.arguments
|
|
110
|
-
});
|
|
111
|
-
if (this.contract.bindings) {
|
|
112
|
-
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);
|
|
113
|
-
else if (binding.type === "exchange") await this.channel.bindExchange(binding.destination, binding.source, binding.routingKey ?? "", binding.arguments);
|
|
114
|
-
}
|
|
89
|
+
close() {
|
|
90
|
+
return _swan_io_boxed.Future.fromPromise(this.amqpClient.close()).mapError((error) => new TechnicalError("Failed to close AMQP connection", error)).mapOk(() => void 0);
|
|
115
91
|
}
|
|
116
92
|
};
|
|
117
93
|
|
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { Options } from "amqplib";
|
|
2
|
-
import {
|
|
3
|
-
import { Result } from "@swan-io/boxed";
|
|
2
|
+
import { ContractDefinition, InferPublisherNames, PublisherDefinition } from "@amqp-contract/contract";
|
|
3
|
+
import { Future, Result } from "@swan-io/boxed";
|
|
4
|
+
import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
5
|
+
import { AmqpConnectionManagerOptions, ConnectionUrl } from "amqp-connection-manager";
|
|
4
6
|
|
|
5
7
|
//#region src/errors.d.ts
|
|
6
8
|
/**
|
|
@@ -26,49 +28,66 @@ declare class MessageValidationError extends ClientError {
|
|
|
26
28
|
constructor(publisherName: string, issues: unknown);
|
|
27
29
|
}
|
|
28
30
|
//#endregion
|
|
31
|
+
//#region src/types.d.ts
|
|
32
|
+
/**
|
|
33
|
+
* Infer the TypeScript type from a schema
|
|
34
|
+
*/
|
|
35
|
+
type InferSchemaInput<TSchema extends StandardSchemaV1> = TSchema extends StandardSchemaV1<infer TInput> ? TInput : never;
|
|
36
|
+
/**
|
|
37
|
+
* Infer publisher message input type
|
|
38
|
+
*/
|
|
39
|
+
type PublisherInferInput<TPublisher extends PublisherDefinition> = InferSchemaInput<TPublisher["message"]["payload"]>;
|
|
40
|
+
/**
|
|
41
|
+
* Infer all publishers from contract
|
|
42
|
+
*/
|
|
43
|
+
type InferPublishers<TContract extends ContractDefinition> = NonNullable<TContract["publishers"]>;
|
|
44
|
+
/**
|
|
45
|
+
* Get specific publisher definition from contract
|
|
46
|
+
*/
|
|
47
|
+
type InferPublisher<TContract extends ContractDefinition, TName extends InferPublisherNames<TContract>> = InferPublishers<TContract>[TName];
|
|
48
|
+
/**
|
|
49
|
+
* Infer publisher input type (message payload) for a specific publisher in a contract
|
|
50
|
+
*/
|
|
51
|
+
type ClientInferPublisherInput<TContract extends ContractDefinition, TName extends InferPublisherNames<TContract>> = PublisherInferInput<InferPublisher<TContract, TName>>;
|
|
52
|
+
//#endregion
|
|
29
53
|
//#region src/client.d.ts
|
|
30
54
|
/**
|
|
31
55
|
* Options for creating a client
|
|
32
56
|
*/
|
|
33
|
-
|
|
57
|
+
type CreateClientOptions<TContract extends ContractDefinition> = {
|
|
34
58
|
contract: TContract;
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
* Options for publishing a message
|
|
39
|
-
*/
|
|
40
|
-
interface PublishOptions {
|
|
41
|
-
routingKey?: string;
|
|
42
|
-
options?: Options.Publish;
|
|
43
|
-
}
|
|
59
|
+
urls: ConnectionUrl[];
|
|
60
|
+
connectionOptions?: AmqpConnectionManagerOptions | undefined;
|
|
61
|
+
};
|
|
44
62
|
/**
|
|
45
63
|
* Type-safe AMQP client for publishing messages
|
|
46
64
|
*/
|
|
47
65
|
declare class TypedAmqpClient<TContract extends ContractDefinition> {
|
|
48
66
|
private readonly contract;
|
|
49
|
-
private readonly
|
|
50
|
-
private channel;
|
|
51
|
-
private connection;
|
|
67
|
+
private readonly amqpClient;
|
|
52
68
|
private constructor();
|
|
53
69
|
/**
|
|
54
|
-
* Create a type-safe AMQP client from a contract
|
|
55
|
-
*
|
|
70
|
+
* Create a type-safe AMQP client from a contract.
|
|
71
|
+
*
|
|
72
|
+
* Connection management (including automatic reconnection) is handled internally
|
|
73
|
+
* by amqp-connection-manager via the {@link AmqpClient}. The client establishes
|
|
74
|
+
* infrastructure asynchronously in the background once the connection is ready.
|
|
56
75
|
*/
|
|
57
|
-
static create<TContract extends ContractDefinition>(
|
|
76
|
+
static create<TContract extends ContractDefinition>({
|
|
77
|
+
contract,
|
|
78
|
+
urls,
|
|
79
|
+
connectionOptions
|
|
80
|
+
}: CreateClientOptions<TContract>): TypedAmqpClient<TContract>;
|
|
58
81
|
/**
|
|
59
82
|
* Publish a message using a defined publisher
|
|
60
83
|
* Returns Result.Ok(true) on success, or Result.Error with specific error on failure
|
|
61
84
|
*/
|
|
62
|
-
publish<TName extends InferPublisherNames<TContract>>(publisherName: TName, message: ClientInferPublisherInput<TContract, TName>, options?:
|
|
85
|
+
publish<TName extends InferPublisherNames<TContract>>(publisherName: TName, message: ClientInferPublisherInput<TContract, TName>, options?: Options.Publish): Future<Result<boolean, TechnicalError | MessageValidationError>>;
|
|
63
86
|
/**
|
|
64
87
|
* Close the channel and connection
|
|
65
88
|
*/
|
|
66
|
-
close():
|
|
67
|
-
/**
|
|
68
|
-
* Connect to AMQP broker
|
|
69
|
-
*/
|
|
70
|
-
private init;
|
|
89
|
+
close(): Future<Result<void, TechnicalError>>;
|
|
71
90
|
}
|
|
72
91
|
//#endregion
|
|
73
|
-
export { type CreateClientOptions, MessageValidationError,
|
|
92
|
+
export { type ClientInferPublisherInput, type CreateClientOptions, MessageValidationError, TechnicalError, TypedAmqpClient };
|
|
74
93
|
//# sourceMappingURL=index.d.cts.map
|
package/dist/index.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/errors.ts","../src/client.ts"],"sourcesContent":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/errors.ts","../src/types.ts","../src/client.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;uBAGe,WAAA,SAAoB,KAAA;;;;;AAkBnC;AAaA;cAba,cAAA,SAAuB,WAAA;;;AChBH;;;;AAMC,cDuBrB,sBAAA,SAA+B,WAAA,CCvBV;EAK7B,SAAA,aAAmB,EAAA,MAAA;EAAoB,SAAA,MAAA,EAAA,OAAA;EAC1C,WAAA,CAAA,aAAA,EAAA,MAAA,EAAA,MAAA,EAAA,OAAA;;;;;;;KAPG,iCAAiC,oBACpC,gBAAgB;;;ADUlB;AAaA,KClBK,mBDkBQ,CAAA,mBClB+B,mBDkBW,CAAA,GClBY,gBDkBZ,CCjBrD,UDiBqD,CAAA,SAAA,CAAA,CAAA,SAAA,CAAA,CAAA;;;;AC7BtB,KAkB5B,eAbA,CAAA,kBAakC,kBAblB,CAAA,GAawC,WAbxC,CAaoD,SAbpD,CAAA,YAAA,CAAA,CAAA;;;;KAkBhB,cAjB6B,CAAA,kBAkBd,kBAlBc,EAAA,cAmBlB,mBAnBkB,CAmBE,SAnBF,CAAA,CAAA,GAoB9B,eApB8B,CAoBd,SApBc,CAAA,CAoBH,KApBG,CAAA;AAAA;;;AAKiC,KAoBvD,yBApBuD,CAAA,kBAqB/C,kBArB+C,EAAA,cAsBnD,mBAtBmD,CAsB/B,SAtB+B,CAAA,CAAA,GAuB/D,mBAvB+D,CAuB3C,cAvB2C,CAuB5B,SAvB4B,EAuBjB,KAvBiB,CAAA,CAAA;;;;;ADKnE;AAaa,KEvBD,mBFuBwB,CAAA,kBEvBc,kBFuBK,CAAA,GAAA;YEtB3C;QACJ;sBACc;ADTW,CAAA;;;;AAMC,cCSrB,eDTqB,CAAA,kBCSa,kBDTb,CAAA,CAAA;EAK7B,iBAAA,QAAmB;EAAoB,iBAAA,UAAA;EAC1C,QAAA,WAAA,CAAA;EADiE;;AAAgB;;;;;EAY9E,OAAA,MAAA,CAAA,kBCK6B,kBDLf,CAAA,CAAA;IAAA,QAAA;IAAA,IAAA;IAAA;EAAA,CAAA,ECSd,mBDTc,CCSM,SDTN,CAAA,CAAA,ECSmB,eDTnB,CCSmC,SDTnC,CAAA;EACC;;;;EAEhB,OAAA,CAAA,cCcoB,mBDdpB,CCcwC,SDdxC,CAAA,CAAA,CAAA,aAAA,ECee,KDff,EAAA,OAAA,ECgBS,yBDhBT,CCgBmC,SDhBnC,ECgB8C,KDhB9C,CAAA,EAAA,OAAA,CAAA,ECiBU,OAAA,CAAQ,ODjBlB,CAAA,ECkBC,MDlBD,CCkBQ,MDlBR,CAAA,OAAA,ECkBwB,cDlBxB,GCkByC,sBDlBzC,CAAA,CAAA;EAA2B;;AAK/B;EACoB,KAAA,CAAA,CAAA,EC0ET,MD1ES,CC0EF,MD1EE,CAAA,IAAA,EC0EW,cD1EX,CAAA,CAAA"}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { Future, Result } from "@swan-io/boxed";
|
|
1
2
|
import { Options } from "amqplib";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
3
|
+
import { ContractDefinition, InferPublisherNames, PublisherDefinition } from "@amqp-contract/contract";
|
|
4
|
+
import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
5
|
+
import { AmqpConnectionManagerOptions, ConnectionUrl } from "amqp-connection-manager";
|
|
4
6
|
|
|
5
7
|
//#region src/errors.d.ts
|
|
6
8
|
/**
|
|
@@ -26,49 +28,66 @@ declare class MessageValidationError extends ClientError {
|
|
|
26
28
|
constructor(publisherName: string, issues: unknown);
|
|
27
29
|
}
|
|
28
30
|
//#endregion
|
|
31
|
+
//#region src/types.d.ts
|
|
32
|
+
/**
|
|
33
|
+
* Infer the TypeScript type from a schema
|
|
34
|
+
*/
|
|
35
|
+
type InferSchemaInput<TSchema extends StandardSchemaV1> = TSchema extends StandardSchemaV1<infer TInput> ? TInput : never;
|
|
36
|
+
/**
|
|
37
|
+
* Infer publisher message input type
|
|
38
|
+
*/
|
|
39
|
+
type PublisherInferInput<TPublisher extends PublisherDefinition> = InferSchemaInput<TPublisher["message"]["payload"]>;
|
|
40
|
+
/**
|
|
41
|
+
* Infer all publishers from contract
|
|
42
|
+
*/
|
|
43
|
+
type InferPublishers<TContract extends ContractDefinition> = NonNullable<TContract["publishers"]>;
|
|
44
|
+
/**
|
|
45
|
+
* Get specific publisher definition from contract
|
|
46
|
+
*/
|
|
47
|
+
type InferPublisher<TContract extends ContractDefinition, TName extends InferPublisherNames<TContract>> = InferPublishers<TContract>[TName];
|
|
48
|
+
/**
|
|
49
|
+
* Infer publisher input type (message payload) for a specific publisher in a contract
|
|
50
|
+
*/
|
|
51
|
+
type ClientInferPublisherInput<TContract extends ContractDefinition, TName extends InferPublisherNames<TContract>> = PublisherInferInput<InferPublisher<TContract, TName>>;
|
|
52
|
+
//#endregion
|
|
29
53
|
//#region src/client.d.ts
|
|
30
54
|
/**
|
|
31
55
|
* Options for creating a client
|
|
32
56
|
*/
|
|
33
|
-
|
|
57
|
+
type CreateClientOptions<TContract extends ContractDefinition> = {
|
|
34
58
|
contract: TContract;
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
* Options for publishing a message
|
|
39
|
-
*/
|
|
40
|
-
interface PublishOptions {
|
|
41
|
-
routingKey?: string;
|
|
42
|
-
options?: Options.Publish;
|
|
43
|
-
}
|
|
59
|
+
urls: ConnectionUrl[];
|
|
60
|
+
connectionOptions?: AmqpConnectionManagerOptions | undefined;
|
|
61
|
+
};
|
|
44
62
|
/**
|
|
45
63
|
* Type-safe AMQP client for publishing messages
|
|
46
64
|
*/
|
|
47
65
|
declare class TypedAmqpClient<TContract extends ContractDefinition> {
|
|
48
66
|
private readonly contract;
|
|
49
|
-
private readonly
|
|
50
|
-
private channel;
|
|
51
|
-
private connection;
|
|
67
|
+
private readonly amqpClient;
|
|
52
68
|
private constructor();
|
|
53
69
|
/**
|
|
54
|
-
* Create a type-safe AMQP client from a contract
|
|
55
|
-
*
|
|
70
|
+
* Create a type-safe AMQP client from a contract.
|
|
71
|
+
*
|
|
72
|
+
* Connection management (including automatic reconnection) is handled internally
|
|
73
|
+
* by amqp-connection-manager via the {@link AmqpClient}. The client establishes
|
|
74
|
+
* infrastructure asynchronously in the background once the connection is ready.
|
|
56
75
|
*/
|
|
57
|
-
static create<TContract extends ContractDefinition>(
|
|
76
|
+
static create<TContract extends ContractDefinition>({
|
|
77
|
+
contract,
|
|
78
|
+
urls,
|
|
79
|
+
connectionOptions
|
|
80
|
+
}: CreateClientOptions<TContract>): TypedAmqpClient<TContract>;
|
|
58
81
|
/**
|
|
59
82
|
* Publish a message using a defined publisher
|
|
60
83
|
* Returns Result.Ok(true) on success, or Result.Error with specific error on failure
|
|
61
84
|
*/
|
|
62
|
-
publish<TName extends InferPublisherNames<TContract>>(publisherName: TName, message: ClientInferPublisherInput<TContract, TName>, options?:
|
|
85
|
+
publish<TName extends InferPublisherNames<TContract>>(publisherName: TName, message: ClientInferPublisherInput<TContract, TName>, options?: Options.Publish): Future<Result<boolean, TechnicalError | MessageValidationError>>;
|
|
63
86
|
/**
|
|
64
87
|
* Close the channel and connection
|
|
65
88
|
*/
|
|
66
|
-
close():
|
|
67
|
-
/**
|
|
68
|
-
* Connect to AMQP broker
|
|
69
|
-
*/
|
|
70
|
-
private init;
|
|
89
|
+
close(): Future<Result<void, TechnicalError>>;
|
|
71
90
|
}
|
|
72
91
|
//#endregion
|
|
73
|
-
export { type CreateClientOptions, MessageValidationError,
|
|
92
|
+
export { type ClientInferPublisherInput, type CreateClientOptions, MessageValidationError, TechnicalError, TypedAmqpClient };
|
|
74
93
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/errors.ts","../src/client.ts"],"sourcesContent":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/errors.ts","../src/types.ts","../src/client.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;uBAGe,WAAA,SAAoB,KAAA;;;;;AAkBnC;AAaA;cAba,cAAA,SAAuB,WAAA;;;AChBH;;;;AAMC,cDuBrB,sBAAA,SAA+B,WAAA,CCvBV;EAK7B,SAAA,aAAmB,EAAA,MAAA;EAAoB,SAAA,MAAA,EAAA,OAAA;EAC1C,WAAA,CAAA,aAAA,EAAA,MAAA,EAAA,MAAA,EAAA,OAAA;;;;;;;KAPG,iCAAiC,oBACpC,gBAAgB;;;ADUlB;AAaA,KClBK,mBDkBQ,CAAA,mBClB+B,mBDkBW,CAAA,GClBY,gBDkBZ,CCjBrD,UDiBqD,CAAA,SAAA,CAAA,CAAA,SAAA,CAAA,CAAA;;;;AC7BtB,KAkB5B,eAbA,CAAA,kBAakC,kBAblB,CAAA,GAawC,WAbxC,CAaoD,SAbpD,CAAA,YAAA,CAAA,CAAA;;;;KAkBhB,cAjB6B,CAAA,kBAkBd,kBAlBc,EAAA,cAmBlB,mBAnBkB,CAmBE,SAnBF,CAAA,CAAA,GAoB9B,eApB8B,CAoBd,SApBc,CAAA,CAoBH,KApBG,CAAA;AAAA;;;AAKiC,KAoBvD,yBApBuD,CAAA,kBAqB/C,kBArB+C,EAAA,cAsBnD,mBAtBmD,CAsB/B,SAtB+B,CAAA,CAAA,GAuB/D,mBAvB+D,CAuB3C,cAvB2C,CAuB5B,SAvB4B,EAuBjB,KAvBiB,CAAA,CAAA;;;;;ADKnE;AAaa,KEvBD,mBFuBwB,CAAA,kBEvBc,kBFuBK,CAAA,GAAA;YEtB3C;QACJ;sBACc;ADTW,CAAA;;;;AAMC,cCSrB,eDTqB,CAAA,kBCSa,kBDTb,CAAA,CAAA;EAK7B,iBAAA,QAAmB;EAAoB,iBAAA,UAAA;EAC1C,QAAA,WAAA,CAAA;EADiE;;AAAgB;;;;;EAY9E,OAAA,MAAA,CAAA,kBCK6B,kBDLf,CAAA,CAAA;IAAA,QAAA;IAAA,IAAA;IAAA;EAAA,CAAA,ECSd,mBDTc,CCSM,SDTN,CAAA,CAAA,ECSmB,eDTnB,CCSmC,SDTnC,CAAA;EACC;;;;EAEhB,OAAA,CAAA,cCcoB,mBDdpB,CCcwC,SDdxC,CAAA,CAAA,CAAA,aAAA,ECee,KDff,EAAA,OAAA,ECgBS,yBDhBT,CCgBmC,SDhBnC,ECgB8C,KDhB9C,CAAA,EAAA,OAAA,CAAA,ECiBU,OAAA,CAAQ,ODjBlB,CAAA,ECkBC,MDlBD,CCkBQ,MDlBR,CAAA,OAAA,ECkBwB,cDlBxB,GCkByC,sBDlBzC,CAAA,CAAA;EAA2B;;AAK/B;EACoB,KAAA,CAAA,CAAA,EC0ET,MD1ES,CC0EF,MD1EE,CAAA,IAAA,EC0EW,cD1EX,CAAA,CAAA"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { Future, Result } from "@swan-io/boxed";
|
|
2
|
+
import { AmqpClient } from "@amqp-contract/core";
|
|
3
3
|
|
|
4
4
|
//#region src/errors.ts
|
|
5
5
|
/**
|
|
@@ -42,76 +42,52 @@ var MessageValidationError = class extends ClientError {
|
|
|
42
42
|
* Type-safe AMQP client for publishing messages
|
|
43
43
|
*/
|
|
44
44
|
var TypedAmqpClient = class TypedAmqpClient {
|
|
45
|
-
|
|
46
|
-
connection = null;
|
|
47
|
-
constructor(contract, connectionOptions) {
|
|
45
|
+
constructor(contract, amqpClient) {
|
|
48
46
|
this.contract = contract;
|
|
49
|
-
this.
|
|
47
|
+
this.amqpClient = amqpClient;
|
|
50
48
|
}
|
|
51
49
|
/**
|
|
52
|
-
* Create a type-safe AMQP client from a contract
|
|
53
|
-
*
|
|
50
|
+
* Create a type-safe AMQP client from a contract.
|
|
51
|
+
*
|
|
52
|
+
* Connection management (including automatic reconnection) is handled internally
|
|
53
|
+
* by amqp-connection-manager via the {@link AmqpClient}. The client establishes
|
|
54
|
+
* infrastructure asynchronously in the background once the connection is ready.
|
|
54
55
|
*/
|
|
55
|
-
static
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
static create({ contract, urls, connectionOptions }) {
|
|
57
|
+
return new TypedAmqpClient(contract, new AmqpClient(contract, {
|
|
58
|
+
urls,
|
|
59
|
+
connectionOptions
|
|
60
|
+
}));
|
|
59
61
|
}
|
|
60
62
|
/**
|
|
61
63
|
* Publish a message using a defined publisher
|
|
62
64
|
* Returns Result.Ok(true) on success, or Result.Error with specific error on failure
|
|
63
65
|
*/
|
|
64
66
|
publish(publisherName, message, options) {
|
|
65
|
-
if (!this.channel) throw new Error("Client not initialized. Create the client using TypedAmqpClient.create() to establish a connection.");
|
|
66
67
|
const publishers = this.contract.publishers;
|
|
67
|
-
if (!publishers)
|
|
68
|
+
if (!publishers) return Future.value(Result.Error(new TechnicalError("No publishers defined in contract")));
|
|
68
69
|
const publisher = publishers[publisherName];
|
|
69
|
-
if (!publisher
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
70
|
+
if (!publisher) return Future.value(Result.Error(new TechnicalError(`Publisher "${String(publisherName)}" not found in contract`)));
|
|
71
|
+
const validateMessage = () => {
|
|
72
|
+
const validationResult = publisher.message.payload["~standard"].validate(message);
|
|
73
|
+
return Future.fromPromise(validationResult instanceof Promise ? validationResult : Promise.resolve(validationResult)).mapError((error) => new TechnicalError(`Validation failed`, error)).mapOkToResult((validation) => {
|
|
74
|
+
if (validation.issues) return Result.Error(new MessageValidationError(String(publisherName), validation.issues));
|
|
75
|
+
return Result.Ok(validation.value);
|
|
76
|
+
});
|
|
77
|
+
};
|
|
78
|
+
const publishMessage = (validatedMessage) => {
|
|
79
|
+
return Future.fromPromise(this.amqpClient.channel.publish(publisher.exchange.name, publisher.routingKey ?? "", validatedMessage, options)).mapError((error) => new TechnicalError(`Failed to publish message`, error)).mapOkToResult((published) => {
|
|
80
|
+
if (!published) return Result.Error(new TechnicalError(`Failed to publish message for publisher "${String(publisherName)}": Channel rejected the message (buffer full or other channel issue)`));
|
|
81
|
+
return Result.Ok(published);
|
|
82
|
+
});
|
|
83
|
+
};
|
|
84
|
+
return validateMessage().flatMapOk((validatedMessage) => publishMessage(validatedMessage));
|
|
79
85
|
}
|
|
80
86
|
/**
|
|
81
87
|
* Close the channel and connection
|
|
82
88
|
*/
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
await this.channel.close();
|
|
86
|
-
this.channel = null;
|
|
87
|
-
}
|
|
88
|
-
if (this.connection) {
|
|
89
|
-
await this.connection.close();
|
|
90
|
-
this.connection = null;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
/**
|
|
94
|
-
* Connect to AMQP broker
|
|
95
|
-
*/
|
|
96
|
-
async init() {
|
|
97
|
-
this.connection = await connect(this.connectionOptions);
|
|
98
|
-
this.channel = await this.connection.createChannel();
|
|
99
|
-
if (this.contract.exchanges) for (const exchange of Object.values(this.contract.exchanges)) await this.channel.assertExchange(exchange.name, exchange.type, {
|
|
100
|
-
durable: exchange.durable,
|
|
101
|
-
autoDelete: exchange.autoDelete,
|
|
102
|
-
internal: exchange.internal,
|
|
103
|
-
arguments: exchange.arguments
|
|
104
|
-
});
|
|
105
|
-
if (this.contract.queues) for (const queue of Object.values(this.contract.queues)) await this.channel.assertQueue(queue.name, {
|
|
106
|
-
durable: queue.durable,
|
|
107
|
-
exclusive: queue.exclusive,
|
|
108
|
-
autoDelete: queue.autoDelete,
|
|
109
|
-
arguments: queue.arguments
|
|
110
|
-
});
|
|
111
|
-
if (this.contract.bindings) {
|
|
112
|
-
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);
|
|
113
|
-
else if (binding.type === "exchange") await this.channel.bindExchange(binding.destination, binding.source, binding.routingKey ?? "", binding.arguments);
|
|
114
|
-
}
|
|
89
|
+
close() {
|
|
90
|
+
return Future.fromPromise(this.amqpClient.close()).mapError((error) => new TechnicalError("Failed to close AMQP connection", error)).mapOk(() => void 0);
|
|
115
91
|
}
|
|
116
92
|
};
|
|
117
93
|
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["cause?: unknown","publisherName: string","issues: unknown","contract: TContract","
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["cause?: unknown","publisherName: string","issues: unknown","contract: TContract","amqpClient: AmqpClient"],"sources":["../src/errors.ts","../src/client.ts"],"sourcesContent":["/**\n * Base error class for client errors\n */\nabstract class ClientError extends Error {\n protected constructor(message: string) {\n super(message);\n this.name = \"ClientError\";\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 that cannot be prevented by TypeScript\n * This includes validation failures and AMQP channel issues\n */\nexport class TechnicalError extends ClientError {\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 ClientError {\n constructor(\n public readonly publisherName: string,\n public readonly issues: unknown,\n ) {\n super(`Message validation failed for publisher \"${publisherName}\"`);\n this.name = \"MessageValidationError\";\n }\n}\n","import type { Options } from \"amqplib\";\nimport type { ContractDefinition, InferPublisherNames } from \"@amqp-contract/contract\";\nimport { Future, Result } from \"@swan-io/boxed\";\nimport { MessageValidationError, TechnicalError } from \"./errors.js\";\nimport type { ClientInferPublisherInput } from \"./types.js\";\nimport { AmqpClient } from \"@amqp-contract/core\";\nimport type { AmqpConnectionManagerOptions, ConnectionUrl } from \"amqp-connection-manager\";\n\n/**\n * Options for creating a client\n */\nexport type CreateClientOptions<TContract extends ContractDefinition> = {\n contract: TContract;\n urls: ConnectionUrl[];\n connectionOptions?: AmqpConnectionManagerOptions | undefined;\n};\n\n/**\n * Type-safe AMQP client for publishing messages\n */\nexport class TypedAmqpClient<TContract extends ContractDefinition> {\n private constructor(\n private readonly contract: TContract,\n private readonly amqpClient: AmqpClient,\n ) {}\n\n /**\n * Create a type-safe AMQP client from a contract.\n *\n * Connection management (including automatic reconnection) is handled internally\n * by amqp-connection-manager via the {@link AmqpClient}. The client establishes\n * infrastructure asynchronously in the background once the connection is ready.\n */\n static create<TContract extends ContractDefinition>({\n contract,\n urls,\n connectionOptions,\n }: CreateClientOptions<TContract>): TypedAmqpClient<TContract> {\n return new TypedAmqpClient(contract, new AmqpClient(contract, { urls, connectionOptions }));\n }\n\n /**\n * Publish a message using a defined publisher\n * Returns Result.Ok(true) on success, or Result.Error with specific error on failure\n */\n publish<TName extends InferPublisherNames<TContract>>(\n publisherName: TName,\n message: ClientInferPublisherInput<TContract, TName>,\n options?: Options.Publish,\n ): Future<Result<boolean, TechnicalError | MessageValidationError>> {\n const publishers = this.contract.publishers;\n if (!publishers) {\n return Future.value(Result.Error(new TechnicalError(\"No publishers defined in contract\")));\n }\n\n const publisher = publishers[publisherName as string];\n if (!publisher) {\n return Future.value(\n Result.Error(\n new TechnicalError(`Publisher \"${String(publisherName)}\" not found in contract`),\n ),\n );\n }\n\n const validateMessage = () => {\n const validationResult = publisher.message.payload[\"~standard\"].validate(message);\n return Future.fromPromise(\n validationResult instanceof Promise ? validationResult : Promise.resolve(validationResult),\n )\n .mapError((error) => new TechnicalError(`Validation failed`, error))\n .mapOkToResult((validation) => {\n if (validation.issues) {\n return Result.Error(\n new MessageValidationError(String(publisherName), validation.issues),\n );\n }\n\n return Result.Ok(validation.value);\n });\n };\n\n const publishMessage = (validatedMessage: unknown) => {\n return Future.fromPromise(\n this.amqpClient.channel.publish(\n publisher.exchange.name,\n publisher.routingKey ?? \"\",\n validatedMessage,\n options,\n ),\n )\n .mapError((error) => new TechnicalError(`Failed to publish message`, error))\n .mapOkToResult((published) => {\n if (!published) {\n return Result.Error(\n new TechnicalError(\n `Failed to publish message for publisher \"${String(publisherName)}\": Channel rejected the message (buffer full or other channel issue)`,\n ),\n );\n }\n\n return Result.Ok(published);\n });\n };\n\n // Validate message using schema\n return validateMessage().flatMapOk((validatedMessage) => publishMessage(validatedMessage));\n }\n\n /**\n * Close the channel and connection\n */\n close(): Future<Result<void, TechnicalError>> {\n return Future.fromPromise(this.amqpClient.close())\n .mapError((error) => new TechnicalError(\"Failed to close AMQP connection\", error))\n .mapOk(() => undefined);\n }\n}\n"],"mappings":";;;;;;;AAGA,IAAe,cAAf,cAAmC,MAAM;CACvC,AAAU,YAAY,SAAiB;AACrC,QAAM,QAAQ;AACd,OAAK,OAAO;EAEZ,MAAM,mBAAmB;AAGzB,MAAI,OAAO,iBAAiB,sBAAsB,WAChD,kBAAiB,kBAAkB,MAAM,KAAK,YAAY;;;;;;;AAShE,IAAa,iBAAb,cAAoC,YAAY;CAC9C,YACE,SACA,AAAyBA,OACzB;AACA,QAAM,QAAQ;EAFW;AAGzB,OAAK,OAAO;;;;;;AAOhB,IAAa,yBAAb,cAA4C,YAAY;CACtD,YACE,AAAgBC,eAChB,AAAgBC,QAChB;AACA,QAAM,4CAA4C,cAAc,GAAG;EAHnD;EACA;AAGhB,OAAK,OAAO;;;;;;;;;ACpBhB,IAAa,kBAAb,MAAa,gBAAsD;CACjE,AAAQ,YACN,AAAiBC,UACjB,AAAiBC,YACjB;EAFiB;EACA;;;;;;;;;CAUnB,OAAO,OAA6C,EAClD,UACA,MACA,qBAC6D;AAC7D,SAAO,IAAI,gBAAgB,UAAU,IAAI,WAAW,UAAU;GAAE;GAAM;GAAmB,CAAC,CAAC;;;;;;CAO7F,QACE,eACA,SACA,SACkE;EAClE,MAAM,aAAa,KAAK,SAAS;AACjC,MAAI,CAAC,WACH,QAAO,OAAO,MAAM,OAAO,MAAM,IAAI,eAAe,oCAAoC,CAAC,CAAC;EAG5F,MAAM,YAAY,WAAW;AAC7B,MAAI,CAAC,UACH,QAAO,OAAO,MACZ,OAAO,MACL,IAAI,eAAe,cAAc,OAAO,cAAc,CAAC,yBAAyB,CACjF,CACF;EAGH,MAAM,wBAAwB;GAC5B,MAAM,mBAAmB,UAAU,QAAQ,QAAQ,aAAa,SAAS,QAAQ;AACjF,UAAO,OAAO,YACZ,4BAA4B,UAAU,mBAAmB,QAAQ,QAAQ,iBAAiB,CAC3F,CACE,UAAU,UAAU,IAAI,eAAe,qBAAqB,MAAM,CAAC,CACnE,eAAe,eAAe;AAC7B,QAAI,WAAW,OACb,QAAO,OAAO,MACZ,IAAI,uBAAuB,OAAO,cAAc,EAAE,WAAW,OAAO,CACrE;AAGH,WAAO,OAAO,GAAG,WAAW,MAAM;KAClC;;EAGN,MAAM,kBAAkB,qBAA8B;AACpD,UAAO,OAAO,YACZ,KAAK,WAAW,QAAQ,QACtB,UAAU,SAAS,MACnB,UAAU,cAAc,IACxB,kBACA,QACD,CACF,CACE,UAAU,UAAU,IAAI,eAAe,6BAA6B,MAAM,CAAC,CAC3E,eAAe,cAAc;AAC5B,QAAI,CAAC,UACH,QAAO,OAAO,MACZ,IAAI,eACF,4CAA4C,OAAO,cAAc,CAAC,sEACnE,CACF;AAGH,WAAO,OAAO,GAAG,UAAU;KAC3B;;AAIN,SAAO,iBAAiB,CAAC,WAAW,qBAAqB,eAAe,iBAAiB,CAAC;;;;;CAM5F,QAA8C;AAC5C,SAAO,OAAO,YAAY,KAAK,WAAW,OAAO,CAAC,CAC/C,UAAU,UAAU,IAAI,eAAe,mCAAmC,MAAM,CAAC,CACjF,YAAY,OAAU"}
|
package/docs/index.md
ADDED
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
**@amqp-contract/client**
|
|
2
|
+
|
|
3
|
+
***
|
|
4
|
+
|
|
5
|
+
# @amqp-contract/client
|
|
6
|
+
|
|
7
|
+
## Classes
|
|
8
|
+
|
|
9
|
+
### MessageValidationError
|
|
10
|
+
|
|
11
|
+
Defined in: [packages/client/src/errors.ts:35](https://github.com/btravers/amqp-contract/blob/f0945f098387fd3a6a40beac8cbe2ed7a4de210a/packages/client/src/errors.ts#L35)
|
|
12
|
+
|
|
13
|
+
Error thrown when message validation fails
|
|
14
|
+
|
|
15
|
+
#### Extends
|
|
16
|
+
|
|
17
|
+
- `ClientError`
|
|
18
|
+
|
|
19
|
+
#### Constructors
|
|
20
|
+
|
|
21
|
+
##### Constructor
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
new MessageValidationError(publisherName, issues): MessageValidationError;
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Defined in: [packages/client/src/errors.ts:36](https://github.com/btravers/amqp-contract/blob/f0945f098387fd3a6a40beac8cbe2ed7a4de210a/packages/client/src/errors.ts#L36)
|
|
28
|
+
|
|
29
|
+
###### Parameters
|
|
30
|
+
|
|
31
|
+
| Parameter | Type |
|
|
32
|
+
| ------ | ------ |
|
|
33
|
+
| `publisherName` | `string` |
|
|
34
|
+
| `issues` | `unknown` |
|
|
35
|
+
|
|
36
|
+
###### Returns
|
|
37
|
+
|
|
38
|
+
[`MessageValidationError`](#messagevalidationerror)
|
|
39
|
+
|
|
40
|
+
###### Overrides
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
ClientError.constructor
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
#### Properties
|
|
47
|
+
|
|
48
|
+
| Property | Modifier | Type | Description | Inherited from | Defined in |
|
|
49
|
+
| ------ | ------ | ------ | ------ | ------ | ------ |
|
|
50
|
+
| <a id="cause"></a> `cause?` | `public` | `unknown` | - | `ClientError.cause` | node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es2022.error.d.ts:26 |
|
|
51
|
+
| <a id="issues"></a> `issues` | `readonly` | `unknown` | - | - | [packages/client/src/errors.ts:38](https://github.com/btravers/amqp-contract/blob/f0945f098387fd3a6a40beac8cbe2ed7a4de210a/packages/client/src/errors.ts#L38) |
|
|
52
|
+
| <a id="message"></a> `message` | `public` | `string` | - | `ClientError.message` | node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es5.d.ts:1077 |
|
|
53
|
+
| <a id="name"></a> `name` | `public` | `string` | - | `ClientError.name` | node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es5.d.ts:1076 |
|
|
54
|
+
| <a id="publishername"></a> `publisherName` | `readonly` | `string` | - | - | [packages/client/src/errors.ts:37](https://github.com/btravers/amqp-contract/blob/f0945f098387fd3a6a40beac8cbe2ed7a4de210a/packages/client/src/errors.ts#L37) |
|
|
55
|
+
| <a id="stack"></a> `stack?` | `public` | `string` | - | `ClientError.stack` | node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es5.d.ts:1078 |
|
|
56
|
+
| <a id="stacktracelimit"></a> `stackTraceLimit` | `static` | `number` | The `Error.stackTraceLimit` property specifies the number of stack frames collected by a stack trace (whether generated by `new Error().stack` or `Error.captureStackTrace(obj)`). The default value is `10` but may be set to any valid JavaScript number. Changes will affect any stack trace captured _after_ the value has been changed. If set to a non-number value, or set to a negative number, stack traces will not capture any frames. | `ClientError.stackTraceLimit` | node\_modules/.pnpm/@types+node@25.0.3/node\_modules/@types/node/globals.d.ts:67 |
|
|
57
|
+
|
|
58
|
+
#### Methods
|
|
59
|
+
|
|
60
|
+
##### captureStackTrace()
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
static captureStackTrace(targetObject, constructorOpt?): void;
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Defined in: node\_modules/.pnpm/@types+node@25.0.3/node\_modules/@types/node/globals.d.ts:51
|
|
67
|
+
|
|
68
|
+
Creates a `.stack` property on `targetObject`, which when accessed returns
|
|
69
|
+
a string representing the location in the code at which
|
|
70
|
+
`Error.captureStackTrace()` was called.
|
|
71
|
+
|
|
72
|
+
```js
|
|
73
|
+
const myObject = {};
|
|
74
|
+
Error.captureStackTrace(myObject);
|
|
75
|
+
myObject.stack; // Similar to `new Error().stack`
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
The first line of the trace will be prefixed with
|
|
79
|
+
`${myObject.name}: ${myObject.message}`.
|
|
80
|
+
|
|
81
|
+
The optional `constructorOpt` argument accepts a function. If given, all frames
|
|
82
|
+
above `constructorOpt`, including `constructorOpt`, will be omitted from the
|
|
83
|
+
generated stack trace.
|
|
84
|
+
|
|
85
|
+
The `constructorOpt` argument is useful for hiding implementation
|
|
86
|
+
details of error generation from the user. For instance:
|
|
87
|
+
|
|
88
|
+
```js
|
|
89
|
+
function a() {
|
|
90
|
+
b();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function b() {
|
|
94
|
+
c();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function c() {
|
|
98
|
+
// Create an error without stack trace to avoid calculating the stack trace twice.
|
|
99
|
+
const { stackTraceLimit } = Error;
|
|
100
|
+
Error.stackTraceLimit = 0;
|
|
101
|
+
const error = new Error();
|
|
102
|
+
Error.stackTraceLimit = stackTraceLimit;
|
|
103
|
+
|
|
104
|
+
// Capture the stack trace above function b
|
|
105
|
+
Error.captureStackTrace(error, b); // Neither function c, nor b is included in the stack trace
|
|
106
|
+
throw error;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
a();
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
###### Parameters
|
|
113
|
+
|
|
114
|
+
| Parameter | Type |
|
|
115
|
+
| ------ | ------ |
|
|
116
|
+
| `targetObject` | `object` |
|
|
117
|
+
| `constructorOpt?` | `Function` |
|
|
118
|
+
|
|
119
|
+
###### Returns
|
|
120
|
+
|
|
121
|
+
`void`
|
|
122
|
+
|
|
123
|
+
###### Inherited from
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
ClientError.captureStackTrace
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
##### prepareStackTrace()
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
static prepareStackTrace(err, stackTraces): any;
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Defined in: node\_modules/.pnpm/@types+node@25.0.3/node\_modules/@types/node/globals.d.ts:55
|
|
136
|
+
|
|
137
|
+
###### Parameters
|
|
138
|
+
|
|
139
|
+
| Parameter | Type |
|
|
140
|
+
| ------ | ------ |
|
|
141
|
+
| `err` | `Error` |
|
|
142
|
+
| `stackTraces` | `CallSite`[] |
|
|
143
|
+
|
|
144
|
+
###### Returns
|
|
145
|
+
|
|
146
|
+
`any`
|
|
147
|
+
|
|
148
|
+
###### See
|
|
149
|
+
|
|
150
|
+
https://v8.dev/docs/stack-trace-api#customizing-stack-traces
|
|
151
|
+
|
|
152
|
+
###### Inherited from
|
|
153
|
+
|
|
154
|
+
```ts
|
|
155
|
+
ClientError.prepareStackTrace
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
***
|
|
159
|
+
|
|
160
|
+
### TechnicalError
|
|
161
|
+
|
|
162
|
+
Defined in: [packages/client/src/errors.ts:22](https://github.com/btravers/amqp-contract/blob/f0945f098387fd3a6a40beac8cbe2ed7a4de210a/packages/client/src/errors.ts#L22)
|
|
163
|
+
|
|
164
|
+
Error for technical/runtime failures that cannot be prevented by TypeScript
|
|
165
|
+
This includes validation failures and AMQP channel issues
|
|
166
|
+
|
|
167
|
+
#### Extends
|
|
168
|
+
|
|
169
|
+
- `ClientError`
|
|
170
|
+
|
|
171
|
+
#### Constructors
|
|
172
|
+
|
|
173
|
+
##### Constructor
|
|
174
|
+
|
|
175
|
+
```ts
|
|
176
|
+
new TechnicalError(message, cause?): TechnicalError;
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Defined in: [packages/client/src/errors.ts:23](https://github.com/btravers/amqp-contract/blob/f0945f098387fd3a6a40beac8cbe2ed7a4de210a/packages/client/src/errors.ts#L23)
|
|
180
|
+
|
|
181
|
+
###### Parameters
|
|
182
|
+
|
|
183
|
+
| Parameter | Type |
|
|
184
|
+
| ------ | ------ |
|
|
185
|
+
| `message` | `string` |
|
|
186
|
+
| `cause?` | `unknown` |
|
|
187
|
+
|
|
188
|
+
###### Returns
|
|
189
|
+
|
|
190
|
+
[`TechnicalError`](#technicalerror)
|
|
191
|
+
|
|
192
|
+
###### Overrides
|
|
193
|
+
|
|
194
|
+
```ts
|
|
195
|
+
ClientError.constructor
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
#### Properties
|
|
199
|
+
|
|
200
|
+
| Property | Modifier | Type | Description | Inherited from | Defined in |
|
|
201
|
+
| ------ | ------ | ------ | ------ | ------ | ------ |
|
|
202
|
+
| <a id="cause-1"></a> `cause?` | `readonly` | `unknown` | - | `ClientError.cause` | [packages/client/src/errors.ts:25](https://github.com/btravers/amqp-contract/blob/f0945f098387fd3a6a40beac8cbe2ed7a4de210a/packages/client/src/errors.ts#L25) |
|
|
203
|
+
| <a id="message-1"></a> `message` | `public` | `string` | - | `ClientError.message` | node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es5.d.ts:1077 |
|
|
204
|
+
| <a id="name-1"></a> `name` | `public` | `string` | - | `ClientError.name` | node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es5.d.ts:1076 |
|
|
205
|
+
| <a id="stack-1"></a> `stack?` | `public` | `string` | - | `ClientError.stack` | node\_modules/.pnpm/typescript@5.9.3/node\_modules/typescript/lib/lib.es5.d.ts:1078 |
|
|
206
|
+
| <a id="stacktracelimit-1"></a> `stackTraceLimit` | `static` | `number` | The `Error.stackTraceLimit` property specifies the number of stack frames collected by a stack trace (whether generated by `new Error().stack` or `Error.captureStackTrace(obj)`). The default value is `10` but may be set to any valid JavaScript number. Changes will affect any stack trace captured _after_ the value has been changed. If set to a non-number value, or set to a negative number, stack traces will not capture any frames. | `ClientError.stackTraceLimit` | node\_modules/.pnpm/@types+node@25.0.3/node\_modules/@types/node/globals.d.ts:67 |
|
|
207
|
+
|
|
208
|
+
#### Methods
|
|
209
|
+
|
|
210
|
+
##### captureStackTrace()
|
|
211
|
+
|
|
212
|
+
```ts
|
|
213
|
+
static captureStackTrace(targetObject, constructorOpt?): void;
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Defined in: node\_modules/.pnpm/@types+node@25.0.3/node\_modules/@types/node/globals.d.ts:51
|
|
217
|
+
|
|
218
|
+
Creates a `.stack` property on `targetObject`, which when accessed returns
|
|
219
|
+
a string representing the location in the code at which
|
|
220
|
+
`Error.captureStackTrace()` was called.
|
|
221
|
+
|
|
222
|
+
```js
|
|
223
|
+
const myObject = {};
|
|
224
|
+
Error.captureStackTrace(myObject);
|
|
225
|
+
myObject.stack; // Similar to `new Error().stack`
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
The first line of the trace will be prefixed with
|
|
229
|
+
`${myObject.name}: ${myObject.message}`.
|
|
230
|
+
|
|
231
|
+
The optional `constructorOpt` argument accepts a function. If given, all frames
|
|
232
|
+
above `constructorOpt`, including `constructorOpt`, will be omitted from the
|
|
233
|
+
generated stack trace.
|
|
234
|
+
|
|
235
|
+
The `constructorOpt` argument is useful for hiding implementation
|
|
236
|
+
details of error generation from the user. For instance:
|
|
237
|
+
|
|
238
|
+
```js
|
|
239
|
+
function a() {
|
|
240
|
+
b();
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function b() {
|
|
244
|
+
c();
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function c() {
|
|
248
|
+
// Create an error without stack trace to avoid calculating the stack trace twice.
|
|
249
|
+
const { stackTraceLimit } = Error;
|
|
250
|
+
Error.stackTraceLimit = 0;
|
|
251
|
+
const error = new Error();
|
|
252
|
+
Error.stackTraceLimit = stackTraceLimit;
|
|
253
|
+
|
|
254
|
+
// Capture the stack trace above function b
|
|
255
|
+
Error.captureStackTrace(error, b); // Neither function c, nor b is included in the stack trace
|
|
256
|
+
throw error;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
a();
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
###### Parameters
|
|
263
|
+
|
|
264
|
+
| Parameter | Type |
|
|
265
|
+
| ------ | ------ |
|
|
266
|
+
| `targetObject` | `object` |
|
|
267
|
+
| `constructorOpt?` | `Function` |
|
|
268
|
+
|
|
269
|
+
###### Returns
|
|
270
|
+
|
|
271
|
+
`void`
|
|
272
|
+
|
|
273
|
+
###### Inherited from
|
|
274
|
+
|
|
275
|
+
```ts
|
|
276
|
+
ClientError.captureStackTrace
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
##### prepareStackTrace()
|
|
280
|
+
|
|
281
|
+
```ts
|
|
282
|
+
static prepareStackTrace(err, stackTraces): any;
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
Defined in: node\_modules/.pnpm/@types+node@25.0.3/node\_modules/@types/node/globals.d.ts:55
|
|
286
|
+
|
|
287
|
+
###### Parameters
|
|
288
|
+
|
|
289
|
+
| Parameter | Type |
|
|
290
|
+
| ------ | ------ |
|
|
291
|
+
| `err` | `Error` |
|
|
292
|
+
| `stackTraces` | `CallSite`[] |
|
|
293
|
+
|
|
294
|
+
###### Returns
|
|
295
|
+
|
|
296
|
+
`any`
|
|
297
|
+
|
|
298
|
+
###### See
|
|
299
|
+
|
|
300
|
+
https://v8.dev/docs/stack-trace-api#customizing-stack-traces
|
|
301
|
+
|
|
302
|
+
###### Inherited from
|
|
303
|
+
|
|
304
|
+
```ts
|
|
305
|
+
ClientError.prepareStackTrace
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
***
|
|
309
|
+
|
|
310
|
+
### TypedAmqpClient
|
|
311
|
+
|
|
312
|
+
Defined in: [packages/client/src/client.ts:21](https://github.com/btravers/amqp-contract/blob/f0945f098387fd3a6a40beac8cbe2ed7a4de210a/packages/client/src/client.ts#L21)
|
|
313
|
+
|
|
314
|
+
Type-safe AMQP client for publishing messages
|
|
315
|
+
|
|
316
|
+
#### Type Parameters
|
|
317
|
+
|
|
318
|
+
| Type Parameter |
|
|
319
|
+
| ------ |
|
|
320
|
+
| `TContract` *extends* `ContractDefinition` |
|
|
321
|
+
|
|
322
|
+
#### Methods
|
|
323
|
+
|
|
324
|
+
##### close()
|
|
325
|
+
|
|
326
|
+
```ts
|
|
327
|
+
close(): Future<Result<void, TechnicalError>>;
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
Defined in: [packages/client/src/client.ts:112](https://github.com/btravers/amqp-contract/blob/f0945f098387fd3a6a40beac8cbe2ed7a4de210a/packages/client/src/client.ts#L112)
|
|
331
|
+
|
|
332
|
+
Close the channel and connection
|
|
333
|
+
|
|
334
|
+
###### Returns
|
|
335
|
+
|
|
336
|
+
`Future`\<`Result`\<`void`, [`TechnicalError`](#technicalerror)\>\>
|
|
337
|
+
|
|
338
|
+
##### publish()
|
|
339
|
+
|
|
340
|
+
```ts
|
|
341
|
+
publish<TName>(
|
|
342
|
+
publisherName,
|
|
343
|
+
message,
|
|
344
|
+
options?): Future<Result<boolean,
|
|
345
|
+
| TechnicalError
|
|
346
|
+
| MessageValidationError>>;
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
Defined in: [packages/client/src/client.ts:46](https://github.com/btravers/amqp-contract/blob/f0945f098387fd3a6a40beac8cbe2ed7a4de210a/packages/client/src/client.ts#L46)
|
|
350
|
+
|
|
351
|
+
Publish a message using a defined publisher
|
|
352
|
+
Returns Result.Ok(true) on success, or Result.Error with specific error on failure
|
|
353
|
+
|
|
354
|
+
###### Type Parameters
|
|
355
|
+
|
|
356
|
+
| Type Parameter |
|
|
357
|
+
| ------ |
|
|
358
|
+
| `TName` *extends* `string` \| `number` \| `symbol` |
|
|
359
|
+
|
|
360
|
+
###### Parameters
|
|
361
|
+
|
|
362
|
+
| Parameter | Type |
|
|
363
|
+
| ------ | ------ |
|
|
364
|
+
| `publisherName` | `TName` |
|
|
365
|
+
| `message` | [`ClientInferPublisherInput`](#clientinferpublisherinput)\<`TContract`, `TName`\> |
|
|
366
|
+
| `options?` | `Publish` |
|
|
367
|
+
|
|
368
|
+
###### Returns
|
|
369
|
+
|
|
370
|
+
`Future`\<`Result`\<`boolean`,
|
|
371
|
+
\| [`TechnicalError`](#technicalerror)
|
|
372
|
+
\| [`MessageValidationError`](#messagevalidationerror)\>\>
|
|
373
|
+
|
|
374
|
+
##### create()
|
|
375
|
+
|
|
376
|
+
```ts
|
|
377
|
+
static create<TContract>(__namedParameters): TypedAmqpClient<TContract>;
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
Defined in: [packages/client/src/client.ts:34](https://github.com/btravers/amqp-contract/blob/f0945f098387fd3a6a40beac8cbe2ed7a4de210a/packages/client/src/client.ts#L34)
|
|
381
|
+
|
|
382
|
+
Create a type-safe AMQP client from a contract.
|
|
383
|
+
|
|
384
|
+
Connection management (including automatic reconnection) is handled internally
|
|
385
|
+
by amqp-connection-manager via the AmqpClient. The client establishes
|
|
386
|
+
infrastructure asynchronously in the background once the connection is ready.
|
|
387
|
+
|
|
388
|
+
###### Type Parameters
|
|
389
|
+
|
|
390
|
+
| Type Parameter |
|
|
391
|
+
| ------ |
|
|
392
|
+
| `TContract` *extends* `ContractDefinition` |
|
|
393
|
+
|
|
394
|
+
###### Parameters
|
|
395
|
+
|
|
396
|
+
| Parameter | Type |
|
|
397
|
+
| ------ | ------ |
|
|
398
|
+
| `__namedParameters` | [`CreateClientOptions`](#createclientoptions)\<`TContract`\> |
|
|
399
|
+
|
|
400
|
+
###### Returns
|
|
401
|
+
|
|
402
|
+
[`TypedAmqpClient`](#typedamqpclient)\<`TContract`\>
|
|
403
|
+
|
|
404
|
+
## Type Aliases
|
|
405
|
+
|
|
406
|
+
### ClientInferPublisherInput
|
|
407
|
+
|
|
408
|
+
```ts
|
|
409
|
+
type ClientInferPublisherInput<TContract, TName> = PublisherInferInput<InferPublisher<TContract, TName>>;
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
Defined in: [packages/client/src/types.ts:37](https://github.com/btravers/amqp-contract/blob/f0945f098387fd3a6a40beac8cbe2ed7a4de210a/packages/client/src/types.ts#L37)
|
|
413
|
+
|
|
414
|
+
Infer publisher input type (message payload) for a specific publisher in a contract
|
|
415
|
+
|
|
416
|
+
#### Type Parameters
|
|
417
|
+
|
|
418
|
+
| Type Parameter |
|
|
419
|
+
| ------ |
|
|
420
|
+
| `TContract` *extends* `ContractDefinition` |
|
|
421
|
+
| `TName` *extends* `InferPublisherNames`\<`TContract`\> |
|
|
422
|
+
|
|
423
|
+
***
|
|
424
|
+
|
|
425
|
+
### CreateClientOptions
|
|
426
|
+
|
|
427
|
+
```ts
|
|
428
|
+
type CreateClientOptions<TContract> = object;
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
Defined in: [packages/client/src/client.ts:12](https://github.com/btravers/amqp-contract/blob/f0945f098387fd3a6a40beac8cbe2ed7a4de210a/packages/client/src/client.ts#L12)
|
|
432
|
+
|
|
433
|
+
Options for creating a client
|
|
434
|
+
|
|
435
|
+
#### Type Parameters
|
|
436
|
+
|
|
437
|
+
| Type Parameter |
|
|
438
|
+
| ------ |
|
|
439
|
+
| `TContract` *extends* `ContractDefinition` |
|
|
440
|
+
|
|
441
|
+
#### Properties
|
|
442
|
+
|
|
443
|
+
| Property | Type | Defined in |
|
|
444
|
+
| ------ | ------ | ------ |
|
|
445
|
+
| <a id="connectionoptions"></a> `connectionOptions?` | `AmqpConnectionManagerOptions` | [packages/client/src/client.ts:15](https://github.com/btravers/amqp-contract/blob/f0945f098387fd3a6a40beac8cbe2ed7a4de210a/packages/client/src/client.ts#L15) |
|
|
446
|
+
| <a id="contract"></a> `contract` | `TContract` | [packages/client/src/client.ts:13](https://github.com/btravers/amqp-contract/blob/f0945f098387fd3a6a40beac8cbe2ed7a4de210a/packages/client/src/client.ts#L13) |
|
|
447
|
+
| <a id="urls"></a> `urls` | `ConnectionUrl`[] | [packages/client/src/client.ts:14](https://github.com/btravers/amqp-contract/blob/f0945f098387fd3a6a40beac8cbe2ed7a4de210a/packages/client/src/client.ts#L14) |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@amqp-contract/client",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Client utilities for publishing messages using amqp-contract",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"amqp",
|
|
@@ -38,29 +38,34 @@
|
|
|
38
38
|
"module": "./dist/index.mjs",
|
|
39
39
|
"types": "./dist/index.d.mts",
|
|
40
40
|
"files": [
|
|
41
|
-
"dist"
|
|
41
|
+
"dist",
|
|
42
|
+
"docs"
|
|
42
43
|
],
|
|
43
44
|
"dependencies": {
|
|
45
|
+
"@standard-schema/spec": "1.1.0",
|
|
44
46
|
"@swan-io/boxed": "3.2.1",
|
|
45
|
-
"@amqp-contract/contract": "0.1
|
|
47
|
+
"@amqp-contract/contract": "0.2.1",
|
|
48
|
+
"@amqp-contract/core": "0.2.1"
|
|
46
49
|
},
|
|
47
50
|
"devDependencies": {
|
|
48
51
|
"@types/amqplib": "0.10.8",
|
|
49
52
|
"@types/node": "25.0.3",
|
|
50
53
|
"@vitest/coverage-v8": "4.0.16",
|
|
54
|
+
"amqp-connection-manager": "5.0.0",
|
|
51
55
|
"amqplib": "0.10.9",
|
|
52
56
|
"tsdown": "0.18.2",
|
|
57
|
+
"typedoc": "0.28.3",
|
|
58
|
+
"typedoc-plugin-markdown": "4.9.0",
|
|
53
59
|
"typescript": "5.9.3",
|
|
54
60
|
"vitest": "4.0.16",
|
|
55
61
|
"zod": "4.2.1",
|
|
56
|
-
"@amqp-contract/testing": "0.1
|
|
57
|
-
"@amqp-contract/tsconfig": "0.0.0"
|
|
58
|
-
|
|
59
|
-
"peerDependencies": {
|
|
60
|
-
"amqplib": ">=0.10.0"
|
|
62
|
+
"@amqp-contract/testing": "0.2.1",
|
|
63
|
+
"@amqp-contract/tsconfig": "0.0.0",
|
|
64
|
+
"@amqp-contract/typedoc": "0.0.1"
|
|
61
65
|
},
|
|
62
66
|
"scripts": {
|
|
63
67
|
"build": "tsdown src/index.ts --format cjs,esm --dts --clean",
|
|
68
|
+
"build:docs": "typedoc",
|
|
64
69
|
"dev": "tsdown src/index.ts --format cjs,esm --dts --watch",
|
|
65
70
|
"test": "vitest run --project unit",
|
|
66
71
|
"test:integration": "vitest run --project integration",
|