@zdavison/matador 2.0.4 → 2.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/fanout.test.js +0 -1
- package/dist/errors/index.d.ts +1 -1
- package/dist/errors/index.d.ts.map +1 -1
- package/dist/errors/index.js +1 -1
- package/dist/errors/retry-errors.d.ts +40 -5
- package/dist/errors/retry-errors.d.ts.map +1 -1
- package/dist/errors/retry-errors.js +56 -9
- package/dist/errors/retry-errors.test.d.ts +2 -0
- package/dist/errors/retry-errors.test.d.ts.map +1 -0
- package/dist/errors/retry-errors.test.js +136 -0
- package/dist/index.cjs +29 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/pipeline/pipeline.test.js +33 -0
- package/dist/retry/standard-policy.test.js +7 -1
- package/dist/schema/registry.d.ts.map +1 -1
- package/dist/schema/registry.js +4 -1
- package/dist/schema/types.test.js +0 -1
- package/dist/types/envelope.d.ts +17 -0
- package/dist/types/envelope.d.ts.map +1 -1
- package/dist/types/envelope.js +19 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +1 -1
- package/dist/types/subscriber.d.ts +10 -6
- package/dist/types/subscriber.d.ts.map +1 -1
- package/dist/types/subscriber.js +0 -2
- package/package.json +1 -1
- package/src/core/fanout.test.ts +0 -1
- package/src/errors/index.ts +1 -0
- package/src/errors/retry-errors.test.ts +175 -0
- package/src/errors/retry-errors.ts +63 -9
- package/src/index.ts +2 -0
- package/src/pipeline/pipeline.test.ts +38 -0
- package/src/retry/standard-policy.test.ts +9 -1
- package/src/schema/registry.ts +4 -1
- package/src/schema/types.test.ts +0 -1
- package/src/types/envelope.ts +23 -0
- package/src/types/index.ts +1 -1
- package/src/types/subscriber.ts +11 -7
- package/tsconfig.tsbuildinfo +1 -1
package/dist/core/fanout.test.js
CHANGED
|
@@ -489,7 +489,6 @@ describe('FanoutEngine', () => {
|
|
|
489
489
|
it('should work with subscriber stubs', async () => {
|
|
490
490
|
const stub = createSubscriberStub({
|
|
491
491
|
name: 'remote-subscriber',
|
|
492
|
-
description: 'Remote subscriber stub',
|
|
493
492
|
enabled: () => false,
|
|
494
493
|
});
|
|
495
494
|
schema.register(UserCreatedEvent, [stub]);
|
package/dist/errors/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export type { HasDescription } from './has-description.js';
|
|
2
2
|
export { hasDescription } from './has-description.js';
|
|
3
|
-
export { DontRetry, DoRetry, EventAssertionError, isAssertionError, isDontRetry, isDoRetry, RetryControlError, } from './retry-errors.js';
|
|
3
|
+
export { assertEvent, DontRetry, DoRetry, EventAssertionError, isAssertionError, isDontRetry, isDoRetry, RetryControlError, } from './retry-errors.js';
|
|
4
4
|
export { MatadorError, isMatadorError, NotStartedError, isNotStartedError, ShutdownInProgressError, TransportNotConnectedError, isTransportNotConnectedError, TransportClosedError, TransportSendError, AllTransportsFailedError, DelayedMessagesNotSupportedError, EventNotRegisteredError, isEventNotRegisteredError, SubscriberNotRegisteredError, isSubscriberNotRegisteredError, NoSubscribersExistError, InvalidSchemaError, SubscriberIsStubError, LocalTransportCannotProcessStubError, QueueNotFoundError, InvalidEventError, MessageMaybePoisonedError, isMessageMaybePoisonedError, IdempotentMessageCannotRetryError, isIdempotentMessageCannotRetryError, TimeoutError, } from './matador-errors.js';
|
|
5
5
|
export { CheckpointStoreError, DuplicateIoKeyError, isCheckpointStoreError, isDuplicateIoKeyError, } from './checkpoint-errors.js';
|
|
6
6
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/errors/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,OAAO,EACL,SAAS,EACT,OAAO,EACP,mBAAmB,EACnB,gBAAgB,EAChB,WAAW,EACX,SAAS,EACT,iBAAiB,GAClB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAEL,YAAY,EACZ,cAAc,EAEd,eAAe,EACf,iBAAiB,EACjB,uBAAuB,EAEvB,0BAA0B,EAC1B,4BAA4B,EAC5B,oBAAoB,EACpB,kBAAkB,EAClB,wBAAwB,EACxB,gCAAgC,EAEhC,uBAAuB,EACvB,yBAAyB,EACzB,4BAA4B,EAC5B,8BAA8B,EAC9B,uBAAuB,EACvB,kBAAkB,EAClB,qBAAqB,EACrB,oCAAoC,EAEpC,kBAAkB,EAElB,iBAAiB,EAEjB,yBAAyB,EACzB,2BAA2B,EAC3B,iCAAiC,EACjC,mCAAmC,EAEnC,YAAY,GACb,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAEL,oBAAoB,EACpB,mBAAmB,EACnB,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,wBAAwB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/errors/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,OAAO,EACL,WAAW,EACX,SAAS,EACT,OAAO,EACP,mBAAmB,EACnB,gBAAgB,EAChB,WAAW,EACX,SAAS,EACT,iBAAiB,GAClB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAEL,YAAY,EACZ,cAAc,EAEd,eAAe,EACf,iBAAiB,EACjB,uBAAuB,EAEvB,0BAA0B,EAC1B,4BAA4B,EAC5B,oBAAoB,EACpB,kBAAkB,EAClB,wBAAwB,EACxB,gCAAgC,EAEhC,uBAAuB,EACvB,yBAAyB,EACzB,4BAA4B,EAC5B,8BAA8B,EAC9B,uBAAuB,EACvB,kBAAkB,EAClB,qBAAqB,EACrB,oCAAoC,EAEpC,kBAAkB,EAElB,iBAAiB,EAEjB,yBAAyB,EACzB,2BAA2B,EAC3B,iCAAiC,EACjC,mCAAmC,EAEnC,YAAY,GACb,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAEL,oBAAoB,EACpB,mBAAmB,EACnB,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,wBAAwB,CAAC"}
|
package/dist/errors/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { hasDescription } from './has-description.js';
|
|
2
|
-
export { DontRetry, DoRetry, EventAssertionError, isAssertionError, isDontRetry, isDoRetry, RetryControlError, } from './retry-errors.js';
|
|
2
|
+
export { assertEvent, DontRetry, DoRetry, EventAssertionError, isAssertionError, isDontRetry, isDoRetry, RetryControlError, } from './retry-errors.js';
|
|
3
3
|
export {
|
|
4
4
|
// Base class
|
|
5
5
|
MatadorError, isMatadorError,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Envelope } from '../types/envelope.js';
|
|
1
2
|
/**
|
|
2
3
|
* Base class for retry control errors.
|
|
3
4
|
* These errors control the retry behavior of message processing.
|
|
@@ -40,16 +41,24 @@ export declare class DontRetry extends RetryControlError {
|
|
|
40
41
|
constructor(message?: string);
|
|
41
42
|
}
|
|
42
43
|
/**
|
|
43
|
-
*
|
|
44
|
-
* Use for programming errors and invariant violations.
|
|
44
|
+
* Thrown by `assertEvent` in the event of a failed assertion.
|
|
45
45
|
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
46
|
+
* This error indicates that an event was in an unexpected state when it reached the subscriber.
|
|
47
|
+
* Throwing this error will indicate to Matador NOT to retry the event - it will be sent
|
|
48
|
+
* directly to the undeliverable dead-letter queue.
|
|
49
|
+
*
|
|
50
|
+
* Use `assertEvent` in your subscribers to assert properties about an event payload.
|
|
51
|
+
* This is useful in scenarios where you would expect an event payload to contain a field
|
|
52
|
+
* based on the types, but something unexpected caused it to not be present.
|
|
53
|
+
*
|
|
54
|
+
* @see assertEvent
|
|
48
55
|
*/
|
|
49
56
|
export declare class EventAssertionError extends Error {
|
|
50
57
|
readonly name: string;
|
|
51
58
|
readonly description: string;
|
|
52
|
-
|
|
59
|
+
/** The envelope that failed the assertion */
|
|
60
|
+
readonly envelope: Envelope<unknown>;
|
|
61
|
+
constructor(envelope: Envelope<unknown>, message: string);
|
|
53
62
|
toJSON(): Record<string, unknown>;
|
|
54
63
|
}
|
|
55
64
|
/**
|
|
@@ -64,4 +73,30 @@ export declare function isDontRetry(error: unknown): error is DontRetry;
|
|
|
64
73
|
* Checks if an error is an assertion error (never retry).
|
|
65
74
|
*/
|
|
66
75
|
export declare function isAssertionError(error: unknown): error is EventAssertionError;
|
|
76
|
+
/**
|
|
77
|
+
* Same as Node.js `assert`, but throws an `EventAssertionError` if the assertion fails.
|
|
78
|
+
*
|
|
79
|
+
* `EventAssertionError`s thrown within a subscriber do not cause the subscriber to be retried.
|
|
80
|
+
* The event will be delivered to the undeliverable dead-letter queue instead.
|
|
81
|
+
*
|
|
82
|
+
* @see https://nodejs.org/api/assert.html#assertvalue-message
|
|
83
|
+
* @param envelope - The envelope this assertion relates to.
|
|
84
|
+
* @param value - The value to assert. If falsy, the assertion fails.
|
|
85
|
+
* @param message - The message to include in the error if the assertion fails.
|
|
86
|
+
* @throws {EventAssertionError} If the assertion fails.
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* ```typescript
|
|
90
|
+
* const subscriber = createSubscriber<MyEvent>({
|
|
91
|
+
* name: 'my-subscriber',
|
|
92
|
+
* description: 'Processes MyEvent',
|
|
93
|
+
* callback: async (envelope) => {
|
|
94
|
+
* // Assert that userId exists - if not, event goes to DLQ without retry
|
|
95
|
+
* assertEvent(envelope, envelope.data.userId, 'userId is required');
|
|
96
|
+
* // ... rest of processing
|
|
97
|
+
* },
|
|
98
|
+
* });
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
export declare function assertEvent(envelope: Envelope<unknown>, value: unknown, message: string): asserts value;
|
|
67
102
|
//# sourceMappingURL=retry-errors.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"retry-errors.d.ts","sourceRoot":"","sources":["../../src/errors/retry-errors.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"retry-errors.d.ts","sourceRoot":"","sources":["../../src/errors/retry-errors.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAErD;;;GAGG;AACH,8BAAsB,iBAAkB,SAAQ,KAAK;IACnD;;OAEG;IACH,SAAiB,IAAI,EAAE,MAAM,CAAC;IAE9B;;OAEG;IACH,QAAQ,CAAC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;gBAE1B,OAAO,EAAE,MAAM;IAY3B;;OAEG;IACH,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAQlC;AAED;;;;;;GAMG;AACH,qBAAa,OAAQ,SAAQ,iBAAiB;IAC5C,QAAQ,CAAC,WAAW,SAGyD;gBAEjE,OAAO,SAA2B;CAG/C;AAED;;;;;;GAMG;AACH,qBAAa,SAAU,SAAQ,iBAAiB;IAC9C,QAAQ,CAAC,WAAW,SAIqD;gBAE7D,OAAO,SAA8B;CAGlD;AAED;;;;;;;;;;;;GAYG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,SAAiB,IAAI,EAAE,MAAM,CAAC;IAE9B,QAAQ,CAAC,WAAW,SAO0E;IAE9F,6CAA6C;IAC7C,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;gBAEzB,QAAQ,EAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,MAAM;IAYxD,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CASlC;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,OAAO,CAE1D;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,SAAS,CAE9D;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,mBAAmB,CAE7E;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,WAAW,CACzB,QAAQ,EAAE,QAAQ,CAAC,OAAO,CAAC,EAC3B,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,KAAK,CAMf"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import assert from 'node:assert';
|
|
1
2
|
/**
|
|
2
3
|
* Base class for retry control errors.
|
|
3
4
|
* These errors control the retry behavior of message processing.
|
|
@@ -58,20 +59,32 @@ export class DontRetry extends RetryControlError {
|
|
|
58
59
|
}
|
|
59
60
|
}
|
|
60
61
|
/**
|
|
61
|
-
*
|
|
62
|
-
* Use for programming errors and invariant violations.
|
|
62
|
+
* Thrown by `assertEvent` in the event of a failed assertion.
|
|
63
63
|
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
64
|
+
* This error indicates that an event was in an unexpected state when it reached the subscriber.
|
|
65
|
+
* Throwing this error will indicate to Matador NOT to retry the event - it will be sent
|
|
66
|
+
* directly to the undeliverable dead-letter queue.
|
|
67
|
+
*
|
|
68
|
+
* Use `assertEvent` in your subscribers to assert properties about an event payload.
|
|
69
|
+
* This is useful in scenarios where you would expect an event payload to contain a field
|
|
70
|
+
* based on the types, but something unexpected caused it to not be present.
|
|
71
|
+
*
|
|
72
|
+
* @see assertEvent
|
|
66
73
|
*/
|
|
67
74
|
export class EventAssertionError extends Error {
|
|
68
|
-
description = '
|
|
69
|
-
'
|
|
70
|
-
'
|
|
71
|
-
'
|
|
72
|
-
|
|
75
|
+
description = 'Thrown to indicate that an event was in an unexpected state when it reached the subscriber. ' +
|
|
76
|
+
'Throwing this error will indicate to Matador NOT to retry the event. ' +
|
|
77
|
+
'Matador does not throw this error directly - it is thrown by assertEvent, which you can ' +
|
|
78
|
+
'use in your subscribers to assert properties about an event. ' +
|
|
79
|
+
'This can be useful in scenarios where you would expect an event payload to contain a field ' +
|
|
80
|
+
'based on the types, but something unexpected caused it to not be present. ' +
|
|
81
|
+
'Using this instead of the NodeJS assert allows Matador to not retry the event subscriber.';
|
|
82
|
+
/** The envelope that failed the assertion */
|
|
83
|
+
envelope;
|
|
84
|
+
constructor(envelope, message) {
|
|
73
85
|
super(message);
|
|
74
86
|
this.name = 'EventAssertionError';
|
|
87
|
+
this.envelope = envelope;
|
|
75
88
|
Object.defineProperty(this, 'name', {
|
|
76
89
|
value: 'EventAssertionError',
|
|
77
90
|
enumerable: true,
|
|
@@ -84,6 +97,7 @@ export class EventAssertionError extends Error {
|
|
|
84
97
|
name: this.name,
|
|
85
98
|
message: this.message,
|
|
86
99
|
description: this.description,
|
|
100
|
+
envelope: this.envelope,
|
|
87
101
|
stack: this.stack,
|
|
88
102
|
};
|
|
89
103
|
}
|
|
@@ -106,3 +120,36 @@ export function isDontRetry(error) {
|
|
|
106
120
|
export function isAssertionError(error) {
|
|
107
121
|
return error instanceof EventAssertionError;
|
|
108
122
|
}
|
|
123
|
+
/**
|
|
124
|
+
* Same as Node.js `assert`, but throws an `EventAssertionError` if the assertion fails.
|
|
125
|
+
*
|
|
126
|
+
* `EventAssertionError`s thrown within a subscriber do not cause the subscriber to be retried.
|
|
127
|
+
* The event will be delivered to the undeliverable dead-letter queue instead.
|
|
128
|
+
*
|
|
129
|
+
* @see https://nodejs.org/api/assert.html#assertvalue-message
|
|
130
|
+
* @param envelope - The envelope this assertion relates to.
|
|
131
|
+
* @param value - The value to assert. If falsy, the assertion fails.
|
|
132
|
+
* @param message - The message to include in the error if the assertion fails.
|
|
133
|
+
* @throws {EventAssertionError} If the assertion fails.
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* ```typescript
|
|
137
|
+
* const subscriber = createSubscriber<MyEvent>({
|
|
138
|
+
* name: 'my-subscriber',
|
|
139
|
+
* description: 'Processes MyEvent',
|
|
140
|
+
* callback: async (envelope) => {
|
|
141
|
+
* // Assert that userId exists - if not, event goes to DLQ without retry
|
|
142
|
+
* assertEvent(envelope, envelope.data.userId, 'userId is required');
|
|
143
|
+
* // ... rest of processing
|
|
144
|
+
* },
|
|
145
|
+
* });
|
|
146
|
+
* ```
|
|
147
|
+
*/
|
|
148
|
+
export function assertEvent(envelope, value, message) {
|
|
149
|
+
try {
|
|
150
|
+
assert(value, message);
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
throw new EventAssertionError(envelope, message);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry-errors.test.d.ts","sourceRoot":"","sources":["../../src/errors/retry-errors.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { describe, expect, it } from 'bun:test';
|
|
2
|
+
import { createEnvelope } from '../types/envelope.js';
|
|
3
|
+
import { assertEvent, DontRetry, DoRetry, EventAssertionError, isAssertionError, isDontRetry, isDoRetry, } from './retry-errors.js';
|
|
4
|
+
function createTestEnvelope(data = { foo: 'bar' }) {
|
|
5
|
+
return createEnvelope({
|
|
6
|
+
data,
|
|
7
|
+
eventKey: 'test.event',
|
|
8
|
+
targetSubscriber: 'test-subscriber',
|
|
9
|
+
importance: 'can-ignore',
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
describe('retry-errors', () => {
|
|
13
|
+
describe('DoRetry', () => {
|
|
14
|
+
it('should have correct name', () => {
|
|
15
|
+
const error = new DoRetry('test message');
|
|
16
|
+
expect(error.name).toBe('DoRetry');
|
|
17
|
+
});
|
|
18
|
+
it('should have description', () => {
|
|
19
|
+
const error = new DoRetry('test message');
|
|
20
|
+
expect(error.description).toBeDefined();
|
|
21
|
+
});
|
|
22
|
+
it('should serialize to JSON', () => {
|
|
23
|
+
const error = new DoRetry('test message');
|
|
24
|
+
const json = error.toJSON();
|
|
25
|
+
expect(json.name).toBe('DoRetry');
|
|
26
|
+
expect(json.message).toBe('test message');
|
|
27
|
+
expect(json.description).toBeDefined();
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
describe('DontRetry', () => {
|
|
31
|
+
it('should have correct name', () => {
|
|
32
|
+
const error = new DontRetry('test message');
|
|
33
|
+
expect(error.name).toBe('DontRetry');
|
|
34
|
+
});
|
|
35
|
+
it('should have description', () => {
|
|
36
|
+
const error = new DontRetry('test message');
|
|
37
|
+
expect(error.description).toBeDefined();
|
|
38
|
+
});
|
|
39
|
+
it('should serialize to JSON', () => {
|
|
40
|
+
const error = new DontRetry('test message');
|
|
41
|
+
const json = error.toJSON();
|
|
42
|
+
expect(json.name).toBe('DontRetry');
|
|
43
|
+
expect(json.message).toBe('test message');
|
|
44
|
+
expect(json.description).toBeDefined();
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
describe('EventAssertionError', () => {
|
|
48
|
+
it('should have correct name', () => {
|
|
49
|
+
const envelope = createTestEnvelope();
|
|
50
|
+
const error = new EventAssertionError(envelope, 'assertion failed');
|
|
51
|
+
expect(error.name).toBe('EventAssertionError');
|
|
52
|
+
});
|
|
53
|
+
it('should store the envelope', () => {
|
|
54
|
+
const envelope = createTestEnvelope();
|
|
55
|
+
const error = new EventAssertionError(envelope, 'assertion failed');
|
|
56
|
+
expect(error.envelope).toBe(envelope);
|
|
57
|
+
});
|
|
58
|
+
it('should have description', () => {
|
|
59
|
+
const envelope = createTestEnvelope();
|
|
60
|
+
const error = new EventAssertionError(envelope, 'assertion failed');
|
|
61
|
+
expect(error.description).toBeDefined();
|
|
62
|
+
expect(error.description).toContain('NOT to retry');
|
|
63
|
+
});
|
|
64
|
+
it('should serialize to JSON with envelope', () => {
|
|
65
|
+
const envelope = createTestEnvelope();
|
|
66
|
+
const error = new EventAssertionError(envelope, 'assertion failed');
|
|
67
|
+
const json = error.toJSON();
|
|
68
|
+
expect(json.name).toBe('EventAssertionError');
|
|
69
|
+
expect(json.message).toBe('assertion failed');
|
|
70
|
+
expect(json.envelope).toBe(envelope);
|
|
71
|
+
expect(json.description).toBeDefined();
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
describe('assertEvent', () => {
|
|
75
|
+
it('should not throw when value is truthy', () => {
|
|
76
|
+
const envelope = createTestEnvelope();
|
|
77
|
+
expect(() => assertEvent(envelope, true, 'should be true')).not.toThrow();
|
|
78
|
+
expect(() => assertEvent(envelope, 'string', 'should be string')).not.toThrow();
|
|
79
|
+
expect(() => assertEvent(envelope, 1, 'should be number')).not.toThrow();
|
|
80
|
+
expect(() => assertEvent(envelope, {}, 'should be object')).not.toThrow();
|
|
81
|
+
expect(() => assertEvent(envelope, [], 'should be array')).not.toThrow();
|
|
82
|
+
});
|
|
83
|
+
it('should throw EventAssertionError when value is falsy', () => {
|
|
84
|
+
const envelope = createTestEnvelope();
|
|
85
|
+
expect(() => assertEvent(envelope, false, 'value was false')).toThrow(EventAssertionError);
|
|
86
|
+
expect(() => assertEvent(envelope, null, 'value was null')).toThrow(EventAssertionError);
|
|
87
|
+
expect(() => assertEvent(envelope, undefined, 'value was undefined')).toThrow(EventAssertionError);
|
|
88
|
+
expect(() => assertEvent(envelope, 0, 'value was zero')).toThrow(EventAssertionError);
|
|
89
|
+
expect(() => assertEvent(envelope, '', 'value was empty string')).toThrow(EventAssertionError);
|
|
90
|
+
});
|
|
91
|
+
it('should include envelope in thrown error', () => {
|
|
92
|
+
const envelope = createTestEnvelope();
|
|
93
|
+
try {
|
|
94
|
+
assertEvent(envelope, false, 'assertion failed');
|
|
95
|
+
expect.unreachable('should have thrown');
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
expect(error).toBeInstanceOf(EventAssertionError);
|
|
99
|
+
expect(error.envelope).toBe(envelope);
|
|
100
|
+
expect(error.message).toBe('assertion failed');
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
it('should include message in thrown error', () => {
|
|
104
|
+
const envelope = createTestEnvelope();
|
|
105
|
+
try {
|
|
106
|
+
assertEvent(envelope, null, 'userId is required');
|
|
107
|
+
expect.unreachable('should have thrown');
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
expect(error).toBeInstanceOf(EventAssertionError);
|
|
111
|
+
expect(error.message).toBe('userId is required');
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
describe('type guards', () => {
|
|
116
|
+
it('isDoRetry should identify DoRetry errors', () => {
|
|
117
|
+
expect(isDoRetry(new DoRetry('test'))).toBe(true);
|
|
118
|
+
expect(isDoRetry(new DontRetry('test'))).toBe(false);
|
|
119
|
+
expect(isDoRetry(new Error('test'))).toBe(false);
|
|
120
|
+
expect(isDoRetry(null)).toBe(false);
|
|
121
|
+
});
|
|
122
|
+
it('isDontRetry should identify DontRetry errors', () => {
|
|
123
|
+
expect(isDontRetry(new DontRetry('test'))).toBe(true);
|
|
124
|
+
expect(isDontRetry(new DoRetry('test'))).toBe(false);
|
|
125
|
+
expect(isDontRetry(new Error('test'))).toBe(false);
|
|
126
|
+
expect(isDontRetry(null)).toBe(false);
|
|
127
|
+
});
|
|
128
|
+
it('isAssertionError should identify EventAssertionError', () => {
|
|
129
|
+
const envelope = createTestEnvelope();
|
|
130
|
+
expect(isAssertionError(new EventAssertionError(envelope, 'test'))).toBe(true);
|
|
131
|
+
expect(isAssertionError(new DoRetry('test'))).toBe(false);
|
|
132
|
+
expect(isAssertionError(new Error('test'))).toBe(false);
|
|
133
|
+
expect(isAssertionError(null)).toBe(false);
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
});
|
package/dist/index.cjs
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var assert = require('assert');
|
|
3
4
|
var amqplib = require('amqplib');
|
|
4
5
|
|
|
5
6
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
6
7
|
|
|
8
|
+
var assert__default = /*#__PURE__*/_interopDefault(assert);
|
|
7
9
|
var amqplib__default = /*#__PURE__*/_interopDefault(amqplib);
|
|
8
10
|
|
|
9
11
|
// src/errors/has-description.ts
|
|
10
12
|
function hasDescription(error) {
|
|
11
13
|
return typeof error === "object" && error !== null && "description" in error && typeof error.description === "string";
|
|
12
14
|
}
|
|
13
|
-
|
|
14
|
-
// src/errors/retry-errors.ts
|
|
15
15
|
var RetryControlError = class extends Error {
|
|
16
16
|
constructor(message) {
|
|
17
17
|
super(message);
|
|
@@ -48,10 +48,13 @@ var DontRetry = class extends RetryControlError {
|
|
|
48
48
|
}
|
|
49
49
|
};
|
|
50
50
|
var EventAssertionError = class extends Error {
|
|
51
|
-
description = "
|
|
52
|
-
|
|
51
|
+
description = "Thrown to indicate that an event was in an unexpected state when it reached the subscriber. Throwing this error will indicate to Matador NOT to retry the event. Matador does not throw this error directly - it is thrown by assertEvent, which you can use in your subscribers to assert properties about an event. This can be useful in scenarios where you would expect an event payload to contain a field based on the types, but something unexpected caused it to not be present. Using this instead of the NodeJS assert allows Matador to not retry the event subscriber.";
|
|
52
|
+
/** The envelope that failed the assertion */
|
|
53
|
+
envelope;
|
|
54
|
+
constructor(envelope, message) {
|
|
53
55
|
super(message);
|
|
54
56
|
this.name = "EventAssertionError";
|
|
57
|
+
this.envelope = envelope;
|
|
55
58
|
Object.defineProperty(this, "name", {
|
|
56
59
|
value: "EventAssertionError",
|
|
57
60
|
enumerable: true,
|
|
@@ -64,6 +67,7 @@ var EventAssertionError = class extends Error {
|
|
|
64
67
|
name: this.name,
|
|
65
68
|
message: this.message,
|
|
66
69
|
description: this.description,
|
|
70
|
+
envelope: this.envelope,
|
|
67
71
|
stack: this.stack
|
|
68
72
|
};
|
|
69
73
|
}
|
|
@@ -77,6 +81,13 @@ function isDontRetry(error) {
|
|
|
77
81
|
function isAssertionError(error) {
|
|
78
82
|
return error instanceof EventAssertionError;
|
|
79
83
|
}
|
|
84
|
+
function assertEvent(envelope, value, message) {
|
|
85
|
+
try {
|
|
86
|
+
assert__default.default(value, message);
|
|
87
|
+
} catch {
|
|
88
|
+
throw new EventAssertionError(envelope, message);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
80
91
|
|
|
81
92
|
// src/errors/matador-errors.ts
|
|
82
93
|
var MatadorError = class extends Error {
|
|
@@ -534,6 +545,14 @@ function createEnvelope(options) {
|
|
|
534
545
|
}
|
|
535
546
|
};
|
|
536
547
|
}
|
|
548
|
+
function createDummyEnvelope(event) {
|
|
549
|
+
return createEnvelope({
|
|
550
|
+
data: event.data,
|
|
551
|
+
eventKey: event.constructor.key,
|
|
552
|
+
targetSubscriber: "dummy-subscriber",
|
|
553
|
+
importance: "can-ignore"
|
|
554
|
+
});
|
|
555
|
+
}
|
|
537
556
|
|
|
538
557
|
// src/types/event.ts
|
|
539
558
|
var MatadorEvent = class {
|
|
@@ -581,7 +600,6 @@ function createSubscriber(input) {
|
|
|
581
600
|
function createSubscriberStub(input) {
|
|
582
601
|
return {
|
|
583
602
|
name: input.name,
|
|
584
|
-
description: input.description,
|
|
585
603
|
isStub: true,
|
|
586
604
|
idempotent: input.idempotent ?? "unknown",
|
|
587
605
|
importance: input.importance ?? "should-investigate",
|
|
@@ -1834,9 +1852,11 @@ var SchemaRegistry = class {
|
|
|
1834
1852
|
if (!subscriber) return void 0;
|
|
1835
1853
|
const def = {
|
|
1836
1854
|
name: subscriber.name,
|
|
1837
|
-
description: subscriber.description,
|
|
1838
1855
|
idempotent: subscriber.idempotent ?? "unknown",
|
|
1839
|
-
importance: subscriber.importance ?? "should-investigate"
|
|
1856
|
+
importance: subscriber.importance ?? "should-investigate",
|
|
1857
|
+
..."description" in subscriber && subscriber.description !== void 0 && {
|
|
1858
|
+
description: subscriber.description
|
|
1859
|
+
}
|
|
1840
1860
|
};
|
|
1841
1861
|
if (subscriber.targetQueue !== void 0) {
|
|
1842
1862
|
def.targetQueue = subscriber.targetQueue;
|
|
@@ -3166,8 +3186,10 @@ exports.TopologyValidationError = TopologyValidationError;
|
|
|
3166
3186
|
exports.TransportClosedError = TransportClosedError;
|
|
3167
3187
|
exports.TransportNotConnectedError = TransportNotConnectedError;
|
|
3168
3188
|
exports.TransportSendError = TransportSendError;
|
|
3189
|
+
exports.assertEvent = assertEvent;
|
|
3169
3190
|
exports.bind = bind;
|
|
3170
3191
|
exports.consoleLogger = consoleLogger;
|
|
3192
|
+
exports.createDummyEnvelope = createDummyEnvelope;
|
|
3171
3193
|
exports.createEnvelope = createEnvelope;
|
|
3172
3194
|
exports.createSubscriber = createSubscriber;
|
|
3173
3195
|
exports.createSubscriberStub = createSubscriberStub;
|