@zdavison/matador 2.0.9 → 2.0.10

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.
Files changed (259) hide show
  1. package/dist/checkpoint/context.d.ts +59 -0
  2. package/dist/checkpoint/context.d.ts.map +1 -0
  3. package/dist/checkpoint/context.js +140 -0
  4. package/dist/checkpoint/context.test.d.ts +2 -0
  5. package/dist/checkpoint/context.test.d.ts.map +1 -0
  6. package/dist/checkpoint/context.test.js +424 -0
  7. package/dist/checkpoint/index.d.ts +7 -0
  8. package/dist/checkpoint/index.d.ts.map +1 -0
  9. package/dist/checkpoint/index.js +6 -0
  10. package/dist/checkpoint/stores/memory.d.ts +29 -0
  11. package/dist/checkpoint/stores/memory.d.ts.map +1 -0
  12. package/dist/checkpoint/stores/memory.js +39 -0
  13. package/{src/checkpoint/stores/noop.ts → dist/checkpoint/stores/noop.d.ts} +5 -13
  14. package/dist/checkpoint/stores/noop.d.ts.map +1 -0
  15. package/dist/checkpoint/stores/noop.js +18 -0
  16. package/dist/checkpoint/stores/stores.test.d.ts +2 -0
  17. package/dist/checkpoint/stores/stores.test.d.ts.map +1 -0
  18. package/dist/checkpoint/stores/stores.test.js +146 -0
  19. package/dist/checkpoint/types.d.ts +119 -0
  20. package/dist/checkpoint/types.d.ts.map +1 -0
  21. package/dist/checkpoint/types.js +1 -0
  22. package/dist/codec/codec.d.ts +29 -0
  23. package/dist/codec/codec.d.ts.map +1 -0
  24. package/dist/codec/codec.js +15 -0
  25. package/dist/codec/header-aware-codec.d.ts +36 -0
  26. package/dist/codec/header-aware-codec.d.ts.map +1 -0
  27. package/dist/codec/header-aware-codec.js +1 -0
  28. package/{src/codec/index.ts → dist/codec/index.d.ts} +2 -7
  29. package/dist/codec/index.d.ts.map +1 -0
  30. package/dist/codec/index.js +3 -0
  31. package/dist/codec/json-codec.d.ts +13 -0
  32. package/dist/codec/json-codec.d.ts.map +1 -0
  33. package/dist/codec/json-codec.js +64 -0
  34. package/dist/codec/rabbitmq-codec.d.ts +28 -0
  35. package/dist/codec/rabbitmq-codec.d.ts.map +1 -0
  36. package/dist/codec/rabbitmq-codec.js +242 -0
  37. package/dist/codec/rabbitmq-codec.test.d.ts +2 -0
  38. package/dist/codec/rabbitmq-codec.test.d.ts.map +1 -0
  39. package/dist/codec/rabbitmq-codec.test.js +433 -0
  40. package/dist/core/fanout.d.ts +59 -0
  41. package/dist/core/fanout.d.ts.map +1 -0
  42. package/dist/core/fanout.js +121 -0
  43. package/dist/core/fanout.test.d.ts +2 -0
  44. package/dist/core/fanout.test.d.ts.map +1 -0
  45. package/dist/core/fanout.test.js +1054 -0
  46. package/{src/core/index.ts → dist/core/index.d.ts} +2 -7
  47. package/dist/core/index.d.ts.map +1 -0
  48. package/dist/core/index.js +3 -0
  49. package/dist/core/matador.d.ts +133 -0
  50. package/dist/core/matador.d.ts.map +1 -0
  51. package/dist/core/matador.js +228 -0
  52. package/dist/core/matador.test.d.ts +2 -0
  53. package/dist/core/matador.test.d.ts.map +1 -0
  54. package/dist/core/matador.test.js +446 -0
  55. package/dist/core/shutdown.d.ts +78 -0
  56. package/dist/core/shutdown.d.ts.map +1 -0
  57. package/dist/core/shutdown.js +111 -0
  58. package/dist/core/shutdown.test.d.ts +2 -0
  59. package/dist/core/shutdown.test.d.ts.map +1 -0
  60. package/dist/core/shutdown.test.js +524 -0
  61. package/dist/errors/checkpoint-errors.d.ts +30 -0
  62. package/dist/errors/checkpoint-errors.d.ts.map +1 -0
  63. package/dist/errors/checkpoint-errors.js +49 -0
  64. package/dist/errors/has-description.d.ts +18 -0
  65. package/dist/errors/has-description.d.ts.map +1 -0
  66. package/dist/errors/has-description.js +9 -0
  67. package/dist/errors/index.d.ts +6 -0
  68. package/dist/errors/index.d.ts.map +1 -0
  69. package/dist/errors/index.js +22 -0
  70. package/dist/errors/matador-errors.d.ts +183 -0
  71. package/dist/errors/matador-errors.d.ts.map +1 -0
  72. package/dist/errors/matador-errors.js +376 -0
  73. package/dist/errors/retry-errors.d.ts +102 -0
  74. package/dist/errors/retry-errors.d.ts.map +1 -0
  75. package/dist/errors/retry-errors.js +155 -0
  76. package/dist/errors/retry-errors.test.d.ts +2 -0
  77. package/dist/errors/retry-errors.test.d.ts.map +1 -0
  78. package/dist/errors/retry-errors.test.js +136 -0
  79. package/dist/hooks/index.d.ts +4 -0
  80. package/dist/hooks/index.d.ts.map +1 -0
  81. package/dist/hooks/index.js +2 -0
  82. package/dist/hooks/safe-hooks.d.ts +32 -0
  83. package/dist/hooks/safe-hooks.d.ts.map +1 -0
  84. package/dist/hooks/safe-hooks.js +120 -0
  85. package/dist/hooks/types.d.ts +172 -0
  86. package/dist/hooks/types.d.ts.map +1 -0
  87. package/dist/hooks/types.js +9 -0
  88. package/dist/index.cjs +3228 -0
  89. package/dist/index.cjs.map +1 -0
  90. package/dist/index.d.cts +23 -0
  91. package/dist/index.d.ts +23 -0
  92. package/dist/index.d.ts.map +1 -0
  93. package/dist/index.js +31 -0
  94. package/dist/index.js.map +1 -0
  95. package/{src/pipeline/index.ts → dist/pipeline/index.d.ts} +1 -0
  96. package/dist/pipeline/index.d.ts.map +1 -0
  97. package/dist/pipeline/index.js +1 -0
  98. package/dist/pipeline/pipeline.d.ts +63 -0
  99. package/dist/pipeline/pipeline.d.ts.map +1 -0
  100. package/dist/pipeline/pipeline.js +273 -0
  101. package/dist/pipeline/pipeline.test.d.ts +2 -0
  102. package/dist/pipeline/pipeline.test.d.ts.map +1 -0
  103. package/dist/pipeline/pipeline.test.js +1112 -0
  104. package/{src/retry/index.ts → dist/retry/index.d.ts} +1 -1
  105. package/dist/retry/index.d.ts.map +1 -0
  106. package/dist/retry/index.js +1 -0
  107. package/dist/retry/policy.d.ts +43 -0
  108. package/dist/retry/policy.d.ts.map +1 -0
  109. package/dist/retry/policy.js +1 -0
  110. package/dist/retry/standard-policy.d.ts +44 -0
  111. package/dist/retry/standard-policy.d.ts.map +1 -0
  112. package/dist/retry/standard-policy.js +102 -0
  113. package/dist/retry/standard-policy.test.d.ts +2 -0
  114. package/dist/retry/standard-policy.test.d.ts.map +1 -0
  115. package/dist/retry/standard-policy.test.js +196 -0
  116. package/dist/schema/index.d.ts +4 -0
  117. package/dist/schema/index.d.ts.map +1 -0
  118. package/dist/schema/index.js +2 -0
  119. package/dist/schema/registry.d.ts +63 -0
  120. package/dist/schema/registry.d.ts.map +1 -0
  121. package/dist/schema/registry.js +174 -0
  122. package/dist/schema/registry.test.d.ts +2 -0
  123. package/dist/schema/registry.test.d.ts.map +1 -0
  124. package/dist/schema/registry.test.js +278 -0
  125. package/{src/schema/types.ts → dist/schema/types.d.ts} +29 -88
  126. package/dist/schema/types.d.ts.map +1 -0
  127. package/dist/schema/types.js +74 -0
  128. package/dist/schema/types.test.d.ts +2 -0
  129. package/dist/schema/types.test.d.ts.map +1 -0
  130. package/dist/schema/types.test.js +242 -0
  131. package/dist/topology/builder.d.ts +80 -0
  132. package/dist/topology/builder.d.ts.map +1 -0
  133. package/dist/topology/builder.js +165 -0
  134. package/dist/topology/builder.test.d.ts +2 -0
  135. package/dist/topology/builder.test.d.ts.map +1 -0
  136. package/dist/topology/builder.test.js +360 -0
  137. package/dist/topology/index.d.ts +5 -0
  138. package/dist/topology/index.d.ts.map +1 -0
  139. package/dist/topology/index.js +2 -0
  140. package/dist/topology/types.d.ts +128 -0
  141. package/dist/topology/types.d.ts.map +1 -0
  142. package/dist/topology/types.js +28 -0
  143. package/dist/transport/capabilities.d.ts +66 -0
  144. package/dist/transport/capabilities.d.ts.map +1 -0
  145. package/dist/transport/capabilities.js +18 -0
  146. package/dist/transport/connection-manager.d.ts +95 -0
  147. package/dist/transport/connection-manager.d.ts.map +1 -0
  148. package/dist/transport/connection-manager.js +144 -0
  149. package/dist/transport/index.d.ts +11 -0
  150. package/dist/transport/index.d.ts.map +1 -0
  151. package/dist/transport/index.js +5 -0
  152. package/dist/transport/local/local-transport.d.ts +62 -0
  153. package/dist/transport/local/local-transport.d.ts.map +1 -0
  154. package/dist/transport/local/local-transport.js +241 -0
  155. package/dist/transport/local/local-transport.test.d.ts +2 -0
  156. package/dist/transport/local/local-transport.test.d.ts.map +1 -0
  157. package/dist/transport/local/local-transport.test.js +192 -0
  158. package/dist/transport/multi/multi-transport.d.ts +94 -0
  159. package/dist/transport/multi/multi-transport.d.ts.map +1 -0
  160. package/dist/transport/multi/multi-transport.js +184 -0
  161. package/dist/transport/multi/multi-transport.test.d.ts +2 -0
  162. package/dist/transport/multi/multi-transport.test.d.ts.map +1 -0
  163. package/dist/transport/multi/multi-transport.test.js +236 -0
  164. package/dist/transport/rabbitmq/rabbitmq-transport.d.ts +83 -0
  165. package/dist/transport/rabbitmq/rabbitmq-transport.d.ts.map +1 -0
  166. package/dist/transport/rabbitmq/rabbitmq-transport.js +541 -0
  167. package/dist/transport/rabbitmq/rabbitmq-transport.test.d.ts +2 -0
  168. package/dist/transport/rabbitmq/rabbitmq-transport.test.d.ts.map +1 -0
  169. package/dist/transport/rabbitmq/rabbitmq-transport.test.js +100 -0
  170. package/dist/transport/transport.d.ts +152 -0
  171. package/dist/transport/transport.d.ts.map +1 -0
  172. package/dist/transport/transport.js +1 -0
  173. package/{src/types/common.ts → dist/types/common.d.ts} +8 -20
  174. package/dist/types/common.d.ts.map +1 -0
  175. package/dist/types/common.js +12 -0
  176. package/dist/types/dispatcher.d.ts +14 -0
  177. package/dist/types/dispatcher.d.ts.map +1 -0
  178. package/dist/types/dispatcher.js +1 -0
  179. package/dist/types/envelope.d.ts +131 -0
  180. package/dist/types/envelope.d.ts.map +1 -0
  181. package/dist/types/envelope.js +99 -0
  182. package/dist/types/event.d.ts +92 -0
  183. package/dist/types/event.d.ts.map +1 -0
  184. package/dist/types/event.js +26 -0
  185. package/dist/types/event.test.d.ts +2 -0
  186. package/dist/types/event.test.d.ts.map +1 -0
  187. package/dist/types/event.test.js +130 -0
  188. package/dist/types/index.d.ts +11 -0
  189. package/dist/types/index.d.ts.map +1 -0
  190. package/dist/types/index.js +4 -0
  191. package/dist/types/subscriber.d.ts +227 -0
  192. package/dist/types/subscriber.d.ts.map +1 -0
  193. package/dist/types/subscriber.js +97 -0
  194. package/package.json +6 -2
  195. package/examples/config.ts +0 -126
  196. package/examples/event.ts +0 -26
  197. package/examples/order-event.json +0 -19
  198. package/src/checkpoint/context.test.ts +0 -510
  199. package/src/checkpoint/context.ts +0 -213
  200. package/src/checkpoint/index.ts +0 -30
  201. package/src/checkpoint/stores/memory.ts +0 -47
  202. package/src/checkpoint/stores/stores.test.ts +0 -177
  203. package/src/checkpoint/types.ts +0 -147
  204. package/src/codec/codec.ts +0 -42
  205. package/src/codec/header-aware-codec.ts +0 -41
  206. package/src/codec/json-codec.ts +0 -69
  207. package/src/codec/rabbitmq-codec.test.ts +0 -516
  208. package/src/codec/rabbitmq-codec.ts +0 -336
  209. package/src/core/fanout.test.ts +0 -1350
  210. package/src/core/fanout.ts +0 -184
  211. package/src/core/matador.test.ts +0 -575
  212. package/src/core/matador.ts +0 -357
  213. package/src/core/shutdown.test.ts +0 -853
  214. package/src/core/shutdown.ts +0 -165
  215. package/src/errors/checkpoint-errors.ts +0 -62
  216. package/src/errors/has-description.ts +0 -25
  217. package/src/errors/index.ts +0 -58
  218. package/src/errors/matador-errors.ts +0 -477
  219. package/src/errors/retry-errors.test.ts +0 -175
  220. package/src/errors/retry-errors.ts +0 -188
  221. package/src/hooks/index.ts +0 -14
  222. package/src/hooks/safe-hooks.ts +0 -200
  223. package/src/hooks/types.ts +0 -226
  224. package/src/index.ts +0 -242
  225. package/src/pipeline/pipeline.test.ts +0 -1377
  226. package/src/pipeline/pipeline.ts +0 -393
  227. package/src/retry/policy.ts +0 -46
  228. package/src/retry/standard-policy.test.ts +0 -290
  229. package/src/retry/standard-policy.ts +0 -156
  230. package/src/schema/index.ts +0 -16
  231. package/src/schema/registry.test.ts +0 -339
  232. package/src/schema/registry.ts +0 -229
  233. package/src/schema/types.test.ts +0 -280
  234. package/src/topology/builder.test.ts +0 -451
  235. package/src/topology/builder.ts +0 -238
  236. package/src/topology/index.ts +0 -19
  237. package/src/topology/types.ts +0 -183
  238. package/src/transport/capabilities.ts +0 -88
  239. package/src/transport/connection-manager.ts +0 -218
  240. package/src/transport/index.ts +0 -42
  241. package/src/transport/local/local-transport.test.ts +0 -262
  242. package/src/transport/local/local-transport.ts +0 -330
  243. package/src/transport/multi/multi-transport.test.ts +0 -320
  244. package/src/transport/multi/multi-transport.ts +0 -294
  245. package/src/transport/rabbitmq/rabbitmq-transport.test.ts +0 -120
  246. package/src/transport/rabbitmq/rabbitmq-transport.ts +0 -782
  247. package/src/transport/transport.ts +0 -200
  248. package/src/types/dispatcher.ts +0 -18
  249. package/src/types/envelope.ts +0 -244
  250. package/src/types/event.test.ts +0 -157
  251. package/src/types/event.ts +0 -112
  252. package/src/types/index.ts +0 -62
  253. package/src/types/subscriber.ts +0 -333
  254. package/test/e2e/multi-transport.e2e.test.ts +0 -237
  255. package/test/e2e/rabbitmq-transport.e2e.test.ts +0 -618
  256. package/test/e2e/transport-compliance.e2e.test.ts +0 -506
  257. package/test/integration/matador.integration.test.ts +0 -634
  258. package/tsconfig.json +0 -29
  259. package/tsup.config.ts +0 -13
@@ -1,290 +0,0 @@
1
- import { describe, expect, it } from 'bun:test';
2
- import {
3
- DoRetry,
4
- DontRetry,
5
- EventAssertionError,
6
- } from '../errors/retry-errors.js';
7
- import type { MessageReceipt } from '../transport/transport.js';
8
- import { createEnvelope } from '../types/envelope.js';
9
- import type { SubscriberDefinition } from '../types/subscriber.js';
10
- import type { RetryContext, RetryDecision } from './policy.js';
11
- import { StandardRetryPolicy } from './standard-policy.js';
12
-
13
- describe('StandardRetryPolicy', () => {
14
- describe('shouldRetry', () => {
15
- it('should dead-letter on EventAssertionError', () => {
16
- const policy = new StandardRetryPolicy();
17
- const envelope = createEnvelope({
18
- eventKey: 'test.event',
19
- targetSubscriber: 'test-subscriber',
20
- data: { test: 'data' },
21
- importance: 'should-investigate',
22
- });
23
- const context = createContext(
24
- new EventAssertionError(envelope, 'Invalid event'),
25
- );
26
-
27
- const decision = policy.shouldRetry(context);
28
-
29
- expect(decision.action).toBe('dead-letter');
30
- assertDeadLetter(decision);
31
- expect(decision.queue).toBe('undeliverable');
32
- expect(decision.reason).toContain('assertion error');
33
- });
34
-
35
- it('should dead-letter on DontRetry error', () => {
36
- const policy = new StandardRetryPolicy();
37
- const context = createContext(new DontRetry('Business rule violation'));
38
-
39
- const decision = policy.shouldRetry(context);
40
-
41
- expect(decision.action).toBe('dead-letter');
42
- assertDeadLetter(decision);
43
- expect(decision.queue).toBe('undeliverable');
44
- expect(decision.reason).toContain('Business rule violation');
45
- });
46
-
47
- it('should retry on DoRetry error if under max attempts', () => {
48
- const policy = new StandardRetryPolicy({ maxAttempts: 3 });
49
- const context = createContext(new DoRetry('Temporary failure'), {
50
- attemptNumber: 1,
51
- });
52
-
53
- const decision = policy.shouldRetry(context);
54
-
55
- expect(decision.action).toBe('retry');
56
- assertRetry(decision);
57
- expect(decision.delay).toBeGreaterThan(0);
58
- });
59
-
60
- it('should dead-letter on DoRetry error if max attempts exceeded', () => {
61
- const policy = new StandardRetryPolicy({ maxAttempts: 3 });
62
- const context = createContext(new DoRetry('Temporary failure'), {
63
- attemptNumber: 3,
64
- });
65
-
66
- const decision = policy.shouldRetry(context);
67
-
68
- expect(decision.action).toBe('dead-letter');
69
- assertDeadLetter(decision);
70
- expect(decision.reason).toContain('max attempts exceeded');
71
- });
72
-
73
- it('should dead-letter when max attempts exceeded', () => {
74
- const policy = new StandardRetryPolicy({ maxAttempts: 3 });
75
- const context = createContext(new Error('Generic error'), {
76
- attemptNumber: 3,
77
- });
78
-
79
- const decision = policy.shouldRetry(context);
80
-
81
- expect(decision.action).toBe('dead-letter');
82
- assertDeadLetter(decision);
83
- expect(decision.queue).toBe('undeliverable');
84
- expect(decision.reason).toContain('max attempts exceeded (3)');
85
- });
86
-
87
- it('should dead-letter non-idempotent subscriber on redelivery', () => {
88
- const policy = new StandardRetryPolicy();
89
- const context = createContext(
90
- new Error('Some error'),
91
- { attemptNumber: 1, redelivered: true },
92
- { idempotent: 'no' },
93
- );
94
-
95
- const decision = policy.shouldRetry(context);
96
-
97
- expect(decision.action).toBe('dead-letter');
98
- assertDeadLetter(decision);
99
- expect(decision.reason).toContain('Non-idempotent subscriber');
100
- });
101
-
102
- it('should retry idempotent subscriber on redelivery', () => {
103
- const policy = new StandardRetryPolicy();
104
- const context = createContext(
105
- new Error('Some error'),
106
- { attemptNumber: 1, redelivered: true },
107
- { idempotent: 'yes' },
108
- );
109
-
110
- const decision = policy.shouldRetry(context);
111
-
112
- expect(decision.action).toBe('retry');
113
- });
114
-
115
- it('should dead-letter unknown idempotency subscriber on redelivery (unknown == no)', () => {
116
- const policy = new StandardRetryPolicy();
117
- const context = createContext(
118
- new Error('Some error'),
119
- { attemptNumber: 1, redelivered: true },
120
- { idempotent: 'unknown' },
121
- );
122
-
123
- const decision = policy.shouldRetry(context);
124
-
125
- expect(decision.action).toBe('dead-letter');
126
- assertDeadLetter(decision);
127
- expect(decision.reason).toContain('Non-idempotent subscriber');
128
- });
129
-
130
- it('should retry resumable subscriber on redelivery', () => {
131
- const policy = new StandardRetryPolicy();
132
- const context = createContext(
133
- new Error('Some error'),
134
- { attemptNumber: 1, redelivered: true },
135
- { idempotent: 'resumable' },
136
- );
137
-
138
- const decision = policy.shouldRetry(context);
139
-
140
- expect(decision.action).toBe('retry');
141
- });
142
-
143
- it('should retry generic errors with backoff', () => {
144
- const policy = new StandardRetryPolicy({ maxAttempts: 5 });
145
- const context = createContext(new Error('Generic error'), {
146
- attemptNumber: 1,
147
- });
148
-
149
- const decision = policy.shouldRetry(context);
150
-
151
- expect(decision.action).toBe('retry');
152
- assertRetry(decision);
153
- expect(decision.delay).toBeDefined();
154
- });
155
- });
156
-
157
- describe('getDelay', () => {
158
- it('should calculate exponential backoff', () => {
159
- const policy = new StandardRetryPolicy({
160
- baseDelay: 1000,
161
- backoffMultiplier: 2,
162
- });
163
-
164
- const delay1 = policy.getDelay(
165
- createContext(new Error(), { attemptNumber: 1 }),
166
- );
167
- const delay2 = policy.getDelay(
168
- createContext(new Error(), { attemptNumber: 2 }),
169
- );
170
- const delay3 = policy.getDelay(
171
- createContext(new Error(), { attemptNumber: 3 }),
172
- );
173
-
174
- expect(delay1).toBe(1000); // 1000 * 2^0
175
- expect(delay2).toBe(2000); // 1000 * 2^1
176
- expect(delay3).toBe(4000); // 1000 * 2^2
177
- });
178
-
179
- it('should cap delay at maxDelay', () => {
180
- const policy = new StandardRetryPolicy({
181
- baseDelay: 1000,
182
- backoffMultiplier: 10,
183
- maxDelay: 5000,
184
- });
185
-
186
- const delay = policy.getDelay(
187
- createContext(new Error(), { attemptNumber: 5 }),
188
- );
189
-
190
- expect(delay).toBe(5000);
191
- });
192
- });
193
-
194
- describe('configuration', () => {
195
- it('should use default configuration', () => {
196
- const policy = new StandardRetryPolicy();
197
- const context = createContext(new Error(), { attemptNumber: 1 });
198
-
199
- const decision = policy.shouldRetry(context);
200
-
201
- // Default maxAttempts is 3, so should retry on attempt 1
202
- expect(decision.action).toBe('retry');
203
- });
204
-
205
- it('should accept custom configuration', () => {
206
- const policy = new StandardRetryPolicy({
207
- maxAttempts: 1,
208
- baseDelay: 500,
209
- maxDelay: 1000,
210
- backoffMultiplier: 1.5,
211
- });
212
-
213
- // With maxAttempts: 1, first attempt should dead-letter
214
- const context = createContext(new Error(), { attemptNumber: 1 });
215
- const decision = policy.shouldRetry(context);
216
-
217
- expect(decision.action).toBe('dead-letter');
218
- });
219
-
220
- it('should merge partial configuration with defaults', () => {
221
- const policy = new StandardRetryPolicy({ maxAttempts: 10 });
222
- // Use attemptNumber: 5 but deliveryCount: 3 to avoid poison detection
223
- const context = createContext(new Error(), {
224
- attemptNumber: 5,
225
- deliveryCount: 3,
226
- });
227
-
228
- // Should still retry because we increased maxAttempts
229
- const decision = policy.shouldRetry(context);
230
- expect(decision.action).toBe('retry');
231
- });
232
- });
233
- });
234
-
235
- function assertDeadLetter(decision: RetryDecision): asserts decision is {
236
- action: 'dead-letter';
237
- queue: string;
238
- reason: string;
239
- } {
240
- if (decision.action !== 'dead-letter') {
241
- throw new Error(`Expected dead-letter action, got ${decision.action}`);
242
- }
243
- }
244
-
245
- function assertRetry(
246
- decision: RetryDecision,
247
- ): asserts decision is { action: 'retry'; delay: number } {
248
- if (decision.action !== 'retry') {
249
- throw new Error(`Expected retry action, got ${decision.action}`);
250
- }
251
- }
252
-
253
- function createContext(
254
- error: Error,
255
- receiptOverrides: Partial<MessageReceipt> = {},
256
- subscriberOverrides: Partial<SubscriberDefinition> = {},
257
- ): RetryContext {
258
- const receipt: MessageReceipt = {
259
- handle: {},
260
- redelivered: false,
261
- attemptNumber: 1,
262
- deliveryCount:
263
- receiptOverrides.deliveryCount ?? receiptOverrides.attemptNumber ?? 1,
264
- sourceQueue: 'test-queue',
265
- sourceTransport: 'mock',
266
- ...receiptOverrides,
267
- };
268
-
269
- const subscriber: SubscriberDefinition = {
270
- name: 'test-subscriber',
271
- description: 'Test subscriber',
272
- idempotent: 'yes',
273
- importance: 'should-investigate',
274
- ...subscriberOverrides,
275
- };
276
-
277
- const envelope = createEnvelope({
278
- eventKey: 'test.event',
279
- targetSubscriber: 'test-subscriber',
280
- data: { test: 'data' },
281
- importance: 'should-investigate',
282
- });
283
-
284
- return {
285
- envelope,
286
- error,
287
- receipt,
288
- subscriber,
289
- };
290
- }
@@ -1,156 +0,0 @@
1
- import {
2
- IdempotentMessageCannotRetryError,
3
- MessageMaybePoisonedError,
4
- isAssertionError,
5
- isDoRetry,
6
- isDontRetry,
7
- } from '../errors/index.js';
8
- import type { RetryContext, RetryDecision, RetryPolicy } from './policy.js';
9
-
10
- /**
11
- * Configuration for the standard retry policy.
12
- */
13
- export interface StandardRetryPolicyConfig {
14
- /** Maximum number of attempts before dead-lettering */
15
- readonly maxAttempts: number;
16
-
17
- /** Base delay between retries in milliseconds */
18
- readonly baseDelay: number;
19
-
20
- /** Maximum delay between retries in milliseconds */
21
- readonly maxDelay: number;
22
-
23
- /** Multiplier for exponential backoff */
24
- readonly backoffMultiplier: number;
25
-
26
- /**
27
- * Maximum native delivery count before considering message poisoned.
28
- * This prevents crash loops from messages that crash the worker.
29
- * Poison messages are sent directly to the dead-letter queue.
30
- * Default: 5
31
- */
32
- readonly maxDeliveries: number;
33
- }
34
-
35
- /**
36
- * Default configuration values.
37
- */
38
- export const defaultRetryConfig: StandardRetryPolicyConfig = {
39
- maxAttempts: 3,
40
- baseDelay: 1000,
41
- maxDelay: 300000, // 5 minutes
42
- backoffMultiplier: 2,
43
- maxDeliveries: 5,
44
- };
45
-
46
- /**
47
- * Standard retry policy implementing Matador v1 behavior.
48
- *
49
- * Decision logic (in priority order):
50
- * 1. Poison message → dead-letter (prevent crash loops)
51
- * 2. EventAssertionError → dead-letter (never retry)
52
- * 3. DontRetry → dead-letter (explicit no-retry)
53
- * 4. DoRetry → retry if under max attempts
54
- * 5. Max attempts exceeded → dead-letter
55
- * 6. Non-idempotent subscriber on redelivery → dead-letter
56
- * 7. Default → retry with exponential backoff
57
- */
58
- export class StandardRetryPolicy implements RetryPolicy {
59
- private readonly config: StandardRetryPolicyConfig;
60
-
61
- constructor(config: Partial<StandardRetryPolicyConfig> = {}) {
62
- this.config = { ...defaultRetryConfig, ...config };
63
- }
64
-
65
- shouldRetry(context: RetryContext): RetryDecision {
66
- const { envelope, error, subscriber, receipt } = context;
67
- const errorMessage = error.message;
68
-
69
- // 1. Poison message detection - prevent crash loops
70
- if (receipt.deliveryCount >= this.config.maxDeliveries) {
71
- const poisonError = new MessageMaybePoisonedError(
72
- envelope.id,
73
- receipt.deliveryCount,
74
- this.config.maxDeliveries,
75
- );
76
- return {
77
- action: 'dead-letter',
78
- queue: 'undeliverable',
79
- reason: poisonError.message,
80
- };
81
- }
82
-
83
- // 2. Assertion errors never retry
84
- if (isAssertionError(error)) {
85
- return {
86
- action: 'dead-letter',
87
- queue: 'undeliverable',
88
- reason: `assertion error: ${errorMessage}`,
89
- };
90
- }
91
-
92
- // 3. Explicit no-retry
93
- if (isDontRetry(error)) {
94
- return {
95
- action: 'dead-letter',
96
- queue: 'undeliverable',
97
- reason: errorMessage,
98
- };
99
- }
100
-
101
- // 4. Explicit retry request
102
- if (isDoRetry(error)) {
103
- if (receipt.attemptNumber >= this.config.maxAttempts) {
104
- return {
105
- action: 'dead-letter',
106
- queue: 'undeliverable',
107
- reason: `max attempts exceeded (${this.config.maxAttempts}) with forced retry`,
108
- };
109
- }
110
- return {
111
- action: 'retry',
112
- delay: this.getDelay(context),
113
- };
114
- }
115
-
116
- // 5. Max attempts exceeded
117
- if (receipt.attemptNumber >= this.config.maxAttempts) {
118
- return {
119
- action: 'dead-letter',
120
- queue: 'undeliverable',
121
- reason: `max attempts exceeded (${this.config.maxAttempts})`,
122
- };
123
- }
124
-
125
- // 6. Non-idempotent subscriber on redelivery
126
- // 'no' and 'unknown' are treated the same (safer default)
127
- if (
128
- receipt.redelivered &&
129
- (subscriber.idempotent === 'no' || subscriber.idempotent === 'unknown')
130
- ) {
131
- const idempotentError = new IdempotentMessageCannotRetryError(
132
- envelope.id,
133
- subscriber.name,
134
- );
135
- return {
136
- action: 'dead-letter',
137
- queue: 'undeliverable',
138
- reason: idempotentError.message,
139
- };
140
- }
141
-
142
- // 7. Default: retry with backoff
143
- return {
144
- action: 'retry',
145
- delay: this.getDelay(context),
146
- };
147
- }
148
-
149
- getDelay(context: RetryContext): number {
150
- const attempt = context.receipt.attemptNumber;
151
- const delay =
152
- this.config.baseDelay *
153
- Math.pow(this.config.backoffMultiplier, attempt - 1);
154
- return Math.min(delay, this.config.maxDelay);
155
- }
156
- }
@@ -1,16 +0,0 @@
1
- export type {
2
- AnyEventClass,
3
- MatadorSchema,
4
- RegisterOptions,
5
- RuntimeSchemaEntry,
6
- RuntimeSchemaEntryTuple,
7
- SchemaEntry,
8
- SchemaEntryTuple,
9
- SchemaIssue,
10
- SchemaPlugin,
11
- SchemaValidationResult,
12
- } from './types.js';
13
-
14
- export { bind, installPlugins, isSchemaEntryTuple } from './types.js';
15
-
16
- export { SchemaError, SchemaRegistry } from './registry.js';