@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.
- package/dist/command-bus.d.ts +30 -0
- package/dist/command-bus.d.ts.map +1 -0
- package/dist/command-bus.js +2 -0
- package/dist/command-bus.js.map +1 -0
- package/dist/command-handler.d.ts +58 -0
- package/dist/command-handler.d.ts.map +1 -0
- package/dist/command-handler.js +12 -0
- package/dist/command-handler.js.map +1 -0
- package/dist/command-handling-module.d.ts +53 -0
- package/dist/command-handling-module.d.ts.map +1 -0
- package/dist/command-handling-module.js +130 -0
- package/dist/command-handling-module.js.map +1 -0
- package/dist/correlation-data.d.ts +79 -0
- package/dist/correlation-data.d.ts.map +1 -0
- package/dist/correlation-data.js +133 -0
- package/dist/correlation-data.js.map +1 -0
- package/dist/dead-letter-queue.d.ts +134 -0
- package/dist/dead-letter-queue.d.ts.map +1 -0
- package/dist/dead-letter-queue.js +176 -0
- package/dist/dead-letter-queue.js.map +1 -0
- package/dist/dead-lettering-handler.d.ts +42 -0
- package/dist/dead-lettering-handler.d.ts.map +1 -0
- package/dist/dead-lettering-handler.js +67 -0
- package/dist/dead-lettering-handler.js.map +1 -0
- package/dist/descriptor.d.ts +135 -0
- package/dist/descriptor.d.ts.map +1 -0
- package/dist/descriptor.js +36 -0
- package/dist/descriptor.js.map +1 -0
- package/dist/emit-update.d.ts +22 -0
- package/dist/emit-update.d.ts.map +1 -0
- package/dist/emit-update.js +23 -0
- package/dist/emit-update.js.map +1 -0
- package/dist/event-bus.d.ts +29 -0
- package/dist/event-bus.d.ts.map +1 -0
- package/dist/event-bus.js +22 -0
- package/dist/event-bus.js.map +1 -0
- package/dist/event-criteria.d.ts +87 -0
- package/dist/event-criteria.d.ts.map +1 -0
- package/dist/event-criteria.js +90 -0
- package/dist/event-criteria.js.map +1 -0
- package/dist/event-gateway.d.ts +19 -0
- package/dist/event-gateway.d.ts.map +1 -0
- package/dist/event-gateway.js +22 -0
- package/dist/event-gateway.js.map +1 -0
- package/dist/event-handler.d.ts +30 -0
- package/dist/event-handler.d.ts.map +1 -0
- package/dist/event-handler.js +18 -0
- package/dist/event-handler.js.map +1 -0
- package/dist/event-processor-builder.d.ts +148 -0
- package/dist/event-processor-builder.d.ts.map +1 -0
- package/dist/event-processor-builder.js +175 -0
- package/dist/event-processor-builder.js.map +1 -0
- package/dist/event-processor.d.ts +10 -0
- package/dist/event-processor.d.ts.map +1 -0
- package/dist/event-processor.js +2 -0
- package/dist/event-processor.js.map +1 -0
- package/dist/event-sink.d.ts +23 -0
- package/dist/event-sink.d.ts.map +1 -0
- package/dist/event-sink.js +2 -0
- package/dist/event-sink.js.map +1 -0
- package/dist/event-source.d.ts +98 -0
- package/dist/event-source.d.ts.map +1 -0
- package/dist/event-source.js +191 -0
- package/dist/event-source.js.map +1 -0
- package/dist/gateway.d.ts +68 -0
- package/dist/gateway.d.ts.map +1 -0
- package/dist/gateway.js +62 -0
- package/dist/gateway.js.map +1 -0
- package/dist/handler-enhancer.d.ts +53 -0
- package/dist/handler-enhancer.d.ts.map +1 -0
- package/dist/handler-enhancer.js +17 -0
- package/dist/handler-enhancer.js.map +1 -0
- package/dist/handler.d.ts +51 -0
- package/dist/handler.d.ts.map +1 -0
- package/dist/handler.js +26 -0
- package/dist/handler.js.map +1 -0
- package/dist/index.d.ts +53 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +103 -0
- package/dist/index.js.map +1 -0
- package/dist/intercepting-command-bus.d.ts +17 -0
- package/dist/intercepting-command-bus.d.ts.map +1 -0
- package/dist/intercepting-command-bus.js +54 -0
- package/dist/intercepting-command-bus.js.map +1 -0
- package/dist/intercepting-event-bus.d.ts +8 -0
- package/dist/intercepting-event-bus.d.ts.map +1 -0
- package/dist/intercepting-event-bus.js +22 -0
- package/dist/intercepting-event-bus.js.map +1 -0
- package/dist/intercepting-query-bus.d.ts +17 -0
- package/dist/intercepting-query-bus.d.ts.map +1 -0
- package/dist/intercepting-query-bus.js +68 -0
- package/dist/intercepting-query-bus.js.map +1 -0
- package/dist/interceptor.d.ts +46 -0
- package/dist/interceptor.d.ts.map +1 -0
- package/dist/interceptor.js +2 -0
- package/dist/interceptor.js.map +1 -0
- package/dist/message-monitor-registry.d.ts +28 -0
- package/dist/message-monitor-registry.d.ts.map +1 -0
- package/dist/message-monitor-registry.js +37 -0
- package/dist/message-monitor-registry.js.map +1 -0
- package/dist/message-monitor.d.ts +36 -0
- package/dist/message-monitor.d.ts.map +1 -0
- package/dist/message-monitor.js +39 -0
- package/dist/message-monitor.js.map +1 -0
- package/dist/message.d.ts +42 -0
- package/dist/message.d.ts.map +1 -0
- package/dist/message.js +2 -0
- package/dist/message.js.map +1 -0
- package/dist/processing-state.d.ts +115 -0
- package/dist/processing-state.d.ts.map +1 -0
- package/dist/processing-state.js +205 -0
- package/dist/processing-state.js.map +1 -0
- package/dist/processor-configuration.d.ts +51 -0
- package/dist/processor-configuration.d.ts.map +1 -0
- package/dist/processor-configuration.js +2 -0
- package/dist/processor-configuration.js.map +1 -0
- package/dist/query-bus.d.ts +51 -0
- package/dist/query-bus.d.ts.map +1 -0
- package/dist/query-bus.js +2 -0
- package/dist/query-bus.js.map +1 -0
- package/dist/query-handler.d.ts +35 -0
- package/dist/query-handler.d.ts.map +1 -0
- package/dist/query-handler.js +19 -0
- package/dist/query-handler.js.map +1 -0
- package/dist/query-handling-module.d.ts +24 -0
- package/dist/query-handling-module.d.ts.map +1 -0
- package/dist/query-handling-module.js +32 -0
- package/dist/query-handling-module.js.map +1 -0
- package/dist/replay-token.d.ts +31 -0
- package/dist/replay-token.d.ts.map +1 -0
- package/dist/replay-token.js +37 -0
- package/dist/replay-token.js.map +1 -0
- package/dist/retrying-command-bus.d.ts +32 -0
- package/dist/retrying-command-bus.d.ts.map +1 -0
- package/dist/retrying-command-bus.js +58 -0
- package/dist/retrying-command-bus.js.map +1 -0
- package/dist/routing-strategy.d.ts +30 -0
- package/dist/routing-strategy.d.ts.map +1 -0
- package/dist/routing-strategy.js +37 -0
- package/dist/routing-strategy.js.map +1 -0
- package/dist/segment.d.ts +72 -0
- package/dist/segment.d.ts.map +1 -0
- package/dist/segment.js +103 -0
- package/dist/segment.js.map +1 -0
- package/dist/send.d.ts +28 -0
- package/dist/send.d.ts.map +1 -0
- package/dist/send.js +36 -0
- package/dist/send.js.map +1 -0
- package/dist/serializer.d.ts +40 -0
- package/dist/serializer.d.ts.map +1 -0
- package/dist/serializer.js +90 -0
- package/dist/serializer.js.map +1 -0
- package/dist/simple-command-bus.d.ts +23 -0
- package/dist/simple-command-bus.d.ts.map +1 -0
- package/dist/simple-command-bus.js +49 -0
- package/dist/simple-command-bus.js.map +1 -0
- package/dist/simple-query-bus.d.ts +16 -0
- package/dist/simple-query-bus.d.ts.map +1 -0
- package/dist/simple-query-bus.js +122 -0
- package/dist/simple-query-bus.js.map +1 -0
- package/dist/span-factory.d.ts +58 -0
- package/dist/span-factory.d.ts.map +1 -0
- package/dist/span-factory.js +19 -0
- package/dist/span-factory.js.map +1 -0
- package/dist/streaming-event-processor.d.ts +65 -0
- package/dist/streaming-event-processor.d.ts.map +1 -0
- package/dist/streaming-event-processor.js +239 -0
- package/dist/streaming-event-processor.js.map +1 -0
- package/dist/subscribing-event-processor.d.ts +57 -0
- package/dist/subscribing-event-processor.d.ts.map +1 -0
- package/dist/subscribing-event-processor.js +100 -0
- package/dist/subscribing-event-processor.js.map +1 -0
- package/dist/subscription-query.d.ts +63 -0
- package/dist/subscription-query.d.ts.map +1 -0
- package/dist/subscription-query.js +119 -0
- package/dist/subscription-query.js.map +1 -0
- package/dist/token-store.d.ts +83 -0
- package/dist/token-store.d.ts.map +1 -0
- package/dist/token-store.js +112 -0
- package/dist/token-store.js.map +1 -0
- package/dist/tracing-command-bus.d.ts +16 -0
- package/dist/tracing-command-bus.d.ts.map +1 -0
- package/dist/tracing-command-bus.js +44 -0
- package/dist/tracing-command-bus.js.map +1 -0
- package/dist/tracing-handler-enhancer.d.ts +11 -0
- package/dist/tracing-handler-enhancer.d.ts.map +1 -0
- package/dist/tracing-handler-enhancer.js +27 -0
- package/dist/tracing-handler-enhancer.js.map +1 -0
- package/dist/tracking-event-processor.d.ts +72 -0
- package/dist/tracking-event-processor.d.ts.map +1 -0
- package/dist/tracking-event-processor.js +223 -0
- package/dist/tracking-event-processor.js.map +1 -0
- package/dist/tracking-token.d.ts +120 -0
- package/dist/tracking-token.d.ts.map +1 -0
- package/dist/tracking-token.js +132 -0
- package/dist/tracking-token.js.map +1 -0
- package/dist/transaction.d.ts +60 -0
- package/dist/transaction.d.ts.map +1 -0
- package/dist/transaction.js +74 -0
- package/dist/transaction.js.map +1 -0
- package/dist/unit-of-work.d.ts +41 -0
- package/dist/unit-of-work.d.ts.map +1 -0
- package/dist/unit-of-work.js +96 -0
- package/dist/unit-of-work.js.map +1 -0
- package/dist/upcaster.d.ts +91 -0
- package/dist/upcaster.d.ts.map +1 -0
- package/dist/upcaster.js +114 -0
- package/dist/upcaster.js.map +1 -0
- package/dist/with-namespace.d.ts +59 -0
- package/dist/with-namespace.d.ts.map +1 -0
- package/dist/with-namespace.js +42 -0
- package/dist/with-namespace.js.map +1 -0
- package/package.json +65 -0
- package/src/command-bus.ts +34 -0
- package/src/command-handler.ts +116 -0
- package/src/command-handling-module.ts +183 -0
- package/src/correlation-data.ts +169 -0
- package/src/dead-letter-queue.ts +330 -0
- package/src/dead-lettering-handler.ts +109 -0
- package/src/descriptor.ts +176 -0
- package/src/emit-update.ts +35 -0
- package/src/event-bus.ts +45 -0
- package/src/event-criteria.ts +141 -0
- package/src/event-gateway.ts +42 -0
- package/src/event-handler.ts +44 -0
- package/src/event-processor-builder.ts +246 -0
- package/src/event-processor.ts +9 -0
- package/src/event-sink.ts +23 -0
- package/src/event-source.ts +301 -0
- package/src/gateway.ts +144 -0
- package/src/handler-enhancer.ts +70 -0
- package/src/handler.ts +133 -0
- package/src/index.ts +356 -0
- package/src/intercepting-command-bus.ts +73 -0
- package/src/intercepting-event-bus.ts +29 -0
- package/src/intercepting-query-bus.ts +104 -0
- package/src/interceptor.ts +48 -0
- package/src/message-monitor-registry.ts +64 -0
- package/src/message-monitor.ts +68 -0
- package/src/message.ts +41 -0
- package/src/processing-state.ts +258 -0
- package/src/processor-configuration.ts +59 -0
- package/src/query-bus.ts +69 -0
- package/src/query-handler.ts +49 -0
- package/src/query-handling-module.ts +44 -0
- package/src/replay-token.ts +53 -0
- package/src/retrying-command-bus.ts +80 -0
- package/src/routing-strategy.ts +59 -0
- package/src/segment.ts +136 -0
- package/src/send.ts +44 -0
- package/src/serializer.ts +122 -0
- package/src/simple-command-bus.ts +59 -0
- package/src/simple-query-bus.ts +158 -0
- package/src/span-factory.ts +81 -0
- package/src/streaming-event-processor.ts +351 -0
- package/src/subscribing-event-processor.ts +169 -0
- package/src/subscription-query.ts +173 -0
- package/src/token-store.ts +211 -0
- package/src/tracing-command-bus.ts +52 -0
- package/src/tracing-handler-enhancer.ts +34 -0
- package/src/tracking-event-processor.ts +336 -0
- package/src/tracking-token.ts +231 -0
- package/src/transaction.ts +98 -0
- package/src/unit-of-work.ts +138 -0
- package/src/upcaster.ts +174 -0
- 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,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
|
+
}
|