@zdavison/matador 2.0.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/cli.ts +453 -0
- package/dist/checkpoint/context.d.ts +59 -0
- package/dist/checkpoint/context.d.ts.map +1 -0
- package/dist/checkpoint/context.js +140 -0
- package/dist/checkpoint/context.test.d.ts +2 -0
- package/dist/checkpoint/context.test.d.ts.map +1 -0
- package/dist/checkpoint/context.test.js +424 -0
- package/dist/checkpoint/index.d.ts +7 -0
- package/dist/checkpoint/index.d.ts.map +1 -0
- package/dist/checkpoint/index.js +6 -0
- package/dist/checkpoint/stores/memory.d.ts +29 -0
- package/dist/checkpoint/stores/memory.d.ts.map +1 -0
- package/dist/checkpoint/stores/memory.js +39 -0
- package/dist/checkpoint/stores/noop.d.ts +14 -0
- package/dist/checkpoint/stores/noop.d.ts.map +1 -0
- package/dist/checkpoint/stores/noop.js +18 -0
- package/dist/checkpoint/stores/stores.test.d.ts +2 -0
- package/dist/checkpoint/stores/stores.test.d.ts.map +1 -0
- package/dist/checkpoint/stores/stores.test.js +146 -0
- package/dist/checkpoint/types.d.ts +119 -0
- package/dist/checkpoint/types.d.ts.map +1 -0
- package/dist/checkpoint/types.js +1 -0
- package/dist/codec/codec.d.ts +29 -0
- package/dist/codec/codec.d.ts.map +1 -0
- package/dist/codec/codec.js +15 -0
- package/dist/codec/header-aware-codec.d.ts +36 -0
- package/dist/codec/header-aware-codec.d.ts.map +1 -0
- package/dist/codec/header-aware-codec.js +1 -0
- package/dist/codec/index.d.ts +6 -0
- package/dist/codec/index.d.ts.map +1 -0
- package/dist/codec/index.js +3 -0
- package/dist/codec/json-codec.d.ts +13 -0
- package/dist/codec/json-codec.d.ts.map +1 -0
- package/dist/codec/json-codec.js +64 -0
- package/dist/codec/rabbitmq-codec.d.ts +28 -0
- package/dist/codec/rabbitmq-codec.d.ts.map +1 -0
- package/dist/codec/rabbitmq-codec.js +242 -0
- package/dist/codec/rabbitmq-codec.test.d.ts +2 -0
- package/dist/codec/rabbitmq-codec.test.d.ts.map +1 -0
- package/dist/codec/rabbitmq-codec.test.js +433 -0
- package/dist/core/fanout.d.ts +59 -0
- package/dist/core/fanout.d.ts.map +1 -0
- package/dist/core/fanout.js +121 -0
- package/dist/core/fanout.test.d.ts +2 -0
- package/dist/core/fanout.test.d.ts.map +1 -0
- package/dist/core/fanout.test.js +1055 -0
- package/dist/core/index.d.ts +7 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +3 -0
- package/dist/core/matador.d.ts +133 -0
- package/dist/core/matador.d.ts.map +1 -0
- package/dist/core/matador.js +222 -0
- package/dist/core/matador.test.d.ts +2 -0
- package/dist/core/matador.test.d.ts.map +1 -0
- package/dist/core/matador.test.js +446 -0
- package/dist/core/shutdown.d.ts +78 -0
- package/dist/core/shutdown.d.ts.map +1 -0
- package/dist/core/shutdown.js +111 -0
- package/dist/core/shutdown.test.d.ts +2 -0
- package/dist/core/shutdown.test.d.ts.map +1 -0
- package/dist/core/shutdown.test.js +524 -0
- package/dist/errors/checkpoint-errors.d.ts +30 -0
- package/dist/errors/checkpoint-errors.d.ts.map +1 -0
- package/dist/errors/checkpoint-errors.js +49 -0
- package/dist/errors/has-description.d.ts +18 -0
- package/dist/errors/has-description.d.ts.map +1 -0
- package/dist/errors/has-description.js +9 -0
- package/dist/errors/index.d.ts +6 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +22 -0
- package/dist/errors/matador-errors.d.ts +183 -0
- package/dist/errors/matador-errors.d.ts.map +1 -0
- package/dist/errors/matador-errors.js +376 -0
- package/dist/errors/retry-errors.d.ts +67 -0
- package/dist/errors/retry-errors.d.ts.map +1 -0
- package/dist/errors/retry-errors.js +108 -0
- package/dist/hooks/index.d.ts +4 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +2 -0
- package/dist/hooks/safe-hooks.d.ts +34 -0
- package/dist/hooks/safe-hooks.d.ts.map +1 -0
- package/dist/hooks/safe-hooks.js +135 -0
- package/dist/hooks/types.d.ts +189 -0
- package/dist/hooks/types.d.ts.map +1 -0
- package/dist/hooks/types.js +9 -0
- package/dist/index.cjs +3156 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +23 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -0
- package/dist/pipeline/index.d.ts +3 -0
- package/dist/pipeline/index.d.ts.map +1 -0
- package/dist/pipeline/index.js +1 -0
- package/dist/pipeline/pipeline.d.ts +60 -0
- package/dist/pipeline/pipeline.d.ts.map +1 -0
- package/dist/pipeline/pipeline.js +261 -0
- package/dist/pipeline/pipeline.test.d.ts +2 -0
- package/dist/pipeline/pipeline.test.d.ts.map +1 -0
- package/dist/pipeline/pipeline.test.js +1065 -0
- package/dist/retry/index.d.ts +4 -0
- package/dist/retry/index.d.ts.map +1 -0
- package/dist/retry/index.js +1 -0
- package/dist/retry/policy.d.ts +43 -0
- package/dist/retry/policy.d.ts.map +1 -0
- package/dist/retry/policy.js +1 -0
- package/dist/retry/standard-policy.d.ts +44 -0
- package/dist/retry/standard-policy.d.ts.map +1 -0
- package/dist/retry/standard-policy.js +102 -0
- package/dist/retry/standard-policy.test.d.ts +2 -0
- package/dist/retry/standard-policy.test.d.ts.map +1 -0
- package/dist/retry/standard-policy.test.js +190 -0
- package/dist/schema/index.d.ts +4 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/index.js +2 -0
- package/dist/schema/registry.d.ts +63 -0
- package/dist/schema/registry.d.ts.map +1 -0
- package/dist/schema/registry.js +171 -0
- package/dist/schema/registry.test.d.ts +2 -0
- package/dist/schema/registry.test.d.ts.map +1 -0
- package/dist/schema/registry.test.js +278 -0
- package/dist/schema/types.d.ts +158 -0
- package/dist/schema/types.d.ts.map +1 -0
- package/dist/schema/types.js +74 -0
- package/dist/schema/types.test.d.ts +2 -0
- package/dist/schema/types.test.d.ts.map +1 -0
- package/dist/schema/types.test.js +243 -0
- package/dist/topology/builder.d.ts +66 -0
- package/dist/topology/builder.d.ts.map +1 -0
- package/dist/topology/builder.js +156 -0
- package/dist/topology/builder.test.d.ts +2 -0
- package/dist/topology/builder.test.d.ts.map +1 -0
- package/dist/topology/builder.test.js +222 -0
- package/dist/topology/index.d.ts +5 -0
- package/dist/topology/index.d.ts.map +1 -0
- package/dist/topology/index.js +2 -0
- package/dist/topology/types.d.ts +76 -0
- package/dist/topology/types.d.ts.map +1 -0
- package/dist/topology/types.js +18 -0
- package/dist/transport/capabilities.d.ts +66 -0
- package/dist/transport/capabilities.d.ts.map +1 -0
- package/dist/transport/capabilities.js +18 -0
- package/dist/transport/connection-manager.d.ts +95 -0
- package/dist/transport/connection-manager.d.ts.map +1 -0
- package/dist/transport/connection-manager.js +144 -0
- package/dist/transport/index.d.ts +11 -0
- package/dist/transport/index.d.ts.map +1 -0
- package/dist/transport/index.js +5 -0
- package/dist/transport/local/local-transport.d.ts +62 -0
- package/dist/transport/local/local-transport.d.ts.map +1 -0
- package/dist/transport/local/local-transport.js +241 -0
- package/dist/transport/local/local-transport.test.d.ts +2 -0
- package/dist/transport/local/local-transport.test.d.ts.map +1 -0
- package/dist/transport/local/local-transport.test.js +192 -0
- package/dist/transport/multi/multi-transport.d.ts +94 -0
- package/dist/transport/multi/multi-transport.d.ts.map +1 -0
- package/dist/transport/multi/multi-transport.js +184 -0
- package/dist/transport/multi/multi-transport.test.d.ts +2 -0
- package/dist/transport/multi/multi-transport.test.d.ts.map +1 -0
- package/dist/transport/multi/multi-transport.test.js +236 -0
- package/dist/transport/rabbitmq/rabbitmq-transport.d.ts +73 -0
- package/dist/transport/rabbitmq/rabbitmq-transport.d.ts.map +1 -0
- package/dist/transport/rabbitmq/rabbitmq-transport.js +518 -0
- package/dist/transport/transport.d.ts +152 -0
- package/dist/transport/transport.d.ts.map +1 -0
- package/dist/transport/transport.js +1 -0
- package/dist/types/common.d.ts +41 -0
- package/dist/types/common.d.ts.map +1 -0
- package/dist/types/common.js +12 -0
- package/dist/types/envelope.d.ts +82 -0
- package/dist/types/envelope.d.ts.map +1 -0
- package/dist/types/envelope.js +36 -0
- package/dist/types/event.d.ts +96 -0
- package/dist/types/event.d.ts.map +1 -0
- package/dist/types/event.js +26 -0
- package/dist/types/event.test.d.ts +2 -0
- package/dist/types/event.test.d.ts.map +1 -0
- package/dist/types/event.test.js +130 -0
- package/dist/types/index.d.ts +9 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +4 -0
- package/dist/types/subscriber.d.ts +207 -0
- package/dist/types/subscriber.d.ts.map +1 -0
- package/dist/types/subscriber.js +99 -0
- package/examples/config.ts +126 -0
- package/examples/event.ts +26 -0
- package/examples/order-event.json +19 -0
- package/package.json +66 -0
- package/src/checkpoint/context.test.ts +510 -0
- package/src/checkpoint/context.ts +213 -0
- package/src/checkpoint/index.ts +30 -0
- package/src/checkpoint/stores/memory.ts +47 -0
- package/src/checkpoint/stores/noop.ts +22 -0
- package/src/checkpoint/stores/stores.test.ts +177 -0
- package/src/checkpoint/types.ts +147 -0
- package/src/codec/codec.ts +42 -0
- package/src/codec/header-aware-codec.ts +41 -0
- package/src/codec/index.ts +11 -0
- package/src/codec/json-codec.ts +69 -0
- package/src/codec/rabbitmq-codec.test.ts +516 -0
- package/src/codec/rabbitmq-codec.ts +336 -0
- package/src/core/fanout.test.ts +1351 -0
- package/src/core/fanout.ts +184 -0
- package/src/core/index.ts +12 -0
- package/src/core/matador.test.ts +575 -0
- package/src/core/matador.ts +344 -0
- package/src/core/shutdown.test.ts +853 -0
- package/src/core/shutdown.ts +165 -0
- package/src/errors/checkpoint-errors.ts +62 -0
- package/src/errors/has-description.ts +25 -0
- package/src/errors/index.ts +57 -0
- package/src/errors/matador-errors.ts +477 -0
- package/src/errors/retry-errors.ts +134 -0
- package/src/hooks/index.ts +15 -0
- package/src/hooks/safe-hooks.ts +223 -0
- package/src/hooks/types.ts +248 -0
- package/src/index.ts +231 -0
- package/src/pipeline/index.ts +2 -0
- package/src/pipeline/pipeline.test.ts +1317 -0
- package/src/pipeline/pipeline.ts +371 -0
- package/src/retry/index.ts +4 -0
- package/src/retry/policy.ts +46 -0
- package/src/retry/standard-policy.test.ts +282 -0
- package/src/retry/standard-policy.ts +156 -0
- package/src/schema/index.ts +16 -0
- package/src/schema/registry.test.ts +339 -0
- package/src/schema/registry.ts +226 -0
- package/src/schema/types.test.ts +281 -0
- package/src/schema/types.ts +217 -0
- package/src/topology/builder.test.ts +275 -0
- package/src/topology/builder.ts +199 -0
- package/src/topology/index.ts +15 -0
- package/src/topology/types.ts +109 -0
- package/src/transport/capabilities.ts +88 -0
- package/src/transport/connection-manager.ts +218 -0
- package/src/transport/index.ts +42 -0
- package/src/transport/local/local-transport.test.ts +262 -0
- package/src/transport/local/local-transport.ts +327 -0
- package/src/transport/multi/multi-transport.test.ts +320 -0
- package/src/transport/multi/multi-transport.ts +294 -0
- package/src/transport/rabbitmq/rabbitmq-transport.ts +753 -0
- package/src/transport/transport.ts +200 -0
- package/src/types/common.ts +53 -0
- package/src/types/envelope.ts +152 -0
- package/src/types/event.test.ts +157 -0
- package/src/types/event.ts +118 -0
- package/src/types/index.ts +52 -0
- package/src/types/subscriber.ts +310 -0
- package/test/e2e/multi-transport.e2e.test.ts +236 -0
- package/test/e2e/rabbitmq-transport.e2e.test.ts +327 -0
- package/test/e2e/transport-compliance.e2e.test.ts +505 -0
- package/test/integration/matador.integration.test.ts +634 -0
- package/tsconfig.json +29 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/tsup.config.ts +13 -0
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base class for all Matador errors.
|
|
3
|
+
* All errors have a unique name that appears in monitoring tools (e.g., DataDog)
|
|
4
|
+
* and a description explaining the error and actions to resolve it.
|
|
5
|
+
*
|
|
6
|
+
* The `name` property is automatically set to the class name for proper
|
|
7
|
+
* identification in error monitoring and logging systems.
|
|
8
|
+
*/
|
|
9
|
+
export abstract class MatadorError extends Error {
|
|
10
|
+
/**
|
|
11
|
+
* Error class name (e.g., "NotStartedError", "TransportNotConnectedError").
|
|
12
|
+
* This is preserved during serialization for error monitoring tools.
|
|
13
|
+
*/
|
|
14
|
+
declare readonly name: string;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Human-readable description explaining what went wrong and
|
|
18
|
+
* what ACTION the user should take to resolve the issue.
|
|
19
|
+
*/
|
|
20
|
+
abstract readonly description: string;
|
|
21
|
+
|
|
22
|
+
constructor(message: string) {
|
|
23
|
+
super(message);
|
|
24
|
+
this.name = this.constructor.name;
|
|
25
|
+
// Ensure name is preserved when serialized
|
|
26
|
+
Object.defineProperty(this, 'name', {
|
|
27
|
+
value: this.constructor.name,
|
|
28
|
+
enumerable: true,
|
|
29
|
+
configurable: false,
|
|
30
|
+
writable: false,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Returns a serializable representation for logging/monitoring.
|
|
36
|
+
*/
|
|
37
|
+
toJSON(): Record<string, unknown> {
|
|
38
|
+
return {
|
|
39
|
+
name: this.name,
|
|
40
|
+
message: this.message,
|
|
41
|
+
description: this.description,
|
|
42
|
+
stack: this.stack,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ============================================================================
|
|
48
|
+
// Lifecycle Errors
|
|
49
|
+
// ============================================================================
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Thrown when attempting to use Matador before calling start().
|
|
53
|
+
*/
|
|
54
|
+
export class NotStartedError extends MatadorError {
|
|
55
|
+
readonly description =
|
|
56
|
+
'Matador has not been started. ' +
|
|
57
|
+
'ACTION: Call matador.start() before dispatching events or performing other operations. ' +
|
|
58
|
+
'Ensure start() completes successfully before using other methods.';
|
|
59
|
+
|
|
60
|
+
constructor(operation = 'operation') {
|
|
61
|
+
super(
|
|
62
|
+
`Cannot perform ${operation}: Matador has not been started. Call start() first.`,
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Thrown when attempting to dispatch events during shutdown.
|
|
69
|
+
*/
|
|
70
|
+
export class ShutdownInProgressError extends MatadorError {
|
|
71
|
+
readonly description =
|
|
72
|
+
'Matador is shutting down and will not accept new events. ' +
|
|
73
|
+
'ACTION: Do not dispatch events after calling shutdown(). ' +
|
|
74
|
+
'If you need to send events, do so before initiating shutdown. ' +
|
|
75
|
+
'Consider implementing a pre-shutdown event flush if needed.';
|
|
76
|
+
|
|
77
|
+
constructor() {
|
|
78
|
+
super('Cannot dispatch events: Matador is shutting down.');
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ============================================================================
|
|
83
|
+
// Transport Errors
|
|
84
|
+
// ============================================================================
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Thrown when a transport operation is attempted but the transport is not connected.
|
|
88
|
+
*/
|
|
89
|
+
export class TransportNotConnectedError extends MatadorError {
|
|
90
|
+
readonly description =
|
|
91
|
+
'The transport is not connected to the message broker. ' +
|
|
92
|
+
'ACTION: Ensure the transport is connected by calling transport.connect() or matador.start(). ' +
|
|
93
|
+
'Check that the broker (e.g., RabbitMQ) is running and accessible. ' +
|
|
94
|
+
'Verify connection settings (URL, credentials, network access).';
|
|
95
|
+
|
|
96
|
+
constructor(
|
|
97
|
+
public readonly transportName: string,
|
|
98
|
+
operation = 'operation',
|
|
99
|
+
) {
|
|
100
|
+
super(
|
|
101
|
+
`Cannot perform ${operation}: Transport "${transportName}" is not connected. ` +
|
|
102
|
+
`Ensure connect() was called and the broker is accessible.`,
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Thrown when the transport has been closed (during shutdown).
|
|
109
|
+
*/
|
|
110
|
+
export class TransportClosedError extends MatadorError {
|
|
111
|
+
readonly description =
|
|
112
|
+
'The transport has been closed and will not accept new operations. ' +
|
|
113
|
+
'ACTION: This typically occurs during application shutdown. ' +
|
|
114
|
+
'If unexpected, check for early shutdown triggers. ' +
|
|
115
|
+
'Events sent after transport closure will be lost.';
|
|
116
|
+
|
|
117
|
+
constructor(public readonly transportName: string) {
|
|
118
|
+
super(
|
|
119
|
+
`Transport "${transportName}" has been closed and will not accept new operations.`,
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Thrown when all transports in a fallback chain fail.
|
|
126
|
+
*/
|
|
127
|
+
export class AllTransportsFailedError extends MatadorError {
|
|
128
|
+
readonly description =
|
|
129
|
+
'All transports failed to send the message. ' +
|
|
130
|
+
'ACTION: Check the health of all configured transports (primary and fallbacks). ' +
|
|
131
|
+
'Review the errors array for specific failure reasons. ' +
|
|
132
|
+
'Ensure at least one transport is properly configured and reachable. ' +
|
|
133
|
+
'Consider adding a LocalTransport as a last-resort fallback.';
|
|
134
|
+
|
|
135
|
+
constructor(
|
|
136
|
+
public readonly queue: string,
|
|
137
|
+
public readonly errors: readonly Error[],
|
|
138
|
+
) {
|
|
139
|
+
super(
|
|
140
|
+
`All transports failed to send message to queue "${queue}". ` +
|
|
141
|
+
`Errors: ${errors.map((e) => e.message).join('; ')}`,
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Thrown when sending a message to the transport fails.
|
|
148
|
+
*/
|
|
149
|
+
export class TransportSendError extends MatadorError {
|
|
150
|
+
readonly description =
|
|
151
|
+
'Failed to send a message through the transport. ' +
|
|
152
|
+
'ACTION: Check the underlying error for details. Common causes: ' +
|
|
153
|
+
'(1) Transport disconnected during send, ' +
|
|
154
|
+
'(2) Network issues between application and broker, ' +
|
|
155
|
+
'(3) Broker rejected the message (size, permissions, queue limits). ' +
|
|
156
|
+
'The message was NOT delivered and should be retried or logged.';
|
|
157
|
+
|
|
158
|
+
constructor(
|
|
159
|
+
public readonly queue: string,
|
|
160
|
+
public readonly cause: Error,
|
|
161
|
+
) {
|
|
162
|
+
super(`Failed to send message to queue "${queue}": ${cause.message}`);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
override toJSON(): Record<string, unknown> {
|
|
166
|
+
return {
|
|
167
|
+
...super.toJSON(),
|
|
168
|
+
queue: this.queue,
|
|
169
|
+
cause: {
|
|
170
|
+
name: this.cause.name,
|
|
171
|
+
message: this.cause.message,
|
|
172
|
+
},
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Thrown when delayed messages are requested but the plugin is not available.
|
|
179
|
+
*/
|
|
180
|
+
export class DelayedMessagesNotSupportedError extends MatadorError {
|
|
181
|
+
readonly description =
|
|
182
|
+
'Delayed messages were requested but the transport does not support them. ' +
|
|
183
|
+
'ACTION: For RabbitMQ, install the rabbitmq_delayed_message_exchange plugin. ' +
|
|
184
|
+
'Run: rabbitmq-plugins enable rabbitmq_delayed_message_exchange ' +
|
|
185
|
+
'Then restart RabbitMQ and reconnect. ' +
|
|
186
|
+
'Alternatively, remove delayMs from your event options if delays are not required.';
|
|
187
|
+
|
|
188
|
+
constructor(public readonly transportName: string) {
|
|
189
|
+
super(
|
|
190
|
+
`Delayed messages require the RabbitMQ delayed message exchange plugin. ` +
|
|
191
|
+
`Install rabbitmq_delayed_message_exchange or remove delayMs from event options.`,
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// ============================================================================
|
|
197
|
+
// Schema & Configuration Errors
|
|
198
|
+
// ============================================================================
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Thrown when an event is not registered in the schema.
|
|
202
|
+
*/
|
|
203
|
+
export class EventNotRegisteredError extends MatadorError {
|
|
204
|
+
readonly description =
|
|
205
|
+
'The event type is not registered in the schema. ' +
|
|
206
|
+
'ACTION: Register the event using matador.register(EventClass, subscribers) before dispatching. ' +
|
|
207
|
+
'If this occurs during message consumption, it may indicate schema drift between services. ' +
|
|
208
|
+
'Ensure all services have matching schema registrations for shared events.';
|
|
209
|
+
|
|
210
|
+
constructor(public readonly eventKey: string) {
|
|
211
|
+
super(
|
|
212
|
+
`Event "${eventKey}" is not registered in schema. ` +
|
|
213
|
+
`Register it using matador.register(EventClass, subscribers).`,
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Thrown when a subscriber is not found for an event.
|
|
220
|
+
*/
|
|
221
|
+
export class SubscriberNotRegisteredError extends MatadorError {
|
|
222
|
+
readonly description =
|
|
223
|
+
'The subscriber is not registered for this event in the schema. ' +
|
|
224
|
+
'ACTION: Ensure the subscriber is included in the registration for this event. ' +
|
|
225
|
+
'This may occur if: (1) The subscriber was removed from the schema but messages still exist, ' +
|
|
226
|
+
'(2) Schema drift between producer and consumer services, ' +
|
|
227
|
+
'(3) A deployment is in progress with different schema versions. ' +
|
|
228
|
+
'Check the dead-letter queue for affected messages.';
|
|
229
|
+
|
|
230
|
+
constructor(
|
|
231
|
+
public readonly subscriberName: string,
|
|
232
|
+
public readonly eventKey?: string,
|
|
233
|
+
) {
|
|
234
|
+
super(
|
|
235
|
+
`Subscriber "${subscriberName}" is not registered` +
|
|
236
|
+
(eventKey ? ` for event "${eventKey}"` : '') +
|
|
237
|
+
'. Check schema registration.',
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Thrown when no subscribers exist for an event during fanout.
|
|
244
|
+
*/
|
|
245
|
+
export class NoSubscribersExistError extends MatadorError {
|
|
246
|
+
readonly description =
|
|
247
|
+
'The event has no subscribers registered. ' +
|
|
248
|
+
'ACTION: Register at least one subscriber for this event type. ' +
|
|
249
|
+
'If subscribers were intentionally removed, consider also removing the event dispatch. ' +
|
|
250
|
+
'Events without subscribers are not useful and may indicate configuration issues.';
|
|
251
|
+
|
|
252
|
+
constructor(public readonly eventKey: string) {
|
|
253
|
+
super(
|
|
254
|
+
`No subscribers registered for event "${eventKey}". ` +
|
|
255
|
+
`Add subscribers using matador.register(EventClass, [subscriber1, subscriber2]).`,
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Thrown when the schema configuration is invalid.
|
|
262
|
+
*/
|
|
263
|
+
export class InvalidSchemaError extends MatadorError {
|
|
264
|
+
readonly description =
|
|
265
|
+
'The schema configuration is invalid. ' +
|
|
266
|
+
'ACTION: Review the schema registration for issues. Common problems: ' +
|
|
267
|
+
'(1) Duplicate subscriber names for the same event, ' +
|
|
268
|
+
'(2) Missing required fields on event class (key, description), ' +
|
|
269
|
+
'(3) Invalid alias configuration. ' +
|
|
270
|
+
'Check the cause property for specific details.';
|
|
271
|
+
|
|
272
|
+
constructor(
|
|
273
|
+
message: string,
|
|
274
|
+
public readonly cause?: string,
|
|
275
|
+
) {
|
|
276
|
+
super(`Invalid schema: ${message}` + (cause ? `. Cause: ${cause}` : ''));
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Thrown when a subscriber is a stub but is being processed locally.
|
|
282
|
+
*/
|
|
283
|
+
export class SubscriberIsStubError extends MatadorError {
|
|
284
|
+
readonly description =
|
|
285
|
+
'A SubscriberStub was registered in a consuming schema. ' +
|
|
286
|
+
'ACTION: SubscriberStubs should only be used in producer schemas to declare ' +
|
|
287
|
+
'that a subscriber exists in another service. ' +
|
|
288
|
+
'In the consumer service, provide a full Subscriber with a callback function. ' +
|
|
289
|
+
'Remove the stub from the consumer schema and add the actual implementation.';
|
|
290
|
+
|
|
291
|
+
constructor(public readonly subscriberName: string) {
|
|
292
|
+
super(
|
|
293
|
+
`Subscriber "${subscriberName}" is a stub and cannot be processed locally. ` +
|
|
294
|
+
`Replace with a full Subscriber implementation in the consumer schema.`,
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Thrown when a LocalTransport tries to process a stub subscriber.
|
|
301
|
+
*/
|
|
302
|
+
export class LocalTransportCannotProcessStubError extends MatadorError {
|
|
303
|
+
readonly description =
|
|
304
|
+
'The LocalTransport cannot process events for SubscriberStubs. ' +
|
|
305
|
+
'ACTION: SubscriberStubs represent remote implementations that only RabbitMQ can route. ' +
|
|
306
|
+
'If using LocalTransport for testing, provide mock implementations instead of stubs. ' +
|
|
307
|
+
'For production fallback scenarios, be aware that stub-targeted events will be dropped.';
|
|
308
|
+
|
|
309
|
+
constructor(public readonly subscriberName: string) {
|
|
310
|
+
super(
|
|
311
|
+
`LocalTransport cannot process stub subscriber "${subscriberName}". ` +
|
|
312
|
+
`Stub subscribers require a distributed transport like RabbitMQ.`,
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// ============================================================================
|
|
318
|
+
// Queue Errors
|
|
319
|
+
// ============================================================================
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Thrown when a queue is not found or not created.
|
|
323
|
+
*/
|
|
324
|
+
export class QueueNotFoundError extends MatadorError {
|
|
325
|
+
readonly description =
|
|
326
|
+
'The specified queue does not exist or has not been created. ' +
|
|
327
|
+
'ACTION: Ensure the queue is defined in the topology configuration. ' +
|
|
328
|
+
'Call transport.applyTopology() or matador.start() to create queues. ' +
|
|
329
|
+
'Check that the queue name matches the topology definition.';
|
|
330
|
+
|
|
331
|
+
constructor(public readonly queueName: string) {
|
|
332
|
+
super(
|
|
333
|
+
`Queue "${queueName}" not found. ` +
|
|
334
|
+
`Ensure it is defined in topology and applyTopology() was called.`,
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// ============================================================================
|
|
340
|
+
// Event Validation Errors
|
|
341
|
+
// ============================================================================
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Thrown when an event is invalid or malformed.
|
|
345
|
+
*/
|
|
346
|
+
export class InvalidEventError extends MatadorError {
|
|
347
|
+
readonly description =
|
|
348
|
+
'The event is invalid or missing required fields. ' +
|
|
349
|
+
'ACTION: Ensure the event has all required properties. ' +
|
|
350
|
+
'Common issues: missing targetSubscriber during processing, ' +
|
|
351
|
+
'null/undefined data when the event type requires data, ' +
|
|
352
|
+
'malformed event structure from codec decode failure.';
|
|
353
|
+
|
|
354
|
+
constructor(
|
|
355
|
+
message: string,
|
|
356
|
+
public readonly cause?: string,
|
|
357
|
+
) {
|
|
358
|
+
super(`Invalid event: ${message}` + (cause ? `. Cause: ${cause}` : ''));
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// ============================================================================
|
|
363
|
+
// Message Processing Errors
|
|
364
|
+
// ============================================================================
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Thrown when a message has been redelivered too many times (poison message).
|
|
368
|
+
*/
|
|
369
|
+
export class MessageMaybePoisonedError extends MatadorError {
|
|
370
|
+
readonly description =
|
|
371
|
+
'A message was redelivered multiple times without successful processing. ' +
|
|
372
|
+
'This usually indicates the message causes a crash or timeout during processing. ' +
|
|
373
|
+
'ACTION: (1) Check application logs for errors/crashes during message processing, ' +
|
|
374
|
+
'(2) Inspect the message in the dead-letter queue for malformed data, ' +
|
|
375
|
+
'(3) Review the subscriber code for bugs that cause crashes, ' +
|
|
376
|
+
'(4) Consider increasing processing timeout if the operation is legitimately slow. ' +
|
|
377
|
+
'This message will NOT be retried to prevent crash loops.';
|
|
378
|
+
|
|
379
|
+
constructor(
|
|
380
|
+
public readonly eventId: string,
|
|
381
|
+
public readonly deliveryCount: number,
|
|
382
|
+
public readonly maxDeliveries: number,
|
|
383
|
+
) {
|
|
384
|
+
super(
|
|
385
|
+
`Message "${eventId}" delivered ${deliveryCount} times (max: ${maxDeliveries}). ` +
|
|
386
|
+
`Possible poison message - will not be retried.`,
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Thrown when a non-idempotent message cannot be retried after redelivery.
|
|
393
|
+
*/
|
|
394
|
+
export class IdempotentMessageCannotRetryError extends MatadorError {
|
|
395
|
+
readonly description =
|
|
396
|
+
'A non-idempotent subscriber received a redelivered message. ' +
|
|
397
|
+
'Retrying would risk duplicate side effects (e.g., double payments, duplicate emails). ' +
|
|
398
|
+
'ACTION: (1) Mark the subscriber as idempotent if it safely handles duplicates, ' +
|
|
399
|
+
'(2) Implement idempotency keys in the subscriber logic, ' +
|
|
400
|
+
'(3) Manually inspect and replay the message from the dead-letter queue after verification. ' +
|
|
401
|
+
'The message will be sent to the dead-letter queue for manual review.';
|
|
402
|
+
|
|
403
|
+
constructor(
|
|
404
|
+
public readonly eventId: string,
|
|
405
|
+
public readonly subscriberName: string,
|
|
406
|
+
) {
|
|
407
|
+
super(
|
|
408
|
+
`Non-idempotent subscriber "${subscriberName}" cannot retry redelivered message "${eventId}". ` +
|
|
409
|
+
`Mark subscriber as idempotent='yes' or implement idempotency handling.`,
|
|
410
|
+
);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// ============================================================================
|
|
415
|
+
// Timeout Errors
|
|
416
|
+
// ============================================================================
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Thrown when an operation times out.
|
|
420
|
+
*/
|
|
421
|
+
export class TimeoutError extends MatadorError {
|
|
422
|
+
readonly description =
|
|
423
|
+
'An operation timed out before completing. ' +
|
|
424
|
+
'ACTION: (1) Increase the timeout if the operation legitimately needs more time, ' +
|
|
425
|
+
'(2) Optimize the operation to complete faster, ' +
|
|
426
|
+
'(3) Check for deadlocks or blocking operations, ' +
|
|
427
|
+
'(4) Verify external service dependencies are responsive.';
|
|
428
|
+
|
|
429
|
+
constructor(
|
|
430
|
+
public readonly operation: string,
|
|
431
|
+
public readonly timeoutMs: number,
|
|
432
|
+
) {
|
|
433
|
+
super(`Operation "${operation}" timed out after ${timeoutMs}ms.`);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// ============================================================================
|
|
438
|
+
// Type Guards
|
|
439
|
+
// ============================================================================
|
|
440
|
+
|
|
441
|
+
export function isMatadorError(error: unknown): error is MatadorError {
|
|
442
|
+
return error instanceof MatadorError;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
export function isNotStartedError(error: unknown): error is NotStartedError {
|
|
446
|
+
return error instanceof NotStartedError;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
export function isTransportNotConnectedError(
|
|
450
|
+
error: unknown,
|
|
451
|
+
): error is TransportNotConnectedError {
|
|
452
|
+
return error instanceof TransportNotConnectedError;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
export function isEventNotRegisteredError(
|
|
456
|
+
error: unknown,
|
|
457
|
+
): error is EventNotRegisteredError {
|
|
458
|
+
return error instanceof EventNotRegisteredError;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
export function isSubscriberNotRegisteredError(
|
|
462
|
+
error: unknown,
|
|
463
|
+
): error is SubscriberNotRegisteredError {
|
|
464
|
+
return error instanceof SubscriberNotRegisteredError;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
export function isMessageMaybePoisonedError(
|
|
468
|
+
error: unknown,
|
|
469
|
+
): error is MessageMaybePoisonedError {
|
|
470
|
+
return error instanceof MessageMaybePoisonedError;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
export function isIdempotentMessageCannotRetryError(
|
|
474
|
+
error: unknown,
|
|
475
|
+
): error is IdempotentMessageCannotRetryError {
|
|
476
|
+
return error instanceof IdempotentMessageCannotRetryError;
|
|
477
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base class for retry control errors.
|
|
3
|
+
* These errors control the retry behavior of message processing.
|
|
4
|
+
*/
|
|
5
|
+
export abstract class RetryControlError extends Error {
|
|
6
|
+
/**
|
|
7
|
+
* Error class name for monitoring tools.
|
|
8
|
+
*/
|
|
9
|
+
declare readonly name: string;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Human-readable description of the error and recommended actions.
|
|
13
|
+
*/
|
|
14
|
+
abstract readonly description: string;
|
|
15
|
+
|
|
16
|
+
constructor(message: string) {
|
|
17
|
+
super(message);
|
|
18
|
+
this.name = this.constructor.name;
|
|
19
|
+
// Ensure name is preserved when serialized
|
|
20
|
+
Object.defineProperty(this, 'name', {
|
|
21
|
+
value: this.constructor.name,
|
|
22
|
+
enumerable: true,
|
|
23
|
+
configurable: false,
|
|
24
|
+
writable: false,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Returns a serializable representation for logging/monitoring.
|
|
30
|
+
*/
|
|
31
|
+
toJSON(): Record<string, unknown> {
|
|
32
|
+
return {
|
|
33
|
+
name: this.name,
|
|
34
|
+
message: this.message,
|
|
35
|
+
description: this.description,
|
|
36
|
+
stack: this.stack,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Forces retry regardless of subscriber idempotency setting.
|
|
43
|
+
* Use when you know the operation is safe to retry.
|
|
44
|
+
*
|
|
45
|
+
* ACTION: Check subscriber code to understand why retry was forced.
|
|
46
|
+
* This overrides default retry behavior based on idempotency settings.
|
|
47
|
+
*/
|
|
48
|
+
export class DoRetry extends RetryControlError {
|
|
49
|
+
readonly description =
|
|
50
|
+
'A subscriber explicitly requested retry by throwing DoRetry. ' +
|
|
51
|
+
'ACTION: Check the subscriber code to understand why retry was forced. ' +
|
|
52
|
+
'This overrides the default retry behavior based on idempotency settings.';
|
|
53
|
+
|
|
54
|
+
constructor(message = 'Forced retry requested') {
|
|
55
|
+
super(message);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Prevents retry regardless of subscriber idempotency setting.
|
|
61
|
+
* Use for permanent failures that should not be retried.
|
|
62
|
+
*
|
|
63
|
+
* ACTION: Check subscriber code to understand why retry was disabled.
|
|
64
|
+
* Typically used for permanent failures like invalid data or business rule violations.
|
|
65
|
+
*/
|
|
66
|
+
export class DontRetry extends RetryControlError {
|
|
67
|
+
readonly description =
|
|
68
|
+
'A subscriber explicitly prevented retry by throwing DontRetry. ' +
|
|
69
|
+
'ACTION: Check the subscriber code to understand why retry was disabled. ' +
|
|
70
|
+
'Typically used for permanent failures like invalid data or business rule violations. ' +
|
|
71
|
+
'The message will be sent to the dead-letter queue for manual review.';
|
|
72
|
+
|
|
73
|
+
constructor(message = 'Retry explicitly disabled') {
|
|
74
|
+
super(message);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Assertion error that should never be retried.
|
|
80
|
+
* Use for programming errors and invariant violations.
|
|
81
|
+
*
|
|
82
|
+
* ACTION: Review the assertion failure message to identify the bug
|
|
83
|
+
* in the event payload or subscriber logic.
|
|
84
|
+
*/
|
|
85
|
+
export class EventAssertionError extends Error {
|
|
86
|
+
declare readonly name: string;
|
|
87
|
+
|
|
88
|
+
readonly description =
|
|
89
|
+
'An event assertion failed, indicating a programming error or invariant violation. ' +
|
|
90
|
+
'ACTION: Review the assertion failure message to identify the bug in the ' +
|
|
91
|
+
'event payload or subscriber logic. These errors are never retried and go ' +
|
|
92
|
+
'directly to the dead-letter queue.';
|
|
93
|
+
|
|
94
|
+
constructor(message: string) {
|
|
95
|
+
super(message);
|
|
96
|
+
this.name = 'EventAssertionError';
|
|
97
|
+
Object.defineProperty(this, 'name', {
|
|
98
|
+
value: 'EventAssertionError',
|
|
99
|
+
enumerable: true,
|
|
100
|
+
configurable: false,
|
|
101
|
+
writable: false,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
toJSON(): Record<string, unknown> {
|
|
106
|
+
return {
|
|
107
|
+
name: this.name,
|
|
108
|
+
message: this.message,
|
|
109
|
+
description: this.description,
|
|
110
|
+
stack: this.stack,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Checks if an error forces a retry.
|
|
117
|
+
*/
|
|
118
|
+
export function isDoRetry(error: unknown): error is DoRetry {
|
|
119
|
+
return error instanceof DoRetry;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Checks if an error prevents retry.
|
|
124
|
+
*/
|
|
125
|
+
export function isDontRetry(error: unknown): error is DontRetry {
|
|
126
|
+
return error instanceof DontRetry;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Checks if an error is an assertion error (never retry).
|
|
131
|
+
*/
|
|
132
|
+
export function isAssertionError(error: unknown): error is EventAssertionError {
|
|
133
|
+
return error instanceof EventAssertionError;
|
|
134
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export type {
|
|
2
|
+
DecodeErrorContext,
|
|
3
|
+
EnqueueErrorContext,
|
|
4
|
+
EnqueueSuccessContext,
|
|
5
|
+
EnqueueWarningContext,
|
|
6
|
+
Logger,
|
|
7
|
+
MatadorHooks,
|
|
8
|
+
TransportFallbackContext,
|
|
9
|
+
WorkerErrorContext,
|
|
10
|
+
WorkerExecuteFn,
|
|
11
|
+
WorkerSuccessContext,
|
|
12
|
+
} from './types.js';
|
|
13
|
+
|
|
14
|
+
export { consoleLogger } from './types.js';
|
|
15
|
+
export { SafeHooks } from './safe-hooks.js';
|