@arki/event-sourcing 0.1.5 → 0.2.1
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/README.md +91 -40
- package/dist/builders/command.d.ts +8 -1
- package/dist/builders/command.d.ts.map +1 -1
- package/dist/builders/command.js +38 -1
- package/dist/builders/command.js.map +1 -1
- package/dist/builders/event.d.ts +8 -1
- package/dist/builders/event.d.ts.map +1 -1
- package/dist/builders/event.js +38 -1
- package/dist/builders/event.js.map +1 -1
- package/dist/dot-action.d.ts +31 -0
- package/dist/dot-action.d.ts.map +1 -0
- package/dist/dot-action.js +73 -0
- package/dist/dot-action.js.map +1 -0
- package/dist/dot.d.ts +79 -11
- package/dist/dot.d.ts.map +1 -1
- package/dist/dot.js +199 -17
- package/dist/dot.js.map +1 -1
- package/dist/projection.d.ts +53 -0
- package/dist/projection.d.ts.map +1 -0
- package/dist/projection.js +76 -0
- package/dist/projection.js.map +1 -0
- package/package.json +8 -3
- package/src/builders/command.ts +59 -2
- package/src/builders/event.ts +59 -2
- package/src/dot-action.ts +96 -0
- package/src/dot.ts +297 -22
- package/src/projection.ts +135 -0
package/dist/dot.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* DOT adapter for `@arki/event-sourcing`.
|
|
3
3
|
*
|
|
4
4
|
* Wraps `eventSourcingFeatures.initEventSourcing` + `initMessageBus` as a
|
|
5
|
-
* single `
|
|
5
|
+
* single `DotPlugin`. The plugin opens the PostgreSQL event store in
|
|
6
6
|
* `boot`, attaches command handlers to an in-memory message bus, publishes
|
|
7
7
|
* both as `services.eventStore` and `services.messageBus`, and closes the
|
|
8
8
|
* event store pool in `dispose` (reverse declaration order).
|
|
@@ -36,18 +36,50 @@
|
|
|
36
36
|
* Importing this adapter without `@arki/dot` installed will fail at module
|
|
37
37
|
* load — that is intentional: the adapter only makes sense in a DOT app.
|
|
38
38
|
*/
|
|
39
|
-
import {
|
|
39
|
+
import { plugin, DotPluginError } from '@arki/dot/plugin';
|
|
40
|
+
import { debugBuilder } from './debug.js';
|
|
40
41
|
import { EVENT_STORE_URL_VARIANTS, eventSourcingFeatures } from './event-sourcing-features.js';
|
|
41
42
|
/**
|
|
42
|
-
* Stable error codes thrown by the event-sourcing
|
|
43
|
+
* Stable error codes thrown by the event-sourcing plugin. Exported so consumers
|
|
43
44
|
* and coding agents can match against them — never parse the message.
|
|
44
45
|
*
|
|
45
46
|
* @see packages/dot/docs/principles.md — principle 1.3 ("errors are part
|
|
46
47
|
* of the API") and principle 4 ("agent-discoverable everywhere").
|
|
47
48
|
*/
|
|
48
|
-
export const
|
|
49
|
+
export const EVENT_SOURCING_PLUGIN_ERROR_CODES = {
|
|
49
50
|
/** boot was called without a configured event-store URL. */
|
|
50
|
-
dbUrlNotConfigured: '
|
|
51
|
+
dbUrlNotConfigured: 'EVENT_SOURCING_PLUGIN_E001',
|
|
52
|
+
/** two collected handlers claim the same command type. */
|
|
53
|
+
duplicateCommandHandler: 'EVENT_SOURCING_PLUGIN_E002',
|
|
54
|
+
/** a bundle token listed in options.bundles was not provided. */
|
|
55
|
+
bundleMissing: 'EVENT_SOURCING_PLUGIN_E003',
|
|
56
|
+
/** a command was dispatched with no registered handler. */
|
|
57
|
+
commandHandlerMissing: 'EVENT_SOURCING_PLUGIN_E004',
|
|
58
|
+
/** a command was dispatched after the dispatcher closed. */
|
|
59
|
+
dispatcherClosed: 'EVENT_SOURCING_PLUGIN_E005',
|
|
60
|
+
/** reserved for a future declared-action/handler parity diagnostic. */
|
|
61
|
+
declaredActionUnbound: 'EVENT_SOURCING_PLUGIN_E006',
|
|
62
|
+
/** command/event schema could not be converted to JSON Schema. */
|
|
63
|
+
schemaRejected: 'EVENT_SOURCING_PLUGIN_E007',
|
|
64
|
+
};
|
|
65
|
+
const EVENT_SOURCING_PLUGIN_VERSION = '0.2.0';
|
|
66
|
+
function bundle(input = {}) {
|
|
67
|
+
return {
|
|
68
|
+
handlers: input.handlers ?? [],
|
|
69
|
+
readModels: input.readModels ?? [],
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
function handle(command, handler, getStreamName) {
|
|
73
|
+
return {
|
|
74
|
+
commandType: command.type,
|
|
75
|
+
handler,
|
|
76
|
+
getStreamName,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/** Namespace for feature-local ES bundle helpers. */
|
|
80
|
+
export const es = {
|
|
81
|
+
bundle,
|
|
82
|
+
handle,
|
|
51
83
|
};
|
|
52
84
|
/**
|
|
53
85
|
* Resolve the event-store connection URL from explicit options first, then
|
|
@@ -64,50 +96,200 @@ function resolveDbUrl(explicit) {
|
|
|
64
96
|
}
|
|
65
97
|
return undefined;
|
|
66
98
|
}
|
|
99
|
+
function dotPluginError(args) {
|
|
100
|
+
return new DotPluginError({
|
|
101
|
+
code: args.code,
|
|
102
|
+
message: args.message,
|
|
103
|
+
remediation: args.remediation,
|
|
104
|
+
docsUrl: `https://arki.dev/event-sourcing/errors/${args.docsSlug}`,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
function assertUniqueCommandHandlers(commandHandlers) {
|
|
108
|
+
const seen = new Map();
|
|
109
|
+
for (const registration of commandHandlers) {
|
|
110
|
+
const count = seen.get(registration.commandType) ?? 0;
|
|
111
|
+
seen.set(registration.commandType, count + 1);
|
|
112
|
+
}
|
|
113
|
+
const duplicate = [...seen.entries()].find(([, count]) => count > 1)?.[0];
|
|
114
|
+
if (duplicate === undefined)
|
|
115
|
+
return;
|
|
116
|
+
throw dotPluginError({
|
|
117
|
+
code: EVENT_SOURCING_PLUGIN_ERROR_CODES.duplicateCommandHandler,
|
|
118
|
+
message: `[event-sourcing] command handler "${duplicate}" is registered more than once.`,
|
|
119
|
+
remediation: 'Each command type may have exactly one handler across options.bundles and dynamic commandHandlers. Remove one registration or rename the command type.',
|
|
120
|
+
docsSlug: 'event-sourcing-plugin-e002',
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
function controlledMessageBus(inner, commandTypes) {
|
|
124
|
+
let closed = false;
|
|
125
|
+
let inflight = 0;
|
|
126
|
+
const waiters = [];
|
|
127
|
+
const finishOne = () => {
|
|
128
|
+
inflight -= 1;
|
|
129
|
+
if (inflight !== 0)
|
|
130
|
+
return;
|
|
131
|
+
const drained = waiters.splice(0);
|
|
132
|
+
for (const waiter of drained)
|
|
133
|
+
waiter();
|
|
134
|
+
};
|
|
135
|
+
return {
|
|
136
|
+
async send(command) {
|
|
137
|
+
if (closed) {
|
|
138
|
+
throw dotPluginError({
|
|
139
|
+
code: EVENT_SOURCING_PLUGIN_ERROR_CODES.dispatcherClosed,
|
|
140
|
+
message: `[event-sourcing] command "${command.type}" was dispatched after the message bus closed.`,
|
|
141
|
+
remediation: 'Do not send commands after app.stop() begins. Stop ingress before closing event-sourcing consumers.',
|
|
142
|
+
docsSlug: 'event-sourcing-plugin-e005',
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
if (!commandTypes.has(command.type)) {
|
|
146
|
+
throw dotPluginError({
|
|
147
|
+
code: EVENT_SOURCING_PLUGIN_ERROR_CODES.commandHandlerMissing,
|
|
148
|
+
message: `[event-sourcing] no command handler is registered for "${command.type}".`,
|
|
149
|
+
remediation: 'Declare a feature ES bundle with es.handle(...), publish it before eventSourcing(...), and list its token in options.bundles.',
|
|
150
|
+
docsSlug: 'event-sourcing-plugin-e004',
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
inflight += 1;
|
|
154
|
+
try {
|
|
155
|
+
await inner.send(command);
|
|
156
|
+
}
|
|
157
|
+
finally {
|
|
158
|
+
finishOne();
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
publish: event => inner.publish(event),
|
|
162
|
+
schedule: (message, when) => {
|
|
163
|
+
inner.schedule(message, when);
|
|
164
|
+
},
|
|
165
|
+
close() {
|
|
166
|
+
closed = true;
|
|
167
|
+
if (inflight === 0)
|
|
168
|
+
return Promise.resolve();
|
|
169
|
+
return new Promise(resolve => {
|
|
170
|
+
waiters.push(resolve);
|
|
171
|
+
});
|
|
172
|
+
},
|
|
173
|
+
};
|
|
174
|
+
}
|
|
67
175
|
/**
|
|
68
|
-
* Build a DOT
|
|
176
|
+
* Build a DOT plugin that opens the event store, wires command handlers
|
|
69
177
|
* into an in-memory message bus, and publishes both as services. The
|
|
70
178
|
* kernel calls `dispose` in reverse declaration order to release the
|
|
71
179
|
* underlying PG pool.
|
|
72
180
|
*/
|
|
73
|
-
export function eventSourcing(options) {
|
|
74
|
-
const
|
|
181
|
+
export function eventSourcing(options = {}) {
|
|
182
|
+
const bundleTokens = options.bundles ?? [];
|
|
183
|
+
const featureTokens = options.features ?? [];
|
|
184
|
+
const needs = {};
|
|
185
|
+
for (const tok of [...bundleTokens, ...featureTokens]) {
|
|
186
|
+
if (needs[tok.key] !== undefined) {
|
|
187
|
+
throw dotPluginError({
|
|
188
|
+
code: EVENT_SOURCING_PLUGIN_ERROR_CODES.duplicateCommandHandler,
|
|
189
|
+
message: `[event-sourcing] token "${tok.key}" is listed twice across options.bundles/options.features.`,
|
|
190
|
+
remediation: 'List each ES bundle or feature token once.',
|
|
191
|
+
docsSlug: 'event-sourcing-plugin-e002',
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
needs[tok.key] = tok;
|
|
195
|
+
}
|
|
75
196
|
// Captured at boot so dispose can call it without re-reading services
|
|
76
197
|
// (dispose is allowed to run even when services failed to publish).
|
|
77
198
|
let closeStore;
|
|
78
|
-
|
|
199
|
+
let closeBus;
|
|
200
|
+
const inner = plugin({
|
|
79
201
|
name: 'event-sourcing',
|
|
80
|
-
version:
|
|
202
|
+
version: EVENT_SOURCING_PLUGIN_VERSION,
|
|
203
|
+
needs,
|
|
81
204
|
configure(ctx) {
|
|
82
205
|
ctx.registerService('eventStore', 'event-store');
|
|
83
206
|
ctx.registerService('messageBus', 'message-bus');
|
|
207
|
+
ctx.registerProjection({
|
|
208
|
+
format: 'event-catalog',
|
|
209
|
+
binding: 'es',
|
|
210
|
+
module: '@arki/event-sourcing/projection',
|
|
211
|
+
});
|
|
84
212
|
ctx.declareProvides('event-store', 'message-bus');
|
|
85
213
|
},
|
|
86
|
-
boot() {
|
|
87
|
-
// Validate at the
|
|
214
|
+
boot(ctx) {
|
|
215
|
+
// Validate at the plugin boundary so the DOT lifecycle gets a coded
|
|
88
216
|
// error. `eventSourcingFeatures.initEventSourcing` still throws raw
|
|
89
217
|
// `Error` for non-DOT consumers (its public contract is unchanged);
|
|
90
218
|
// the check here makes sure we never reach it without a URL.
|
|
91
219
|
const dbUrl = resolveDbUrl(options.dbUrl);
|
|
92
220
|
if (dbUrl === undefined) {
|
|
93
|
-
throw new
|
|
94
|
-
code:
|
|
221
|
+
throw new DotPluginError({
|
|
222
|
+
code: EVENT_SOURCING_PLUGIN_ERROR_CODES.dbUrlNotConfigured,
|
|
95
223
|
message: '[event-sourcing] Event Store database URL is not configured.',
|
|
96
224
|
remediation: `Pass options.dbUrl to eventSourcing(...) or set one of ${EVENT_STORE_URL_VARIANTS.join(', ')} in the environment before booting the app.`,
|
|
97
|
-
docsUrl: 'https://arki.dev/dot/errors/event-sourcing-
|
|
225
|
+
docsUrl: 'https://arki.dev/dot/errors/event-sourcing-plugin-e001',
|
|
98
226
|
});
|
|
99
227
|
}
|
|
100
|
-
const
|
|
228
|
+
const record = ctx;
|
|
229
|
+
const bundles = [];
|
|
230
|
+
for (const tok of bundleTokens) {
|
|
231
|
+
const collected = record[tok.key];
|
|
232
|
+
if (collected === undefined) {
|
|
233
|
+
throw dotPluginError({
|
|
234
|
+
code: EVENT_SOURCING_PLUGIN_ERROR_CODES.bundleMissing,
|
|
235
|
+
message: `[event-sourcing] bundle "${tok.key}" was not provided by any earlier plugin.`,
|
|
236
|
+
remediation: 'Mount the plugin that publishes this ES bundle before eventSourcing(...). The typed builder enforces this; erased composition reaches this check.',
|
|
237
|
+
docsSlug: 'event-sourcing-plugin-e003',
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
bundles.push(collected);
|
|
241
|
+
}
|
|
242
|
+
const featureRecord = ctx;
|
|
243
|
+
for (const tok of featureTokens) {
|
|
244
|
+
const slice = featureRecord[tok.key];
|
|
245
|
+
if (slice === undefined) {
|
|
246
|
+
throw dotPluginError({
|
|
247
|
+
code: EVENT_SOURCING_PLUGIN_ERROR_CODES.bundleMissing,
|
|
248
|
+
message: `[event-sourcing] feature "${tok.key}" was not provided by any earlier plugin.`,
|
|
249
|
+
remediation: 'Mount the feature plugin that provides this token before eventSourcing(...). The typed builder enforces this; erased composition reaches this check.',
|
|
250
|
+
docsSlug: 'event-sourcing-plugin-e003',
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
if (slice.es === undefined) {
|
|
254
|
+
debugBuilder('[event-sourcing] feature "%s" did not contribute an es slice.', tok.key);
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
bundles.push(slice.es);
|
|
258
|
+
}
|
|
259
|
+
const readModels = [
|
|
260
|
+
...(options.projections ?? []),
|
|
261
|
+
...bundles.flatMap(collected => collected.readModels),
|
|
262
|
+
];
|
|
263
|
+
const commandHandlers = [
|
|
264
|
+
...bundles.flatMap(collected => collected.handlers),
|
|
265
|
+
...(options.commandHandlers ?? []),
|
|
266
|
+
];
|
|
267
|
+
assertUniqueCommandHandlers(commandHandlers);
|
|
268
|
+
const { eventStore, close } = eventSourcingFeatures.initEventSourcing(readModels, dbUrl);
|
|
101
269
|
closeStore = close;
|
|
102
|
-
const messageBus = eventSourcingFeatures.initMessageBus(eventStore, [...commandHandlers]);
|
|
270
|
+
const messageBus = controlledMessageBus(eventSourcingFeatures.initMessageBus(eventStore, [...commandHandlers]), new Set(commandHandlers.map(registration => registration.commandType)));
|
|
271
|
+
closeBus = () => messageBus.close();
|
|
103
272
|
return { eventStore, messageBus };
|
|
104
273
|
},
|
|
274
|
+
start() {
|
|
275
|
+
// Marks the plugin active so DOT will run `stop`, where the dispatcher
|
|
276
|
+
// closes before dispose releases the store pool.
|
|
277
|
+
},
|
|
278
|
+
async stop() {
|
|
279
|
+
await closeBus?.();
|
|
280
|
+
},
|
|
105
281
|
async dispose() {
|
|
282
|
+
await closeBus?.();
|
|
283
|
+
closeBus = undefined;
|
|
106
284
|
if (closeStore !== undefined) {
|
|
107
285
|
await closeStore();
|
|
108
286
|
closeStore = undefined;
|
|
109
287
|
}
|
|
110
288
|
},
|
|
111
289
|
});
|
|
290
|
+
// Erasure seam: `needs` is assembled from runtime tokens, while the
|
|
291
|
+
// return type re-attaches the token-derived wire shape for the app
|
|
292
|
+
// builder guard. Same pattern as @arki/http's bundle collector.
|
|
293
|
+
return inner;
|
|
112
294
|
}
|
|
113
295
|
//# sourceMappingURL=dot.js.map
|
package/dist/dot.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dot.js","sourceRoot":"","sources":["../src/dot.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAKH,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"dot.js","sourceRoot":"","sources":["../src/dot.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DOT projection entry point for `dot explain --as event-catalog`.
|
|
3
|
+
*
|
|
4
|
+
* Pure manifest in, JSON document out. It intentionally renders only
|
|
5
|
+
* configure-time ES actions: command handlers collected dynamically through
|
|
6
|
+
* deprecated 0.1.x options are executable but manifest-invisible.
|
|
7
|
+
*/
|
|
8
|
+
import type { JsonObject } from './dot-action.js';
|
|
9
|
+
type DotActionLike = {
|
|
10
|
+
readonly id: string;
|
|
11
|
+
readonly plugin: string;
|
|
12
|
+
readonly binding: string;
|
|
13
|
+
readonly direction: 'in' | 'out';
|
|
14
|
+
readonly address?: string;
|
|
15
|
+
readonly summary?: string;
|
|
16
|
+
readonly meta?: JsonObject;
|
|
17
|
+
readonly metaSchema?: string;
|
|
18
|
+
};
|
|
19
|
+
type DotManifestLike = {
|
|
20
|
+
readonly app: {
|
|
21
|
+
readonly name: string;
|
|
22
|
+
readonly version?: string;
|
|
23
|
+
};
|
|
24
|
+
readonly actions: readonly DotActionLike[];
|
|
25
|
+
};
|
|
26
|
+
type CatalogCommand = JsonObject & {
|
|
27
|
+
readonly type: string;
|
|
28
|
+
readonly address: string;
|
|
29
|
+
readonly summary?: string;
|
|
30
|
+
readonly input: JsonObject;
|
|
31
|
+
};
|
|
32
|
+
type CatalogEvent = JsonObject & {
|
|
33
|
+
readonly type: string;
|
|
34
|
+
readonly address: string;
|
|
35
|
+
readonly summary?: string;
|
|
36
|
+
readonly data: JsonObject;
|
|
37
|
+
};
|
|
38
|
+
type CatalogPlugin = JsonObject & {
|
|
39
|
+
readonly name: string;
|
|
40
|
+
readonly commands: CatalogCommand[];
|
|
41
|
+
readonly events: CatalogEvent[];
|
|
42
|
+
};
|
|
43
|
+
type EventCatalog = JsonObject & {
|
|
44
|
+
readonly format: 'event-catalog';
|
|
45
|
+
readonly app: {
|
|
46
|
+
readonly name: string;
|
|
47
|
+
readonly version?: string;
|
|
48
|
+
};
|
|
49
|
+
readonly plugins: CatalogPlugin[];
|
|
50
|
+
};
|
|
51
|
+
export declare function project(manifest: DotManifestLike): EventCatalog;
|
|
52
|
+
export {};
|
|
53
|
+
//# sourceMappingURL=projection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projection.d.ts","sourceRoot":"","sources":["../src/projection.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAOlD,KAAK,aAAa,GAAG;IACnB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,IAAI,GAAG,KAAK,CAAC;IACjC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,CAAC,EAAE,UAAU,CAAC;IAC3B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B,CAAC;AAEF,KAAK,eAAe,GAAG;IACrB,QAAQ,CAAC,GAAG,EAAE;QACZ,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;KAC3B,CAAC;IACF,QAAQ,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE,CAAC;CAC5C,CAAC;AAEF,KAAK,cAAc,GAAG,UAAU,GAAG;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;CAC5B,CAAC;AAEF,KAAK,YAAY,GAAG,UAAU,GAAG;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;CAC3B,CAAC;AAEF,KAAK,aAAa,GAAG,UAAU,GAAG;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,cAAc,EAAE,CAAC;IACpC,QAAQ,CAAC,MAAM,EAAE,YAAY,EAAE,CAAC;CACjC,CAAC;AAEF,KAAK,YAAY,GAAG,UAAU,GAAG;IAC/B,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC;IACjC,QAAQ,CAAC,GAAG,EAAE;QACZ,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;KAC3B,CAAC;IACF,QAAQ,CAAC,OAAO,EAAE,aAAa,EAAE,CAAC;CACnC,CAAC;AAoDF,wBAAgB,OAAO,CAAC,QAAQ,EAAE,eAAe,GAAG,YAAY,CAqB/D"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DOT projection entry point for `dot explain --as event-catalog`.
|
|
3
|
+
*
|
|
4
|
+
* Pure manifest in, JSON document out. It intentionally renders only
|
|
5
|
+
* configure-time ES actions: command handlers collected dynamically through
|
|
6
|
+
* deprecated 0.1.x options are executable but manifest-invisible.
|
|
7
|
+
*/
|
|
8
|
+
import { EVENT_SOURCING_ACTION_META_SCHEMA, EventSourcingActionError, isJsonObject, } from './dot-action.js';
|
|
9
|
+
function projectionError(message) {
|
|
10
|
+
return new EventSourcingActionError(message, new TypeError(message));
|
|
11
|
+
}
|
|
12
|
+
function getGroup(groups, plugin) {
|
|
13
|
+
const existing = groups.find(group => group.name === plugin);
|
|
14
|
+
if (existing !== undefined)
|
|
15
|
+
return existing;
|
|
16
|
+
const created = { name: plugin, commands: [], events: [] };
|
|
17
|
+
groups.push(created);
|
|
18
|
+
return created;
|
|
19
|
+
}
|
|
20
|
+
function assertMeta(action) {
|
|
21
|
+
if (action.metaSchema !== EVENT_SOURCING_ACTION_META_SCHEMA) {
|
|
22
|
+
throw projectionError(`[event-sourcing] action "${action.id}" uses unsupported meta schema "${action.metaSchema ?? 'missing'}".`);
|
|
23
|
+
}
|
|
24
|
+
if (action.meta === undefined) {
|
|
25
|
+
throw projectionError(`[event-sourcing] action "${action.id}" has no event-sourcing metadata.`);
|
|
26
|
+
}
|
|
27
|
+
return action.meta;
|
|
28
|
+
}
|
|
29
|
+
function commandFromAction(action, meta) {
|
|
30
|
+
const input = meta['input'];
|
|
31
|
+
if (meta['kind'] !== 'command' || !isJsonObject(input)) {
|
|
32
|
+
throw projectionError(`[event-sourcing] action "${action.id}" has invalid command metadata.`);
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
type: action.id,
|
|
36
|
+
address: action.address ?? action.id,
|
|
37
|
+
...(action.summary === undefined ? {} : { summary: action.summary }),
|
|
38
|
+
input,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function eventFromAction(action, meta) {
|
|
42
|
+
const data = meta['data'];
|
|
43
|
+
if (meta['kind'] !== 'event' || !isJsonObject(data)) {
|
|
44
|
+
throw projectionError(`[event-sourcing] action "${action.id}" has invalid event metadata.`);
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
type: action.id,
|
|
48
|
+
address: action.address ?? action.id,
|
|
49
|
+
...(action.summary === undefined ? {} : { summary: action.summary }),
|
|
50
|
+
data,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
export function project(manifest) {
|
|
54
|
+
const plugins = [];
|
|
55
|
+
for (const action of manifest.actions) {
|
|
56
|
+
if (action.binding !== 'es')
|
|
57
|
+
continue;
|
|
58
|
+
const meta = assertMeta(action);
|
|
59
|
+
const group = getGroup(plugins, action.plugin);
|
|
60
|
+
if (action.direction === 'in') {
|
|
61
|
+
group.commands.push(commandFromAction(action, meta));
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
group.events.push(eventFromAction(action, meta));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
format: 'event-catalog',
|
|
69
|
+
app: {
|
|
70
|
+
name: manifest.app.name,
|
|
71
|
+
...(manifest.app.version === undefined ? {} : { version: manifest.app.version }),
|
|
72
|
+
},
|
|
73
|
+
plugins,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=projection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projection.js","sourceRoot":"","sources":["../src/projection.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arki/event-sourcing",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Event sourcing primitives — event store, message bus, projections, process managers — built on Emmett for ARKI.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -60,6 +60,11 @@
|
|
|
60
60
|
"import": "./dist/dot.js",
|
|
61
61
|
"default": "./dist/dot.js"
|
|
62
62
|
},
|
|
63
|
+
"./projection": {
|
|
64
|
+
"types": "./dist/projection.d.ts",
|
|
65
|
+
"import": "./dist/projection.js",
|
|
66
|
+
"default": "./dist/projection.js"
|
|
67
|
+
},
|
|
63
68
|
"./event": {
|
|
64
69
|
"types": "./dist/event.d.ts",
|
|
65
70
|
"import": "./dist/event.js",
|
|
@@ -85,7 +90,7 @@
|
|
|
85
90
|
"package.json"
|
|
86
91
|
],
|
|
87
92
|
"dependencies": {
|
|
88
|
-
"@arki/contracts": "0.0.
|
|
93
|
+
"@arki/contracts": "0.0.3",
|
|
89
94
|
"@arki/log": "0.0.2",
|
|
90
95
|
"@event-driven-io/emmett": "^0.41.0",
|
|
91
96
|
"@event-driven-io/emmett-postgresql": "^0.41.0",
|
|
@@ -93,7 +98,7 @@
|
|
|
93
98
|
"drizzle-orm": "1.0.0-rc.1"
|
|
94
99
|
},
|
|
95
100
|
"peerDependencies": {
|
|
96
|
-
"@arki/dot": "^0.
|
|
101
|
+
"@arki/dot": "^0.4.0"
|
|
97
102
|
},
|
|
98
103
|
"peerDependenciesMeta": {
|
|
99
104
|
"@arki/dot": {
|
package/src/builders/command.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { z } from '@arki/contracts';
|
|
2
2
|
|
|
3
3
|
import type { Command, DefaultCommandMetadata } from '../command.js';
|
|
4
|
+
import type { EventSourcingActionDeclaration, JsonObject } from '../dot-action.js';
|
|
5
|
+
import { EVENT_SOURCING_ACTION_META_SCHEMA, schemaToJsonObject } from '../dot-action.js';
|
|
4
6
|
import type { DefaultRecord } from '../types.js';
|
|
5
7
|
|
|
6
8
|
/**
|
|
@@ -19,6 +21,35 @@ export type CommandConfig<
|
|
|
19
21
|
metadataSchema?: z.ZodType<TMetadata>;
|
|
20
22
|
};
|
|
21
23
|
|
|
24
|
+
export type CommandFactory<
|
|
25
|
+
TType extends string,
|
|
26
|
+
TInput extends DefaultRecord,
|
|
27
|
+
TMetadata extends DefaultCommandMetadata | undefined = undefined,
|
|
28
|
+
> = ((input: TInput, metadata?: TMetadata) => Command<TType, TInput, TMetadata>) &
|
|
29
|
+
EventSourcingActionDeclaration & {
|
|
30
|
+
readonly type: TType;
|
|
31
|
+
readonly inputSchema: z.ZodType<TInput>;
|
|
32
|
+
readonly metadataSchema?: z.ZodType<TMetadata>;
|
|
33
|
+
toDotAction(): EventSourcingActionDeclaration;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
function commandAction<TType extends string>(
|
|
37
|
+
type: TType,
|
|
38
|
+
inputSchema: z.ZodType,
|
|
39
|
+
): EventSourcingActionDeclaration {
|
|
40
|
+
return {
|
|
41
|
+
id: type,
|
|
42
|
+
binding: 'es',
|
|
43
|
+
direction: 'in',
|
|
44
|
+
address: type,
|
|
45
|
+
metaSchema: EVENT_SOURCING_ACTION_META_SCHEMA,
|
|
46
|
+
meta: {
|
|
47
|
+
kind: 'command',
|
|
48
|
+
input: schemaToJsonObject(inputSchema, `command "${type}" input`),
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
22
53
|
/**
|
|
23
54
|
* Creates a strongly-typed command factory function.
|
|
24
55
|
*
|
|
@@ -48,10 +79,11 @@ export function defineCommand<
|
|
|
48
79
|
TMetadata extends DefaultCommandMetadata | undefined = undefined,
|
|
49
80
|
>(
|
|
50
81
|
config: CommandConfig<TType, TInput, TMetadata>,
|
|
51
|
-
):
|
|
82
|
+
): CommandFactory<TType, TInput, TMetadata> {
|
|
52
83
|
const { type, inputSchema, metadataSchema } = config;
|
|
84
|
+
let cachedAction: EventSourcingActionDeclaration | undefined;
|
|
53
85
|
|
|
54
|
-
|
|
86
|
+
const factory = (input: TInput, metadata?: TMetadata) => {
|
|
55
87
|
const validatedInput = inputSchema.parse(input);
|
|
56
88
|
const validatedMetadata = metadataSchema ? metadataSchema.parse(metadata) : metadata;
|
|
57
89
|
|
|
@@ -69,4 +101,29 @@ export function defineCommand<
|
|
|
69
101
|
kind: 'Command' as const,
|
|
70
102
|
}) as unknown as Command<TType, TInput, TMetadata>;
|
|
71
103
|
};
|
|
104
|
+
|
|
105
|
+
const getAction = (): EventSourcingActionDeclaration => {
|
|
106
|
+
cachedAction ??= commandAction(type, inputSchema);
|
|
107
|
+
return cachedAction;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
Object.defineProperties(factory, {
|
|
111
|
+
type: { value: type, enumerable: true },
|
|
112
|
+
inputSchema: { value: inputSchema },
|
|
113
|
+
...(metadataSchema === undefined ? {} : { metadataSchema: { value: metadataSchema } }),
|
|
114
|
+
id: { value: type, enumerable: true },
|
|
115
|
+
binding: { value: 'es', enumerable: true },
|
|
116
|
+
direction: { value: 'in', enumerable: true },
|
|
117
|
+
address: { value: type, enumerable: true },
|
|
118
|
+
metaSchema: { value: EVENT_SOURCING_ACTION_META_SCHEMA, enumerable: true },
|
|
119
|
+
meta: {
|
|
120
|
+
enumerable: true,
|
|
121
|
+
get(): JsonObject {
|
|
122
|
+
return getAction().meta;
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
toDotAction: { value: getAction },
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
return factory as CommandFactory<TType, TInput, TMetadata>;
|
|
72
129
|
}
|
package/src/builders/event.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { z } from '@arki/contracts';
|
|
2
2
|
|
|
3
3
|
import type { Event } from '../event.js';
|
|
4
|
+
import type { EventSourcingActionDeclaration, JsonObject } from '../dot-action.js';
|
|
5
|
+
import { EVENT_SOURCING_ACTION_META_SCHEMA, schemaToJsonObject } from '../dot-action.js';
|
|
4
6
|
import type { DefaultRecord } from '../types.js';
|
|
5
7
|
|
|
6
8
|
/**
|
|
@@ -19,6 +21,35 @@ export type EventConfig<
|
|
|
19
21
|
metadataSchema?: z.ZodType<TMetadata>;
|
|
20
22
|
};
|
|
21
23
|
|
|
24
|
+
export type EventFactory<
|
|
25
|
+
TType extends string,
|
|
26
|
+
TData extends DefaultRecord,
|
|
27
|
+
TMetadata extends DefaultRecord | undefined = undefined,
|
|
28
|
+
> = ((data: TData, metadata?: TMetadata) => Event<TType, TData, TMetadata>) &
|
|
29
|
+
EventSourcingActionDeclaration & {
|
|
30
|
+
readonly type: TType;
|
|
31
|
+
readonly dataSchema: z.ZodType<TData>;
|
|
32
|
+
readonly metadataSchema?: z.ZodType<TMetadata>;
|
|
33
|
+
toDotAction(): EventSourcingActionDeclaration;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
function eventAction<TType extends string>(
|
|
37
|
+
type: TType,
|
|
38
|
+
dataSchema: z.ZodType,
|
|
39
|
+
): EventSourcingActionDeclaration {
|
|
40
|
+
return {
|
|
41
|
+
id: type,
|
|
42
|
+
binding: 'es',
|
|
43
|
+
direction: 'out',
|
|
44
|
+
address: type,
|
|
45
|
+
metaSchema: EVENT_SOURCING_ACTION_META_SCHEMA,
|
|
46
|
+
meta: {
|
|
47
|
+
kind: 'event',
|
|
48
|
+
data: schemaToJsonObject(dataSchema, `event "${type}" data`),
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
22
53
|
/**
|
|
23
54
|
* Creates a strongly-typed event factory function.
|
|
24
55
|
*
|
|
@@ -46,10 +77,11 @@ export function defineEvent<
|
|
|
46
77
|
TType extends string,
|
|
47
78
|
TData extends DefaultRecord,
|
|
48
79
|
TMetadata extends DefaultRecord | undefined = undefined,
|
|
49
|
-
>(config: EventConfig<TType, TData, TMetadata>):
|
|
80
|
+
>(config: EventConfig<TType, TData, TMetadata>): EventFactory<TType, TData, TMetadata> {
|
|
50
81
|
const { type, dataSchema, metadataSchema } = config;
|
|
82
|
+
let cachedAction: EventSourcingActionDeclaration | undefined;
|
|
51
83
|
|
|
52
|
-
|
|
84
|
+
const factory = (data: TData, metadata?: TMetadata) => {
|
|
53
85
|
const validatedData = dataSchema.parse(data);
|
|
54
86
|
const validatedMetadata = metadataSchema ? metadataSchema.parse(metadata) : metadata;
|
|
55
87
|
|
|
@@ -65,4 +97,29 @@ export function defineEvent<
|
|
|
65
97
|
metadata: validatedMetadata,
|
|
66
98
|
}) as unknown as Event<TType, TData, TMetadata>;
|
|
67
99
|
};
|
|
100
|
+
|
|
101
|
+
const getAction = (): EventSourcingActionDeclaration => {
|
|
102
|
+
cachedAction ??= eventAction(type, dataSchema);
|
|
103
|
+
return cachedAction;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
Object.defineProperties(factory, {
|
|
107
|
+
type: { value: type, enumerable: true },
|
|
108
|
+
dataSchema: { value: dataSchema },
|
|
109
|
+
...(metadataSchema === undefined ? {} : { metadataSchema: { value: metadataSchema } }),
|
|
110
|
+
id: { value: type, enumerable: true },
|
|
111
|
+
binding: { value: 'es', enumerable: true },
|
|
112
|
+
direction: { value: 'out', enumerable: true },
|
|
113
|
+
address: { value: type, enumerable: true },
|
|
114
|
+
metaSchema: { value: EVENT_SOURCING_ACTION_META_SCHEMA, enumerable: true },
|
|
115
|
+
meta: {
|
|
116
|
+
enumerable: true,
|
|
117
|
+
get(): JsonObject {
|
|
118
|
+
return getAction().meta;
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
toDotAction: { value: getAction },
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
return factory as EventFactory<TType, TData, TMetadata>;
|
|
68
125
|
}
|