@ocoda/event-sourcing-mariadb 1.0.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/CHANGELOG.md ADDED
@@ -0,0 +1,18 @@
1
+ # @ocoda/event-sourcing-mariadb
2
+
3
+ ## 1.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - This marks the first stable release of the library, which consists of the following changes:
8
+
9
+ - all database-specific libraries have been migrated to their own libraries to reduce the bundle size
10
+ - the `SnapshotHandler` was renamed to `SnapshotRepository`
11
+ - the `SnapshotRepository` was provided with additional methods for retrieving snapshots in bulk
12
+ - the database drivers were optimized (e.g. by only fetching the needed fields)
13
+ - the DynamoDB snapshot-store serialization was fixed
14
+
15
+ ### Patch Changes
16
+
17
+ - Updated dependencies
18
+ - @ocoda/event-sourcing@1.0.0
package/LICENSE.md ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 Ocoda
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,67 @@
1
+ import { IEventPayload, IEvent, EventStore, IEventPool, IEventCollection, EventStream, IEventFilter, EventEnvelope, EventStoreConfig, AggregateRoot, ISnapshot, SnapshotStore, ISnapshotPool, ISnapshotCollection, SnapshotStream, ISnapshotFilter, SnapshotEnvelope, ILatestSnapshotFilter, SnapshotStoreConfig } from '@ocoda/event-sourcing';
2
+ import { Type } from '@nestjs/common';
3
+ import { PoolConfig } from 'mariadb';
4
+
5
+ type MariaDBEventEntity = {
6
+ stream_id: string;
7
+ version: number;
8
+ event: string;
9
+ payload: IEventPayload<IEvent>;
10
+ event_id: string;
11
+ aggregate_id: string;
12
+ occurred_on: Date;
13
+ correlation_id: string | null;
14
+ causation_id: string | null;
15
+ };
16
+
17
+ declare class MariaDBEventStore extends EventStore<MariaDBEventStoreConfig> {
18
+ private pool;
19
+ connect(): Promise<void>;
20
+ disconnect(): Promise<void>;
21
+ ensureCollection(pool?: IEventPool): Promise<IEventCollection>;
22
+ getEvents({ streamId }: EventStream, filter?: IEventFilter): AsyncGenerator<IEvent[]>;
23
+ getEvent({ streamId }: EventStream, version: number, pool?: IEventPool): Promise<IEvent>;
24
+ appendEvents(stream: EventStream, aggregateVersion: number, events: IEvent[], pool?: IEventPool): Promise<EventEnvelope[]>;
25
+ getEnvelopes({ streamId }: EventStream, filter?: IEventFilter): AsyncGenerator<EventEnvelope[]>;
26
+ getEnvelope({ streamId }: EventStream, version: number, pool?: IEventPool): Promise<EventEnvelope>;
27
+ }
28
+
29
+ interface MariaDBEventStoreConfig extends EventStoreConfig, PoolConfig {
30
+ driver: Type<MariaDBEventStore>;
31
+ }
32
+
33
+ type MariaDBSnapshotEntity<A extends AggregateRoot> = {
34
+ stream_id: string;
35
+ version: number;
36
+ payload: ISnapshot<A>;
37
+ snapshot_id: string;
38
+ aggregate_id: string;
39
+ registered_on: Date;
40
+ aggregate_name: string;
41
+ latest?: string;
42
+ };
43
+
44
+ declare class MariaDBSnapshotStore extends SnapshotStore<MariaDBSnapshotStoreConfig> {
45
+ private pool;
46
+ connect(): Promise<void>;
47
+ disconnect(): Promise<void>;
48
+ ensureCollection(pool?: ISnapshotPool): Promise<ISnapshotCollection>;
49
+ stop(): Promise<void>;
50
+ getSnapshots<A extends AggregateRoot>({ streamId }: SnapshotStream, filter?: ISnapshotFilter): AsyncGenerator<ISnapshot<A>[]>;
51
+ getSnapshot<A extends AggregateRoot>({ streamId }: SnapshotStream, version: number, pool?: ISnapshotPool): Promise<ISnapshot<A>>;
52
+ appendSnapshot<A extends AggregateRoot>(stream: SnapshotStream, aggregateVersion: number, snapshot: ISnapshot<A>, pool?: ISnapshotPool): Promise<SnapshotEnvelope<A>>;
53
+ getLastSnapshot<A extends AggregateRoot>(stream: SnapshotStream, pool?: ISnapshotPool): Promise<ISnapshot<A>>;
54
+ getManyLastSnapshots<A extends AggregateRoot>(streams: SnapshotStream[], pool?: ISnapshotPool): Promise<Map<SnapshotStream, ISnapshot<A>>>;
55
+ getLastEnvelope<A extends AggregateRoot>(stream: SnapshotStream, pool?: ISnapshotPool): Promise<SnapshotEnvelope<A>>;
56
+ getEnvelopes<A extends AggregateRoot>({ streamId }: SnapshotStream, filter?: ISnapshotFilter): AsyncGenerator<SnapshotEnvelope<A>[]>;
57
+ getEnvelope<A extends AggregateRoot>({ streamId }: SnapshotStream, version: number, pool?: ISnapshotPool): Promise<SnapshotEnvelope<A>>;
58
+ getLastAggregateEnvelopes<A extends AggregateRoot>(aggregateName: string, filter?: ILatestSnapshotFilter): AsyncGenerator<SnapshotEnvelope<A>[]>;
59
+ getManyLastSnapshotEnvelopes<A extends AggregateRoot>(streams: SnapshotStream[], pool?: ISnapshotPool): Promise<Map<SnapshotStream, SnapshotEnvelope<A>>>;
60
+ private getLastStreamEntities;
61
+ }
62
+
63
+ interface MariaDBSnapshotStoreConfig extends SnapshotStoreConfig, PoolConfig {
64
+ driver: Type<MariaDBSnapshotStore>;
65
+ }
66
+
67
+ export { type MariaDBEventEntity, MariaDBEventStore, type MariaDBEventStoreConfig, type MariaDBSnapshotEntity, MariaDBSnapshotStore, type MariaDBSnapshotStoreConfig };
package/dist/index.js ADDED
@@ -0,0 +1,716 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : Symbol.for("Symbol." + name);
6
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
7
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
21
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
22
+ var __async = (__this, __arguments, generator) => {
23
+ return new Promise((resolve, reject) => {
24
+ var fulfilled = (value) => {
25
+ try {
26
+ step(generator.next(value));
27
+ } catch (e) {
28
+ reject(e);
29
+ }
30
+ };
31
+ var rejected = (value) => {
32
+ try {
33
+ step(generator.throw(value));
34
+ } catch (e) {
35
+ reject(e);
36
+ }
37
+ };
38
+ var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
39
+ step((generator = generator.apply(__this, __arguments)).next());
40
+ });
41
+ };
42
+ var __await = function(promise, isYieldStar) {
43
+ this[0] = promise;
44
+ this[1] = isYieldStar;
45
+ };
46
+ var __asyncGenerator = (__this, __arguments, generator) => {
47
+ var resume = (k, v, yes, no) => {
48
+ try {
49
+ var x = generator[k](v), isAwait = (v = x.value) instanceof __await, done = x.done;
50
+ Promise.resolve(isAwait ? v[0] : v).then((y) => isAwait ? resume(k === "return" ? k : "next", v[1] ? { done: y.done, value: y.value } : y, yes, no) : yes({ value: y, done })).catch((e) => resume("throw", e, yes, no));
51
+ } catch (e) {
52
+ no(e);
53
+ }
54
+ }, method = (k) => it[k] = (x) => new Promise((yes, no) => resume(k, x, yes, no)), it = {};
55
+ return generator = generator.apply(__this, __arguments), it[__knownSymbol("asyncIterator")] = () => it, method("next"), method("throw"), method("return"), it;
56
+ };
57
+ var __forAwait = (obj, it, method) => (it = obj[__knownSymbol("asyncIterator")]) ? it.call(obj) : (obj = obj[__knownSymbol("iterator")](), it = {}, method = (key, fn) => (fn = obj[key]) && (it[key] = (arg) => new Promise((yes, no, done) => (arg = fn.call(obj, arg), done = arg.done, Promise.resolve(arg.value).then((value) => yes({ value, done }), no)))), method("next"), method("return"), it);
58
+
59
+ // lib/index.ts
60
+ var lib_exports = {};
61
+ __export(lib_exports, {
62
+ MariaDBEventStore: () => MariaDBEventStore,
63
+ MariaDBSnapshotStore: () => MariaDBSnapshotStore
64
+ });
65
+ module.exports = __toCommonJS(lib_exports);
66
+
67
+ // lib/mariadb.event-store.ts
68
+ var import_event_sourcing = require("@ocoda/event-sourcing");
69
+ var import_mariadb = require("mariadb");
70
+ var _MariaDBEventStore = class _MariaDBEventStore extends import_event_sourcing.EventStore {
71
+ constructor() {
72
+ super(...arguments);
73
+ __publicField(this, "pool");
74
+ }
75
+ connect() {
76
+ return __async(this, null, function* () {
77
+ this.logger.log("Starting store");
78
+ this.pool = (0, import_mariadb.createPool)(this.options);
79
+ });
80
+ }
81
+ disconnect() {
82
+ return __async(this, null, function* () {
83
+ this.logger.log("Stopping store");
84
+ yield this.pool.end();
85
+ });
86
+ }
87
+ ensureCollection(pool) {
88
+ return __async(this, null, function* () {
89
+ const collection = import_event_sourcing.EventCollection.get(pool);
90
+ try {
91
+ yield this.pool.query(`CREATE TABLE IF NOT EXISTS \`${collection}\` (
92
+ stream_id VARCHAR(120) NOT NULL,
93
+ version INT NOT NULL,
94
+ event VARCHAR(80) NOT NULL,
95
+ payload JSON NOT NULL,
96
+ event_id VARCHAR(40) NOT NULL,
97
+ aggregate_id VARCHAR(40) NOT NULL,
98
+ occurred_on TIMESTAMP NOT NULL,
99
+ correlation_id VARCHAR(255),
100
+ causation_id VARCHAR(255),
101
+ PRIMARY KEY (stream_id, version)
102
+ )`);
103
+ return collection;
104
+ } catch (error) {
105
+ throw new import_event_sourcing.EventStoreCollectionCreationException(collection, error);
106
+ }
107
+ });
108
+ }
109
+ getEvents(_0, _1) {
110
+ return __asyncGenerator(this, arguments, function* ({ streamId }, filter) {
111
+ const connection = this.pool.getConnection();
112
+ const collection = import_event_sourcing.EventCollection.get(filter == null ? void 0 : filter.pool);
113
+ const fromVersion = filter == null ? void 0 : filter.fromVersion;
114
+ const direction = (filter == null ? void 0 : filter.direction) || import_event_sourcing.StreamReadingDirection.FORWARD;
115
+ const limit = (filter == null ? void 0 : filter.limit) || Number.MAX_SAFE_INTEGER;
116
+ const batch = (filter == null ? void 0 : filter.batch) || import_event_sourcing.DEFAULT_BATCH_SIZE;
117
+ const query = `
118
+ SELECT event, payload
119
+ FROM \`${collection}\`
120
+ WHERE stream_id = ?
121
+ ${fromVersion ? "AND version >= ?" : ""}
122
+ ORDER BY version ${direction === import_event_sourcing.StreamReadingDirection.FORWARD ? "ASC" : "DESC"}
123
+ LIMIT ?
124
+ `;
125
+ const params = fromVersion ? [
126
+ streamId,
127
+ fromVersion,
128
+ limit
129
+ ] : [
130
+ streamId,
131
+ limit
132
+ ];
133
+ const client = yield new __await(connection);
134
+ const stream = client.queryStream(query, params);
135
+ try {
136
+ let batchedEvents = [];
137
+ try {
138
+ for (var iter = __forAwait(stream), more, temp, error; more = !(temp = yield new __await(iter.next())).done; more = false) {
139
+ const { event, payload } = temp.value;
140
+ batchedEvents.push(this.eventMap.deserializeEvent(event, payload));
141
+ if (batchedEvents.length === batch) {
142
+ yield batchedEvents;
143
+ batchedEvents = [];
144
+ }
145
+ }
146
+ } catch (temp) {
147
+ error = [temp];
148
+ } finally {
149
+ try {
150
+ more && (temp = iter.return) && (yield new __await(temp.call(iter)));
151
+ } finally {
152
+ if (error)
153
+ throw error[0];
154
+ }
155
+ }
156
+ if (batchedEvents.length > 0) {
157
+ yield batchedEvents;
158
+ }
159
+ } catch (e) {
160
+ stream.destroy();
161
+ } finally {
162
+ yield new __await(client.release());
163
+ }
164
+ });
165
+ }
166
+ getEvent(_0, _1, _2) {
167
+ return __async(this, arguments, function* ({ streamId }, version, pool) {
168
+ const collection = import_event_sourcing.EventCollection.get(pool);
169
+ const [entity] = yield this.pool.query(`SELECT event, payload FROM \`${collection}\` WHERE stream_id = ? AND version = ?`, [
170
+ streamId,
171
+ version
172
+ ]);
173
+ if (!entity) {
174
+ throw new import_event_sourcing.EventNotFoundException(streamId, version);
175
+ }
176
+ return this.eventMap.deserializeEvent(entity.event, entity.payload);
177
+ });
178
+ }
179
+ appendEvents(stream, aggregateVersion, events, pool) {
180
+ return __async(this, null, function* () {
181
+ const connection = yield this.pool.getConnection();
182
+ const collection = import_event_sourcing.EventCollection.get(pool);
183
+ try {
184
+ const [currentVersionResult] = yield connection.query(`SELECT MAX(version) as version FROM \`${collection}\` WHERE stream_id = ?`, [
185
+ stream.streamId
186
+ ]);
187
+ const currentVersion = (currentVersionResult == null ? void 0 : currentVersionResult.version) || 0;
188
+ if (aggregateVersion <= currentVersion) {
189
+ throw new import_event_sourcing.EventStoreVersionConflictException(stream, aggregateVersion, currentVersion);
190
+ }
191
+ let version = aggregateVersion - events.length + 1;
192
+ const envelopes = [];
193
+ for (const event of events) {
194
+ const name = this.eventMap.getName(event);
195
+ const payload = this.eventMap.serializeEvent(event);
196
+ const envelope = import_event_sourcing.EventEnvelope.create(name, payload, {
197
+ aggregateId: stream.aggregateId,
198
+ version: version++
199
+ });
200
+ envelopes.push(envelope);
201
+ }
202
+ yield connection.beginTransaction();
203
+ yield connection.batch(`INSERT INTO \`${collection}\` VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, envelopes.map(({ event, payload, metadata }) => {
204
+ var _a, _b;
205
+ return [
206
+ stream.streamId,
207
+ metadata.version,
208
+ event,
209
+ JSON.stringify(payload),
210
+ metadata.eventId,
211
+ metadata.aggregateId,
212
+ metadata.occurredOn,
213
+ (_a = metadata.correlationId) != null ? _a : null,
214
+ (_b = metadata.causationId) != null ? _b : null
215
+ ];
216
+ }));
217
+ yield connection.commit();
218
+ return envelopes;
219
+ } catch (error) {
220
+ yield connection.rollback();
221
+ switch (error.constructor) {
222
+ case import_event_sourcing.EventStoreVersionConflictException:
223
+ throw error;
224
+ default:
225
+ throw new import_event_sourcing.EventStorePersistenceException(collection, error);
226
+ }
227
+ } finally {
228
+ connection.release();
229
+ }
230
+ });
231
+ }
232
+ getEnvelopes(_0, _1) {
233
+ return __asyncGenerator(this, arguments, function* ({ streamId }, filter) {
234
+ const connection = this.pool.getConnection();
235
+ const collection = import_event_sourcing.EventCollection.get(filter == null ? void 0 : filter.pool);
236
+ const fromVersion = filter == null ? void 0 : filter.fromVersion;
237
+ const direction = (filter == null ? void 0 : filter.direction) || import_event_sourcing.StreamReadingDirection.FORWARD;
238
+ const limit = (filter == null ? void 0 : filter.limit) || Number.MAX_SAFE_INTEGER;
239
+ const batch = (filter == null ? void 0 : filter.batch) || import_event_sourcing.DEFAULT_BATCH_SIZE;
240
+ const query = `
241
+ SELECT event, payload, event_id, aggregate_id, version, occurred_on, correlation_id, causation_id
242
+ FROM \`${collection}\`
243
+ WHERE stream_id = ?
244
+ ${fromVersion ? "AND version >= ?" : ""}
245
+ ORDER BY version ${direction === import_event_sourcing.StreamReadingDirection.FORWARD ? "ASC" : "DESC"}
246
+ LIMIT ?
247
+ `;
248
+ const params = fromVersion ? [
249
+ streamId,
250
+ fromVersion,
251
+ limit
252
+ ] : [
253
+ streamId,
254
+ limit
255
+ ];
256
+ const client = yield new __await(connection);
257
+ const stream = client.queryStream(query, params);
258
+ try {
259
+ let batchedEvents = [];
260
+ try {
261
+ for (var iter = __forAwait(stream), more, temp, error; more = !(temp = yield new __await(iter.next())).done; more = false) {
262
+ const { event, payload, event_id, aggregate_id, version, occurred_on, correlation_id, causation_id } = temp.value;
263
+ batchedEvents.push(import_event_sourcing.EventEnvelope.from(event, payload, {
264
+ eventId: event_id,
265
+ aggregateId: aggregate_id,
266
+ version,
267
+ occurredOn: occurred_on,
268
+ correlationId: correlation_id,
269
+ causationId: causation_id
270
+ }));
271
+ if (batchedEvents.length === batch) {
272
+ yield batchedEvents;
273
+ batchedEvents = [];
274
+ }
275
+ }
276
+ } catch (temp) {
277
+ error = [temp];
278
+ } finally {
279
+ try {
280
+ more && (temp = iter.return) && (yield new __await(temp.call(iter)));
281
+ } finally {
282
+ if (error)
283
+ throw error[0];
284
+ }
285
+ }
286
+ if (batchedEvents.length > 0) {
287
+ yield batchedEvents;
288
+ }
289
+ } catch (e) {
290
+ stream.destroy();
291
+ } finally {
292
+ yield new __await(client.release());
293
+ }
294
+ });
295
+ }
296
+ getEnvelope(_0, _1, _2) {
297
+ return __async(this, arguments, function* ({ streamId }, version, pool) {
298
+ const collection = import_event_sourcing.EventCollection.get(pool);
299
+ const [entity] = yield this.pool.query(`SELECT event, payload, event_id, aggregate_id, version, occurred_on, correlation_id, causation_id FROM \`${collection}\` WHERE stream_id = ? AND version = ?`, [
300
+ streamId,
301
+ version
302
+ ]);
303
+ if (!entity) {
304
+ throw new import_event_sourcing.EventNotFoundException(streamId, version);
305
+ }
306
+ return import_event_sourcing.EventEnvelope.from(entity.event, entity.payload, {
307
+ eventId: entity.event_id,
308
+ aggregateId: entity.aggregate_id,
309
+ version: entity.version,
310
+ occurredOn: entity.occurred_on,
311
+ correlationId: entity.correlation_id,
312
+ causationId: entity.causation_id
313
+ });
314
+ });
315
+ }
316
+ };
317
+ __name(_MariaDBEventStore, "MariaDBEventStore");
318
+ var MariaDBEventStore = _MariaDBEventStore;
319
+
320
+ // lib/mariadb.snapshot-store.ts
321
+ var import_event_sourcing2 = require("@ocoda/event-sourcing");
322
+ var import_mariadb2 = require("mariadb");
323
+ var _MariaDBSnapshotStore = class _MariaDBSnapshotStore extends import_event_sourcing2.SnapshotStore {
324
+ constructor() {
325
+ super(...arguments);
326
+ __publicField(this, "pool");
327
+ }
328
+ connect() {
329
+ return __async(this, null, function* () {
330
+ this.logger.log("Starting store");
331
+ this.pool = (0, import_mariadb2.createPool)(this.options);
332
+ });
333
+ }
334
+ disconnect() {
335
+ return __async(this, null, function* () {
336
+ this.logger.log("Stopping store");
337
+ yield this.pool.end();
338
+ });
339
+ }
340
+ ensureCollection(pool) {
341
+ return __async(this, null, function* () {
342
+ const collection = import_event_sourcing2.SnapshotCollection.get(pool);
343
+ try {
344
+ yield this.pool.query(`CREATE TABLE IF NOT EXISTS \`${collection}\` (
345
+ stream_id VARCHAR(90) NOT NULL,
346
+ version INT NOT NULL,
347
+ payload JSON NOT NULL,
348
+ snapshot_id VARCHAR(40) NOT NULL,
349
+ aggregate_id VARCHAR(40) NOT NULL,
350
+ registered_on TIMESTAMP NOT NULL,
351
+ aggregate_name VARCHAR(50) NOT NULL,
352
+ latest VARCHAR(100),
353
+ PRIMARY KEY (stream_id, version),
354
+ INDEX idx_aggregate_name_latest (aggregate_name, latest)
355
+ )`);
356
+ return collection;
357
+ } catch (error) {
358
+ throw new import_event_sourcing2.SnapshotStoreCollectionCreationException(collection, error);
359
+ }
360
+ });
361
+ }
362
+ stop() {
363
+ return __async(this, null, function* () {
364
+ this.logger.log("Stopping store");
365
+ yield this.pool.end();
366
+ });
367
+ }
368
+ getSnapshots(_0, _1) {
369
+ return __asyncGenerator(this, arguments, function* ({ streamId }, filter) {
370
+ const connection = this.pool.getConnection();
371
+ const collection = import_event_sourcing2.SnapshotCollection.get(filter == null ? void 0 : filter.pool);
372
+ const fromVersion = filter == null ? void 0 : filter.fromVersion;
373
+ const direction = (filter == null ? void 0 : filter.direction) || import_event_sourcing2.StreamReadingDirection.FORWARD;
374
+ const limit = (filter == null ? void 0 : filter.limit) || Number.MAX_SAFE_INTEGER;
375
+ const batch = (filter == null ? void 0 : filter.batch) || import_event_sourcing2.DEFAULT_BATCH_SIZE;
376
+ const query = `
377
+ SELECT payload
378
+ FROM \`${collection}\`
379
+ WHERE stream_id = ?
380
+ ${fromVersion ? "AND version >= ?" : ""}
381
+ ORDER BY version ${direction === import_event_sourcing2.StreamReadingDirection.FORWARD ? "ASC" : "DESC"}
382
+ LIMIT ?
383
+ `;
384
+ const params = fromVersion ? [
385
+ streamId,
386
+ fromVersion,
387
+ limit
388
+ ] : [
389
+ streamId,
390
+ limit
391
+ ];
392
+ const client = yield new __await(connection);
393
+ const stream = client.queryStream(query, params);
394
+ try {
395
+ let batchedEvents = [];
396
+ try {
397
+ for (var iter = __forAwait(stream), more, temp, error; more = !(temp = yield new __await(iter.next())).done; more = false) {
398
+ const { payload } = temp.value;
399
+ batchedEvents.push(payload);
400
+ if (batchedEvents.length === batch) {
401
+ yield batchedEvents;
402
+ batchedEvents = [];
403
+ }
404
+ }
405
+ } catch (temp) {
406
+ error = [temp];
407
+ } finally {
408
+ try {
409
+ more && (temp = iter.return) && (yield new __await(temp.call(iter)));
410
+ } finally {
411
+ if (error)
412
+ throw error[0];
413
+ }
414
+ }
415
+ if (batchedEvents.length > 0) {
416
+ yield batchedEvents;
417
+ }
418
+ } catch (e) {
419
+ stream.destroy();
420
+ } finally {
421
+ yield new __await(client.release());
422
+ }
423
+ });
424
+ }
425
+ getSnapshot(_0, _1, _2) {
426
+ return __async(this, arguments, function* ({ streamId }, version, pool) {
427
+ const collection = import_event_sourcing2.SnapshotCollection.get(pool);
428
+ const [entity] = yield this.pool.query(`SELECT payload FROM \`${collection}\` WHERE stream_id = ? AND version = ?`, [
429
+ streamId,
430
+ version
431
+ ]);
432
+ if (!entity) {
433
+ throw new import_event_sourcing2.SnapshotNotFoundException(streamId, version);
434
+ }
435
+ return entity.payload;
436
+ });
437
+ }
438
+ appendSnapshot(stream, aggregateVersion, snapshot, pool) {
439
+ return __async(this, null, function* () {
440
+ const connection = yield this.pool.getConnection();
441
+ const collection = import_event_sourcing2.SnapshotCollection.get(pool);
442
+ try {
443
+ const envelope = import_event_sourcing2.SnapshotEnvelope.create(snapshot, {
444
+ aggregateId: stream.aggregateId,
445
+ version: aggregateVersion
446
+ });
447
+ const [lastStreamEntity] = yield this.getLastStreamEntities(collection, [
448
+ stream
449
+ ], [
450
+ "stream_id",
451
+ "version"
452
+ ], connection);
453
+ if (aggregateVersion <= (lastStreamEntity == null ? void 0 : lastStreamEntity.version)) {
454
+ throw new import_event_sourcing2.SnapshotStoreVersionConflictException(stream, aggregateVersion, lastStreamEntity.version);
455
+ }
456
+ yield connection.beginTransaction();
457
+ if (lastStreamEntity) {
458
+ yield connection.query(`UPDATE \`${collection}\` SET latest = null WHERE stream_id = ? AND version = ?`, [
459
+ lastStreamEntity.stream_id,
460
+ lastStreamEntity.version
461
+ ]);
462
+ }
463
+ yield connection.query(`INSERT INTO \`${collection}\` VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [
464
+ stream.streamId,
465
+ envelope.metadata.version,
466
+ JSON.stringify(envelope.payload),
467
+ envelope.metadata.snapshotId,
468
+ envelope.metadata.aggregateId,
469
+ envelope.metadata.registeredOn,
470
+ stream.aggregate,
471
+ `latest#${stream.streamId}`
472
+ ]);
473
+ yield connection.commit();
474
+ return envelope;
475
+ } catch (error) {
476
+ yield connection.rollback();
477
+ switch (error.constructor) {
478
+ case import_event_sourcing2.SnapshotStoreVersionConflictException:
479
+ throw error;
480
+ default:
481
+ throw new import_event_sourcing2.SnapshotStorePersistenceException(collection, error);
482
+ }
483
+ } finally {
484
+ connection.release();
485
+ }
486
+ });
487
+ }
488
+ getLastSnapshot(stream, pool) {
489
+ return __async(this, null, function* () {
490
+ const collection = import_event_sourcing2.SnapshotCollection.get(pool);
491
+ const [entity] = yield this.getLastStreamEntities(collection, [
492
+ stream
493
+ ], [
494
+ "payload"
495
+ ]);
496
+ if (entity) {
497
+ return entity.payload;
498
+ }
499
+ });
500
+ }
501
+ getManyLastSnapshots(streams, pool) {
502
+ return __async(this, null, function* () {
503
+ const collection = import_event_sourcing2.SnapshotCollection.get(pool);
504
+ const entities = yield this.getLastStreamEntities(collection, streams, [
505
+ "stream_id",
506
+ "payload"
507
+ ]);
508
+ return new Map(entities.map(({ stream_id, payload }) => [
509
+ streams.find(({ streamId: currentStreamId }) => currentStreamId === stream_id),
510
+ payload
511
+ ]));
512
+ });
513
+ }
514
+ getLastEnvelope(stream, pool) {
515
+ return __async(this, null, function* () {
516
+ const collection = import_event_sourcing2.SnapshotCollection.get(pool);
517
+ const [entity] = yield this.getLastStreamEntities(collection, [
518
+ stream
519
+ ], [
520
+ "payload",
521
+ "snapshot_id",
522
+ "aggregate_id",
523
+ "registered_on",
524
+ "version"
525
+ ]);
526
+ if (entity) {
527
+ return import_event_sourcing2.SnapshotEnvelope.from(entity.payload, {
528
+ snapshotId: entity.snapshot_id,
529
+ aggregateId: entity.aggregate_id,
530
+ registeredOn: entity.registered_on,
531
+ version: entity.version
532
+ });
533
+ }
534
+ });
535
+ }
536
+ getEnvelopes(_0, _1) {
537
+ return __asyncGenerator(this, arguments, function* ({ streamId }, filter) {
538
+ const connection = this.pool.getConnection();
539
+ const collection = import_event_sourcing2.SnapshotCollection.get(filter == null ? void 0 : filter.pool);
540
+ const fromVersion = filter == null ? void 0 : filter.fromVersion;
541
+ const direction = (filter == null ? void 0 : filter.direction) || import_event_sourcing2.StreamReadingDirection.FORWARD;
542
+ const limit = (filter == null ? void 0 : filter.limit) || Number.MAX_SAFE_INTEGER;
543
+ const batch = (filter == null ? void 0 : filter.batch) || import_event_sourcing2.DEFAULT_BATCH_SIZE;
544
+ const query = `
545
+ SELECT payload, aggregate_id, registered_on, snapshot_id, version
546
+ FROM \`${collection}\`
547
+ WHERE stream_id = ?
548
+ ${fromVersion ? "AND version >= ?" : ""}
549
+ ORDER BY version ${direction === import_event_sourcing2.StreamReadingDirection.FORWARD ? "ASC" : "DESC"}
550
+ LIMIT ?
551
+ `;
552
+ const params = fromVersion ? [
553
+ streamId,
554
+ fromVersion,
555
+ limit
556
+ ] : [
557
+ streamId,
558
+ limit
559
+ ];
560
+ const client = yield new __await(connection);
561
+ const stream = client.queryStream(query, params);
562
+ try {
563
+ let batchedSnapshots = [];
564
+ try {
565
+ for (var iter = __forAwait(stream), more, temp, error; more = !(temp = yield new __await(iter.next())).done; more = false) {
566
+ const { payload, aggregate_id, registered_on, snapshot_id, version } = temp.value;
567
+ batchedSnapshots.push(import_event_sourcing2.SnapshotEnvelope.from(payload, {
568
+ aggregateId: aggregate_id,
569
+ registeredOn: registered_on,
570
+ snapshotId: snapshot_id,
571
+ version
572
+ }));
573
+ if (batchedSnapshots.length === batch) {
574
+ yield batchedSnapshots;
575
+ batchedSnapshots = [];
576
+ }
577
+ }
578
+ } catch (temp) {
579
+ error = [temp];
580
+ } finally {
581
+ try {
582
+ more && (temp = iter.return) && (yield new __await(temp.call(iter)));
583
+ } finally {
584
+ if (error)
585
+ throw error[0];
586
+ }
587
+ }
588
+ if (batchedSnapshots.length > 0) {
589
+ yield batchedSnapshots;
590
+ }
591
+ } catch (e) {
592
+ stream.destroy();
593
+ } finally {
594
+ yield new __await(client.release());
595
+ }
596
+ });
597
+ }
598
+ getEnvelope(_0, _1, _2) {
599
+ return __async(this, arguments, function* ({ streamId }, version, pool) {
600
+ const collection = import_event_sourcing2.SnapshotCollection.get(pool);
601
+ const [entity] = yield this.pool.query(`SELECT payload, aggregate_id, registered_on, snapshot_id, version FROM \`${collection}\` WHERE stream_id = ? AND version = ?`, [
602
+ streamId,
603
+ version
604
+ ]);
605
+ if (!entity) {
606
+ throw new import_event_sourcing2.SnapshotNotFoundException(streamId, version);
607
+ }
608
+ return import_event_sourcing2.SnapshotEnvelope.from(entity.payload, {
609
+ aggregateId: entity.aggregate_id,
610
+ registeredOn: entity.registered_on,
611
+ snapshotId: entity.snapshot_id,
612
+ version: entity.version
613
+ });
614
+ });
615
+ }
616
+ getLastAggregateEnvelopes(aggregateName, filter) {
617
+ return __asyncGenerator(this, null, function* () {
618
+ const connection = this.pool.getConnection();
619
+ const collection = import_event_sourcing2.SnapshotCollection.get(filter == null ? void 0 : filter.pool);
620
+ const fromId = filter == null ? void 0 : filter.fromId;
621
+ const limit = (filter == null ? void 0 : filter.limit) || Number.MAX_SAFE_INTEGER;
622
+ const batch = (filter == null ? void 0 : filter.batch) || import_event_sourcing2.DEFAULT_BATCH_SIZE;
623
+ const query = `
624
+ SELECT payload, aggregate_id, registered_on, snapshot_id, version
625
+ FROM \`${collection}\`
626
+ WHERE aggregate_name = ?
627
+ AND ${fromId ? "latest >= ?" : "latest LIKE 'latest%'"}
628
+ LIMIT ?
629
+ `;
630
+ const params = fromId ? [
631
+ aggregateName,
632
+ fromId,
633
+ limit
634
+ ] : [
635
+ aggregateName,
636
+ limit
637
+ ];
638
+ const client = yield new __await(connection);
639
+ const stream = client.queryStream(query, params);
640
+ try {
641
+ let batchedSnapshots = [];
642
+ try {
643
+ for (var iter = __forAwait(stream), more, temp, error; more = !(temp = yield new __await(iter.next())).done; more = false) {
644
+ const { payload, aggregate_id, registered_on, snapshot_id, version } = temp.value;
645
+ batchedSnapshots.push(import_event_sourcing2.SnapshotEnvelope.from(payload, {
646
+ aggregateId: aggregate_id,
647
+ registeredOn: registered_on,
648
+ snapshotId: snapshot_id,
649
+ version
650
+ }));
651
+ if (batchedSnapshots.length === batch) {
652
+ yield batchedSnapshots;
653
+ batchedSnapshots = [];
654
+ }
655
+ }
656
+ } catch (temp) {
657
+ error = [temp];
658
+ } finally {
659
+ try {
660
+ more && (temp = iter.return) && (yield new __await(temp.call(iter)));
661
+ } finally {
662
+ if (error)
663
+ throw error[0];
664
+ }
665
+ }
666
+ if (batchedSnapshots.length > 0) {
667
+ yield batchedSnapshots;
668
+ }
669
+ } catch (e) {
670
+ stream.destroy();
671
+ } finally {
672
+ yield new __await(client.release());
673
+ }
674
+ });
675
+ }
676
+ getManyLastSnapshotEnvelopes(streams, pool) {
677
+ return __async(this, null, function* () {
678
+ const collection = import_event_sourcing2.SnapshotCollection.get(pool);
679
+ const entities = yield this.getLastStreamEntities(collection, streams, [
680
+ "stream_id",
681
+ "payload",
682
+ "aggregate_id",
683
+ "registered_on",
684
+ "snapshot_id",
685
+ "version"
686
+ ]);
687
+ return new Map(entities.map(({ stream_id, payload, aggregate_id, registered_on, snapshot_id, version }) => [
688
+ streams.find(({ streamId: currentStreamId }) => currentStreamId === stream_id),
689
+ import_event_sourcing2.SnapshotEnvelope.from(payload, {
690
+ aggregateId: aggregate_id,
691
+ registeredOn: new Date(registered_on),
692
+ snapshotId: snapshot_id,
693
+ version
694
+ })
695
+ ]));
696
+ });
697
+ }
698
+ getLastStreamEntities(collection, streams, fields, connection) {
699
+ return __async(this, null, function* () {
700
+ const latestIds = streams.map(({ streamId }) => `latest#${streamId}`);
701
+ return (connection || this.pool).query(`SELECT ${fields.join(", ")}
702
+ FROM \`${collection}\`
703
+ WHERE latest IN (?)
704
+ `, [
705
+ latestIds
706
+ ]);
707
+ });
708
+ }
709
+ };
710
+ __name(_MariaDBSnapshotStore, "MariaDBSnapshotStore");
711
+ var MariaDBSnapshotStore = _MariaDBSnapshotStore;
712
+ // Annotate the CommonJS export names for ESM import in node:
713
+ 0 && (module.exports = {
714
+ MariaDBEventStore,
715
+ MariaDBSnapshotStore
716
+ });
package/index.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './dist';
package/index.js ADDED
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./dist"), exports);
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@ocoda/event-sourcing-mariadb",
3
+ "version": "1.0.0",
4
+ "description": "NestJS Event Sourcing Library (@mariadb)",
5
+ "author": "Dries Hooghe <dries@drieshooghe.com>",
6
+ "license": "MIT",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "bugs": {
10
+ "url": "https://github.com/ocoda/event-sourcing/issues"
11
+ },
12
+ "engines": {
13
+ "node": ">= 18.0.0"
14
+ },
15
+ "dependencies": {
16
+ "mariadb": "^3.3.1"
17
+ },
18
+ "peerDependencies": {
19
+ "@nestjs/core": "^10.0.0",
20
+ "reflect-metadata": "^0.2.0",
21
+ "rxjs": "^7.2.0",
22
+ "@ocoda/event-sourcing": "1.0.0"
23
+ },
24
+ "devDependencies": {
25
+ "@biomejs/biome": "1.9.3",
26
+ "@faker-js/faker": "^9.0.0",
27
+ "@nestjs/common": "10.4.4",
28
+ "@nestjs/core": "10.4.4",
29
+ "@nestjs/platform-express": "10.4.4",
30
+ "@nestjs/testing": "10.4.4",
31
+ "@swc-node/register": "^1.5.2",
32
+ "@swc/core": "1.7.26",
33
+ "@swc/jest": "0.2.36",
34
+ "@types/jest": "29.5.13",
35
+ "@types/node": "20.16.10",
36
+ "jest": "29.7.0",
37
+ "jest-mock": "29.7.0",
38
+ "reflect-metadata": "0.2.2",
39
+ "rxjs": "7.8.1",
40
+ "tsup": "^8.3.0",
41
+ "typescript": "5.6.2",
42
+ "@ocoda/event-sourcing-config": "1.0.0",
43
+ "@ocoda/event-sourcing-testing": "1.0.0"
44
+ },
45
+ "repository": {
46
+ "type": "git",
47
+ "url": "https://github.com/ocoda/event-sourcing",
48
+ "directory": "packages/integration/mariadb"
49
+ },
50
+ "scripts": {
51
+ "build": "rm -rf dist && tsup lib/index.ts --format cjs --dts",
52
+ "test": "jest --config jest.config.js",
53
+ "test:ci": "jest --config jest.config.js --runInBand --coverage",
54
+ "ci": "biome ci",
55
+ "format": "biome format --write",
56
+ "lint": "biome check --write"
57
+ }
58
+ }