@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,141 @@
1
+ import type { Tag, QualifiedName } from "@kronos-ts/common"
2
+ import { qualifiedNameToString, tagsFromRecord } from "@kronos-ts/common"
3
+ import type { EventDescriptor } from "./descriptor.js"
4
+
5
+ /**
6
+ * Criteria for selecting events from the event store.
7
+ * Used for both sourcing conditions (which events to load) and
8
+ * append conditions (which events define the consistency boundary).
9
+ *
10
+ * Criteria are composable via `or()` and restrictable via `ofTypes()`.
11
+ */
12
+ export type EventCriteria =
13
+ | TagCriteria
14
+ | TypeRestrictedCriteria
15
+ | EitherCriteria
16
+ | AnyTagCriteria
17
+
18
+ export interface TagCriteria {
19
+ readonly kind: "tags"
20
+ readonly tags: ReadonlyArray<Tag>
21
+ }
22
+
23
+ export interface TypeRestrictedCriteria {
24
+ readonly kind: "type-restricted"
25
+ readonly inner: TagCriteria | AnyTagCriteria
26
+ readonly types: ReadonlyArray<string>
27
+ }
28
+
29
+ export interface EitherCriteria {
30
+ readonly kind: "either"
31
+ readonly criteria: ReadonlyArray<EventCriteria>
32
+ }
33
+
34
+ export interface AnyTagCriteria {
35
+ readonly kind: "any-tag"
36
+ }
37
+
38
+ /**
39
+ * A tag or any-tag criteria that allows further restriction by event types.
40
+ */
41
+ export type RestrictableEventCriteria = (TagCriteria | AnyTagCriteria) & {
42
+ /**
43
+ * Restrict matched events to the given types.
44
+ * Accepts event descriptors or qualified name strings.
45
+ */
46
+ ofTypes(...types: Array<EventDescriptor<any> | QualifiedName | string>): EventCriteria
47
+ }
48
+
49
+ function resolveTypeName(t: EventDescriptor<any> | QualifiedName | string): string {
50
+ if (typeof t === "string") return t
51
+ if ("kind" in t && t.kind === "event") return qualifiedNameToString(t.name)
52
+ if ("namespace" in t && "name" in t) return qualifiedNameToString(t as QualifiedName)
53
+ return String(t)
54
+ }
55
+
56
+ function makeRestrictable(criteria: TagCriteria | AnyTagCriteria): RestrictableEventCriteria {
57
+ return Object.assign(criteria, {
58
+ ofTypes(...types: Array<EventDescriptor<any> | QualifiedName | string>): EventCriteria {
59
+ return {
60
+ kind: "type-restricted" as const,
61
+ inner: criteria,
62
+ types: types.map(resolveTypeName),
63
+ }
64
+ },
65
+ })
66
+ }
67
+
68
+ export const EventCriteria = {
69
+ /**
70
+ * Match events having all the specified tags.
71
+ *
72
+ * Accepts individual Tag objects or a record of key-value pairs:
73
+ * ```typescript
74
+ * EventCriteria.havingTags({ courseId: id.courseId })
75
+ * EventCriteria.havingTags(tag("courseId", id.courseId))
76
+ * ```
77
+ */
78
+ havingTags(...args: Tag[] | [Record<string, string>]): RestrictableEventCriteria {
79
+ if (args.length === 1 && typeof args[0] === "object" && !("key" in args[0])) {
80
+ return makeRestrictable({ kind: "tags", tags: tagsFromRecord(args[0] as Record<string, string>) })
81
+ }
82
+ return makeRestrictable({ kind: "tags", tags: args as Tag[] })
83
+ },
84
+
85
+ /**
86
+ * Match events having any tag (i.e., all tagged events).
87
+ */
88
+ havingAnyTag(): RestrictableEventCriteria {
89
+ return makeRestrictable({ kind: "any-tag" })
90
+ },
91
+
92
+ /**
93
+ * Match events matching any of the given criteria (logical OR).
94
+ */
95
+ either(...criteria: EventCriteria[]): EventCriteria {
96
+ return { kind: "either", criteria }
97
+ },
98
+ } as const
99
+
100
+ // ---------------------------------------------------------------------------
101
+ // Standalone shorthand functions
102
+ // ---------------------------------------------------------------------------
103
+
104
+ /**
105
+ * Match events having all the specified tags.
106
+ *
107
+ * Shorthand for `EventCriteria.havingTags()`. Supports `.ofTypes()` chaining.
108
+ *
109
+ * ```typescript
110
+ * tags({ courseId: id.courseId })
111
+ * tags({ courseId: id.courseId }).ofTypes(CourseCreated, CourseCapacityChanged)
112
+ * ```
113
+ */
114
+ export function tags(...args: Tag[] | [Record<string, string>]): RestrictableEventCriteria {
115
+ return EventCriteria.havingTags(...args)
116
+ }
117
+
118
+ /**
119
+ * Match events having any tag.
120
+ *
121
+ * Shorthand for `EventCriteria.havingAnyTag()`. Supports `.ofTypes()` chaining.
122
+ */
123
+ export function anyTag(): RestrictableEventCriteria {
124
+ return EventCriteria.havingAnyTag()
125
+ }
126
+
127
+ /**
128
+ * Match events matching any of the given criteria (logical OR).
129
+ *
130
+ * Shorthand for `EventCriteria.either()`.
131
+ *
132
+ * ```typescript
133
+ * either(
134
+ * tags({ courseId: id.courseId }),
135
+ * tags({ studentId: id.studentId }).ofTypes(StudentSubscribed),
136
+ * )
137
+ * ```
138
+ */
139
+ export function either(...criteria: EventCriteria[]): EventCriteria {
140
+ return EventCriteria.either(...criteria)
141
+ }
@@ -0,0 +1,42 @@
1
+ import type { EventSink } from "./event-sink.js"
2
+ import type { EventMessage } from "./message.js"
3
+ import type { EventDescriptor } from "./descriptor.js"
4
+ import { generateIdentifier } from "@kronos-ts/common"
5
+ import type { z } from "zod"
6
+
7
+ /**
8
+ * User-facing gateway for publishing events directly (without going through
9
+ * command handlers).
10
+ *
11
+ */
12
+ export interface EventGateway {
13
+ /**
14
+ * Publish a single event described by its descriptor.
15
+ */
16
+ publish<P extends z.ZodType>(
17
+ descriptor: EventDescriptor<P>,
18
+ payload: z.infer<P>,
19
+ metadata?: Record<string, unknown>,
20
+ ): Promise<void>
21
+ }
22
+
23
+ /**
24
+ * Creates an event gateway backed by an event sink.
25
+ */
26
+ export function createEventGateway(eventSink: EventSink): EventGateway {
27
+ return {
28
+ async publish(descriptor, payload, metadata = {}) {
29
+ const tags = descriptor.tags ? descriptor.tags(payload) : []
30
+ const event: EventMessage = {
31
+ identifier: generateIdentifier(),
32
+ name: descriptor.name,
33
+ version: descriptor.version,
34
+ payload,
35
+ metadata,
36
+ timestamp: Date.now(),
37
+ tags,
38
+ }
39
+ await eventSink.publish([event])
40
+ },
41
+ }
42
+ }
@@ -0,0 +1,44 @@
1
+ import type { z } from "zod"
2
+ import type { Metadata } from "@kronos-ts/common"
3
+ import type { EventDescriptor } from "./descriptor.js"
4
+
5
+ // ---------------------------------------------------------------------------
6
+ // Singular factory — mirrors commandHandler / queryHandler.
7
+ // The processor builder consumes these via `.eventHandlers(...handlers)` varargs.
8
+ // ---------------------------------------------------------------------------
9
+
10
+ /**
11
+ * A registered singular event handler — pairs an event descriptor with its handler
12
+ * function. Mirrors {@link import("./command-handler.js").CommandHandlerDefinition}
13
+ * structurally so all three handler shapes (command / event / query) share the same
14
+ * pattern.
15
+ */
16
+ export interface EventHandlerDefinition<P extends z.ZodType = z.ZodType> {
17
+ readonly kind: "event-handler"
18
+ readonly descriptor: EventDescriptor<P>
19
+ readonly handler: (
20
+ event: z.infer<P>,
21
+ metadata: Metadata,
22
+ ) => Promise<void> | void
23
+ }
24
+
25
+ /**
26
+ * Defines a singular event handler.
27
+ *
28
+ * ```
29
+ * const onCourseCreated = eventHandler(CourseCreated, async (event, metadata) => {
30
+ * await db.courses.insert({ id: event.courseId, name: event.name })
31
+ * })
32
+ * ```
33
+ *
34
+ * Use with `trackingProcessor(...).eventHandlers(onCreated, onCapChanged).build()` or
35
+ * `subscribingProcessor(...).eventHandlers(...).build()`. Symmetric to
36
+ * {@link import("./command-handler.js").commandHandler} and
37
+ * {@link import("./query-handler.js").queryHandler}.
38
+ */
39
+ export function eventHandler<P extends z.ZodType>(
40
+ descriptor: EventDescriptor<P>,
41
+ handler: (event: z.infer<P>, metadata: Metadata) => Promise<void> | void,
42
+ ): EventHandlerDefinition<P> {
43
+ return { kind: "event-handler", descriptor, handler }
44
+ }
@@ -0,0 +1,246 @@
1
+ import type { EventHandlerDefinition } from "./event-handler.js"
2
+ import type { TokenStore } from "./token-store.js"
3
+ import type { UoWRunner } from "./unit-of-work.js"
4
+ import type { EventProcessingErrorHandler } from "./tracking-event-processor.js"
5
+ import type { SequencedDeadLetterQueue } from "./dead-letter-queue.js"
6
+
7
+ /**
8
+ * Base configuration shared by all event processor types.
9
+ *
10
+ * Plan 11-02: option types carry a flat `eventHandlers: EventHandlerDefinition[]`
11
+ * array. The processor (not the handler bundle) owns reset semantics — see
12
+ * `TrackingProcessorModule.onReset`.
13
+ */
14
+ interface EventProcessorBase {
15
+ readonly name: string
16
+ readonly eventHandlers: ReadonlyArray<EventHandlerDefinition>
17
+ }
18
+
19
+ /**
20
+ * Configuration for a tracking event processor (polling-based, with token store).
21
+ */
22
+ export interface TrackingProcessorModule extends EventProcessorBase {
23
+ readonly kind: "tracking"
24
+ readonly batchSize?: number
25
+ readonly pollingIntervalMs?: number
26
+ readonly tokenStore?: TokenStore
27
+ readonly unitOfWorkRunner?: UoWRunner
28
+ readonly errorHandler?: EventProcessingErrorHandler
29
+ readonly deadLetterQueue?: SequencedDeadLetterQueue
30
+ /** Number of segments created on first startup. Default 16 (Axon Framework parity). Always set by builder.build(). */
31
+ readonly initialSegmentCount: number
32
+ readonly claimExtensionThresholdMs?: number
33
+ readonly tokenClaimIntervalMs?: number
34
+ /** Reset callback invoked when the processor is reset. Reset is processor-level (clear token + replay), and the callback that wipes view state belongs alongside it. */
35
+ readonly onReset?: () => Promise<void> | void
36
+ }
37
+
38
+ /**
39
+ * Configuration for a subscribing event processor (push-based, no tracking).
40
+ *
41
+ * Subscribing processors do not support reset — see `supportsReset()` on the
42
+ * runtime instance — so there is no `onReset` field here.
43
+ */
44
+ export interface SubscribingProcessorModule extends EventProcessorBase {
45
+ readonly kind: "subscribing"
46
+ readonly unitOfWorkRunner?: UoWRunner
47
+ readonly errorHandler?: EventProcessingErrorHandler
48
+ }
49
+
50
+ export type EventProcessorModule = TrackingProcessorModule | SubscribingProcessorModule
51
+
52
+ // ---------------------------------------------------------------------------
53
+ // Tracking processor builder
54
+ // ---------------------------------------------------------------------------
55
+
56
+ /**
57
+ * Builder for a tracking event processor.
58
+ *
59
+ * Tracking processors poll the event store for new events, maintain
60
+ * position via a token store, and support replay/reset.
61
+ *
62
+ * ```typescript
63
+ * const onCreated = eventHandler(CourseCreated, async (e) => { ... })
64
+ * const onCapChanged = eventHandler(CourseCapacityChanged, async (e) => { ... })
65
+ *
66
+ * trackingProcessor("course-projection")
67
+ * .eventHandlers(onCreated, onCapChanged)
68
+ * .onReset(async () => courseViews.clear())
69
+ * .batchSize(50)
70
+ * .build()
71
+ * ```
72
+ *
73
+ * NOTE: Pooled streaming processor support is deferred to a follow-up
74
+ * research phase exploring how that model should fit Node/Bun runtime
75
+ * semantics, where worker threads are not reservable the same way as JVM
76
+ * threads.
77
+ */
78
+ export function trackingProcessor(name: string): TrackingProcessorBuilder {
79
+ return new TrackingProcessorBuilder(name)
80
+ }
81
+
82
+ export class TrackingProcessorBuilder {
83
+ private readonly _name: string
84
+ private readonly _eventHandlers: EventHandlerDefinition[] = []
85
+ private _onReset?: () => Promise<void> | void
86
+ private _batchSize?: number
87
+ private _pollingIntervalMs?: number
88
+ private _tokenStore?: TokenStore
89
+ private _unitOfWorkRunner?: UoWRunner
90
+ private _errorHandler?: EventProcessingErrorHandler
91
+ private _deadLetterQueue?: SequencedDeadLetterQueue
92
+ private _initialSegmentCount?: number
93
+ private _claimExtensionThresholdMs?: number
94
+ private _tokenClaimIntervalMs?: number
95
+
96
+ constructor(name: string) {
97
+ this._name = name
98
+ }
99
+
100
+ /** Register one or more singular event handlers on this processor. */
101
+ eventHandlers(...handlers: EventHandlerDefinition[]): this {
102
+ this._eventHandlers.push(...handlers)
103
+ return this
104
+ }
105
+
106
+ /** Register a callback fired when the processor is reset (clears view state for replay). */
107
+ onReset(fn: () => Promise<void> | void): this {
108
+ this._onReset = fn
109
+ return this
110
+ }
111
+
112
+ /** Events per batch/transaction. Default: 100. */
113
+ batchSize(size: number): this {
114
+ this._batchSize = size
115
+ return this
116
+ }
117
+
118
+ /** Polling interval in ms. Default: 500. */
119
+ pollingIntervalMs(ms: number): this {
120
+ this._pollingIntervalMs = ms
121
+ return this
122
+ }
123
+
124
+ /** Override the token store for this processor. */
125
+ tokenStore(store: TokenStore): this {
126
+ this._tokenStore = store
127
+ return this
128
+ }
129
+
130
+ /**
131
+ * Override the UnitOfWork runner for this processor. Compose with
132
+ * `transactionalUnitOfWorkFactory(runInUoW, txManager)` to attach
133
+ * transactional semantics.
134
+ */
135
+ unitOfWorkRunner(runner: UoWRunner): this {
136
+ this._unitOfWorkRunner = runner
137
+ return this
138
+ }
139
+
140
+ /** Override the error handler for this processor. */
141
+ errorHandler(handler: EventProcessingErrorHandler): this {
142
+ this._errorHandler = handler
143
+ return this
144
+ }
145
+
146
+ /** Set a dead letter queue for this processor. */
147
+ deadLetterQueue(queue: SequencedDeadLetterQueue): this {
148
+ this._deadLetterQueue = queue
149
+ return this
150
+ }
151
+
152
+ /** Number of segments to create on first startup. Default: 16 (Axon Framework parity). */
153
+ initialSegmentCount(count: number): this {
154
+ this._initialSegmentCount = count
155
+ return this
156
+ }
157
+
158
+ /** @internal Build the processor configuration. */
159
+ build(): TrackingProcessorModule {
160
+ return {
161
+ kind: "tracking",
162
+ name: this._name,
163
+ eventHandlers: this._eventHandlers,
164
+ batchSize: this._batchSize,
165
+ pollingIntervalMs: this._pollingIntervalMs,
166
+ tokenStore: this._tokenStore,
167
+ unitOfWorkRunner: this._unitOfWorkRunner,
168
+ errorHandler: this._errorHandler,
169
+ deadLetterQueue: this._deadLetterQueue,
170
+ initialSegmentCount: this._initialSegmentCount ?? 16,
171
+ claimExtensionThresholdMs: this._claimExtensionThresholdMs,
172
+ tokenClaimIntervalMs: this._tokenClaimIntervalMs,
173
+ onReset: this._onReset,
174
+ }
175
+ }
176
+ }
177
+
178
+ // ---------------------------------------------------------------------------
179
+ // Subscribing processor builder
180
+ // ---------------------------------------------------------------------------
181
+
182
+ /**
183
+ * Builder for a subscribing event processor.
184
+ *
185
+ * Subscribing processors receive events pushed from the event source
186
+ * as they are appended. No token store, no position tracking, no replay.
187
+ *
188
+ * ```typescript
189
+ * const onNotification = eventHandler(NotificationRaised, async (e) => { ... })
190
+ *
191
+ * subscribingProcessor("notifications")
192
+ * .eventHandlers(onNotification)
193
+ * .build()
194
+ * ```
195
+ *
196
+ * Subscribing processors do NOT support reset (`supportsReset() === false`),
197
+ * so there is no `.onReset(fn)` builder method here — that lives on
198
+ * `TrackingProcessorBuilder` only.
199
+ */
200
+ export function subscribingProcessor(name: string): SubscribingProcessorBuilder {
201
+ return new SubscribingProcessorBuilder(name)
202
+ }
203
+
204
+ export class SubscribingProcessorBuilder {
205
+ private readonly _name: string
206
+ private readonly _eventHandlers: EventHandlerDefinition[] = []
207
+ private _unitOfWorkRunner?: UoWRunner
208
+ private _errorHandler?: EventProcessingErrorHandler
209
+
210
+ constructor(name: string) {
211
+ this._name = name
212
+ }
213
+
214
+ /** Register one or more singular event handlers on this processor. */
215
+ eventHandlers(...handlers: EventHandlerDefinition[]): this {
216
+ this._eventHandlers.push(...handlers)
217
+ return this
218
+ }
219
+
220
+ /**
221
+ * Override the UnitOfWork runner for this processor. Compose with
222
+ * `transactionalUnitOfWorkFactory(runInUoW, txManager)` to attach
223
+ * transactional semantics.
224
+ */
225
+ unitOfWorkRunner(runner: UoWRunner): this {
226
+ this._unitOfWorkRunner = runner
227
+ return this
228
+ }
229
+
230
+ /** Override the error handler for this processor. */
231
+ errorHandler(handler: EventProcessingErrorHandler): this {
232
+ this._errorHandler = handler
233
+ return this
234
+ }
235
+
236
+ /** @internal Build the processor configuration. */
237
+ build(): SubscribingProcessorModule {
238
+ return {
239
+ kind: "subscribing",
240
+ name: this._name,
241
+ eventHandlers: this._eventHandlers,
242
+ unitOfWorkRunner: this._unitOfWorkRunner,
243
+ errorHandler: this._errorHandler,
244
+ }
245
+ }
246
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Common interface for all event processors.
3
+ */
4
+ export interface EventProcessor {
5
+ readonly name: string
6
+ readonly running: boolean
7
+ start(): Promise<void>
8
+ stop(): void
9
+ }
@@ -0,0 +1,23 @@
1
+ import type { EventMessage } from "./message.js"
2
+
3
+ /**
4
+ * Publish-only abstraction for event publication.
5
+ *
6
+ * This is the messaging-level contract for publishing events. The EventGateway
7
+ * and command handlers use this to emit events without needing to know about
8
+ * event storage.
9
+ *
10
+ * Implementations include:
11
+ * - The EventStore (which persists AND publishes)
12
+ * - A simple EventBus (which only distributes to subscribers)
13
+ *
14
+ */
15
+ export interface EventSink {
16
+ /**
17
+ * Publish events. The events are distributed to any subscribed handlers.
18
+ *
19
+ * In an event sourcing context, this also persists the events to the
20
+ * underlying storage engine.
21
+ */
22
+ publish(events: ReadonlyArray<EventMessage>): Promise<void>
23
+ }