@event-driven-io/emmett-esdb 0.43.0-beta.2 → 0.43.0-beta.21

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