@event-driven-io/emmett-esdb 0.36.0 → 0.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +93 -37
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -4
- package/dist/index.d.ts +12 -4
- package/dist/index.js +81 -25
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -15,7 +15,7 @@ var ConcurrencyError = class _ConcurrencyError extends EmmettError {
|
|
|
15
15
|
constructor(current, expected, message) {
|
|
16
16
|
super({
|
|
17
17
|
errorCode: 412,
|
|
18
|
-
message: _nullishCoalesce(message, () => ( `Expected version ${expected.toString()} does not match current ${_optionalChain([current, 'optionalAccess',
|
|
18
|
+
message: _nullishCoalesce(message, () => ( `Expected version ${expected.toString()} does not match current ${_optionalChain([current, 'optionalAccess', _2 => _2.toString, 'call', _3 => _3()])}`))
|
|
19
19
|
});
|
|
20
20
|
this.current = current;
|
|
21
21
|
this.expected = expected;
|
|
@@ -60,7 +60,7 @@ var assertExpectedVersionMatchesCurrent = (current, expected, defaultVersion) =>
|
|
|
60
60
|
};
|
|
61
61
|
var ExpectedVersionConflictError = class _ExpectedVersionConflictError extends ConcurrencyError {
|
|
62
62
|
constructor(current, expected) {
|
|
63
|
-
super(_optionalChain([current, 'optionalAccess',
|
|
63
|
+
super(_optionalChain([current, 'optionalAccess', _4 => _4.toString, 'call', _5 => _5()]), _optionalChain([expected, 'optionalAccess', _6 => _6.toString, 'call', _7 => _7()]));
|
|
64
64
|
Object.setPrototypeOf(this, _ExpectedVersionConflictError.prototype);
|
|
65
65
|
}
|
|
66
66
|
};
|
|
@@ -74,9 +74,9 @@ var NotifyAboutNoActiveReadersStream = (_class = class extends _webstreamspolyfi
|
|
|
74
74
|
}
|
|
75
75
|
});_class.prototype.__init.call(this);_class.prototype.__init2.call(this);;
|
|
76
76
|
this.onNoActiveReaderCallback = onNoActiveReaderCallback;
|
|
77
|
-
this.streamId = _nullishCoalesce(_optionalChain([options, 'optionalAccess',
|
|
77
|
+
this.streamId = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _8 => _8.streamId]), () => ( _uuid.v4.call(void 0, )));
|
|
78
78
|
this.onNoActiveReaderCallback = onNoActiveReaderCallback;
|
|
79
|
-
this.startChecking(_nullishCoalesce(_optionalChain([options, 'optionalAccess',
|
|
79
|
+
this.startChecking(_nullishCoalesce(_optionalChain([options, 'optionalAccess', _9 => _9.intervalCheckInMs]), () => ( 20)));
|
|
80
80
|
}
|
|
81
81
|
__init() {this.checkInterval = null}
|
|
82
82
|
|
|
@@ -102,14 +102,41 @@ var NotifyAboutNoActiveReadersStream = (_class = class extends _webstreamspolyfi
|
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
104
|
}, _class);
|
|
105
|
+
var ParseError = class extends Error {
|
|
106
|
+
constructor(text) {
|
|
107
|
+
super(`Cannot parse! ${text}`);
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
var JSONParser = {
|
|
111
|
+
stringify: (value, options) => {
|
|
112
|
+
return JSON.stringify(
|
|
113
|
+
_optionalChain([options, 'optionalAccess', _10 => _10.map]) ? options.map(value) : value,
|
|
114
|
+
//TODO: Consider adding support to DateTime and adding specific format to mark that's a bigint
|
|
115
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
116
|
+
(_, v) => typeof v === "bigint" ? v.toString() : v
|
|
117
|
+
);
|
|
118
|
+
},
|
|
119
|
+
parse: (text, options) => {
|
|
120
|
+
const parsed = JSON.parse(text, _optionalChain([options, 'optionalAccess', _11 => _11.reviver]));
|
|
121
|
+
if (_optionalChain([options, 'optionalAccess', _12 => _12.typeCheck]) && !_optionalChain([options, 'optionalAccess', _13 => _13.typeCheck, 'call', _14 => _14(parsed)]))
|
|
122
|
+
throw new ParseError(text);
|
|
123
|
+
return _optionalChain([options, 'optionalAccess', _15 => _15.map]) ? options.map(parsed) : parsed;
|
|
124
|
+
}
|
|
125
|
+
};
|
|
105
126
|
var asyncRetry = async (fn, opts) => {
|
|
106
127
|
if (opts === void 0 || opts.retries === 0) return fn();
|
|
107
128
|
return _asyncretry2.default.call(void 0,
|
|
108
129
|
async (bail) => {
|
|
109
130
|
try {
|
|
110
|
-
|
|
131
|
+
const result = await fn();
|
|
132
|
+
if (_optionalChain([opts, 'optionalAccess', _16 => _16.shouldRetryResult]) && opts.shouldRetryResult(result)) {
|
|
133
|
+
throw new EmmettError(
|
|
134
|
+
`Retrying because of result: ${JSONParser.stringify(result)}`
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
return result;
|
|
111
138
|
} catch (error2) {
|
|
112
|
-
if (_optionalChain([opts, 'optionalAccess',
|
|
139
|
+
if (_optionalChain([opts, 'optionalAccess', _17 => _17.shouldRetryError]) && !opts.shouldRetryError(error2)) {
|
|
113
140
|
bail(error2);
|
|
114
141
|
}
|
|
115
142
|
throw error2;
|
|
@@ -348,7 +375,7 @@ var getEventStoreDBEventStore = (eventStore) => {
|
|
|
348
375
|
return {
|
|
349
376
|
async aggregateStream(streamName, options) {
|
|
350
377
|
const { evolve, initialState, read } = options;
|
|
351
|
-
const expectedStreamVersion = _optionalChain([read, 'optionalAccess',
|
|
378
|
+
const expectedStreamVersion = _optionalChain([read, 'optionalAccess', _18 => _18.expectedStreamVersion]);
|
|
352
379
|
let state = initialState();
|
|
353
380
|
let currentStreamVersion = EventStoreDBEventStoreDefaultStreamVersion;
|
|
354
381
|
let lastEventGlobalPosition = void 0;
|
|
@@ -360,7 +387,7 @@ var getEventStoreDBEventStore = (eventStore) => {
|
|
|
360
387
|
if (!event) continue;
|
|
361
388
|
state = evolve(state, mapFromESDBEvent(event));
|
|
362
389
|
currentStreamVersion = event.revision;
|
|
363
|
-
lastEventGlobalPosition = _optionalChain([event, 'access',
|
|
390
|
+
lastEventGlobalPosition = _optionalChain([event, 'access', _19 => _19.position, 'optionalAccess', _20 => _20.commit]);
|
|
364
391
|
}
|
|
365
392
|
assertExpectedVersionMatchesCurrent(
|
|
366
393
|
currentStreamVersion,
|
|
@@ -420,7 +447,7 @@ var getEventStoreDBEventStore = (eventStore) => {
|
|
|
420
447
|
try {
|
|
421
448
|
const serializedEvents = events.map(_dbclient.jsonEvent);
|
|
422
449
|
const expectedRevision = toExpectedRevision(
|
|
423
|
-
_optionalChain([options, 'optionalAccess',
|
|
450
|
+
_optionalChain([options, 'optionalAccess', _21 => _21.expectedStreamVersion])
|
|
424
451
|
);
|
|
425
452
|
const appendResult = await eventStore.appendToStream(
|
|
426
453
|
streamName,
|
|
@@ -490,45 +517,71 @@ var toStreamPosition = (startFrom) => startFrom === "BEGINNING" ? _dbclient.STAR
|
|
|
490
517
|
var subscribe = (client, from, options) => from == void 0 || from.stream == $all ? client.subscribeToAll({
|
|
491
518
|
fromPosition: toGlobalPosition(options.startFrom),
|
|
492
519
|
filter: _dbclient.excludeSystemEvents.call(void 0, ),
|
|
493
|
-
..._nullishCoalesce(_optionalChain([from, 'optionalAccess',
|
|
520
|
+
..._nullishCoalesce(_optionalChain([from, 'optionalAccess', _22 => _22.options]), () => ( {}))
|
|
494
521
|
}) : client.subscribeToStream(from.stream, {
|
|
495
522
|
fromRevision: toStreamPosition(options.startFrom),
|
|
496
523
|
..._nullishCoalesce(from.options, () => ( {}))
|
|
497
524
|
});
|
|
525
|
+
var isDatabaseUnavailableError = (error) => error instanceof Error && "type" in error && error.type === "unavailable" && "code" in error && error.code === 14;
|
|
526
|
+
var EventStoreDBResubscribeDefaultOptions = {
|
|
527
|
+
forever: true,
|
|
528
|
+
minTimeout: 100,
|
|
529
|
+
factor: 1.5,
|
|
530
|
+
shouldRetryError: (error) => !isDatabaseUnavailableError(error)
|
|
531
|
+
};
|
|
498
532
|
var eventStoreDBSubscription = ({
|
|
499
533
|
client,
|
|
500
534
|
from,
|
|
501
535
|
//batchSize,
|
|
502
|
-
eachBatch
|
|
536
|
+
eachBatch,
|
|
537
|
+
resilience
|
|
503
538
|
}) => {
|
|
504
539
|
let isRunning = false;
|
|
505
540
|
let start;
|
|
506
541
|
let subscription;
|
|
507
|
-
const
|
|
542
|
+
const resubscribeOptions = _nullishCoalesce(_optionalChain([resilience, 'optionalAccess', _23 => _23.resubscribeOptions]), () => ( {
|
|
543
|
+
...EventStoreDBResubscribeDefaultOptions,
|
|
544
|
+
shouldRetryResult: () => isRunning,
|
|
545
|
+
shouldRetryError: (error) => isRunning && EventStoreDBResubscribeDefaultOptions.shouldRetryError(error)
|
|
546
|
+
}));
|
|
547
|
+
const pipeMessages = (options) => {
|
|
508
548
|
subscription = subscribe(client, from, options);
|
|
509
|
-
return
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
549
|
+
return asyncRetry(
|
|
550
|
+
() => new Promise((resolve, reject) => {
|
|
551
|
+
_stream.finished.call(void 0,
|
|
552
|
+
subscription.on("data", async (resolvedEvent) => {
|
|
553
|
+
if (!resolvedEvent.event) return;
|
|
554
|
+
const event = mapFromESDBEvent(
|
|
555
|
+
resolvedEvent.event
|
|
556
|
+
);
|
|
557
|
+
const result = await eachBatch({ messages: [event] });
|
|
558
|
+
if (result && result.type === "STOP") {
|
|
559
|
+
isRunning = false;
|
|
560
|
+
await subscription.unsubscribe();
|
|
561
|
+
}
|
|
562
|
+
from = {
|
|
563
|
+
stream: _nullishCoalesce(_optionalChain([from, 'optionalAccess', _24 => _24.stream]), () => ( $all)),
|
|
564
|
+
options: {
|
|
565
|
+
..._nullishCoalesce(_optionalChain([from, 'optionalAccess', _25 => _25.options]), () => ( {})),
|
|
566
|
+
...!from || _optionalChain([from, 'optionalAccess', _26 => _26.stream]) === $all ? {
|
|
567
|
+
fromPosition: resolvedEvent.event.position
|
|
568
|
+
} : { fromRevision: resolvedEvent.event.revision }
|
|
569
|
+
}
|
|
570
|
+
};
|
|
571
|
+
}),
|
|
572
|
+
(error) => {
|
|
573
|
+
if (error) {
|
|
574
|
+
console.error(`Received error: ${JSON.stringify(error)}.`);
|
|
575
|
+
reject(error);
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
523
578
|
console.info(`Stopping subscription.`);
|
|
524
579
|
resolve();
|
|
525
|
-
return;
|
|
526
580
|
}
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
});
|
|
581
|
+
);
|
|
582
|
+
}),
|
|
583
|
+
resubscribeOptions
|
|
584
|
+
);
|
|
532
585
|
};
|
|
533
586
|
return {
|
|
534
587
|
get isRunning() {
|
|
@@ -538,14 +591,14 @@ var eventStoreDBSubscription = ({
|
|
|
538
591
|
if (isRunning) return start;
|
|
539
592
|
start = (async () => {
|
|
540
593
|
isRunning = true;
|
|
541
|
-
return
|
|
594
|
+
return pipeMessages(options);
|
|
542
595
|
})();
|
|
543
596
|
return start;
|
|
544
597
|
},
|
|
545
598
|
stop: async () => {
|
|
546
599
|
if (!isRunning) return;
|
|
547
600
|
isRunning = false;
|
|
548
|
-
await _optionalChain([subscription, 'optionalAccess',
|
|
601
|
+
await _optionalChain([subscription, 'optionalAccess', _27 => _27.unsubscribe, 'call', _28 => _28()]);
|
|
549
602
|
await start;
|
|
550
603
|
}
|
|
551
604
|
};
|
|
@@ -579,7 +632,7 @@ var eventStoreDBEventStoreConsumer = (options) => {
|
|
|
579
632
|
})
|
|
580
633
|
);
|
|
581
634
|
return result.some(
|
|
582
|
-
(r) => r.status === "fulfilled" && _optionalChain([r, 'access',
|
|
635
|
+
(r) => r.status === "fulfilled" && _optionalChain([r, 'access', _29 => _29.value, 'optionalAccess', _30 => _30.type]) !== "STOP"
|
|
583
636
|
) ? void 0 : {
|
|
584
637
|
type: "STOP"
|
|
585
638
|
};
|
|
@@ -588,7 +641,8 @@ var eventStoreDBEventStoreConsumer = (options) => {
|
|
|
588
641
|
client,
|
|
589
642
|
from: options.from,
|
|
590
643
|
eachBatch,
|
|
591
|
-
batchSize: _nullishCoalesce(_optionalChain([pulling, 'optionalAccess',
|
|
644
|
+
batchSize: _nullishCoalesce(_optionalChain([pulling, 'optionalAccess', _31 => _31.batchSize]), () => ( DefaultEventStoreDBEventStoreProcessorBatchSize)),
|
|
645
|
+
resilience: options.resilience
|
|
592
646
|
});
|
|
593
647
|
const stop = async () => {
|
|
594
648
|
if (!isRunning) return;
|
|
@@ -644,5 +698,7 @@ var eventStoreDBEventStoreConsumer = (options) => {
|
|
|
644
698
|
|
|
645
699
|
|
|
646
700
|
|
|
647
|
-
|
|
701
|
+
|
|
702
|
+
|
|
703
|
+
exports.$all = $all; exports.DefaultEventStoreDBEventStoreProcessorBatchSize = DefaultEventStoreDBEventStoreProcessorBatchSize; exports.DefaultEventStoreDBEventStoreProcessorPullingFrequencyInMs = DefaultEventStoreDBEventStoreProcessorPullingFrequencyInMs; exports.EventStoreDBEventStoreDefaultStreamVersion = EventStoreDBEventStoreDefaultStreamVersion; exports.EventStoreDBEventStoreProcessor = EventStoreDBEventStoreProcessor; exports.EventStoreDBResubscribeDefaultOptions = EventStoreDBResubscribeDefaultOptions; exports.eventStoreDBEventStoreConsumer = eventStoreDBEventStoreConsumer; exports.eventStoreDBEventStoreProcessor = eventStoreDBEventStoreProcessor; exports.eventStoreDBSubscription = eventStoreDBSubscription; exports.getEventStoreDBEventStore = getEventStoreDBEventStore; exports.isDatabaseUnavailableError = isDatabaseUnavailableError; exports.mapFromESDBEvent = mapFromESDBEvent; exports.zipEventStoreDBEventStoreMessageBatchPullerStartFrom = zipEventStoreDBEventStoreMessageBatchPullerStartFrom;
|
|
648
704
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/runner/work/emmett/emmett/src/packages/emmett-esdb/dist/index.cjs","../../emmett/src/validation/index.ts","../../emmett/src/errors/index.ts","../../emmett/src/eventStore/inMemoryEventStore.ts","../../emmett/src/eventStore/subscriptions/caughtUpTransformStream.ts","../../emmett/src/eventStore/subscriptions/streamingCoordinator.ts","../../emmett/src/streaming/transformations/notifyAboutNoActiveReaders.ts","../../emmett/src/utils/retry.ts","../../emmett/src/database/inMemoryDatabase.ts","../../emmett/src/streaming/generators/fromArray.ts","../../emmett/src/streaming/restream.ts","../../emmett/src/streaming/transformations/filter.ts","../../emmett/src/streaming/transformations/map.ts","../../emmett/src/streaming/transformations/reduce.ts","../../emmett/src/streaming/transformations/retry.ts","../../emmett/src/streaming/transformations/skip.ts","../../emmett/src/streaming/transformations/stopAfter.ts","../../emmett/src/streaming/transformations/stopOn.ts","../../emmett/src/streaming/transformations/take.ts","../../emmett/src/streaming/transformations/waitAtMost.ts","../../emmett/src/eventStore/expectedVersion.ts","../../emmett/src/streaming/transformations/index.ts","../src/eventStore/consumers/eventStoreDBEventStoreConsumer.ts","../src/eventStore/consumers/eventStoreDBEventStoreProcessor.ts","../src/eventStore/consumers/subscriptions/index.ts","../src/eventStore/eventstoreDBEventStore.ts"],"names":[],"mappings":"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","file":"/home/runner/work/emmett/emmett/src/packages/emmett-esdb/dist/index.cjs","sourcesContent":[null,"import { ValidationError } from '../errors';\n\nexport const enum ValidationErrors {\n NOT_A_NONEMPTY_STRING = 'NOT_A_NONEMPTY_STRING',\n NOT_A_POSITIVE_NUMBER = 'NOT_A_POSITIVE_NUMBER',\n NOT_AN_UNSIGNED_BIGINT = 'NOT_AN_UNSIGNED_BIGINT',\n}\n\nexport const isNumber = (val: unknown): val is number =>\n typeof val === 'number' && val === val;\n\nexport const isString = (val: unknown): val is string =>\n typeof val === 'string';\n\nexport const assertNotEmptyString = (value: unknown): string => {\n if (!isString(value) || value.length === 0) {\n throw new ValidationError(ValidationErrors.NOT_A_NONEMPTY_STRING);\n }\n return value;\n};\n\nexport const assertPositiveNumber = (value: unknown): number => {\n if (!isNumber(value) || value <= 0) {\n throw new ValidationError(ValidationErrors.NOT_A_POSITIVE_NUMBER);\n }\n return value;\n};\n\nexport const assertUnsignedBigInt = (value: string): bigint => {\n const number = BigInt(value);\n if (number < 0) {\n throw new ValidationError(ValidationErrors.NOT_AN_UNSIGNED_BIGINT);\n }\n return number;\n};\n\nexport * from './dates';\n","import { isNumber, isString } from '../validation';\n\nexport type ErrorConstructor<ErrorType extends Error> = new (\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ...args: any[]\n) => ErrorType;\n\nexport const isErrorConstructor = <ErrorType extends Error>(\n // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\n expect: Function,\n): expect is ErrorConstructor<ErrorType> => {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return (\n typeof expect === 'function' &&\n expect.prototype &&\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n expect.prototype.constructor === expect\n );\n};\n\nexport class EmmettError extends Error {\n public errorCode: number;\n\n constructor(\n options?: { errorCode: number; message?: string } | string | number,\n ) {\n const errorCode =\n options && typeof options === 'object' && 'errorCode' in options\n ? options.errorCode\n : isNumber(options)\n ? options\n : 500;\n const message =\n options && typeof options === 'object' && 'message' in options\n ? options.message\n : isString(options)\n ? options\n : `Error with status code '${errorCode}' ocurred during Emmett processing`;\n\n super(message);\n this.errorCode = errorCode;\n\n // 👇️ because we are extending a built-in class\n Object.setPrototypeOf(this, EmmettError.prototype);\n }\n}\n\nexport class ConcurrencyError extends EmmettError {\n constructor(\n public current: string | undefined,\n public expected: string,\n message?: string,\n ) {\n super({\n errorCode: 412,\n message:\n message ??\n `Expected version ${expected.toString()} does not match current ${current?.toString()}`,\n });\n\n // 👇️ because we are extending a built-in class\n Object.setPrototypeOf(this, ConcurrencyError.prototype);\n }\n}\n\nexport class ConcurrencyInMemoryDatabaseError extends EmmettError {\n constructor(message?: string) {\n super({\n errorCode: 412,\n message: message ?? `Expected document state does not match current one!`,\n });\n\n // 👇️ because we are extending a built-in class\n Object.setPrototypeOf(this, ConcurrencyInMemoryDatabaseError.prototype);\n }\n}\n\nexport class ValidationError extends EmmettError {\n constructor(message?: string) {\n super({\n errorCode: 400,\n message: message ?? `Validation Error ocurred during Emmett processing`,\n });\n\n // 👇️ because we are extending a built-in class\n Object.setPrototypeOf(this, ValidationError.prototype);\n }\n}\n\nexport class IllegalStateError extends EmmettError {\n constructor(message?: string) {\n super({\n errorCode: 403,\n message: message ?? `Illegal State ocurred during Emmett processing`,\n });\n\n // 👇️ because we are extending a built-in class\n Object.setPrototypeOf(this, IllegalStateError.prototype);\n }\n}\n\nexport class NotFoundError extends EmmettError {\n constructor(options?: { id: string; type: string; message?: string }) {\n super({\n errorCode: 404,\n message:\n options?.message ??\n (options?.id\n ? options.type\n ? `${options.type} with ${options.id} was not found during Emmett processing`\n : `State with ${options.id} was not found during Emmett processing`\n : options?.type\n ? `${options.type} was not found during Emmett processing`\n : 'State was not found during Emmett processing'),\n });\n\n // 👇️ because we are extending a built-in class\n Object.setPrototypeOf(this, NotFoundError.prototype);\n }\n}\n","import { v4 as uuid } from 'uuid';\nimport type {\n BigIntStreamPosition,\n CombinedReadEventMetadata,\n Event,\n ReadEvent,\n ReadEventMetadataWithGlobalPosition,\n} from '../typing';\nimport { tryPublishMessagesAfterCommit } from './afterCommit';\nimport {\n type AggregateStreamOptions,\n type AggregateStreamResult,\n type AppendToStreamOptions,\n type AppendToStreamResult,\n type DefaultEventStoreOptions,\n type EventStore,\n type ReadStreamOptions,\n type ReadStreamResult,\n} from './eventStore';\nimport { assertExpectedVersionMatchesCurrent } from './expectedVersion';\nimport { StreamingCoordinator } from './subscriptions';\nimport type { ProjectionRegistration } from '../projections';\n\nexport const InMemoryEventStoreDefaultStreamVersion = 0n;\n\nexport type InMemoryEventStore =\n EventStore<ReadEventMetadataWithGlobalPosition>;\n\nexport type InMemoryReadEventMetadata = ReadEventMetadataWithGlobalPosition;\n\nexport type InMemoryProjectionHandlerContext = {\n eventStore: InMemoryEventStore;\n};\n\nexport type InMemoryEventStoreOptions =\n DefaultEventStoreOptions<InMemoryEventStore> & {\n projections?: ProjectionRegistration<\n 'inline',\n InMemoryReadEventMetadata,\n InMemoryProjectionHandlerContext\n >[];\n };\n\nexport type InMemoryReadEvent<EventType extends Event = Event> = ReadEvent<\n EventType,\n ReadEventMetadataWithGlobalPosition\n>;\n\nexport const getInMemoryEventStore = (\n eventStoreOptions?: InMemoryEventStoreOptions,\n): InMemoryEventStore => {\n const streams = new Map<\n string,\n ReadEvent<Event, ReadEventMetadataWithGlobalPosition>[]\n >();\n const streamingCoordinator = StreamingCoordinator();\n\n const getAllEventsCount = () => {\n return Array.from<ReadEvent[]>(streams.values())\n .map((s) => s.length)\n .reduce((p, c) => p + c, 0);\n };\n\n const _inlineProjections = (eventStoreOptions?.projections ?? [])\n .filter(({ type }) => type === 'inline')\n .map(({ projection }) => projection);\n\n return {\n async aggregateStream<State, EventType extends Event>(\n streamName: string,\n options: AggregateStreamOptions<\n State,\n EventType,\n ReadEventMetadataWithGlobalPosition\n >,\n ): Promise<AggregateStreamResult<State>> {\n const { evolve, initialState, read } = options;\n\n const result = await this.readStream<EventType>(streamName, read);\n\n const events = result?.events ?? [];\n\n return {\n currentStreamVersion: BigInt(events.length),\n state: events.reduce(evolve, initialState()),\n streamExists: result.streamExists,\n };\n },\n\n readStream: <EventType extends Event>(\n streamName: string,\n options?: ReadStreamOptions<BigIntStreamPosition>,\n ): Promise<\n ReadStreamResult<EventType, ReadEventMetadataWithGlobalPosition>\n > => {\n const events = streams.get(streamName);\n const currentStreamVersion = events\n ? BigInt(events.length)\n : InMemoryEventStoreDefaultStreamVersion;\n\n assertExpectedVersionMatchesCurrent(\n currentStreamVersion,\n options?.expectedStreamVersion,\n InMemoryEventStoreDefaultStreamVersion,\n );\n\n const from = Number(options && 'from' in options ? options.from : 0);\n const to = Number(\n options && 'to' in options\n ? options.to\n : options && 'maxCount' in options && options.maxCount\n ? options.from + options.maxCount\n : (events?.length ?? 1),\n );\n\n const resultEvents =\n events !== undefined && events.length > 0\n ? events\n .map(\n (e) =>\n e as ReadEvent<\n EventType,\n ReadEventMetadataWithGlobalPosition\n >,\n )\n .slice(from, to)\n : [];\n\n const result: ReadStreamResult<\n EventType,\n ReadEventMetadataWithGlobalPosition\n > = {\n currentStreamVersion,\n events: resultEvents,\n streamExists: events !== undefined && events.length > 0,\n };\n\n return Promise.resolve(result);\n },\n\n appendToStream: async <EventType extends Event>(\n streamName: string,\n events: EventType[],\n options?: AppendToStreamOptions,\n ): Promise<AppendToStreamResult> => {\n const currentEvents = streams.get(streamName) ?? [];\n const currentStreamVersion =\n currentEvents.length > 0\n ? BigInt(currentEvents.length)\n : InMemoryEventStoreDefaultStreamVersion;\n\n assertExpectedVersionMatchesCurrent(\n currentStreamVersion,\n options?.expectedStreamVersion,\n InMemoryEventStoreDefaultStreamVersion,\n );\n\n const newEvents: ReadEvent<\n EventType,\n ReadEventMetadataWithGlobalPosition\n >[] = events.map((event, index) => {\n const metadata: ReadEventMetadataWithGlobalPosition = {\n streamName,\n messageId: uuid(),\n streamPosition: BigInt(currentEvents.length + index + 1),\n globalPosition: BigInt(getAllEventsCount() + index + 1),\n };\n return {\n ...event,\n kind: event.kind ?? 'Event',\n metadata: {\n ...('metadata' in event ? (event.metadata ?? {}) : {}),\n ...metadata,\n } as CombinedReadEventMetadata<\n EventType,\n ReadEventMetadataWithGlobalPosition\n >,\n };\n });\n\n const positionOfLastEventInTheStream = BigInt(\n newEvents.slice(-1)[0]!.metadata.streamPosition,\n );\n\n streams.set(streamName, [...currentEvents, ...newEvents]);\n await streamingCoordinator.notify(newEvents);\n\n const result: AppendToStreamResult = {\n nextExpectedStreamVersion: positionOfLastEventInTheStream,\n createdNewStream:\n currentStreamVersion === InMemoryEventStoreDefaultStreamVersion,\n };\n\n await tryPublishMessagesAfterCommit<InMemoryEventStore>(\n newEvents,\n eventStoreOptions?.hooks,\n );\n\n return result;\n },\n\n //streamEvents: streamingCoordinator.stream,\n };\n};\n","import { TransformStream } from 'web-streams-polyfill';\nimport type {\n Event,\n ReadEvent,\n ReadEventMetadataWithGlobalPosition,\n} from '../../typing';\nimport { globalStreamCaughtUp, type GlobalSubscriptionEvent } from '../events';\n\nexport const streamTrackingGlobalPosition = (\n currentEvents: ReadEvent<Event, ReadEventMetadataWithGlobalPosition>[],\n) => new CaughtUpTransformStream(currentEvents);\n\nexport class CaughtUpTransformStream extends TransformStream<\n ReadEvent<Event, ReadEventMetadataWithGlobalPosition>,\n | ReadEvent<Event, ReadEventMetadataWithGlobalPosition>\n | GlobalSubscriptionEvent\n> {\n private _currentPosition: bigint;\n private _logPosition: bigint;\n\n constructor(events: ReadEvent<Event, ReadEventMetadataWithGlobalPosition>[]) {\n super({\n start: (controller) => {\n let globalPosition = 0n;\n for (const event of events) {\n controller.enqueue(event);\n globalPosition = event.metadata.globalPosition;\n }\n controller.enqueue(globalStreamCaughtUp({ globalPosition }));\n },\n transform: (event, controller) => {\n this._currentPosition = event.metadata.globalPosition;\n controller.enqueue(event);\n\n if (this._currentPosition < this._logPosition) return;\n\n controller.enqueue(\n globalStreamCaughtUp({ globalPosition: this._currentPosition }),\n );\n },\n });\n\n this._currentPosition = this._logPosition =\n events.length > 0\n ? events[events.length - 1]!.metadata.globalPosition\n : 0n;\n }\n\n public set logPosition(value: bigint) {\n this._logPosition = value;\n }\n}\n","import { v4 as uuid } from 'uuid';\nimport { notifyAboutNoActiveReadersStream } from '../../streaming/transformations/notifyAboutNoActiveReaders';\nimport { writeToStream } from '../../streaming/writers';\nimport type {\n Event,\n ReadEvent,\n ReadEventMetadataWithGlobalPosition,\n} from '../../typing';\nimport {\n CaughtUpTransformStream,\n streamTrackingGlobalPosition,\n} from './caughtUpTransformStream';\n\nexport const StreamingCoordinator = () => {\n const allEvents: ReadEvent<Event, ReadEventMetadataWithGlobalPosition>[] = [];\n const listeners = new Map<string, CaughtUpTransformStream>();\n\n return {\n notify: async (\n events: ReadEvent<Event, ReadEventMetadataWithGlobalPosition>[],\n ) => {\n if (events.length === 0) return;\n\n allEvents.push(...events);\n\n for (const listener of listeners.values()) {\n listener.logPosition =\n events[events.length - 1]!.metadata.globalPosition;\n\n await writeToStream(listener, events);\n }\n },\n\n stream: () => {\n const streamId = uuid();\n const transformStream = streamTrackingGlobalPosition(allEvents);\n\n listeners.set(streamId, transformStream);\n return transformStream.readable.pipeThrough(\n notifyAboutNoActiveReadersStream(\n (stream) => {\n if (listeners.has(stream.streamId))\n listeners.delete(stream.streamId);\n },\n { streamId },\n ),\n );\n },\n };\n};\n","import { v4 as uuid } from 'uuid';\nimport { TransformStream } from 'web-streams-polyfill';\n\nexport const notifyAboutNoActiveReadersStream = <Item>(\n onNoActiveReaderCallback: (\n stream: NotifyAboutNoActiveReadersStream<Item>,\n ) => void,\n options: { streamId?: string; intervalCheckInMs?: number } = {},\n) => new NotifyAboutNoActiveReadersStream(onNoActiveReaderCallback, options);\n\nexport class NotifyAboutNoActiveReadersStream<Item> extends TransformStream<\n Item,\n Item\n> {\n private checkInterval: NodeJS.Timeout | null = null;\n public readonly streamId: string;\n private _isStopped: boolean = false;\n public get hasActiveSubscribers() {\n return !this._isStopped;\n }\n\n constructor(\n private onNoActiveReaderCallback: (\n stream: NotifyAboutNoActiveReadersStream<Item>,\n ) => void,\n options: { streamId?: string; intervalCheckInMs?: number } = {},\n ) {\n super({\n cancel: (reason) => {\n console.log('Stream was canceled. Reason:', reason);\n this.stopChecking();\n },\n });\n this.streamId = options?.streamId ?? uuid();\n\n this.onNoActiveReaderCallback = onNoActiveReaderCallback;\n\n this.startChecking(options?.intervalCheckInMs ?? 20);\n }\n\n private startChecking(interval: number) {\n this.checkInterval = setInterval(() => {\n this.checkNoActiveReader();\n }, interval);\n }\n\n private stopChecking() {\n if (!this.checkInterval) return;\n\n clearInterval(this.checkInterval);\n this.checkInterval = null;\n this._isStopped = true;\n this.onNoActiveReaderCallback(this);\n }\n\n private checkNoActiveReader() {\n if (!this.readable.locked && !this._isStopped) {\n this.stopChecking();\n }\n }\n}\n","import retry from 'async-retry';\n\nexport type AsyncRetryOptions = retry.Options & {\n shouldRetryError?: (error: unknown) => boolean;\n};\n\nexport const NoRetries: AsyncRetryOptions = { retries: 0 };\n\nexport const asyncRetry = async <T>(\n fn: () => Promise<T>,\n opts?: AsyncRetryOptions,\n): Promise<T> => {\n if (opts === undefined || opts.retries === 0) return fn();\n\n return retry(\n async (bail) => {\n try {\n return await fn();\n } catch (error) {\n if (opts?.shouldRetryError && !opts.shouldRetryError(error)) {\n bail(error as Error);\n }\n throw error;\n }\n },\n opts ?? { retries: 0 },\n );\n};\n","import { v7 as uuid } from 'uuid';\nimport { deepEquals } from '../utils';\nimport {\n type DeleteResult,\n type Document,\n type DocumentHandler,\n type HandleOptionErrors,\n type HandleOptions,\n type HandleResult,\n type InsertOneResult,\n type OptionalUnlessRequiredIdAndVersion,\n type ReplaceOneOptions,\n type UpdateResult,\n type WithoutId,\n type WithIdAndVersion,\n} from './types';\nimport { expectedVersionValue, operationResult } from './utils';\n\nexport interface DocumentsCollection<T extends Document> {\n handle: (\n id: string,\n handle: DocumentHandler<T>,\n options?: HandleOptions,\n ) => HandleResult<T>;\n findOne: (predicate?: Predicate<T>) => T | null;\n find: (predicate?: Predicate<T>) => T[];\n insertOne: (\n document: OptionalUnlessRequiredIdAndVersion<T>,\n ) => InsertOneResult;\n deleteOne: (predicate?: Predicate<T>) => DeleteResult;\n replaceOne: (\n predicate: Predicate<T>,\n document: WithoutId<T>,\n options?: ReplaceOneOptions,\n ) => UpdateResult;\n}\n\nexport interface Database {\n collection: <T extends Document>(name: string) => DocumentsCollection<T>;\n}\n\ntype Predicate<T> = (item: T) => boolean;\ntype CollectionName = string;\n\nexport const getInMemoryDatabase = (): Database => {\n const storage = new Map<CollectionName, WithIdAndVersion<Document>[]>();\n\n return {\n collection: <T extends Document, CollectionName extends string>(\n collectionName: CollectionName,\n collectionOptions: {\n errors?: HandleOptionErrors;\n } = {},\n ): DocumentsCollection<T> => {\n const ensureCollectionCreated = () => {\n if (!storage.has(collectionName)) storage.set(collectionName, []);\n };\n\n const errors = collectionOptions.errors;\n\n const collection = {\n collectionName,\n insertOne: (\n document: OptionalUnlessRequiredIdAndVersion<T>,\n ): InsertOneResult => {\n ensureCollectionCreated();\n\n const _id = (document._id as string | undefined | null) ?? uuid();\n const _version = document._version ?? 1n;\n\n const existing = collection.findOne((c) => c._id === _id);\n\n if (existing) {\n return operationResult<InsertOneResult>(\n {\n successful: false,\n insertedId: null,\n nextExpectedVersion: _version,\n },\n { operationName: 'insertOne', collectionName, errors },\n );\n }\n\n const documentsInCollection = storage.get(collectionName)!;\n const newDocument = { ...document, _id, _version };\n const newCollection = [...documentsInCollection, newDocument];\n storage.set(collectionName, newCollection);\n\n return operationResult<InsertOneResult>(\n {\n successful: true,\n insertedId: _id,\n nextExpectedVersion: _version,\n },\n { operationName: 'insertOne', collectionName, errors },\n );\n },\n findOne: (predicate?: Predicate<T>): T | null => {\n ensureCollectionCreated();\n\n const documentsInCollection = storage.get(collectionName);\n const filteredDocuments = predicate\n ? documentsInCollection?.filter((doc) => predicate(doc as T))\n : documentsInCollection;\n\n const firstOne = filteredDocuments?.[0] ?? null;\n\n return firstOne as T | null;\n },\n find: (predicate?: Predicate<T>): T[] => {\n ensureCollectionCreated();\n\n const documentsInCollection = storage.get(collectionName);\n const filteredDocuments = predicate\n ? documentsInCollection?.filter((doc) => predicate(doc as T))\n : documentsInCollection;\n\n return filteredDocuments as T[];\n },\n deleteOne: (predicate?: Predicate<T>): DeleteResult => {\n ensureCollectionCreated();\n\n const documentsInCollection = storage.get(collectionName)!;\n\n if (predicate) {\n const foundIndex = documentsInCollection.findIndex((doc) =>\n predicate(doc as T),\n );\n\n if (foundIndex === -1) {\n return operationResult<DeleteResult>(\n {\n successful: false,\n matchedCount: 0,\n deletedCount: 0,\n },\n { operationName: 'deleteOne', collectionName, errors },\n );\n } else {\n const newCollection = documentsInCollection.toSpliced(\n foundIndex,\n 1,\n );\n\n storage.set(collectionName, newCollection);\n\n return operationResult<DeleteResult>(\n {\n successful: true,\n matchedCount: 1,\n deletedCount: 1,\n },\n { operationName: 'deleteOne', collectionName, errors },\n );\n }\n }\n\n const newCollection = documentsInCollection.slice(1);\n\n storage.set(collectionName, newCollection);\n\n return operationResult<DeleteResult>(\n {\n successful: true,\n matchedCount: 1,\n deletedCount: 1,\n },\n { operationName: 'deleteOne', collectionName, errors },\n );\n },\n replaceOne: (\n predicate: Predicate<T>,\n document: WithoutId<T>,\n options?: ReplaceOneOptions,\n ): UpdateResult => {\n ensureCollectionCreated();\n\n const documentsInCollection = storage.get(collectionName)!;\n\n const foundIndexes = documentsInCollection\n .filter((doc) => predicate(doc as T))\n .map((_, index) => index);\n\n const firstIndex = foundIndexes[0];\n\n if (firstIndex === undefined || firstIndex === -1) {\n return operationResult<UpdateResult>(\n {\n successful: false,\n matchedCount: 0,\n modifiedCount: 0,\n nextExpectedVersion: 0n,\n },\n { operationName: 'replaceOne', collectionName, errors },\n );\n }\n\n const existing = documentsInCollection[firstIndex]!;\n\n if (\n typeof options?.expectedVersion === 'bigint' &&\n existing._version !== options.expectedVersion\n ) {\n return operationResult<UpdateResult>(\n {\n successful: false,\n matchedCount: 1,\n modifiedCount: 0,\n nextExpectedVersion: existing._version,\n },\n { operationName: 'replaceOne', collectionName, errors },\n );\n }\n\n const newVersion = existing._version + 1n;\n\n const newCollection = documentsInCollection.with(firstIndex, {\n _id: existing._id,\n ...document,\n _version: newVersion,\n });\n\n storage.set(collectionName, newCollection);\n\n return operationResult<UpdateResult>(\n {\n successful: true,\n modifiedCount: 1,\n matchedCount: foundIndexes.length,\n nextExpectedVersion: newVersion,\n },\n { operationName: 'replaceOne', collectionName, errors },\n );\n },\n handle: (\n id: string,\n handle: DocumentHandler<T>,\n options?: HandleOptions,\n ): HandleResult<T> => {\n const { expectedVersion: version, ...operationOptions } =\n options ?? {};\n ensureCollectionCreated();\n const existing = collection.findOne(({ _id }) => _id === id);\n\n const expectedVersion = expectedVersionValue(version);\n\n if (\n (existing == null && version === 'DOCUMENT_EXISTS') ||\n (existing == null && expectedVersion != null) ||\n (existing != null && version === 'DOCUMENT_DOES_NOT_EXIST') ||\n (existing != null &&\n expectedVersion !== null &&\n existing._version !== expectedVersion)\n ) {\n return operationResult<HandleResult<T>>(\n {\n successful: false,\n document: existing as WithIdAndVersion<T>,\n },\n { operationName: 'handle', collectionName, errors },\n );\n }\n\n const result = handle(existing !== null ? { ...existing } : null);\n\n if (deepEquals(existing, result))\n return operationResult<HandleResult<T>>(\n {\n successful: true,\n document: existing as WithIdAndVersion<T>,\n },\n { operationName: 'handle', collectionName, errors },\n );\n\n if (!existing && result) {\n const newDoc = { ...result, _id: id };\n const insertResult = collection.insertOne({\n ...newDoc,\n _id: id,\n } as OptionalUnlessRequiredIdAndVersion<T>);\n return {\n ...insertResult,\n document: {\n ...newDoc,\n _version: insertResult.nextExpectedVersion,\n } as unknown as WithIdAndVersion<T>,\n };\n }\n\n if (existing && !result) {\n const deleteResult = collection.deleteOne(({ _id }) => id === _id);\n return { ...deleteResult, document: null };\n }\n\n if (existing && result) {\n const replaceResult = collection.replaceOne(\n ({ _id }) => id === _id,\n result,\n {\n ...operationOptions,\n expectedVersion: expectedVersion ?? 'DOCUMENT_EXISTS',\n },\n );\n return {\n ...replaceResult,\n document: {\n ...result,\n _version: replaceResult.nextExpectedVersion,\n } as unknown as WithIdAndVersion<T>,\n };\n }\n\n return operationResult<HandleResult<T>>(\n {\n successful: true,\n document: existing as WithIdAndVersion<T>,\n },\n { operationName: 'handle', collectionName, errors },\n );\n },\n };\n\n return collection;\n },\n };\n};\n","import { ReadableStream } from 'web-streams-polyfill';\n\nexport const fromArray = <T>(chunks: T[]) =>\n new ReadableStream<T>({\n start(controller) {\n for (const chunk of chunks) controller.enqueue(chunk);\n controller.close();\n },\n });\n","import {\n type ReadableStream,\n type ReadableStreamDefaultReadResult,\n type TransformStreamDefaultController,\n} from 'web-streams-polyfill';\nimport type { AsyncRetryOptions } from '../utils';\nimport type { Decoder } from './decoders';\nimport { DefaultDecoder } from './decoders/composite';\nimport { streamTransformations } from './transformations';\n\nconst { retry } = streamTransformations;\n\nexport const restream = <\n Source = unknown,\n Transformed = Source,\n StreamType = Source,\n>(\n createSourceStream: () => ReadableStream<StreamType>,\n transform: (input: Source) => Transformed = (source) =>\n source as unknown as Transformed,\n retryOptions: AsyncRetryOptions = { forever: true, minTimeout: 25 },\n decoder: Decoder<StreamType, Source> = new DefaultDecoder<Source>(),\n): ReadableStream<Transformed> =>\n retry(createSourceStream, handleChunk(transform, decoder), retryOptions)\n .readable;\n\nconst handleChunk =\n <Source = unknown, Transformed = Source, StreamType = Source>(\n transform: (input: Source) => Transformed = (source) =>\n source as unknown as Transformed,\n decoder: Decoder<StreamType, Source> = new DefaultDecoder<Source>(),\n ) =>\n (\n readResult: ReadableStreamDefaultReadResult<StreamType>,\n controller: TransformStreamDefaultController<Transformed>,\n ): void => {\n const { done: isDone, value } = readResult;\n\n if (value) decoder.addToBuffer(value);\n\n if (!isDone && !decoder.hasCompleteMessage()) return;\n\n decodeAndTransform(decoder, transform, controller);\n };\n\nconst decodeAndTransform = <StreamType, Source, Transformed = Source>(\n decoder: Decoder<StreamType, Source>,\n transform: (input: Source) => Transformed,\n controller: TransformStreamDefaultController<Transformed>,\n) => {\n try {\n const decoded = decoder.decode();\n if (!decoded) return; // TODO: Add a proper handling of decode errors\n\n const transformed = transform(decoded);\n controller.enqueue(transformed);\n } catch (error) {\n controller.error(new Error(`Decoding error: ${error?.toString()}`));\n }\n};\n","import { TransformStream } from 'web-streams-polyfill';\n\nexport const filter = <Item>(filter: (item: Item) => boolean) =>\n new TransformStream<Item, Item>({\n transform(chunk, controller) {\n if (filter(chunk)) {\n controller.enqueue(chunk);\n }\n },\n });\n","import { TransformStream } from 'web-streams-polyfill';\n\nexport const map = <From, To>(map: (item: From) => To) =>\n new TransformStream<From, To>({\n transform(chunk, controller) {\n controller.enqueue(map(chunk));\n },\n });\n","import { TransformStream } from 'web-streams-polyfill';\n\nexport const reduce = <I, O>(\n reducer: (accumulator: O, chunk: I) => O,\n initialValue: O,\n) => new ReduceTransformStream<I, O>(reducer, initialValue);\n\nexport class ReduceTransformStream<I, O> extends TransformStream<I, O> {\n private accumulator: O;\n private reducer: (accumulator: O, chunk: I) => O;\n\n constructor(reducer: (accumulator: O, chunk: I) => O, initialValue: O) {\n super({\n transform: (chunk) => {\n this.accumulator = this.reducer(this.accumulator, chunk);\n },\n flush: (controller) => {\n controller.enqueue(this.accumulator);\n controller.terminate();\n },\n });\n\n this.accumulator = initialValue;\n this.reducer = reducer;\n }\n}\n","import {\n type ReadableStream,\n type ReadableStreamDefaultReadResult,\n TransformStream,\n type TransformStreamDefaultController,\n} from 'web-streams-polyfill';\nimport { type AsyncRetryOptions, asyncRetry } from '../../utils';\n\nexport const retryStream = <\n Source = unknown,\n Transformed = Source,\n StreamType = Source,\n>(\n createSourceStream: () => ReadableStream<StreamType>,\n handleChunk: (\n readResult: ReadableStreamDefaultReadResult<StreamType>,\n controller: TransformStreamDefaultController<Transformed>,\n ) => Promise<void> | void,\n retryOptions: AsyncRetryOptions = { forever: true, minTimeout: 25 },\n): TransformStream<Source, Transformed> =>\n new TransformStream<Source, Transformed>({\n start(controller) {\n asyncRetry(\n () => onRestream(createSourceStream, handleChunk, controller),\n retryOptions,\n ).catch((error) => {\n controller.error(error);\n });\n },\n });\n\nconst onRestream = async <StreamType, Source, Transformed = Source>(\n createSourceStream: () => ReadableStream<StreamType>,\n handleChunk: (\n readResult: ReadableStreamDefaultReadResult<StreamType>,\n controller: TransformStreamDefaultController<Transformed>,\n ) => Promise<void> | void,\n controller: TransformStreamDefaultController<Transformed>,\n): Promise<void> => {\n const sourceStream = createSourceStream();\n const reader = sourceStream.getReader();\n\n try {\n let done: boolean;\n\n do {\n const result = await reader.read();\n done = result.done;\n\n await handleChunk(result, controller);\n\n if (done) {\n controller.terminate();\n }\n } while (!done);\n } finally {\n reader.releaseLock();\n }\n};\n","import { TransformStream } from 'web-streams-polyfill';\n\nexport const skip = <T>(limit: number) => new SkipTransformStream<T>(limit);\n\nexport class SkipTransformStream<T> extends TransformStream<T, T> {\n private count = 0;\n private skip: number;\n\n constructor(skip: number) {\n super({\n transform: (chunk, controller) => {\n this.count++;\n if (this.count > this.skip) {\n controller.enqueue(chunk);\n }\n },\n });\n\n this.skip = skip;\n }\n}\n","import { TransformStream } from 'web-streams-polyfill';\n\nexport const stopAfter = <Item>(stopCondition: (item: Item) => boolean) =>\n new TransformStream<Item, Item>({\n transform(chunk, controller) {\n controller.enqueue(chunk);\n\n if (stopCondition(chunk)) {\n controller.terminate();\n }\n },\n });\n","import { TransformStream } from 'web-streams-polyfill';\n\nexport const stopOn = <Item>(stopCondition: (item: Item) => boolean) =>\n new TransformStream<Item, Item>({\n async transform(chunk, controller) {\n if (!stopCondition(chunk)) {\n controller.enqueue(chunk);\n return;\n }\n await Promise.resolve();\n controller.terminate();\n },\n });\n","import { TransformStream } from 'web-streams-polyfill';\n\nexport const take = <T>(limit: number) => new TakeTransformStream<T>(limit);\n\nexport class TakeTransformStream<T> extends TransformStream<T, T> {\n private count = 0;\n private limit: number;\n\n constructor(limit: number) {\n super({\n transform: (chunk, controller) => {\n if (this.count < this.limit) {\n this.count++;\n controller.enqueue(chunk);\n } else {\n controller.terminate();\n }\n },\n });\n\n this.limit = limit;\n }\n}\n","import { TransformStream } from 'web-streams-polyfill';\n\nexport const waitAtMost = <Item>(waitTimeInMs: number) =>\n new TransformStream<Item, Item>({\n start(controller) {\n const timeoutId = setTimeout(() => {\n controller.terminate();\n }, waitTimeInMs);\n\n const originalTerminate = controller.terminate.bind(controller);\n\n // Clear the timeout if the stream is terminated early\n controller.terminate = () => {\n clearTimeout(timeoutId);\n originalTerminate();\n };\n },\n transform(chunk, controller) {\n controller.enqueue(chunk);\n },\n });\n","import { ConcurrencyError } from '../errors';\nimport type { BigIntStreamPosition, Flavour } from '../typing';\n\nexport type ExpectedStreamVersion<VersionType = BigIntStreamPosition> =\n | ExpectedStreamVersionWithValue<VersionType>\n | ExpectedStreamVersionGeneral;\n\nexport type ExpectedStreamVersionWithValue<VersionType = BigIntStreamPosition> =\n Flavour<VersionType, 'StreamVersion'>;\n\nexport type ExpectedStreamVersionGeneral = Flavour<\n 'STREAM_EXISTS' | 'STREAM_DOES_NOT_EXIST' | 'NO_CONCURRENCY_CHECK',\n 'StreamVersion'\n>;\n\nexport const STREAM_EXISTS = 'STREAM_EXISTS' as ExpectedStreamVersionGeneral;\nexport const STREAM_DOES_NOT_EXIST =\n 'STREAM_DOES_NOT_EXIST' as ExpectedStreamVersionGeneral;\nexport const NO_CONCURRENCY_CHECK =\n 'NO_CONCURRENCY_CHECK' as ExpectedStreamVersionGeneral;\n\nexport const matchesExpectedVersion = <StreamVersion = BigIntStreamPosition>(\n current: StreamVersion | undefined,\n expected: ExpectedStreamVersion<StreamVersion>,\n defaultVersion: StreamVersion,\n): boolean => {\n if (expected === NO_CONCURRENCY_CHECK) return true;\n\n if (expected == STREAM_DOES_NOT_EXIST) return current === defaultVersion;\n\n if (expected == STREAM_EXISTS) return current !== defaultVersion;\n\n return current === expected;\n};\n\nexport const assertExpectedVersionMatchesCurrent = <\n StreamVersion = BigIntStreamPosition,\n>(\n current: StreamVersion,\n expected: ExpectedStreamVersion<StreamVersion> | undefined,\n defaultVersion: StreamVersion,\n): void => {\n expected ??= NO_CONCURRENCY_CHECK;\n\n if (!matchesExpectedVersion(current, expected, defaultVersion))\n throw new ExpectedVersionConflictError(current, expected);\n};\n\nexport class ExpectedVersionConflictError<\n VersionType = BigIntStreamPosition,\n> extends ConcurrencyError {\n constructor(\n current: VersionType,\n expected: ExpectedStreamVersion<VersionType>,\n ) {\n super(current?.toString(), expected?.toString());\n\n // 👇️ because we are extending a built-in class\n Object.setPrototypeOf(this, ExpectedVersionConflictError.prototype);\n }\n}\n\nexport const isExpectedVersionConflictError = (\n error: unknown,\n): error is ExpectedVersionConflictError =>\n error instanceof ExpectedVersionConflictError;\n","import { filter } from './filter';\nimport { map } from './map';\nimport {\n notifyAboutNoActiveReadersStream,\n NotifyAboutNoActiveReadersStream,\n} from './notifyAboutNoActiveReaders';\nimport { reduce, ReduceTransformStream } from './reduce';\nimport { retryStream } from './retry';\nimport { skip, SkipTransformStream } from './skip';\nimport { stopAfter } from './stopAfter';\nimport { stopOn } from './stopOn';\nimport { take, TakeTransformStream } from './take';\nimport { waitAtMost } from './waitAtMost';\n\nexport const streamTransformations = {\n filter,\n take,\n TakeTransformStream,\n skip,\n SkipTransformStream,\n map,\n notifyAboutNoActiveReadersStream,\n NotifyAboutNoActiveReadersStream,\n reduce,\n ReduceTransformStream,\n retry: retryStream,\n stopAfter,\n stopOn,\n waitAtMost,\n};\n","import { EmmettError, type Event } from '@event-driven-io/emmett';\nimport {\n EventStoreDBClient,\n type SubscribeToAllOptions,\n type SubscribeToStreamOptions,\n} from '@eventstore/db-client';\nimport {\n eventStoreDBEventStoreProcessor,\n type EventStoreDBEventStoreProcessor,\n type EventStoreDBEventStoreProcessorOptions,\n} from './eventStoreDBEventStoreProcessor';\nimport {\n DefaultEventStoreDBEventStoreProcessorBatchSize,\n eventStoreDBSubscription,\n zipEventStoreDBEventStoreMessageBatchPullerStartFrom,\n type EventStoreDBEventStoreMessageBatchPuller,\n type EventStoreDBEventStoreMessagesBatchHandler,\n} from './subscriptions';\n\nexport type EventStoreDBEventStoreConsumerConfig<\n ConsumerEventType extends Event = Event,\n> = {\n from?: EventStoreDBEventStoreConsumerType;\n processors?: EventStoreDBEventStoreProcessor<ConsumerEventType>[];\n pulling?: {\n batchSize?: number;\n };\n};\n\nexport type EventStoreDBEventStoreConsumerOptions<\n ConsumerEventType extends Event = Event,\n> = EventStoreDBEventStoreConsumerConfig<ConsumerEventType> &\n (\n | {\n connectionString: string;\n }\n | { client: EventStoreDBClient }\n );\n\nexport type $all = '$all';\nexport const $all = '$all';\n\nexport type EventStoreDBEventStoreConsumerType =\n | {\n stream: $all;\n options?: Exclude<SubscribeToAllOptions, 'fromPosition'>;\n }\n | {\n stream: string;\n options?: Exclude<SubscribeToStreamOptions, 'fromRevision'>;\n };\n\nexport type EventStoreDBEventStoreConsumer<\n ConsumerEventType extends Event = Event,\n> = Readonly<{\n isRunning: boolean;\n processors: EventStoreDBEventStoreProcessor<ConsumerEventType>[];\n processor: <EventType extends ConsumerEventType = ConsumerEventType>(\n options: EventStoreDBEventStoreProcessorOptions<EventType>,\n ) => EventStoreDBEventStoreProcessor<EventType>;\n start: () => Promise<void>;\n stop: () => Promise<void>;\n close: () => Promise<void>;\n}>;\n\nexport const eventStoreDBEventStoreConsumer = <\n ConsumerEventType extends Event = Event,\n>(\n options: EventStoreDBEventStoreConsumerOptions<ConsumerEventType>,\n): EventStoreDBEventStoreConsumer<ConsumerEventType> => {\n let isRunning = false;\n const { pulling } = options;\n const processors = options.processors ?? [];\n\n let start: Promise<void>;\n\n let currentSubscription: EventStoreDBEventStoreMessageBatchPuller | undefined;\n\n const client =\n 'client' in options\n ? options.client\n : EventStoreDBClient.connectionString(options.connectionString);\n\n const eachBatch: EventStoreDBEventStoreMessagesBatchHandler<\n ConsumerEventType\n > = async (messagesBatch) => {\n const activeProcessors = processors.filter((s) => s.isActive);\n\n if (activeProcessors.length === 0)\n return {\n type: 'STOP',\n reason: 'No active processors',\n };\n\n const result = await Promise.allSettled(\n activeProcessors.map((s) => {\n // TODO: Add here filtering to only pass messages that can be handled by processor\n return s.handle(messagesBatch, { client });\n }),\n );\n\n return result.some(\n (r) => r.status === 'fulfilled' && r.value?.type !== 'STOP',\n )\n ? undefined\n : {\n type: 'STOP',\n };\n };\n\n const subscription = (currentSubscription = eventStoreDBSubscription({\n client,\n from: options.from,\n eachBatch,\n batchSize:\n pulling?.batchSize ?? DefaultEventStoreDBEventStoreProcessorBatchSize,\n }));\n\n const stop = async () => {\n if (!isRunning) return;\n isRunning = false;\n if (currentSubscription) {\n await currentSubscription.stop();\n currentSubscription = undefined;\n }\n await start;\n };\n\n return {\n processors,\n get isRunning() {\n return isRunning;\n },\n processor: <EventType extends ConsumerEventType = ConsumerEventType>(\n options: EventStoreDBEventStoreProcessorOptions<EventType>,\n ): EventStoreDBEventStoreProcessor<EventType> => {\n const processor = eventStoreDBEventStoreProcessor<EventType>(options);\n\n processors.push(processor);\n\n return processor;\n },\n start: () => {\n if (isRunning) return start;\n\n start = (async () => {\n if (processors.length === 0)\n return Promise.reject(\n new EmmettError(\n 'Cannot start consumer without at least a single processor',\n ),\n );\n\n isRunning = true;\n\n const startFrom = zipEventStoreDBEventStoreMessageBatchPullerStartFrom(\n await Promise.all(processors.map((o) => o.start(client))),\n );\n\n return subscription.start({ startFrom });\n })();\n\n return start;\n },\n stop,\n close: async () => {\n await stop();\n },\n };\n};\n","import {\n EmmettError,\n type Event,\n type ReadEvent,\n type ReadEventMetadataWithGlobalPosition,\n} from '@event-driven-io/emmett';\nimport type { EventStoreDBClient } from '@eventstore/db-client';\nimport { v7 as uuid } from 'uuid';\nimport type { EventStoreDBSubscriptionStartFrom } from './subscriptions';\n\nexport type EventStoreDBEventStoreProcessorEventsBatch<\n EventType extends Event = Event,\n> = {\n messages: ReadEvent<EventType, ReadEventMetadataWithGlobalPosition>[];\n};\n\nexport type EventStoreDBEventStoreProcessor<EventType extends Event = Event> = {\n id: string;\n start: (\n client: EventStoreDBClient,\n ) => Promise<EventStoreDBSubscriptionStartFrom | undefined>;\n isActive: boolean;\n handle: (\n messagesBatch: EventStoreDBEventStoreProcessorEventsBatch<EventType>,\n context: { client: EventStoreDBClient },\n ) => Promise<EventStoreDBEventStoreProcessorMessageHandlerResult>;\n};\n\nexport const EventStoreDBEventStoreProcessor = {\n result: {\n skip: (options?: {\n reason?: string;\n }): EventStoreDBEventStoreProcessorMessageHandlerResult => ({\n type: 'SKIP',\n ...(options ?? {}),\n }),\n stop: (options?: {\n reason?: string;\n error?: EmmettError;\n }): EventStoreDBEventStoreProcessorMessageHandlerResult => ({\n type: 'STOP',\n ...(options ?? {}),\n }),\n },\n};\n\nexport type EventStoreDBEventStoreProcessorMessageHandlerResult =\n | void\n | { type: 'SKIP'; reason?: string }\n | { type: 'STOP'; reason?: string; error?: EmmettError };\n\nexport type EventStoreDBEventStoreProcessorEachMessageHandler<\n EventType extends Event = Event,\n> = (\n event: ReadEvent<EventType, ReadEventMetadataWithGlobalPosition>,\n) =>\n | Promise<EventStoreDBEventStoreProcessorMessageHandlerResult>\n | EventStoreDBEventStoreProcessorMessageHandlerResult;\n\nexport type EventStoreDBEventStoreProcessorStartFrom =\n | EventStoreDBSubscriptionStartFrom\n | 'CURRENT';\n\nexport type EventStoreDBEventStoreProcessorOptions<\n EventType extends Event = Event,\n> = {\n processorId?: string;\n version?: number;\n partition?: string;\n startFrom?: EventStoreDBEventStoreProcessorStartFrom;\n stopAfter?: (\n message: ReadEvent<EventType, ReadEventMetadataWithGlobalPosition>,\n ) => boolean;\n eachMessage: EventStoreDBEventStoreProcessorEachMessageHandler<EventType>;\n};\n\nexport const eventStoreDBEventStoreProcessor = <\n EventType extends Event = Event,\n>(\n options: EventStoreDBEventStoreProcessorOptions<EventType>,\n): EventStoreDBEventStoreProcessor => {\n const { eachMessage } = options;\n let isActive = true;\n //let lastProcessedPosition: bigint | null = null;\n\n options.processorId = options.processorId ?? uuid();\n\n return {\n id: options.processorId,\n start: (\n _client: EventStoreDBClient,\n ): Promise<EventStoreDBSubscriptionStartFrom | undefined> => {\n isActive = true;\n if (options.startFrom !== 'CURRENT')\n return Promise.resolve(options.startFrom);\n\n // const { lastProcessedPosition } = await readProcessorCheckpoint(\n // execute,\n // {\n // processorId: options.processorId,\n // partition: options.partition,\n // },\n // );\n\n // if (lastProcessedPosition === null) return 'BEGINNING';\n\n // return { globalPosition: lastProcessedPosition };\n return Promise.resolve('BEGINNING');\n },\n get isActive() {\n return isActive;\n },\n handle: async ({\n messages,\n }): Promise<EventStoreDBEventStoreProcessorMessageHandlerResult> => {\n if (!isActive) return;\n\n let result:\n | EventStoreDBEventStoreProcessorMessageHandlerResult\n | undefined = undefined;\n\n //let lastProcessedPosition: bigint | null = null;\n\n for (const message of messages) {\n const typedMessage = message as ReadEvent<\n EventType,\n ReadEventMetadataWithGlobalPosition\n >;\n\n const messageProcessingResult = await eachMessage(typedMessage);\n\n // TODO: Add correct handling of the storing checkpoint\n // await storeProcessorCheckpoint(tx.execute, {\n // processorId: options.processorId,\n // version: options.version,\n // lastProcessedPosition,\n // newPosition: typedMessage.metadata.globalPosition,\n // partition: options.partition,\n // });\n\n //lastProcessedPosition = typedMessage.metadata.globalPosition;\n\n if (\n messageProcessingResult &&\n messageProcessingResult.type === 'STOP'\n ) {\n isActive = false;\n result = messageProcessingResult;\n break;\n }\n\n if (options.stopAfter && options.stopAfter(typedMessage)) {\n isActive = false;\n result = { type: 'STOP', reason: 'Stop condition reached' };\n break;\n }\n\n if (messageProcessingResult && messageProcessingResult.type === 'SKIP')\n continue;\n }\n\n return result;\n },\n };\n};\n","import type {\n EmmettError,\n Event,\n ReadEvent,\n ReadEventMetadataWithGlobalPosition,\n} from '@event-driven-io/emmett';\nimport {\n END,\n EventStoreDBClient,\n excludeSystemEvents,\n START,\n type JSONRecordedEvent,\n type StreamSubscription,\n} from '@eventstore/db-client';\nimport { finished, Readable } from 'stream';\nimport { mapFromESDBEvent } from '../../eventstoreDBEventStore';\nimport {\n $all,\n type EventStoreDBEventStoreConsumerType,\n} from '../eventStoreDBEventStoreConsumer';\n\nexport const DefaultEventStoreDBEventStoreProcessorBatchSize = 100;\nexport const DefaultEventStoreDBEventStoreProcessorPullingFrequencyInMs = 50;\n\nexport type EventStoreDBEventStoreMessagesBatch<\n EventType extends Event = Event,\n> = {\n messages: ReadEvent<EventType, ReadEventMetadataWithGlobalPosition>[];\n};\n\nexport type EventStoreDBEventStoreMessagesBatchHandlerResult = void | {\n type: 'STOP';\n reason?: string;\n error?: EmmettError;\n};\n\nexport type EventStoreDBEventStoreMessagesBatchHandler<\n EventType extends Event = Event,\n> = (\n messagesBatch: EventStoreDBEventStoreMessagesBatch<EventType>,\n) =>\n | Promise<EventStoreDBEventStoreMessagesBatchHandlerResult>\n | EventStoreDBEventStoreMessagesBatchHandlerResult;\n\nexport type EventStoreDBSubscriptionOptions<EventType extends Event = Event> = {\n from?: EventStoreDBEventStoreConsumerType;\n client: EventStoreDBClient;\n batchSize: number;\n eachBatch: EventStoreDBEventStoreMessagesBatchHandler<EventType>;\n};\n\nexport type EventStoreDBSubscriptionStartFrom =\n | { position: bigint }\n | 'BEGINNING'\n | 'END';\n\nexport type EventStoreDBSubscriptionStartOptions = {\n startFrom: EventStoreDBSubscriptionStartFrom;\n};\n\nexport type EventStoreDBEventStoreMessageBatchPuller = {\n isRunning: boolean;\n start(options: EventStoreDBSubscriptionStartOptions): Promise<void>;\n stop(): Promise<void>;\n};\n\nconst toGlobalPosition = (startFrom: EventStoreDBSubscriptionStartFrom) =>\n startFrom === 'BEGINNING'\n ? START\n : startFrom === 'END'\n ? END\n : {\n prepare: startFrom.position,\n commit: startFrom.position,\n };\n\nconst toStreamPosition = (startFrom: EventStoreDBSubscriptionStartFrom) =>\n startFrom === 'BEGINNING'\n ? START\n : startFrom === 'END'\n ? END\n : startFrom.position;\n\nconst subscribe = (\n client: EventStoreDBClient,\n from: EventStoreDBEventStoreConsumerType | undefined,\n options: EventStoreDBSubscriptionStartOptions,\n) =>\n from == undefined || from.stream == $all\n ? client.subscribeToAll({\n fromPosition: toGlobalPosition(options.startFrom),\n filter: excludeSystemEvents(),\n ...(from?.options ?? {}),\n })\n : client.subscribeToStream(from.stream, {\n fromRevision: toStreamPosition(options.startFrom),\n ...(from.options ?? {}),\n });\n\nexport const eventStoreDBSubscription = <EventType extends Event = Event>({\n client,\n from,\n //batchSize,\n eachBatch,\n}: EventStoreDBSubscriptionOptions<EventType>): EventStoreDBEventStoreMessageBatchPuller => {\n let isRunning = false;\n\n let start: Promise<void>;\n\n let subscription: StreamSubscription<EventType>;\n\n const pullMessages = async (\n options: EventStoreDBSubscriptionStartOptions,\n ) => {\n subscription = subscribe(client, from, options);\n\n return new Promise<void>((resolve, reject) => {\n finished(\n subscription.on('data', async (resolvedEvent) => {\n if (!resolvedEvent.event) return;\n\n const event = mapFromESDBEvent(\n resolvedEvent.event as JSONRecordedEvent<EventType>,\n );\n\n const result = await eachBatch({ messages: [event] });\n\n if (result && result.type === 'STOP') {\n await subscription.unsubscribe();\n }\n }) as unknown as Readable,\n (error) => {\n if (!error) {\n console.info(`Stopping subscription.`);\n resolve();\n return;\n }\n console.error(`Received error: ${JSON.stringify(error)}.`);\n reject(error);\n },\n );\n });\n };\n\n return {\n get isRunning() {\n return isRunning;\n },\n start: (options) => {\n if (isRunning) return start;\n\n start = (async () => {\n isRunning = true;\n\n return pullMessages(options);\n })();\n\n return start;\n },\n stop: async () => {\n if (!isRunning) return;\n isRunning = false;\n await subscription?.unsubscribe();\n await start;\n },\n };\n};\n\nexport const zipEventStoreDBEventStoreMessageBatchPullerStartFrom = (\n options: (EventStoreDBSubscriptionStartFrom | undefined)[],\n): EventStoreDBSubscriptionStartFrom => {\n if (\n options.length === 0 ||\n options.some((o) => o === undefined || o === 'BEGINNING')\n )\n return 'BEGINNING';\n\n if (options.every((o) => o === 'END')) return 'END';\n\n return options\n .filter((o) => o !== undefined && o !== 'BEGINNING' && o !== 'END')\n .sort((a, b) => (a > b ? 1 : -1))[0]!;\n};\n","import {\n ExpectedVersionConflictError,\n NO_CONCURRENCY_CHECK,\n STREAM_DOES_NOT_EXIST,\n STREAM_EXISTS,\n assertExpectedVersionMatchesCurrent,\n type AggregateStreamOptions,\n type AggregateStreamResultWithGlobalPosition,\n type AppendToStreamOptions,\n type AppendToStreamResultWithGlobalPosition,\n type Event,\n type EventStore,\n type ExpectedStreamVersion,\n type ReadEvent,\n type ReadEventMetadataWithGlobalPosition,\n type ReadStreamOptions,\n type ReadStreamResult,\n} from '@event-driven-io/emmett';\nimport {\n ANY,\n STREAM_EXISTS as ESDB_STREAM_EXISTS,\n EventStoreDBClient,\n NO_STREAM,\n StreamNotFoundError,\n WrongExpectedVersionError,\n jsonEvent,\n type AppendExpectedRevision,\n type ReadStreamOptions as ESDBReadStreamOptions,\n type JSONRecordedEvent,\n} from '@eventstore/db-client';\nimport {\n eventStoreDBEventStoreConsumer,\n type EventStoreDBEventStoreConsumer,\n type EventStoreDBEventStoreConsumerConfig,\n} from './consumers';\n\nconst toEventStoreDBReadOptions = (\n options: ReadStreamOptions | undefined,\n): ESDBReadStreamOptions | undefined => {\n return options\n ? {\n fromRevision: 'from' in options ? options.from : undefined,\n maxCount:\n 'maxCount' in options\n ? options.maxCount\n : 'to' in options\n ? options.to\n : undefined,\n }\n : undefined;\n};\n\nexport const EventStoreDBEventStoreDefaultStreamVersion = -1n;\n\nexport type EventStoreDBReadEventMetadata = ReadEventMetadataWithGlobalPosition;\n\nexport type EventStoreDBReadEvent<EventType extends Event = Event> = ReadEvent<\n EventType,\n EventStoreDBReadEventMetadata\n>;\n\nexport interface EventStoreDBEventStore\n extends EventStore<EventStoreDBReadEventMetadata> {\n appendToStream<EventType extends Event>(\n streamName: string,\n events: EventType[],\n options?: AppendToStreamOptions,\n ): Promise<AppendToStreamResultWithGlobalPosition>;\n consumer<ConsumerEventType extends Event = Event>(\n options?: EventStoreDBEventStoreConsumerConfig<ConsumerEventType>,\n ): EventStoreDBEventStoreConsumer<ConsumerEventType>;\n}\n\nexport const getEventStoreDBEventStore = (\n eventStore: EventStoreDBClient,\n): EventStoreDBEventStore => {\n return {\n async aggregateStream<State, EventType extends Event>(\n streamName: string,\n options: AggregateStreamOptions<\n State,\n EventType,\n EventStoreDBReadEventMetadata\n >,\n ): Promise<AggregateStreamResultWithGlobalPosition<State>> {\n const { evolve, initialState, read } = options;\n\n const expectedStreamVersion = read?.expectedStreamVersion;\n\n let state = initialState();\n let currentStreamVersion: bigint =\n EventStoreDBEventStoreDefaultStreamVersion;\n let lastEventGlobalPosition: bigint | undefined = undefined;\n\n try {\n for await (const { event } of eventStore.readStream<EventType>(\n streamName,\n toEventStoreDBReadOptions(options.read),\n )) {\n if (!event) continue;\n\n state = evolve(state, mapFromESDBEvent<EventType>(event));\n currentStreamVersion = event.revision;\n lastEventGlobalPosition = event.position?.commit;\n }\n\n assertExpectedVersionMatchesCurrent(\n currentStreamVersion,\n expectedStreamVersion,\n EventStoreDBEventStoreDefaultStreamVersion,\n );\n\n return lastEventGlobalPosition\n ? {\n currentStreamVersion,\n lastEventGlobalPosition,\n state,\n streamExists: true,\n }\n : {\n currentStreamVersion,\n state,\n streamExists: false,\n };\n } catch (error) {\n if (error instanceof StreamNotFoundError) {\n return {\n currentStreamVersion,\n state,\n streamExists: false,\n };\n }\n\n throw error;\n }\n },\n\n readStream: async <EventType extends Event>(\n streamName: string,\n options?: ReadStreamOptions,\n ): Promise<ReadStreamResult<EventType, EventStoreDBReadEventMetadata>> => {\n const events: ReadEvent<EventType, EventStoreDBReadEventMetadata>[] = [];\n\n let currentStreamVersion: bigint =\n EventStoreDBEventStoreDefaultStreamVersion;\n\n try {\n for await (const { event } of eventStore.readStream<EventType>(\n streamName,\n toEventStoreDBReadOptions(options),\n )) {\n if (!event) continue;\n events.push(mapFromESDBEvent(event));\n currentStreamVersion = event.revision;\n }\n return {\n currentStreamVersion,\n events,\n streamExists: true,\n };\n } catch (error) {\n if (error instanceof StreamNotFoundError) {\n return {\n currentStreamVersion,\n events: [],\n streamExists: false,\n };\n }\n\n throw error;\n }\n },\n\n appendToStream: async <EventType extends Event>(\n streamName: string,\n events: EventType[],\n options?: AppendToStreamOptions,\n ): Promise<AppendToStreamResultWithGlobalPosition> => {\n try {\n const serializedEvents = events.map(jsonEvent);\n\n const expectedRevision = toExpectedRevision(\n options?.expectedStreamVersion,\n );\n\n const appendResult = await eventStore.appendToStream(\n streamName,\n serializedEvents,\n {\n expectedRevision,\n },\n );\n\n return {\n nextExpectedStreamVersion: appendResult.nextExpectedRevision,\n lastEventGlobalPosition: appendResult.position!.commit,\n createdNewStream:\n appendResult.nextExpectedRevision >=\n BigInt(serializedEvents.length),\n };\n } catch (error) {\n if (error instanceof WrongExpectedVersionError) {\n throw new ExpectedVersionConflictError(\n error.actualVersion,\n toExpectedVersion(error.expectedVersion),\n );\n }\n\n throw error;\n }\n },\n\n consumer: <ConsumerEventType extends Event = Event>(\n options?: EventStoreDBEventStoreConsumerConfig<ConsumerEventType>,\n ): EventStoreDBEventStoreConsumer<ConsumerEventType> =>\n eventStoreDBEventStoreConsumer<ConsumerEventType>({\n ...(options ?? {}),\n client: eventStore,\n }),\n\n //streamEvents: streamEvents(eventStore),\n };\n};\n\nexport const mapFromESDBEvent = <EventType extends Event = Event>(\n event: JSONRecordedEvent<EventType>,\n): ReadEvent<EventType, EventStoreDBReadEventMetadata> => {\n return <ReadEvent<EventType, EventStoreDBReadEventMetadata>>{\n type: event.type,\n data: event.data,\n metadata: {\n ...((event.metadata as EventStoreDBReadEventMetadata) ??\n ({} as EventStoreDBReadEventMetadata)),\n eventId: event.id,\n streamName: event.streamId,\n streamPosition: event.revision,\n globalPosition: event.position!.commit,\n },\n };\n};\n\nconst toExpectedRevision = (\n expected: ExpectedStreamVersion | undefined,\n): AppendExpectedRevision => {\n if (expected === undefined) return ANY;\n\n if (expected === NO_CONCURRENCY_CHECK) return ANY;\n\n if (expected == STREAM_DOES_NOT_EXIST) return NO_STREAM;\n\n if (expected == STREAM_EXISTS) return ESDB_STREAM_EXISTS;\n\n return expected as bigint;\n};\n\nconst toExpectedVersion = (\n expected: AppendExpectedRevision | undefined,\n): ExpectedStreamVersion => {\n if (expected === undefined) return NO_CONCURRENCY_CHECK;\n\n if (expected === ANY) return NO_CONCURRENCY_CHECK;\n\n if (expected == NO_STREAM) return STREAM_DOES_NOT_EXIST;\n\n if (expected == ESDB_STREAM_EXISTS) return STREAM_EXISTS;\n\n return expected;\n};\n\n// const { map } = streamTransformations;\n//\n// // eslint-disable-next-line @typescript-eslint/no-unused-vars\n// const convertToWebReadableStream = (\n// allStreamSubscription: AllStreamSubscription,\n// ): ReadableStream<AllStreamResolvedEvent | GlobalStreamCaughtUp> => {\n// // Validate the input type\n// if (!(allStreamSubscription instanceof Readable)) {\n// throw new Error('Provided stream is not a Node.js Readable stream.');\n// }\n\n// let globalPosition = 0n;\n\n// const stream = Readable.toWeb(\n// allStreamSubscription,\n// ) as ReadableStream<AllStreamResolvedEvent>;\n\n// const writable = new WritableStream<\n// AllStreamResolvedEvent | GlobalStreamCaughtUp\n// >();\n\n// allStreamSubscription.on('caughtUp', async () => {\n// console.log(globalPosition);\n// await writable.getWriter().write(globalStreamCaughtUp({ globalPosition }));\n// });\n\n// const transform = map<\n// AllStreamResolvedEvent,\n// AllStreamResolvedEvent | GlobalStreamCaughtUp\n// >((event) => {\n// if (event?.event?.position.commit)\n// globalPosition = event.event?.position.commit;\n\n// return event;\n// });\n\n// return stream.pipeThrough<AllStreamResolvedEvent | GlobalStreamCaughtUp>(\n// transform,\n// );\n// };\n\n// const streamEvents = (eventStore: EventStoreDBClient) => () => {\n// return restream<\n// AllStreamResolvedEvent | GlobalSubscriptionEvent,\n// | ReadEvent<Event, EventStoreDBReadEventMetadata>\n// | GlobalSubscriptionEvent\n// >(\n// (): ReadableStream<AllStreamResolvedEvent | GlobalSubscriptionEvent> =>\n// convertToWebReadableStream(\n// eventStore.subscribeToAll({\n// fromPosition: START,\n// filter: excludeSystemEvents(),\n// }),\n// ),\n// (\n// resolvedEvent: AllStreamResolvedEvent | GlobalSubscriptionEvent,\n// ): ReadEvent<Event, EventStoreDBReadEventMetadata> =>\n// mapFromESDBEvent(resolvedEvent.event as JSONRecordedEvent<Event>),\n// );\n// };\n"]}
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/emmett/emmett/src/packages/emmett-esdb/dist/index.cjs","../../emmett/src/validation/index.ts","../../emmett/src/errors/index.ts","../../emmett/src/eventStore/inMemoryEventStore.ts","../../emmett/src/eventStore/subscriptions/caughtUpTransformStream.ts","../../emmett/src/eventStore/subscriptions/streamingCoordinator.ts","../../emmett/src/streaming/transformations/notifyAboutNoActiveReaders.ts","../../emmett/src/utils/retry.ts","../../emmett/src/database/inMemoryDatabase.ts","../../emmett/src/streaming/generators/fromArray.ts","../../emmett/src/streaming/restream.ts","../../emmett/src/streaming/transformations/filter.ts","../../emmett/src/streaming/transformations/map.ts","../../emmett/src/streaming/transformations/reduce.ts","../../emmett/src/streaming/transformations/retry.ts","../../emmett/src/streaming/transformations/skip.ts","../../emmett/src/streaming/transformations/stopAfter.ts","../../emmett/src/streaming/transformations/stopOn.ts","../../emmett/src/streaming/transformations/take.ts","../../emmett/src/streaming/transformations/waitAtMost.ts","../../emmett/src/eventStore/expectedVersion.ts","../../emmett/src/serialization/json/JSONParser.ts","../../emmett/src/streaming/transformations/index.ts","../src/eventStore/consumers/eventStoreDBEventStoreConsumer.ts","../src/eventStore/consumers/eventStoreDBEventStoreProcessor.ts","../src/eventStore/consumers/subscriptions/index.ts","../src/eventStore/eventstoreDBEventStore.ts"],"names":[],"mappings":"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","file":"/home/runner/work/emmett/emmett/src/packages/emmett-esdb/dist/index.cjs","sourcesContent":[null,"import { ValidationError } from '../errors';\n\nexport const enum ValidationErrors {\n NOT_A_NONEMPTY_STRING = 'NOT_A_NONEMPTY_STRING',\n NOT_A_POSITIVE_NUMBER = 'NOT_A_POSITIVE_NUMBER',\n NOT_AN_UNSIGNED_BIGINT = 'NOT_AN_UNSIGNED_BIGINT',\n}\n\nexport const isNumber = (val: unknown): val is number =>\n typeof val === 'number' && val === val;\n\nexport const isString = (val: unknown): val is string =>\n typeof val === 'string';\n\nexport const assertNotEmptyString = (value: unknown): string => {\n if (!isString(value) || value.length === 0) {\n throw new ValidationError(ValidationErrors.NOT_A_NONEMPTY_STRING);\n }\n return value;\n};\n\nexport const assertPositiveNumber = (value: unknown): number => {\n if (!isNumber(value) || value <= 0) {\n throw new ValidationError(ValidationErrors.NOT_A_POSITIVE_NUMBER);\n }\n return value;\n};\n\nexport const assertUnsignedBigInt = (value: string): bigint => {\n const number = BigInt(value);\n if (number < 0) {\n throw new ValidationError(ValidationErrors.NOT_AN_UNSIGNED_BIGINT);\n }\n return number;\n};\n\nexport * from './dates';\n","import { isNumber, isString } from '../validation';\n\nexport type ErrorConstructor<ErrorType extends Error> = new (\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ...args: any[]\n) => ErrorType;\n\nexport const isErrorConstructor = <ErrorType extends Error>(\n // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\n expect: Function,\n): expect is ErrorConstructor<ErrorType> => {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return (\n typeof expect === 'function' &&\n expect.prototype &&\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n expect.prototype.constructor === expect\n );\n};\n\nexport class EmmettError extends Error {\n public errorCode: number;\n\n constructor(\n options?: { errorCode: number; message?: string } | string | number,\n ) {\n const errorCode =\n options && typeof options === 'object' && 'errorCode' in options\n ? options.errorCode\n : isNumber(options)\n ? options\n : 500;\n const message =\n options && typeof options === 'object' && 'message' in options\n ? options.message\n : isString(options)\n ? options\n : `Error with status code '${errorCode}' ocurred during Emmett processing`;\n\n super(message);\n this.errorCode = errorCode;\n\n // 👇️ because we are extending a built-in class\n Object.setPrototypeOf(this, EmmettError.prototype);\n }\n}\n\nexport class ConcurrencyError extends EmmettError {\n constructor(\n public current: string | undefined,\n public expected: string,\n message?: string,\n ) {\n super({\n errorCode: 412,\n message:\n message ??\n `Expected version ${expected.toString()} does not match current ${current?.toString()}`,\n });\n\n // 👇️ because we are extending a built-in class\n Object.setPrototypeOf(this, ConcurrencyError.prototype);\n }\n}\n\nexport class ConcurrencyInMemoryDatabaseError extends EmmettError {\n constructor(message?: string) {\n super({\n errorCode: 412,\n message: message ?? `Expected document state does not match current one!`,\n });\n\n // 👇️ because we are extending a built-in class\n Object.setPrototypeOf(this, ConcurrencyInMemoryDatabaseError.prototype);\n }\n}\n\nexport class ValidationError extends EmmettError {\n constructor(message?: string) {\n super({\n errorCode: 400,\n message: message ?? `Validation Error ocurred during Emmett processing`,\n });\n\n // 👇️ because we are extending a built-in class\n Object.setPrototypeOf(this, ValidationError.prototype);\n }\n}\n\nexport class IllegalStateError extends EmmettError {\n constructor(message?: string) {\n super({\n errorCode: 403,\n message: message ?? `Illegal State ocurred during Emmett processing`,\n });\n\n // 👇️ because we are extending a built-in class\n Object.setPrototypeOf(this, IllegalStateError.prototype);\n }\n}\n\nexport class NotFoundError extends EmmettError {\n constructor(options?: { id: string; type: string; message?: string }) {\n super({\n errorCode: 404,\n message:\n options?.message ??\n (options?.id\n ? options.type\n ? `${options.type} with ${options.id} was not found during Emmett processing`\n : `State with ${options.id} was not found during Emmett processing`\n : options?.type\n ? `${options.type} was not found during Emmett processing`\n : 'State was not found during Emmett processing'),\n });\n\n // 👇️ because we are extending a built-in class\n Object.setPrototypeOf(this, NotFoundError.prototype);\n }\n}\n","import { v4 as uuid } from 'uuid';\nimport type {\n BigIntStreamPosition,\n CombinedReadEventMetadata,\n Event,\n ReadEvent,\n ReadEventMetadataWithGlobalPosition,\n} from '../typing';\nimport { tryPublishMessagesAfterCommit } from './afterCommit';\nimport {\n type AggregateStreamOptions,\n type AggregateStreamResult,\n type AppendToStreamOptions,\n type AppendToStreamResult,\n type DefaultEventStoreOptions,\n type EventStore,\n type ReadStreamOptions,\n type ReadStreamResult,\n} from './eventStore';\nimport { assertExpectedVersionMatchesCurrent } from './expectedVersion';\nimport { StreamingCoordinator } from './subscriptions';\nimport type { ProjectionRegistration } from '../projections';\n\nexport const InMemoryEventStoreDefaultStreamVersion = 0n;\n\nexport type InMemoryEventStore =\n EventStore<ReadEventMetadataWithGlobalPosition>;\n\nexport type InMemoryReadEventMetadata = ReadEventMetadataWithGlobalPosition;\n\nexport type InMemoryProjectionHandlerContext = {\n eventStore: InMemoryEventStore;\n};\n\nexport type InMemoryEventStoreOptions =\n DefaultEventStoreOptions<InMemoryEventStore> & {\n projections?: ProjectionRegistration<\n 'inline',\n InMemoryReadEventMetadata,\n InMemoryProjectionHandlerContext\n >[];\n };\n\nexport type InMemoryReadEvent<EventType extends Event = Event> = ReadEvent<\n EventType,\n ReadEventMetadataWithGlobalPosition\n>;\n\nexport const getInMemoryEventStore = (\n eventStoreOptions?: InMemoryEventStoreOptions,\n): InMemoryEventStore => {\n const streams = new Map<\n string,\n ReadEvent<Event, ReadEventMetadataWithGlobalPosition>[]\n >();\n const streamingCoordinator = StreamingCoordinator();\n\n const getAllEventsCount = () => {\n return Array.from<ReadEvent[]>(streams.values())\n .map((s) => s.length)\n .reduce((p, c) => p + c, 0);\n };\n\n const _inlineProjections = (eventStoreOptions?.projections ?? [])\n .filter(({ type }) => type === 'inline')\n .map(({ projection }) => projection);\n\n return {\n async aggregateStream<State, EventType extends Event>(\n streamName: string,\n options: AggregateStreamOptions<\n State,\n EventType,\n ReadEventMetadataWithGlobalPosition\n >,\n ): Promise<AggregateStreamResult<State>> {\n const { evolve, initialState, read } = options;\n\n const result = await this.readStream<EventType>(streamName, read);\n\n const events = result?.events ?? [];\n\n return {\n currentStreamVersion: BigInt(events.length),\n state: events.reduce(evolve, initialState()),\n streamExists: result.streamExists,\n };\n },\n\n readStream: <EventType extends Event>(\n streamName: string,\n options?: ReadStreamOptions<BigIntStreamPosition>,\n ): Promise<\n ReadStreamResult<EventType, ReadEventMetadataWithGlobalPosition>\n > => {\n const events = streams.get(streamName);\n const currentStreamVersion = events\n ? BigInt(events.length)\n : InMemoryEventStoreDefaultStreamVersion;\n\n assertExpectedVersionMatchesCurrent(\n currentStreamVersion,\n options?.expectedStreamVersion,\n InMemoryEventStoreDefaultStreamVersion,\n );\n\n const from = Number(options && 'from' in options ? options.from : 0);\n const to = Number(\n options && 'to' in options\n ? options.to\n : options && 'maxCount' in options && options.maxCount\n ? options.from + options.maxCount\n : (events?.length ?? 1),\n );\n\n const resultEvents =\n events !== undefined && events.length > 0\n ? events\n .map(\n (e) =>\n e as ReadEvent<\n EventType,\n ReadEventMetadataWithGlobalPosition\n >,\n )\n .slice(from, to)\n : [];\n\n const result: ReadStreamResult<\n EventType,\n ReadEventMetadataWithGlobalPosition\n > = {\n currentStreamVersion,\n events: resultEvents,\n streamExists: events !== undefined && events.length > 0,\n };\n\n return Promise.resolve(result);\n },\n\n appendToStream: async <EventType extends Event>(\n streamName: string,\n events: EventType[],\n options?: AppendToStreamOptions,\n ): Promise<AppendToStreamResult> => {\n const currentEvents = streams.get(streamName) ?? [];\n const currentStreamVersion =\n currentEvents.length > 0\n ? BigInt(currentEvents.length)\n : InMemoryEventStoreDefaultStreamVersion;\n\n assertExpectedVersionMatchesCurrent(\n currentStreamVersion,\n options?.expectedStreamVersion,\n InMemoryEventStoreDefaultStreamVersion,\n );\n\n const newEvents: ReadEvent<\n EventType,\n ReadEventMetadataWithGlobalPosition\n >[] = events.map((event, index) => {\n const metadata: ReadEventMetadataWithGlobalPosition = {\n streamName,\n messageId: uuid(),\n streamPosition: BigInt(currentEvents.length + index + 1),\n globalPosition: BigInt(getAllEventsCount() + index + 1),\n };\n return {\n ...event,\n kind: event.kind ?? 'Event',\n metadata: {\n ...('metadata' in event ? (event.metadata ?? {}) : {}),\n ...metadata,\n } as CombinedReadEventMetadata<\n EventType,\n ReadEventMetadataWithGlobalPosition\n >,\n };\n });\n\n const positionOfLastEventInTheStream = BigInt(\n newEvents.slice(-1)[0]!.metadata.streamPosition,\n );\n\n streams.set(streamName, [...currentEvents, ...newEvents]);\n await streamingCoordinator.notify(newEvents);\n\n const result: AppendToStreamResult = {\n nextExpectedStreamVersion: positionOfLastEventInTheStream,\n createdNewStream:\n currentStreamVersion === InMemoryEventStoreDefaultStreamVersion,\n };\n\n await tryPublishMessagesAfterCommit<InMemoryEventStore>(\n newEvents,\n eventStoreOptions?.hooks,\n );\n\n return result;\n },\n\n //streamEvents: streamingCoordinator.stream,\n };\n};\n","import { TransformStream } from 'web-streams-polyfill';\nimport type {\n Event,\n ReadEvent,\n ReadEventMetadataWithGlobalPosition,\n} from '../../typing';\nimport { globalStreamCaughtUp, type GlobalSubscriptionEvent } from '../events';\n\nexport const streamTrackingGlobalPosition = (\n currentEvents: ReadEvent<Event, ReadEventMetadataWithGlobalPosition>[],\n) => new CaughtUpTransformStream(currentEvents);\n\nexport class CaughtUpTransformStream extends TransformStream<\n ReadEvent<Event, ReadEventMetadataWithGlobalPosition>,\n | ReadEvent<Event, ReadEventMetadataWithGlobalPosition>\n | GlobalSubscriptionEvent\n> {\n private _currentPosition: bigint;\n private _logPosition: bigint;\n\n constructor(events: ReadEvent<Event, ReadEventMetadataWithGlobalPosition>[]) {\n super({\n start: (controller) => {\n let globalPosition = 0n;\n for (const event of events) {\n controller.enqueue(event);\n globalPosition = event.metadata.globalPosition;\n }\n controller.enqueue(globalStreamCaughtUp({ globalPosition }));\n },\n transform: (event, controller) => {\n this._currentPosition = event.metadata.globalPosition;\n controller.enqueue(event);\n\n if (this._currentPosition < this._logPosition) return;\n\n controller.enqueue(\n globalStreamCaughtUp({ globalPosition: this._currentPosition }),\n );\n },\n });\n\n this._currentPosition = this._logPosition =\n events.length > 0\n ? events[events.length - 1]!.metadata.globalPosition\n : 0n;\n }\n\n public set logPosition(value: bigint) {\n this._logPosition = value;\n }\n}\n","import { v4 as uuid } from 'uuid';\nimport { notifyAboutNoActiveReadersStream } from '../../streaming/transformations/notifyAboutNoActiveReaders';\nimport { writeToStream } from '../../streaming/writers';\nimport type {\n Event,\n ReadEvent,\n ReadEventMetadataWithGlobalPosition,\n} from '../../typing';\nimport {\n CaughtUpTransformStream,\n streamTrackingGlobalPosition,\n} from './caughtUpTransformStream';\n\nexport const StreamingCoordinator = () => {\n const allEvents: ReadEvent<Event, ReadEventMetadataWithGlobalPosition>[] = [];\n const listeners = new Map<string, CaughtUpTransformStream>();\n\n return {\n notify: async (\n events: ReadEvent<Event, ReadEventMetadataWithGlobalPosition>[],\n ) => {\n if (events.length === 0) return;\n\n allEvents.push(...events);\n\n for (const listener of listeners.values()) {\n listener.logPosition =\n events[events.length - 1]!.metadata.globalPosition;\n\n await writeToStream(listener, events);\n }\n },\n\n stream: () => {\n const streamId = uuid();\n const transformStream = streamTrackingGlobalPosition(allEvents);\n\n listeners.set(streamId, transformStream);\n return transformStream.readable.pipeThrough(\n notifyAboutNoActiveReadersStream(\n (stream) => {\n if (listeners.has(stream.streamId))\n listeners.delete(stream.streamId);\n },\n { streamId },\n ),\n );\n },\n };\n};\n","import { v4 as uuid } from 'uuid';\nimport { TransformStream } from 'web-streams-polyfill';\n\nexport const notifyAboutNoActiveReadersStream = <Item>(\n onNoActiveReaderCallback: (\n stream: NotifyAboutNoActiveReadersStream<Item>,\n ) => void,\n options: { streamId?: string; intervalCheckInMs?: number } = {},\n) => new NotifyAboutNoActiveReadersStream(onNoActiveReaderCallback, options);\n\nexport class NotifyAboutNoActiveReadersStream<Item> extends TransformStream<\n Item,\n Item\n> {\n private checkInterval: NodeJS.Timeout | null = null;\n public readonly streamId: string;\n private _isStopped: boolean = false;\n public get hasActiveSubscribers() {\n return !this._isStopped;\n }\n\n constructor(\n private onNoActiveReaderCallback: (\n stream: NotifyAboutNoActiveReadersStream<Item>,\n ) => void,\n options: { streamId?: string; intervalCheckInMs?: number } = {},\n ) {\n super({\n cancel: (reason) => {\n console.log('Stream was canceled. Reason:', reason);\n this.stopChecking();\n },\n });\n this.streamId = options?.streamId ?? uuid();\n\n this.onNoActiveReaderCallback = onNoActiveReaderCallback;\n\n this.startChecking(options?.intervalCheckInMs ?? 20);\n }\n\n private startChecking(interval: number) {\n this.checkInterval = setInterval(() => {\n this.checkNoActiveReader();\n }, interval);\n }\n\n private stopChecking() {\n if (!this.checkInterval) return;\n\n clearInterval(this.checkInterval);\n this.checkInterval = null;\n this._isStopped = true;\n this.onNoActiveReaderCallback(this);\n }\n\n private checkNoActiveReader() {\n if (!this.readable.locked && !this._isStopped) {\n this.stopChecking();\n }\n }\n}\n","import retry from 'async-retry';\nimport { EmmettError } from '../errors';\nimport { JSONParser } from '../serialization';\n\nexport type AsyncRetryOptions<T = unknown> = retry.Options & {\n shouldRetryResult?: (result: T) => boolean;\n shouldRetryError?: (error?: unknown) => boolean;\n};\n\nexport const NoRetries: AsyncRetryOptions = { retries: 0 };\n\nexport const asyncRetry = async <T>(\n fn: () => Promise<T>,\n opts?: AsyncRetryOptions<T>,\n): Promise<T> => {\n if (opts === undefined || opts.retries === 0) return fn();\n\n return retry(\n async (bail) => {\n try {\n const result = await fn();\n\n if (opts?.shouldRetryResult && opts.shouldRetryResult(result)) {\n throw new EmmettError(\n `Retrying because of result: ${JSONParser.stringify(result)}`,\n );\n }\n return result;\n } catch (error) {\n if (opts?.shouldRetryError && !opts.shouldRetryError(error)) {\n bail(error as Error);\n }\n throw error;\n }\n },\n opts ?? { retries: 0 },\n );\n};\n","import { v7 as uuid } from 'uuid';\nimport { deepEquals } from '../utils';\nimport {\n type DeleteResult,\n type Document,\n type DocumentHandler,\n type HandleOptionErrors,\n type HandleOptions,\n type HandleResult,\n type InsertOneResult,\n type OptionalUnlessRequiredIdAndVersion,\n type ReplaceOneOptions,\n type UpdateResult,\n type WithoutId,\n type WithIdAndVersion,\n} from './types';\nimport { expectedVersionValue, operationResult } from './utils';\n\nexport interface DocumentsCollection<T extends Document> {\n handle: (\n id: string,\n handle: DocumentHandler<T>,\n options?: HandleOptions,\n ) => HandleResult<T>;\n findOne: (predicate?: Predicate<T>) => T | null;\n find: (predicate?: Predicate<T>) => T[];\n insertOne: (\n document: OptionalUnlessRequiredIdAndVersion<T>,\n ) => InsertOneResult;\n deleteOne: (predicate?: Predicate<T>) => DeleteResult;\n replaceOne: (\n predicate: Predicate<T>,\n document: WithoutId<T>,\n options?: ReplaceOneOptions,\n ) => UpdateResult;\n}\n\nexport interface Database {\n collection: <T extends Document>(name: string) => DocumentsCollection<T>;\n}\n\ntype Predicate<T> = (item: T) => boolean;\ntype CollectionName = string;\n\nexport const getInMemoryDatabase = (): Database => {\n const storage = new Map<CollectionName, WithIdAndVersion<Document>[]>();\n\n return {\n collection: <T extends Document, CollectionName extends string>(\n collectionName: CollectionName,\n collectionOptions: {\n errors?: HandleOptionErrors;\n } = {},\n ): DocumentsCollection<T> => {\n const ensureCollectionCreated = () => {\n if (!storage.has(collectionName)) storage.set(collectionName, []);\n };\n\n const errors = collectionOptions.errors;\n\n const collection = {\n collectionName,\n insertOne: (\n document: OptionalUnlessRequiredIdAndVersion<T>,\n ): InsertOneResult => {\n ensureCollectionCreated();\n\n const _id = (document._id as string | undefined | null) ?? uuid();\n const _version = document._version ?? 1n;\n\n const existing = collection.findOne((c) => c._id === _id);\n\n if (existing) {\n return operationResult<InsertOneResult>(\n {\n successful: false,\n insertedId: null,\n nextExpectedVersion: _version,\n },\n { operationName: 'insertOne', collectionName, errors },\n );\n }\n\n const documentsInCollection = storage.get(collectionName)!;\n const newDocument = { ...document, _id, _version };\n const newCollection = [...documentsInCollection, newDocument];\n storage.set(collectionName, newCollection);\n\n return operationResult<InsertOneResult>(\n {\n successful: true,\n insertedId: _id,\n nextExpectedVersion: _version,\n },\n { operationName: 'insertOne', collectionName, errors },\n );\n },\n findOne: (predicate?: Predicate<T>): T | null => {\n ensureCollectionCreated();\n\n const documentsInCollection = storage.get(collectionName);\n const filteredDocuments = predicate\n ? documentsInCollection?.filter((doc) => predicate(doc as T))\n : documentsInCollection;\n\n const firstOne = filteredDocuments?.[0] ?? null;\n\n return firstOne as T | null;\n },\n find: (predicate?: Predicate<T>): T[] => {\n ensureCollectionCreated();\n\n const documentsInCollection = storage.get(collectionName);\n const filteredDocuments = predicate\n ? documentsInCollection?.filter((doc) => predicate(doc as T))\n : documentsInCollection;\n\n return filteredDocuments as T[];\n },\n deleteOne: (predicate?: Predicate<T>): DeleteResult => {\n ensureCollectionCreated();\n\n const documentsInCollection = storage.get(collectionName)!;\n\n if (predicate) {\n const foundIndex = documentsInCollection.findIndex((doc) =>\n predicate(doc as T),\n );\n\n if (foundIndex === -1) {\n return operationResult<DeleteResult>(\n {\n successful: false,\n matchedCount: 0,\n deletedCount: 0,\n },\n { operationName: 'deleteOne', collectionName, errors },\n );\n } else {\n const newCollection = documentsInCollection.toSpliced(\n foundIndex,\n 1,\n );\n\n storage.set(collectionName, newCollection);\n\n return operationResult<DeleteResult>(\n {\n successful: true,\n matchedCount: 1,\n deletedCount: 1,\n },\n { operationName: 'deleteOne', collectionName, errors },\n );\n }\n }\n\n const newCollection = documentsInCollection.slice(1);\n\n storage.set(collectionName, newCollection);\n\n return operationResult<DeleteResult>(\n {\n successful: true,\n matchedCount: 1,\n deletedCount: 1,\n },\n { operationName: 'deleteOne', collectionName, errors },\n );\n },\n replaceOne: (\n predicate: Predicate<T>,\n document: WithoutId<T>,\n options?: ReplaceOneOptions,\n ): UpdateResult => {\n ensureCollectionCreated();\n\n const documentsInCollection = storage.get(collectionName)!;\n\n const foundIndexes = documentsInCollection\n .filter((doc) => predicate(doc as T))\n .map((_, index) => index);\n\n const firstIndex = foundIndexes[0];\n\n if (firstIndex === undefined || firstIndex === -1) {\n return operationResult<UpdateResult>(\n {\n successful: false,\n matchedCount: 0,\n modifiedCount: 0,\n nextExpectedVersion: 0n,\n },\n { operationName: 'replaceOne', collectionName, errors },\n );\n }\n\n const existing = documentsInCollection[firstIndex]!;\n\n if (\n typeof options?.expectedVersion === 'bigint' &&\n existing._version !== options.expectedVersion\n ) {\n return operationResult<UpdateResult>(\n {\n successful: false,\n matchedCount: 1,\n modifiedCount: 0,\n nextExpectedVersion: existing._version,\n },\n { operationName: 'replaceOne', collectionName, errors },\n );\n }\n\n const newVersion = existing._version + 1n;\n\n const newCollection = documentsInCollection.with(firstIndex, {\n _id: existing._id,\n ...document,\n _version: newVersion,\n });\n\n storage.set(collectionName, newCollection);\n\n return operationResult<UpdateResult>(\n {\n successful: true,\n modifiedCount: 1,\n matchedCount: foundIndexes.length,\n nextExpectedVersion: newVersion,\n },\n { operationName: 'replaceOne', collectionName, errors },\n );\n },\n handle: (\n id: string,\n handle: DocumentHandler<T>,\n options?: HandleOptions,\n ): HandleResult<T> => {\n const { expectedVersion: version, ...operationOptions } =\n options ?? {};\n ensureCollectionCreated();\n const existing = collection.findOne(({ _id }) => _id === id);\n\n const expectedVersion = expectedVersionValue(version);\n\n if (\n (existing == null && version === 'DOCUMENT_EXISTS') ||\n (existing == null && expectedVersion != null) ||\n (existing != null && version === 'DOCUMENT_DOES_NOT_EXIST') ||\n (existing != null &&\n expectedVersion !== null &&\n existing._version !== expectedVersion)\n ) {\n return operationResult<HandleResult<T>>(\n {\n successful: false,\n document: existing as WithIdAndVersion<T>,\n },\n { operationName: 'handle', collectionName, errors },\n );\n }\n\n const result = handle(existing !== null ? { ...existing } : null);\n\n if (deepEquals(existing, result))\n return operationResult<HandleResult<T>>(\n {\n successful: true,\n document: existing as WithIdAndVersion<T>,\n },\n { operationName: 'handle', collectionName, errors },\n );\n\n if (!existing && result) {\n const newDoc = { ...result, _id: id };\n const insertResult = collection.insertOne({\n ...newDoc,\n _id: id,\n } as OptionalUnlessRequiredIdAndVersion<T>);\n return {\n ...insertResult,\n document: {\n ...newDoc,\n _version: insertResult.nextExpectedVersion,\n } as unknown as WithIdAndVersion<T>,\n };\n }\n\n if (existing && !result) {\n const deleteResult = collection.deleteOne(({ _id }) => id === _id);\n return { ...deleteResult, document: null };\n }\n\n if (existing && result) {\n const replaceResult = collection.replaceOne(\n ({ _id }) => id === _id,\n result,\n {\n ...operationOptions,\n expectedVersion: expectedVersion ?? 'DOCUMENT_EXISTS',\n },\n );\n return {\n ...replaceResult,\n document: {\n ...result,\n _version: replaceResult.nextExpectedVersion,\n } as unknown as WithIdAndVersion<T>,\n };\n }\n\n return operationResult<HandleResult<T>>(\n {\n successful: true,\n document: existing as WithIdAndVersion<T>,\n },\n { operationName: 'handle', collectionName, errors },\n );\n },\n };\n\n return collection;\n },\n };\n};\n","import { ReadableStream } from 'web-streams-polyfill';\n\nexport const fromArray = <T>(chunks: T[]) =>\n new ReadableStream<T>({\n start(controller) {\n for (const chunk of chunks) controller.enqueue(chunk);\n controller.close();\n },\n });\n","import {\n type ReadableStream,\n type ReadableStreamDefaultReadResult,\n type TransformStreamDefaultController,\n} from 'web-streams-polyfill';\nimport type { AsyncRetryOptions } from '../utils';\nimport type { Decoder } from './decoders';\nimport { DefaultDecoder } from './decoders/composite';\nimport { streamTransformations } from './transformations';\n\nconst { retry } = streamTransformations;\n\nexport const restream = <\n Source = unknown,\n Transformed = Source,\n StreamType = Source,\n>(\n createSourceStream: () => ReadableStream<StreamType>,\n transform: (input: Source) => Transformed = (source) =>\n source as unknown as Transformed,\n retryOptions: AsyncRetryOptions = { forever: true, minTimeout: 25 },\n decoder: Decoder<StreamType, Source> = new DefaultDecoder<Source>(),\n): ReadableStream<Transformed> =>\n retry(createSourceStream, handleChunk(transform, decoder), retryOptions)\n .readable;\n\nconst handleChunk =\n <Source = unknown, Transformed = Source, StreamType = Source>(\n transform: (input: Source) => Transformed = (source) =>\n source as unknown as Transformed,\n decoder: Decoder<StreamType, Source> = new DefaultDecoder<Source>(),\n ) =>\n (\n readResult: ReadableStreamDefaultReadResult<StreamType>,\n controller: TransformStreamDefaultController<Transformed>,\n ): void => {\n const { done: isDone, value } = readResult;\n\n if (value) decoder.addToBuffer(value);\n\n if (!isDone && !decoder.hasCompleteMessage()) return;\n\n decodeAndTransform(decoder, transform, controller);\n };\n\nconst decodeAndTransform = <StreamType, Source, Transformed = Source>(\n decoder: Decoder<StreamType, Source>,\n transform: (input: Source) => Transformed,\n controller: TransformStreamDefaultController<Transformed>,\n) => {\n try {\n const decoded = decoder.decode();\n if (!decoded) return; // TODO: Add a proper handling of decode errors\n\n const transformed = transform(decoded);\n controller.enqueue(transformed);\n } catch (error) {\n controller.error(new Error(`Decoding error: ${error?.toString()}`));\n }\n};\n","import { TransformStream } from 'web-streams-polyfill';\n\nexport const filter = <Item>(filter: (item: Item) => boolean) =>\n new TransformStream<Item, Item>({\n transform(chunk, controller) {\n if (filter(chunk)) {\n controller.enqueue(chunk);\n }\n },\n });\n","import { TransformStream } from 'web-streams-polyfill';\n\nexport const map = <From, To>(map: (item: From) => To) =>\n new TransformStream<From, To>({\n transform(chunk, controller) {\n controller.enqueue(map(chunk));\n },\n });\n","import { TransformStream } from 'web-streams-polyfill';\n\nexport const reduce = <I, O>(\n reducer: (accumulator: O, chunk: I) => O,\n initialValue: O,\n) => new ReduceTransformStream<I, O>(reducer, initialValue);\n\nexport class ReduceTransformStream<I, O> extends TransformStream<I, O> {\n private accumulator: O;\n private reducer: (accumulator: O, chunk: I) => O;\n\n constructor(reducer: (accumulator: O, chunk: I) => O, initialValue: O) {\n super({\n transform: (chunk) => {\n this.accumulator = this.reducer(this.accumulator, chunk);\n },\n flush: (controller) => {\n controller.enqueue(this.accumulator);\n controller.terminate();\n },\n });\n\n this.accumulator = initialValue;\n this.reducer = reducer;\n }\n}\n","import {\n type ReadableStream,\n type ReadableStreamDefaultReadResult,\n TransformStream,\n type TransformStreamDefaultController,\n} from 'web-streams-polyfill';\nimport { type AsyncRetryOptions, asyncRetry } from '../../utils';\n\nexport const retryStream = <\n Source = unknown,\n Transformed = Source,\n StreamType = Source,\n>(\n createSourceStream: () => ReadableStream<StreamType>,\n handleChunk: (\n readResult: ReadableStreamDefaultReadResult<StreamType>,\n controller: TransformStreamDefaultController<Transformed>,\n ) => Promise<void> | void,\n retryOptions: AsyncRetryOptions = { forever: true, minTimeout: 25 },\n): TransformStream<Source, Transformed> =>\n new TransformStream<Source, Transformed>({\n start(controller) {\n asyncRetry(\n () => onRestream(createSourceStream, handleChunk, controller),\n retryOptions,\n ).catch((error) => {\n controller.error(error);\n });\n },\n });\n\nconst onRestream = async <StreamType, Source, Transformed = Source>(\n createSourceStream: () => ReadableStream<StreamType>,\n handleChunk: (\n readResult: ReadableStreamDefaultReadResult<StreamType>,\n controller: TransformStreamDefaultController<Transformed>,\n ) => Promise<void> | void,\n controller: TransformStreamDefaultController<Transformed>,\n): Promise<void> => {\n const sourceStream = createSourceStream();\n const reader = sourceStream.getReader();\n\n try {\n let done: boolean;\n\n do {\n const result = await reader.read();\n done = result.done;\n\n await handleChunk(result, controller);\n\n if (done) {\n controller.terminate();\n }\n } while (!done);\n } finally {\n reader.releaseLock();\n }\n};\n","import { TransformStream } from 'web-streams-polyfill';\n\nexport const skip = <T>(limit: number) => new SkipTransformStream<T>(limit);\n\nexport class SkipTransformStream<T> extends TransformStream<T, T> {\n private count = 0;\n private skip: number;\n\n constructor(skip: number) {\n super({\n transform: (chunk, controller) => {\n this.count++;\n if (this.count > this.skip) {\n controller.enqueue(chunk);\n }\n },\n });\n\n this.skip = skip;\n }\n}\n","import { TransformStream } from 'web-streams-polyfill';\n\nexport const stopAfter = <Item>(stopCondition: (item: Item) => boolean) =>\n new TransformStream<Item, Item>({\n transform(chunk, controller) {\n controller.enqueue(chunk);\n\n if (stopCondition(chunk)) {\n controller.terminate();\n }\n },\n });\n","import { TransformStream } from 'web-streams-polyfill';\n\nexport const stopOn = <Item>(stopCondition: (item: Item) => boolean) =>\n new TransformStream<Item, Item>({\n async transform(chunk, controller) {\n if (!stopCondition(chunk)) {\n controller.enqueue(chunk);\n return;\n }\n await Promise.resolve();\n controller.terminate();\n },\n });\n","import { TransformStream } from 'web-streams-polyfill';\n\nexport const take = <T>(limit: number) => new TakeTransformStream<T>(limit);\n\nexport class TakeTransformStream<T> extends TransformStream<T, T> {\n private count = 0;\n private limit: number;\n\n constructor(limit: number) {\n super({\n transform: (chunk, controller) => {\n if (this.count < this.limit) {\n this.count++;\n controller.enqueue(chunk);\n } else {\n controller.terminate();\n }\n },\n });\n\n this.limit = limit;\n }\n}\n","import { TransformStream } from 'web-streams-polyfill';\n\nexport const waitAtMost = <Item>(waitTimeInMs: number) =>\n new TransformStream<Item, Item>({\n start(controller) {\n const timeoutId = setTimeout(() => {\n controller.terminate();\n }, waitTimeInMs);\n\n const originalTerminate = controller.terminate.bind(controller);\n\n // Clear the timeout if the stream is terminated early\n controller.terminate = () => {\n clearTimeout(timeoutId);\n originalTerminate();\n };\n },\n transform(chunk, controller) {\n controller.enqueue(chunk);\n },\n });\n","import { ConcurrencyError } from '../errors';\nimport type { BigIntStreamPosition, Flavour } from '../typing';\n\nexport type ExpectedStreamVersion<VersionType = BigIntStreamPosition> =\n | ExpectedStreamVersionWithValue<VersionType>\n | ExpectedStreamVersionGeneral;\n\nexport type ExpectedStreamVersionWithValue<VersionType = BigIntStreamPosition> =\n Flavour<VersionType, 'StreamVersion'>;\n\nexport type ExpectedStreamVersionGeneral = Flavour<\n 'STREAM_EXISTS' | 'STREAM_DOES_NOT_EXIST' | 'NO_CONCURRENCY_CHECK',\n 'StreamVersion'\n>;\n\nexport const STREAM_EXISTS = 'STREAM_EXISTS' as ExpectedStreamVersionGeneral;\nexport const STREAM_DOES_NOT_EXIST =\n 'STREAM_DOES_NOT_EXIST' as ExpectedStreamVersionGeneral;\nexport const NO_CONCURRENCY_CHECK =\n 'NO_CONCURRENCY_CHECK' as ExpectedStreamVersionGeneral;\n\nexport const matchesExpectedVersion = <StreamVersion = BigIntStreamPosition>(\n current: StreamVersion | undefined,\n expected: ExpectedStreamVersion<StreamVersion>,\n defaultVersion: StreamVersion,\n): boolean => {\n if (expected === NO_CONCURRENCY_CHECK) return true;\n\n if (expected == STREAM_DOES_NOT_EXIST) return current === defaultVersion;\n\n if (expected == STREAM_EXISTS) return current !== defaultVersion;\n\n return current === expected;\n};\n\nexport const assertExpectedVersionMatchesCurrent = <\n StreamVersion = BigIntStreamPosition,\n>(\n current: StreamVersion,\n expected: ExpectedStreamVersion<StreamVersion> | undefined,\n defaultVersion: StreamVersion,\n): void => {\n expected ??= NO_CONCURRENCY_CHECK;\n\n if (!matchesExpectedVersion(current, expected, defaultVersion))\n throw new ExpectedVersionConflictError(current, expected);\n};\n\nexport class ExpectedVersionConflictError<\n VersionType = BigIntStreamPosition,\n> extends ConcurrencyError {\n constructor(\n current: VersionType,\n expected: ExpectedStreamVersion<VersionType>,\n ) {\n super(current?.toString(), expected?.toString());\n\n // 👇️ because we are extending a built-in class\n Object.setPrototypeOf(this, ExpectedVersionConflictError.prototype);\n }\n}\n\nexport const isExpectedVersionConflictError = (\n error: unknown,\n): error is ExpectedVersionConflictError =>\n error instanceof ExpectedVersionConflictError;\n","export class ParseError extends Error {\n constructor(text: string) {\n super(`Cannot parse! ${text}`);\n }\n}\n\nexport type Mapper<From, To = From> =\n | ((value: unknown) => To)\n | ((value: Partial<From>) => To)\n | ((value: From) => To)\n | ((value: Partial<To>) => To)\n | ((value: To) => To)\n | ((value: Partial<To | From>) => To)\n | ((value: To | From) => To);\n\nexport type MapperArgs<From, To = From> = Partial<From> &\n From &\n Partial<To> &\n To;\n\nexport type ParseOptions<From, To = From> = {\n reviver?: (key: string, value: unknown) => unknown;\n map?: Mapper<From, To>;\n typeCheck?: <To>(value: unknown) => value is To;\n};\n\nexport type StringifyOptions<From, To = From> = {\n map?: Mapper<From, To>;\n};\n\nexport const JSONParser = {\n stringify: <From, To = From>(\n value: From,\n options?: StringifyOptions<From, To>,\n ) => {\n return JSON.stringify(\n options?.map ? options.map(value as MapperArgs<From, To>) : value,\n //TODO: Consider adding support to DateTime and adding specific format to mark that's a bigint\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n (_, v) => (typeof v === 'bigint' ? v.toString() : v),\n );\n },\n parse: <From, To = From>(\n text: string,\n options?: ParseOptions<From, To>,\n ): To | undefined => {\n const parsed: unknown = JSON.parse(text, options?.reviver);\n\n if (options?.typeCheck && !options?.typeCheck<To>(parsed))\n throw new ParseError(text);\n\n return options?.map\n ? options.map(parsed as MapperArgs<From, To>)\n : (parsed as To | undefined);\n },\n};\n","import { filter } from './filter';\nimport { map } from './map';\nimport {\n notifyAboutNoActiveReadersStream,\n NotifyAboutNoActiveReadersStream,\n} from './notifyAboutNoActiveReaders';\nimport { reduce, ReduceTransformStream } from './reduce';\nimport { retryStream } from './retry';\nimport { skip, SkipTransformStream } from './skip';\nimport { stopAfter } from './stopAfter';\nimport { stopOn } from './stopOn';\nimport { take, TakeTransformStream } from './take';\nimport { waitAtMost } from './waitAtMost';\n\nexport const streamTransformations = {\n filter,\n take,\n TakeTransformStream,\n skip,\n SkipTransformStream,\n map,\n notifyAboutNoActiveReadersStream,\n NotifyAboutNoActiveReadersStream,\n reduce,\n ReduceTransformStream,\n retry: retryStream,\n stopAfter,\n stopOn,\n waitAtMost,\n};\n","import {\n EmmettError,\n type AsyncRetryOptions,\n type Event,\n} from '@event-driven-io/emmett';\nimport {\n EventStoreDBClient,\n type SubscribeToAllOptions,\n type SubscribeToStreamOptions,\n} from '@eventstore/db-client';\nimport {\n eventStoreDBEventStoreProcessor,\n type EventStoreDBEventStoreProcessor,\n type EventStoreDBEventStoreProcessorOptions,\n} from './eventStoreDBEventStoreProcessor';\nimport {\n DefaultEventStoreDBEventStoreProcessorBatchSize,\n eventStoreDBSubscription,\n zipEventStoreDBEventStoreMessageBatchPullerStartFrom,\n type EventStoreDBEventStoreMessagesBatchHandler,\n type EventStoreDBSubscription,\n} from './subscriptions';\n\nexport type EventStoreDBEventStoreConsumerConfig<\n ConsumerEventType extends Event = Event,\n> = {\n from?: EventStoreDBEventStoreConsumerType;\n processors?: EventStoreDBEventStoreProcessor<ConsumerEventType>[];\n pulling?: {\n batchSize?: number;\n };\n resilience?: {\n resubscribeOptions?: AsyncRetryOptions;\n };\n};\n\nexport type EventStoreDBEventStoreConsumerOptions<\n ConsumerEventType extends Event = Event,\n> = EventStoreDBEventStoreConsumerConfig<ConsumerEventType> &\n (\n | {\n connectionString: string;\n }\n | { client: EventStoreDBClient }\n );\n\nexport type $all = '$all';\nexport const $all = '$all';\n\nexport type EventStoreDBEventStoreConsumerType =\n | {\n stream: $all;\n options?: Exclude<SubscribeToAllOptions, 'fromPosition'>;\n }\n | {\n stream: string;\n options?: Exclude<SubscribeToStreamOptions, 'fromRevision'>;\n };\n\nexport type EventStoreDBEventStoreConsumer<\n ConsumerEventType extends Event = Event,\n> = Readonly<{\n isRunning: boolean;\n processors: EventStoreDBEventStoreProcessor<ConsumerEventType>[];\n processor: <EventType extends ConsumerEventType = ConsumerEventType>(\n options: EventStoreDBEventStoreProcessorOptions<EventType>,\n ) => EventStoreDBEventStoreProcessor<EventType>;\n start: () => Promise<void>;\n stop: () => Promise<void>;\n close: () => Promise<void>;\n}>;\n\nexport const eventStoreDBEventStoreConsumer = <\n ConsumerEventType extends Event = Event,\n>(\n options: EventStoreDBEventStoreConsumerOptions<ConsumerEventType>,\n): EventStoreDBEventStoreConsumer<ConsumerEventType> => {\n let isRunning = false;\n const { pulling } = options;\n const processors = options.processors ?? [];\n\n let start: Promise<void>;\n\n let currentSubscription: EventStoreDBSubscription | undefined;\n\n const client =\n 'client' in options\n ? options.client\n : EventStoreDBClient.connectionString(options.connectionString);\n\n const eachBatch: EventStoreDBEventStoreMessagesBatchHandler<\n ConsumerEventType\n > = async (messagesBatch) => {\n const activeProcessors = processors.filter((s) => s.isActive);\n\n if (activeProcessors.length === 0)\n return {\n type: 'STOP',\n reason: 'No active processors',\n };\n\n const result = await Promise.allSettled(\n activeProcessors.map((s) => {\n // TODO: Add here filtering to only pass messages that can be handled by processor\n return s.handle(messagesBatch, { client });\n }),\n );\n\n return result.some(\n (r) => r.status === 'fulfilled' && r.value?.type !== 'STOP',\n )\n ? undefined\n : {\n type: 'STOP',\n };\n };\n\n const subscription = (currentSubscription = eventStoreDBSubscription({\n client,\n from: options.from,\n eachBatch,\n batchSize:\n pulling?.batchSize ?? DefaultEventStoreDBEventStoreProcessorBatchSize,\n resilience: options.resilience,\n }));\n\n const stop = async () => {\n if (!isRunning) return;\n isRunning = false;\n if (currentSubscription) {\n await currentSubscription.stop();\n currentSubscription = undefined;\n }\n await start;\n };\n\n return {\n processors,\n get isRunning() {\n return isRunning;\n },\n processor: <EventType extends ConsumerEventType = ConsumerEventType>(\n options: EventStoreDBEventStoreProcessorOptions<EventType>,\n ): EventStoreDBEventStoreProcessor<EventType> => {\n const processor = eventStoreDBEventStoreProcessor<EventType>(options);\n\n processors.push(processor);\n\n return processor;\n },\n start: () => {\n if (isRunning) return start;\n\n start = (async () => {\n if (processors.length === 0)\n return Promise.reject(\n new EmmettError(\n 'Cannot start consumer without at least a single processor',\n ),\n );\n\n isRunning = true;\n\n const startFrom = zipEventStoreDBEventStoreMessageBatchPullerStartFrom(\n await Promise.all(processors.map((o) => o.start(client))),\n );\n\n return subscription.start({ startFrom });\n })();\n\n return start;\n },\n stop,\n close: async () => {\n await stop();\n },\n };\n};\n","import {\n EmmettError,\n type Event,\n type ReadEvent,\n type ReadEventMetadataWithGlobalPosition,\n} from '@event-driven-io/emmett';\nimport type { EventStoreDBClient } from '@eventstore/db-client';\nimport { v7 as uuid } from 'uuid';\nimport type { EventStoreDBSubscriptionStartFrom } from './subscriptions';\n\nexport type EventStoreDBEventStoreProcessorEventsBatch<\n EventType extends Event = Event,\n> = {\n messages: ReadEvent<EventType, ReadEventMetadataWithGlobalPosition>[];\n};\n\nexport type EventStoreDBEventStoreProcessor<EventType extends Event = Event> = {\n id: string;\n start: (\n client: EventStoreDBClient,\n ) => Promise<EventStoreDBSubscriptionStartFrom | undefined>;\n isActive: boolean;\n handle: (\n messagesBatch: EventStoreDBEventStoreProcessorEventsBatch<EventType>,\n context: { client: EventStoreDBClient },\n ) => Promise<EventStoreDBEventStoreProcessorMessageHandlerResult>;\n};\n\nexport const EventStoreDBEventStoreProcessor = {\n result: {\n skip: (options?: {\n reason?: string;\n }): EventStoreDBEventStoreProcessorMessageHandlerResult => ({\n type: 'SKIP',\n ...(options ?? {}),\n }),\n stop: (options?: {\n reason?: string;\n error?: EmmettError;\n }): EventStoreDBEventStoreProcessorMessageHandlerResult => ({\n type: 'STOP',\n ...(options ?? {}),\n }),\n },\n};\n\nexport type EventStoreDBEventStoreProcessorMessageHandlerResult =\n | void\n | { type: 'SKIP'; reason?: string }\n | { type: 'STOP'; reason?: string; error?: EmmettError };\n\nexport type EventStoreDBEventStoreProcessorEachMessageHandler<\n EventType extends Event = Event,\n> = (\n event: ReadEvent<EventType, ReadEventMetadataWithGlobalPosition>,\n) =>\n | Promise<EventStoreDBEventStoreProcessorMessageHandlerResult>\n | EventStoreDBEventStoreProcessorMessageHandlerResult;\n\nexport type EventStoreDBEventStoreProcessorStartFrom =\n | EventStoreDBSubscriptionStartFrom\n | 'CURRENT';\n\nexport type EventStoreDBEventStoreProcessorOptions<\n EventType extends Event = Event,\n> = {\n processorId?: string;\n version?: number;\n partition?: string;\n startFrom?: EventStoreDBEventStoreProcessorStartFrom;\n stopAfter?: (\n message: ReadEvent<EventType, ReadEventMetadataWithGlobalPosition>,\n ) => boolean;\n eachMessage: EventStoreDBEventStoreProcessorEachMessageHandler<EventType>;\n};\n\nexport const eventStoreDBEventStoreProcessor = <\n EventType extends Event = Event,\n>(\n options: EventStoreDBEventStoreProcessorOptions<EventType>,\n): EventStoreDBEventStoreProcessor => {\n const { eachMessage } = options;\n let isActive = true;\n //let lastProcessedPosition: bigint | null = null;\n\n options.processorId = options.processorId ?? uuid();\n\n return {\n id: options.processorId,\n start: (\n _client: EventStoreDBClient,\n ): Promise<EventStoreDBSubscriptionStartFrom | undefined> => {\n isActive = true;\n if (options.startFrom !== 'CURRENT')\n return Promise.resolve(options.startFrom);\n\n // const { lastProcessedPosition } = await readProcessorCheckpoint(\n // execute,\n // {\n // processorId: options.processorId,\n // partition: options.partition,\n // },\n // );\n\n // if (lastProcessedPosition === null) return 'BEGINNING';\n\n // return { globalPosition: lastProcessedPosition };\n return Promise.resolve('BEGINNING');\n },\n get isActive() {\n return isActive;\n },\n handle: async ({\n messages,\n }): Promise<EventStoreDBEventStoreProcessorMessageHandlerResult> => {\n if (!isActive) return;\n\n let result:\n | EventStoreDBEventStoreProcessorMessageHandlerResult\n | undefined = undefined;\n\n //let lastProcessedPosition: bigint | null = null;\n\n for (const message of messages) {\n const typedMessage = message as ReadEvent<\n EventType,\n ReadEventMetadataWithGlobalPosition\n >;\n\n const messageProcessingResult = await eachMessage(typedMessage);\n\n // TODO: Add correct handling of the storing checkpoint\n // await storeProcessorCheckpoint(tx.execute, {\n // processorId: options.processorId,\n // version: options.version,\n // lastProcessedPosition,\n // newPosition: typedMessage.metadata.globalPosition,\n // partition: options.partition,\n // });\n\n //lastProcessedPosition = typedMessage.metadata.globalPosition;\n\n if (\n messageProcessingResult &&\n messageProcessingResult.type === 'STOP'\n ) {\n isActive = false;\n result = messageProcessingResult;\n break;\n }\n\n if (options.stopAfter && options.stopAfter(typedMessage)) {\n isActive = false;\n result = { type: 'STOP', reason: 'Stop condition reached' };\n break;\n }\n\n if (messageProcessingResult && messageProcessingResult.type === 'SKIP')\n continue;\n }\n\n return result;\n },\n };\n};\n","import {\n asyncRetry,\n type AsyncRetryOptions,\n type EmmettError,\n type Event,\n type ReadEvent,\n type ReadEventMetadataWithGlobalPosition,\n} from '@event-driven-io/emmett';\nimport {\n END,\n EventStoreDBClient,\n excludeSystemEvents,\n START,\n type JSONRecordedEvent,\n type StreamSubscription,\n} from '@eventstore/db-client';\nimport { finished, Readable } from 'stream';\nimport { mapFromESDBEvent } from '../../eventstoreDBEventStore';\nimport {\n $all,\n type EventStoreDBEventStoreConsumerType,\n} from '../eventStoreDBEventStoreConsumer';\n\nexport const DefaultEventStoreDBEventStoreProcessorBatchSize = 100;\nexport const DefaultEventStoreDBEventStoreProcessorPullingFrequencyInMs = 50;\n\nexport type EventStoreDBEventStoreMessagesBatch<\n EventType extends Event = Event,\n> = {\n messages: ReadEvent<EventType, ReadEventMetadataWithGlobalPosition>[];\n};\n\nexport type EventStoreDBEventStoreMessagesBatchHandlerResult = void | {\n type: 'STOP';\n reason?: string;\n error?: EmmettError;\n};\n\nexport type EventStoreDBEventStoreMessagesBatchHandler<\n EventType extends Event = Event,\n> = (\n messagesBatch: EventStoreDBEventStoreMessagesBatch<EventType>,\n) =>\n | Promise<EventStoreDBEventStoreMessagesBatchHandlerResult>\n | EventStoreDBEventStoreMessagesBatchHandlerResult;\n\nexport type EventStoreDBSubscriptionOptions<EventType extends Event = Event> = {\n from?: EventStoreDBEventStoreConsumerType;\n client: EventStoreDBClient;\n batchSize: number;\n eachBatch: EventStoreDBEventStoreMessagesBatchHandler<EventType>;\n resilience?: {\n resubscribeOptions?: AsyncRetryOptions;\n };\n};\n\nexport type EventStoreDBSubscriptionStartFrom =\n | { position: bigint }\n | 'BEGINNING'\n | 'END';\n\nexport type EventStoreDBSubscriptionStartOptions = {\n startFrom: EventStoreDBSubscriptionStartFrom;\n};\n\nexport type EventStoreDBSubscription = {\n isRunning: boolean;\n start(options: EventStoreDBSubscriptionStartOptions): Promise<void>;\n stop(): Promise<void>;\n};\n\nconst toGlobalPosition = (startFrom: EventStoreDBSubscriptionStartFrom) =>\n startFrom === 'BEGINNING'\n ? START\n : startFrom === 'END'\n ? END\n : {\n prepare: startFrom.position,\n commit: startFrom.position,\n };\n\nconst toStreamPosition = (startFrom: EventStoreDBSubscriptionStartFrom) =>\n startFrom === 'BEGINNING'\n ? START\n : startFrom === 'END'\n ? END\n : startFrom.position;\n\nconst subscribe = (\n client: EventStoreDBClient,\n from: EventStoreDBEventStoreConsumerType | undefined,\n options: EventStoreDBSubscriptionStartOptions,\n) =>\n from == undefined || from.stream == $all\n ? client.subscribeToAll({\n fromPosition: toGlobalPosition(options.startFrom),\n filter: excludeSystemEvents(),\n ...(from?.options ?? {}),\n })\n : client.subscribeToStream(from.stream, {\n fromRevision: toStreamPosition(options.startFrom),\n ...(from.options ?? {}),\n });\n\nexport const isDatabaseUnavailableError = (error: unknown) =>\n error instanceof Error &&\n 'type' in error &&\n error.type === 'unavailable' &&\n 'code' in error &&\n error.code === 14;\n\nexport const EventStoreDBResubscribeDefaultOptions: AsyncRetryOptions = {\n forever: true,\n minTimeout: 100,\n factor: 1.5,\n shouldRetryError: (error) => !isDatabaseUnavailableError(error),\n};\n\nexport const eventStoreDBSubscription = <EventType extends Event = Event>({\n client,\n from,\n //batchSize,\n eachBatch,\n resilience,\n}: EventStoreDBSubscriptionOptions<EventType>): EventStoreDBSubscription => {\n let isRunning = false;\n\n let start: Promise<void>;\n\n let subscription: StreamSubscription<EventType>;\n\n const resubscribeOptions: AsyncRetryOptions =\n resilience?.resubscribeOptions ?? {\n ...EventStoreDBResubscribeDefaultOptions,\n shouldRetryResult: () => isRunning,\n shouldRetryError: (error) =>\n isRunning &&\n EventStoreDBResubscribeDefaultOptions.shouldRetryError!(error),\n };\n\n const pipeMessages = (options: EventStoreDBSubscriptionStartOptions) => {\n subscription = subscribe(client, from, options);\n\n return asyncRetry(\n () =>\n new Promise<void>((resolve, reject) => {\n finished(\n subscription.on('data', async (resolvedEvent) => {\n if (!resolvedEvent.event) return;\n\n const event = mapFromESDBEvent(\n resolvedEvent.event as JSONRecordedEvent<EventType>,\n );\n\n const result = await eachBatch({ messages: [event] });\n\n if (result && result.type === 'STOP') {\n isRunning = false;\n await subscription.unsubscribe();\n }\n\n from = {\n stream: from?.stream ?? $all,\n options: {\n ...(from?.options ?? {}),\n ...(!from || from?.stream === $all\n ? {\n fromPosition: resolvedEvent.event.position,\n }\n : { fromRevision: resolvedEvent.event.revision }),\n },\n };\n }) as unknown as Readable,\n (error) => {\n if (error) {\n console.error(`Received error: ${JSON.stringify(error)}.`);\n reject(error);\n return;\n }\n console.info(`Stopping subscription.`);\n resolve();\n },\n );\n }),\n resubscribeOptions,\n );\n };\n\n return {\n get isRunning() {\n return isRunning;\n },\n start: (options) => {\n if (isRunning) return start;\n\n start = (async () => {\n isRunning = true;\n\n return pipeMessages(options);\n })();\n\n return start;\n },\n stop: async () => {\n if (!isRunning) return;\n isRunning = false;\n await subscription?.unsubscribe();\n await start;\n },\n };\n};\n\nexport const zipEventStoreDBEventStoreMessageBatchPullerStartFrom = (\n options: (EventStoreDBSubscriptionStartFrom | undefined)[],\n): EventStoreDBSubscriptionStartFrom => {\n if (\n options.length === 0 ||\n options.some((o) => o === undefined || o === 'BEGINNING')\n )\n return 'BEGINNING';\n\n if (options.every((o) => o === 'END')) return 'END';\n\n return options\n .filter((o) => o !== undefined && o !== 'BEGINNING' && o !== 'END')\n .sort((a, b) => (a > b ? 1 : -1))[0]!;\n};\n","import {\n ExpectedVersionConflictError,\n NO_CONCURRENCY_CHECK,\n STREAM_DOES_NOT_EXIST,\n STREAM_EXISTS,\n assertExpectedVersionMatchesCurrent,\n type AggregateStreamOptions,\n type AggregateStreamResultWithGlobalPosition,\n type AppendToStreamOptions,\n type AppendToStreamResultWithGlobalPosition,\n type Event,\n type EventStore,\n type ExpectedStreamVersion,\n type ReadEvent,\n type ReadEventMetadataWithGlobalPosition,\n type ReadStreamOptions,\n type ReadStreamResult,\n} from '@event-driven-io/emmett';\nimport {\n ANY,\n STREAM_EXISTS as ESDB_STREAM_EXISTS,\n EventStoreDBClient,\n NO_STREAM,\n StreamNotFoundError,\n WrongExpectedVersionError,\n jsonEvent,\n type AppendExpectedRevision,\n type ReadStreamOptions as ESDBReadStreamOptions,\n type JSONRecordedEvent,\n} from '@eventstore/db-client';\nimport {\n eventStoreDBEventStoreConsumer,\n type EventStoreDBEventStoreConsumer,\n type EventStoreDBEventStoreConsumerConfig,\n} from './consumers';\n\nconst toEventStoreDBReadOptions = (\n options: ReadStreamOptions | undefined,\n): ESDBReadStreamOptions | undefined => {\n return options\n ? {\n fromRevision: 'from' in options ? options.from : undefined,\n maxCount:\n 'maxCount' in options\n ? options.maxCount\n : 'to' in options\n ? options.to\n : undefined,\n }\n : undefined;\n};\n\nexport const EventStoreDBEventStoreDefaultStreamVersion = -1n;\n\nexport type EventStoreDBReadEventMetadata = ReadEventMetadataWithGlobalPosition;\n\nexport type EventStoreDBReadEvent<EventType extends Event = Event> = ReadEvent<\n EventType,\n EventStoreDBReadEventMetadata\n>;\n\nexport interface EventStoreDBEventStore\n extends EventStore<EventStoreDBReadEventMetadata> {\n appendToStream<EventType extends Event>(\n streamName: string,\n events: EventType[],\n options?: AppendToStreamOptions,\n ): Promise<AppendToStreamResultWithGlobalPosition>;\n consumer<ConsumerEventType extends Event = Event>(\n options?: EventStoreDBEventStoreConsumerConfig<ConsumerEventType>,\n ): EventStoreDBEventStoreConsumer<ConsumerEventType>;\n}\n\nexport const getEventStoreDBEventStore = (\n eventStore: EventStoreDBClient,\n): EventStoreDBEventStore => {\n return {\n async aggregateStream<State, EventType extends Event>(\n streamName: string,\n options: AggregateStreamOptions<\n State,\n EventType,\n EventStoreDBReadEventMetadata\n >,\n ): Promise<AggregateStreamResultWithGlobalPosition<State>> {\n const { evolve, initialState, read } = options;\n\n const expectedStreamVersion = read?.expectedStreamVersion;\n\n let state = initialState();\n let currentStreamVersion: bigint =\n EventStoreDBEventStoreDefaultStreamVersion;\n let lastEventGlobalPosition: bigint | undefined = undefined;\n\n try {\n for await (const { event } of eventStore.readStream<EventType>(\n streamName,\n toEventStoreDBReadOptions(options.read),\n )) {\n if (!event) continue;\n\n state = evolve(state, mapFromESDBEvent<EventType>(event));\n currentStreamVersion = event.revision;\n lastEventGlobalPosition = event.position?.commit;\n }\n\n assertExpectedVersionMatchesCurrent(\n currentStreamVersion,\n expectedStreamVersion,\n EventStoreDBEventStoreDefaultStreamVersion,\n );\n\n return lastEventGlobalPosition\n ? {\n currentStreamVersion,\n lastEventGlobalPosition,\n state,\n streamExists: true,\n }\n : {\n currentStreamVersion,\n state,\n streamExists: false,\n };\n } catch (error) {\n if (error instanceof StreamNotFoundError) {\n return {\n currentStreamVersion,\n state,\n streamExists: false,\n };\n }\n\n throw error;\n }\n },\n\n readStream: async <EventType extends Event>(\n streamName: string,\n options?: ReadStreamOptions,\n ): Promise<ReadStreamResult<EventType, EventStoreDBReadEventMetadata>> => {\n const events: ReadEvent<EventType, EventStoreDBReadEventMetadata>[] = [];\n\n let currentStreamVersion: bigint =\n EventStoreDBEventStoreDefaultStreamVersion;\n\n try {\n for await (const { event } of eventStore.readStream<EventType>(\n streamName,\n toEventStoreDBReadOptions(options),\n )) {\n if (!event) continue;\n events.push(mapFromESDBEvent(event));\n currentStreamVersion = event.revision;\n }\n return {\n currentStreamVersion,\n events,\n streamExists: true,\n };\n } catch (error) {\n if (error instanceof StreamNotFoundError) {\n return {\n currentStreamVersion,\n events: [],\n streamExists: false,\n };\n }\n\n throw error;\n }\n },\n\n appendToStream: async <EventType extends Event>(\n streamName: string,\n events: EventType[],\n options?: AppendToStreamOptions,\n ): Promise<AppendToStreamResultWithGlobalPosition> => {\n try {\n const serializedEvents = events.map(jsonEvent);\n\n const expectedRevision = toExpectedRevision(\n options?.expectedStreamVersion,\n );\n\n const appendResult = await eventStore.appendToStream(\n streamName,\n serializedEvents,\n {\n expectedRevision,\n },\n );\n\n return {\n nextExpectedStreamVersion: appendResult.nextExpectedRevision,\n lastEventGlobalPosition: appendResult.position!.commit,\n createdNewStream:\n appendResult.nextExpectedRevision >=\n BigInt(serializedEvents.length),\n };\n } catch (error) {\n if (error instanceof WrongExpectedVersionError) {\n throw new ExpectedVersionConflictError(\n error.actualVersion,\n toExpectedVersion(error.expectedVersion),\n );\n }\n\n throw error;\n }\n },\n\n consumer: <ConsumerEventType extends Event = Event>(\n options?: EventStoreDBEventStoreConsumerConfig<ConsumerEventType>,\n ): EventStoreDBEventStoreConsumer<ConsumerEventType> =>\n eventStoreDBEventStoreConsumer<ConsumerEventType>({\n ...(options ?? {}),\n client: eventStore,\n }),\n\n //streamEvents: streamEvents(eventStore),\n };\n};\n\nexport const mapFromESDBEvent = <EventType extends Event = Event>(\n event: JSONRecordedEvent<EventType>,\n): ReadEvent<EventType, EventStoreDBReadEventMetadata> => {\n return <ReadEvent<EventType, EventStoreDBReadEventMetadata>>{\n type: event.type,\n data: event.data,\n metadata: {\n ...((event.metadata as EventStoreDBReadEventMetadata) ??\n ({} as EventStoreDBReadEventMetadata)),\n eventId: event.id,\n streamName: event.streamId,\n streamPosition: event.revision,\n globalPosition: event.position!.commit,\n },\n };\n};\n\nconst toExpectedRevision = (\n expected: ExpectedStreamVersion | undefined,\n): AppendExpectedRevision => {\n if (expected === undefined) return ANY;\n\n if (expected === NO_CONCURRENCY_CHECK) return ANY;\n\n if (expected == STREAM_DOES_NOT_EXIST) return NO_STREAM;\n\n if (expected == STREAM_EXISTS) return ESDB_STREAM_EXISTS;\n\n return expected as bigint;\n};\n\nconst toExpectedVersion = (\n expected: AppendExpectedRevision | undefined,\n): ExpectedStreamVersion => {\n if (expected === undefined) return NO_CONCURRENCY_CHECK;\n\n if (expected === ANY) return NO_CONCURRENCY_CHECK;\n\n if (expected == NO_STREAM) return STREAM_DOES_NOT_EXIST;\n\n if (expected == ESDB_STREAM_EXISTS) return STREAM_EXISTS;\n\n return expected;\n};\n\n// const { map } = streamTransformations;\n//\n// // eslint-disable-next-line @typescript-eslint/no-unused-vars\n// const convertToWebReadableStream = (\n// allStreamSubscription: AllStreamSubscription,\n// ): ReadableStream<AllStreamResolvedEvent | GlobalStreamCaughtUp> => {\n// // Validate the input type\n// if (!(allStreamSubscription instanceof Readable)) {\n// throw new Error('Provided stream is not a Node.js Readable stream.');\n// }\n\n// let globalPosition = 0n;\n\n// const stream = Readable.toWeb(\n// allStreamSubscription,\n// ) as ReadableStream<AllStreamResolvedEvent>;\n\n// const writable = new WritableStream<\n// AllStreamResolvedEvent | GlobalStreamCaughtUp\n// >();\n\n// allStreamSubscription.on('caughtUp', async () => {\n// console.log(globalPosition);\n// await writable.getWriter().write(globalStreamCaughtUp({ globalPosition }));\n// });\n\n// const transform = map<\n// AllStreamResolvedEvent,\n// AllStreamResolvedEvent | GlobalStreamCaughtUp\n// >((event) => {\n// if (event?.event?.position.commit)\n// globalPosition = event.event?.position.commit;\n\n// return event;\n// });\n\n// return stream.pipeThrough<AllStreamResolvedEvent | GlobalStreamCaughtUp>(\n// transform,\n// );\n// };\n\n// const streamEvents = (eventStore: EventStoreDBClient) => () => {\n// return restream<\n// AllStreamResolvedEvent | GlobalSubscriptionEvent,\n// | ReadEvent<Event, EventStoreDBReadEventMetadata>\n// | GlobalSubscriptionEvent\n// >(\n// (): ReadableStream<AllStreamResolvedEvent | GlobalSubscriptionEvent> =>\n// convertToWebReadableStream(\n// eventStore.subscribeToAll({\n// fromPosition: START,\n// filter: excludeSystemEvents(),\n// }),\n// ),\n// (\n// resolvedEvent: AllStreamResolvedEvent | GlobalSubscriptionEvent,\n// ): ReadEvent<Event, EventStoreDBReadEventMetadata> =>\n// mapFromESDBEvent(resolvedEvent.event as JSONRecordedEvent<Event>),\n// );\n// };\n"]}
|