@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.
Files changed (256) hide show
  1. package/cli.ts +453 -0
  2. package/dist/checkpoint/context.d.ts +59 -0
  3. package/dist/checkpoint/context.d.ts.map +1 -0
  4. package/dist/checkpoint/context.js +140 -0
  5. package/dist/checkpoint/context.test.d.ts +2 -0
  6. package/dist/checkpoint/context.test.d.ts.map +1 -0
  7. package/dist/checkpoint/context.test.js +424 -0
  8. package/dist/checkpoint/index.d.ts +7 -0
  9. package/dist/checkpoint/index.d.ts.map +1 -0
  10. package/dist/checkpoint/index.js +6 -0
  11. package/dist/checkpoint/stores/memory.d.ts +29 -0
  12. package/dist/checkpoint/stores/memory.d.ts.map +1 -0
  13. package/dist/checkpoint/stores/memory.js +39 -0
  14. package/dist/checkpoint/stores/noop.d.ts +14 -0
  15. package/dist/checkpoint/stores/noop.d.ts.map +1 -0
  16. package/dist/checkpoint/stores/noop.js +18 -0
  17. package/dist/checkpoint/stores/stores.test.d.ts +2 -0
  18. package/dist/checkpoint/stores/stores.test.d.ts.map +1 -0
  19. package/dist/checkpoint/stores/stores.test.js +146 -0
  20. package/dist/checkpoint/types.d.ts +119 -0
  21. package/dist/checkpoint/types.d.ts.map +1 -0
  22. package/dist/checkpoint/types.js +1 -0
  23. package/dist/codec/codec.d.ts +29 -0
  24. package/dist/codec/codec.d.ts.map +1 -0
  25. package/dist/codec/codec.js +15 -0
  26. package/dist/codec/header-aware-codec.d.ts +36 -0
  27. package/dist/codec/header-aware-codec.d.ts.map +1 -0
  28. package/dist/codec/header-aware-codec.js +1 -0
  29. package/dist/codec/index.d.ts +6 -0
  30. package/dist/codec/index.d.ts.map +1 -0
  31. package/dist/codec/index.js +3 -0
  32. package/dist/codec/json-codec.d.ts +13 -0
  33. package/dist/codec/json-codec.d.ts.map +1 -0
  34. package/dist/codec/json-codec.js +64 -0
  35. package/dist/codec/rabbitmq-codec.d.ts +28 -0
  36. package/dist/codec/rabbitmq-codec.d.ts.map +1 -0
  37. package/dist/codec/rabbitmq-codec.js +242 -0
  38. package/dist/codec/rabbitmq-codec.test.d.ts +2 -0
  39. package/dist/codec/rabbitmq-codec.test.d.ts.map +1 -0
  40. package/dist/codec/rabbitmq-codec.test.js +433 -0
  41. package/dist/core/fanout.d.ts +59 -0
  42. package/dist/core/fanout.d.ts.map +1 -0
  43. package/dist/core/fanout.js +121 -0
  44. package/dist/core/fanout.test.d.ts +2 -0
  45. package/dist/core/fanout.test.d.ts.map +1 -0
  46. package/dist/core/fanout.test.js +1055 -0
  47. package/dist/core/index.d.ts +7 -0
  48. package/dist/core/index.d.ts.map +1 -0
  49. package/dist/core/index.js +3 -0
  50. package/dist/core/matador.d.ts +133 -0
  51. package/dist/core/matador.d.ts.map +1 -0
  52. package/dist/core/matador.js +222 -0
  53. package/dist/core/matador.test.d.ts +2 -0
  54. package/dist/core/matador.test.d.ts.map +1 -0
  55. package/dist/core/matador.test.js +446 -0
  56. package/dist/core/shutdown.d.ts +78 -0
  57. package/dist/core/shutdown.d.ts.map +1 -0
  58. package/dist/core/shutdown.js +111 -0
  59. package/dist/core/shutdown.test.d.ts +2 -0
  60. package/dist/core/shutdown.test.d.ts.map +1 -0
  61. package/dist/core/shutdown.test.js +524 -0
  62. package/dist/errors/checkpoint-errors.d.ts +30 -0
  63. package/dist/errors/checkpoint-errors.d.ts.map +1 -0
  64. package/dist/errors/checkpoint-errors.js +49 -0
  65. package/dist/errors/has-description.d.ts +18 -0
  66. package/dist/errors/has-description.d.ts.map +1 -0
  67. package/dist/errors/has-description.js +9 -0
  68. package/dist/errors/index.d.ts +6 -0
  69. package/dist/errors/index.d.ts.map +1 -0
  70. package/dist/errors/index.js +22 -0
  71. package/dist/errors/matador-errors.d.ts +183 -0
  72. package/dist/errors/matador-errors.d.ts.map +1 -0
  73. package/dist/errors/matador-errors.js +376 -0
  74. package/dist/errors/retry-errors.d.ts +67 -0
  75. package/dist/errors/retry-errors.d.ts.map +1 -0
  76. package/dist/errors/retry-errors.js +108 -0
  77. package/dist/hooks/index.d.ts +4 -0
  78. package/dist/hooks/index.d.ts.map +1 -0
  79. package/dist/hooks/index.js +2 -0
  80. package/dist/hooks/safe-hooks.d.ts +34 -0
  81. package/dist/hooks/safe-hooks.d.ts.map +1 -0
  82. package/dist/hooks/safe-hooks.js +135 -0
  83. package/dist/hooks/types.d.ts +189 -0
  84. package/dist/hooks/types.d.ts.map +1 -0
  85. package/dist/hooks/types.js +9 -0
  86. package/dist/index.cjs +3156 -0
  87. package/dist/index.cjs.map +1 -0
  88. package/dist/index.d.cts +23 -0
  89. package/dist/index.d.ts +23 -0
  90. package/dist/index.d.ts.map +1 -0
  91. package/dist/index.js +31 -0
  92. package/dist/index.js.map +1 -0
  93. package/dist/pipeline/index.d.ts +3 -0
  94. package/dist/pipeline/index.d.ts.map +1 -0
  95. package/dist/pipeline/index.js +1 -0
  96. package/dist/pipeline/pipeline.d.ts +60 -0
  97. package/dist/pipeline/pipeline.d.ts.map +1 -0
  98. package/dist/pipeline/pipeline.js +261 -0
  99. package/dist/pipeline/pipeline.test.d.ts +2 -0
  100. package/dist/pipeline/pipeline.test.d.ts.map +1 -0
  101. package/dist/pipeline/pipeline.test.js +1065 -0
  102. package/dist/retry/index.d.ts +4 -0
  103. package/dist/retry/index.d.ts.map +1 -0
  104. package/dist/retry/index.js +1 -0
  105. package/dist/retry/policy.d.ts +43 -0
  106. package/dist/retry/policy.d.ts.map +1 -0
  107. package/dist/retry/policy.js +1 -0
  108. package/dist/retry/standard-policy.d.ts +44 -0
  109. package/dist/retry/standard-policy.d.ts.map +1 -0
  110. package/dist/retry/standard-policy.js +102 -0
  111. package/dist/retry/standard-policy.test.d.ts +2 -0
  112. package/dist/retry/standard-policy.test.d.ts.map +1 -0
  113. package/dist/retry/standard-policy.test.js +190 -0
  114. package/dist/schema/index.d.ts +4 -0
  115. package/dist/schema/index.d.ts.map +1 -0
  116. package/dist/schema/index.js +2 -0
  117. package/dist/schema/registry.d.ts +63 -0
  118. package/dist/schema/registry.d.ts.map +1 -0
  119. package/dist/schema/registry.js +171 -0
  120. package/dist/schema/registry.test.d.ts +2 -0
  121. package/dist/schema/registry.test.d.ts.map +1 -0
  122. package/dist/schema/registry.test.js +278 -0
  123. package/dist/schema/types.d.ts +158 -0
  124. package/dist/schema/types.d.ts.map +1 -0
  125. package/dist/schema/types.js +74 -0
  126. package/dist/schema/types.test.d.ts +2 -0
  127. package/dist/schema/types.test.d.ts.map +1 -0
  128. package/dist/schema/types.test.js +243 -0
  129. package/dist/topology/builder.d.ts +66 -0
  130. package/dist/topology/builder.d.ts.map +1 -0
  131. package/dist/topology/builder.js +156 -0
  132. package/dist/topology/builder.test.d.ts +2 -0
  133. package/dist/topology/builder.test.d.ts.map +1 -0
  134. package/dist/topology/builder.test.js +222 -0
  135. package/dist/topology/index.d.ts +5 -0
  136. package/dist/topology/index.d.ts.map +1 -0
  137. package/dist/topology/index.js +2 -0
  138. package/dist/topology/types.d.ts +76 -0
  139. package/dist/topology/types.d.ts.map +1 -0
  140. package/dist/topology/types.js +18 -0
  141. package/dist/transport/capabilities.d.ts +66 -0
  142. package/dist/transport/capabilities.d.ts.map +1 -0
  143. package/dist/transport/capabilities.js +18 -0
  144. package/dist/transport/connection-manager.d.ts +95 -0
  145. package/dist/transport/connection-manager.d.ts.map +1 -0
  146. package/dist/transport/connection-manager.js +144 -0
  147. package/dist/transport/index.d.ts +11 -0
  148. package/dist/transport/index.d.ts.map +1 -0
  149. package/dist/transport/index.js +5 -0
  150. package/dist/transport/local/local-transport.d.ts +62 -0
  151. package/dist/transport/local/local-transport.d.ts.map +1 -0
  152. package/dist/transport/local/local-transport.js +241 -0
  153. package/dist/transport/local/local-transport.test.d.ts +2 -0
  154. package/dist/transport/local/local-transport.test.d.ts.map +1 -0
  155. package/dist/transport/local/local-transport.test.js +192 -0
  156. package/dist/transport/multi/multi-transport.d.ts +94 -0
  157. package/dist/transport/multi/multi-transport.d.ts.map +1 -0
  158. package/dist/transport/multi/multi-transport.js +184 -0
  159. package/dist/transport/multi/multi-transport.test.d.ts +2 -0
  160. package/dist/transport/multi/multi-transport.test.d.ts.map +1 -0
  161. package/dist/transport/multi/multi-transport.test.js +236 -0
  162. package/dist/transport/rabbitmq/rabbitmq-transport.d.ts +73 -0
  163. package/dist/transport/rabbitmq/rabbitmq-transport.d.ts.map +1 -0
  164. package/dist/transport/rabbitmq/rabbitmq-transport.js +518 -0
  165. package/dist/transport/transport.d.ts +152 -0
  166. package/dist/transport/transport.d.ts.map +1 -0
  167. package/dist/transport/transport.js +1 -0
  168. package/dist/types/common.d.ts +41 -0
  169. package/dist/types/common.d.ts.map +1 -0
  170. package/dist/types/common.js +12 -0
  171. package/dist/types/envelope.d.ts +82 -0
  172. package/dist/types/envelope.d.ts.map +1 -0
  173. package/dist/types/envelope.js +36 -0
  174. package/dist/types/event.d.ts +96 -0
  175. package/dist/types/event.d.ts.map +1 -0
  176. package/dist/types/event.js +26 -0
  177. package/dist/types/event.test.d.ts +2 -0
  178. package/dist/types/event.test.d.ts.map +1 -0
  179. package/dist/types/event.test.js +130 -0
  180. package/dist/types/index.d.ts +9 -0
  181. package/dist/types/index.d.ts.map +1 -0
  182. package/dist/types/index.js +4 -0
  183. package/dist/types/subscriber.d.ts +207 -0
  184. package/dist/types/subscriber.d.ts.map +1 -0
  185. package/dist/types/subscriber.js +99 -0
  186. package/examples/config.ts +126 -0
  187. package/examples/event.ts +26 -0
  188. package/examples/order-event.json +19 -0
  189. package/package.json +66 -0
  190. package/src/checkpoint/context.test.ts +510 -0
  191. package/src/checkpoint/context.ts +213 -0
  192. package/src/checkpoint/index.ts +30 -0
  193. package/src/checkpoint/stores/memory.ts +47 -0
  194. package/src/checkpoint/stores/noop.ts +22 -0
  195. package/src/checkpoint/stores/stores.test.ts +177 -0
  196. package/src/checkpoint/types.ts +147 -0
  197. package/src/codec/codec.ts +42 -0
  198. package/src/codec/header-aware-codec.ts +41 -0
  199. package/src/codec/index.ts +11 -0
  200. package/src/codec/json-codec.ts +69 -0
  201. package/src/codec/rabbitmq-codec.test.ts +516 -0
  202. package/src/codec/rabbitmq-codec.ts +336 -0
  203. package/src/core/fanout.test.ts +1351 -0
  204. package/src/core/fanout.ts +184 -0
  205. package/src/core/index.ts +12 -0
  206. package/src/core/matador.test.ts +575 -0
  207. package/src/core/matador.ts +344 -0
  208. package/src/core/shutdown.test.ts +853 -0
  209. package/src/core/shutdown.ts +165 -0
  210. package/src/errors/checkpoint-errors.ts +62 -0
  211. package/src/errors/has-description.ts +25 -0
  212. package/src/errors/index.ts +57 -0
  213. package/src/errors/matador-errors.ts +477 -0
  214. package/src/errors/retry-errors.ts +134 -0
  215. package/src/hooks/index.ts +15 -0
  216. package/src/hooks/safe-hooks.ts +223 -0
  217. package/src/hooks/types.ts +248 -0
  218. package/src/index.ts +231 -0
  219. package/src/pipeline/index.ts +2 -0
  220. package/src/pipeline/pipeline.test.ts +1317 -0
  221. package/src/pipeline/pipeline.ts +371 -0
  222. package/src/retry/index.ts +4 -0
  223. package/src/retry/policy.ts +46 -0
  224. package/src/retry/standard-policy.test.ts +282 -0
  225. package/src/retry/standard-policy.ts +156 -0
  226. package/src/schema/index.ts +16 -0
  227. package/src/schema/registry.test.ts +339 -0
  228. package/src/schema/registry.ts +226 -0
  229. package/src/schema/types.test.ts +281 -0
  230. package/src/schema/types.ts +217 -0
  231. package/src/topology/builder.test.ts +275 -0
  232. package/src/topology/builder.ts +199 -0
  233. package/src/topology/index.ts +15 -0
  234. package/src/topology/types.ts +109 -0
  235. package/src/transport/capabilities.ts +88 -0
  236. package/src/transport/connection-manager.ts +218 -0
  237. package/src/transport/index.ts +42 -0
  238. package/src/transport/local/local-transport.test.ts +262 -0
  239. package/src/transport/local/local-transport.ts +327 -0
  240. package/src/transport/multi/multi-transport.test.ts +320 -0
  241. package/src/transport/multi/multi-transport.ts +294 -0
  242. package/src/transport/rabbitmq/rabbitmq-transport.ts +753 -0
  243. package/src/transport/transport.ts +200 -0
  244. package/src/types/common.ts +53 -0
  245. package/src/types/envelope.ts +152 -0
  246. package/src/types/event.test.ts +157 -0
  247. package/src/types/event.ts +118 -0
  248. package/src/types/index.ts +52 -0
  249. package/src/types/subscriber.ts +310 -0
  250. package/test/e2e/multi-transport.e2e.test.ts +236 -0
  251. package/test/e2e/rabbitmq-transport.e2e.test.ts +327 -0
  252. package/test/e2e/transport-compliance.e2e.test.ts +505 -0
  253. package/test/integration/matador.integration.test.ts +634 -0
  254. package/tsconfig.json +29 -0
  255. package/tsconfig.tsbuildinfo +1 -0
  256. package/tsup.config.ts +13 -0
@@ -0,0 +1,4 @@
1
+ export type { RetryContext, RetryDecision, RetryPolicy } from './policy.js';
2
+ export type { StandardRetryPolicyConfig } from './standard-policy.js';
3
+ export { defaultRetryConfig, StandardRetryPolicy } from './standard-policy.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/retry/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE5E,YAAY,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AACtE,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC"}
@@ -0,0 +1 @@
1
+ export { defaultRetryConfig, StandardRetryPolicy } from './standard-policy.js';
@@ -0,0 +1,43 @@
1
+ import type { MessageReceipt } from '../transport/index.js';
2
+ import type { Envelope, SubscriberDefinition } from '../types/index.js';
3
+ /**
4
+ * Context provided to retry policy for decision making.
5
+ */
6
+ export interface RetryContext {
7
+ /** The message envelope */
8
+ readonly envelope: Envelope;
9
+ /** The error that caused the failure */
10
+ readonly error: Error;
11
+ /** The subscriber definition */
12
+ readonly subscriber: SubscriberDefinition;
13
+ /** Message receipt with delivery information */
14
+ readonly receipt: MessageReceipt;
15
+ }
16
+ /**
17
+ * Decision returned by retry policy.
18
+ */
19
+ export type RetryDecision = {
20
+ readonly action: 'retry';
21
+ readonly delay: number;
22
+ } | {
23
+ readonly action: 'dead-letter';
24
+ readonly queue: string;
25
+ readonly reason: string;
26
+ } | {
27
+ readonly action: 'discard';
28
+ readonly reason: string;
29
+ };
30
+ /**
31
+ * Interface for retry policies.
32
+ */
33
+ export interface RetryPolicy {
34
+ /**
35
+ * Determines what to do with a failed message.
36
+ */
37
+ shouldRetry(context: RetryContext): RetryDecision;
38
+ /**
39
+ * Calculates the delay for a retry attempt.
40
+ */
41
+ getDelay(context: RetryContext): number;
42
+ }
43
+ //# sourceMappingURL=policy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../../src/retry/policy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,QAAQ,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAExE;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,2BAA2B;IAC3B,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAE5B,wCAAwC;IACxC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IAEtB,gCAAgC;IAChC,QAAQ,CAAC,UAAU,EAAE,oBAAoB,CAAC;IAE1C,gDAAgD;IAChD,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;CAClC;AAED;;GAEG;AACH,MAAM,MAAM,aAAa,GACrB;IAAE,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACpD;IACE,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB,GACD;IAAE,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE5D;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B;;OAEG;IACH,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,aAAa,CAAC;IAElD;;OAEG;IACH,QAAQ,CAAC,OAAO,EAAE,YAAY,GAAG,MAAM,CAAC;CACzC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,44 @@
1
+ import type { RetryContext, RetryDecision, RetryPolicy } from './policy.js';
2
+ /**
3
+ * Configuration for the standard retry policy.
4
+ */
5
+ export interface StandardRetryPolicyConfig {
6
+ /** Maximum number of attempts before dead-lettering */
7
+ readonly maxAttempts: number;
8
+ /** Base delay between retries in milliseconds */
9
+ readonly baseDelay: number;
10
+ /** Maximum delay between retries in milliseconds */
11
+ readonly maxDelay: number;
12
+ /** Multiplier for exponential backoff */
13
+ readonly backoffMultiplier: number;
14
+ /**
15
+ * Maximum native delivery count before considering message poisoned.
16
+ * This prevents crash loops from messages that crash the worker.
17
+ * Poison messages are sent directly to the dead-letter queue.
18
+ * Default: 5
19
+ */
20
+ readonly maxDeliveries: number;
21
+ }
22
+ /**
23
+ * Default configuration values.
24
+ */
25
+ export declare const defaultRetryConfig: StandardRetryPolicyConfig;
26
+ /**
27
+ * Standard retry policy implementing Matador v1 behavior.
28
+ *
29
+ * Decision logic (in priority order):
30
+ * 1. Poison message → dead-letter (prevent crash loops)
31
+ * 2. EventAssertionError → dead-letter (never retry)
32
+ * 3. DontRetry → dead-letter (explicit no-retry)
33
+ * 4. DoRetry → retry if under max attempts
34
+ * 5. Max attempts exceeded → dead-letter
35
+ * 6. Non-idempotent subscriber on redelivery → dead-letter
36
+ * 7. Default → retry with exponential backoff
37
+ */
38
+ export declare class StandardRetryPolicy implements RetryPolicy {
39
+ private readonly config;
40
+ constructor(config?: Partial<StandardRetryPolicyConfig>);
41
+ shouldRetry(context: RetryContext): RetryDecision;
42
+ getDelay(context: RetryContext): number;
43
+ }
44
+ //# sourceMappingURL=standard-policy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"standard-policy.d.ts","sourceRoot":"","sources":["../../src/retry/standard-policy.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE5E;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC,uDAAuD;IACvD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAE7B,iDAAiD;IACjD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAE3B,oDAAoD;IACpD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAE1B,yCAAyC;IACzC,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IAEnC;;;;;OAKG;IACH,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AAED;;GAEG;AACH,eAAO,MAAM,kBAAkB,EAAE,yBAMhC,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,qBAAa,mBAAoB,YAAW,WAAW;IACrD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA4B;gBAEvC,MAAM,GAAE,OAAO,CAAC,yBAAyB,CAAM;IAI3D,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,aAAa;IAoFjD,QAAQ,CAAC,OAAO,EAAE,YAAY,GAAG,MAAM;CAOxC"}
@@ -0,0 +1,102 @@
1
+ import { IdempotentMessageCannotRetryError, MessageMaybePoisonedError, isAssertionError, isDoRetry, isDontRetry, } from '../errors/index.js';
2
+ /**
3
+ * Default configuration values.
4
+ */
5
+ export const defaultRetryConfig = {
6
+ maxAttempts: 3,
7
+ baseDelay: 1000,
8
+ maxDelay: 300000, // 5 minutes
9
+ backoffMultiplier: 2,
10
+ maxDeliveries: 5,
11
+ };
12
+ /**
13
+ * Standard retry policy implementing Matador v1 behavior.
14
+ *
15
+ * Decision logic (in priority order):
16
+ * 1. Poison message → dead-letter (prevent crash loops)
17
+ * 2. EventAssertionError → dead-letter (never retry)
18
+ * 3. DontRetry → dead-letter (explicit no-retry)
19
+ * 4. DoRetry → retry if under max attempts
20
+ * 5. Max attempts exceeded → dead-letter
21
+ * 6. Non-idempotent subscriber on redelivery → dead-letter
22
+ * 7. Default → retry with exponential backoff
23
+ */
24
+ export class StandardRetryPolicy {
25
+ config;
26
+ constructor(config = {}) {
27
+ this.config = { ...defaultRetryConfig, ...config };
28
+ }
29
+ shouldRetry(context) {
30
+ const { envelope, error, subscriber, receipt } = context;
31
+ const errorMessage = error.message;
32
+ // 1. Poison message detection - prevent crash loops
33
+ if (receipt.deliveryCount >= this.config.maxDeliveries) {
34
+ const poisonError = new MessageMaybePoisonedError(envelope.id, receipt.deliveryCount, this.config.maxDeliveries);
35
+ return {
36
+ action: 'dead-letter',
37
+ queue: 'undeliverable',
38
+ reason: poisonError.message,
39
+ };
40
+ }
41
+ // 2. Assertion errors never retry
42
+ if (isAssertionError(error)) {
43
+ return {
44
+ action: 'dead-letter',
45
+ queue: 'undeliverable',
46
+ reason: `assertion error: ${errorMessage}`,
47
+ };
48
+ }
49
+ // 3. Explicit no-retry
50
+ if (isDontRetry(error)) {
51
+ return {
52
+ action: 'dead-letter',
53
+ queue: 'undeliverable',
54
+ reason: errorMessage,
55
+ };
56
+ }
57
+ // 4. Explicit retry request
58
+ if (isDoRetry(error)) {
59
+ if (receipt.attemptNumber >= this.config.maxAttempts) {
60
+ return {
61
+ action: 'dead-letter',
62
+ queue: 'undeliverable',
63
+ reason: `max attempts exceeded (${this.config.maxAttempts}) with forced retry`,
64
+ };
65
+ }
66
+ return {
67
+ action: 'retry',
68
+ delay: this.getDelay(context),
69
+ };
70
+ }
71
+ // 5. Max attempts exceeded
72
+ if (receipt.attemptNumber >= this.config.maxAttempts) {
73
+ return {
74
+ action: 'dead-letter',
75
+ queue: 'undeliverable',
76
+ reason: `max attempts exceeded (${this.config.maxAttempts})`,
77
+ };
78
+ }
79
+ // 6. Non-idempotent subscriber on redelivery
80
+ // 'no' and 'unknown' are treated the same (safer default)
81
+ if (receipt.redelivered &&
82
+ (subscriber.idempotent === 'no' || subscriber.idempotent === 'unknown')) {
83
+ const idempotentError = new IdempotentMessageCannotRetryError(envelope.id, subscriber.name);
84
+ return {
85
+ action: 'dead-letter',
86
+ queue: 'undeliverable',
87
+ reason: idempotentError.message,
88
+ };
89
+ }
90
+ // 7. Default: retry with backoff
91
+ return {
92
+ action: 'retry',
93
+ delay: this.getDelay(context),
94
+ };
95
+ }
96
+ getDelay(context) {
97
+ const attempt = context.receipt.attemptNumber;
98
+ const delay = this.config.baseDelay *
99
+ Math.pow(this.config.backoffMultiplier, attempt - 1);
100
+ return Math.min(delay, this.config.maxDelay);
101
+ }
102
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=standard-policy.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"standard-policy.test.d.ts","sourceRoot":"","sources":["../../src/retry/standard-policy.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,190 @@
1
+ import { describe, expect, it } from 'bun:test';
2
+ import { DoRetry, DontRetry, EventAssertionError, } from '../errors/retry-errors.js';
3
+ import { createEnvelope } from '../types/envelope.js';
4
+ import { StandardRetryPolicy } from './standard-policy.js';
5
+ describe('StandardRetryPolicy', () => {
6
+ describe('shouldRetry', () => {
7
+ it('should dead-letter on EventAssertionError', () => {
8
+ const policy = new StandardRetryPolicy();
9
+ const context = createContext(new EventAssertionError('Invalid event'));
10
+ const decision = policy.shouldRetry(context);
11
+ expect(decision.action).toBe('dead-letter');
12
+ assertDeadLetter(decision);
13
+ expect(decision.queue).toBe('undeliverable');
14
+ expect(decision.reason).toContain('assertion error');
15
+ });
16
+ it('should dead-letter on DontRetry error', () => {
17
+ const policy = new StandardRetryPolicy();
18
+ const context = createContext(new DontRetry('Business rule violation'));
19
+ const decision = policy.shouldRetry(context);
20
+ expect(decision.action).toBe('dead-letter');
21
+ assertDeadLetter(decision);
22
+ expect(decision.queue).toBe('undeliverable');
23
+ expect(decision.reason).toContain('Business rule violation');
24
+ });
25
+ it('should retry on DoRetry error if under max attempts', () => {
26
+ const policy = new StandardRetryPolicy({ maxAttempts: 3 });
27
+ const context = createContext(new DoRetry('Temporary failure'), {
28
+ attemptNumber: 1,
29
+ });
30
+ const decision = policy.shouldRetry(context);
31
+ expect(decision.action).toBe('retry');
32
+ assertRetry(decision);
33
+ expect(decision.delay).toBeGreaterThan(0);
34
+ });
35
+ it('should dead-letter on DoRetry error if max attempts exceeded', () => {
36
+ const policy = new StandardRetryPolicy({ maxAttempts: 3 });
37
+ const context = createContext(new DoRetry('Temporary failure'), {
38
+ attemptNumber: 3,
39
+ });
40
+ const decision = policy.shouldRetry(context);
41
+ expect(decision.action).toBe('dead-letter');
42
+ assertDeadLetter(decision);
43
+ expect(decision.reason).toContain('max attempts exceeded');
44
+ });
45
+ it('should dead-letter when max attempts exceeded', () => {
46
+ const policy = new StandardRetryPolicy({ maxAttempts: 3 });
47
+ const context = createContext(new Error('Generic error'), {
48
+ attemptNumber: 3,
49
+ });
50
+ const decision = policy.shouldRetry(context);
51
+ expect(decision.action).toBe('dead-letter');
52
+ assertDeadLetter(decision);
53
+ expect(decision.queue).toBe('undeliverable');
54
+ expect(decision.reason).toContain('max attempts exceeded (3)');
55
+ });
56
+ it('should dead-letter non-idempotent subscriber on redelivery', () => {
57
+ const policy = new StandardRetryPolicy();
58
+ const context = createContext(new Error('Some error'), { attemptNumber: 1, redelivered: true }, { idempotent: 'no' });
59
+ const decision = policy.shouldRetry(context);
60
+ expect(decision.action).toBe('dead-letter');
61
+ assertDeadLetter(decision);
62
+ expect(decision.reason).toContain('Non-idempotent subscriber');
63
+ });
64
+ it('should retry idempotent subscriber on redelivery', () => {
65
+ const policy = new StandardRetryPolicy();
66
+ const context = createContext(new Error('Some error'), { attemptNumber: 1, redelivered: true }, { idempotent: 'yes' });
67
+ const decision = policy.shouldRetry(context);
68
+ expect(decision.action).toBe('retry');
69
+ });
70
+ it('should dead-letter unknown idempotency subscriber on redelivery (unknown == no)', () => {
71
+ const policy = new StandardRetryPolicy();
72
+ const context = createContext(new Error('Some error'), { attemptNumber: 1, redelivered: true }, { idempotent: 'unknown' });
73
+ const decision = policy.shouldRetry(context);
74
+ expect(decision.action).toBe('dead-letter');
75
+ assertDeadLetter(decision);
76
+ expect(decision.reason).toContain('Non-idempotent subscriber');
77
+ });
78
+ it('should retry resumable subscriber on redelivery', () => {
79
+ const policy = new StandardRetryPolicy();
80
+ const context = createContext(new Error('Some error'), { attemptNumber: 1, redelivered: true }, { idempotent: 'resumable' });
81
+ const decision = policy.shouldRetry(context);
82
+ expect(decision.action).toBe('retry');
83
+ });
84
+ it('should retry generic errors with backoff', () => {
85
+ const policy = new StandardRetryPolicy({ maxAttempts: 5 });
86
+ const context = createContext(new Error('Generic error'), {
87
+ attemptNumber: 1,
88
+ });
89
+ const decision = policy.shouldRetry(context);
90
+ expect(decision.action).toBe('retry');
91
+ assertRetry(decision);
92
+ expect(decision.delay).toBeDefined();
93
+ });
94
+ });
95
+ describe('getDelay', () => {
96
+ it('should calculate exponential backoff', () => {
97
+ const policy = new StandardRetryPolicy({
98
+ baseDelay: 1000,
99
+ backoffMultiplier: 2,
100
+ });
101
+ const delay1 = policy.getDelay(createContext(new Error(), { attemptNumber: 1 }));
102
+ const delay2 = policy.getDelay(createContext(new Error(), { attemptNumber: 2 }));
103
+ const delay3 = policy.getDelay(createContext(new Error(), { attemptNumber: 3 }));
104
+ expect(delay1).toBe(1000); // 1000 * 2^0
105
+ expect(delay2).toBe(2000); // 1000 * 2^1
106
+ expect(delay3).toBe(4000); // 1000 * 2^2
107
+ });
108
+ it('should cap delay at maxDelay', () => {
109
+ const policy = new StandardRetryPolicy({
110
+ baseDelay: 1000,
111
+ backoffMultiplier: 10,
112
+ maxDelay: 5000,
113
+ });
114
+ const delay = policy.getDelay(createContext(new Error(), { attemptNumber: 5 }));
115
+ expect(delay).toBe(5000);
116
+ });
117
+ });
118
+ describe('configuration', () => {
119
+ it('should use default configuration', () => {
120
+ const policy = new StandardRetryPolicy();
121
+ const context = createContext(new Error(), { attemptNumber: 1 });
122
+ const decision = policy.shouldRetry(context);
123
+ // Default maxAttempts is 3, so should retry on attempt 1
124
+ expect(decision.action).toBe('retry');
125
+ });
126
+ it('should accept custom configuration', () => {
127
+ const policy = new StandardRetryPolicy({
128
+ maxAttempts: 1,
129
+ baseDelay: 500,
130
+ maxDelay: 1000,
131
+ backoffMultiplier: 1.5,
132
+ });
133
+ // With maxAttempts: 1, first attempt should dead-letter
134
+ const context = createContext(new Error(), { attemptNumber: 1 });
135
+ const decision = policy.shouldRetry(context);
136
+ expect(decision.action).toBe('dead-letter');
137
+ });
138
+ it('should merge partial configuration with defaults', () => {
139
+ const policy = new StandardRetryPolicy({ maxAttempts: 10 });
140
+ // Use attemptNumber: 5 but deliveryCount: 3 to avoid poison detection
141
+ const context = createContext(new Error(), {
142
+ attemptNumber: 5,
143
+ deliveryCount: 3,
144
+ });
145
+ // Should still retry because we increased maxAttempts
146
+ const decision = policy.shouldRetry(context);
147
+ expect(decision.action).toBe('retry');
148
+ });
149
+ });
150
+ });
151
+ function assertDeadLetter(decision) {
152
+ if (decision.action !== 'dead-letter') {
153
+ throw new Error(`Expected dead-letter action, got ${decision.action}`);
154
+ }
155
+ }
156
+ function assertRetry(decision) {
157
+ if (decision.action !== 'retry') {
158
+ throw new Error(`Expected retry action, got ${decision.action}`);
159
+ }
160
+ }
161
+ function createContext(error, receiptOverrides = {}, subscriberOverrides = {}) {
162
+ const receipt = {
163
+ handle: {},
164
+ redelivered: false,
165
+ attemptNumber: 1,
166
+ deliveryCount: receiptOverrides.deliveryCount ?? receiptOverrides.attemptNumber ?? 1,
167
+ sourceQueue: 'test-queue',
168
+ sourceTransport: 'mock',
169
+ ...receiptOverrides,
170
+ };
171
+ const subscriber = {
172
+ name: 'test-subscriber',
173
+ description: 'Test subscriber',
174
+ idempotent: 'yes',
175
+ importance: 'should-investigate',
176
+ ...subscriberOverrides,
177
+ };
178
+ const envelope = createEnvelope({
179
+ eventKey: 'test.event',
180
+ targetSubscriber: 'test-subscriber',
181
+ data: { test: 'data' },
182
+ importance: 'should-investigate',
183
+ });
184
+ return {
185
+ envelope,
186
+ error,
187
+ receipt,
188
+ subscriber,
189
+ };
190
+ }
@@ -0,0 +1,4 @@
1
+ export type { AnyEventClass, MatadorSchema, RegisterOptions, RuntimeSchemaEntry, RuntimeSchemaEntryTuple, SchemaEntry, SchemaEntryTuple, SchemaIssue, SchemaPlugin, SchemaValidationResult, } from './types.js';
2
+ export { bind, installPlugins, isSchemaEntryTuple } from './types.js';
3
+ export { SchemaError, SchemaRegistry } from './registry.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/schema/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,aAAa,EACb,aAAa,EACb,eAAe,EACf,kBAAkB,EAClB,uBAAuB,EACvB,WAAW,EACX,gBAAgB,EAChB,WAAW,EACX,YAAY,EACZ,sBAAsB,GACvB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEtE,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { bind, installPlugins, isSchemaEntryTuple } from './types.js';
2
+ export { SchemaError, SchemaRegistry } from './registry.js';
@@ -0,0 +1,63 @@
1
+ import type { HasDescription } from '../errors/index.js';
2
+ import type { AnySubscriber, EventClass, MatadorEvent, Subscriber, SubscriberDefinition } from '../types/index.js';
3
+ import type { RegisterOptions, SchemaValidationResult } from './types.js';
4
+ /**
5
+ * Error thrown when schema operations fail.
6
+ */
7
+ export declare class SchemaError extends Error implements HasDescription {
8
+ readonly description: string;
9
+ constructor(message: string);
10
+ }
11
+ /**
12
+ * Registry for managing event-subscriber relationships.
13
+ */
14
+ export declare class SchemaRegistry {
15
+ private readonly entries;
16
+ private readonly aliases;
17
+ /**
18
+ * Registers an event class with its subscribers.
19
+ */
20
+ register<T>(eventClass: EventClass<T>, subscribers: readonly AnySubscriber[], options?: RegisterOptions): void;
21
+ /**
22
+ * Gets the event class for a given key.
23
+ */
24
+ getEventClass(key: string): EventClass | undefined;
25
+ /**
26
+ * Gets all subscribers for an event key.
27
+ */
28
+ getSubscribers(eventKey: string): readonly AnySubscriber[];
29
+ /**
30
+ * Gets a specific subscriber by event key and subscriber name.
31
+ */
32
+ getSubscriber(eventKey: string, subscriberName: string): AnySubscriber | undefined;
33
+ /**
34
+ * Gets a subscriber definition for the pipeline.
35
+ */
36
+ getSubscriberDefinition(eventKey: string, subscriberName: string): SubscriberDefinition | undefined;
37
+ /**
38
+ * Gets a subscriber with callback for execution.
39
+ */
40
+ getExecutableSubscriber(eventKey: string, subscriberName: string): Subscriber<MatadorEvent<unknown>> | undefined;
41
+ /**
42
+ * Gets the event class by alias.
43
+ */
44
+ getEventByAlias(alias: string): EventClass | undefined;
45
+ /**
46
+ * Checks if an event key is registered.
47
+ */
48
+ hasEvent(key: string): boolean;
49
+ /**
50
+ * Gets all registered event keys.
51
+ */
52
+ getEventKeys(): readonly string[];
53
+ /**
54
+ * Validates the schema for issues.
55
+ */
56
+ validate(): SchemaValidationResult;
57
+ /**
58
+ * Clears all registered entries.
59
+ */
60
+ clear(): void;
61
+ private getEntry;
62
+ }
63
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/schema/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EACV,aAAa,EACb,UAAU,EACV,YAAY,EACZ,UAAU,EACV,oBAAoB,EACrB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,KAAK,EACV,eAAe,EAGf,sBAAsB,EACvB,MAAM,YAAY,CAAC;AAEpB;;GAEG;AACH,qBAAa,WAAY,SAAQ,KAAM,YAAW,cAAc;IAC9D,QAAQ,CAAC,WAAW,SAIM;gBAEd,OAAO,EAAE,MAAM;CAI5B;AAED;;GAEG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAkC;IAC1D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA6B;IAErD;;OAEG;IACH,QAAQ,CAAC,CAAC,EACR,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,EACzB,WAAW,EAAE,SAAS,aAAa,EAAE,EACrC,OAAO,GAAE,eAAoB,GAC5B,IAAI;IA6BP;;OAEG;IACH,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;IAalD;;OAEG;IACH,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,aAAa,EAAE;IAK1D;;OAEG;IACH,aAAa,CACX,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,MAAM,GACrB,aAAa,GAAG,SAAS;IAK5B;;OAEG;IACH,uBAAuB,CACrB,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,MAAM,GACrB,oBAAoB,GAAG,SAAS;IAkBnC;;OAEG;IACH,uBAAuB,CACrB,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,MAAM,GACrB,UAAU,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,GAAG,SAAS;IAMhD;;OAEG;IACH,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;IAMtD;;OAEG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAI9B;;OAEG;IACH,YAAY,IAAI,SAAS,MAAM,EAAE;IAIjC;;OAEG;IACH,QAAQ,IAAI,sBAAsB;IAkClC;;OAEG;IACH,KAAK,IAAI,IAAI;IAKb,OAAO,CAAC,QAAQ;CAWjB"}
@@ -0,0 +1,171 @@
1
+ import { isSubscriber } from '../types/index.js';
2
+ /**
3
+ * Error thrown when schema operations fail.
4
+ */
5
+ export class SchemaError extends Error {
6
+ description = 'A schema operation failed, such as registering a duplicate event or ' +
7
+ 'conflicting alias. This is a configuration error that should be fixed ' +
8
+ 'during development. Review the event and subscriber registrations to ' +
9
+ 'resolve the conflict.';
10
+ constructor(message) {
11
+ super(message);
12
+ this.name = 'SchemaError';
13
+ }
14
+ }
15
+ /**
16
+ * Registry for managing event-subscriber relationships.
17
+ */
18
+ export class SchemaRegistry {
19
+ entries = new Map();
20
+ aliases = new Map();
21
+ /**
22
+ * Registers an event class with its subscribers.
23
+ */
24
+ register(eventClass, subscribers, options = {}) {
25
+ const key = eventClass.key;
26
+ if (this.entries.has(key) && !options.override) {
27
+ throw new SchemaError(`Event "${key}" is already registered`);
28
+ }
29
+ this.entries.set(key, {
30
+ eventClass,
31
+ subscribers,
32
+ });
33
+ // Register aliases
34
+ if (eventClass.aliases) {
35
+ for (const alias of eventClass.aliases) {
36
+ if (this.aliases.has(alias) &&
37
+ this.aliases.get(alias) !== key &&
38
+ !options.override) {
39
+ throw new SchemaError(`Alias "${alias}" is already registered for event "${this.aliases.get(alias)}"`);
40
+ }
41
+ this.aliases.set(alias, key);
42
+ }
43
+ }
44
+ }
45
+ /**
46
+ * Gets the event class for a given key.
47
+ */
48
+ getEventClass(key) {
49
+ const entry = this.entries.get(key);
50
+ if (entry)
51
+ return entry.eventClass;
52
+ // Check aliases
53
+ const aliasKey = this.aliases.get(key);
54
+ if (aliasKey) {
55
+ return this.entries.get(aliasKey)?.eventClass;
56
+ }
57
+ return undefined;
58
+ }
59
+ /**
60
+ * Gets all subscribers for an event key.
61
+ */
62
+ getSubscribers(eventKey) {
63
+ const entry = this.getEntry(eventKey);
64
+ return entry?.subscribers ?? [];
65
+ }
66
+ /**
67
+ * Gets a specific subscriber by event key and subscriber name.
68
+ */
69
+ getSubscriber(eventKey, subscriberName) {
70
+ const subscribers = this.getSubscribers(eventKey);
71
+ return subscribers.find((s) => s.name === subscriberName);
72
+ }
73
+ /**
74
+ * Gets a subscriber definition for the pipeline.
75
+ */
76
+ getSubscriberDefinition(eventKey, subscriberName) {
77
+ const subscriber = this.getSubscriber(eventKey, subscriberName);
78
+ if (!subscriber)
79
+ return undefined;
80
+ const def = {
81
+ name: subscriber.name,
82
+ description: subscriber.description,
83
+ idempotent: subscriber.idempotent ?? 'unknown',
84
+ importance: subscriber.importance ?? 'should-investigate',
85
+ };
86
+ if (subscriber.targetQueue !== undefined) {
87
+ def.targetQueue = subscriber.targetQueue;
88
+ }
89
+ return def;
90
+ }
91
+ /**
92
+ * Gets a subscriber with callback for execution.
93
+ */
94
+ getExecutableSubscriber(eventKey, subscriberName) {
95
+ const subscriber = this.getSubscriber(eventKey, subscriberName);
96
+ if (!subscriber || !isSubscriber(subscriber))
97
+ return undefined;
98
+ return subscriber;
99
+ }
100
+ /**
101
+ * Gets the event class by alias.
102
+ */
103
+ getEventByAlias(alias) {
104
+ const key = this.aliases.get(alias);
105
+ if (!key)
106
+ return undefined;
107
+ return this.entries.get(key)?.eventClass;
108
+ }
109
+ /**
110
+ * Checks if an event key is registered.
111
+ */
112
+ hasEvent(key) {
113
+ return this.entries.has(key) || this.aliases.has(key);
114
+ }
115
+ /**
116
+ * Gets all registered event keys.
117
+ */
118
+ getEventKeys() {
119
+ return [...this.entries.keys()];
120
+ }
121
+ /**
122
+ * Validates the schema for issues.
123
+ */
124
+ validate() {
125
+ const issues = [];
126
+ for (const [key, entry] of this.entries) {
127
+ // Check for duplicate subscriber names
128
+ const names = new Set();
129
+ for (const subscriber of entry.subscribers) {
130
+ if (names.has(subscriber.name)) {
131
+ issues.push({
132
+ severity: 'error',
133
+ eventKey: key,
134
+ message: `Duplicate subscriber name: "${subscriber.name}"`,
135
+ });
136
+ }
137
+ names.add(subscriber.name);
138
+ }
139
+ // Check for subscribers without callbacks (stubs in consuming instance)
140
+ const stubs = entry.subscribers.filter((s) => !isSubscriber(s));
141
+ if (stubs.length === entry.subscribers.length && stubs.length > 0) {
142
+ issues.push({
143
+ severity: 'warning',
144
+ eventKey: key,
145
+ message: 'All subscribers are stubs, no local handlers',
146
+ });
147
+ }
148
+ }
149
+ return {
150
+ valid: issues.filter((i) => i.severity === 'error').length === 0,
151
+ issues,
152
+ };
153
+ }
154
+ /**
155
+ * Clears all registered entries.
156
+ */
157
+ clear() {
158
+ this.entries.clear();
159
+ this.aliases.clear();
160
+ }
161
+ getEntry(eventKey) {
162
+ const entry = this.entries.get(eventKey);
163
+ if (entry)
164
+ return entry;
165
+ const aliasKey = this.aliases.get(eventKey);
166
+ if (aliasKey) {
167
+ return this.entries.get(aliasKey);
168
+ }
169
+ return undefined;
170
+ }
171
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=registry.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.test.d.ts","sourceRoot":"","sources":["../../src/schema/registry.test.ts"],"names":[],"mappings":""}