@event-driven-io/emmett-esdb 0.43.0-beta.13 → 0.43.0-beta.14

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/index.js CHANGED
@@ -1,1500 +1,395 @@
1
- // ../emmett/dist/chunk-AZDDB5SF.js
2
- var isNumber = (val) => typeof val === "number" && val === val;
3
- var isString = (val) => typeof val === "string";
4
- var EmmettError = class _EmmettError extends Error {
5
- static Codes = {
6
- ValidationError: 400,
7
- IllegalStateError: 403,
8
- NotFoundError: 404,
9
- ConcurrencyError: 412,
10
- InternalServerError: 500
11
- };
12
- errorCode;
13
- constructor(options) {
14
- const errorCode = options && typeof options === "object" && "errorCode" in options ? options.errorCode : isNumber(options) ? options : _EmmettError.Codes.InternalServerError;
15
- const message = options && typeof options === "object" && "message" in options ? options.message : isString(options) ? options : `Error with status code '${errorCode}' ocurred during Emmett processing`;
16
- super(message);
17
- this.errorCode = errorCode;
18
- Object.setPrototypeOf(this, _EmmettError.prototype);
19
- }
20
- static mapFrom(error) {
21
- if (_EmmettError.isInstanceOf(error)) {
22
- return error;
23
- }
24
- return new _EmmettError({
25
- errorCode: "errorCode" in error && error.errorCode !== void 0 && error.errorCode !== null ? error.errorCode : _EmmettError.Codes.InternalServerError,
26
- message: error.message ?? "An unknown error occurred"
27
- });
28
- }
29
- static isInstanceOf(error, errorCode) {
30
- return typeof error === "object" && error !== null && "errorCode" in error && isNumber(error.errorCode) && (errorCode === void 0 || error.errorCode === errorCode);
31
- }
32
- };
33
- var ConcurrencyError = class _ConcurrencyError extends EmmettError {
34
- constructor(current, expected, message) {
35
- super({
36
- errorCode: EmmettError.Codes.ConcurrencyError,
37
- message: message ?? `Expected version ${expected.toString()} does not match current ${current?.toString()}`
38
- });
39
- this.current = current;
40
- this.expected = expected;
41
- Object.setPrototypeOf(this, _ConcurrencyError.prototype);
42
- }
43
- };
44
- var ConcurrencyInMemoryDatabaseError = class _ConcurrencyInMemoryDatabaseError extends EmmettError {
45
- constructor(message) {
46
- super({
47
- errorCode: EmmettError.Codes.ConcurrencyError,
48
- message: message ?? `Expected document state does not match current one!`
49
- });
50
- Object.setPrototypeOf(this, _ConcurrencyInMemoryDatabaseError.prototype);
51
- }
52
- };
53
-
54
- // ../emmett/dist/index.js
55
- import { v4 as uuid5 } from "uuid";
56
- import { v7 as uuid2 } from "uuid";
57
- import { v7 as uuid } from "uuid";
58
- import retry from "async-retry";
59
- import { v7 as uuid3 } from "uuid";
60
- import { v4 as uuid4 } from "uuid";
61
- import { v7 as uuid6 } from "uuid";
62
- var emmettPrefix = "emt";
63
- var defaultTag = `${emmettPrefix}:default`;
64
- var unknownTag = `${emmettPrefix}:unknown`;
65
- var STREAM_EXISTS = "STREAM_EXISTS";
66
- var STREAM_DOES_NOT_EXIST = "STREAM_DOES_NOT_EXIST";
67
- var NO_CONCURRENCY_CHECK = "NO_CONCURRENCY_CHECK";
68
- var matchesExpectedVersion = (current, expected, defaultVersion) => {
69
- if (expected === NO_CONCURRENCY_CHECK) return true;
70
- if (expected == STREAM_DOES_NOT_EXIST) return current === defaultVersion;
71
- if (expected == STREAM_EXISTS) return current !== defaultVersion;
72
- return current === expected;
73
- };
74
- var assertExpectedVersionMatchesCurrent = (current, expected, defaultVersion) => {
75
- expected ??= NO_CONCURRENCY_CHECK;
76
- if (!matchesExpectedVersion(current, expected, defaultVersion))
77
- throw new ExpectedVersionConflictError(current, expected);
78
- };
79
- var ExpectedVersionConflictError = class _ExpectedVersionConflictError extends ConcurrencyError {
80
- constructor(current, expected) {
81
- super(current?.toString(), expected?.toString());
82
- Object.setPrototypeOf(this, _ExpectedVersionConflictError.prototype);
83
- }
84
- };
85
- var isPrimitive = (value) => {
86
- const type = typeof value;
87
- return value === null || value === void 0 || type === "boolean" || type === "number" || type === "string" || type === "symbol" || type === "bigint";
88
- };
89
- var compareArrays = (left, right) => {
90
- if (left.length !== right.length) {
91
- return false;
92
- }
93
- for (let i = 0; i < left.length; i++) {
94
- const leftHas = i in left;
95
- const rightHas = i in right;
96
- if (leftHas !== rightHas) return false;
97
- if (leftHas && !deepEquals(left[i], right[i])) return false;
98
- }
99
- return true;
100
- };
101
- var compareDates = (left, right) => {
102
- return left.getTime() === right.getTime();
103
- };
104
- var compareRegExps = (left, right) => {
105
- return left.toString() === right.toString();
106
- };
107
- var compareErrors = (left, right) => {
108
- if (left.message !== right.message || left.name !== right.name) {
109
- return false;
110
- }
111
- const leftKeys = Object.keys(left);
112
- const rightKeys = Object.keys(right);
113
- if (leftKeys.length !== rightKeys.length) return false;
114
- const rightKeySet = new Set(rightKeys);
115
- for (const key of leftKeys) {
116
- if (!rightKeySet.has(key)) return false;
117
- if (!deepEquals(left[key], right[key])) return false;
118
- }
119
- return true;
120
- };
121
- var compareMaps = (left, right) => {
122
- if (left.size !== right.size) return false;
123
- for (const [key, value] of left) {
124
- if (isPrimitive(key)) {
125
- if (!right.has(key) || !deepEquals(value, right.get(key))) {
126
- return false;
127
- }
128
- } else {
129
- let found = false;
130
- for (const [rightKey, rightValue] of right) {
131
- if (deepEquals(key, rightKey) && deepEquals(value, rightValue)) {
132
- found = true;
133
- break;
134
- }
135
- }
136
- if (!found) return false;
137
- }
138
- }
139
- return true;
140
- };
141
- var compareSets = (left, right) => {
142
- if (left.size !== right.size) return false;
143
- for (const leftItem of left) {
144
- if (isPrimitive(leftItem)) {
145
- if (!right.has(leftItem)) return false;
146
- } else {
147
- let found = false;
148
- for (const rightItem of right) {
149
- if (deepEquals(leftItem, rightItem)) {
150
- found = true;
151
- break;
152
- }
153
- }
154
- if (!found) return false;
155
- }
156
- }
157
- return true;
158
- };
159
- var compareArrayBuffers = (left, right) => {
160
- if (left.byteLength !== right.byteLength) return false;
161
- const leftView = new Uint8Array(left);
162
- const rightView = new Uint8Array(right);
163
- for (let i = 0; i < leftView.length; i++) {
164
- if (leftView[i] !== rightView[i]) return false;
165
- }
166
- return true;
167
- };
168
- var compareTypedArrays = (left, right) => {
169
- if (left.constructor !== right.constructor) return false;
170
- if (left.byteLength !== right.byteLength) return false;
171
- const leftArray = new Uint8Array(
172
- left.buffer,
173
- left.byteOffset,
174
- left.byteLength
175
- );
176
- const rightArray = new Uint8Array(
177
- right.buffer,
178
- right.byteOffset,
179
- right.byteLength
180
- );
181
- for (let i = 0; i < leftArray.length; i++) {
182
- if (leftArray[i] !== rightArray[i]) return false;
183
- }
184
- return true;
185
- };
186
- var compareObjects = (left, right) => {
187
- const keys1 = Object.keys(left);
188
- const keys2 = Object.keys(right);
189
- if (keys1.length !== keys2.length) {
190
- return false;
191
- }
192
- for (const key of keys1) {
193
- if (left[key] instanceof Function && right[key] instanceof Function) {
194
- continue;
195
- }
196
- const isEqual = deepEquals(left[key], right[key]);
197
- if (!isEqual) {
198
- return false;
199
- }
200
- }
201
- return true;
202
- };
203
- var getType = (value) => {
204
- if (value === null) return "null";
205
- if (value === void 0) return "undefined";
206
- const primitiveType = typeof value;
207
- if (primitiveType !== "object") return primitiveType;
208
- if (Array.isArray(value)) return "array";
209
- if (value instanceof Boolean) return "boxed-boolean";
210
- if (value instanceof Number) return "boxed-number";
211
- if (value instanceof String) return "boxed-string";
212
- if (value instanceof Date) return "date";
213
- if (value instanceof RegExp) return "regexp";
214
- if (value instanceof Error) return "error";
215
- if (value instanceof Map) return "map";
216
- if (value instanceof Set) return "set";
217
- if (value instanceof ArrayBuffer) return "arraybuffer";
218
- if (value instanceof DataView) return "dataview";
219
- if (value instanceof WeakMap) return "weakmap";
220
- if (value instanceof WeakSet) return "weakset";
221
- if (ArrayBuffer.isView(value)) return "typedarray";
222
- return "object";
223
- };
224
- var deepEquals = (left, right) => {
225
- if (left === right) return true;
226
- if (isEquatable(left)) {
227
- return left.equals(right);
228
- }
229
- const leftType = getType(left);
230
- const rightType = getType(right);
231
- if (leftType !== rightType) return false;
232
- switch (leftType) {
233
- case "null":
234
- case "undefined":
235
- case "boolean":
236
- case "number":
237
- case "bigint":
238
- case "string":
239
- case "symbol":
240
- case "function":
241
- return left === right;
242
- case "array":
243
- return compareArrays(left, right);
244
- case "date":
245
- return compareDates(left, right);
246
- case "regexp":
247
- return compareRegExps(left, right);
248
- case "error":
249
- return compareErrors(left, right);
250
- case "map":
251
- return compareMaps(
252
- left,
253
- right
254
- );
255
- case "set":
256
- return compareSets(left, right);
257
- case "arraybuffer":
258
- return compareArrayBuffers(left, right);
259
- case "dataview":
260
- case "weakmap":
261
- case "weakset":
262
- return false;
263
- case "typedarray":
264
- return compareTypedArrays(
265
- left,
266
- right
267
- );
268
- case "boxed-boolean":
269
- return left.valueOf() === right.valueOf();
270
- case "boxed-number":
271
- return left.valueOf() === right.valueOf();
272
- case "boxed-string":
273
- return left.valueOf() === right.valueOf();
274
- case "object":
275
- return compareObjects(
276
- left,
277
- right
278
- );
279
- default:
280
- return false;
281
- }
282
- };
283
- var isEquatable = (left) => {
284
- return left !== null && left !== void 0 && typeof left === "object" && "equals" in left && typeof left["equals"] === "function";
285
- };
286
- var toNormalizedString = (value) => value.toString().padStart(19, "0");
287
- var bigInt = {
288
- toNormalizedString
289
- };
290
- var bigIntReplacer = (_key, value) => {
291
- return typeof value === "bigint" ? value.toString() : value;
292
- };
293
- var dateReplacer = (_key, value) => {
294
- return value instanceof Date ? value.toISOString() : value;
295
- };
296
- var isFirstLetterNumeric = (str) => {
297
- const c = str.charCodeAt(0);
298
- return c >= 48 && c <= 57;
299
- };
300
- var isFirstLetterNumericOrMinus = (str) => {
301
- const c = str.charCodeAt(0);
302
- return c >= 48 && c <= 57 || c === 45;
303
- };
304
- var bigIntReviver = (_key, value, context) => {
305
- if (typeof value === "number" && Number.isInteger(value) && !Number.isSafeInteger(value)) {
306
- try {
307
- return BigInt(context?.source ?? value.toString());
308
- } catch {
309
- return value;
310
- }
311
- }
312
- if (typeof value === "string" && value.length > 15) {
313
- if (isFirstLetterNumericOrMinus(value)) {
314
- const num = Number(value);
315
- if (Number.isFinite(num) && !Number.isSafeInteger(num)) {
316
- try {
317
- return BigInt(value);
318
- } catch {
319
- }
320
- }
321
- }
322
- }
323
- return value;
324
- };
325
- var dateReviver = (_key, value) => {
326
- if (typeof value === "string" && value.length === 24 && isFirstLetterNumeric(value) && value[10] === "T" && value[23] === "Z") {
327
- const date = new Date(value);
328
- if (!isNaN(date.getTime())) {
329
- return date;
330
- }
331
- }
332
- return value;
333
- };
334
- var composeJSONReplacers = (...replacers) => {
335
- const filteredReplacers = replacers.filter((r) => r !== void 0);
336
- if (filteredReplacers.length === 0) return void 0;
337
- return (key, value) => (
338
- // eslint-disable-next-line @typescript-eslint/no-unsafe-return
339
- filteredReplacers.reduce(
340
- // eslint-disable-next-line @typescript-eslint/no-unsafe-return
341
- (accValue, replacer) => replacer(key, accValue),
342
- value
343
- )
344
- );
345
- };
346
- var composeJSONRevivers = (...revivers) => {
347
- const filteredRevivers = revivers.filter((r) => r !== void 0);
348
- if (filteredRevivers.length === 0) return void 0;
349
- return (key, value, context) => (
350
- // eslint-disable-next-line @typescript-eslint/no-unsafe-return
351
- filteredRevivers.reduce(
352
- // eslint-disable-next-line @typescript-eslint/no-unsafe-return
353
- (accValue, reviver) => reviver(key, accValue, context),
354
- value
355
- )
356
- );
357
- };
358
- var JSONReplacer = (opts) => composeJSONReplacers(
359
- opts?.replacer,
360
- opts?.failOnBigIntSerialization !== true ? JSONReplacers.bigInt : void 0,
361
- opts?.useDefaultDateSerialization !== true ? JSONReplacers.date : void 0
362
- );
363
- var JSONReviver = (opts) => composeJSONRevivers(
364
- opts?.reviver,
365
- opts?.parseBigInts === true ? JSONRevivers.bigInt : void 0,
366
- opts?.parseDates === true ? JSONRevivers.date : void 0
367
- );
368
- var JSONReplacers = {
369
- bigInt: bigIntReplacer,
370
- date: dateReplacer
371
- };
372
- var JSONRevivers = {
373
- bigInt: bigIntReviver,
374
- date: dateReviver
375
- };
376
- var jsonSerializer = (options) => {
377
- const defaultReplacer = JSONReplacer(options);
378
- const defaultReviver = JSONReviver(options);
379
- return {
380
- serialize: (object, serializerOptions) => JSON.stringify(
381
- object,
382
- serializerOptions ? JSONReplacer(serializerOptions) : defaultReplacer
383
- ),
384
- deserialize: (payload, deserializerOptions) => JSON.parse(
385
- payload,
386
- deserializerOptions ? JSONReviver(deserializerOptions) : defaultReviver
387
- )
388
- };
389
- };
390
- var JSONSerializer = Object.assign(jsonSerializer(), {
391
- from: (options) => options?.serialization?.serializer ?? (options?.serialization?.options ? jsonSerializer(options?.serialization?.options) : JSONSerializer)
392
- });
393
- var asyncRetry = async (fn, opts) => {
394
- if (opts === void 0 || opts.retries === 0) return fn();
395
- return retry(
396
- async (bail) => {
397
- try {
398
- const result = await fn();
399
- if (opts?.shouldRetryResult && opts.shouldRetryResult(result)) {
400
- throw new EmmettError(
401
- `Retrying because of result: ${JSONSerializer.serialize(result)}`
402
- );
403
- }
404
- return result;
405
- } catch (error) {
406
- if (opts?.shouldRetryError && !opts.shouldRetryError(error)) {
407
- bail(error);
408
- return void 0;
409
- }
410
- throw error;
411
- }
412
- },
413
- opts ?? { retries: 0 }
414
- );
415
- };
416
- var onShutdown = (handler) => {
417
- const signals = ["SIGTERM", "SIGINT"];
418
- if (typeof process !== "undefined" && typeof process.on === "function") {
419
- for (const signal of signals) {
420
- process.on(signal, handler);
421
- }
422
- return () => {
423
- for (const signal of signals) {
424
- process.off(signal, handler);
425
- }
426
- };
427
- }
428
- const deno = globalThis.Deno;
429
- if (deno && typeof deno.addSignalListener === "function") {
430
- for (const signal of signals) {
431
- deno.addSignalListener(signal, handler);
432
- }
433
- return () => {
434
- for (const signal of signals) {
435
- deno.removeSignalListener(signal, handler);
436
- }
437
- };
438
- }
439
- return () => {
440
- };
441
- };
442
- var textEncoder = new TextEncoder();
443
- var isGeneralExpectedDocumentVersion = (version) => {
444
- return version === "DOCUMENT_DOES_NOT_EXIST" || version === "DOCUMENT_EXISTS" || version === "NO_CONCURRENCY_CHECK";
445
- };
446
- var expectedVersionValue = (version) => version === void 0 || isGeneralExpectedDocumentVersion(version) ? null : version;
447
- var operationResult = (result, options) => {
448
- const operationResult2 = {
449
- ...result,
450
- acknowledged: true,
451
- successful: result.successful,
452
- assertSuccessful: (errorMessage) => {
453
- const { successful } = result;
454
- const { operationName, collectionName } = options;
455
- if (!successful)
456
- throw new ConcurrencyInMemoryDatabaseError(
457
- errorMessage ?? `${operationName} on ${collectionName} failed. Expected document state does not match current one! Result: ${JSONSerializer.serialize(result)}!`
458
- );
459
- }
460
- };
461
- if (options.errors?.throwOnOperationFailures)
462
- operationResult2.assertSuccessful();
463
- return operationResult2;
464
- };
465
- var getInMemoryDatabase = () => {
466
- const storage = /* @__PURE__ */ new Map();
467
- return {
468
- collection: (collectionName, collectionOptions = {}) => {
469
- const ensureCollectionCreated = () => {
470
- if (!storage.has(collectionName)) storage.set(collectionName, []);
471
- };
472
- const errors = collectionOptions.errors;
473
- const collection = {
474
- collectionName,
475
- insertOne: async (document) => {
476
- ensureCollectionCreated();
477
- const _id = document._id ?? uuid2();
478
- const _version = document._version ?? 1n;
479
- const existing = await collection.findOne((c) => c._id === _id);
480
- if (existing) {
481
- return operationResult(
482
- {
483
- successful: false,
484
- insertedId: null,
485
- nextExpectedVersion: _version
486
- },
487
- { operationName: "insertOne", collectionName, errors }
488
- );
489
- }
490
- const documentsInCollection = storage.get(collectionName);
491
- const newDocument = { ...document, _id, _version };
492
- const newCollection = [...documentsInCollection, newDocument];
493
- storage.set(collectionName, newCollection);
494
- return operationResult(
495
- {
496
- successful: true,
497
- insertedId: _id,
498
- nextExpectedVersion: _version
499
- },
500
- { operationName: "insertOne", collectionName, errors }
501
- );
502
- },
503
- findOne: (predicate) => {
504
- ensureCollectionCreated();
505
- const documentsInCollection = storage.get(collectionName);
506
- const filteredDocuments = predicate ? documentsInCollection?.filter((doc) => predicate(doc)) : documentsInCollection;
507
- const firstOne = filteredDocuments?.[0] ?? null;
508
- return Promise.resolve(firstOne);
509
- },
510
- find: (predicate) => {
511
- ensureCollectionCreated();
512
- const documentsInCollection = storage.get(collectionName);
513
- const filteredDocuments = predicate ? documentsInCollection?.filter((doc) => predicate(doc)) : documentsInCollection;
514
- return Promise.resolve(filteredDocuments);
515
- },
516
- deleteOne: (predicate) => {
517
- ensureCollectionCreated();
518
- const documentsInCollection = storage.get(collectionName);
519
- if (predicate) {
520
- const foundIndex = documentsInCollection.findIndex(
521
- (doc) => predicate(doc)
522
- );
523
- if (foundIndex === -1) {
524
- return Promise.resolve(
525
- operationResult(
526
- {
527
- successful: false,
528
- matchedCount: 0,
529
- deletedCount: 0
530
- },
531
- { operationName: "deleteOne", collectionName, errors }
532
- )
533
- );
534
- } else {
535
- const newCollection2 = documentsInCollection.toSpliced(
536
- foundIndex,
537
- 1
538
- );
539
- storage.set(collectionName, newCollection2);
540
- return Promise.resolve(
541
- operationResult(
542
- {
543
- successful: true,
544
- matchedCount: 1,
545
- deletedCount: 1
546
- },
547
- { operationName: "deleteOne", collectionName, errors }
548
- )
549
- );
550
- }
551
- }
552
- const newCollection = documentsInCollection.slice(1);
553
- storage.set(collectionName, newCollection);
554
- return Promise.resolve(
555
- operationResult(
556
- {
557
- successful: true,
558
- matchedCount: 1,
559
- deletedCount: 1
560
- },
561
- { operationName: "deleteOne", collectionName, errors }
562
- )
563
- );
564
- },
565
- replaceOne: (predicate, document, options) => {
566
- ensureCollectionCreated();
567
- const documentsInCollection = storage.get(collectionName);
568
- const firstIndex = documentsInCollection.findIndex(
569
- (doc) => predicate(doc)
570
- );
571
- if (firstIndex === void 0 || firstIndex === -1) {
572
- return Promise.resolve(
573
- operationResult(
574
- {
575
- successful: false,
576
- matchedCount: 0,
577
- modifiedCount: 0,
578
- nextExpectedVersion: 0n
579
- },
580
- { operationName: "replaceOne", collectionName, errors }
581
- )
582
- );
583
- }
584
- const existing = documentsInCollection[firstIndex];
585
- if (typeof options?.expectedVersion === "bigint" && existing._version !== options.expectedVersion) {
586
- return Promise.resolve(
587
- operationResult(
588
- {
589
- successful: false,
590
- matchedCount: 1,
591
- modifiedCount: 0,
592
- nextExpectedVersion: existing._version
593
- },
594
- { operationName: "replaceOne", collectionName, errors }
595
- )
596
- );
597
- }
598
- const newVersion = existing._version + 1n;
599
- const newCollection = documentsInCollection.with(firstIndex, {
600
- _id: existing._id,
601
- ...document,
602
- _version: newVersion
603
- });
604
- storage.set(collectionName, newCollection);
605
- return Promise.resolve(
606
- operationResult(
607
- {
608
- successful: true,
609
- modifiedCount: 1,
610
- matchedCount: firstIndex,
611
- nextExpectedVersion: newVersion
612
- },
613
- { operationName: "replaceOne", collectionName, errors }
614
- )
615
- );
616
- },
617
- handle: async (id, handle, options) => {
618
- const { expectedVersion: version, ...operationOptions } = options ?? {};
619
- ensureCollectionCreated();
620
- const existing = await collection.findOne(({ _id }) => _id === id);
621
- const expectedVersion = expectedVersionValue(version);
622
- if (existing == null && version === "DOCUMENT_EXISTS" || existing == null && expectedVersion != null || existing != null && version === "DOCUMENT_DOES_NOT_EXIST" || existing != null && expectedVersion !== null && existing._version !== expectedVersion) {
623
- return operationResult(
624
- {
625
- successful: false,
626
- document: existing
627
- },
628
- { operationName: "handle", collectionName, errors }
629
- );
630
- }
631
- const result = handle(existing !== null ? { ...existing } : null);
632
- if (deepEquals(existing, result))
633
- return operationResult(
634
- {
635
- successful: true,
636
- document: existing
637
- },
638
- { operationName: "handle", collectionName, errors }
639
- );
640
- if (!existing && result) {
641
- const newDoc = { ...result, _id: id };
642
- const insertResult = await collection.insertOne({
643
- ...newDoc,
644
- _id: id
645
- });
646
- return {
647
- ...insertResult,
648
- document: {
649
- ...newDoc,
650
- _version: insertResult.nextExpectedVersion
651
- }
652
- };
653
- }
654
- if (existing && !result) {
655
- const deleteResult = await collection.deleteOne(
656
- ({ _id }) => id === _id
657
- );
658
- return { ...deleteResult, document: null };
659
- }
660
- if (existing && result) {
661
- const replaceResult = await collection.replaceOne(
662
- ({ _id }) => id === _id,
663
- result,
664
- {
665
- ...operationOptions,
666
- expectedVersion: expectedVersion ?? "DOCUMENT_EXISTS"
667
- }
668
- );
669
- return {
670
- ...replaceResult,
671
- document: {
672
- ...result,
673
- _version: replaceResult.nextExpectedVersion
674
- }
675
- };
676
- }
677
- return operationResult(
678
- {
679
- successful: true,
680
- document: existing
681
- },
682
- { operationName: "handle", collectionName, errors }
683
- );
684
- }
685
- };
686
- return collection;
687
- }
688
- };
689
- };
690
- var getCheckpoint = (message2) => {
691
- return message2.metadata.checkpoint;
692
- };
693
- var wasMessageHandled = (message2, checkpoint) => {
694
- const messageCheckpoint = getCheckpoint(message2);
695
- return messageCheckpoint !== null && messageCheckpoint !== void 0 && checkpoint !== null && checkpoint !== void 0 && messageCheckpoint <= checkpoint;
696
- };
697
- var MessageProcessorType = {
698
- PROJECTOR: "projector",
699
- REACTOR: "reactor"
700
- };
701
- var defaultProcessingMessageProcessingScope = (handler, partialContext) => handler(partialContext);
702
- var bigIntProcessorCheckpoint = (value) => bigInt.toNormalizedString(value);
703
- var parseBigIntProcessorCheckpoint = (value) => BigInt(value);
704
- var defaultProcessorVersion = 1;
705
- var defaultProcessorPartition = defaultTag;
706
- var getProcessorInstanceId = (processorId) => `${processorId}:${uuid3()}`;
707
- var getProjectorId = (options) => `emt:processor:projector:${options.projectionName}`;
708
- var reactor = (options) => {
709
- const {
710
- checkpoints,
711
- processorId,
712
- processorInstanceId: instanceId = getProcessorInstanceId(processorId),
713
- type = MessageProcessorType.REACTOR,
714
- version = defaultProcessorVersion,
715
- partition = defaultProcessorPartition,
716
- hooks = {},
717
- processingScope = defaultProcessingMessageProcessingScope,
718
- startFrom,
719
- canHandle,
720
- stopAfter
721
- } = options;
722
- const isCustomBatch = "eachBatch" in options && !!options.eachBatch;
723
- const eachBatch = isCustomBatch ? options.eachBatch : async (messages, context) => {
724
- let result = void 0;
725
- for (let i = 0; i < messages.length; i++) {
726
- const message2 = messages[i];
727
- const messageProcessingResult = await options.eachMessage(
728
- message2,
729
- context
730
- );
731
- if (messageProcessingResult && messageProcessingResult.type === "STOP") {
732
- result = {
733
- ...messageProcessingResult,
734
- lastSuccessfulMessage: messageProcessingResult.error ? messages[i - 1] : message2
735
- };
736
- break;
737
- }
738
- if (stopAfter && stopAfter(message2)) {
739
- result = {
740
- type: "STOP",
741
- reason: "Stop condition reached",
742
- lastSuccessfulMessage: message2
743
- };
744
- break;
745
- }
746
- if (messageProcessingResult && messageProcessingResult.type === "SKIP") {
747
- result = {
748
- ...messageProcessingResult,
749
- lastSuccessfulMessage: message2
750
- };
751
- continue;
752
- }
753
- }
754
- return result;
755
- };
756
- let isInitiated = false;
757
- let isActive = false;
758
- let lastCheckpoint = null;
759
- let closeSignal = null;
760
- const init = async (initOptions) => {
761
- if (isInitiated) return;
762
- if (hooks.onInit === void 0) {
763
- isInitiated = true;
764
- return;
765
- }
766
- return await processingScope(async (context) => {
767
- await hooks.onInit(context);
768
- isInitiated = true;
769
- }, initOptions);
770
- };
771
- const close = async (closeOptions) => {
772
- isActive = false;
773
- if (closeSignal) {
774
- closeSignal();
775
- closeSignal = null;
776
- }
777
- if (hooks.onClose) {
778
- await processingScope(hooks.onClose, closeOptions);
779
- }
780
- };
781
- return {
782
- // TODO: Consider whether not make it optional or add URN prefix
783
- id: processorId,
784
- instanceId,
785
- type,
786
- canHandle,
787
- init,
788
- start: async (startOptions) => {
789
- if (isActive) return;
790
- await init(startOptions);
791
- isActive = true;
792
- closeSignal = onShutdown(() => close(startOptions));
793
- if (lastCheckpoint !== null) {
794
- return {
795
- lastCheckpoint
796
- };
797
- }
798
- return await processingScope(async (context) => {
799
- if (hooks.onStart) {
800
- await hooks.onStart(context);
801
- }
802
- if (startFrom && startFrom !== "CURRENT") return startFrom;
803
- if (checkpoints) {
804
- const readResult = await checkpoints?.read(
805
- {
806
- processorId,
807
- partition
808
- },
809
- { ...startOptions, ...context }
810
- );
811
- lastCheckpoint = readResult.lastCheckpoint;
812
- }
813
- if (lastCheckpoint === null) return "BEGINNING";
814
- return {
815
- lastCheckpoint
816
- };
817
- }, startOptions);
818
- },
819
- close,
820
- get isActive() {
821
- return isActive;
822
- },
823
- handle: async (messages, partialContext) => {
824
- if (!isActive) return Promise.resolve();
825
- return await processingScope(async (context) => {
826
- const messagesAboveCheckpoint = messages.filter(
827
- (message2) => !wasMessageHandled(message2, lastCheckpoint)
828
- );
829
- const upcastedMessages = messagesAboveCheckpoint.map(
830
- (message2) => upcastRecordedMessage(
831
- // TODO: Make it smarter
832
- message2,
833
- options.messageOptions?.schema?.versioning
834
- )
835
- ).filter(
836
- (upcasted) => !canHandle || canHandle.includes(upcasted.type)
837
- );
838
- const stopMessageIndex = isCustomBatch && stopAfter ? upcastedMessages.findIndex(stopAfter) : -1;
839
- const unhandledMessages = stopMessageIndex !== -1 ? upcastedMessages.slice(0, stopMessageIndex + 1) : upcastedMessages;
840
- const batchResult = await eachBatch(unhandledMessages, context);
841
- const messageProcessingResult = batchResult?.type === "STOP" ? batchResult : stopMessageIndex !== -1 ? {
842
- type: "STOP",
843
- reason: "Stop condition reached",
844
- lastSuccessfulMessage: unhandledMessages[stopMessageIndex]
845
- } : batchResult;
846
- const isStop = messageProcessingResult && messageProcessingResult.type === "STOP";
847
- const checkpointMessage = messageProcessingResult?.type === "STOP" ? messageProcessingResult.lastSuccessfulMessage : messagesAboveCheckpoint[messagesAboveCheckpoint.length - 1];
848
- if (checkpointMessage && checkpoints) {
849
- const storeCheckpointResult = await checkpoints.store(
850
- {
851
- processorId,
852
- version,
853
- message: checkpointMessage,
854
- lastCheckpoint,
855
- partition
856
- },
857
- context
858
- );
859
- if (storeCheckpointResult.success) {
860
- lastCheckpoint = storeCheckpointResult.newCheckpoint;
861
- }
862
- }
863
- if (isStop) {
864
- isActive = false;
865
- return messageProcessingResult;
866
- }
867
- return void 0;
868
- }, partialContext);
869
- }
870
- };
871
- };
872
- var projector = (options) => {
873
- const {
874
- projection: projection2,
875
- processorId = getProjectorId({
876
- projectionName: projection2.name ?? "unknown"
877
- }),
878
- ...rest
879
- } = options;
880
- return reactor({
881
- ...rest,
882
- type: MessageProcessorType.PROJECTOR,
883
- canHandle: projection2.canHandle,
884
- processorId,
885
- messageOptions: options.projection.eventsOptions,
886
- hooks: {
887
- onInit: options.hooks?.onInit,
888
- onStart: options.truncateOnStart && options.projection.truncate || options.hooks?.onStart ? async (context) => {
889
- if (options.truncateOnStart && options.projection.truncate)
890
- await options.projection.truncate(context);
891
- if (options.hooks?.onStart) await options.hooks?.onStart(context);
892
- } : void 0,
893
- onClose: options.hooks?.onClose
894
- },
895
- eachBatch: async (events, context) => projection2.handle(events, context)
896
- });
897
- };
898
- var inMemoryCheckpointer = () => {
899
- return {
900
- read: async ({ processorId }, { database }) => {
901
- const checkpoint = await database.collection("emt_processor_checkpoints").findOne((d) => d._id === processorId);
902
- return Promise.resolve({
903
- lastCheckpoint: checkpoint?.lastCheckpoint ?? null
904
- });
905
- },
906
- store: async (context, { database }) => {
907
- const { message: message2, processorId, lastCheckpoint } = context;
908
- const checkpoints = database.collection(
909
- "emt_processor_checkpoints"
910
- );
911
- const checkpoint = await checkpoints.findOne(
912
- (d) => d._id === processorId
913
- );
914
- const currentPosition = checkpoint?.lastCheckpoint ?? null;
915
- const newCheckpoint = getCheckpoint(message2);
916
- if (currentPosition && (currentPosition === newCheckpoint || currentPosition !== lastCheckpoint)) {
917
- return {
918
- success: false,
919
- reason: currentPosition === newCheckpoint ? "IGNORED" : newCheckpoint !== null && currentPosition > newCheckpoint ? "CURRENT_AHEAD" : "MISMATCH"
920
- };
921
- }
922
- await checkpoints.handle(processorId, (existing) => ({
923
- ...existing ?? {},
924
- _id: processorId,
925
- lastCheckpoint: newCheckpoint
926
- }));
927
- return { success: true, newCheckpoint };
928
- }
929
- };
930
- };
931
- var inMemoryProcessingScope = (options) => {
932
- const processorDatabase = options.database;
933
- const processingScope = (handler, partialContext) => {
934
- const database = processorDatabase ?? partialContext?.database;
935
- if (!database)
936
- throw new EmmettError(
937
- `InMemory processor '${options.processorId}' is missing database. Ensure that you passed it through options`
938
- );
939
- return handler({ ...partialContext, database });
940
- };
941
- return processingScope;
942
- };
943
- var inMemoryProjector = (options) => {
944
- const database = options.connectionOptions?.database ?? getInMemoryDatabase();
945
- const hooks = {
946
- onInit: options.hooks?.onInit,
947
- onStart: options.hooks?.onStart,
948
- onClose: options.hooks?.onClose ? async (context) => {
949
- if (options.hooks?.onClose) await options.hooks?.onClose(context);
950
- } : void 0
951
- };
952
- const processor = projector({
953
- ...options,
954
- hooks,
955
- processingScope: inMemoryProcessingScope({
956
- database,
957
- processorId: options.processorId ?? `projection:${options.projection.name}`
958
- }),
959
- checkpoints: inMemoryCheckpointer()
960
- });
961
- return Object.assign(processor, { database });
962
- };
963
- var inMemoryReactor = (options) => {
964
- const database = options.connectionOptions?.database ?? getInMemoryDatabase();
965
- const hooks = {
966
- onInit: options.hooks?.onInit,
967
- onStart: options.hooks?.onStart,
968
- onClose: options.hooks?.onClose
969
- };
970
- const processor = reactor({
971
- ...options,
972
- hooks,
973
- processingScope: inMemoryProcessingScope({
974
- database,
975
- processorId: options.processorId
976
- }),
977
- checkpoints: inMemoryCheckpointer()
978
- });
979
- return Object.assign(processor, { database });
980
- };
981
- var downcastRecordedMessage = (recordedMessage, options) => {
982
- if (!options?.downcast)
983
- return recordedMessage;
984
- const downcasted = options.downcast(
985
- recordedMessage
986
- );
987
- return {
988
- ...recordedMessage,
989
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
990
- data: downcasted.data,
991
- ..."metadata" in recordedMessage || "metadata" in downcasted ? {
992
- metadata: {
993
- ..."metadata" in recordedMessage ? recordedMessage.metadata : {},
994
- ..."metadata" in downcasted ? downcasted.metadata : {}
995
- }
996
- } : {}
997
- };
998
- };
999
- var downcastRecordedMessages = (recordedMessages, options) => {
1000
- if (!options?.downcast)
1001
- return recordedMessages;
1002
- return recordedMessages.map(
1003
- (recordedMessage) => downcastRecordedMessage(recordedMessage, options)
1004
- );
1005
- };
1006
- var upcastRecordedMessage = (recordedMessage, options) => {
1007
- if (!options?.upcast)
1008
- return recordedMessage;
1009
- const upcasted = options.upcast(
1010
- recordedMessage
1011
- );
1012
- return {
1013
- ...recordedMessage,
1014
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
1015
- data: upcasted.data,
1016
- ..."metadata" in recordedMessage || "metadata" in upcasted ? {
1017
- metadata: {
1018
- ..."metadata" in recordedMessage ? recordedMessage.metadata : {},
1019
- ..."metadata" in upcasted ? upcasted.metadata : {}
1020
- }
1021
- } : {}
1022
- };
1023
- };
1024
-
1025
- // src/eventStore/consumers/eventStoreDBEventStoreConsumer.ts
1026
- import {
1027
- EventStoreDBClient
1028
- } from "@eventstore/db-client";
1029
- import { v7 as uuid7 } from "uuid";
1
+ import { EmmettError, ExpectedVersionConflictError, JSONSerializer, NO_CONCURRENCY_CHECK, STREAM_DOES_NOT_EXIST, STREAM_EXISTS, assertExpectedVersionMatchesCurrent, asyncAwaiter, asyncRetry, bigIntProcessorCheckpoint, downcastRecordedMessages, getCheckpoint, inMemoryProjector, inMemoryReactor, isString, parseBigIntProcessorCheckpoint, upcastRecordedMessage } from "@event-driven-io/emmett";
2
+ import { ANY, END, EventStoreDBClient, NO_STREAM, START, STREAM_EXISTS as STREAM_EXISTS$1, StreamNotFoundError, WrongExpectedVersionError, excludeSystemEvents, jsonEvent } from "@eventstore/db-client";
3
+ import { v7 } from "uuid";
4
+ import { Transform, Writable, pipeline } from "stream";
1030
5
 
1031
- // src/eventStore/consumers/subscriptions/index.ts
1032
- import {
1033
- END,
1034
- excludeSystemEvents,
1035
- START
1036
- } from "@eventstore/db-client";
1037
- import { pipeline, Transform, Writable } from "stream";
1038
-
1039
- // src/eventStore/eventstoreDBEventStore.ts
1040
- import {
1041
- ANY,
1042
- STREAM_EXISTS as ESDB_STREAM_EXISTS,
1043
- NO_STREAM,
1044
- StreamNotFoundError,
1045
- WrongExpectedVersionError,
1046
- jsonEvent
1047
- } from "@eventstore/db-client";
1048
- var toEventStoreDBReadOptions = (options) => {
1049
- return options ? {
1050
- fromRevision: "from" in options ? options.from : void 0,
1051
- maxCount: "maxCount" in options ? options.maxCount : "to" in options ? options.to : void 0
1052
- } : void 0;
1053
- };
1054
- var EventStoreDBEventStoreDefaultStreamVersion = -1n;
1055
- var getEventStoreDBEventStore = (eventStore) => {
1056
- return {
1057
- async aggregateStream(streamName, options) {
1058
- const { evolve, initialState, read } = options;
1059
- const expectedStreamVersion = read?.expectedStreamVersion;
1060
- let state = initialState();
1061
- let currentStreamVersion = EventStoreDBEventStoreDefaultStreamVersion;
1062
- let lastEventGlobalPosition = void 0;
1063
- try {
1064
- for await (const resolvedEvent of eventStore.readStream(
1065
- streamName,
1066
- toEventStoreDBReadOptions(options.read)
1067
- )) {
1068
- const { event } = resolvedEvent;
1069
- if (!event) continue;
1070
- state = evolve(
1071
- state,
1072
- upcastRecordedMessage(
1073
- mapFromESDBEvent(resolvedEvent),
1074
- options?.read?.schema?.versioning
1075
- )
1076
- );
1077
- currentStreamVersion = event.revision;
1078
- lastEventGlobalPosition = event.position?.commit;
1079
- }
1080
- assertExpectedVersionMatchesCurrent(
1081
- currentStreamVersion,
1082
- expectedStreamVersion,
1083
- EventStoreDBEventStoreDefaultStreamVersion
1084
- );
1085
- return lastEventGlobalPosition ? {
1086
- currentStreamVersion,
1087
- lastEventGlobalPosition,
1088
- state,
1089
- streamExists: true
1090
- } : {
1091
- currentStreamVersion,
1092
- state,
1093
- streamExists: false
1094
- };
1095
- } catch (error) {
1096
- if (error instanceof StreamNotFoundError) {
1097
- return {
1098
- currentStreamVersion,
1099
- state,
1100
- streamExists: false
1101
- };
1102
- }
1103
- throw error;
1104
- }
1105
- },
1106
- readStream: async (streamName, options) => {
1107
- const events = [];
1108
- let currentStreamVersion = EventStoreDBEventStoreDefaultStreamVersion;
1109
- try {
1110
- for await (const resolvedEvent of eventStore.readStream(
1111
- streamName,
1112
- toEventStoreDBReadOptions(options)
1113
- )) {
1114
- const { event } = resolvedEvent;
1115
- if (!event) continue;
1116
- events.push(
1117
- upcastRecordedMessage(
1118
- mapFromESDBEvent(resolvedEvent),
1119
- options?.schema?.versioning
1120
- )
1121
- );
1122
- currentStreamVersion = event.revision;
1123
- }
1124
- return {
1125
- currentStreamVersion,
1126
- events,
1127
- streamExists: true
1128
- };
1129
- } catch (error) {
1130
- if (error instanceof StreamNotFoundError) {
1131
- return {
1132
- currentStreamVersion,
1133
- events: [],
1134
- streamExists: false
1135
- };
1136
- }
1137
- throw error;
1138
- }
1139
- },
1140
- appendToStream: async (streamName, events, options) => {
1141
- try {
1142
- const eventsToStore = downcastRecordedMessages(
1143
- events,
1144
- options?.schema?.versioning
1145
- );
1146
- const serializedEvents = eventsToStore.map(jsonEvent);
1147
- const expectedRevision = toExpectedRevision(
1148
- options?.expectedStreamVersion
1149
- );
1150
- const appendResult = await eventStore.appendToStream(
1151
- streamName,
1152
- serializedEvents,
1153
- {
1154
- expectedRevision
1155
- }
1156
- );
1157
- return {
1158
- nextExpectedStreamVersion: appendResult.nextExpectedRevision,
1159
- lastEventGlobalPosition: appendResult.position.commit,
1160
- createdNewStream: appendResult.nextExpectedRevision >= BigInt(serializedEvents.length)
1161
- };
1162
- } catch (error) {
1163
- if (error instanceof WrongExpectedVersionError) {
1164
- throw new ExpectedVersionConflictError(
1165
- BigInt(error.actualVersion),
1166
- toExpectedVersion(error.expectedVersion)
1167
- );
1168
- }
1169
- throw error;
1170
- }
1171
- },
1172
- consumer: (options) => eventStoreDBEventStoreConsumer({
1173
- ...options ?? {},
1174
- client: eventStore
1175
- }),
1176
- streamExists: async (streamName) => {
1177
- try {
1178
- for await (const resolvedEvent of eventStore.readStream(streamName)) {
1179
- const { event } = resolvedEvent;
1180
- if (!event) continue;
1181
- return true;
1182
- }
1183
- return false;
1184
- } catch (error) {
1185
- if (error instanceof StreamNotFoundError) {
1186
- return false;
1187
- }
1188
- throw error;
1189
- }
1190
- }
1191
- //streamEvents: streamEvents(eventStore),
1192
- };
1193
- };
1194
- var getESDBCheckpoint = (resolvedEvent, from) => {
1195
- return !from || from?.stream === $all ? resolvedEvent.link?.position?.commit ?? resolvedEvent.event?.position?.commit : resolvedEvent.link?.revision ?? resolvedEvent.event.revision;
1196
- };
1197
- var mapFromESDBEvent = (resolvedEvent, from) => {
1198
- const event = resolvedEvent.event;
1199
- return {
1200
- type: event.type,
1201
- data: event.data,
1202
- metadata: {
1203
- ...event.metadata ?? {},
1204
- eventId: event.id,
1205
- streamName: event.streamId,
1206
- streamPosition: event.revision,
1207
- globalPosition: event.position.commit,
1208
- checkpoint: bigIntProcessorCheckpoint(
1209
- getESDBCheckpoint(resolvedEvent, from)
1210
- )
1211
- }
1212
- };
1213
- };
1214
- var toExpectedRevision = (expected) => {
1215
- if (expected === void 0) return ANY;
1216
- if (expected === NO_CONCURRENCY_CHECK) return ANY;
1217
- if (expected == STREAM_DOES_NOT_EXIST) return NO_STREAM;
1218
- if (expected == STREAM_EXISTS) return ESDB_STREAM_EXISTS;
1219
- return expected;
1220
- };
1221
- var toExpectedVersion = (expected) => {
1222
- if (expected === void 0) return NO_CONCURRENCY_CHECK;
1223
- if (expected === ANY) return NO_CONCURRENCY_CHECK;
1224
- if (expected == NO_STREAM) return STREAM_DOES_NOT_EXIST;
1225
- if (expected == ESDB_STREAM_EXISTS) return STREAM_EXISTS;
1226
- return expected;
6
+ //#region src/eventStore/eventstoreDBEventStore.ts
7
+ const toEventStoreDBReadOptions = (options) => {
8
+ return options ? {
9
+ fromRevision: "from" in options ? options.from : void 0,
10
+ maxCount: "maxCount" in options ? options.maxCount : "to" in options ? options.to : void 0
11
+ } : void 0;
12
+ };
13
+ const EventStoreDBEventStoreDefaultStreamVersion = -1n;
14
+ const getEventStoreDBEventStore = (eventStore) => {
15
+ return {
16
+ async aggregateStream(streamName, options) {
17
+ const { evolve, initialState, read } = options;
18
+ const expectedStreamVersion = read?.expectedStreamVersion;
19
+ let state = initialState();
20
+ let currentStreamVersion = EventStoreDBEventStoreDefaultStreamVersion;
21
+ let lastEventGlobalPosition = void 0;
22
+ try {
23
+ for await (const resolvedEvent of eventStore.readStream(streamName, toEventStoreDBReadOptions(options.read))) {
24
+ const { event } = resolvedEvent;
25
+ if (!event) continue;
26
+ state = evolve(state, upcastRecordedMessage(mapFromESDBEvent(resolvedEvent), options?.read?.schema?.versioning));
27
+ currentStreamVersion = event.revision;
28
+ lastEventGlobalPosition = event.position?.commit;
29
+ }
30
+ assertExpectedVersionMatchesCurrent(currentStreamVersion, expectedStreamVersion, EventStoreDBEventStoreDefaultStreamVersion);
31
+ return lastEventGlobalPosition ? {
32
+ currentStreamVersion,
33
+ lastEventGlobalPosition,
34
+ state,
35
+ streamExists: true
36
+ } : {
37
+ currentStreamVersion,
38
+ state,
39
+ streamExists: false
40
+ };
41
+ } catch (error) {
42
+ if (error instanceof StreamNotFoundError) return {
43
+ currentStreamVersion,
44
+ state,
45
+ streamExists: false
46
+ };
47
+ throw error;
48
+ }
49
+ },
50
+ readStream: async (streamName, options) => {
51
+ const events = [];
52
+ let currentStreamVersion = EventStoreDBEventStoreDefaultStreamVersion;
53
+ try {
54
+ for await (const resolvedEvent of eventStore.readStream(streamName, toEventStoreDBReadOptions(options))) {
55
+ const { event } = resolvedEvent;
56
+ if (!event) continue;
57
+ events.push(upcastRecordedMessage(mapFromESDBEvent(resolvedEvent), options?.schema?.versioning));
58
+ currentStreamVersion = event.revision;
59
+ }
60
+ return {
61
+ currentStreamVersion,
62
+ events,
63
+ streamExists: true
64
+ };
65
+ } catch (error) {
66
+ if (error instanceof StreamNotFoundError) return {
67
+ currentStreamVersion,
68
+ events: [],
69
+ streamExists: false
70
+ };
71
+ throw error;
72
+ }
73
+ },
74
+ appendToStream: async (streamName, events, options) => {
75
+ try {
76
+ const serializedEvents = downcastRecordedMessages(events, options?.schema?.versioning).map(jsonEvent);
77
+ const expectedRevision = toExpectedRevision(options?.expectedStreamVersion);
78
+ const appendResult = await eventStore.appendToStream(streamName, serializedEvents, { expectedRevision });
79
+ return {
80
+ nextExpectedStreamVersion: appendResult.nextExpectedRevision,
81
+ lastEventGlobalPosition: appendResult.position.commit,
82
+ createdNewStream: appendResult.nextExpectedRevision >= BigInt(serializedEvents.length)
83
+ };
84
+ } catch (error) {
85
+ if (error instanceof WrongExpectedVersionError) throw new ExpectedVersionConflictError(BigInt(error.actualVersion), toExpectedVersion(error.expectedVersion));
86
+ throw error;
87
+ }
88
+ },
89
+ consumer: (options) => eventStoreDBEventStoreConsumer({
90
+ ...options ?? {},
91
+ client: eventStore
92
+ }),
93
+ streamExists: async (streamName) => {
94
+ try {
95
+ for await (const resolvedEvent of eventStore.readStream(streamName)) {
96
+ const { event } = resolvedEvent;
97
+ if (!event) continue;
98
+ return true;
99
+ }
100
+ return false;
101
+ } catch (error) {
102
+ if (error instanceof StreamNotFoundError) return false;
103
+ throw error;
104
+ }
105
+ }
106
+ };
107
+ };
108
+ const getESDBCheckpoint = (resolvedEvent, from) => {
109
+ return !from || from?.stream === "$all" ? resolvedEvent.link?.position?.commit ?? resolvedEvent.event?.position?.commit : resolvedEvent.link?.revision ?? resolvedEvent.event.revision;
110
+ };
111
+ const mapFromESDBEvent = (resolvedEvent, from) => {
112
+ const event = resolvedEvent.event;
113
+ return {
114
+ type: event.type,
115
+ data: event.data,
116
+ metadata: {
117
+ ...event.metadata ?? {},
118
+ eventId: event.id,
119
+ streamName: event.streamId,
120
+ streamPosition: event.revision,
121
+ globalPosition: event.position.commit,
122
+ checkpoint: bigIntProcessorCheckpoint(getESDBCheckpoint(resolvedEvent, from))
123
+ }
124
+ };
125
+ };
126
+ const toExpectedRevision = (expected) => {
127
+ if (expected === void 0) return ANY;
128
+ if (expected === NO_CONCURRENCY_CHECK) return ANY;
129
+ if (expected == STREAM_DOES_NOT_EXIST) return NO_STREAM;
130
+ if (expected == STREAM_EXISTS) return STREAM_EXISTS$1;
131
+ return expected;
132
+ };
133
+ const toExpectedVersion = (expected) => {
134
+ if (expected === void 0) return NO_CONCURRENCY_CHECK;
135
+ if (expected === ANY) return NO_CONCURRENCY_CHECK;
136
+ if (expected == NO_STREAM) return STREAM_DOES_NOT_EXIST;
137
+ if (expected == STREAM_EXISTS$1) return STREAM_EXISTS;
138
+ return expected;
1227
139
  };
1228
140
 
1229
- // src/eventStore/consumers/subscriptions/index.ts
1230
- var DefaultEventStoreDBEventStoreProcessorBatchSize = 100;
1231
- var DefaultEventStoreDBEventStoreProcessorPullingFrequencyInMs = 50;
1232
- var toGlobalPosition = (startFrom) => startFrom === "BEGINNING" ? START : startFrom === "END" ? END : {
1233
- prepare: parseBigIntProcessorCheckpoint(startFrom.lastCheckpoint),
1234
- commit: parseBigIntProcessorCheckpoint(startFrom.lastCheckpoint)
1235
- };
1236
- var toStreamPosition = (startFrom) => startFrom === "BEGINNING" ? START : startFrom === "END" ? END : parseBigIntProcessorCheckpoint(startFrom.lastCheckpoint);
1237
- var subscribe = (client, from, options) => from == void 0 || from.stream == $all ? client.subscribeToAll({
1238
- ...from?.options ?? {},
1239
- fromPosition: toGlobalPosition(options.startFrom),
1240
- filter: excludeSystemEvents()
141
+ //#endregion
142
+ //#region src/eventStore/consumers/subscriptions/index.ts
143
+ const DefaultEventStoreDBEventStoreProcessorBatchSize = 100;
144
+ const DefaultEventStoreDBEventStoreProcessorPullingFrequencyInMs = 50;
145
+ const toGlobalPosition = (startFrom) => startFrom === "BEGINNING" ? START : startFrom === "END" ? END : {
146
+ prepare: parseBigIntProcessorCheckpoint(startFrom.lastCheckpoint),
147
+ commit: parseBigIntProcessorCheckpoint(startFrom.lastCheckpoint)
148
+ };
149
+ const toStreamPosition = (startFrom) => startFrom === "BEGINNING" ? START : startFrom === "END" ? END : parseBigIntProcessorCheckpoint(startFrom.lastCheckpoint);
150
+ const subscribe = (client, from, options) => from == void 0 || from.stream == "$all" ? client.subscribeToAll({
151
+ ...from?.options ?? {},
152
+ fromPosition: toGlobalPosition(options.startFrom),
153
+ filter: excludeSystemEvents()
1241
154
  }) : client.subscribeToStream(from.stream, {
1242
- ...from.options ?? {},
1243
- fromRevision: toStreamPosition(options.startFrom)
155
+ ...from.options ?? {},
156
+ fromRevision: toStreamPosition(options.startFrom)
1244
157
  });
1245
- var isDatabaseUnavailableError = (error) => error instanceof Error && "type" in error && error.type === "unavailable" && "code" in error && error.code === 14;
1246
- var EventStoreDBResubscribeDefaultOptions = {
1247
- forever: true,
1248
- minTimeout: 100,
1249
- factor: 1.5,
1250
- shouldRetryError: (error) => !isDatabaseUnavailableError(error)
158
+ const isDatabaseUnavailableError = (error) => error instanceof Error && "type" in error && error.type === "unavailable" && "code" in error && error.code === 14;
159
+ const EventStoreDBResubscribeDefaultOptions = {
160
+ forever: true,
161
+ minTimeout: 100,
162
+ factor: 1.5,
163
+ shouldRetryError: (error) => !isDatabaseUnavailableError(error)
1251
164
  };
1252
165
  var SubscriptionSequentialHandler = class extends Transform {
1253
- options;
1254
- from;
1255
- isRunning;
1256
- constructor(options) {
1257
- super({ objectMode: true, ...options });
1258
- this.options = options;
1259
- this.from = options.from;
1260
- this.isRunning = true;
1261
- }
1262
- async _transform(resolvedEvent, _encoding, callback) {
1263
- try {
1264
- if (!this.isRunning || !resolvedEvent.event) {
1265
- callback();
1266
- return;
1267
- }
1268
- const message = mapFromESDBEvent(resolvedEvent, this.from);
1269
- const messageCheckpoint = getCheckpoint(message);
1270
- const result = await this.options.eachBatch([message]);
1271
- if (result && result.type === "STOP") {
1272
- this.isRunning = false;
1273
- if (!result.error) this.push(messageCheckpoint);
1274
- this.push(result);
1275
- this.push(null);
1276
- callback();
1277
- return;
1278
- }
1279
- this.push(messageCheckpoint);
1280
- callback();
1281
- } catch (error) {
1282
- callback(error);
1283
- }
1284
- }
1285
- };
1286
- var eventStoreDBSubscription = ({
1287
- client,
1288
- from,
1289
- batchSize,
1290
- eachBatch,
1291
- resilience
1292
- }) => {
1293
- let isRunning = false;
1294
- let start;
1295
- let processor;
1296
- let subscription;
1297
- const resubscribeOptions = resilience?.resubscribeOptions ?? {
1298
- ...EventStoreDBResubscribeDefaultOptions,
1299
- shouldRetryResult: () => isRunning,
1300
- shouldRetryError: (error) => isRunning && EventStoreDBResubscribeDefaultOptions.shouldRetryError(error)
1301
- };
1302
- const stopSubscription = (callback) => {
1303
- isRunning = false;
1304
- if (processor) processor.isRunning = false;
1305
- return subscription.unsubscribe().then(() => {
1306
- subscription.destroy();
1307
- }).catch((err) => console.error("Error during unsubscribe.%s", err)).finally(callback ?? (() => {
1308
- }));
1309
- };
1310
- const pipeMessages = (options) => {
1311
- let retry2 = 0;
1312
- return asyncRetry(
1313
- () => new Promise((resolve, reject) => {
1314
- if (!isRunning) {
1315
- resolve();
1316
- return;
1317
- }
1318
- console.info(
1319
- `Starting subscription. ${retry2++} retries. From: ${JSONSerializer.serialize(from ?? "$all")}, Start from: ${JSONSerializer.serialize(
1320
- options.startFrom
1321
- )}`
1322
- );
1323
- subscription = subscribe(client, from, options);
1324
- processor = new SubscriptionSequentialHandler({
1325
- client,
1326
- from,
1327
- batchSize,
1328
- eachBatch,
1329
- resilience
1330
- });
1331
- const handler = new class extends Writable {
1332
- async _write(result, _encoding, done) {
1333
- if (!isRunning) return;
1334
- if (isString(result)) {
1335
- options.startFrom = {
1336
- lastCheckpoint: result
1337
- };
1338
- done();
1339
- return;
1340
- }
1341
- if (result && result.type === "STOP" && result.error) {
1342
- console.error(
1343
- `Subscription stopped with error code: ${result.error.errorCode}, message: ${result.error.message}.`
1344
- );
1345
- }
1346
- await stopSubscription();
1347
- done();
1348
- }
1349
- }({ objectMode: true });
1350
- pipeline(
1351
- subscription,
1352
- processor,
1353
- handler,
1354
- async (error) => {
1355
- console.info(`Stopping subscription.`);
1356
- await stopSubscription(() => {
1357
- if (!error) {
1358
- console.info("Subscription ended successfully.");
1359
- resolve();
1360
- return;
1361
- }
1362
- console.error(
1363
- `Received error: ${JSONSerializer.serialize(error)}.`
1364
- );
1365
- reject(error);
1366
- });
1367
- }
1368
- );
1369
- }),
1370
- resubscribeOptions
1371
- );
1372
- };
1373
- return {
1374
- get isRunning() {
1375
- return isRunning;
1376
- },
1377
- start: (options) => {
1378
- if (isRunning) return start;
1379
- start = (async () => {
1380
- isRunning = true;
1381
- return pipeMessages(options);
1382
- })();
1383
- return start;
1384
- },
1385
- stop: async () => {
1386
- if (!isRunning) return start ? await start : Promise.resolve();
1387
- await stopSubscription();
1388
- await start;
1389
- }
1390
- };
1391
- };
1392
- var zipEventStoreDBEventStoreMessageBatchPullerStartFrom = (options) => {
1393
- if (options.length === 0 || options.some((o) => o === void 0 || o === "BEGINNING"))
1394
- return "BEGINNING";
1395
- if (options.every((o) => o === "END")) return "END";
1396
- return options.filter((o) => o !== void 0 && o !== "BEGINNING" && o !== "END").sort((a, b) => a > b ? 1 : -1)[0];
166
+ options;
167
+ from;
168
+ isRunning;
169
+ constructor(options) {
170
+ super({
171
+ objectMode: true,
172
+ ...options
173
+ });
174
+ this.options = options;
175
+ this.from = options.from;
176
+ this.isRunning = true;
177
+ }
178
+ async _transform(resolvedEvent, _encoding, callback) {
179
+ try {
180
+ if (!this.isRunning || !resolvedEvent.event) {
181
+ callback();
182
+ return;
183
+ }
184
+ const message = mapFromESDBEvent(resolvedEvent, this.from);
185
+ const messageCheckpoint = getCheckpoint(message);
186
+ const result = await this.options.eachBatch([message]);
187
+ if (result && result.type === "STOP") {
188
+ this.isRunning = false;
189
+ if (!result.error) this.push(messageCheckpoint);
190
+ this.push(result);
191
+ this.push(null);
192
+ callback();
193
+ return;
194
+ }
195
+ this.push(messageCheckpoint);
196
+ callback();
197
+ } catch (error) {
198
+ callback(error);
199
+ }
200
+ }
201
+ };
202
+ const eventStoreDBSubscription = ({ client, from, batchSize, eachBatch, resilience }) => {
203
+ let isRunning = false;
204
+ let start;
205
+ let processor;
206
+ let subscription;
207
+ const resubscribeOptions = resilience?.resubscribeOptions ?? {
208
+ ...EventStoreDBResubscribeDefaultOptions,
209
+ shouldRetryResult: () => isRunning,
210
+ shouldRetryError: (error) => isRunning && EventStoreDBResubscribeDefaultOptions.shouldRetryError(error)
211
+ };
212
+ const stopSubscription = (callback) => {
213
+ isRunning = false;
214
+ if (processor) processor.isRunning = false;
215
+ return subscription.unsubscribe().then(() => {
216
+ subscription.destroy();
217
+ }).catch((err) => console.error("Error during unsubscribe.%s", err)).finally(callback ?? (() => {}));
218
+ };
219
+ const pipeMessages = (options) => {
220
+ let retry = 0;
221
+ return asyncRetry(() => new Promise((resolve, reject) => {
222
+ if (!isRunning) {
223
+ resolve();
224
+ return;
225
+ }
226
+ console.info(`Starting subscription. ${retry++} retries. From: ${JSONSerializer.serialize(from ?? "$all")}, Start from: ${JSONSerializer.serialize(options.startFrom)}`);
227
+ subscription = subscribe(client, from, options);
228
+ subscription.once("confirmation", () => options.started?.resolve());
229
+ processor = new SubscriptionSequentialHandler({
230
+ client,
231
+ from,
232
+ batchSize,
233
+ eachBatch,
234
+ resilience
235
+ });
236
+ const handler = new class extends Writable {
237
+ async _write(result, _encoding, done) {
238
+ if (!isRunning) return;
239
+ if (isString(result)) {
240
+ options.startFrom = { lastCheckpoint: result };
241
+ done();
242
+ return;
243
+ }
244
+ if (result && result.type === "STOP" && result.error) console.error(`Subscription stopped with error code: ${result.error.errorCode}, message: ${result.error.message}.`);
245
+ await stopSubscription();
246
+ done();
247
+ }
248
+ }({ objectMode: true });
249
+ pipeline(subscription, processor, handler, async (error) => {
250
+ console.info(`Stopping subscription.`);
251
+ await stopSubscription(() => {
252
+ if (!error) {
253
+ console.info("Subscription ended successfully.");
254
+ resolve();
255
+ return;
256
+ }
257
+ console.error(`Received error: ${JSONSerializer.serialize(error)}.`);
258
+ options.started?.reject(error);
259
+ reject(error);
260
+ });
261
+ });
262
+ }), resubscribeOptions);
263
+ };
264
+ return {
265
+ get isRunning() {
266
+ return isRunning;
267
+ },
268
+ start: (options) => {
269
+ if (isRunning) return start;
270
+ start = (async () => {
271
+ isRunning = true;
272
+ return pipeMessages(options);
273
+ })();
274
+ return start;
275
+ },
276
+ stop: async () => {
277
+ if (!isRunning) return start ? await start : Promise.resolve();
278
+ await stopSubscription();
279
+ await start;
280
+ }
281
+ };
282
+ };
283
+ const zipEventStoreDBEventStoreMessageBatchPullerStartFrom = (options) => {
284
+ if (options.length === 0 || options.some((o) => o === void 0 || o === "BEGINNING")) return "BEGINNING";
285
+ if (options.every((o) => o === "END")) return "END";
286
+ return options.filter((o) => o !== void 0 && o !== "BEGINNING" && o !== "END").sort((a, b) => a > b ? 1 : -1)[0];
1397
287
  };
1398
288
 
1399
- // src/eventStore/consumers/eventStoreDBEventStoreConsumer.ts
1400
- var $all = "$all";
1401
- var eventStoreDBEventStoreConsumer = (options) => {
1402
- let isRunning = false;
1403
- const { pulling } = options;
1404
- const processors = options.processors ?? [];
1405
- let start;
1406
- let currentSubscription;
1407
- const client = "client" in options && options.client ? options.client : EventStoreDBClient.connectionString(options.connectionString);
1408
- const eachBatch = async (messagesBatch) => {
1409
- const activeProcessors = processors.filter((s) => s.isActive);
1410
- if (activeProcessors.length === 0)
1411
- return {
1412
- type: "STOP",
1413
- reason: "No active processors"
1414
- };
1415
- const result = await Promise.allSettled(
1416
- activeProcessors.map(async (s) => {
1417
- return await s.handle(messagesBatch, { client });
1418
- })
1419
- );
1420
- const error = result.find((r) => r.status === "rejected")?.reason;
1421
- return result.some(
1422
- (r) => r.status === "fulfilled" && r.value?.type !== "STOP"
1423
- ) ? void 0 : {
1424
- type: "STOP",
1425
- error: error ? EmmettError.mapFrom(error) : void 0
1426
- };
1427
- };
1428
- const subscription = currentSubscription = eventStoreDBSubscription({
1429
- client,
1430
- from: options.from,
1431
- eachBatch,
1432
- batchSize: pulling?.batchSize ?? DefaultEventStoreDBEventStoreProcessorBatchSize,
1433
- resilience: options.resilience
1434
- });
1435
- const stop = async () => {
1436
- if (!isRunning) return;
1437
- isRunning = false;
1438
- if (currentSubscription) {
1439
- await currentSubscription.stop();
1440
- currentSubscription = void 0;
1441
- }
1442
- await start;
1443
- };
1444
- return {
1445
- consumerId: options.consumerId ?? uuid7(),
1446
- get isRunning() {
1447
- return isRunning;
1448
- },
1449
- processors,
1450
- reactor: (options2) => {
1451
- const processor = inMemoryReactor(options2);
1452
- processors.push(
1453
- // TODO: change that
1454
- processor
1455
- );
1456
- return processor;
1457
- },
1458
- projector: (options2) => {
1459
- const processor = inMemoryProjector(options2);
1460
- processors.push(
1461
- // TODO: change that
1462
- processor
1463
- );
1464
- return processor;
1465
- },
1466
- start: () => {
1467
- if (isRunning) return start;
1468
- start = (async () => {
1469
- if (processors.length === 0)
1470
- return Promise.reject(
1471
- new EmmettError(
1472
- "Cannot start consumer without at least a single processor"
1473
- )
1474
- );
1475
- isRunning = true;
1476
- const startFrom = zipEventStoreDBEventStoreMessageBatchPullerStartFrom(
1477
- await Promise.all(processors.map((o) => o.start(client)))
1478
- );
1479
- return subscription.start({ startFrom });
1480
- })();
1481
- return start;
1482
- },
1483
- stop,
1484
- close: stop
1485
- };
1486
- };
1487
- export {
1488
- $all,
1489
- DefaultEventStoreDBEventStoreProcessorBatchSize,
1490
- DefaultEventStoreDBEventStoreProcessorPullingFrequencyInMs,
1491
- EventStoreDBEventStoreDefaultStreamVersion,
1492
- EventStoreDBResubscribeDefaultOptions,
1493
- eventStoreDBEventStoreConsumer,
1494
- eventStoreDBSubscription,
1495
- getEventStoreDBEventStore,
1496
- isDatabaseUnavailableError,
1497
- mapFromESDBEvent,
1498
- zipEventStoreDBEventStoreMessageBatchPullerStartFrom
289
+ //#endregion
290
+ //#region src/eventStore/consumers/eventStoreDBEventStoreConsumer.ts
291
+ const $all = "$all";
292
+ const eventStoreDBEventStoreConsumer = (options) => {
293
+ let isRunning = false;
294
+ let isInitialized = false;
295
+ const { pulling } = options;
296
+ const processors = options.processors ?? [];
297
+ let abortController = null;
298
+ let start;
299
+ let currentSubscription;
300
+ const startedAwaiter = asyncAwaiter();
301
+ const client = "client" in options && options.client ? options.client : EventStoreDBClient.connectionString(options.connectionString);
302
+ const eachBatch = async (messagesBatch) => {
303
+ const activeProcessors = processors.filter((s) => s.isActive);
304
+ if (activeProcessors.length === 0) return {
305
+ type: "STOP",
306
+ reason: "No active processors"
307
+ };
308
+ const result = await Promise.allSettled(activeProcessors.map(async (s) => {
309
+ return await s.handle(messagesBatch, { client });
310
+ }));
311
+ const error = result.find((r) => r.status === "rejected")?.reason;
312
+ return result.some((r) => r.status === "fulfilled" && r.value?.type !== "STOP") ? void 0 : {
313
+ type: "STOP",
314
+ error: error ? EmmettError.mapFrom(error) : void 0
315
+ };
316
+ };
317
+ const subscription = currentSubscription = eventStoreDBSubscription({
318
+ client,
319
+ from: options.from,
320
+ eachBatch,
321
+ batchSize: pulling?.batchSize ?? 100,
322
+ resilience: options.resilience
323
+ });
324
+ const init = async () => {
325
+ if (isInitialized) return;
326
+ for (const processor of processors) await processor.init({});
327
+ isInitialized = true;
328
+ };
329
+ const stopProcessors = () => Promise.all(processors.map((p) => p.close({})));
330
+ const stop = async () => {
331
+ if (!isRunning) return;
332
+ isRunning = false;
333
+ abortController?.abort();
334
+ if (currentSubscription) {
335
+ await currentSubscription.stop();
336
+ currentSubscription = void 0;
337
+ }
338
+ await start;
339
+ abortController = null;
340
+ await stopProcessors();
341
+ };
342
+ return {
343
+ consumerId: options.consumerId ?? v7(),
344
+ get isRunning() {
345
+ return isRunning;
346
+ },
347
+ whenStarted: () => startedAwaiter.wait,
348
+ processors,
349
+ reactor: (options) => {
350
+ const processor = inMemoryReactor(options);
351
+ processors.push(processor);
352
+ return processor;
353
+ },
354
+ projector: (options) => {
355
+ const processor = inMemoryProjector(options);
356
+ processors.push(processor);
357
+ return processor;
358
+ },
359
+ start: () => {
360
+ if (isRunning) return start;
361
+ startedAwaiter.reset();
362
+ if (processors.length === 0) {
363
+ const error = new EmmettError("Cannot start consumer without at least a single processor");
364
+ startedAwaiter.reject(error);
365
+ return Promise.reject(error);
366
+ }
367
+ isRunning = true;
368
+ abortController = new AbortController();
369
+ start = (async () => {
370
+ if (!isRunning) return;
371
+ try {
372
+ if (!isInitialized) await init();
373
+ const startFrom = zipEventStoreDBEventStoreMessageBatchPullerStartFrom(await Promise.all(processors.map((o) => o.start(client))));
374
+ await subscription.start({
375
+ startFrom,
376
+ started: startedAwaiter
377
+ });
378
+ } catch (error) {
379
+ isRunning = false;
380
+ startedAwaiter.reject(error);
381
+ throw error;
382
+ } finally {
383
+ await stopProcessors();
384
+ }
385
+ })();
386
+ return start;
387
+ },
388
+ stop,
389
+ close: stop
390
+ };
1499
391
  };
392
+
393
+ //#endregion
394
+ export { $all, DefaultEventStoreDBEventStoreProcessorBatchSize, DefaultEventStoreDBEventStoreProcessorPullingFrequencyInMs, EventStoreDBEventStoreDefaultStreamVersion, EventStoreDBResubscribeDefaultOptions, eventStoreDBEventStoreConsumer, eventStoreDBSubscription, getEventStoreDBEventStore, isDatabaseUnavailableError, mapFromESDBEvent, zipEventStoreDBEventStoreMessageBatchPullerStartFrom };
1500
395
  //# sourceMappingURL=index.js.map