@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,134 @@
|
|
|
1
|
+
import type { EventMessage } from "./message.js";
|
|
2
|
+
/**
|
|
3
|
+
* A dead letter — an event that failed processing and was parked
|
|
4
|
+
* for later retry or manual intervention.
|
|
5
|
+
*/
|
|
6
|
+
export interface DeadLetter {
|
|
7
|
+
/** The original event message that failed. */
|
|
8
|
+
readonly message: EventMessage;
|
|
9
|
+
/** The error that caused the failure. */
|
|
10
|
+
readonly cause: Error;
|
|
11
|
+
/** When this letter was first enqueued. */
|
|
12
|
+
readonly enqueuedAt: number;
|
|
13
|
+
/** When this letter was last touched (enqueued or requeued). */
|
|
14
|
+
readonly lastTouched: number;
|
|
15
|
+
/** Additional diagnostics metadata about the failure. */
|
|
16
|
+
readonly diagnostics: Record<string, unknown>;
|
|
17
|
+
/** The sequence identifier this letter belongs to. */
|
|
18
|
+
readonly sequenceIdentifier: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Decision on whether to enqueue a failed event as a dead letter.
|
|
22
|
+
*/
|
|
23
|
+
export interface EnqueueDecision {
|
|
24
|
+
/** Whether to enqueue the failed event. */
|
|
25
|
+
readonly shouldEnqueue: boolean;
|
|
26
|
+
/** Optional updated cause (may differ from original). */
|
|
27
|
+
readonly cause?: Error;
|
|
28
|
+
/** Additional diagnostics to attach. */
|
|
29
|
+
readonly diagnostics?: Record<string, unknown>;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Policy that determines whether a failed event should be dead-lettered.
|
|
33
|
+
*/
|
|
34
|
+
export interface EnqueuePolicy {
|
|
35
|
+
/**
|
|
36
|
+
* Decide whether to enqueue a failed event.
|
|
37
|
+
* @param letter The dead letter candidate
|
|
38
|
+
* @param cause The error that caused the failure
|
|
39
|
+
*/
|
|
40
|
+
decide(letter: DeadLetter, cause: Error): EnqueueDecision;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Default policy: always enqueue with the original cause.
|
|
44
|
+
*/
|
|
45
|
+
export declare function alwaysEnqueuePolicy(): EnqueuePolicy;
|
|
46
|
+
/**
|
|
47
|
+
* A sequenced dead letter queue that maintains ordering within sequences.
|
|
48
|
+
*
|
|
49
|
+
* Events in the same sequence (identified by `sequenceIdentifier`) are
|
|
50
|
+
* ordered by insertion. When a sequence has dead letters, subsequent
|
|
51
|
+
* events in that sequence are also dead-lettered to preserve ordering.
|
|
52
|
+
*
|
|
53
|
+
* Typical sequence identifier: aggregate ID or correlation ID.
|
|
54
|
+
*
|
|
55
|
+
* Database-backed implementations participate in the active UnitOfWork via
|
|
56
|
+
* the ALS-managed transaction (read through `getActiveTransaction()` /
|
|
57
|
+
* `getResource(TRANSACTION_KEY)`); no `ProcessingContext` parameter is
|
|
58
|
+
* threaded through the public surface.
|
|
59
|
+
*/
|
|
60
|
+
export interface SequencedDeadLetterQueue {
|
|
61
|
+
/**
|
|
62
|
+
* Enqueue a dead letter into the given sequence.
|
|
63
|
+
*/
|
|
64
|
+
enqueue(letter: DeadLetter): Promise<void>;
|
|
65
|
+
/**
|
|
66
|
+
* Enqueue a dead letter only if the sequence already has dead letters.
|
|
67
|
+
* Used to block subsequent events in a failed sequence.
|
|
68
|
+
* The supplier is only called if the sequence exists (avoids creating
|
|
69
|
+
* unnecessary objects).
|
|
70
|
+
* Returns true if the letter was enqueued (sequence existed).
|
|
71
|
+
*/
|
|
72
|
+
enqueueIfPresent(sequenceIdentifier: string, letterSupplier: () => DeadLetter): Promise<boolean>;
|
|
73
|
+
/**
|
|
74
|
+
* Remove a dead letter from the queue (successfully reprocessed).
|
|
75
|
+
*/
|
|
76
|
+
evict(sequenceIdentifier: string, letter: DeadLetter): Promise<void>;
|
|
77
|
+
/**
|
|
78
|
+
* Re-insert a dead letter at the front of its sequence with updated properties.
|
|
79
|
+
*/
|
|
80
|
+
requeue(letter: DeadLetter, update?: Partial<Pick<DeadLetter, "cause" | "diagnostics">>): Promise<void>;
|
|
81
|
+
/**
|
|
82
|
+
* Check if a sequence has any dead letters.
|
|
83
|
+
*/
|
|
84
|
+
contains(sequenceIdentifier: string): Promise<boolean>;
|
|
85
|
+
/**
|
|
86
|
+
* Get all dead letters in a sequence, in insertion order.
|
|
87
|
+
*/
|
|
88
|
+
deadLetterSequence(sequenceIdentifier: string): Promise<DeadLetter[]>;
|
|
89
|
+
/**
|
|
90
|
+
* Get all sequence identifiers that have dead letters.
|
|
91
|
+
*/
|
|
92
|
+
sequenceIdentifiers(): Promise<string[]>;
|
|
93
|
+
/**
|
|
94
|
+
* Process the oldest dead letter sequence matching the filter.
|
|
95
|
+
* For each letter in the sequence:
|
|
96
|
+
* - If processingTask returns `{ shouldEnqueue: false }`: letter is evicted, continue
|
|
97
|
+
* - If processingTask returns `{ shouldEnqueue: true }`: letter is requeued, stop
|
|
98
|
+
*
|
|
99
|
+
* Returns true if a sequence was processed.
|
|
100
|
+
*/
|
|
101
|
+
process(sequenceFilter: (sequenceId: string) => boolean, processingTask: (letter: DeadLetter) => Promise<EnqueueDecision>): Promise<boolean>;
|
|
102
|
+
/** Total number of dead letters across all sequences. */
|
|
103
|
+
size(): number;
|
|
104
|
+
/** Number of sequences with dead letters. */
|
|
105
|
+
amountOfSequences(): number;
|
|
106
|
+
/** Clear all dead letters. */
|
|
107
|
+
clear(): Promise<void>;
|
|
108
|
+
/**
|
|
109
|
+
* Check if the queue is full for the given sequence.
|
|
110
|
+
* Returns true if max sequences or max sequence size is reached.
|
|
111
|
+
*/
|
|
112
|
+
isFull(sequenceIdentifier: string): boolean;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Creates an in-memory dead letter queue.
|
|
116
|
+
*
|
|
117
|
+
* @param options.maxSequences Maximum number of sequences (default: 1024)
|
|
118
|
+
* @param options.maxSequenceSize Maximum letters per sequence (default: 1024)
|
|
119
|
+
*/
|
|
120
|
+
export declare function createInMemoryDeadLetterQueue(options?: {
|
|
121
|
+
maxSequences?: number;
|
|
122
|
+
maxSequenceSize?: number;
|
|
123
|
+
}): SequencedDeadLetterQueue;
|
|
124
|
+
/**
|
|
125
|
+
* Thrown when the dead letter queue is full.
|
|
126
|
+
*/
|
|
127
|
+
export declare class DeadLetterQueueOverflowError extends Error {
|
|
128
|
+
constructor(message: string);
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Creates a DeadLetter from a failed event.
|
|
132
|
+
*/
|
|
133
|
+
export declare function createDeadLetter(message: EventMessage, cause: Error, sequenceIdentifier: string, diagnostics?: Record<string, unknown>): DeadLetter;
|
|
134
|
+
//# sourceMappingURL=dead-letter-queue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dead-letter-queue.d.ts","sourceRoot":"","sources":["../src/dead-letter-queue.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default policy: always enqueue with the original cause.
|
|
3
|
+
*/
|
|
4
|
+
export function alwaysEnqueuePolicy() {
|
|
5
|
+
return {
|
|
6
|
+
decide() {
|
|
7
|
+
return { shouldEnqueue: true };
|
|
8
|
+
},
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Creates an in-memory dead letter queue.
|
|
13
|
+
*
|
|
14
|
+
* @param options.maxSequences Maximum number of sequences (default: 1024)
|
|
15
|
+
* @param options.maxSequenceSize Maximum letters per sequence (default: 1024)
|
|
16
|
+
*/
|
|
17
|
+
export function createInMemoryDeadLetterQueue(options) {
|
|
18
|
+
const maxSequences = options?.maxSequences ?? 1024;
|
|
19
|
+
const maxSequenceSize = options?.maxSequenceSize ?? 1024;
|
|
20
|
+
const sequences = new Map();
|
|
21
|
+
const processing = new Set();
|
|
22
|
+
return {
|
|
23
|
+
async enqueue(letter) {
|
|
24
|
+
const seq = sequences.get(letter.sequenceIdentifier);
|
|
25
|
+
if (seq) {
|
|
26
|
+
if (seq.length >= maxSequenceSize) {
|
|
27
|
+
throw new DeadLetterQueueOverflowError(`sequence "${letter.sequenceIdentifier}" has reached max size ${maxSequenceSize}`);
|
|
28
|
+
}
|
|
29
|
+
seq.push(letter);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
if (sequences.size >= maxSequences) {
|
|
33
|
+
throw new DeadLetterQueueOverflowError(`max sequences ${maxSequences} reached`);
|
|
34
|
+
}
|
|
35
|
+
sequences.set(letter.sequenceIdentifier, [letter]);
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
async enqueueIfPresent(sequenceIdentifier, letterSupplier) {
|
|
39
|
+
const seq = sequences.get(sequenceIdentifier);
|
|
40
|
+
if (!seq)
|
|
41
|
+
return false;
|
|
42
|
+
if (seq.length >= maxSequenceSize) {
|
|
43
|
+
throw new DeadLetterQueueOverflowError(`sequence "${sequenceIdentifier}" has reached max size ${maxSequenceSize}`);
|
|
44
|
+
}
|
|
45
|
+
seq.push(letterSupplier());
|
|
46
|
+
return true;
|
|
47
|
+
},
|
|
48
|
+
async evict(sequenceIdentifier, letter) {
|
|
49
|
+
const seq = sequences.get(sequenceIdentifier);
|
|
50
|
+
if (!seq)
|
|
51
|
+
return;
|
|
52
|
+
const idx = seq.indexOf(letter);
|
|
53
|
+
if (idx >= 0)
|
|
54
|
+
seq.splice(idx, 1);
|
|
55
|
+
if (seq.length === 0)
|
|
56
|
+
sequences.delete(sequenceIdentifier);
|
|
57
|
+
},
|
|
58
|
+
async requeue(letter, update) {
|
|
59
|
+
const seq = sequences.get(letter.sequenceIdentifier);
|
|
60
|
+
if (!seq)
|
|
61
|
+
return;
|
|
62
|
+
const updated = {
|
|
63
|
+
...letter,
|
|
64
|
+
lastTouched: Date.now(),
|
|
65
|
+
cause: update?.cause ?? letter.cause,
|
|
66
|
+
diagnostics: update?.diagnostics
|
|
67
|
+
? { ...letter.diagnostics, ...update.diagnostics }
|
|
68
|
+
: letter.diagnostics,
|
|
69
|
+
};
|
|
70
|
+
const idx = seq.indexOf(letter);
|
|
71
|
+
if (idx >= 0)
|
|
72
|
+
seq.splice(idx, 1);
|
|
73
|
+
seq.unshift(updated);
|
|
74
|
+
},
|
|
75
|
+
async contains(sequenceIdentifier) {
|
|
76
|
+
const seq = sequences.get(sequenceIdentifier);
|
|
77
|
+
return seq !== undefined && seq.length > 0;
|
|
78
|
+
},
|
|
79
|
+
async deadLetterSequence(sequenceIdentifier) {
|
|
80
|
+
return sequences.get(sequenceIdentifier) ?? [];
|
|
81
|
+
},
|
|
82
|
+
async sequenceIdentifiers() {
|
|
83
|
+
return [...sequences.keys()];
|
|
84
|
+
},
|
|
85
|
+
async process(sequenceFilter, processingTask) {
|
|
86
|
+
// Find oldest untaken sequence matching filter
|
|
87
|
+
let oldestId;
|
|
88
|
+
let oldestTime = Infinity;
|
|
89
|
+
for (const [id, letters] of sequences) {
|
|
90
|
+
if (processing.has(id))
|
|
91
|
+
continue;
|
|
92
|
+
if (letters.length === 0)
|
|
93
|
+
continue;
|
|
94
|
+
if (!sequenceFilter(id))
|
|
95
|
+
continue;
|
|
96
|
+
const firstTouched = letters[0].lastTouched;
|
|
97
|
+
if (firstTouched < oldestTime) {
|
|
98
|
+
oldestTime = firstTouched;
|
|
99
|
+
oldestId = id;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (!oldestId)
|
|
103
|
+
return false;
|
|
104
|
+
processing.add(oldestId);
|
|
105
|
+
try {
|
|
106
|
+
const letters = sequences.get(oldestId);
|
|
107
|
+
if (!letters)
|
|
108
|
+
return false;
|
|
109
|
+
// Process letters in order — take a snapshot of current letters
|
|
110
|
+
const snapshot = [...letters];
|
|
111
|
+
for (const letter of snapshot) {
|
|
112
|
+
const decision = await processingTask(letter);
|
|
113
|
+
if (decision.shouldEnqueue) {
|
|
114
|
+
// Requeue and stop — sequence is still blocked
|
|
115
|
+
await this.requeue(letter, {
|
|
116
|
+
cause: decision.cause,
|
|
117
|
+
diagnostics: decision.diagnostics,
|
|
118
|
+
});
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
// Evict — successfully reprocessed
|
|
122
|
+
await this.evict(oldestId, letter);
|
|
123
|
+
}
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
finally {
|
|
127
|
+
processing.delete(oldestId);
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
size() {
|
|
131
|
+
let total = 0;
|
|
132
|
+
for (const letters of sequences.values()) {
|
|
133
|
+
total += letters.length;
|
|
134
|
+
}
|
|
135
|
+
return total;
|
|
136
|
+
},
|
|
137
|
+
amountOfSequences() {
|
|
138
|
+
return sequences.size;
|
|
139
|
+
},
|
|
140
|
+
async clear() {
|
|
141
|
+
sequences.clear();
|
|
142
|
+
processing.clear();
|
|
143
|
+
},
|
|
144
|
+
isFull(sequenceIdentifier) {
|
|
145
|
+
const seq = sequences.get(sequenceIdentifier);
|
|
146
|
+
if (seq) {
|
|
147
|
+
return seq.length >= maxSequenceSize;
|
|
148
|
+
}
|
|
149
|
+
return sequences.size >= maxSequences;
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Thrown when the dead letter queue is full.
|
|
155
|
+
*/
|
|
156
|
+
export class DeadLetterQueueOverflowError extends Error {
|
|
157
|
+
constructor(message) {
|
|
158
|
+
super(`Dead letter queue overflow: ${message}`);
|
|
159
|
+
this.name = "DeadLetterQueueOverflowError";
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Creates a DeadLetter from a failed event.
|
|
164
|
+
*/
|
|
165
|
+
export function createDeadLetter(message, cause, sequenceIdentifier, diagnostics) {
|
|
166
|
+
const now = Date.now();
|
|
167
|
+
return {
|
|
168
|
+
message,
|
|
169
|
+
cause,
|
|
170
|
+
enqueuedAt: now,
|
|
171
|
+
lastTouched: now,
|
|
172
|
+
diagnostics: diagnostics ?? {},
|
|
173
|
+
sequenceIdentifier,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
//# sourceMappingURL=dead-letter-queue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dead-letter-queue.js","sourceRoot":"","sources":["../src/dead-letter-queue.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { EventMessage } from "./message.js";
|
|
2
|
+
import type { EventHandlerRegistration } from "./handler.js";
|
|
3
|
+
import type { SequencedEvent } from "./event-source.js";
|
|
4
|
+
import { type SequencedDeadLetterQueue, type EnqueuePolicy } from "./dead-letter-queue.js";
|
|
5
|
+
/**
|
|
6
|
+
* Options for dead-lettering event handler wrapper.
|
|
7
|
+
*/
|
|
8
|
+
export interface DeadLetteringOptions {
|
|
9
|
+
/** The dead letter queue to use. */
|
|
10
|
+
queue: SequencedDeadLetterQueue;
|
|
11
|
+
/** Policy deciding whether to dead-letter a failed event. Default: always. */
|
|
12
|
+
policy?: EnqueuePolicy;
|
|
13
|
+
/**
|
|
14
|
+
* Extract a sequence identifier from an event. Events in the same
|
|
15
|
+
* sequence are ordered — if one fails, subsequent ones are blocked.
|
|
16
|
+
* Default: uses the first tag value or event name.
|
|
17
|
+
*/
|
|
18
|
+
sequenceIdentifier?: (event: EventMessage) => string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Wraps event delivery with dead-letter support.
|
|
22
|
+
*
|
|
23
|
+
* When a handler fails:
|
|
24
|
+
* 1. Creates a DeadLetter and consults the EnqueuePolicy
|
|
25
|
+
* 2. If policy says enqueue: adds to DLQ, continues with next event
|
|
26
|
+
* 3. If policy says don't enqueue: error is swallowed
|
|
27
|
+
*
|
|
28
|
+
* When processing an event whose sequence already has dead letters:
|
|
29
|
+
* - The event is automatically dead-lettered (sequence is blocked)
|
|
30
|
+
* - This preserves ordering within the sequence
|
|
31
|
+
*/
|
|
32
|
+
export declare function createDeadLetteringDelivery(options: DeadLetteringOptions): {
|
|
33
|
+
/**
|
|
34
|
+
* Deliver an event to handlers, with dead-letter support.
|
|
35
|
+
*
|
|
36
|
+
* The DLQ participates in any active transaction via ALS — both the
|
|
37
|
+
* caller and the DLQ implementation read transactional state from the
|
|
38
|
+
* UnitOfWork ALS store, no explicit ProcessingContext is threaded.
|
|
39
|
+
*/
|
|
40
|
+
deliver(sequencedEvent: SequencedEvent, handlers: Array<EventHandlerRegistration<any>>): Promise<void>;
|
|
41
|
+
};
|
|
42
|
+
//# sourceMappingURL=dead-lettering-handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dead-lettering-handler.d.ts","sourceRoot":"","sources":["../src/dead-lettering-handler.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAChD,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAA;AAC5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AACvD,OAAO,EACL,KAAK,wBAAwB,EAC7B,KAAK,aAAa,EAGnB,MAAM,wBAAwB,CAAA;AAE/B;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,oCAAoC;IACpC,KAAK,EAAE,wBAAwB,CAAA;IAC/B,8EAA8E;IAC9E,MAAM,CAAC,EAAE,aAAa,CAAA;IACtB;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,MAAM,CAAA;CACrD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,oBAAoB;IAQrE;;;;;;OAMG;4BAEe,cAAc,YACpB,KAAK,CAAC,wBAAwB,CAAC,GAAG,CAAC,CAAC,GAC7C,OAAO,CAAC,IAAI,CAAC;EA2CnB"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { qualifiedNameToString } from "@kronos-ts/common";
|
|
2
|
+
import { alwaysEnqueuePolicy, createDeadLetter, } from "./dead-letter-queue.js";
|
|
3
|
+
/**
|
|
4
|
+
* Wraps event delivery with dead-letter support.
|
|
5
|
+
*
|
|
6
|
+
* When a handler fails:
|
|
7
|
+
* 1. Creates a DeadLetter and consults the EnqueuePolicy
|
|
8
|
+
* 2. If policy says enqueue: adds to DLQ, continues with next event
|
|
9
|
+
* 3. If policy says don't enqueue: error is swallowed
|
|
10
|
+
*
|
|
11
|
+
* When processing an event whose sequence already has dead letters:
|
|
12
|
+
* - The event is automatically dead-lettered (sequence is blocked)
|
|
13
|
+
* - This preserves ordering within the sequence
|
|
14
|
+
*/
|
|
15
|
+
export function createDeadLetteringDelivery(options) {
|
|
16
|
+
const { queue, policy = alwaysEnqueuePolicy(), sequenceIdentifier = defaultSequenceIdentifier, } = options;
|
|
17
|
+
return {
|
|
18
|
+
/**
|
|
19
|
+
* Deliver an event to handlers, with dead-letter support.
|
|
20
|
+
*
|
|
21
|
+
* The DLQ participates in any active transaction via ALS — both the
|
|
22
|
+
* caller and the DLQ implementation read transactional state from the
|
|
23
|
+
* UnitOfWork ALS store, no explicit ProcessingContext is threaded.
|
|
24
|
+
*/
|
|
25
|
+
async deliver(sequencedEvent, handlers) {
|
|
26
|
+
const event = sequencedEvent.event;
|
|
27
|
+
const seqId = sequenceIdentifier(event);
|
|
28
|
+
// If this sequence already has dead letters, block this event too
|
|
29
|
+
const blocked = await queue.enqueueIfPresent(seqId, () => createDeadLetter(event, new Error("Blocked: previous event in sequence failed"), seqId, { blocked: true, position: Number(sequencedEvent.sequence) }));
|
|
30
|
+
if (blocked)
|
|
31
|
+
return;
|
|
32
|
+
// Try to deliver to all handlers
|
|
33
|
+
for (const reg of handlers) {
|
|
34
|
+
try {
|
|
35
|
+
await reg.handler(event.payload, event.metadata);
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
39
|
+
const letter = createDeadLetter(event, error, seqId, {
|
|
40
|
+
position: Number(sequencedEvent.sequence),
|
|
41
|
+
handlerName: qualifiedNameToString(reg.descriptor.name),
|
|
42
|
+
});
|
|
43
|
+
const decision = policy.decide(letter, error);
|
|
44
|
+
if (decision.shouldEnqueue) {
|
|
45
|
+
await queue.enqueue({
|
|
46
|
+
...letter,
|
|
47
|
+
cause: decision.cause ?? letter.cause,
|
|
48
|
+
diagnostics: decision.diagnostics
|
|
49
|
+
? { ...letter.diagnostics, ...decision.diagnostics }
|
|
50
|
+
: letter.diagnostics,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
// Error is consumed by DLQ — don't propagate
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function defaultSequenceIdentifier(event) {
|
|
61
|
+
// Use first tag value if available, otherwise event name
|
|
62
|
+
if (event.tags && event.tags.length > 0) {
|
|
63
|
+
return event.tags[0].value;
|
|
64
|
+
}
|
|
65
|
+
return qualifiedNameToString(event.name);
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=dead-lettering-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dead-lettering-handler.js","sourceRoot":"","sources":["../src/dead-lettering-handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAA;AAIzD,OAAO,EAGL,mBAAmB,EACnB,gBAAgB,GACjB,MAAM,wBAAwB,CAAA;AAkB/B;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,2BAA2B,CAAC,OAA6B;IACvE,MAAM,EACJ,KAAK,EACL,MAAM,GAAG,mBAAmB,EAAE,EAC9B,kBAAkB,GAAG,yBAAyB,GAC/C,GAAG,OAAO,CAAA;IAEX,OAAO;QACL;;;;;;WAMG;QACH,KAAK,CAAC,OAAO,CACX,cAA8B,EAC9B,QAA8C;YAE9C,MAAM,KAAK,GAAG,cAAc,CAAC,KAAK,CAAA;YAClC,MAAM,KAAK,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAA;YAEvC,kEAAkE;YAClE,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,gBAAgB,CAC1C,KAAK,EACL,GAAG,EAAE,CAAC,gBAAgB,CACpB,KAAK,EACL,IAAI,KAAK,CAAC,4CAA4C,CAAC,EACvD,KAAK,EACL,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,CAC7D,CACF,CAAA;YACD,IAAI,OAAO;gBAAE,OAAM;YAEnB,iCAAiC;YACjC,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;gBAC3B,IAAI,CAAC;oBACH,MAAM,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAA;gBAClD,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,MAAM,KAAK,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAA;oBACjE,MAAM,MAAM,GAAG,gBAAgB,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE;wBACnD,QAAQ,EAAE,MAAM,CAAC,cAAc,CAAC,QAAQ,CAAC;wBACzC,WAAW,EAAE,qBAAqB,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC;qBACxD,CAAC,CAAA;oBAEF,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;oBAC7C,IAAI,QAAQ,CAAC,aAAa,EAAE,CAAC;wBAC3B,MAAM,KAAK,CAAC,OAAO,CAAC;4BAClB,GAAG,MAAM;4BACT,KAAK,EAAE,QAAQ,CAAC,KAAK,IAAI,MAAM,CAAC,KAAK;4BACrC,WAAW,EAAE,QAAQ,CAAC,WAAW;gCAC/B,CAAC,CAAC,EAAE,GAAG,MAAM,CAAC,WAAW,EAAE,GAAG,QAAQ,CAAC,WAAW,EAAE;gCACpD,CAAC,CAAC,MAAM,CAAC,WAAW;yBACvB,CAAC,CAAA;oBACJ,CAAC;oBACD,6CAA6C;oBAC7C,OAAM;gBACR,CAAC;YACH,CAAC;QACH,CAAC;KACF,CAAA;AACH,CAAC;AAED,SAAS,yBAAyB,CAAC,KAAmB;IACpD,yDAAyD;IACzD,IAAI,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxC,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,KAAK,CAAA;IAC7B,CAAC;IACD,OAAO,qBAAqB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;AAC1C,CAAC"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import type { z } from "zod";
|
|
2
|
+
import type { QualifiedName, Tag } from "@kronos-ts/common";
|
|
3
|
+
/**
|
|
4
|
+
* Describes a command message type — its name, payload schema,
|
|
5
|
+
* and optional result schema for typed gateway returns.
|
|
6
|
+
*/
|
|
7
|
+
export interface CommandDescriptor<P extends z.ZodType = z.ZodType, R extends z.ZodType | undefined = undefined> {
|
|
8
|
+
readonly kind: "command";
|
|
9
|
+
readonly name: QualifiedName;
|
|
10
|
+
/** Version of the command. Default: "1.0". */
|
|
11
|
+
readonly version: string;
|
|
12
|
+
readonly payload: P;
|
|
13
|
+
/** Optional result schema — enables typed return from `commandGateway.send()`. */
|
|
14
|
+
readonly result?: R;
|
|
15
|
+
/**
|
|
16
|
+
* The payload field that contains the routing key for distributed command routing.
|
|
17
|
+
*
|
|
18
|
+
* Used by the command gateway to extract the routing key before dispatch.
|
|
19
|
+
* Commands with the same routing key are routed to the same handler instance.
|
|
20
|
+
*/
|
|
21
|
+
readonly routingKey?: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Describes an event message type — its name, payload schema, and tag derivation.
|
|
25
|
+
* Tags define how events are indexed for criteria-based sourcing.
|
|
26
|
+
*/
|
|
27
|
+
export interface EventDescriptor<P extends z.ZodType = z.ZodType> {
|
|
28
|
+
readonly kind: "event";
|
|
29
|
+
readonly name: QualifiedName;
|
|
30
|
+
readonly version: string;
|
|
31
|
+
readonly payload: P;
|
|
32
|
+
readonly tags?: (payload: z.infer<P>) => Tag[];
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Describes a query message type — its name, payload schema,
|
|
36
|
+
* and optional result schema for typed gateway returns.
|
|
37
|
+
*/
|
|
38
|
+
export interface QueryDescriptor<P extends z.ZodType = z.ZodType, R extends z.ZodType | undefined = undefined> {
|
|
39
|
+
readonly kind: "query";
|
|
40
|
+
readonly name: QualifiedName;
|
|
41
|
+
/** Version of the query. Default: "1.0". */
|
|
42
|
+
readonly version: string;
|
|
43
|
+
readonly payload: P;
|
|
44
|
+
/** Optional result schema — enables typed return from `queryGateway.query()`. */
|
|
45
|
+
readonly result?: R;
|
|
46
|
+
}
|
|
47
|
+
/** Any message descriptor. */
|
|
48
|
+
export type MessageDescriptor = CommandDescriptor | EventDescriptor | QueryDescriptor;
|
|
49
|
+
/**
|
|
50
|
+
* Creates a command descriptor.
|
|
51
|
+
*
|
|
52
|
+
* Without result schema (void command):
|
|
53
|
+
* ```
|
|
54
|
+
* const CreateCourse = command({
|
|
55
|
+
* name: qn("university", "CreateCourse"),
|
|
56
|
+
* payload: z.object({ courseId: z.string(), name: z.string() }),
|
|
57
|
+
* routingKey: "courseId",
|
|
58
|
+
* })
|
|
59
|
+
* ```
|
|
60
|
+
*
|
|
61
|
+
* With result schema (typed return):
|
|
62
|
+
* ```
|
|
63
|
+
* const CreateCourse = command({
|
|
64
|
+
* name: qn("university", "CreateCourse"),
|
|
65
|
+
* payload: z.object({ courseId: z.string() }),
|
|
66
|
+
* result: z.object({ courseId: z.string() }),
|
|
67
|
+
* routingKey: "courseId",
|
|
68
|
+
* })
|
|
69
|
+
* // commandGateway.send(CreateCourse, { courseId: "cs-101" }) → Promise<{ courseId: string }>
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
export declare function command<P extends z.ZodType>(def: {
|
|
73
|
+
name: QualifiedName;
|
|
74
|
+
version?: string;
|
|
75
|
+
payload: P;
|
|
76
|
+
routingKey?: string;
|
|
77
|
+
}): CommandDescriptor<P, undefined>;
|
|
78
|
+
export declare function command<P extends z.ZodType, R extends z.ZodType>(def: {
|
|
79
|
+
name: QualifiedName;
|
|
80
|
+
version?: string;
|
|
81
|
+
payload: P;
|
|
82
|
+
result: R;
|
|
83
|
+
routingKey?: string;
|
|
84
|
+
}): CommandDescriptor<P, R>;
|
|
85
|
+
/**
|
|
86
|
+
* Creates an event descriptor.
|
|
87
|
+
*
|
|
88
|
+
* Tags can return `Tag[]` or a `Record<string, string>`:
|
|
89
|
+
* ```typescript
|
|
90
|
+
* event({
|
|
91
|
+
* name: qn("university", "CourseCreated"),
|
|
92
|
+
* payload: z.object({ courseId: z.string(), name: z.string() }),
|
|
93
|
+
* tags: (p) => ({ courseId: p.courseId }),
|
|
94
|
+
* })
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
export declare function event<P extends z.ZodType>(def: {
|
|
98
|
+
name: QualifiedName;
|
|
99
|
+
version?: string;
|
|
100
|
+
payload: P;
|
|
101
|
+
tags?: (payload: z.infer<P>) => Tag[] | Record<string, string>;
|
|
102
|
+
}): EventDescriptor<P>;
|
|
103
|
+
/**
|
|
104
|
+
* Creates a query descriptor.
|
|
105
|
+
*
|
|
106
|
+
* Without result schema:
|
|
107
|
+
* ```
|
|
108
|
+
* const GetCourse = query({
|
|
109
|
+
* name: qn("university", "GetCourseView"),
|
|
110
|
+
* payload: z.object({ courseId: z.string() }),
|
|
111
|
+
* })
|
|
112
|
+
* ```
|
|
113
|
+
*
|
|
114
|
+
* With result schema (typed return):
|
|
115
|
+
* ```
|
|
116
|
+
* const GetCourse = query({
|
|
117
|
+
* name: qn("university", "GetCourseView"),
|
|
118
|
+
* payload: z.object({ courseId: z.string() }),
|
|
119
|
+
* result: z.object({ courseId: z.string(), name: z.string() }),
|
|
120
|
+
* })
|
|
121
|
+
* // queryGateway.query(GetCourse, { courseId: "cs-101" }) → Promise<{ courseId: string, name: string }>
|
|
122
|
+
* ```
|
|
123
|
+
*/
|
|
124
|
+
export declare function query<P extends z.ZodType>(def: {
|
|
125
|
+
name: QualifiedName;
|
|
126
|
+
version?: string;
|
|
127
|
+
payload: P;
|
|
128
|
+
}): QueryDescriptor<P, undefined>;
|
|
129
|
+
export declare function query<P extends z.ZodType, R extends z.ZodType>(def: {
|
|
130
|
+
name: QualifiedName;
|
|
131
|
+
version?: string;
|
|
132
|
+
payload: P;
|
|
133
|
+
result: R;
|
|
134
|
+
}): QueryDescriptor<P, R>;
|
|
135
|
+
//# sourceMappingURL=descriptor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"descriptor.d.ts","sourceRoot":"","sources":["../src/descriptor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAC5B,OAAO,KAAK,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAA;AAG3D;;;GAGG;AACH,MAAM,WAAW,iBAAiB,CAChC,CAAC,SAAS,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,OAAO,EAC/B,CAAC,SAAS,CAAC,CAAC,OAAO,GAAG,SAAS,GAAG,SAAS;IAE3C,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;IACxB,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAA;IAC5B,8CAA8C;IAC9C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAA;IACnB,kFAAkF;IAClF,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;IACnB;;;;;OAKG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAC7B;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,OAAO;IAC9D,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;IACtB,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAA;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAA;IACnB,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,CAAA;CAC/C;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe,CAC9B,CAAC,SAAS,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,OAAO,EAC/B,CAAC,SAAS,CAAC,CAAC,OAAO,GAAG,SAAS,GAAG,SAAS;IAE3C,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;IACtB,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAA;IAC5B,4CAA4C;IAC5C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAA;IACnB,iFAAiF;IACjF,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;CACpB;AAED,8BAA8B;AAC9B,MAAM,MAAM,iBAAiB,GACzB,iBAAiB,GACjB,eAAe,GACf,eAAe,CAAA;AAEnB;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,GAAG,EAAE;IAChD,IAAI,EAAE,aAAa,CAAA;IACnB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,CAAC,CAAA;IACV,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,GAAG,iBAAiB,CAAC,CAAC,EAAE,SAAS,CAAC,CAAA;AAEnC,wBAAgB,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,GAAG,EAAE;IACrE,IAAI,EAAE,aAAa,CAAA;IACnB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,CAAC,CAAA;IACV,MAAM,EAAE,CAAC,CAAA;IACT,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,GAAG,iBAAiB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;AAM3B;;;;;;;;;;;GAWG;AACH,wBAAgB,KAAK,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,GAAG,EAAE;IAC9C,IAAI,EAAE,aAAa,CAAA;IACnB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,CAAC,CAAA;IACV,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC/D,GAAG,eAAe,CAAC,CAAC,CAAC,CAerB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,KAAK,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,GAAG,EAAE;IAC9C,IAAI,EAAE,aAAa,CAAA;IACnB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,CAAC,CAAA;CACX,GAAG,eAAe,CAAC,CAAC,EAAE,SAAS,CAAC,CAAA;AAEjC,wBAAgB,KAAK,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,GAAG,EAAE;IACnE,IAAI,EAAE,aAAa,CAAA;IACnB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,CAAC,CAAA;IACV,MAAM,EAAE,CAAC,CAAA;CACV,GAAG,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { tagsFromRecord } from "@kronos-ts/common";
|
|
2
|
+
export function command(def) {
|
|
3
|
+
return { kind: "command", version: def.version ?? "1.0", ...def };
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Creates an event descriptor.
|
|
7
|
+
*
|
|
8
|
+
* Tags can return `Tag[]` or a `Record<string, string>`:
|
|
9
|
+
* ```typescript
|
|
10
|
+
* event({
|
|
11
|
+
* name: qn("university", "CourseCreated"),
|
|
12
|
+
* payload: z.object({ courseId: z.string(), name: z.string() }),
|
|
13
|
+
* tags: (p) => ({ courseId: p.courseId }),
|
|
14
|
+
* })
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export function event(def) {
|
|
18
|
+
const rawTags = def.tags;
|
|
19
|
+
const tags = rawTags
|
|
20
|
+
? (payload) => {
|
|
21
|
+
const result = rawTags(payload);
|
|
22
|
+
return Array.isArray(result) ? result : tagsFromRecord(result);
|
|
23
|
+
}
|
|
24
|
+
: undefined;
|
|
25
|
+
return {
|
|
26
|
+
kind: "event",
|
|
27
|
+
name: def.name,
|
|
28
|
+
version: def.version ?? "1.0",
|
|
29
|
+
payload: def.payload,
|
|
30
|
+
...(tags ? { tags } : {}),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export function query(def) {
|
|
34
|
+
return { kind: "query", version: def.version ?? "1.0", ...def };
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=descriptor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"descriptor.js","sourceRoot":"","sources":["../src/descriptor.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAmGlD,MAAM,UAAU,OAAO,CAAC,GAAQ;IAC9B,OAAO,EAAE,IAAI,EAAE,SAAkB,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,IAAI,KAAK,EAAE,GAAG,GAAG,EAAE,CAAA;AAC5E,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,KAAK,CAAsB,GAK1C;IACC,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,CAAA;IACxB,MAAM,IAAI,GAAiD,OAAO;QAChE,CAAC,CAAC,CAAC,OAAmB,EAAS,EAAE;YAC7B,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;YAC/B,OAAO,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,cAAc,CAAC,MAAM,CAAC,CAAA;QAChE,CAAC;QACH,CAAC,CAAC,SAAS,CAAA;IACb,OAAO;QACL,IAAI,EAAE,OAAgB;QACtB,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,OAAO,EAAE,GAAG,CAAC,OAAO,IAAI,KAAK;QAC7B,OAAO,EAAE,GAAG,CAAC,OAAO;QACpB,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC1B,CAAA;AACH,CAAC;AAoCD,MAAM,UAAU,KAAK,CAAC,GAAQ;IAC5B,OAAO,EAAE,IAAI,EAAE,OAAgB,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,IAAI,KAAK,EAAE,GAAG,GAAG,EAAE,CAAA;AAC1E,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { z } from "zod";
|
|
2
|
+
import { type ResourceKey } from "@kronos-ts/common";
|
|
3
|
+
import type { QueryBus } from "./query-bus.js";
|
|
4
|
+
import type { QueryDescriptor } from "./descriptor.js";
|
|
5
|
+
/** Emit a subscription-query update from within the current processing context. */
|
|
6
|
+
export interface EmitUpdateFunction {
|
|
7
|
+
<Q extends z.ZodType>(query: QueryDescriptor<Q>, filter: (query: z.infer<Q>) => boolean, update: unknown): void;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Resource key for the query bus component.
|
|
11
|
+
* Written by handling modules + processors at handler-invocation entry (D-44).
|
|
12
|
+
*/
|
|
13
|
+
export declare const QUERY_BUS_KEY: ResourceKey<QueryBus>;
|
|
14
|
+
/**
|
|
15
|
+
* Plan 04-01 (HDL-02 / D-42): module-level emitUpdate.
|
|
16
|
+
*
|
|
17
|
+
* Throws NoActiveUnitOfWork outside a UoW; throws WrongUoWPhase outside
|
|
18
|
+
* INVOCATION phase (D-43 mutator guard). Emits a subscription query update
|
|
19
|
+
* through the active query bus.
|
|
20
|
+
*/
|
|
21
|
+
export declare const emitUpdate: EmitUpdateFunction;
|
|
22
|
+
//# sourceMappingURL=emit-update.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"emit-update.d.ts","sourceRoot":"","sources":["../src/emit-update.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAC5B,OAAO,EAAsC,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAExF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAC9C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAEtD,mFAAmF;AACnF,MAAM,WAAW,kBAAkB;IACjC,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAClB,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC,EACzB,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,OAAO,EACtC,MAAM,EAAE,OAAO,GACd,IAAI,CAAA;CACR;AAED;;;GAGG;AACH,eAAO,MAAM,aAAa,EAAE,WAAW,CAAC,QAAQ,CAA2B,CAAA;AAE3E;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,EAAE,kBAMxB,CAAA"}
|