@event-driven-io/emmett-mongodb 0.22.1 → 0.23.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +73 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +28 -19
- package/dist/index.d.ts +28 -19
- package/dist/index.js +64 -17
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3;// ../emmett/dist/chunk-AEEEXE2R.js
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3; var _class4;// ../emmett/dist/chunk-AEEEXE2R.js
|
|
2
2
|
var isNumber = (val) => typeof val === "number" && val === val;
|
|
3
3
|
var isString = (val) => typeof val === "string";
|
|
4
4
|
var EmmettError = class _EmmettError extends Error {
|
|
@@ -260,7 +260,9 @@ var streamTransformations = {
|
|
|
260
260
|
var { retry: retry2 } = streamTransformations;
|
|
261
261
|
|
|
262
262
|
// src/eventStore/mongoDBEventStore.ts
|
|
263
|
-
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
var _mongodb = require('mongodb');
|
|
264
266
|
|
|
265
267
|
|
|
266
268
|
// src/eventStore/projections/index.ts
|
|
@@ -314,17 +316,29 @@ var mongoDBInlineProjection = (options) => {
|
|
|
314
316
|
|
|
315
317
|
// src/eventStore/mongoDBEventStore.ts
|
|
316
318
|
var MongoDBEventStoreDefaultStreamVersion = 0n;
|
|
317
|
-
var
|
|
319
|
+
var MongoDBEventStoreImplementation = (_class4 = class {
|
|
318
320
|
|
|
319
321
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
__init5() {this.streamCollections = /* @__PURE__ */ new Map()}
|
|
325
|
+
|
|
326
|
+
__init6() {this.isClosed = false}
|
|
327
|
+
constructor(options) {;_class4.prototype.__init5.call(this);_class4.prototype.__init6.call(this);_class4.prototype.__init7.call(this);_class4.prototype.__init8.call(this);
|
|
328
|
+
this.client = "client" in options ? options.client : new (0, _mongodb.MongoClient)(options.connectionString, options.clientOptions);
|
|
329
|
+
this.shouldManageClientLifetime = !("client" in options);
|
|
330
|
+
this.defaultOptions = {
|
|
331
|
+
database: options.database,
|
|
332
|
+
collection: options.collection
|
|
333
|
+
};
|
|
322
334
|
this.inlineProjections = (_nullishCoalesce(options.projections, () => ( []))).filter(({ type }) => type === "inline").map(
|
|
323
335
|
({ projection }) => projection
|
|
324
336
|
);
|
|
325
337
|
}
|
|
326
338
|
async readStream(streamName, options) {
|
|
327
|
-
const
|
|
339
|
+
const { streamType } = fromStreamName(streamName);
|
|
340
|
+
const expectedStreamVersion = _optionalChain([options, 'optionalAccess', _12 => _12.expectedStreamVersion]);
|
|
341
|
+
const collection = await this.collectionFor(streamType);
|
|
328
342
|
const filter2 = {
|
|
329
343
|
streamName: { $eq: streamName }
|
|
330
344
|
};
|
|
@@ -338,11 +352,11 @@ var MongoDBEventStore = class {
|
|
|
338
352
|
eventsSliceArr.push(Number(options.to));
|
|
339
353
|
}
|
|
340
354
|
const eventsSlice = eventsSliceArr.length > 1 ? { $slice: eventsSliceArr } : 1;
|
|
341
|
-
const stream = await
|
|
355
|
+
const stream = await collection.findOne(filter2, {
|
|
342
356
|
useBigInt64: true,
|
|
343
357
|
projection: {
|
|
344
358
|
metadata: 1,
|
|
345
|
-
|
|
359
|
+
messages: eventsSlice
|
|
346
360
|
}
|
|
347
361
|
});
|
|
348
362
|
if (!stream) {
|
|
@@ -358,13 +372,13 @@ var MongoDBEventStore = class {
|
|
|
358
372
|
MongoDBEventStoreDefaultStreamVersion
|
|
359
373
|
);
|
|
360
374
|
return {
|
|
361
|
-
events: stream.
|
|
375
|
+
events: stream.messages,
|
|
362
376
|
currentStreamVersion: stream.metadata.streamPosition,
|
|
363
377
|
streamExists: true
|
|
364
378
|
};
|
|
365
379
|
}
|
|
366
380
|
async aggregateStream(streamName, options) {
|
|
367
|
-
const stream = await this.readStream(streamName, _optionalChain([options, 'optionalAccess',
|
|
381
|
+
const stream = await this.readStream(streamName, _optionalChain([options, 'optionalAccess', _13 => _13.read]));
|
|
368
382
|
const state = stream.events.reduce(options.evolve, options.initialState());
|
|
369
383
|
return {
|
|
370
384
|
state,
|
|
@@ -373,8 +387,10 @@ var MongoDBEventStore = class {
|
|
|
373
387
|
};
|
|
374
388
|
}
|
|
375
389
|
async appendToStream(streamName, events, options) {
|
|
376
|
-
const
|
|
377
|
-
const
|
|
390
|
+
const { streamId, streamType } = fromStreamName(streamName);
|
|
391
|
+
const expectedStreamVersion = _optionalChain([options, 'optionalAccess', _14 => _14.expectedStreamVersion]);
|
|
392
|
+
const collection = await this.collectionFor(streamType);
|
|
393
|
+
const stream = await collection.findOne(
|
|
378
394
|
{ streamName: { $eq: streamName } },
|
|
379
395
|
{
|
|
380
396
|
useBigInt64: true,
|
|
@@ -384,7 +400,7 @@ var MongoDBEventStore = class {
|
|
|
384
400
|
}
|
|
385
401
|
}
|
|
386
402
|
);
|
|
387
|
-
const currentStreamVersion = _nullishCoalesce(_optionalChain([stream, 'optionalAccess',
|
|
403
|
+
const currentStreamVersion = _nullishCoalesce(_optionalChain([stream, 'optionalAccess', _15 => _15.metadata, 'access', _16 => _16.streamPosition]), () => ( MongoDBEventStoreDefaultStreamVersion));
|
|
388
404
|
assertExpectedVersionMatchesCurrent(
|
|
389
405
|
currentStreamVersion,
|
|
390
406
|
expectedStreamVersion,
|
|
@@ -406,10 +422,9 @@ var MongoDBEventStore = class {
|
|
|
406
422
|
}
|
|
407
423
|
};
|
|
408
424
|
});
|
|
409
|
-
const { streamId, streamType } = fromStreamName(streamName);
|
|
410
425
|
const now = /* @__PURE__ */ new Date();
|
|
411
426
|
const updates = {
|
|
412
|
-
$push: {
|
|
427
|
+
$push: { messages: { $each: eventsToAppend } },
|
|
413
428
|
$set: { "metadata.updatedAt": now },
|
|
414
429
|
$inc: { "metadata.streamPosition": BigInt(events.length) },
|
|
415
430
|
$setOnInsert: {
|
|
@@ -420,19 +435,19 @@ var MongoDBEventStore = class {
|
|
|
420
435
|
};
|
|
421
436
|
if (this.inlineProjections) {
|
|
422
437
|
await handleInlineProjections({
|
|
423
|
-
readModels: _nullishCoalesce(_optionalChain([stream, 'optionalAccess',
|
|
438
|
+
readModels: _nullishCoalesce(_optionalChain([stream, 'optionalAccess', _17 => _17.projections]), () => ( {})),
|
|
424
439
|
events: eventsToAppend,
|
|
425
440
|
projections: this.inlineProjections,
|
|
426
|
-
collection
|
|
441
|
+
collection,
|
|
427
442
|
updates,
|
|
428
443
|
client: {}
|
|
429
444
|
});
|
|
430
445
|
}
|
|
431
|
-
const updatedStream = await
|
|
446
|
+
const updatedStream = await collection.updateOne(
|
|
432
447
|
{
|
|
433
448
|
streamName: { $eq: streamName },
|
|
434
449
|
"metadata.streamPosition": toExpectedVersion(
|
|
435
|
-
_optionalChain([options, 'optionalAccess',
|
|
450
|
+
_optionalChain([options, 'optionalAccess', _18 => _18.expectedStreamVersion])
|
|
436
451
|
)
|
|
437
452
|
},
|
|
438
453
|
updates,
|
|
@@ -441,7 +456,7 @@ var MongoDBEventStore = class {
|
|
|
441
456
|
if (!updatedStream) {
|
|
442
457
|
throw new ExpectedVersionConflictError(
|
|
443
458
|
currentStreamVersion,
|
|
444
|
-
_nullishCoalesce(_optionalChain([options, 'optionalAccess',
|
|
459
|
+
_nullishCoalesce(_optionalChain([options, 'optionalAccess', _19 => _19.expectedStreamVersion]), () => ( 0n))
|
|
445
460
|
);
|
|
446
461
|
}
|
|
447
462
|
return {
|
|
@@ -449,11 +464,33 @@ var MongoDBEventStore = class {
|
|
|
449
464
|
createdNewStream: currentStreamVersion === MongoDBEventStoreDefaultStreamVersion
|
|
450
465
|
};
|
|
451
466
|
}
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
467
|
+
close() {
|
|
468
|
+
if (this.isClosed) return Promise.resolve();
|
|
469
|
+
this.isClosed = true;
|
|
470
|
+
if (!this.shouldManageClientLifetime) return Promise.resolve();
|
|
471
|
+
return this.client.close();
|
|
472
|
+
}
|
|
473
|
+
__init7() {this.getDB = async () => {
|
|
474
|
+
if (!this.db) {
|
|
475
|
+
if (!this.isClosed) await this.client.connect();
|
|
476
|
+
this.db = this.client.db(this.defaultOptions.database);
|
|
477
|
+
}
|
|
478
|
+
return this.db;
|
|
479
|
+
}}
|
|
480
|
+
__init8() {this.collectionFor = async (streamType) => {
|
|
481
|
+
const collectionName = _nullishCoalesce(_optionalChain([this, 'access', _20 => _20.defaultOptions, 'optionalAccess', _21 => _21.collection]), () => ( toStreamCollectionName(streamType)));
|
|
482
|
+
let collection = this.streamCollections.get(collectionName);
|
|
483
|
+
if (collection) return collection;
|
|
484
|
+
const db = await this.getDB();
|
|
485
|
+
collection = db.collection(collectionName);
|
|
486
|
+
this.streamCollections.set(
|
|
487
|
+
collectionName,
|
|
488
|
+
collection
|
|
489
|
+
);
|
|
490
|
+
return collection;
|
|
491
|
+
}}
|
|
492
|
+
}, _class4);
|
|
493
|
+
var getMongoDBEventStore = (options) => new MongoDBEventStoreImplementation(options);
|
|
457
494
|
function toExpectedVersion(expectedStreamVersion) {
|
|
458
495
|
if (!expectedStreamVersion) return void 0;
|
|
459
496
|
if (typeof expectedStreamVersion === "string") {
|
|
@@ -476,6 +513,16 @@ function fromStreamName(streamName) {
|
|
|
476
513
|
streamId: parts[1]
|
|
477
514
|
};
|
|
478
515
|
}
|
|
516
|
+
function toStreamCollectionName(streamType) {
|
|
517
|
+
return `emt:${streamType}`;
|
|
518
|
+
}
|
|
519
|
+
function fromStreamCollectionName(streamCollectionName) {
|
|
520
|
+
const parts = streamCollectionName.split(":");
|
|
521
|
+
return {
|
|
522
|
+
streamType: parts[1]
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
|
|
479
526
|
|
|
480
527
|
|
|
481
528
|
|
|
@@ -484,5 +531,5 @@ function fromStreamName(streamName) {
|
|
|
484
531
|
|
|
485
532
|
|
|
486
533
|
|
|
487
|
-
exports.
|
|
534
|
+
exports.MongoDBEventStoreDefaultStreamVersion = MongoDBEventStoreDefaultStreamVersion; exports.fromStreamCollectionName = fromStreamCollectionName; exports.fromStreamName = fromStreamName; exports.getMongoDBEventStore = getMongoDBEventStore; exports.handleInlineProjections = handleInlineProjections; exports.mongoDBInlineProjection = mongoDBInlineProjection; exports.toStreamCollectionName = toStreamCollectionName; exports.toStreamName = toStreamName;
|
|
488
535
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/runner/work/emmett/emmett/src/packages/emmett-mongodb/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/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/mongoDBEventStore.ts","../src/eventStore/projections/index.ts"],"names":[],"mappings":"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","file":"/home/runner/work/emmett/emmett/src/packages/emmett-mongodb/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 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 Event,\n ReadEvent,\n ReadEventMetadataWithGlobalPosition,\n} from '../typing';\nimport {\n type AggregateStreamOptions,\n type AggregateStreamResult,\n type AppendToStreamOptions,\n type AppendToStreamResult,\n type EventStore,\n type ReadStreamOptions,\n type ReadStreamResult,\n} from './eventStore';\nimport { assertExpectedVersionMatchesCurrent } from './expectedVersion';\nimport { StreamingCoordinator } from './subscriptions';\n\nexport type EventHandler<E extends Event = Event> = (\n eventEnvelope: ReadEvent<E>,\n) => void;\n\nexport const InMemoryEventStoreDefaultStreamVersion = 0n;\n\nexport type InMemoryEventStore =\n EventStore<ReadEventMetadataWithGlobalPosition>;\n\nexport const getInMemoryEventStore = (): 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 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 return {\n ...event,\n metadata: {\n ...(event.metadata ?? {}),\n streamName,\n eventId: uuid(),\n streamPosition: BigInt(currentEvents.length + index + 1),\n globalPosition: BigInt(getAllEventsCount() + index + 1),\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 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 { 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 {\n ExpectedVersionConflictError,\n STREAM_DOES_NOT_EXIST,\n assertExpectedVersionMatchesCurrent,\n type AggregateStreamOptions,\n type AggregateStreamResult,\n type AppendToStreamOptions,\n type AppendToStreamResult,\n type Event,\n type EventMetaDataOf,\n type EventStore,\n type ExpectedStreamVersion,\n type ProjectionRegistration,\n type ReadEvent,\n type ReadEventMetadataWithoutGlobalPosition,\n type ReadStreamOptions,\n type ReadStreamResult,\n} from '@event-driven-io/emmett';\nimport {\n type Collection,\n type Document,\n type UpdateFilter,\n type WithId,\n} from 'mongodb';\nimport { v4 as uuid } from 'uuid';\nimport {\n handleInlineProjections,\n type MongoDBInlineProjectionDefinition,\n type MongoDBProjectionInlineHandlerContext,\n} from './projections';\n\nexport const MongoDBEventStoreDefaultStreamVersion = 0n;\n\nexport type StreamType = string;\nexport type StreamName<T extends StreamType = StreamType> = `${T}:${string}`;\n\nexport type StreamNameParts<T extends StreamType = StreamType> = {\n streamType: T;\n streamId: string;\n};\n\nexport type MongoDBReadModelMetadata = {\n name: string;\n schemaVersion: number;\n streamPosition: bigint;\n};\n\nexport type MongoDBReadModel<Doc extends Document = Document> = Doc & {\n _metadata: MongoDBReadModelMetadata;\n};\n\nexport interface EventStream<\n EventType extends Event = Event,\n EventMetaDataType extends EventMetaDataOf<EventType> &\n MongoDBReadEventMetadata = EventMetaDataOf<EventType> &\n MongoDBReadEventMetadata,\n> {\n streamName: string;\n events: Array<ReadEvent<EventType, EventMetaDataType>>;\n metadata: {\n streamId: string;\n streamType: StreamType;\n streamPosition: bigint;\n createdAt: Date;\n updatedAt: Date;\n };\n projections: Record<string, MongoDBReadModel>;\n}\n\nexport interface MongoDBConnectionOptions {\n connectionString: string;\n database: string;\n collection?: string;\n}\n\nexport type MongoDBReadEventMetadata =\n ReadEventMetadataWithoutGlobalPosition<bigint>;\n\nexport type MongoDBReadEvent<EventType extends Event = Event> = ReadEvent<\n EventType,\n MongoDBReadEventMetadata\n>;\n\nexport type MongoDBEventStoreOptions = {\n collection: Collection<EventStream>;\n projections?: ProjectionRegistration<\n 'inline',\n MongoDBReadEventMetadata,\n MongoDBProjectionInlineHandlerContext\n >[];\n};\n\nexport class MongoDBEventStore implements EventStore<MongoDBReadEventMetadata> {\n private readonly collection: Collection<EventStream>;\n private readonly inlineProjections: MongoDBInlineProjectionDefinition[];\n\n constructor(options: MongoDBEventStoreOptions) {\n this.collection = options.collection;\n this.inlineProjections = (options.projections ?? [])\n .filter(({ type }) => type === 'inline')\n .map(\n ({ projection }) => projection,\n ) as MongoDBInlineProjectionDefinition[];\n }\n\n async readStream<EventType extends Event>(\n streamName: StreamName,\n options?: ReadStreamOptions,\n ): Promise<\n Exclude<ReadStreamResult<EventType, MongoDBReadEventMetadata>, null>\n > {\n const expectedStreamVersion = options?.expectedStreamVersion;\n\n const filter = {\n streamName: { $eq: streamName },\n };\n\n const eventsSliceArr: number[] = [];\n\n if (options && 'from' in options) {\n eventsSliceArr.push(Number(options.from));\n } else {\n eventsSliceArr.push(0);\n }\n\n if (options && 'to' in options) {\n eventsSliceArr.push(Number(options.to));\n }\n\n const eventsSlice =\n eventsSliceArr.length > 1 ? { $slice: eventsSliceArr } : 1;\n\n const stream = await this.collection.findOne<\n WithId<Pick<EventStream<EventType>, 'metadata' | 'events'>>\n >(filter, {\n useBigInt64: true,\n projection: {\n metadata: 1,\n events: eventsSlice,\n },\n });\n\n if (!stream) {\n return {\n events: [],\n currentStreamVersion: MongoDBEventStoreDefaultStreamVersion,\n streamExists: false,\n };\n }\n\n assertExpectedVersionMatchesCurrent(\n stream.metadata.streamPosition,\n expectedStreamVersion,\n MongoDBEventStoreDefaultStreamVersion,\n );\n\n return {\n events: stream.events,\n currentStreamVersion: stream.metadata.streamPosition,\n streamExists: true,\n };\n }\n\n async aggregateStream<State, EventType extends Event>(\n streamName: StreamName,\n options: AggregateStreamOptions<State, EventType, MongoDBReadEventMetadata>,\n ): Promise<AggregateStreamResult<State>> {\n const stream = await this.readStream<EventType>(streamName, options?.read);\n\n const state = stream.events.reduce(options.evolve, options.initialState());\n return {\n state,\n currentStreamVersion: stream.currentStreamVersion,\n streamExists: stream.streamExists,\n };\n }\n\n async appendToStream<EventType extends Event>(\n streamName: StreamName,\n events: EventType[],\n options?: AppendToStreamOptions,\n ): Promise<AppendToStreamResult> {\n const expectedStreamVersion = options?.expectedStreamVersion;\n\n const stream = await this.collection.findOne<\n WithId<Pick<EventStream<EventType>, 'metadata' | 'projections'>>\n >(\n { streamName: { $eq: streamName } },\n {\n useBigInt64: true,\n projection: {\n 'metadata.streamPosition': 1,\n projections: 1,\n },\n },\n );\n\n const currentStreamVersion =\n stream?.metadata.streamPosition ?? MongoDBEventStoreDefaultStreamVersion;\n\n assertExpectedVersionMatchesCurrent(\n currentStreamVersion,\n expectedStreamVersion,\n MongoDBEventStoreDefaultStreamVersion,\n );\n\n let streamOffset = currentStreamVersion;\n\n const eventsToAppend: ReadEvent<\n EventType,\n EventMetaDataOf<EventType> & MongoDBReadEventMetadata\n >[] = events.map((event) => {\n const metadata: MongoDBReadEventMetadata = {\n eventId: uuid(),\n streamName,\n streamPosition: ++streamOffset,\n };\n return {\n type: event.type,\n data: event.data,\n metadata: {\n ...metadata,\n ...(event.metadata ?? {}),\n },\n } as ReadEvent<\n EventType,\n EventMetaDataOf<EventType> & MongoDBReadEventMetadata\n >;\n });\n\n const { streamId, streamType } = fromStreamName(streamName);\n const now = new Date();\n const updates: UpdateFilter<EventStream> = {\n $push: { events: { $each: eventsToAppend } },\n $set: { 'metadata.updatedAt': now },\n $inc: { 'metadata.streamPosition': BigInt(events.length) },\n $setOnInsert: {\n 'metadata.streamId': streamId,\n 'metadata.streamType': streamType,\n 'metadata.createdAt': now,\n },\n };\n\n if (this.inlineProjections) {\n await handleInlineProjections({\n readModels: stream?.projections ?? {},\n events: eventsToAppend,\n projections: this.inlineProjections,\n collection: this.collection,\n updates,\n client: {},\n });\n }\n\n const updatedStream = await this.collection.updateOne(\n {\n streamName: { $eq: streamName },\n 'metadata.streamPosition': toExpectedVersion(\n options?.expectedStreamVersion,\n ),\n },\n updates,\n { useBigInt64: true, upsert: true },\n );\n\n if (!updatedStream) {\n throw new ExpectedVersionConflictError(\n currentStreamVersion,\n options?.expectedStreamVersion ?? 0n,\n );\n }\n\n return {\n nextExpectedStreamVersion:\n currentStreamVersion + BigInt(eventsToAppend.length),\n createdNewStream:\n currentStreamVersion === MongoDBEventStoreDefaultStreamVersion,\n };\n }\n}\n\nexport const getMongoDBEventStore = (\n options: ConstructorParameters<typeof MongoDBEventStore>[0],\n) => {\n const eventStore = new MongoDBEventStore(options);\n return eventStore;\n};\n\nfunction toExpectedVersion(\n expectedStreamVersion?: ExpectedStreamVersion,\n): bigint | undefined {\n if (!expectedStreamVersion) return undefined;\n\n if (typeof expectedStreamVersion === 'string') {\n switch (expectedStreamVersion) {\n case STREAM_DOES_NOT_EXIST:\n return BigInt(0);\n default:\n return undefined;\n }\n }\n\n return expectedStreamVersion;\n}\n\n/**\n * Accepts a `streamType` (the type/category of the event stream) and an `streamId`\n * (the individual entity/object or aggregate ID) and combines them to a singular\n * `streamName` which can be used in `EventStore`.\n */\nexport function toStreamName<T extends StreamType>(\n streamType: T,\n streamId: string,\n): StreamName<T> {\n return `${streamType}:${streamId}`;\n}\n\n/**\n * Accepts a fully formatted `streamName` and returns the broken down\n * `streamType` and `streamId`.\n */\nexport function fromStreamName<T extends StreamType>(\n streamName: StreamName<T>,\n): StreamNameParts<T> {\n const parts = streamName.split(':') as [T, string];\n return {\n streamType: parts[0],\n streamId: parts[1],\n };\n}\n","import {\n type CanHandle,\n type Event,\n type EventMetaDataOf,\n type ProjectionHandler,\n type ReadEvent,\n type TypedProjectionDefinition,\n} from '@event-driven-io/emmett';\nimport type { Collection, Document, UpdateFilter } from 'mongodb';\nimport type {\n EventStream,\n MongoDBReadEventMetadata,\n MongoDBReadModel,\n MongoDBReadModelMetadata,\n} from '../mongoDBEventStore';\n\nexport type MongoDBProjectionInlineHandlerContext<\n EventType extends Event = Event,\n EventMetaDataType extends EventMetaDataOf<EventType> &\n MongoDBReadEventMetadata = EventMetaDataOf<EventType> &\n MongoDBReadEventMetadata,\n> = {\n document: MongoDBReadModel | null;\n updates: UpdateFilter<EventStream<EventType, EventMetaDataType>>;\n collection: Collection<EventStream<EventType, EventMetaDataType>>;\n};\n\nexport type MongoDBInlineProjectionHandler<\n EventType extends Event = Event,\n EventMetaDataType extends EventMetaDataOf<EventType> &\n MongoDBReadEventMetadata = EventMetaDataOf<EventType> &\n MongoDBReadEventMetadata,\n> = ProjectionHandler<\n EventType,\n EventMetaDataType,\n MongoDBProjectionInlineHandlerContext\n>;\n\nexport type MongoDBInlineProjectionDefinition<\n EventType extends Event = Event,\n EventMetaDataType extends EventMetaDataOf<EventType> &\n MongoDBReadEventMetadata = EventMetaDataOf<EventType> &\n MongoDBReadEventMetadata,\n> = TypedProjectionDefinition<\n EventType,\n EventMetaDataType,\n MongoDBProjectionInlineHandlerContext\n> & { name: string };\n\nexport type InlineProjectionHandlerOptions<\n EventType extends Event = Event,\n EventMetaDataType extends EventMetaDataOf<EventType> &\n MongoDBReadEventMetadata = EventMetaDataOf<EventType> &\n MongoDBReadEventMetadata,\n> = {\n readModels: Record<string, MongoDBReadModel>;\n events: Array<ReadEvent<EventType, EventMetaDataType>>;\n projections: MongoDBInlineProjectionDefinition<\n EventType,\n EventMetaDataType\n >[];\n collection: Collection<EventStream>;\n updates: UpdateFilter<EventStream<Event>>;\n // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n client: {\n //todo: add client here\n };\n};\n\nexport const handleInlineProjections = async <\n EventType extends Event = Event,\n EventMetaDataType extends EventMetaDataOf<EventType> &\n MongoDBReadEventMetadata = EventMetaDataOf<EventType> &\n MongoDBReadEventMetadata,\n>(\n options: InlineProjectionHandlerOptions<EventType, EventMetaDataType>,\n): Promise<void> => {\n const {\n events,\n projections: allProjections,\n updates: update,\n collection,\n readModels,\n } = options;\n\n const eventTypes = events.map((e) => e.type);\n\n const projections = allProjections.filter((p) =>\n p.canHandle.some((type) => eventTypes.includes(type)),\n );\n\n for (const projection of projections) {\n await projection.handle(events, {\n document: readModels[projection.name] ?? null,\n collection,\n updates: update,\n });\n }\n};\n\nexport type MongoDBWithNotNullDocumentEvolve<\n Doc extends Document,\n EventType extends Event,\n EventMetaDataType extends EventMetaDataOf<EventType> &\n MongoDBReadEventMetadata = EventMetaDataOf<EventType> &\n MongoDBReadEventMetadata,\n> =\n | ((\n document: Doc,\n event: ReadEvent<EventType, EventMetaDataType>,\n ) => Doc | null)\n | ((document: Doc, event: ReadEvent<EventType>) => Promise<Doc | null>);\n\nexport type MongoDBWithNullableDocumentEvolve<\n Doc extends Document,\n EventType extends Event,\n EventMetaDataType extends EventMetaDataOf<EventType> &\n MongoDBReadEventMetadata = EventMetaDataOf<EventType> &\n MongoDBReadEventMetadata,\n> =\n | ((\n document: Doc | null,\n event: ReadEvent<EventType, EventMetaDataType>,\n ) => Doc | null)\n | ((\n document: Doc | null,\n event: ReadEvent<EventType>,\n ) => Promise<Doc | null>);\n\nexport type MongoDBInlineProjectionOptions<\n Doc extends Document,\n EventType extends Event,\n EventMetaDataType extends EventMetaDataOf<EventType> &\n MongoDBReadEventMetadata = EventMetaDataOf<EventType> &\n MongoDBReadEventMetadata,\n> = {\n name?: string;\n schemaVersion?: number;\n canHandle: CanHandle<EventType>;\n} & (\n | {\n evolve: MongoDBWithNullableDocumentEvolve<\n Doc,\n EventType,\n EventMetaDataType\n >;\n }\n | {\n evolve: MongoDBWithNotNullDocumentEvolve<\n Doc,\n EventType,\n EventMetaDataType\n >;\n initialState: () => Doc;\n }\n);\n\nexport const mongoDBInlineProjection = <\n Doc extends Document,\n EventType extends Event,\n EventMetaDataType extends EventMetaDataOf<EventType> &\n MongoDBReadEventMetadata = EventMetaDataOf<EventType> &\n MongoDBReadEventMetadata,\n>(\n options: MongoDBInlineProjectionOptions<Doc, EventType, EventMetaDataType>,\n): MongoDBInlineProjectionDefinition => {\n const projectionName = options.name ?? '_default';\n const schemaVersion = options.schemaVersion ?? 1;\n\n return {\n name: projectionName,\n canHandle: options.canHandle,\n handle: async (events, { document, updates }) => {\n if (events.length === 0) return;\n\n let state =\n 'initialState' in options\n ? (document ?? options.initialState())\n : document;\n\n for (const event of events) {\n state = await options.evolve(\n state as Doc,\n event as ReadEvent<EventType, EventMetaDataType>,\n );\n }\n\n const metadata: MongoDBReadModelMetadata = {\n name: projectionName,\n schemaVersion,\n streamPosition: events[events.length - 1]!.metadata.streamPosition,\n };\n\n updates.$set![`projections.${options.name}`] =\n state !== null\n ? {\n ...state,\n _metadata: metadata,\n }\n : null;\n },\n };\n};\n"]}
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/emmett/emmett/src/packages/emmett-mongodb/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/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/mongoDBEventStore.ts","../src/eventStore/projections/index.ts"],"names":[],"mappings":"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","file":"/home/runner/work/emmett/emmett/src/packages/emmett-mongodb/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 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 Event,\n ReadEvent,\n ReadEventMetadataWithGlobalPosition,\n} from '../typing';\nimport {\n type AggregateStreamOptions,\n type AggregateStreamResult,\n type AppendToStreamOptions,\n type AppendToStreamResult,\n type EventStore,\n type ReadStreamOptions,\n type ReadStreamResult,\n} from './eventStore';\nimport { assertExpectedVersionMatchesCurrent } from './expectedVersion';\nimport { StreamingCoordinator } from './subscriptions';\n\nexport type EventHandler<E extends Event = Event> = (\n eventEnvelope: ReadEvent<E>,\n) => void;\n\nexport const InMemoryEventStoreDefaultStreamVersion = 0n;\n\nexport type InMemoryEventStore =\n EventStore<ReadEventMetadataWithGlobalPosition>;\n\nexport const getInMemoryEventStore = (): 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 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 return {\n ...event,\n metadata: {\n ...(event.metadata ?? {}),\n streamName,\n eventId: uuid(),\n streamPosition: BigInt(currentEvents.length + index + 1),\n globalPosition: BigInt(getAllEventsCount() + index + 1),\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 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 { 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 {\n ExpectedVersionConflictError,\n STREAM_DOES_NOT_EXIST,\n assertExpectedVersionMatchesCurrent,\n type AggregateStreamOptions,\n type AggregateStreamResult,\n type AppendToStreamOptions,\n type AppendToStreamResult,\n type Event,\n type EventMetaDataOf,\n type EventStore,\n type ExpectedStreamVersion,\n type ProjectionRegistration,\n type ReadEvent,\n type ReadEventMetadataWithoutGlobalPosition,\n type ReadStreamOptions,\n type ReadStreamResult,\n} from '@event-driven-io/emmett';\nimport {\n Db,\n MongoClient,\n type Collection,\n type Document,\n type MongoClientOptions,\n type UpdateFilter,\n type WithId,\n} from 'mongodb';\nimport { v4 as uuid } from 'uuid';\nimport {\n handleInlineProjections,\n type MongoDBInlineProjectionDefinition,\n type MongoDBProjectionInlineHandlerContext,\n} from './projections';\n\nexport const MongoDBEventStoreDefaultStreamVersion = 0n;\n\nexport type StreamType = string;\nexport type StreamName<T extends StreamType = StreamType> = `${T}:${string}`;\n\nexport type StreamNameParts<T extends StreamType = StreamType> = {\n streamType: T;\n streamId: string;\n};\n\nexport type StreamCollectionName<T extends StreamType = StreamType> =\n `emt:${T}`;\n\nexport type StreamCollectionNameParts<T extends StreamType = StreamType> = {\n streamType: T;\n};\n\nexport type MongoDBReadModelMetadata = {\n name: string;\n schemaVersion: number;\n streamPosition: bigint;\n};\n\nexport type MongoDBReadModel<Doc extends Document = Document> = Doc & {\n _metadata: MongoDBReadModelMetadata;\n};\n\nexport interface EventStream<\n EventType extends Event = Event,\n EventMetaDataType extends EventMetaDataOf<EventType> &\n MongoDBReadEventMetadata = EventMetaDataOf<EventType> &\n MongoDBReadEventMetadata,\n> {\n streamName: string;\n messages: Array<ReadEvent<EventType, EventMetaDataType>>;\n metadata: {\n streamId: string;\n streamType: StreamType;\n streamPosition: bigint;\n createdAt: Date;\n updatedAt: Date;\n };\n projections: Record<string, MongoDBReadModel>;\n}\n\nexport type MongoDBReadEventMetadata =\n ReadEventMetadataWithoutGlobalPosition<bigint>;\n\nexport type MongoDBReadEvent<EventType extends Event = Event> = ReadEvent<\n EventType,\n MongoDBReadEventMetadata\n>;\n\nexport type MongoDBEventStoreOptions = {\n database?: string;\n collection?: string;\n projections?: ProjectionRegistration<\n 'inline',\n MongoDBReadEventMetadata,\n MongoDBProjectionInlineHandlerContext\n >[];\n} & (\n | {\n client: MongoClient;\n }\n | {\n connectionString: string;\n clientOptions?: MongoClientOptions;\n }\n);\n\nexport type MongoDBEventStore = EventStore<MongoDBReadEventMetadata> & {\n close: () => Promise<void>;\n};\n\nclass MongoDBEventStoreImplementation implements MongoDBEventStore {\n private readonly client: MongoClient;\n private readonly defaultOptions: {\n database: string | undefined;\n collection: string | undefined;\n };\n private shouldManageClientLifetime: boolean;\n private db: Db | undefined;\n private streamCollections: Map<string, Collection<EventStream>> = new Map();\n private readonly inlineProjections: MongoDBInlineProjectionDefinition[];\n private isClosed: boolean = false;\n\n constructor(options: MongoDBEventStoreOptions) {\n this.client =\n 'client' in options\n ? options.client\n : new MongoClient(options.connectionString, options.clientOptions);\n this.shouldManageClientLifetime = !('client' in options);\n this.defaultOptions = {\n database: options.database,\n collection: options.collection,\n };\n this.inlineProjections = (options.projections ?? [])\n .filter(({ type }) => type === 'inline')\n .map(\n ({ projection }) => projection,\n ) as MongoDBInlineProjectionDefinition[];\n }\n\n async readStream<EventType extends Event>(\n streamName: StreamName,\n options?: ReadStreamOptions,\n ): Promise<\n Exclude<ReadStreamResult<EventType, MongoDBReadEventMetadata>, null>\n > {\n const { streamType } = fromStreamName(streamName);\n const expectedStreamVersion = options?.expectedStreamVersion;\n\n const collection = await this.collectionFor(streamType);\n\n const filter = {\n streamName: { $eq: streamName },\n };\n\n const eventsSliceArr: number[] = [];\n\n if (options && 'from' in options) {\n eventsSliceArr.push(Number(options.from));\n } else {\n eventsSliceArr.push(0);\n }\n\n if (options && 'to' in options) {\n eventsSliceArr.push(Number(options.to));\n }\n\n const eventsSlice =\n eventsSliceArr.length > 1 ? { $slice: eventsSliceArr } : 1;\n\n const stream = await collection.findOne<\n WithId<Pick<EventStream<EventType>, 'metadata' | 'messages'>>\n >(filter, {\n useBigInt64: true,\n projection: {\n metadata: 1,\n messages: eventsSlice,\n },\n });\n\n if (!stream) {\n return {\n events: [],\n currentStreamVersion: MongoDBEventStoreDefaultStreamVersion,\n streamExists: false,\n };\n }\n\n assertExpectedVersionMatchesCurrent(\n stream.metadata.streamPosition,\n expectedStreamVersion,\n MongoDBEventStoreDefaultStreamVersion,\n );\n\n return {\n events: stream.messages,\n currentStreamVersion: stream.metadata.streamPosition,\n streamExists: true,\n };\n }\n\n async aggregateStream<State, EventType extends Event>(\n streamName: StreamName,\n options: AggregateStreamOptions<State, EventType, MongoDBReadEventMetadata>,\n ): Promise<AggregateStreamResult<State>> {\n const stream = await this.readStream<EventType>(streamName, options?.read);\n\n const state = stream.events.reduce(options.evolve, options.initialState());\n return {\n state,\n currentStreamVersion: stream.currentStreamVersion,\n streamExists: stream.streamExists,\n };\n }\n\n async appendToStream<EventType extends Event>(\n streamName: StreamName,\n events: EventType[],\n options?: AppendToStreamOptions,\n ): Promise<AppendToStreamResult> {\n const { streamId, streamType } = fromStreamName(streamName);\n const expectedStreamVersion = options?.expectedStreamVersion;\n\n const collection = await this.collectionFor(streamType);\n\n const stream = await collection.findOne<\n WithId<Pick<EventStream<EventType>, 'metadata' | 'projections'>>\n >(\n { streamName: { $eq: streamName } },\n {\n useBigInt64: true,\n projection: {\n 'metadata.streamPosition': 1,\n projections: 1,\n },\n },\n );\n\n const currentStreamVersion =\n stream?.metadata.streamPosition ?? MongoDBEventStoreDefaultStreamVersion;\n\n assertExpectedVersionMatchesCurrent(\n currentStreamVersion,\n expectedStreamVersion,\n MongoDBEventStoreDefaultStreamVersion,\n );\n\n let streamOffset = currentStreamVersion;\n\n const eventsToAppend: ReadEvent<\n EventType,\n EventMetaDataOf<EventType> & MongoDBReadEventMetadata\n >[] = events.map((event) => {\n const metadata: MongoDBReadEventMetadata = {\n eventId: uuid(),\n streamName,\n streamPosition: ++streamOffset,\n };\n return {\n type: event.type,\n data: event.data,\n metadata: {\n ...metadata,\n ...(event.metadata ?? {}),\n },\n } as ReadEvent<\n EventType,\n EventMetaDataOf<EventType> & MongoDBReadEventMetadata\n >;\n });\n\n const now = new Date();\n const updates: UpdateFilter<EventStream> = {\n $push: { messages: { $each: eventsToAppend } },\n $set: { 'metadata.updatedAt': now },\n $inc: { 'metadata.streamPosition': BigInt(events.length) },\n $setOnInsert: {\n 'metadata.streamId': streamId,\n 'metadata.streamType': streamType,\n 'metadata.createdAt': now,\n },\n };\n\n if (this.inlineProjections) {\n await handleInlineProjections({\n readModels: stream?.projections ?? {},\n events: eventsToAppend,\n projections: this.inlineProjections,\n collection,\n updates,\n client: {},\n });\n }\n\n const updatedStream = await collection.updateOne(\n {\n streamName: { $eq: streamName },\n 'metadata.streamPosition': toExpectedVersion(\n options?.expectedStreamVersion,\n ),\n },\n updates,\n { useBigInt64: true, upsert: true },\n );\n\n if (!updatedStream) {\n throw new ExpectedVersionConflictError(\n currentStreamVersion,\n options?.expectedStreamVersion ?? 0n,\n );\n }\n\n return {\n nextExpectedStreamVersion:\n currentStreamVersion + BigInt(eventsToAppend.length),\n createdNewStream:\n currentStreamVersion === MongoDBEventStoreDefaultStreamVersion,\n };\n }\n\n close(): Promise<void> {\n if (this.isClosed) return Promise.resolve();\n\n this.isClosed = true;\n if (!this.shouldManageClientLifetime) return Promise.resolve();\n\n return this.client.close();\n }\n\n private getDB = async (): Promise<Db> => {\n if (!this.db) {\n if (!this.isClosed) await this.client.connect();\n\n this.db = this.client.db(this.defaultOptions.database);\n }\n return this.db;\n };\n\n private collectionFor = async <EventType extends Event>(\n streamType: StreamType,\n ): Promise<Collection<EventStream<EventType>>> => {\n const collectionName =\n this.defaultOptions?.collection ?? toStreamCollectionName(streamType);\n\n let collection = this.streamCollections.get(collectionName) as\n | Collection<EventStream<EventType>>\n | undefined;\n\n if (collection) return collection;\n\n const db = await this.getDB();\n collection = db.collection<EventStream<EventType>>(collectionName);\n\n this.streamCollections.set(\n collectionName,\n collection as Collection<EventStream>,\n );\n\n return collection;\n };\n}\n\nexport const getMongoDBEventStore = (\n options: MongoDBEventStoreOptions,\n): MongoDBEventStore => new MongoDBEventStoreImplementation(options);\n\nfunction toExpectedVersion(\n expectedStreamVersion?: ExpectedStreamVersion,\n): bigint | undefined {\n if (!expectedStreamVersion) return undefined;\n\n if (typeof expectedStreamVersion === 'string') {\n switch (expectedStreamVersion) {\n case STREAM_DOES_NOT_EXIST:\n return BigInt(0);\n default:\n return undefined;\n }\n }\n\n return expectedStreamVersion;\n}\n\n/**\n * Accepts a `streamType` (the type/category of the event stream) and an `streamId`\n * (the individual entity/object or aggregate ID) and combines them to a singular\n * `streamName` which can be used in `EventStore`.\n */\nexport function toStreamName<T extends StreamType>(\n streamType: T,\n streamId: string,\n): StreamName<T> {\n return `${streamType}:${streamId}`;\n}\n\n/**\n * Accepts a fully formatted `streamName` and returns the broken down\n * `streamType` and `streamId`.\n */\nexport function fromStreamName<T extends StreamType>(\n streamName: StreamName<T>,\n): StreamNameParts<T> {\n const parts = streamName.split(':') as [T, string];\n return {\n streamType: parts[0],\n streamId: parts[1],\n };\n}\n\n/**\n * Accepts a `streamType` (the type/category of the event stream)\n * and combines them to a `collectionName` which can be used in `EventStore`.\n */\nexport function toStreamCollectionName<T extends StreamType>(\n streamType: T,\n): StreamCollectionName<T> {\n return `emt:${streamType}`;\n}\n\n/**\n * Accepts a fully formatted `streamCollectionName` and returns the parsed `streamType`.\n */\nexport function fromStreamCollectionName<T extends StreamType>(\n streamCollectionName: StreamCollectionName<T>,\n): StreamCollectionNameParts<T> {\n const parts = streamCollectionName.split(':') as [string, T];\n return {\n streamType: parts[1],\n };\n}\n","import {\n type CanHandle,\n type Event,\n type EventMetaDataOf,\n type ProjectionHandler,\n type ReadEvent,\n type TypedProjectionDefinition,\n} from '@event-driven-io/emmett';\nimport type { Collection, Document, UpdateFilter } from 'mongodb';\nimport type {\n EventStream,\n MongoDBReadEventMetadata,\n MongoDBReadModel,\n MongoDBReadModelMetadata,\n} from '../mongoDBEventStore';\n\nexport type MongoDBProjectionInlineHandlerContext<\n EventType extends Event = Event,\n EventMetaDataType extends EventMetaDataOf<EventType> &\n MongoDBReadEventMetadata = EventMetaDataOf<EventType> &\n MongoDBReadEventMetadata,\n> = {\n document: MongoDBReadModel | null;\n updates: UpdateFilter<EventStream<EventType, EventMetaDataType>>;\n collection: Collection<EventStream<EventType, EventMetaDataType>>;\n};\n\nexport type MongoDBInlineProjectionHandler<\n EventType extends Event = Event,\n EventMetaDataType extends EventMetaDataOf<EventType> &\n MongoDBReadEventMetadata = EventMetaDataOf<EventType> &\n MongoDBReadEventMetadata,\n> = ProjectionHandler<\n EventType,\n EventMetaDataType,\n MongoDBProjectionInlineHandlerContext\n>;\n\nexport type MongoDBInlineProjectionDefinition<\n EventType extends Event = Event,\n EventMetaDataType extends EventMetaDataOf<EventType> &\n MongoDBReadEventMetadata = EventMetaDataOf<EventType> &\n MongoDBReadEventMetadata,\n> = TypedProjectionDefinition<\n EventType,\n EventMetaDataType,\n MongoDBProjectionInlineHandlerContext\n> & { name: string };\n\nexport type InlineProjectionHandlerOptions<\n EventType extends Event = Event,\n EventMetaDataType extends EventMetaDataOf<EventType> &\n MongoDBReadEventMetadata = EventMetaDataOf<EventType> &\n MongoDBReadEventMetadata,\n> = {\n readModels: Record<string, MongoDBReadModel>;\n events: Array<ReadEvent<EventType, EventMetaDataType>>;\n projections: MongoDBInlineProjectionDefinition<\n EventType,\n EventMetaDataType\n >[];\n collection: Collection<EventStream>;\n updates: UpdateFilter<EventStream<Event>>;\n // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n client: {\n //todo: add client here\n };\n};\n\nexport const handleInlineProjections = async <\n EventType extends Event = Event,\n EventMetaDataType extends EventMetaDataOf<EventType> &\n MongoDBReadEventMetadata = EventMetaDataOf<EventType> &\n MongoDBReadEventMetadata,\n>(\n options: InlineProjectionHandlerOptions<EventType, EventMetaDataType>,\n): Promise<void> => {\n const {\n events,\n projections: allProjections,\n updates: update,\n collection,\n readModels,\n } = options;\n\n const eventTypes = events.map((e) => e.type);\n\n const projections = allProjections.filter((p) =>\n p.canHandle.some((type) => eventTypes.includes(type)),\n );\n\n for (const projection of projections) {\n await projection.handle(events, {\n document: readModels[projection.name] ?? null,\n collection,\n updates: update,\n });\n }\n};\n\nexport type MongoDBWithNotNullDocumentEvolve<\n Doc extends Document,\n EventType extends Event,\n EventMetaDataType extends EventMetaDataOf<EventType> &\n MongoDBReadEventMetadata = EventMetaDataOf<EventType> &\n MongoDBReadEventMetadata,\n> =\n | ((\n document: Doc,\n event: ReadEvent<EventType, EventMetaDataType>,\n ) => Doc | null)\n | ((document: Doc, event: ReadEvent<EventType>) => Promise<Doc | null>);\n\nexport type MongoDBWithNullableDocumentEvolve<\n Doc extends Document,\n EventType extends Event,\n EventMetaDataType extends EventMetaDataOf<EventType> &\n MongoDBReadEventMetadata = EventMetaDataOf<EventType> &\n MongoDBReadEventMetadata,\n> =\n | ((\n document: Doc | null,\n event: ReadEvent<EventType, EventMetaDataType>,\n ) => Doc | null)\n | ((\n document: Doc | null,\n event: ReadEvent<EventType>,\n ) => Promise<Doc | null>);\n\nexport type MongoDBInlineProjectionOptions<\n Doc extends Document,\n EventType extends Event,\n EventMetaDataType extends EventMetaDataOf<EventType> &\n MongoDBReadEventMetadata = EventMetaDataOf<EventType> &\n MongoDBReadEventMetadata,\n> = {\n name?: string;\n schemaVersion?: number;\n canHandle: CanHandle<EventType>;\n} & (\n | {\n evolve: MongoDBWithNullableDocumentEvolve<\n Doc,\n EventType,\n EventMetaDataType\n >;\n }\n | {\n evolve: MongoDBWithNotNullDocumentEvolve<\n Doc,\n EventType,\n EventMetaDataType\n >;\n initialState: () => Doc;\n }\n);\n\nexport const mongoDBInlineProjection = <\n Doc extends Document,\n EventType extends Event,\n EventMetaDataType extends EventMetaDataOf<EventType> &\n MongoDBReadEventMetadata = EventMetaDataOf<EventType> &\n MongoDBReadEventMetadata,\n>(\n options: MongoDBInlineProjectionOptions<Doc, EventType, EventMetaDataType>,\n): MongoDBInlineProjectionDefinition => {\n const projectionName = options.name ?? '_default';\n const schemaVersion = options.schemaVersion ?? 1;\n\n return {\n name: projectionName,\n canHandle: options.canHandle,\n handle: async (events, { document, updates }) => {\n if (events.length === 0) return;\n\n let state =\n 'initialState' in options\n ? (document ?? options.initialState())\n : document;\n\n for (const event of events) {\n state = await options.evolve(\n state as Doc,\n event as ReadEvent<EventType, EventMetaDataType>,\n );\n }\n\n const metadata: MongoDBReadModelMetadata = {\n name: projectionName,\n schemaVersion,\n streamPosition: events[events.length - 1]!.metadata.streamPosition,\n };\n\n updates.$set![`projections.${options.name}`] =\n state !== null\n ? {\n ...state,\n _metadata: metadata,\n }\n : null;\n },\n };\n};\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Event, EventMetaDataOf, ProjectionHandler, TypedProjectionDefinition, ReadEvent, CanHandle, ReadEventMetadataWithoutGlobalPosition, ProjectionRegistration, EventStore
|
|
2
|
-
import { UpdateFilter, Collection, Document } from 'mongodb';
|
|
1
|
+
import { Event, EventMetaDataOf, ProjectionHandler, TypedProjectionDefinition, ReadEvent, CanHandle, ReadEventMetadataWithoutGlobalPosition, ProjectionRegistration, EventStore } from '@event-driven-io/emmett';
|
|
2
|
+
import { UpdateFilter, Collection, Document, MongoClient, MongoClientOptions } from 'mongodb';
|
|
3
3
|
|
|
4
4
|
type MongoDBProjectionInlineHandlerContext<EventType extends Event = Event, EventMetaDataType extends EventMetaDataOf<EventType> & MongoDBReadEventMetadata = EventMetaDataOf<EventType> & MongoDBReadEventMetadata> = {
|
|
5
5
|
document: MongoDBReadModel | null;
|
|
@@ -48,6 +48,10 @@ type StreamNameParts<T extends StreamType = StreamType> = {
|
|
|
48
48
|
streamType: T;
|
|
49
49
|
streamId: string;
|
|
50
50
|
};
|
|
51
|
+
type StreamCollectionName<T extends StreamType = StreamType> = `emt:${T}`;
|
|
52
|
+
type StreamCollectionNameParts<T extends StreamType = StreamType> = {
|
|
53
|
+
streamType: T;
|
|
54
|
+
};
|
|
51
55
|
type MongoDBReadModelMetadata = {
|
|
52
56
|
name: string;
|
|
53
57
|
schemaVersion: number;
|
|
@@ -58,7 +62,7 @@ type MongoDBReadModel<Doc extends Document = Document> = Doc & {
|
|
|
58
62
|
};
|
|
59
63
|
interface EventStream<EventType extends Event = Event, EventMetaDataType extends EventMetaDataOf<EventType> & MongoDBReadEventMetadata = EventMetaDataOf<EventType> & MongoDBReadEventMetadata> {
|
|
60
64
|
streamName: string;
|
|
61
|
-
|
|
65
|
+
messages: Array<ReadEvent<EventType, EventMetaDataType>>;
|
|
62
66
|
metadata: {
|
|
63
67
|
streamId: string;
|
|
64
68
|
streamType: StreamType;
|
|
@@ -68,26 +72,22 @@ interface EventStream<EventType extends Event = Event, EventMetaDataType extends
|
|
|
68
72
|
};
|
|
69
73
|
projections: Record<string, MongoDBReadModel>;
|
|
70
74
|
}
|
|
71
|
-
interface MongoDBConnectionOptions {
|
|
72
|
-
connectionString: string;
|
|
73
|
-
database: string;
|
|
74
|
-
collection?: string;
|
|
75
|
-
}
|
|
76
75
|
type MongoDBReadEventMetadata = ReadEventMetadataWithoutGlobalPosition<bigint>;
|
|
77
76
|
type MongoDBReadEvent<EventType extends Event = Event> = ReadEvent<EventType, MongoDBReadEventMetadata>;
|
|
78
77
|
type MongoDBEventStoreOptions = {
|
|
79
|
-
|
|
78
|
+
database?: string;
|
|
79
|
+
collection?: string;
|
|
80
80
|
projections?: ProjectionRegistration<'inline', MongoDBReadEventMetadata, MongoDBProjectionInlineHandlerContext>[];
|
|
81
|
+
} & ({
|
|
82
|
+
client: MongoClient;
|
|
83
|
+
} | {
|
|
84
|
+
connectionString: string;
|
|
85
|
+
clientOptions?: MongoClientOptions;
|
|
86
|
+
});
|
|
87
|
+
type MongoDBEventStore = EventStore<MongoDBReadEventMetadata> & {
|
|
88
|
+
close: () => Promise<void>;
|
|
81
89
|
};
|
|
82
|
-
declare
|
|
83
|
-
private readonly collection;
|
|
84
|
-
private readonly inlineProjections;
|
|
85
|
-
constructor(options: MongoDBEventStoreOptions);
|
|
86
|
-
readStream<EventType extends Event>(streamName: StreamName, options?: ReadStreamOptions): Promise<Exclude<ReadStreamResult<EventType, MongoDBReadEventMetadata>, null>>;
|
|
87
|
-
aggregateStream<State, EventType extends Event>(streamName: StreamName, options: AggregateStreamOptions<State, EventType, MongoDBReadEventMetadata>): Promise<AggregateStreamResult<State>>;
|
|
88
|
-
appendToStream<EventType extends Event>(streamName: StreamName, events: EventType[], options?: AppendToStreamOptions): Promise<AppendToStreamResult>;
|
|
89
|
-
}
|
|
90
|
-
declare const getMongoDBEventStore: (options: ConstructorParameters<typeof MongoDBEventStore>[0]) => MongoDBEventStore;
|
|
90
|
+
declare const getMongoDBEventStore: (options: MongoDBEventStoreOptions) => MongoDBEventStore;
|
|
91
91
|
/**
|
|
92
92
|
* Accepts a `streamType` (the type/category of the event stream) and an `streamId`
|
|
93
93
|
* (the individual entity/object or aggregate ID) and combines them to a singular
|
|
@@ -99,5 +99,14 @@ declare function toStreamName<T extends StreamType>(streamType: T, streamId: str
|
|
|
99
99
|
* `streamType` and `streamId`.
|
|
100
100
|
*/
|
|
101
101
|
declare function fromStreamName<T extends StreamType>(streamName: StreamName<T>): StreamNameParts<T>;
|
|
102
|
+
/**
|
|
103
|
+
* Accepts a `streamType` (the type/category of the event stream)
|
|
104
|
+
* and combines them to a `collectionName` which can be used in `EventStore`.
|
|
105
|
+
*/
|
|
106
|
+
declare function toStreamCollectionName<T extends StreamType>(streamType: T): StreamCollectionName<T>;
|
|
107
|
+
/**
|
|
108
|
+
* Accepts a fully formatted `streamCollectionName` and returns the parsed `streamType`.
|
|
109
|
+
*/
|
|
110
|
+
declare function fromStreamCollectionName<T extends StreamType>(streamCollectionName: StreamCollectionName<T>): StreamCollectionNameParts<T>;
|
|
102
111
|
|
|
103
|
-
export { type EventStream, type InlineProjectionHandlerOptions, type
|
|
112
|
+
export { type EventStream, type InlineProjectionHandlerOptions, type MongoDBEventStore, MongoDBEventStoreDefaultStreamVersion, type MongoDBEventStoreOptions, type MongoDBInlineProjectionDefinition, type MongoDBInlineProjectionHandler, type MongoDBInlineProjectionOptions, type MongoDBProjectionInlineHandlerContext, type MongoDBReadEvent, type MongoDBReadEventMetadata, type MongoDBReadModel, type MongoDBReadModelMetadata, type MongoDBWithNotNullDocumentEvolve, type MongoDBWithNullableDocumentEvolve, type StreamCollectionName, type StreamCollectionNameParts, type StreamName, type StreamNameParts, type StreamType, fromStreamCollectionName, fromStreamName, getMongoDBEventStore, handleInlineProjections, mongoDBInlineProjection, toStreamCollectionName, toStreamName };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Event, EventMetaDataOf, ProjectionHandler, TypedProjectionDefinition, ReadEvent, CanHandle, ReadEventMetadataWithoutGlobalPosition, ProjectionRegistration, EventStore
|
|
2
|
-
import { UpdateFilter, Collection, Document } from 'mongodb';
|
|
1
|
+
import { Event, EventMetaDataOf, ProjectionHandler, TypedProjectionDefinition, ReadEvent, CanHandle, ReadEventMetadataWithoutGlobalPosition, ProjectionRegistration, EventStore } from '@event-driven-io/emmett';
|
|
2
|
+
import { UpdateFilter, Collection, Document, MongoClient, MongoClientOptions } from 'mongodb';
|
|
3
3
|
|
|
4
4
|
type MongoDBProjectionInlineHandlerContext<EventType extends Event = Event, EventMetaDataType extends EventMetaDataOf<EventType> & MongoDBReadEventMetadata = EventMetaDataOf<EventType> & MongoDBReadEventMetadata> = {
|
|
5
5
|
document: MongoDBReadModel | null;
|
|
@@ -48,6 +48,10 @@ type StreamNameParts<T extends StreamType = StreamType> = {
|
|
|
48
48
|
streamType: T;
|
|
49
49
|
streamId: string;
|
|
50
50
|
};
|
|
51
|
+
type StreamCollectionName<T extends StreamType = StreamType> = `emt:${T}`;
|
|
52
|
+
type StreamCollectionNameParts<T extends StreamType = StreamType> = {
|
|
53
|
+
streamType: T;
|
|
54
|
+
};
|
|
51
55
|
type MongoDBReadModelMetadata = {
|
|
52
56
|
name: string;
|
|
53
57
|
schemaVersion: number;
|
|
@@ -58,7 +62,7 @@ type MongoDBReadModel<Doc extends Document = Document> = Doc & {
|
|
|
58
62
|
};
|
|
59
63
|
interface EventStream<EventType extends Event = Event, EventMetaDataType extends EventMetaDataOf<EventType> & MongoDBReadEventMetadata = EventMetaDataOf<EventType> & MongoDBReadEventMetadata> {
|
|
60
64
|
streamName: string;
|
|
61
|
-
|
|
65
|
+
messages: Array<ReadEvent<EventType, EventMetaDataType>>;
|
|
62
66
|
metadata: {
|
|
63
67
|
streamId: string;
|
|
64
68
|
streamType: StreamType;
|
|
@@ -68,26 +72,22 @@ interface EventStream<EventType extends Event = Event, EventMetaDataType extends
|
|
|
68
72
|
};
|
|
69
73
|
projections: Record<string, MongoDBReadModel>;
|
|
70
74
|
}
|
|
71
|
-
interface MongoDBConnectionOptions {
|
|
72
|
-
connectionString: string;
|
|
73
|
-
database: string;
|
|
74
|
-
collection?: string;
|
|
75
|
-
}
|
|
76
75
|
type MongoDBReadEventMetadata = ReadEventMetadataWithoutGlobalPosition<bigint>;
|
|
77
76
|
type MongoDBReadEvent<EventType extends Event = Event> = ReadEvent<EventType, MongoDBReadEventMetadata>;
|
|
78
77
|
type MongoDBEventStoreOptions = {
|
|
79
|
-
|
|
78
|
+
database?: string;
|
|
79
|
+
collection?: string;
|
|
80
80
|
projections?: ProjectionRegistration<'inline', MongoDBReadEventMetadata, MongoDBProjectionInlineHandlerContext>[];
|
|
81
|
+
} & ({
|
|
82
|
+
client: MongoClient;
|
|
83
|
+
} | {
|
|
84
|
+
connectionString: string;
|
|
85
|
+
clientOptions?: MongoClientOptions;
|
|
86
|
+
});
|
|
87
|
+
type MongoDBEventStore = EventStore<MongoDBReadEventMetadata> & {
|
|
88
|
+
close: () => Promise<void>;
|
|
81
89
|
};
|
|
82
|
-
declare
|
|
83
|
-
private readonly collection;
|
|
84
|
-
private readonly inlineProjections;
|
|
85
|
-
constructor(options: MongoDBEventStoreOptions);
|
|
86
|
-
readStream<EventType extends Event>(streamName: StreamName, options?: ReadStreamOptions): Promise<Exclude<ReadStreamResult<EventType, MongoDBReadEventMetadata>, null>>;
|
|
87
|
-
aggregateStream<State, EventType extends Event>(streamName: StreamName, options: AggregateStreamOptions<State, EventType, MongoDBReadEventMetadata>): Promise<AggregateStreamResult<State>>;
|
|
88
|
-
appendToStream<EventType extends Event>(streamName: StreamName, events: EventType[], options?: AppendToStreamOptions): Promise<AppendToStreamResult>;
|
|
89
|
-
}
|
|
90
|
-
declare const getMongoDBEventStore: (options: ConstructorParameters<typeof MongoDBEventStore>[0]) => MongoDBEventStore;
|
|
90
|
+
declare const getMongoDBEventStore: (options: MongoDBEventStoreOptions) => MongoDBEventStore;
|
|
91
91
|
/**
|
|
92
92
|
* Accepts a `streamType` (the type/category of the event stream) and an `streamId`
|
|
93
93
|
* (the individual entity/object or aggregate ID) and combines them to a singular
|
|
@@ -99,5 +99,14 @@ declare function toStreamName<T extends StreamType>(streamType: T, streamId: str
|
|
|
99
99
|
* `streamType` and `streamId`.
|
|
100
100
|
*/
|
|
101
101
|
declare function fromStreamName<T extends StreamType>(streamName: StreamName<T>): StreamNameParts<T>;
|
|
102
|
+
/**
|
|
103
|
+
* Accepts a `streamType` (the type/category of the event stream)
|
|
104
|
+
* and combines them to a `collectionName` which can be used in `EventStore`.
|
|
105
|
+
*/
|
|
106
|
+
declare function toStreamCollectionName<T extends StreamType>(streamType: T): StreamCollectionName<T>;
|
|
107
|
+
/**
|
|
108
|
+
* Accepts a fully formatted `streamCollectionName` and returns the parsed `streamType`.
|
|
109
|
+
*/
|
|
110
|
+
declare function fromStreamCollectionName<T extends StreamType>(streamCollectionName: StreamCollectionName<T>): StreamCollectionNameParts<T>;
|
|
102
111
|
|
|
103
|
-
export { type EventStream, type InlineProjectionHandlerOptions, type
|
|
112
|
+
export { type EventStream, type InlineProjectionHandlerOptions, type MongoDBEventStore, MongoDBEventStoreDefaultStreamVersion, type MongoDBEventStoreOptions, type MongoDBInlineProjectionDefinition, type MongoDBInlineProjectionHandler, type MongoDBInlineProjectionOptions, type MongoDBProjectionInlineHandlerContext, type MongoDBReadEvent, type MongoDBReadEventMetadata, type MongoDBReadModel, type MongoDBReadModelMetadata, type MongoDBWithNotNullDocumentEvolve, type MongoDBWithNullableDocumentEvolve, type StreamCollectionName, type StreamCollectionNameParts, type StreamName, type StreamNameParts, type StreamType, fromStreamCollectionName, fromStreamName, getMongoDBEventStore, handleInlineProjections, mongoDBInlineProjection, toStreamCollectionName, toStreamName };
|