@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,116 @@
|
|
|
1
|
+
import type { z } from "zod"
|
|
2
|
+
import type { Metadata } from "@kronos-ts/common"
|
|
3
|
+
import type { CommandDescriptor } from "./descriptor.js"
|
|
4
|
+
import type { EventCriteria } from "./event-criteria.js"
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A registered command handler — pairs a command descriptor with its handler function.
|
|
8
|
+
* When the descriptor has a result schema, the handler must return that type.
|
|
9
|
+
*/
|
|
10
|
+
export interface CommandHandlerDefinition<
|
|
11
|
+
P extends z.ZodType = z.ZodType,
|
|
12
|
+
R extends z.ZodType | undefined = undefined,
|
|
13
|
+
> {
|
|
14
|
+
readonly kind: "command-handler"
|
|
15
|
+
readonly descriptor: CommandDescriptor<P, R>
|
|
16
|
+
readonly handler: (
|
|
17
|
+
command: z.infer<P>,
|
|
18
|
+
metadata: Metadata,
|
|
19
|
+
) => R extends z.ZodType ? Promise<z.infer<R>> | z.infer<R> : Promise<void> | void
|
|
20
|
+
readonly appendCondition?: (
|
|
21
|
+
command: z.infer<P>,
|
|
22
|
+
sourcedCriteria: EventCriteria,
|
|
23
|
+
) => EventCriteria
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Defines a command handler.
|
|
28
|
+
*
|
|
29
|
+
* Void command (no result on descriptor):
|
|
30
|
+
* ```
|
|
31
|
+
* commandHandler(ChangeCourseCapacity, async (command, metadata) => {
|
|
32
|
+
* const course = await load(Course, command.courseId)
|
|
33
|
+
* append(CourseCapacityChanged, { courseId: command.courseId, capacity: command.capacity })
|
|
34
|
+
* })
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* Typed result command (result on descriptor):
|
|
38
|
+
* ```
|
|
39
|
+
* const CreateCourse = command({
|
|
40
|
+
* name: qn("university", "CreateCourse"),
|
|
41
|
+
* payload: z.object({ courseId: z.string(), name: z.string() }),
|
|
42
|
+
* result: z.object({ courseId: z.string() }),
|
|
43
|
+
* })
|
|
44
|
+
*
|
|
45
|
+
* commandHandler(CreateCourse, async (command, metadata) => {
|
|
46
|
+
* append(CourseCreated, { ... })
|
|
47
|
+
* return { courseId: command.courseId } // ← must match descriptor's result schema
|
|
48
|
+
* })
|
|
49
|
+
* ```
|
|
50
|
+
*
|
|
51
|
+
* With append condition override:
|
|
52
|
+
* ```
|
|
53
|
+
* commandHandler(CreateCourse, {
|
|
54
|
+
* handler: async (command, metadata) => { ... },
|
|
55
|
+
* appendCondition: (command, criteria) => criteria,
|
|
56
|
+
* })
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
export function commandHandler<P extends z.ZodType>(
|
|
60
|
+
descriptor: CommandDescriptor<P, undefined>,
|
|
61
|
+
handler: (
|
|
62
|
+
command: z.infer<P>,
|
|
63
|
+
metadata: Metadata,
|
|
64
|
+
) => Promise<void> | void,
|
|
65
|
+
): CommandHandlerDefinition<P, undefined>
|
|
66
|
+
|
|
67
|
+
export function commandHandler<P extends z.ZodType, R extends z.ZodType>(
|
|
68
|
+
descriptor: CommandDescriptor<P, R>,
|
|
69
|
+
handler: (
|
|
70
|
+
command: z.infer<P>,
|
|
71
|
+
metadata: Metadata,
|
|
72
|
+
) => Promise<z.infer<R>> | z.infer<R>,
|
|
73
|
+
): CommandHandlerDefinition<P, R>
|
|
74
|
+
|
|
75
|
+
export function commandHandler<P extends z.ZodType>(
|
|
76
|
+
descriptor: CommandDescriptor<P, undefined>,
|
|
77
|
+
options: {
|
|
78
|
+
handler: (
|
|
79
|
+
command: z.infer<P>,
|
|
80
|
+
metadata: Metadata,
|
|
81
|
+
) => Promise<void> | void
|
|
82
|
+
appendCondition?: (
|
|
83
|
+
command: z.infer<P>,
|
|
84
|
+
sourcedCriteria: EventCriteria,
|
|
85
|
+
) => EventCriteria
|
|
86
|
+
},
|
|
87
|
+
): CommandHandlerDefinition<P, undefined>
|
|
88
|
+
|
|
89
|
+
export function commandHandler<P extends z.ZodType, R extends z.ZodType>(
|
|
90
|
+
descriptor: CommandDescriptor<P, R>,
|
|
91
|
+
options: {
|
|
92
|
+
handler: (
|
|
93
|
+
command: z.infer<P>,
|
|
94
|
+
metadata: Metadata,
|
|
95
|
+
) => Promise<z.infer<R>> | z.infer<R>
|
|
96
|
+
appendCondition?: (
|
|
97
|
+
command: z.infer<P>,
|
|
98
|
+
sourcedCriteria: EventCriteria,
|
|
99
|
+
) => EventCriteria
|
|
100
|
+
},
|
|
101
|
+
): CommandHandlerDefinition<P, R>
|
|
102
|
+
|
|
103
|
+
export function commandHandler(
|
|
104
|
+
descriptor: CommandDescriptor,
|
|
105
|
+
handlerOrOptions: any,
|
|
106
|
+
): CommandHandlerDefinition {
|
|
107
|
+
if (typeof handlerOrOptions === "function") {
|
|
108
|
+
return { kind: "command-handler", descriptor, handler: handlerOrOptions }
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
kind: "command-handler",
|
|
112
|
+
descriptor,
|
|
113
|
+
handler: handlerOrOptions.handler,
|
|
114
|
+
appendCondition: handlerOrOptions.appendCondition,
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { qualifiedNameToString, resourceKey } from "@kronos-ts/common"
|
|
2
|
+
import type { CommandHandlerDefinition } from "./command-handler.js"
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Minimal Configuration shape consumed by createCommandInvocation /
|
|
6
|
+
* registerCommandHandlersNatively. Phase 8 D-82 reshape: messaging no longer
|
|
7
|
+
* depends on the full @kronos-ts/common Configuration interface (deleted
|
|
8
|
+
* with the configurer trio in Plan 08-04). Callers (kronos() AppImpl.start())
|
|
9
|
+
* pass a shim that satisfies just these methods.
|
|
10
|
+
*
|
|
11
|
+
* The component-key strings used by createCommandInvocation are inlined
|
|
12
|
+
* below (COMMAND_INVOCATION_KEYS) so this file owns its own key set.
|
|
13
|
+
*/
|
|
14
|
+
export interface MinimalConfiguration {
|
|
15
|
+
hasComponent(type: string, name?: string): boolean
|
|
16
|
+
getComponent<T>(type: string, name?: string): T
|
|
17
|
+
getOptionalComponent<T>(type: string, name?: string): T | undefined
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Component-key strings consumed by createCommandInvocation. Inlined here
|
|
22
|
+
* so this file has no shared-constant dependency. The kronos() AppImpl
|
|
23
|
+
* populates its config-shim with the same string keys.
|
|
24
|
+
*/
|
|
25
|
+
const COMMAND_INVOCATION_KEYS = {
|
|
26
|
+
STATE_MANAGER: "stateManager",
|
|
27
|
+
COMMAND_BUS: "commandBus",
|
|
28
|
+
QUERY_BUS: "queryBus",
|
|
29
|
+
EVENT_STORE: "eventStore",
|
|
30
|
+
TAG_RESOLVER: "tagResolver",
|
|
31
|
+
} as const
|
|
32
|
+
|
|
33
|
+
const EVENT_FLUSH_REGISTERED_KEY = resourceKey<boolean>("commandInvocationEventFlushRegistered")
|
|
34
|
+
import type { CommandBus } from "./command-bus.js"
|
|
35
|
+
import type { QueryBus } from "./query-bus.js"
|
|
36
|
+
import type { HandlerEnhancerDefinition } from "./handler-enhancer.js"
|
|
37
|
+
import { CORRELATION_DATA_KEY } from "./correlation-data.js"
|
|
38
|
+
import { getResource, setResource, onPrepareCommit, hasResource } from "./processing-state.js"
|
|
39
|
+
import { COMMAND_BUS_KEY } from "./send.js"
|
|
40
|
+
import { QUERY_BUS_KEY } from "./emit-update.js"
|
|
41
|
+
import type { CommandMessage, EventMessage } from "./message.js"
|
|
42
|
+
import {
|
|
43
|
+
BUFFERED_EVENTS_KEY,
|
|
44
|
+
SOURCING_INFOS_KEY,
|
|
45
|
+
STATE_MANAGER_KEY,
|
|
46
|
+
} from "@kronos-ts/eventsourcing"
|
|
47
|
+
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
// Command invocation — D-82: byte-identical ALS resource setup at invocation entry.
|
|
50
|
+
// Plan 08-03a expanded the ALS three-key set: STATE_MANAGER + COMMAND_BUS + QUERY_BUS.
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Creates a command handler invocation function that uses the ProcessingContext
|
|
55
|
+
* for state caching, event buffering, and lifecycle-aware event flushing.
|
|
56
|
+
*
|
|
57
|
+
* D-82: ALS resource setup is preserved BYTE-IDENTICAL across the configurer
|
|
58
|
+
* deletion. The invocation entry seeds the three-key ALS set so module-level
|
|
59
|
+
* helpers (load/append/send/emitUpdate) and the onPrepareCommit closure all
|
|
60
|
+
* resolve their dependencies from the active UoW state.
|
|
61
|
+
*/
|
|
62
|
+
export function createCommandInvocation(
|
|
63
|
+
handler: CommandHandlerDefinition<any, any>,
|
|
64
|
+
config: MinimalConfiguration,
|
|
65
|
+
) {
|
|
66
|
+
return async (message: CommandMessage): Promise<unknown> => {
|
|
67
|
+
// D-82 — full ALS resource setup at command invocation entry.
|
|
68
|
+
// STATE_MANAGER: read by load() helper. COMMAND_BUS: read by send() helper.
|
|
69
|
+
// QUERY_BUS: read by emitUpdate() helper.
|
|
70
|
+
if (config.hasComponent(COMMAND_INVOCATION_KEYS.STATE_MANAGER)) {
|
|
71
|
+
setResource(STATE_MANAGER_KEY, config.getComponent<any>(COMMAND_INVOCATION_KEYS.STATE_MANAGER))
|
|
72
|
+
}
|
|
73
|
+
if (config.hasComponent(COMMAND_INVOCATION_KEYS.COMMAND_BUS)) {
|
|
74
|
+
setResource(COMMAND_BUS_KEY, config.getComponent<CommandBus>(COMMAND_INVOCATION_KEYS.COMMAND_BUS))
|
|
75
|
+
}
|
|
76
|
+
if (config.hasComponent(COMMAND_INVOCATION_KEYS.QUERY_BUS)) {
|
|
77
|
+
setResource(QUERY_BUS_KEY, config.getComponent<QueryBus>(COMMAND_INVOCATION_KEYS.QUERY_BUS))
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Register event flush in PREPARE_COMMIT phase once per UnitOfWork.
|
|
81
|
+
// Nested context-aware dispatch re-enters createCommandInvocation inside
|
|
82
|
+
// the same ALS state; without this guard every nested command registers an
|
|
83
|
+
// additional flush and the same buffered events are appended repeatedly.
|
|
84
|
+
if (!hasResource(EVENT_FLUSH_REGISTERED_KEY)) {
|
|
85
|
+
setResource(EVENT_FLUSH_REGISTERED_KEY, true)
|
|
86
|
+
onPrepareCommit(async () => {
|
|
87
|
+
const buffered = getResource(BUFFERED_EVENTS_KEY)
|
|
88
|
+
if (!buffered || buffered.length === 0) return
|
|
89
|
+
if (!config.hasComponent(COMMAND_INVOCATION_KEYS.EVENT_STORE)) return
|
|
90
|
+
|
|
91
|
+
const eventStore = config.getComponent<{ append: (events: ReadonlyArray<EventMessage>, condition?: any) => Promise<unknown> }>(COMMAND_INVOCATION_KEYS.EVENT_STORE)
|
|
92
|
+
|
|
93
|
+
// Enrich events with correlation data from ProcessingContext
|
|
94
|
+
// (set by the CorrelationDataHandlerInterceptor during handler execution)
|
|
95
|
+
const correlationData = getResource(CORRELATION_DATA_KEY)
|
|
96
|
+
const enrichedEvents = correlationData
|
|
97
|
+
? buffered.map(event => ({
|
|
98
|
+
...event,
|
|
99
|
+
metadata: { ...event.metadata, ...correlationData },
|
|
100
|
+
}))
|
|
101
|
+
: buffered
|
|
102
|
+
|
|
103
|
+
// Resolve tags via TagResolver (if configured)
|
|
104
|
+
const tagResolver = config.getOptionalComponent<{ resolve: (event: EventMessage) => Array<{ key: string; value: string }> }>(COMMAND_INVOCATION_KEYS.TAG_RESOLVER)
|
|
105
|
+
const resolvedEvents = tagResolver
|
|
106
|
+
? enrichedEvents.map(event => ({
|
|
107
|
+
...event,
|
|
108
|
+
tags: [...event.tags, ...tagResolver.resolve(event)],
|
|
109
|
+
}))
|
|
110
|
+
: enrichedEvents
|
|
111
|
+
const sourcingInfos = getResource(SOURCING_INFOS_KEY) ?? []
|
|
112
|
+
|
|
113
|
+
let appendCondition: any = undefined
|
|
114
|
+
if (sourcingInfos.length > 0) {
|
|
115
|
+
const combinedCriteria = sourcingInfos.length === 1
|
|
116
|
+
? sourcingInfos[0]!.criteria
|
|
117
|
+
: { kind: "either" as const, criteria: sourcingInfos.map((s) => s.criteria) }
|
|
118
|
+
|
|
119
|
+
const maxMarker = sourcingInfos.reduce(
|
|
120
|
+
(max, s) => s.markerPosition > max ? s.markerPosition : max,
|
|
121
|
+
-1n,
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
const finalCriteria = handler.appendCondition
|
|
125
|
+
? handler.appendCondition(message.payload, combinedCriteria)
|
|
126
|
+
: combinedCriteria
|
|
127
|
+
|
|
128
|
+
appendCondition = {
|
|
129
|
+
criteria: finalCriteria,
|
|
130
|
+
marker: { position: maxMarker },
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
await eventStore.append(resolvedEvents, appendCondition)
|
|
135
|
+
})
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return handler.handler(message.payload, message.metadata)
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Plan 08-03a (D-82 reshape): function-style helper called by AppImpl.start()
|
|
144
|
+
* to subscribe command handlers natively, without the configurer's Module shape.
|
|
145
|
+
*
|
|
146
|
+
* Subscribes each handler onto the commandBus with the createCommandInvocation
|
|
147
|
+
* wrapper that does Phase 4 D-44 ALS resource setup at invocation entry.
|
|
148
|
+
*
|
|
149
|
+
* @param handlers Array of handler definitions to register
|
|
150
|
+
* @param deps Resolved dependencies — commandBus to subscribe onto, plus a
|
|
151
|
+
* Configuration shim (built natively in AppImpl.start()) that
|
|
152
|
+
* createCommandInvocation reads inside the dispatch hot path.
|
|
153
|
+
* Optional handlerEnhancer mirrors the legacy module's enhancer
|
|
154
|
+
* wrap (kept for parity; default-decorator pipeline already
|
|
155
|
+
* handles framework-default interceptors).
|
|
156
|
+
* @param moduleName Logical name passed to the handler enhancer for
|
|
157
|
+
* discriminating command handler groups (default: "commands").
|
|
158
|
+
*/
|
|
159
|
+
export function registerCommandHandlersNatively(
|
|
160
|
+
handlers: ReadonlyArray<CommandHandlerDefinition<any, any>>,
|
|
161
|
+
deps: {
|
|
162
|
+
commandBus: CommandBus
|
|
163
|
+
config: MinimalConfiguration
|
|
164
|
+
handlerEnhancer?: HandlerEnhancerDefinition
|
|
165
|
+
moduleName?: string
|
|
166
|
+
},
|
|
167
|
+
): void {
|
|
168
|
+
const moduleName = deps.moduleName ?? "commands"
|
|
169
|
+
for (const handler of handlers) {
|
|
170
|
+
const commandName = qualifiedNameToString(handler.descriptor.name)
|
|
171
|
+
let invocation = createCommandInvocation(handler, deps.config)
|
|
172
|
+
|
|
173
|
+
if (deps.handlerEnhancer) {
|
|
174
|
+
invocation = deps.handlerEnhancer.wrapHandler(invocation, {
|
|
175
|
+
messageType: "command",
|
|
176
|
+
messageName: commandName,
|
|
177
|
+
handlerGroup: moduleName,
|
|
178
|
+
})
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
deps.commandBus.subscribe(commandName, invocation)
|
|
182
|
+
}
|
|
183
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { type Metadata, type ResourceKey, resourceKey, mergeMetadata } from "@kronos-ts/common"
|
|
2
|
+
import type { Message } from "./message.js"
|
|
3
|
+
import type { DispatchInterceptor, HandlerInterceptor } from "./interceptor.js"
|
|
4
|
+
import { processingStateStorage, setResource } from "./processing-state.js"
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Resource key for storing correlation data in a ProcessingContext.
|
|
8
|
+
*/
|
|
9
|
+
export const CORRELATION_DATA_KEY: ResourceKey<Record<string, string>> = resourceKey("correlationData")
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Provides correlation data to attach to outgoing messages based on the
|
|
13
|
+
* incoming message being processed.
|
|
14
|
+
*
|
|
15
|
+
* @see messageOriginProvider
|
|
16
|
+
* @see simpleCorrelationDataProvider
|
|
17
|
+
*/
|
|
18
|
+
export interface CorrelationDataProvider {
|
|
19
|
+
/**
|
|
20
|
+
* Extract correlation data from the given message.
|
|
21
|
+
* Returns a map of key-value pairs to attach to outgoing messages.
|
|
22
|
+
* Should not throw — exceptions are caught and logged by the framework.
|
|
23
|
+
*/
|
|
24
|
+
correlationDataFor(message: Message): Record<string, string>
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Get the active correlation data from the active UnitOfWork (D-29 permissive read).
|
|
29
|
+
*
|
|
30
|
+
* Reads directly from the ALS state. Returns `undefined` when called outside
|
|
31
|
+
* an active UnitOfWork (e.g. primary dispatch path, no handler running) —
|
|
32
|
+
* callers MUST tolerate this no-UoW case.
|
|
33
|
+
*
|
|
34
|
+
* Plan 03-04 (CTX-04 / D-29): no-arg permissive ALS read; the explicit
|
|
35
|
+
* ProcessingContext parameter is gone.
|
|
36
|
+
*/
|
|
37
|
+
export function getActiveCorrelationData(): Record<string, string> | undefined {
|
|
38
|
+
const state = processingStateStorage.getStore()
|
|
39
|
+
if (!state) return undefined
|
|
40
|
+
return state.resources.get(CORRELATION_DATA_KEY.symbol) as
|
|
41
|
+
| Record<string, string>
|
|
42
|
+
| undefined
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
// Built-in providers
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Default correlation data provider that tracks message lineage.
|
|
51
|
+
*
|
|
52
|
+
* - `correlationId`: preserved from the incoming message's metadata, or falls
|
|
53
|
+
* back to the message's own identifier (starts a new correlation chain).
|
|
54
|
+
* - `causationId`: always set to the incoming message's identifier (direct cause).
|
|
55
|
+
*
|
|
56
|
+
* @param correlationKey metadata key for correlation ID (default: "correlationId")
|
|
57
|
+
* @param causationKey metadata key for causation ID (default: "causationId")
|
|
58
|
+
*/
|
|
59
|
+
export function messageOriginProvider(
|
|
60
|
+
correlationKey = "correlationId",
|
|
61
|
+
causationKey = "causationId",
|
|
62
|
+
): CorrelationDataProvider {
|
|
63
|
+
return {
|
|
64
|
+
correlationDataFor(message: Message): Record<string, string> {
|
|
65
|
+
const existingCorrelation = message.metadata[correlationKey]
|
|
66
|
+
return {
|
|
67
|
+
[correlationKey]: existingCorrelation != null
|
|
68
|
+
? String(existingCorrelation)
|
|
69
|
+
: message.identifier,
|
|
70
|
+
[causationKey]: message.identifier,
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Copies specific metadata keys from the incoming message to outgoing messages.
|
|
78
|
+
*
|
|
79
|
+
* Silently ignores missing keys.
|
|
80
|
+
*/
|
|
81
|
+
export function simpleCorrelationDataProvider(...metadataKeys: string[]): CorrelationDataProvider {
|
|
82
|
+
return {
|
|
83
|
+
correlationDataFor(message: Message): Record<string, string> {
|
|
84
|
+
const result: Record<string, string> = {}
|
|
85
|
+
for (const key of metadataKeys) {
|
|
86
|
+
if (key in message.metadata && message.metadata[key] != null) {
|
|
87
|
+
result[key] = String(message.metadata[key])
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return result
|
|
91
|
+
},
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// ---------------------------------------------------------------------------
|
|
96
|
+
// Interceptor factory
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Creates a handler interceptor that extracts correlation data from the
|
|
101
|
+
* incoming message and stores it in the ProcessingContext.
|
|
102
|
+
*
|
|
103
|
+
* This is the "extract" phase of the dual-interceptor pattern.
|
|
104
|
+
*
|
|
105
|
+
* Each provider is called with the message. Exceptions are caught and
|
|
106
|
+
* logged (they don't break message processing). Results are merged —
|
|
107
|
+
* later providers override earlier ones on key conflicts.
|
|
108
|
+
*
|
|
109
|
+
* The correlation data is stored as a ProcessingContext resource under
|
|
110
|
+
* `CORRELATION_DATA_KEY`, where the dispatch interceptor reads it.
|
|
111
|
+
*/
|
|
112
|
+
export function correlationDataHandlerInterceptor(
|
|
113
|
+
providers: ReadonlyArray<CorrelationDataProvider>,
|
|
114
|
+
): HandlerInterceptor {
|
|
115
|
+
return (message, next) => {
|
|
116
|
+
const correlationData: Record<string, string> = {}
|
|
117
|
+
|
|
118
|
+
for (const provider of providers) {
|
|
119
|
+
try {
|
|
120
|
+
const data = provider.correlationDataFor(message)
|
|
121
|
+
Object.assign(correlationData, data)
|
|
122
|
+
} catch (err) {
|
|
123
|
+
console.warn(
|
|
124
|
+
"Encountered exception creating correlation data from provider:",
|
|
125
|
+
err,
|
|
126
|
+
)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Store in ALS-backed processing state.
|
|
131
|
+
// CTX-01 / Plan 03-03: HandlerInterceptor no longer threads ProcessingContext;
|
|
132
|
+
// resource writes go directly through the module-level ALS accessor.
|
|
133
|
+
setResource(CORRELATION_DATA_KEY, correlationData)
|
|
134
|
+
|
|
135
|
+
return next()
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Creates a dispatch interceptor that reads correlation data from the
|
|
141
|
+
* active ProcessingContext and merges it into the outgoing message's
|
|
142
|
+
* metadata.
|
|
143
|
+
*
|
|
144
|
+
* This is the "apply" phase of the dual-interceptor pattern.
|
|
145
|
+
*
|
|
146
|
+
* When a ProcessingContext is available (nested dispatch from a handler),
|
|
147
|
+
* the interceptor reads correlation data stored by the handler interceptor
|
|
148
|
+
* and merges it into the outgoing message's metadata. For the primary
|
|
149
|
+
* dispatch path (no context), this is a no-op — correlation data flows
|
|
150
|
+
* through message metadata inheritance.
|
|
151
|
+
*/
|
|
152
|
+
export function correlationDataDispatchInterceptor<M extends Message>(): DispatchInterceptor<M> {
|
|
153
|
+
return (message: M): M => {
|
|
154
|
+
// D-24: single code path. Read directly from the ALS state — `getResource`
|
|
155
|
+
// throws on no-UoW, but the dispatch interceptor MUST tolerate the no-UoW
|
|
156
|
+
// primary-dispatch path and return the message unchanged.
|
|
157
|
+
// CTX-01 / Plan 03-03: vestigial _context parameter removed.
|
|
158
|
+
const state = processingStateStorage.getStore()
|
|
159
|
+
if (!state) return message
|
|
160
|
+
const correlationData = state.resources.get(CORRELATION_DATA_KEY.symbol) as
|
|
161
|
+
| Record<string, string>
|
|
162
|
+
| undefined
|
|
163
|
+
if (!correlationData || Object.keys(correlationData).length === 0) return message
|
|
164
|
+
return {
|
|
165
|
+
...message,
|
|
166
|
+
metadata: mergeMetadata(message.metadata, correlationData),
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|