@kronos-ts/messaging 0.1.0

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 (266) hide show
  1. package/dist/command-bus.d.ts +30 -0
  2. package/dist/command-bus.d.ts.map +1 -0
  3. package/dist/command-bus.js +2 -0
  4. package/dist/command-bus.js.map +1 -0
  5. package/dist/command-handler.d.ts +58 -0
  6. package/dist/command-handler.d.ts.map +1 -0
  7. package/dist/command-handler.js +12 -0
  8. package/dist/command-handler.js.map +1 -0
  9. package/dist/command-handling-module.d.ts +53 -0
  10. package/dist/command-handling-module.d.ts.map +1 -0
  11. package/dist/command-handling-module.js +130 -0
  12. package/dist/command-handling-module.js.map +1 -0
  13. package/dist/correlation-data.d.ts +79 -0
  14. package/dist/correlation-data.d.ts.map +1 -0
  15. package/dist/correlation-data.js +133 -0
  16. package/dist/correlation-data.js.map +1 -0
  17. package/dist/dead-letter-queue.d.ts +134 -0
  18. package/dist/dead-letter-queue.d.ts.map +1 -0
  19. package/dist/dead-letter-queue.js +176 -0
  20. package/dist/dead-letter-queue.js.map +1 -0
  21. package/dist/dead-lettering-handler.d.ts +42 -0
  22. package/dist/dead-lettering-handler.d.ts.map +1 -0
  23. package/dist/dead-lettering-handler.js +67 -0
  24. package/dist/dead-lettering-handler.js.map +1 -0
  25. package/dist/descriptor.d.ts +135 -0
  26. package/dist/descriptor.d.ts.map +1 -0
  27. package/dist/descriptor.js +36 -0
  28. package/dist/descriptor.js.map +1 -0
  29. package/dist/emit-update.d.ts +22 -0
  30. package/dist/emit-update.d.ts.map +1 -0
  31. package/dist/emit-update.js +23 -0
  32. package/dist/emit-update.js.map +1 -0
  33. package/dist/event-bus.d.ts +29 -0
  34. package/dist/event-bus.d.ts.map +1 -0
  35. package/dist/event-bus.js +22 -0
  36. package/dist/event-bus.js.map +1 -0
  37. package/dist/event-criteria.d.ts +87 -0
  38. package/dist/event-criteria.d.ts.map +1 -0
  39. package/dist/event-criteria.js +90 -0
  40. package/dist/event-criteria.js.map +1 -0
  41. package/dist/event-gateway.d.ts +19 -0
  42. package/dist/event-gateway.d.ts.map +1 -0
  43. package/dist/event-gateway.js +22 -0
  44. package/dist/event-gateway.js.map +1 -0
  45. package/dist/event-handler.d.ts +30 -0
  46. package/dist/event-handler.d.ts.map +1 -0
  47. package/dist/event-handler.js +18 -0
  48. package/dist/event-handler.js.map +1 -0
  49. package/dist/event-processor-builder.d.ts +148 -0
  50. package/dist/event-processor-builder.d.ts.map +1 -0
  51. package/dist/event-processor-builder.js +175 -0
  52. package/dist/event-processor-builder.js.map +1 -0
  53. package/dist/event-processor.d.ts +10 -0
  54. package/dist/event-processor.d.ts.map +1 -0
  55. package/dist/event-processor.js +2 -0
  56. package/dist/event-processor.js.map +1 -0
  57. package/dist/event-sink.d.ts +23 -0
  58. package/dist/event-sink.d.ts.map +1 -0
  59. package/dist/event-sink.js +2 -0
  60. package/dist/event-sink.js.map +1 -0
  61. package/dist/event-source.d.ts +98 -0
  62. package/dist/event-source.d.ts.map +1 -0
  63. package/dist/event-source.js +191 -0
  64. package/dist/event-source.js.map +1 -0
  65. package/dist/gateway.d.ts +68 -0
  66. package/dist/gateway.d.ts.map +1 -0
  67. package/dist/gateway.js +62 -0
  68. package/dist/gateway.js.map +1 -0
  69. package/dist/handler-enhancer.d.ts +53 -0
  70. package/dist/handler-enhancer.d.ts.map +1 -0
  71. package/dist/handler-enhancer.js +17 -0
  72. package/dist/handler-enhancer.js.map +1 -0
  73. package/dist/handler.d.ts +51 -0
  74. package/dist/handler.d.ts.map +1 -0
  75. package/dist/handler.js +26 -0
  76. package/dist/handler.js.map +1 -0
  77. package/dist/index.d.ts +53 -0
  78. package/dist/index.d.ts.map +1 -0
  79. package/dist/index.js +103 -0
  80. package/dist/index.js.map +1 -0
  81. package/dist/intercepting-command-bus.d.ts +17 -0
  82. package/dist/intercepting-command-bus.d.ts.map +1 -0
  83. package/dist/intercepting-command-bus.js +54 -0
  84. package/dist/intercepting-command-bus.js.map +1 -0
  85. package/dist/intercepting-event-bus.d.ts +8 -0
  86. package/dist/intercepting-event-bus.d.ts.map +1 -0
  87. package/dist/intercepting-event-bus.js +22 -0
  88. package/dist/intercepting-event-bus.js.map +1 -0
  89. package/dist/intercepting-query-bus.d.ts +17 -0
  90. package/dist/intercepting-query-bus.d.ts.map +1 -0
  91. package/dist/intercepting-query-bus.js +68 -0
  92. package/dist/intercepting-query-bus.js.map +1 -0
  93. package/dist/interceptor.d.ts +46 -0
  94. package/dist/interceptor.d.ts.map +1 -0
  95. package/dist/interceptor.js +2 -0
  96. package/dist/interceptor.js.map +1 -0
  97. package/dist/message-monitor-registry.d.ts +28 -0
  98. package/dist/message-monitor-registry.d.ts.map +1 -0
  99. package/dist/message-monitor-registry.js +37 -0
  100. package/dist/message-monitor-registry.js.map +1 -0
  101. package/dist/message-monitor.d.ts +36 -0
  102. package/dist/message-monitor.d.ts.map +1 -0
  103. package/dist/message-monitor.js +39 -0
  104. package/dist/message-monitor.js.map +1 -0
  105. package/dist/message.d.ts +42 -0
  106. package/dist/message.d.ts.map +1 -0
  107. package/dist/message.js +2 -0
  108. package/dist/message.js.map +1 -0
  109. package/dist/processing-state.d.ts +115 -0
  110. package/dist/processing-state.d.ts.map +1 -0
  111. package/dist/processing-state.js +205 -0
  112. package/dist/processing-state.js.map +1 -0
  113. package/dist/processor-configuration.d.ts +51 -0
  114. package/dist/processor-configuration.d.ts.map +1 -0
  115. package/dist/processor-configuration.js +2 -0
  116. package/dist/processor-configuration.js.map +1 -0
  117. package/dist/query-bus.d.ts +51 -0
  118. package/dist/query-bus.d.ts.map +1 -0
  119. package/dist/query-bus.js +2 -0
  120. package/dist/query-bus.js.map +1 -0
  121. package/dist/query-handler.d.ts +35 -0
  122. package/dist/query-handler.d.ts.map +1 -0
  123. package/dist/query-handler.js +19 -0
  124. package/dist/query-handler.js.map +1 -0
  125. package/dist/query-handling-module.d.ts +24 -0
  126. package/dist/query-handling-module.d.ts.map +1 -0
  127. package/dist/query-handling-module.js +32 -0
  128. package/dist/query-handling-module.js.map +1 -0
  129. package/dist/replay-token.d.ts +31 -0
  130. package/dist/replay-token.d.ts.map +1 -0
  131. package/dist/replay-token.js +37 -0
  132. package/dist/replay-token.js.map +1 -0
  133. package/dist/retrying-command-bus.d.ts +32 -0
  134. package/dist/retrying-command-bus.d.ts.map +1 -0
  135. package/dist/retrying-command-bus.js +58 -0
  136. package/dist/retrying-command-bus.js.map +1 -0
  137. package/dist/routing-strategy.d.ts +30 -0
  138. package/dist/routing-strategy.d.ts.map +1 -0
  139. package/dist/routing-strategy.js +37 -0
  140. package/dist/routing-strategy.js.map +1 -0
  141. package/dist/segment.d.ts +72 -0
  142. package/dist/segment.d.ts.map +1 -0
  143. package/dist/segment.js +103 -0
  144. package/dist/segment.js.map +1 -0
  145. package/dist/send.d.ts +28 -0
  146. package/dist/send.d.ts.map +1 -0
  147. package/dist/send.js +36 -0
  148. package/dist/send.js.map +1 -0
  149. package/dist/serializer.d.ts +40 -0
  150. package/dist/serializer.d.ts.map +1 -0
  151. package/dist/serializer.js +90 -0
  152. package/dist/serializer.js.map +1 -0
  153. package/dist/simple-command-bus.d.ts +23 -0
  154. package/dist/simple-command-bus.d.ts.map +1 -0
  155. package/dist/simple-command-bus.js +49 -0
  156. package/dist/simple-command-bus.js.map +1 -0
  157. package/dist/simple-query-bus.d.ts +16 -0
  158. package/dist/simple-query-bus.d.ts.map +1 -0
  159. package/dist/simple-query-bus.js +122 -0
  160. package/dist/simple-query-bus.js.map +1 -0
  161. package/dist/span-factory.d.ts +58 -0
  162. package/dist/span-factory.d.ts.map +1 -0
  163. package/dist/span-factory.js +19 -0
  164. package/dist/span-factory.js.map +1 -0
  165. package/dist/streaming-event-processor.d.ts +65 -0
  166. package/dist/streaming-event-processor.d.ts.map +1 -0
  167. package/dist/streaming-event-processor.js +239 -0
  168. package/dist/streaming-event-processor.js.map +1 -0
  169. package/dist/subscribing-event-processor.d.ts +57 -0
  170. package/dist/subscribing-event-processor.d.ts.map +1 -0
  171. package/dist/subscribing-event-processor.js +100 -0
  172. package/dist/subscribing-event-processor.js.map +1 -0
  173. package/dist/subscription-query.d.ts +63 -0
  174. package/dist/subscription-query.d.ts.map +1 -0
  175. package/dist/subscription-query.js +119 -0
  176. package/dist/subscription-query.js.map +1 -0
  177. package/dist/token-store.d.ts +83 -0
  178. package/dist/token-store.d.ts.map +1 -0
  179. package/dist/token-store.js +112 -0
  180. package/dist/token-store.js.map +1 -0
  181. package/dist/tracing-command-bus.d.ts +16 -0
  182. package/dist/tracing-command-bus.d.ts.map +1 -0
  183. package/dist/tracing-command-bus.js +44 -0
  184. package/dist/tracing-command-bus.js.map +1 -0
  185. package/dist/tracing-handler-enhancer.d.ts +11 -0
  186. package/dist/tracing-handler-enhancer.d.ts.map +1 -0
  187. package/dist/tracing-handler-enhancer.js +27 -0
  188. package/dist/tracing-handler-enhancer.js.map +1 -0
  189. package/dist/tracking-event-processor.d.ts +72 -0
  190. package/dist/tracking-event-processor.d.ts.map +1 -0
  191. package/dist/tracking-event-processor.js +223 -0
  192. package/dist/tracking-event-processor.js.map +1 -0
  193. package/dist/tracking-token.d.ts +120 -0
  194. package/dist/tracking-token.d.ts.map +1 -0
  195. package/dist/tracking-token.js +132 -0
  196. package/dist/tracking-token.js.map +1 -0
  197. package/dist/transaction.d.ts +60 -0
  198. package/dist/transaction.d.ts.map +1 -0
  199. package/dist/transaction.js +74 -0
  200. package/dist/transaction.js.map +1 -0
  201. package/dist/unit-of-work.d.ts +41 -0
  202. package/dist/unit-of-work.d.ts.map +1 -0
  203. package/dist/unit-of-work.js +96 -0
  204. package/dist/unit-of-work.js.map +1 -0
  205. package/dist/upcaster.d.ts +91 -0
  206. package/dist/upcaster.d.ts.map +1 -0
  207. package/dist/upcaster.js +114 -0
  208. package/dist/upcaster.js.map +1 -0
  209. package/dist/with-namespace.d.ts +59 -0
  210. package/dist/with-namespace.d.ts.map +1 -0
  211. package/dist/with-namespace.js +42 -0
  212. package/dist/with-namespace.js.map +1 -0
  213. package/package.json +65 -0
  214. package/src/command-bus.ts +34 -0
  215. package/src/command-handler.ts +116 -0
  216. package/src/command-handling-module.ts +183 -0
  217. package/src/correlation-data.ts +169 -0
  218. package/src/dead-letter-queue.ts +330 -0
  219. package/src/dead-lettering-handler.ts +109 -0
  220. package/src/descriptor.ts +176 -0
  221. package/src/emit-update.ts +35 -0
  222. package/src/event-bus.ts +45 -0
  223. package/src/event-criteria.ts +141 -0
  224. package/src/event-gateway.ts +42 -0
  225. package/src/event-handler.ts +44 -0
  226. package/src/event-processor-builder.ts +246 -0
  227. package/src/event-processor.ts +9 -0
  228. package/src/event-sink.ts +23 -0
  229. package/src/event-source.ts +301 -0
  230. package/src/gateway.ts +144 -0
  231. package/src/handler-enhancer.ts +70 -0
  232. package/src/handler.ts +133 -0
  233. package/src/index.ts +356 -0
  234. package/src/intercepting-command-bus.ts +73 -0
  235. package/src/intercepting-event-bus.ts +29 -0
  236. package/src/intercepting-query-bus.ts +104 -0
  237. package/src/interceptor.ts +48 -0
  238. package/src/message-monitor-registry.ts +64 -0
  239. package/src/message-monitor.ts +68 -0
  240. package/src/message.ts +41 -0
  241. package/src/processing-state.ts +258 -0
  242. package/src/processor-configuration.ts +59 -0
  243. package/src/query-bus.ts +69 -0
  244. package/src/query-handler.ts +49 -0
  245. package/src/query-handling-module.ts +44 -0
  246. package/src/replay-token.ts +53 -0
  247. package/src/retrying-command-bus.ts +80 -0
  248. package/src/routing-strategy.ts +59 -0
  249. package/src/segment.ts +136 -0
  250. package/src/send.ts +44 -0
  251. package/src/serializer.ts +122 -0
  252. package/src/simple-command-bus.ts +59 -0
  253. package/src/simple-query-bus.ts +158 -0
  254. package/src/span-factory.ts +81 -0
  255. package/src/streaming-event-processor.ts +351 -0
  256. package/src/subscribing-event-processor.ts +169 -0
  257. package/src/subscription-query.ts +173 -0
  258. package/src/token-store.ts +211 -0
  259. package/src/tracing-command-bus.ts +52 -0
  260. package/src/tracing-handler-enhancer.ts +34 -0
  261. package/src/tracking-event-processor.ts +336 -0
  262. package/src/tracking-token.ts +231 -0
  263. package/src/transaction.ts +98 -0
  264. package/src/unit-of-work.ts +138 -0
  265. package/src/upcaster.ts +174 -0
  266. package/src/with-namespace.ts +75 -0
@@ -0,0 +1,301 @@
1
+ import type { EventMessage } from "./message.js"
2
+ import type { EventCriteria } from "./event-criteria.js"
3
+ import type { TrackingToken } from "./tracking-token.js"
4
+
5
+ /**
6
+ * An event with its global sequence position.
7
+ */
8
+ export interface SequencedEvent {
9
+ readonly sequence: bigint
10
+ readonly event: EventMessage
11
+ }
12
+
13
+ /**
14
+ * Condition for opening a streaming event source.
15
+ * Defines the starting position and optional event criteria filter.
16
+ */
17
+ export interface StreamingCondition {
18
+ /** Position to start streaming from. */
19
+ readonly position: bigint
20
+ /** Optional criteria to filter events. When omitted, all events are delivered. */
21
+ readonly criteria?: EventCriteria
22
+ }
23
+
24
+ /**
25
+ * A push-based message stream. Events are buffered internally and
26
+ * pulled via {@link next}. The stream notifies when events become
27
+ * available via {@link setCallback}.
28
+ */
29
+ export interface MessageStream<M> {
30
+ /** Pull the next available item (non-blocking). */
31
+ next(): M | undefined
32
+ /** Peek at the next item without consuming it. */
33
+ peek(): M | undefined
34
+ /** Check if there are items ready to be pulled. */
35
+ hasNextAvailable(): boolean
36
+ /** Whether the stream has been completed. */
37
+ isCompleted(): boolean
38
+ /** The error that caused the stream to fail, if any. */
39
+ error(): Error | undefined
40
+ /** Register a callback for when items become available. */
41
+ setCallback(callback: () => void): void
42
+ /** Close the stream and release resources. */
43
+ close(): void
44
+ /** Transform each item. */
45
+ map<R>(mapper: (item: M) => R): MessageStream<R>
46
+ /** Filter items by predicate. */
47
+ filter(predicate: (item: M) => boolean): MessageStream<M>
48
+ /** Recover from stream errors by providing a continuation stream. */
49
+ onErrorContinue(recovery: (error: Error) => MessageStream<M>): MessageStream<M>
50
+ /** Reduce all items to a single value. Resolves when the stream completes. */
51
+ reduce<R>(identity: R, accumulator: (acc: R, item: M) => R): Promise<R>
52
+ /** Concatenate another stream after this one completes. */
53
+ concatWith(other: MessageStream<M>): MessageStream<M>
54
+ }
55
+
56
+ /**
57
+ * Creates a MessageStream that wraps a source with transformation support.
58
+ * Used by event store implementations to provide stream instances.
59
+ */
60
+ export function createMessageStream<M>(source: {
61
+ next(): M | undefined
62
+ peek(): M | undefined
63
+ hasNextAvailable(): boolean
64
+ isCompleted(): boolean
65
+ error(): Error | undefined
66
+ setCallback(callback: () => void): void
67
+ close(): void
68
+ }): MessageStream<M> {
69
+ const stream: MessageStream<M> = {
70
+ ...source,
71
+ map<R>(mapper: (item: M) => R): MessageStream<R> {
72
+ return createMappedStream(stream, mapper)
73
+ },
74
+ filter(predicate: (item: M) => boolean): MessageStream<M> {
75
+ return createFilteredStream(stream, predicate)
76
+ },
77
+ onErrorContinue(recovery: (error: Error) => MessageStream<M>): MessageStream<M> {
78
+ return createErrorRecoveryStream(stream, recovery)
79
+ },
80
+ reduce<R>(identity: R, accumulator: (acc: R, item: M) => R): Promise<R> {
81
+ return reduceStream(stream, identity, accumulator)
82
+ },
83
+ concatWith(other: MessageStream<M>): MessageStream<M> {
84
+ return createConcatStream(stream, other)
85
+ },
86
+ }
87
+ return stream
88
+ }
89
+
90
+ /**
91
+ * Creates a completed empty MessageStream.
92
+ */
93
+ export function emptyMessageStream<M>(): MessageStream<M> {
94
+ return createMessageStream<M>({
95
+ next: () => undefined,
96
+ peek: () => undefined,
97
+ hasNextAvailable: () => false,
98
+ isCompleted: () => true,
99
+ error: () => undefined,
100
+ setCallback: () => {},
101
+ close: () => {},
102
+ })
103
+ }
104
+
105
+ /**
106
+ * Creates a MessageStream that immediately fails with the given error.
107
+ */
108
+ export function failedMessageStream<M>(error: Error): MessageStream<M> {
109
+ return createMessageStream<M>({
110
+ next: () => undefined,
111
+ peek: () => undefined,
112
+ hasNextAvailable: () => false,
113
+ isCompleted: () => true,
114
+ error: () => error,
115
+ setCallback: () => {},
116
+ close: () => {},
117
+ })
118
+ }
119
+
120
+ // ---------------------------------------------------------------------------
121
+ // Stream transformations
122
+ // ---------------------------------------------------------------------------
123
+
124
+ function createMappedStream<M, R>(source: MessageStream<M>, mapper: (item: M) => R): MessageStream<R> {
125
+ return createMessageStream<R>({
126
+ next() {
127
+ const item = source.next()
128
+ return item !== undefined ? mapper(item) : undefined
129
+ },
130
+ peek() {
131
+ const item = source.peek()
132
+ return item !== undefined ? mapper(item) : undefined
133
+ },
134
+ hasNextAvailable: () => source.hasNextAvailable(),
135
+ isCompleted: () => source.isCompleted(),
136
+ error: () => source.error(),
137
+ setCallback: (cb) => source.setCallback(cb),
138
+ close: () => source.close(),
139
+ })
140
+ }
141
+
142
+ function createFilteredStream<M>(source: MessageStream<M>, predicate: (item: M) => boolean): MessageStream<M> {
143
+ let buffered: M | undefined
144
+
145
+ function advance(): M | undefined {
146
+ while (true) {
147
+ const item = source.next()
148
+ if (item === undefined) return undefined
149
+ if (predicate(item)) return item
150
+ }
151
+ }
152
+
153
+ return createMessageStream<M>({
154
+ next() {
155
+ if (buffered !== undefined) {
156
+ const item = buffered
157
+ buffered = undefined
158
+ return item
159
+ }
160
+ return advance()
161
+ },
162
+ peek() {
163
+ if (buffered !== undefined) return buffered
164
+ buffered = advance()
165
+ return buffered
166
+ },
167
+ hasNextAvailable() {
168
+ if (buffered !== undefined) return true
169
+ buffered = advance()
170
+ return buffered !== undefined
171
+ },
172
+ isCompleted: () => source.isCompleted() && buffered === undefined,
173
+ error: () => source.error(),
174
+ setCallback: (cb) => source.setCallback(cb),
175
+ close: () => source.close(),
176
+ })
177
+ }
178
+
179
+ function createErrorRecoveryStream<M>(
180
+ source: MessageStream<M>,
181
+ recovery: (error: Error) => MessageStream<M>,
182
+ ): MessageStream<M> {
183
+ let current: MessageStream<M> = source
184
+ let recovered = false
185
+
186
+ function checkRecovery(): void {
187
+ if (!recovered && current.error()) {
188
+ current = recovery(current.error()!)
189
+ recovered = true
190
+ }
191
+ }
192
+
193
+ return createMessageStream<M>({
194
+ next() { checkRecovery(); return current.next() },
195
+ peek() { checkRecovery(); return current.peek() },
196
+ hasNextAvailable() { checkRecovery(); return current.hasNextAvailable() },
197
+ isCompleted() { checkRecovery(); return current.isCompleted() },
198
+ error() { return recovered ? current.error() : undefined },
199
+ setCallback(cb) { current.setCallback(cb) },
200
+ close() { current.close() },
201
+ })
202
+ }
203
+
204
+ function createConcatStream<M>(first: MessageStream<M>, second: MessageStream<M>): MessageStream<M> {
205
+ let usingFirst = true
206
+
207
+ function current(): MessageStream<M> {
208
+ if (usingFirst && first.isCompleted() && !first.hasNextAvailable()) {
209
+ usingFirst = false
210
+ }
211
+ return usingFirst ? first : second
212
+ }
213
+
214
+ return createMessageStream<M>({
215
+ next() { return current().next() },
216
+ peek() { return current().peek() },
217
+ hasNextAvailable() { return current().hasNextAvailable() },
218
+ isCompleted() { return current().isCompleted() },
219
+ error() { return current().error() },
220
+ setCallback(cb) {
221
+ if (usingFirst) {
222
+ first.setCallback(() => {
223
+ if (first.isCompleted() && !first.hasNextAvailable()) {
224
+ usingFirst = false
225
+ second.setCallback(cb)
226
+ cb()
227
+ } else {
228
+ cb()
229
+ }
230
+ })
231
+ } else {
232
+ second.setCallback(cb)
233
+ }
234
+ },
235
+ close() { first.close(); second.close() },
236
+ })
237
+ }
238
+
239
+ async function reduceStream<M, R>(
240
+ stream: MessageStream<M>,
241
+ identity: R,
242
+ accumulator: (acc: R, item: M) => R,
243
+ ): Promise<R> {
244
+ let result = identity
245
+ return new Promise<R>((resolve, reject) => {
246
+ function drain() {
247
+ while (true) {
248
+ const item = stream.next()
249
+ if (item !== undefined) {
250
+ result = accumulator(result, item)
251
+ continue
252
+ }
253
+ if (stream.error()) {
254
+ reject(stream.error())
255
+ return
256
+ }
257
+ if (stream.isCompleted()) {
258
+ resolve(result)
259
+ return
260
+ }
261
+ // Wait for more items
262
+ stream.setCallback(drain)
263
+ return
264
+ }
265
+ }
266
+ drain()
267
+ })
268
+ }
269
+
270
+ // ---------------------------------------------------------------------------
271
+ // StreamableEventSource
272
+ // ---------------------------------------------------------------------------
273
+
274
+ /**
275
+ * A source of events that can be opened as an infinite stream.
276
+ */
277
+ export interface StreamableEventSource {
278
+ /**
279
+ * Open an infinite event stream starting from the given condition.
280
+ */
281
+ open(condition: StreamingCondition): MessageStream<SequencedEvent>
282
+
283
+ /**
284
+ * Get the token representing the beginning of the event stream.
285
+ * A processor starting from this token will read all events.
286
+ */
287
+ firstToken(): Promise<TrackingToken>
288
+
289
+ /**
290
+ * Get the token representing the current tail of the event stream.
291
+ * A processor starting from this token will only see new events.
292
+ */
293
+ latestToken(): Promise<TrackingToken>
294
+
295
+ /**
296
+ * Get the current head position — the sequence of the next event
297
+ * to be appended. Convenience method equivalent to
298
+ * {@code (await latestToken()).position()}.
299
+ */
300
+ getHeadPosition(): Promise<bigint>
301
+ }
package/src/gateway.ts ADDED
@@ -0,0 +1,144 @@
1
+ import {
2
+ generateIdentifier,
3
+ emptyMetadata,
4
+ type Metadata,
5
+ } from "@kronos-ts/common"
6
+ import type { CommandBus } from "./command-bus.js"
7
+ import type { QueryBus } from "./query-bus.js"
8
+ import type { CommandDescriptor, QueryDescriptor } from "./descriptor.js"
9
+ import type { SubscriptionQueryResult } from "./subscription-query.js"
10
+ import { runInNewUoW, type UoWRunner } from "./unit-of-work.js"
11
+ import type { z } from "zod"
12
+
13
+ /**
14
+ * Infers the result type from a descriptor's `result` schema.
15
+ * If no result schema, returns `unknown`.
16
+ */
17
+ type InferResult<R extends z.ZodType | undefined> =
18
+ R extends z.ZodType ? z.infer<R> : unknown
19
+
20
+ /**
21
+ * User-facing command gateway. Wraps payloads into proper command messages
22
+ * and delegates to the command bus.
23
+ *
24
+ * When the command descriptor has a `result` schema, the return type is inferred:
25
+ * ```typescript
26
+ * const CreateCourse = command({
27
+ * name: qn("university", "CreateCourse"),
28
+ * payload: z.object({ courseId: z.string() }),
29
+ * result: z.object({ id: z.string() }),
30
+ * })
31
+ *
32
+ * const result = await gateway.send(CreateCourse, { courseId: "cs-101" })
33
+ * // ^ { id: string } — inferred from descriptor
34
+ * ```
35
+ */
36
+ export interface CommandGateway {
37
+ send<P extends z.ZodType, R extends z.ZodType | undefined = undefined>(
38
+ descriptor: CommandDescriptor<P, R>,
39
+ payload: z.infer<P>,
40
+ metadata?: Metadata,
41
+ ): Promise<InferResult<R>>
42
+ }
43
+
44
+ /**
45
+ * User-facing query gateway. Wraps payloads into proper query messages
46
+ * and delegates to the query bus.
47
+ *
48
+ * When the query descriptor has a `result` schema, the return type is inferred:
49
+ * ```typescript
50
+ * const GetCourse = query({
51
+ * name: qn("university", "GetCourseView"),
52
+ * payload: z.object({ courseId: z.string() }),
53
+ * result: z.object({ courseId: z.string(), name: z.string() }),
54
+ * })
55
+ *
56
+ * const course = await gateway.query(GetCourse, { courseId: "cs-101" })
57
+ * // ^ { courseId: string, name: string } — inferred from descriptor
58
+ * ```
59
+ */
60
+ export interface QueryGateway {
61
+ query<P extends z.ZodType, R extends z.ZodType | undefined = undefined>(
62
+ descriptor: QueryDescriptor<P, R>,
63
+ payload: z.infer<P>,
64
+ metadata?: Metadata,
65
+ ): Promise<InferResult<R>>
66
+
67
+ subscriptionQuery<P extends z.ZodType, R extends z.ZodType | undefined = undefined>(
68
+ descriptor: QueryDescriptor<P, R>,
69
+ payload: z.infer<P>,
70
+ metadata?: Metadata,
71
+ ): SubscriptionQueryResult
72
+ }
73
+
74
+ /**
75
+ * Creates a command gateway backed by a command bus.
76
+ *
77
+ * Plan 03-04 (CTX-04 / D-34): the optional `unitOfWorkRunner` lets the
78
+ * configurer inject a transactional wrapper (`transactionalUnitOfWorkFactory`)
79
+ * around the dispatch boundary. Defaults to `runInNewUoW` — preserves the
80
+ * Plan 03-01 contract that every gateway call starts a fresh UoW.
81
+ */
82
+ export function createCommandGateway(
83
+ bus: CommandBus,
84
+ unitOfWorkRunner: UoWRunner = runInNewUoW,
85
+ ): CommandGateway {
86
+ return {
87
+ async send(descriptor, payload, metadata) {
88
+ const resolvedMetadata = metadata ?? emptyMetadata()
89
+ // Plan 03-01 (D-32) / Plan 03-03 (CTX-01): gateways always start a new
90
+ // UoW. The bus.dispatch call below will detect the ALS state we just
91
+ // established (via runInUoW in simple-command-bus) and reuse it — so
92
+ // this is the single UoW boundary for the dispatch chain. No
93
+ // ProcessingContext parameter is threaded.
94
+ return unitOfWorkRunner(resolvedMetadata, () =>
95
+ bus.dispatch({
96
+ identifier: generateIdentifier(),
97
+ name: descriptor.name,
98
+ payload,
99
+ metadata: resolvedMetadata,
100
+ timestamp: Date.now(),
101
+ }) as Promise<any>,
102
+ ) as any
103
+ },
104
+ }
105
+ }
106
+
107
+ /**
108
+ * Creates a query gateway backed by a query bus.
109
+ *
110
+ * See `createCommandGateway` for the `unitOfWorkRunner` injection contract.
111
+ */
112
+ export function createQueryGateway(
113
+ bus: QueryBus,
114
+ unitOfWorkRunner: UoWRunner = runInNewUoW,
115
+ ): QueryGateway {
116
+ return {
117
+ async query(descriptor, payload, metadata) {
118
+ const resolvedMetadata = metadata ?? emptyMetadata()
119
+ // Plan 03-01 (D-32) / Plan 03-03 (CTX-01): gateway always starts a new
120
+ // UoW; bus.query auto-nests via runInUoW in simple-query-bus.
121
+ // subscriptionQuery below stays as-is — its initialResult goes through
122
+ // bus.query, which handles its own UoW.
123
+ return unitOfWorkRunner(resolvedMetadata, () =>
124
+ bus.query({
125
+ identifier: generateIdentifier(),
126
+ name: descriptor.name,
127
+ payload,
128
+ metadata: resolvedMetadata,
129
+ timestamp: Date.now(),
130
+ }) as Promise<any>,
131
+ ) as any
132
+ },
133
+
134
+ subscriptionQuery(descriptor, payload, metadata) {
135
+ return bus.subscriptionQuery({
136
+ identifier: generateIdentifier(),
137
+ name: descriptor.name,
138
+ payload,
139
+ metadata: metadata ?? emptyMetadata(),
140
+ timestamp: Date.now(),
141
+ })
142
+ },
143
+ }
144
+ }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Metadata about a handler being enhanced. Allows enhancers to
3
+ * selectively wrap based on handler type, message name, etc.
4
+ */
5
+ export interface HandlerMetadata {
6
+ /** The type of message this handler processes. */
7
+ readonly messageType: "command" | "event" | "query"
8
+ /** The qualified name of the message (e.g., "university.courses.CreateCourse"). */
9
+ readonly messageName: string
10
+ /** The name of the handler group or module (e.g., "course-commands"). */
11
+ readonly handlerGroup: string
12
+ }
13
+
14
+ /**
15
+ * Wraps message handler functions at registration time to add cross-cutting
16
+ * concerns. Different from interceptors — interceptors wrap dispatch,
17
+ * enhancers wrap the handler itself.
18
+ *
19
+ * Applied once at handler discovery/registration time, not per-invocation.
20
+ * This makes enhancers ideal for:
21
+ * - Tracing spans per-handler
22
+ * - Security checks
23
+ * - Timeout enforcement
24
+ * - Caching
25
+ *
26
+ * @example
27
+ * ```ts
28
+ * const timingEnhancer: HandlerEnhancerDefinition = {
29
+ * wrapHandler(handler, metadata) {
30
+ * return async (...args) => {
31
+ * const start = performance.now()
32
+ * try {
33
+ * return await handler(...args)
34
+ * } finally {
35
+ * console.log(`${metadata.messageName} took ${performance.now() - start}ms`)
36
+ * }
37
+ * }
38
+ * },
39
+ * }
40
+ * ```
41
+ */
42
+ export interface HandlerEnhancerDefinition {
43
+ /**
44
+ * Wrap a handler function. Return the original handler to skip enhancement.
45
+ * The returned function must have the same signature as the input.
46
+ */
47
+ wrapHandler<T extends (...args: any[]) => any>(
48
+ handler: T,
49
+ metadata: HandlerMetadata,
50
+ ): T
51
+ }
52
+
53
+ /**
54
+ * Combines multiple handler enhancer definitions into a single one.
55
+ * Enhancers are applied in order — the first enhancer wraps outermost.
56
+ */
57
+ export function multiHandlerEnhancerDefinition(
58
+ enhancers: ReadonlyArray<HandlerEnhancerDefinition>,
59
+ ): HandlerEnhancerDefinition {
60
+ return {
61
+ wrapHandler<T extends (...args: any[]) => any>(handler: T, metadata: HandlerMetadata): T {
62
+ let wrapped = handler
63
+ // Apply in reverse order so first enhancer is outermost
64
+ for (let i = enhancers.length - 1; i >= 0; i--) {
65
+ wrapped = enhancers[i]!.wrapHandler(wrapped, metadata)
66
+ }
67
+ return wrapped
68
+ },
69
+ }
70
+ }
package/src/handler.ts ADDED
@@ -0,0 +1,133 @@
1
+ import type { z } from "zod"
2
+ import type { Metadata } from "@kronos-ts/common"
3
+ import type {
4
+ CommandDescriptor,
5
+ EventDescriptor,
6
+ QueryDescriptor,
7
+ } from "./descriptor.js"
8
+
9
+ // ---------------------------------------------------------------------------
10
+ // Handler context shapes — DELETED (Plan 04-02, D-41)
11
+ // CommandHandlerContext / EventHandlerContext / QueryHandlerContext removed.
12
+ // LoadFunction / AppendFunction / SendFunction / EmitUpdateFunction removed.
13
+ // Consumers import load/append from @kronos-ts/eventsourcing and
14
+ // send/dispatch/emitUpdate from @kronos-ts/messaging directly.
15
+ // ---------------------------------------------------------------------------
16
+
17
+ // ---------------------------------------------------------------------------
18
+ // Registration types
19
+ // ---------------------------------------------------------------------------
20
+
21
+ /** A paired event descriptor + handler function, used in handler arrays. */
22
+ export interface EventHandlerRegistration<P extends z.ZodType = z.ZodType> {
23
+ readonly kind: "event-handler"
24
+ readonly descriptor: EventDescriptor<P>
25
+ readonly handler: (
26
+ event: z.infer<P>,
27
+ metadata: Metadata,
28
+ ) => Promise<void> | void
29
+ }
30
+
31
+ /**
32
+ * A paired event descriptor + evolver function, used in entity evolve arrays.
33
+ *
34
+ * Evolvers can be sync or async. Async evolvers don't block the event loop
35
+ * during state reconstruction.
36
+ */
37
+ export interface EvolverRegistration<
38
+ S = unknown,
39
+ P extends z.ZodType = z.ZodType,
40
+ > {
41
+ readonly kind: "evolver"
42
+ readonly descriptor: EventDescriptor<P>
43
+ readonly evolve: (state: S, event: z.infer<P>, id: unknown) => S | Promise<S>
44
+ }
45
+
46
+ /** A paired query descriptor + handler function, with result type on the handler. */
47
+ export interface QueryHandlerRegistration<
48
+ Q extends z.ZodType = z.ZodType,
49
+ R = unknown,
50
+ > {
51
+ readonly kind: "query-handler"
52
+ readonly descriptor: QueryDescriptor<Q>
53
+ readonly handler: (
54
+ query: z.infer<Q>,
55
+ metadata: Metadata,
56
+ ) => Promise<R> | R
57
+ }
58
+
59
+ // ---------------------------------------------------------------------------
60
+ // on() — universal registration
61
+ // ---------------------------------------------------------------------------
62
+
63
+ /**
64
+ * Universal registration function.
65
+ * Pairs a descriptor with its handler for use in handler/evolve arrays.
66
+ *
67
+ * Usage:
68
+ * - Event handlers: `on(CourseCreated, async (event, ctx) => { ... })`
69
+ * - Query handlers: `on(GetCourse, async (query, ctx) => { return { ... } })`
70
+ * - Evolvers: `on(CourseCreated, (state, event) => ({ ...state, name: event.name }))`
71
+ *
72
+ * The overload is resolved by the descriptor kind and how many arguments
73
+ * the callback declares. Evolvers receive `(state, event)` or `(state, event, id)`,
74
+ * while event handlers receive `(event, context)`.
75
+ *
76
+ * In practice the distinction is enforced by the array type:
77
+ * - `evolve: [on(...)]` expects `EvolverRegistration`
78
+ * - `handlers: [on(...)]` expects `EventHandlerRegistration`
79
+ */
80
+
81
+ // Overload: evolver (event descriptor + state evolve function)
82
+ export function on<S, P extends z.ZodType>(
83
+ descriptor: EventDescriptor<P>,
84
+ evolve: (state: S, event: z.infer<P>, id: unknown) => S | Promise<S>,
85
+ ): EvolverRegistration<S, P>
86
+
87
+ // Overload: event handler (event descriptor + handler function)
88
+ export function on<P extends z.ZodType>(
89
+ descriptor: EventDescriptor<P>,
90
+ handler: (event: z.infer<P>, metadata: Metadata) => Promise<void> | void,
91
+ ): EventHandlerRegistration<P>
92
+
93
+ // Overload: query handler
94
+ export function on<Q extends z.ZodType, R>(
95
+ descriptor: QueryDescriptor<Q>,
96
+ handler: (
97
+ query: z.infer<Q>,
98
+ metadata: Metadata,
99
+ ) => Promise<R> | R,
100
+ ): QueryHandlerRegistration<Q, R>
101
+
102
+ export function on(
103
+ descriptor: EventDescriptor | QueryDescriptor,
104
+ handler: (...args: any[]) => any,
105
+ ): EventHandlerRegistration | EvolverRegistration | QueryHandlerRegistration {
106
+ if (descriptor.kind === "event") {
107
+ // For event descriptors, the same `on()` call is used for both event handlers
108
+ // and evolvers. We return an object that satisfies both interfaces — the
109
+ // consumer's array type (evolve: EvolverRegistration[] vs handlers: EventHandlerRegistration[])
110
+ // enforces correct usage at compile time.
111
+ return {
112
+ kind: "event-handler" as any,
113
+ descriptor: descriptor as EventDescriptor,
114
+ handler,
115
+ evolve: handler,
116
+ }
117
+ }
118
+ return {
119
+ kind: "query-handler",
120
+ descriptor: descriptor as QueryDescriptor,
121
+ handler,
122
+ }
123
+ }
124
+
125
+ /**
126
+ * @deprecated Use `on()` instead. `onEvent` is kept for backward compatibility.
127
+ */
128
+ export function onEvent<S, P extends z.ZodType>(
129
+ descriptor: EventDescriptor<P>,
130
+ evolve: (state: S, event: z.infer<P>, id: unknown) => S | Promise<S>,
131
+ ): EvolverRegistration<S, P> {
132
+ return { kind: "evolver", descriptor, evolve }
133
+ }