@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,146 @@
1
+ import { beforeEach, describe, expect, it } from 'bun:test';
2
+ import { MemoryCheckpointStore } from './memory.js';
3
+ import { NoOpCheckpointStore } from './noop.js';
4
+ describe('MemoryCheckpointStore', () => {
5
+ let store;
6
+ beforeEach(() => {
7
+ store = new MemoryCheckpointStore();
8
+ });
9
+ describe('get()', () => {
10
+ it('should return undefined for non-existent checkpoint', async () => {
11
+ const result = await store.get('non-existent');
12
+ expect(result).toBeUndefined();
13
+ });
14
+ it('should return stored checkpoint', async () => {
15
+ const checkpoint = {
16
+ envelopeId: 'test-id',
17
+ subscriberName: 'test-subscriber',
18
+ completedSteps: { 'step-1': 'result-1' },
19
+ };
20
+ await store.set('test-id', checkpoint);
21
+ const result = await store.get('test-id');
22
+ expect(result).toEqual(checkpoint);
23
+ });
24
+ });
25
+ describe('set()', () => {
26
+ it('should store checkpoint', async () => {
27
+ const checkpoint = {
28
+ envelopeId: 'test-id',
29
+ subscriberName: 'test-subscriber',
30
+ completedSteps: {},
31
+ };
32
+ await store.set('test-id', checkpoint);
33
+ expect(store.size).toBe(1);
34
+ });
35
+ it('should overwrite existing checkpoint', async () => {
36
+ const checkpoint1 = {
37
+ envelopeId: 'test-id',
38
+ subscriberName: 'test-subscriber',
39
+ completedSteps: { 'step-1': 'old' },
40
+ };
41
+ const checkpoint2 = {
42
+ envelopeId: 'test-id',
43
+ subscriberName: 'test-subscriber',
44
+ completedSteps: { 'step-1': 'new', 'step-2': 'added' },
45
+ };
46
+ await store.set('test-id', checkpoint1);
47
+ await store.set('test-id', checkpoint2);
48
+ const result = await store.get('test-id');
49
+ expect(result).toEqual(checkpoint2);
50
+ expect(store.size).toBe(1);
51
+ });
52
+ });
53
+ describe('delete()', () => {
54
+ it('should delete checkpoint', async () => {
55
+ const checkpoint = {
56
+ envelopeId: 'test-id',
57
+ subscriberName: 'test-subscriber',
58
+ completedSteps: {},
59
+ };
60
+ await store.set('test-id', checkpoint);
61
+ expect(store.size).toBe(1);
62
+ await store.delete('test-id');
63
+ expect(store.size).toBe(0);
64
+ });
65
+ it('should not throw when deleting non-existent checkpoint', async () => {
66
+ await expect(store.delete('non-existent')).resolves.toBeUndefined();
67
+ });
68
+ });
69
+ describe('utility methods', () => {
70
+ it('size should return number of checkpoints', async () => {
71
+ expect(store.size).toBe(0);
72
+ await store.set('id-1', {
73
+ envelopeId: 'id-1',
74
+ subscriberName: 'sub',
75
+ completedSteps: {},
76
+ });
77
+ expect(store.size).toBe(1);
78
+ await store.set('id-2', {
79
+ envelopeId: 'id-2',
80
+ subscriberName: 'sub',
81
+ completedSteps: {},
82
+ });
83
+ expect(store.size).toBe(2);
84
+ });
85
+ it('clear() should remove all checkpoints', async () => {
86
+ await store.set('id-1', {
87
+ envelopeId: 'id-1',
88
+ subscriberName: 'sub',
89
+ completedSteps: {},
90
+ });
91
+ await store.set('id-2', {
92
+ envelopeId: 'id-2',
93
+ subscriberName: 'sub',
94
+ completedSteps: {},
95
+ });
96
+ expect(store.size).toBe(2);
97
+ store.clear();
98
+ expect(store.size).toBe(0);
99
+ });
100
+ it('keys() should return all checkpoint IDs', async () => {
101
+ await store.set('id-1', {
102
+ envelopeId: 'id-1',
103
+ subscriberName: 'sub',
104
+ completedSteps: {},
105
+ });
106
+ await store.set('id-2', {
107
+ envelopeId: 'id-2',
108
+ subscriberName: 'sub',
109
+ completedSteps: {},
110
+ });
111
+ const keys = store.keys();
112
+ expect(keys).toContain('id-1');
113
+ expect(keys).toContain('id-2');
114
+ expect(keys).toHaveLength(2);
115
+ });
116
+ });
117
+ });
118
+ describe('NoOpCheckpointStore', () => {
119
+ let store;
120
+ beforeEach(() => {
121
+ store = new NoOpCheckpointStore();
122
+ });
123
+ describe('get()', () => {
124
+ it('should always return undefined', async () => {
125
+ const result = await store.get('any-id');
126
+ expect(result).toBeUndefined();
127
+ });
128
+ });
129
+ describe('set()', () => {
130
+ it('should not store anything (no-op)', async () => {
131
+ const checkpoint = {
132
+ envelopeId: 'test-id',
133
+ subscriberName: 'test-subscriber',
134
+ completedSteps: { 'step-1': 'result-1' },
135
+ };
136
+ await store.set('test-id', checkpoint);
137
+ const result = await store.get('test-id');
138
+ expect(result).toBeUndefined();
139
+ });
140
+ });
141
+ describe('delete()', () => {
142
+ it('should not throw', async () => {
143
+ await expect(store.delete('any-id')).resolves.toBeUndefined();
144
+ });
145
+ });
146
+ });
@@ -0,0 +1,119 @@
1
+ import type { Envelope, SubscriberDefinition } from '../types/index.js';
2
+ /**
3
+ * Type constraint ensuring values can be JSON serialized.
4
+ * Used to enforce type safety for io() return values.
5
+ */
6
+ export type JsonSerializable = string | number | boolean | null | JsonSerializable[] | {
7
+ [key: string]: JsonSerializable;
8
+ };
9
+ /**
10
+ * Checkpoint data persisted between retries.
11
+ * Contains cached results from completed io() operations.
12
+ */
13
+ export interface Checkpoint {
14
+ /** Unique envelope ID this checkpoint belongs to */
15
+ readonly envelopeId: string;
16
+ /** Subscriber name (for debugging/admin) */
17
+ readonly subscriberName: string;
18
+ /** Map of io() keys to their cached results */
19
+ readonly completedSteps: Record<string, JsonSerializable>;
20
+ }
21
+ /**
22
+ * Interface for checkpoint persistence.
23
+ * Implementations must handle concurrent access safely.
24
+ */
25
+ export interface CheckpointStore {
26
+ /**
27
+ * Retrieve checkpoint for an envelope.
28
+ * @param envelopeId - Unique envelope identifier
29
+ * @returns Checkpoint if exists, undefined otherwise
30
+ */
31
+ get(envelopeId: string): Promise<Checkpoint | undefined>;
32
+ /**
33
+ * Save or update checkpoint.
34
+ * @param envelopeId - Unique envelope identifier
35
+ * @param checkpoint - Checkpoint data to persist
36
+ */
37
+ set(envelopeId: string, checkpoint: Checkpoint): Promise<void>;
38
+ /**
39
+ * Delete checkpoint (on success or dead-letter).
40
+ * @param envelopeId - Unique envelope identifier
41
+ */
42
+ delete(envelopeId: string): Promise<void>;
43
+ /**
44
+ * Optional: TTL-based cleanup for orphaned checkpoints.
45
+ * @param olderThan - Delete checkpoints older than this date
46
+ * @returns Number of checkpoints deleted
47
+ */
48
+ cleanup?(olderThan: Date): Promise<number>;
49
+ }
50
+ /**
51
+ * Context provided to resumable subscriber callbacks.
52
+ * Contains the io() function for checkpointed operations.
53
+ */
54
+ export interface SubscriberContext {
55
+ /**
56
+ * Wraps a side-effectful operation for caching/replay.
57
+ * On first execution: runs the lambda and caches the result.
58
+ * On retry: returns cached result without re-executing.
59
+ *
60
+ * @param key - Unique identifier for this operation within this subscriber.
61
+ * Must be stable across retries. Use descriptive names like
62
+ * 'fetch-user', 'send-email', 'charge-payment'.
63
+ * @param fn - The side-effectful operation to execute
64
+ * @returns The result of fn (or cached result on retry)
65
+ * @throws DuplicateIoKeyError if key was already used in this execution
66
+ */
67
+ io<T extends JsonSerializable>(key: string, fn: () => Promise<T> | T): Promise<T>;
68
+ /**
69
+ * Execute multiple io() operations in parallel.
70
+ * Each operation requires its own unique key.
71
+ *
72
+ * @param ops - Array of [key, fn] tuples to execute in parallel
73
+ * @returns Array of results in the same order as input
74
+ */
75
+ all<T extends readonly [
76
+ string,
77
+ () => Promise<JsonSerializable> | JsonSerializable
78
+ ][]>(ops: T): Promise<{
79
+ [K in keyof T]: T[K] extends [string, () => Promise<infer R> | infer R] ? R : never;
80
+ }>;
81
+ /** Current attempt number (1-based) */
82
+ readonly attempt: number;
83
+ /** Whether this is a retry (attempt > 1) */
84
+ readonly isRetry: boolean;
85
+ }
86
+ /**
87
+ * Context for checkpoint loaded hook.
88
+ */
89
+ export interface CheckpointLoadedContext {
90
+ readonly envelope: Envelope;
91
+ readonly subscriber: SubscriberDefinition;
92
+ readonly checkpoint: Checkpoint;
93
+ readonly cachedSteps: number;
94
+ }
95
+ /**
96
+ * Context for checkpoint hit hook (cache used).
97
+ */
98
+ export interface CheckpointHitContext {
99
+ readonly envelope: Envelope;
100
+ readonly subscriber: SubscriberDefinition;
101
+ readonly stepKey: string;
102
+ }
103
+ /**
104
+ * Context for checkpoint miss hook (operation executed).
105
+ */
106
+ export interface CheckpointMissContext {
107
+ readonly envelope: Envelope;
108
+ readonly subscriber: SubscriberDefinition;
109
+ readonly stepKey: string;
110
+ }
111
+ /**
112
+ * Context for checkpoint cleared hook.
113
+ */
114
+ export interface CheckpointClearedContext {
115
+ readonly envelope: Envelope;
116
+ readonly subscriber: SubscriberDefinition;
117
+ readonly reason: 'success' | 'dead-letter';
118
+ }
119
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/checkpoint/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,29 @@
1
+ import type { HasDescription } from '../errors/index.js';
2
+ import type { Envelope } from '../types/index.js';
3
+ /**
4
+ * Codec interface for serializing/deserializing envelopes.
5
+ */
6
+ export interface Codec {
7
+ /**
8
+ * Encodes an envelope to a buffer for transport.
9
+ */
10
+ encode(envelope: Envelope): Uint8Array;
11
+ /**
12
+ * Decodes a buffer back to an envelope.
13
+ * @throws Error if the buffer is invalid or cannot be decoded
14
+ */
15
+ decode(buffer: Uint8Array): Envelope;
16
+ /**
17
+ * MIME content type for this codec.
18
+ */
19
+ readonly contentType: string;
20
+ }
21
+ /**
22
+ * Error thrown when decoding fails.
23
+ */
24
+ export declare class CodecDecodeError extends Error implements HasDescription {
25
+ readonly cause?: unknown | undefined;
26
+ readonly description: string;
27
+ constructor(message: string, cause?: unknown | undefined);
28
+ }
29
+ //# sourceMappingURL=codec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codec.d.ts","sourceRoot":"","sources":["../../src/codec/codec.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAElD;;GAEG;AACH,MAAM,WAAW,KAAK;IACpB;;OAEG;IACH,MAAM,CAAC,QAAQ,EAAE,QAAQ,GAAG,UAAU,CAAC;IAEvC;;;OAGG;IACH,MAAM,CAAC,MAAM,EAAE,UAAU,GAAG,QAAQ,CAAC;IAErC;;OAEG;IACH,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED;;GAEG;AACH,qBAAa,gBAAiB,SAAQ,KAAM,YAAW,cAAc;aASjD,KAAK,CAAC,EAAE,OAAO;IARjC,QAAQ,CAAC,WAAW,SAIqD;gBAGvE,OAAO,EAAE,MAAM,EACC,KAAK,CAAC,EAAE,OAAO,YAAA;CAKlC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Error thrown when decoding fails.
3
+ */
4
+ export class CodecDecodeError extends Error {
5
+ cause;
6
+ description = 'Failed to decode a message from the transport. This typically indicates ' +
7
+ 'corrupted data, incompatible codec versions, or messages from a different ' +
8
+ 'system. Check the cause property for the underlying parsing error. ' +
9
+ 'The message will be sent to the dead-letter queue for investigation.';
10
+ constructor(message, cause) {
11
+ super(message);
12
+ this.cause = cause;
13
+ this.name = 'CodecDecodeError';
14
+ }
15
+ }
@@ -0,0 +1,36 @@
1
+ import type { Envelope } from '../types/index.js';
2
+ /**
3
+ * Result of encoding an envelope with header support.
4
+ */
5
+ export interface EncodedMessage {
6
+ /** Serialized message body */
7
+ readonly body: Uint8Array;
8
+ /** Headers to attach to the message */
9
+ readonly headers: Record<string, unknown>;
10
+ /** MIME content type */
11
+ readonly contentType: string;
12
+ }
13
+ /**
14
+ * Codec interface for transports that support message headers (e.g., RabbitMQ).
15
+ *
16
+ * Unlike the basic Codec interface, this allows metadata to be stored in
17
+ * headers rather than duplicated in the body, which is more efficient and
18
+ * enables transport-level filtering/routing.
19
+ */
20
+ export interface HeaderAwareCodec {
21
+ /**
22
+ * Encodes an envelope into a body buffer and headers.
23
+ * The body should contain only the essential payload data.
24
+ * Routing and observability metadata should go in headers.
25
+ */
26
+ encode(envelope: Envelope): EncodedMessage;
27
+ /**
28
+ * Decodes a body buffer and headers back into an envelope.
29
+ */
30
+ decode(body: Uint8Array, headers: Record<string, unknown>): Envelope;
31
+ /**
32
+ * MIME content type for the body.
33
+ */
34
+ readonly contentType: string;
35
+ }
36
+ //# sourceMappingURL=header-aware-codec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"header-aware-codec.d.ts","sourceRoot":"","sources":["../../src/codec/header-aware-codec.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAElD;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,8BAA8B;IAC9B,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAE1B,uCAAuC;IACvC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAE1C,wBAAwB;IACxB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;;GAMG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;;OAIG;IACH,MAAM,CAAC,QAAQ,EAAE,QAAQ,GAAG,cAAc,CAAC;IAE3C;;OAEG;IACH,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAC;IAErE;;OAEG;IACH,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,6 @@
1
+ export type { Codec } from './codec.js';
2
+ export { CodecDecodeError } from './codec.js';
3
+ export { JsonCodec } from './json-codec.js';
4
+ export type { EncodedMessage, HeaderAwareCodec, } from './header-aware-codec.js';
5
+ export { RabbitMQCodec } from './rabbitmq-codec.js';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/codec/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE9C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,YAAY,EACV,cAAc,EACd,gBAAgB,GACjB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { CodecDecodeError } from './codec.js';
2
+ export { JsonCodec } from './json-codec.js';
3
+ export { RabbitMQCodec } from './rabbitmq-codec.js';
@@ -0,0 +1,13 @@
1
+ import type { Envelope } from '../types/index.js';
2
+ import type { Codec } from './codec.js';
3
+ /**
4
+ * JSON codec for envelope serialization.
5
+ * Uses standard JSON.stringify/parse with Date handling.
6
+ */
7
+ export declare class JsonCodec implements Codec {
8
+ readonly contentType = "application/json";
9
+ encode(envelope: Envelope): Uint8Array;
10
+ decode(buffer: Uint8Array): Envelope;
11
+ private isValidEnvelope;
12
+ }
13
+ //# sourceMappingURL=json-codec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-codec.d.ts","sourceRoot":"","sources":["../../src/codec/json-codec.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAGxC;;;GAGG;AACH,qBAAa,SAAU,YAAW,KAAK;IACrC,QAAQ,CAAC,WAAW,sBAAsB;IAE1C,MAAM,CAAC,QAAQ,EAAE,QAAQ,GAAG,UAAU;IAKtC,MAAM,CAAC,MAAM,EAAE,UAAU,GAAG,QAAQ;IA0BpC,OAAO,CAAC,eAAe;CA0BxB"}
@@ -0,0 +1,64 @@
1
+ import { CodecDecodeError } from './codec.js';
2
+ /**
3
+ * JSON codec for envelope serialization.
4
+ * Uses standard JSON.stringify/parse with Date handling.
5
+ */
6
+ export class JsonCodec {
7
+ contentType = 'application/json';
8
+ encode(envelope) {
9
+ const json = JSON.stringify(envelope);
10
+ return new TextEncoder().encode(json);
11
+ }
12
+ decode(buffer) {
13
+ if (buffer.length === 0) {
14
+ throw new CodecDecodeError('Cannot decode empty buffer');
15
+ }
16
+ let json;
17
+ try {
18
+ json = new TextDecoder('utf-8').decode(buffer);
19
+ }
20
+ catch (error) {
21
+ throw new CodecDecodeError('Invalid UTF-8 encoding', error);
22
+ }
23
+ let parsed;
24
+ try {
25
+ parsed = JSON.parse(json);
26
+ }
27
+ catch (error) {
28
+ throw new CodecDecodeError('Invalid JSON', error);
29
+ }
30
+ if (!this.isValidEnvelope(parsed)) {
31
+ throw new CodecDecodeError('Invalid envelope structure');
32
+ }
33
+ return parsed;
34
+ }
35
+ isValidEnvelope(value) {
36
+ if (typeof value !== 'object' || value === null) {
37
+ return false;
38
+ }
39
+ const envelope = value;
40
+ // Required top-level fields
41
+ if (typeof envelope['id'] !== 'string')
42
+ return false;
43
+ if (!('data' in envelope))
44
+ return false;
45
+ if (typeof envelope['docket'] !== 'object' || envelope['docket'] === null)
46
+ return false;
47
+ // Validate docket (routing, processing state, observability)
48
+ const docket = envelope['docket'];
49
+ // Routing
50
+ if (typeof docket['eventKey'] !== 'string')
51
+ return false;
52
+ if (typeof docket['targetSubscriber'] !== 'string')
53
+ return false;
54
+ // Processing state
55
+ if (typeof docket['attempts'] !== 'number')
56
+ return false;
57
+ if (typeof docket['createdAt'] !== 'string')
58
+ return false;
59
+ // Observability
60
+ if (typeof docket['importance'] !== 'string')
61
+ return false;
62
+ return true;
63
+ }
64
+ }
@@ -0,0 +1,28 @@
1
+ import type { Envelope } from '../types/index.js';
2
+ import type { EncodedMessage, HeaderAwareCodec } from './header-aware-codec.js';
3
+ /**
4
+ * RabbitMQ-specific codec that stores routing/metadata in headers.
5
+ *
6
+ * Benefits:
7
+ * - No duplication between body and headers
8
+ * - RabbitMQ can route/filter based on headers
9
+ * - Smaller message body
10
+ * - v1 message format compatibility built-in
11
+ */
12
+ export declare class RabbitMQCodec implements HeaderAwareCodec {
13
+ readonly contentType = "application/json";
14
+ encode(envelope: Envelope): EncodedMessage;
15
+ decode(body: Uint8Array, headers: Record<string, unknown>): Envelope;
16
+ private isV1Body;
17
+ private isV2Body;
18
+ private decodeV2;
19
+ /**
20
+ * Decodes a v1 format message for backwards compatibility.
21
+ * v1 format: { key, data, metadata, universal, targetSubscriber, ... }
22
+ */
23
+ private decodeV1;
24
+ private requireStringHeader;
25
+ private requireNumberHeader;
26
+ private optionalStringHeader;
27
+ }
28
+ //# sourceMappingURL=rabbitmq-codec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rabbitmq-codec.d.ts","sourceRoot":"","sources":["../../src/codec/rabbitmq-codec.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAU,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAE1D,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAwDhF;;;;;;;;GAQG;AACH,qBAAa,aAAc,YAAW,gBAAgB;IACpD,QAAQ,CAAC,WAAW,sBAAsB;IAE1C,MAAM,CAAC,QAAQ,EAAE,QAAQ,GAAG,cAAc;IAgD1C,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,QAAQ;IAgCpE,OAAO,CAAC,QAAQ;IAWhB,OAAO,CAAC,QAAQ;IAQhB,OAAO,CAAC,QAAQ;IAmDhB;;;OAGG;IACH,OAAO,CAAC,QAAQ;IAsEhB,OAAO,CAAC,mBAAmB;IAgB3B,OAAO,CAAC,mBAAmB;IAgB3B,OAAO,CAAC,oBAAoB;CAS7B"}