@event-driven-io/emmett-mongodb 0.43.0-beta.2 → 0.43.0-beta.20

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 CHANGED
@@ -1,1121 +1,538 @@
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;// ../emmett/dist/chunk-AZDDB5SF.js
2
- var isNumber = (val) => typeof val === "number" && val === val;
3
- var isString = (val) => typeof val === "string";
4
- var isErrorConstructor = (expect) => {
5
- return typeof expect === "function" && expect.prototype && // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
6
- expect.prototype.constructor === expect;
7
- };
8
- var EmmettError = (_class = class _EmmettError extends Error {
9
- static __initStatic() {this.Codes = {
10
- ValidationError: 400,
11
- IllegalStateError: 403,
12
- NotFoundError: 404,
13
- ConcurrencyError: 412,
14
- InternalServerError: 500
15
- }}
16
-
17
- constructor(options) {
18
- const errorCode = options && typeof options === "object" && "errorCode" in options ? options.errorCode : isNumber(options) ? options : _EmmettError.Codes.InternalServerError;
19
- const message = options && typeof options === "object" && "message" in options ? options.message : isString(options) ? options : `Error with status code '${errorCode}' ocurred during Emmett processing`;
20
- super(message);
21
- this.errorCode = errorCode;
22
- Object.setPrototypeOf(this, _EmmettError.prototype);
23
- }
24
- static mapFrom(error) {
25
- if (_EmmettError.isInstanceOf(error)) {
26
- return error;
27
- }
28
- return new _EmmettError({
29
- errorCode: "errorCode" in error && error.errorCode !== void 0 && error.errorCode !== null ? error.errorCode : _EmmettError.Codes.InternalServerError,
30
- message: _nullishCoalesce(error.message, () => ( "An unknown error occurred"))
31
- });
32
- }
33
- static isInstanceOf(error, errorCode) {
34
- return typeof error === "object" && error !== null && "errorCode" in error && isNumber(error.errorCode) && (errorCode === void 0 || error.errorCode === errorCode);
35
- }
36
- }, _class.__initStatic(), _class);
37
- var ConcurrencyError = class _ConcurrencyError extends EmmettError {
38
- constructor(current, expected, message) {
39
- super({
40
- errorCode: EmmettError.Codes.ConcurrencyError,
41
- message: _nullishCoalesce(message, () => ( `Expected version ${expected.toString()} does not match current ${_optionalChain([current, 'optionalAccess', _2 => _2.toString, 'call', _3 => _3()])}`))
42
- });
43
- this.current = current;
44
- this.expected = expected;
45
- Object.setPrototypeOf(this, _ConcurrencyError.prototype);
46
- }
47
- };
48
-
49
- // ../emmett/dist/index.js
50
- var _uuid = require('uuid');
51
-
52
- var _asyncretry = require('async-retry'); var _asyncretry2 = _interopRequireDefault(_asyncretry);
53
-
54
-
55
-
56
- async function tryPublishMessagesAfterCommit(messages, options, context) {
57
- if (_optionalChain([options, 'optionalAccess', _4 => _4.onAfterCommit]) === void 0) return false;
58
- try {
59
- await _optionalChain([options, 'optionalAccess', _5 => _5.onAfterCommit, 'call', _6 => _6(messages, context)]);
60
- return true;
61
- } catch (error) {
62
- console.error(`Error in on after commit hook`, error);
63
- return false;
64
- }
65
- }
66
- var emmettPrefix = "emt";
67
- var defaultTag = `${emmettPrefix}:default`;
68
- var unknownTag = `${emmettPrefix}:unknown`;
69
- var STREAM_EXISTS = "STREAM_EXISTS";
70
- var STREAM_DOES_NOT_EXIST = "STREAM_DOES_NOT_EXIST";
71
- var NO_CONCURRENCY_CHECK = "NO_CONCURRENCY_CHECK";
72
- var matchesExpectedVersion = (current, expected, defaultVersion) => {
73
- if (expected === NO_CONCURRENCY_CHECK) return true;
74
- if (expected == STREAM_DOES_NOT_EXIST) return current === defaultVersion;
75
- if (expected == STREAM_EXISTS) return current !== defaultVersion;
76
- return current === expected;
77
- };
78
- var assertExpectedVersionMatchesCurrent = (current, expected, defaultVersion) => {
79
- expected ??= NO_CONCURRENCY_CHECK;
80
- if (!matchesExpectedVersion(current, expected, defaultVersion))
81
- throw new ExpectedVersionConflictError(current, expected);
82
- };
83
- var ExpectedVersionConflictError = class _ExpectedVersionConflictError extends ConcurrencyError {
84
- constructor(current, expected) {
85
- super(_optionalChain([current, 'optionalAccess', _7 => _7.toString, 'call', _8 => _8()]), _optionalChain([expected, 'optionalAccess', _9 => _9.toString, 'call', _10 => _10()]));
86
- Object.setPrototypeOf(this, _ExpectedVersionConflictError.prototype);
87
- }
88
- };
89
- var hasDuplicates = (array, predicate) => {
90
- const mapped = array.map(predicate);
91
- const uniqueValues = new Set(mapped);
92
- return uniqueValues.size < mapped.length;
93
- };
94
- var getDuplicates = (array, predicate) => {
95
- const map = /* @__PURE__ */ new Map();
96
- for (let i = 0; i < array.length; i++) {
97
- const item = array[i];
98
- const key = predicate(item, i, array);
99
- if (!map.has(key)) {
100
- map.set(key, []);
101
- }
102
- map.get(key).push(item);
103
- }
104
- return Array.from(map.values()).filter((group) => group.length > 1).flat();
105
- };
106
- var merge = (array, item, where, onExisting, onNotFound = () => void 0) => {
107
- let wasFound = false;
108
- const result = array.map((p) => {
109
- if (!where(p)) return p;
110
- wasFound = true;
111
- return onExisting(p);
112
- }).filter((p) => p !== void 0).map((p) => {
113
- if (!p) throw Error("That should not happen");
114
- return p;
115
- });
116
- if (!wasFound) {
117
- const result2 = onNotFound();
118
- if (result2 !== void 0) return [...array, item];
119
- }
120
- return result;
121
- };
122
- var arrayUtils = {
123
- merge,
124
- hasDuplicates,
125
- getDuplicates
126
- };
127
- var isPrimitive = (value) => {
128
- const type = typeof value;
129
- return value === null || value === void 0 || type === "boolean" || type === "number" || type === "string" || type === "symbol" || type === "bigint";
130
- };
131
- var compareArrays = (left, right) => {
132
- if (left.length !== right.length) {
133
- return false;
134
- }
135
- for (let i = 0; i < left.length; i++) {
136
- const leftHas = i in left;
137
- const rightHas = i in right;
138
- if (leftHas !== rightHas) return false;
139
- if (leftHas && !deepEquals(left[i], right[i])) return false;
140
- }
141
- return true;
142
- };
143
- var compareDates = (left, right) => {
144
- return left.getTime() === right.getTime();
145
- };
146
- var compareRegExps = (left, right) => {
147
- return left.toString() === right.toString();
148
- };
149
- var compareErrors = (left, right) => {
150
- if (left.message !== right.message || left.name !== right.name) {
151
- return false;
152
- }
153
- const leftKeys = Object.keys(left);
154
- const rightKeys = Object.keys(right);
155
- if (leftKeys.length !== rightKeys.length) return false;
156
- const rightKeySet = new Set(rightKeys);
157
- for (const key of leftKeys) {
158
- if (!rightKeySet.has(key)) return false;
159
- if (!deepEquals(left[key], right[key])) return false;
160
- }
161
- return true;
162
- };
163
- var compareMaps = (left, right) => {
164
- if (left.size !== right.size) return false;
165
- for (const [key, value] of left) {
166
- if (isPrimitive(key)) {
167
- if (!right.has(key) || !deepEquals(value, right.get(key))) {
168
- return false;
169
- }
170
- } else {
171
- let found = false;
172
- for (const [rightKey, rightValue] of right) {
173
- if (deepEquals(key, rightKey) && deepEquals(value, rightValue)) {
174
- found = true;
175
- break;
176
- }
177
- }
178
- if (!found) return false;
179
- }
180
- }
181
- return true;
182
- };
183
- var compareSets = (left, right) => {
184
- if (left.size !== right.size) return false;
185
- for (const leftItem of left) {
186
- if (isPrimitive(leftItem)) {
187
- if (!right.has(leftItem)) return false;
188
- } else {
189
- let found = false;
190
- for (const rightItem of right) {
191
- if (deepEquals(leftItem, rightItem)) {
192
- found = true;
193
- break;
194
- }
195
- }
196
- if (!found) return false;
197
- }
198
- }
199
- return true;
200
- };
201
- var compareArrayBuffers = (left, right) => {
202
- if (left.byteLength !== right.byteLength) return false;
203
- const leftView = new Uint8Array(left);
204
- const rightView = new Uint8Array(right);
205
- for (let i = 0; i < leftView.length; i++) {
206
- if (leftView[i] !== rightView[i]) return false;
207
- }
208
- return true;
209
- };
210
- var compareTypedArrays = (left, right) => {
211
- if (left.constructor !== right.constructor) return false;
212
- if (left.byteLength !== right.byteLength) return false;
213
- const leftArray = new Uint8Array(
214
- left.buffer,
215
- left.byteOffset,
216
- left.byteLength
217
- );
218
- const rightArray = new Uint8Array(
219
- right.buffer,
220
- right.byteOffset,
221
- right.byteLength
222
- );
223
- for (let i = 0; i < leftArray.length; i++) {
224
- if (leftArray[i] !== rightArray[i]) return false;
225
- }
226
- return true;
227
- };
228
- var compareObjects = (left, right) => {
229
- const keys1 = Object.keys(left);
230
- const keys2 = Object.keys(right);
231
- if (keys1.length !== keys2.length) {
232
- return false;
233
- }
234
- for (const key of keys1) {
235
- if (left[key] instanceof Function && right[key] instanceof Function) {
236
- continue;
237
- }
238
- const isEqual = deepEquals(left[key], right[key]);
239
- if (!isEqual) {
240
- return false;
241
- }
242
- }
243
- return true;
244
- };
245
- var getType = (value) => {
246
- if (value === null) return "null";
247
- if (value === void 0) return "undefined";
248
- const primitiveType = typeof value;
249
- if (primitiveType !== "object") return primitiveType;
250
- if (Array.isArray(value)) return "array";
251
- if (value instanceof Boolean) return "boxed-boolean";
252
- if (value instanceof Number) return "boxed-number";
253
- if (value instanceof String) return "boxed-string";
254
- if (value instanceof Date) return "date";
255
- if (value instanceof RegExp) return "regexp";
256
- if (value instanceof Error) return "error";
257
- if (value instanceof Map) return "map";
258
- if (value instanceof Set) return "set";
259
- if (value instanceof ArrayBuffer) return "arraybuffer";
260
- if (value instanceof DataView) return "dataview";
261
- if (value instanceof WeakMap) return "weakmap";
262
- if (value instanceof WeakSet) return "weakset";
263
- if (ArrayBuffer.isView(value)) return "typedarray";
264
- return "object";
265
- };
266
- var deepEquals = (left, right) => {
267
- if (left === right) return true;
268
- if (isEquatable(left)) {
269
- return left.equals(right);
270
- }
271
- const leftType = getType(left);
272
- const rightType = getType(right);
273
- if (leftType !== rightType) return false;
274
- switch (leftType) {
275
- case "null":
276
- case "undefined":
277
- case "boolean":
278
- case "number":
279
- case "bigint":
280
- case "string":
281
- case "symbol":
282
- case "function":
283
- return left === right;
284
- case "array":
285
- return compareArrays(left, right);
286
- case "date":
287
- return compareDates(left, right);
288
- case "regexp":
289
- return compareRegExps(left, right);
290
- case "error":
291
- return compareErrors(left, right);
292
- case "map":
293
- return compareMaps(
294
- left,
295
- right
296
- );
297
- case "set":
298
- return compareSets(left, right);
299
- case "arraybuffer":
300
- return compareArrayBuffers(left, right);
301
- case "dataview":
302
- case "weakmap":
303
- case "weakset":
304
- return false;
305
- case "typedarray":
306
- return compareTypedArrays(
307
- left,
308
- right
309
- );
310
- case "boxed-boolean":
311
- return left.valueOf() === right.valueOf();
312
- case "boxed-number":
313
- return left.valueOf() === right.valueOf();
314
- case "boxed-string":
315
- return left.valueOf() === right.valueOf();
316
- case "object":
317
- return compareObjects(
318
- left,
319
- right
320
- );
321
- default:
322
- return false;
323
- }
324
- };
325
- var isEquatable = (left) => {
326
- return left !== null && left !== void 0 && typeof left === "object" && "equals" in left && typeof left["equals"] === "function";
327
- };
328
- var ParseError = class extends Error {
329
- constructor(text) {
330
- super(`Cannot parse! ${text}`);
331
- }
332
- };
333
- var JSONParser = {
334
- stringify: (value, options) => {
335
- return JSON.stringify(
336
- _optionalChain([options, 'optionalAccess', _11 => _11.map]) ? options.map(value) : value,
337
- //TODO: Consider adding support to DateTime and adding specific format to mark that's a bigint
338
- // eslint-disable-next-line @typescript-eslint/no-unsafe-return
339
- (_, v) => typeof v === "bigint" ? v.toString() : v
340
- );
341
- },
342
- parse: (text, options) => {
343
- const parsed = JSON.parse(text, _optionalChain([options, 'optionalAccess', _12 => _12.reviver]));
344
- if (_optionalChain([options, 'optionalAccess', _13 => _13.typeCheck]) && !_optionalChain([options, 'optionalAccess', _14 => _14.typeCheck, 'call', _15 => _15(parsed)]))
345
- throw new ParseError(text);
346
- return _optionalChain([options, 'optionalAccess', _16 => _16.map]) ? options.map(parsed) : parsed;
347
- }
348
- };
349
- var textEncoder = new TextEncoder();
350
- var AssertionError = class extends Error {
351
- constructor(message2) {
352
- super(message2);
353
- }
354
- };
355
- var isSubset = (superObj, subObj) => {
356
- const sup = superObj;
357
- const sub = subObj;
358
- assertOk(sup);
359
- assertOk(sub);
360
- return Object.keys(sub).every((ele) => {
361
- if (sub[ele] !== null && typeof sub[ele] == "object") {
362
- return isSubset(sup[ele], sub[ele]);
363
- }
364
- return sub[ele] === sup[ele];
365
- });
366
- };
367
- var assertFails = (message2) => {
368
- throw new AssertionError(_nullishCoalesce(message2, () => ( "That should not ever happened, right?")));
369
- };
370
- function assertTrue(condition, message2) {
371
- if (condition !== true)
372
- throw new AssertionError(_nullishCoalesce(message2, () => ( `Condition is false`)));
373
- }
374
- function assertOk(obj, message2) {
375
- if (!obj) throw new AssertionError(_nullishCoalesce(message2, () => ( `Condition is not truthy`)));
376
- }
377
- var downcastRecordedMessage = (recordedMessage, options) => {
378
- if (!_optionalChain([options, 'optionalAccess', _17 => _17.downcast]))
379
- return recordedMessage;
380
- const downcasted = options.downcast(
381
- recordedMessage
382
- );
383
- return {
384
- ...recordedMessage,
385
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
386
- data: downcasted.data,
387
- ..."metadata" in recordedMessage || "metadata" in downcasted ? {
388
- metadata: {
389
- ..."metadata" in recordedMessage ? recordedMessage.metadata : {},
390
- ..."metadata" in downcasted ? downcasted.metadata : {}
391
- }
392
- } : {}
393
- };
394
- };
395
- var upcastRecordedMessage = (recordedMessage, options) => {
396
- if (!_optionalChain([options, 'optionalAccess', _18 => _18.upcast]))
397
- return recordedMessage;
398
- const upcasted = options.upcast(
399
- recordedMessage
400
- );
401
- return {
402
- ...recordedMessage,
403
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
404
- data: upcasted.data,
405
- ..."metadata" in recordedMessage || "metadata" in upcasted ? {
406
- metadata: {
407
- ..."metadata" in recordedMessage ? recordedMessage.metadata : {},
408
- ..."metadata" in upcasted ? upcasted.metadata : {}
409
- }
410
- } : {}
411
- };
412
- };
413
- var upcastRecordedMessages = (recordedMessages, options) => {
414
- if (!_optionalChain([options, 'optionalAccess', _19 => _19.upcast]))
415
- return recordedMessages;
416
- return recordedMessages.map(
417
- (recordedMessage) => upcastRecordedMessage(recordedMessage, options)
418
- );
419
- };
420
- var filterProjections = (type, projections2) => {
421
- const inlineProjections2 = projections2.filter((projection2) => projection2.type === type).map(({ projection: projection2 }) => projection2);
422
- const duplicateRegistrations = arrayUtils.getDuplicates(
423
- inlineProjections2,
424
- (proj) => proj.name
425
- );
426
- if (duplicateRegistrations.length > 0) {
427
- throw new EmmettError(`You cannot register multiple projections with the same name (or without the name).
428
- Ensure that:
429
- ${JSONParser.stringify(duplicateRegistrations)}
430
- have different names`);
431
- }
432
- return inlineProjections2;
433
- };
434
- var inlineProjections = (definitions) => definitions.map((definition) => ({
435
- type: "inline",
436
- projection: definition
437
- }));
438
- var asyncProjections = (definitions) => definitions.map((definition) => ({
439
- type: "inline",
440
- projection: definition
441
- }));
442
- var projections = {
443
- inline: inlineProjections,
444
- async: asyncProjections
445
- };
446
-
447
- // src/eventStore/mongoDBEventStore.ts
448
-
449
-
450
- var _mongodb = require('mongodb');
451
-
452
-
453
- // src/eventStore/projections/mongoDBInlineProjection.ts
454
- var MongoDBDefaultInlineProjectionName = "_default";
455
- var handleInlineProjections = async (options) => {
456
- const {
457
- events,
458
- projections: allProjections,
459
- updates: update,
460
- streamId,
461
- collection,
462
- readModels
463
- } = options;
464
- const eventTypes = events.map((e) => e.type);
465
- const projections2 = allProjections.filter(
466
- (p) => p.canHandle.some((type) => eventTypes.includes(type))
467
- );
468
- for (const projection of projections2) {
469
- await projection.handle(events, {
470
- document: _nullishCoalesce(readModels[projection.name], () => ( null)),
471
- streamId,
472
- collection,
473
- updates: update
474
- });
475
- }
476
- };
477
- var mongoDBInlineProjection = (options) => {
478
- const projectionName = _nullishCoalesce(options.name, () => ( MongoDBDefaultInlineProjectionName));
479
- const schemaVersion = _nullishCoalesce(options.schemaVersion, () => ( 1));
480
- return {
481
- name: projectionName,
482
- canHandle: options.canHandle,
483
- handle: async (events, { document, updates, streamId }) => {
484
- if (events.length === 0) return;
485
- let state = "initialState" in options ? _nullishCoalesce(document, () => ( options.initialState())) : document;
486
- for (const event of events) {
487
- state = await options.evolve(
488
- state,
489
- event
490
- );
491
- }
492
- const metadata = {
493
- streamId,
494
- name: projectionName,
495
- schemaVersion,
496
- streamPosition: events[events.length - 1].metadata.streamPosition
497
- };
498
- updates.$set[`projections.${projectionName}`] = state !== null ? {
499
- ...state,
500
- _metadata: metadata
501
- } : null;
502
- }
503
- };
504
- };
505
-
506
- // src/eventStore/projections/mongoDBInlineProjectionSpec.ts
507
-
508
- var MongoDBInlineProjectionSpec = {
509
- for: (options) => {
510
- {
511
- const { projection, ...connectionOptions } = options;
512
- return (givenStream) => {
513
- const { streamName, events: givenEvents } = givenStream;
514
- return {
515
- when: (events) => {
516
- const allEvents = [...givenEvents, ...events];
517
- const run = (eventStore) => eventStore.appendToStream(streamName, allEvents);
518
- return {
519
- then: async (assert, message) => {
520
- const client = "client" in connectionOptions && connectionOptions.client ? connectionOptions.client : new (0, _mongodb.MongoClient)(
521
- connectionOptions.connectionString,
522
- connectionOptions.clientOptions
523
- );
524
- const eventStore = getMongoDBEventStore({
525
- projections: projections.inline([projection]),
526
- client
527
- });
528
- try {
529
- await run(eventStore);
530
- const succeeded = await assert({ eventStore, streamName });
531
- if (succeeded !== void 0 && succeeded === false)
532
- assertFails(
533
- _nullishCoalesce(message, () => ( "Projection specification didn't match the criteria"))
534
- );
535
- } finally {
536
- await client.close();
537
- }
538
- },
539
- thenThrows: async (...args) => {
540
- const client = "client" in connectionOptions && connectionOptions.client ? connectionOptions.client : new (0, _mongodb.MongoClient)(
541
- connectionOptions.connectionString,
542
- connectionOptions.clientOptions
543
- );
544
- const eventStore = getMongoDBEventStore({
545
- projections: projections.inline([projection]),
546
- client
547
- });
548
- try {
549
- await run(eventStore);
550
- throw new AssertionError("Handler did not fail as expected");
551
- } catch (error) {
552
- if (error instanceof AssertionError) throw error;
553
- if (args.length === 0) return;
554
- if (!isErrorConstructor(args[0])) {
555
- assertTrue(
556
- args[0](error),
557
- `Error didn't match the error condition: ${_optionalChain([error, 'optionalAccess', _20 => _20.toString, 'call', _21 => _21()])}`
558
- );
559
- return;
560
- }
561
- assertTrue(
562
- error instanceof args[0],
563
- `Caught error is not an instance of the expected type: ${_optionalChain([error, 'optionalAccess', _22 => _22.toString, 'call', _23 => _23()])}`
564
- );
565
- if (args[1]) {
566
- assertTrue(
567
- args[1](error),
568
- `Error didn't match the error condition: ${_optionalChain([error, 'optionalAccess', _24 => _24.toString, 'call', _25 => _25()])}`
569
- );
570
- }
571
- } finally {
572
- await client.close();
573
- }
574
- }
575
- };
576
- }
577
- };
578
- };
579
- }
580
- }
581
- };
582
- var eventInStream = (streamName, event) => ({
583
- streamName,
584
- events: [event]
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ let _event_driven_io_emmett = require("@event-driven-io/emmett");
3
+ let mongodb = require("mongodb");
4
+ let uuid = require("uuid");
5
+
6
+ //#region src/eventStore/projections/mongoDBInlineProjection.ts
7
+ const MongoDBDefaultInlineProjectionName = "_default";
8
+ const handleInlineProjections = async (options) => {
9
+ const { events, projections: allProjections, updates: update, streamId, collection, readModels } = options;
10
+ const eventTypes = events.map((e) => e.type);
11
+ const projections = allProjections.filter((p) => p.canHandle.some((type) => eventTypes.includes(type)));
12
+ for (const projection of projections) await projection.handle(events, {
13
+ document: readModels[projection.name] ?? null,
14
+ streamId,
15
+ collection,
16
+ updates: update,
17
+ observabilityScope: _event_driven_io_emmett.noopScope
18
+ });
19
+ };
20
+ const mongoDBInlineProjection = (options) => {
21
+ const projectionName = options.name ?? "_default";
22
+ const schemaVersion = options.schemaVersion ?? 1;
23
+ return {
24
+ name: projectionName,
25
+ canHandle: options.canHandle,
26
+ handle: async (events, { document, updates, streamId }) => {
27
+ if (events.length === 0) return;
28
+ let state = "initialState" in options ? document ?? options.initialState() : document;
29
+ for (const event of events) state = await options.evolve(state, event);
30
+ const metadata = {
31
+ streamId,
32
+ name: projectionName,
33
+ schemaVersion,
34
+ streamPosition: events[events.length - 1].metadata.streamPosition
35
+ };
36
+ updates.$set[`projections.${projectionName}`] = state !== null ? {
37
+ ...state,
38
+ _metadata: metadata
39
+ } : null;
40
+ }
41
+ };
42
+ };
43
+
44
+ //#endregion
45
+ //#region src/eventStore/projections/mongoDBInlineProjectionSpec.ts
46
+ const MongoDBInlineProjectionSpec = { for: (options) => {
47
+ {
48
+ const { projection, ...connectionOptions } = options;
49
+ return (givenStream) => {
50
+ const { streamName, events: givenEvents } = givenStream;
51
+ return { when: (events) => {
52
+ const allEvents = [...givenEvents, ...events];
53
+ const run = (eventStore) => eventStore.appendToStream(streamName, allEvents);
54
+ return {
55
+ then: async (assert, message) => {
56
+ const client = "client" in connectionOptions && connectionOptions.client ? connectionOptions.client : new mongodb.MongoClient(connectionOptions.connectionString, connectionOptions.clientOptions);
57
+ const eventStore = getMongoDBEventStore({
58
+ projections: _event_driven_io_emmett.projections.inline([projection]),
59
+ client
60
+ });
61
+ try {
62
+ await run(eventStore);
63
+ const succeeded = await assert({
64
+ eventStore,
65
+ streamName
66
+ });
67
+ if (succeeded !== void 0 && succeeded === false) (0, _event_driven_io_emmett.assertFails)(message ?? "Projection specification didn't match the criteria");
68
+ } finally {
69
+ await client.close();
70
+ }
71
+ },
72
+ thenThrows: async (...args) => {
73
+ const client = "client" in connectionOptions && connectionOptions.client ? connectionOptions.client : new mongodb.MongoClient(connectionOptions.connectionString, connectionOptions.clientOptions);
74
+ const eventStore = getMongoDBEventStore({
75
+ projections: _event_driven_io_emmett.projections.inline([projection]),
76
+ client
77
+ });
78
+ try {
79
+ await run(eventStore);
80
+ throw new _event_driven_io_emmett.AssertionError("Handler did not fail as expected");
81
+ } catch (error) {
82
+ if (error instanceof _event_driven_io_emmett.AssertionError) throw error;
83
+ if (args.length === 0) return;
84
+ if (!(0, _event_driven_io_emmett.isErrorConstructor)(args[0])) {
85
+ (0, _event_driven_io_emmett.assertTrue)(args[0](error), `Error didn't match the error condition: ${error?.toString()}`);
86
+ return;
87
+ }
88
+ (0, _event_driven_io_emmett.assertTrue)(error instanceof args[0], `Caught error is not an instance of the expected type: ${error?.toString()}`);
89
+ if (args[1]) (0, _event_driven_io_emmett.assertTrue)(args[1](error), `Error didn't match the error condition: ${error?.toString()}`);
90
+ } finally {
91
+ await client.close();
92
+ }
93
+ }
94
+ };
95
+ } };
96
+ };
97
+ }
98
+ } };
99
+ const eventInStream = (streamName, event) => ({
100
+ streamName,
101
+ events: [event]
585
102
  });
586
- var eventsInStream = (streamName, events) => ({
587
- streamName,
588
- events
103
+ const eventsInStream = (streamName, events) => ({
104
+ streamName,
105
+ events
589
106
  });
590
- var expectReadModelToMatch = async (options) => {
591
- const { streamName, projectionName, eventStore, match } = options;
592
- const readModel = await eventStore.projections.inline.findOne({
593
- streamName,
594
- projectionName
595
- });
596
- return match(readModel);
597
- };
598
- var expectInlineReadModelWithName = (projectionName) => ({
599
- toHave: (expected) => ({ eventStore, streamName }) => expectReadModelToMatch({
600
- eventStore,
601
- streamName,
602
- projectionName,
603
- match: (readModel) => isSubset(readModel, expected)
604
- }),
605
- toDeepEquals: (expected) => ({ eventStore, streamName }) => expectReadModelToMatch({
606
- eventStore,
607
- streamName,
608
- projectionName,
609
- match: (readModel) => deepEquals(readModel, expected)
610
- }),
611
- toMatch: (match) => ({ eventStore, streamName }) => expectReadModelToMatch({
612
- eventStore,
613
- streamName,
614
- projectionName,
615
- match
616
- }),
617
- notToExist: () => ({ eventStore, streamName }) => expectReadModelToMatch({
618
- eventStore,
619
- streamName,
620
- projectionName,
621
- match: (readModel) => readModel === null
622
- }),
623
- toExist: () => ({ eventStore, streamName }) => expectReadModelToMatch({
624
- eventStore,
625
- streamName,
626
- projectionName,
627
- match: (readModel) => readModel !== null
628
- })
107
+ const expectReadModelToMatch = async (options) => {
108
+ const { streamName, projectionName, eventStore, match } = options;
109
+ return match(await eventStore.projections.inline.findOne({
110
+ streamName,
111
+ projectionName
112
+ }));
113
+ };
114
+ const expectInlineReadModelWithName = (projectionName) => ({
115
+ toHave: (expected) => ({ eventStore, streamName }) => expectReadModelToMatch({
116
+ eventStore,
117
+ streamName,
118
+ projectionName,
119
+ match: (readModel) => (0, _event_driven_io_emmett.isSubset)(readModel, expected)
120
+ }),
121
+ toDeepEquals: (expected) => ({ eventStore, streamName }) => expectReadModelToMatch({
122
+ eventStore,
123
+ streamName,
124
+ projectionName,
125
+ match: (readModel) => (0, _event_driven_io_emmett.deepEquals)(readModel, expected)
126
+ }),
127
+ toMatch: (match) => ({ eventStore, streamName }) => expectReadModelToMatch({
128
+ eventStore,
129
+ streamName,
130
+ projectionName,
131
+ match
132
+ }),
133
+ notToExist: () => ({ eventStore, streamName }) => expectReadModelToMatch({
134
+ eventStore,
135
+ streamName,
136
+ projectionName,
137
+ match: (readModel) => readModel === null
138
+ }),
139
+ toExist: () => ({ eventStore, streamName }) => expectReadModelToMatch({
140
+ eventStore,
141
+ streamName,
142
+ projectionName,
143
+ match: (readModel) => readModel !== null
144
+ })
629
145
  });
630
- var expectInlineReadModel = {
631
- withName: (name) => expectInlineReadModelWithName(name),
632
- ...expectInlineReadModelWithName(MongoDBDefaultInlineProjectionName)
633
- };
634
-
635
- // src/eventStore/storage/mongoDBEventStoreStorage.ts
636
- var DefaultMongoDBEventStoreStorageOptions = "COLLECTION_PER_STREAM_TYPE";
637
- var DefaultMongoDBEventStoreCollectionName = "emt:streams";
638
- var resolveCollectionAndDatabase = (streamType, options) => {
639
- if (options === "SINGLE_COLLECTION" || typeof options === "object" && options.type === "SINGLE_COLLECTION") {
640
- return {
641
- collectionName: typeof options === "object" ? _nullishCoalesce(options.collectionName, () => ( DefaultMongoDBEventStoreCollectionName)) : DefaultMongoDBEventStoreCollectionName,
642
- databaseName: typeof options === "object" ? options.databaseName : void 0
643
- };
644
- } else if (options === "COLLECTION_PER_STREAM_TYPE" || typeof options === "object" && options.type === "COLLECTION_PER_STREAM_TYPE") {
645
- return {
646
- collectionName: toStreamCollectionName(streamType),
647
- databaseName: typeof options === "object" ? options.databaseName : void 0
648
- };
649
- } else {
650
- const result = options.collectionFor(streamType);
651
- return {
652
- collectionName: typeof result === "object" ? result.collectionName : result,
653
- databaseName: typeof result === "object" ? _nullishCoalesce(result.databaseName, () => ( options.databaseName)) : options.databaseName
654
- };
655
- }
656
- };
657
- var getDB = async (options) => {
658
- const { dbsCache, databaseName, getConnectedClient } = options;
659
- const safeDbName = _nullishCoalesce(databaseName, () => ( "___default"));
660
- let db = dbsCache.get(safeDbName);
661
- if (!db) {
662
- const connectedClient = await getConnectedClient();
663
- db = connectedClient.db(databaseName);
664
- dbsCache.set(safeDbName, db);
665
- }
666
- return db;
667
- };
668
- var collectionFor = async (options) => {
669
- const { collectionName, db, streamCollections } = options;
670
- let collection = streamCollections.get(collectionName);
671
- if (!collection) {
672
- collection = db.collection(collectionName);
673
- await collection.createIndex({ streamName: 1 }, { unique: true });
674
- streamCollections.set(
675
- collectionName,
676
- collection
677
- );
678
- }
679
- return collection;
680
- };
681
- var mongoDBEventStoreStorage = (options) => {
682
- const dbsCache = /* @__PURE__ */ new Map();
683
- const streamCollections = /* @__PURE__ */ new Map();
684
- const storageOptions = _nullishCoalesce(options.storage, () => ( DefaultMongoDBEventStoreStorageOptions));
685
- const { getConnectedClient } = options;
686
- return {
687
- collectionFor: async (streamType) => {
688
- const { collectionName, databaseName } = resolveCollectionAndDatabase(
689
- streamType,
690
- storageOptions
691
- );
692
- let collection = streamCollections.get(collectionName);
693
- if (!collection) {
694
- const db = await getDB({ databaseName, dbsCache, getConnectedClient });
695
- collection = await collectionFor({
696
- collectionName,
697
- streamCollections,
698
- db
699
- });
700
- }
701
- return collection;
702
- }
703
- };
146
+ const expectInlineReadModel = {
147
+ withName: (name) => expectInlineReadModelWithName(name),
148
+ ...expectInlineReadModelWithName(MongoDBDefaultInlineProjectionName)
149
+ };
150
+
151
+ //#endregion
152
+ //#region src/eventStore/storage/mongoDBEventStoreStorage.ts
153
+ const DefaultMongoDBEventStoreStorageOptions = "COLLECTION_PER_STREAM_TYPE";
154
+ const DefaultMongoDBEventStoreCollectionName = "emt:streams";
155
+ const resolveCollectionAndDatabase = (streamType, options) => {
156
+ if (options === "SINGLE_COLLECTION" || typeof options === "object" && options.type === "SINGLE_COLLECTION") return {
157
+ collectionName: typeof options === "object" ? options.collectionName ?? "emt:streams" : DefaultMongoDBEventStoreCollectionName,
158
+ databaseName: typeof options === "object" ? options.databaseName : void 0
159
+ };
160
+ else if (options === "COLLECTION_PER_STREAM_TYPE" || typeof options === "object" && options.type === "COLLECTION_PER_STREAM_TYPE") return {
161
+ collectionName: toStreamCollectionName(streamType),
162
+ databaseName: typeof options === "object" ? options.databaseName : void 0
163
+ };
164
+ else {
165
+ const result = options.collectionFor(streamType);
166
+ return {
167
+ collectionName: typeof result === "object" ? result.collectionName : result,
168
+ databaseName: typeof result === "object" ? result.databaseName ?? options.databaseName : options.databaseName
169
+ };
170
+ }
171
+ };
172
+ const getDB = async (options) => {
173
+ const { dbsCache, databaseName, getConnectedClient } = options;
174
+ const safeDbName = databaseName ?? "___default";
175
+ let db = dbsCache.get(safeDbName);
176
+ if (!db) {
177
+ db = (await getConnectedClient()).db(databaseName);
178
+ dbsCache.set(safeDbName, db);
179
+ }
180
+ return db;
181
+ };
182
+ const collectionFor = async (options) => {
183
+ const { collectionName, db, streamCollections } = options;
184
+ let collection = streamCollections.get(collectionName);
185
+ if (!collection) {
186
+ collection = db.collection(collectionName);
187
+ await collection.createIndex({ streamName: 1 }, { unique: true });
188
+ streamCollections.set(collectionName, collection);
189
+ }
190
+ return collection;
191
+ };
192
+ const mongoDBEventStoreStorage = (options) => {
193
+ const dbsCache = /* @__PURE__ */ new Map();
194
+ const streamCollections = /* @__PURE__ */ new Map();
195
+ const storageOptions = options.storage ?? "COLLECTION_PER_STREAM_TYPE";
196
+ const { getConnectedClient } = options;
197
+ return { collectionFor: async (streamType) => {
198
+ const { collectionName, databaseName } = resolveCollectionAndDatabase(streamType, storageOptions);
199
+ let collection = streamCollections.get(collectionName);
200
+ if (!collection) collection = await collectionFor({
201
+ collectionName,
202
+ streamCollections,
203
+ db: await getDB({
204
+ databaseName,
205
+ dbsCache,
206
+ getConnectedClient
207
+ })
208
+ });
209
+ return collection;
210
+ } };
211
+ };
212
+
213
+ //#endregion
214
+ //#region src/eventStore/mongoDBEventStore.ts
215
+ const MongoDBEventStoreDefaultStreamVersion = 0n;
216
+ var MongoDBEventStoreImplementation = class {
217
+ client;
218
+ inlineProjections;
219
+ shouldManageClientLifetime;
220
+ isClosed = false;
221
+ storage;
222
+ options;
223
+ projections;
224
+ constructor(options) {
225
+ this.options = options;
226
+ this.client = "client" in options && options.client ? options.client : new mongodb.MongoClient(options.connectionString, options.clientOptions);
227
+ this.shouldManageClientLifetime = !("client" in options);
228
+ this.storage = mongoDBEventStoreStorage({
229
+ storage: options.storage,
230
+ getConnectedClient: () => this.getConnectedClient()
231
+ });
232
+ this.inlineProjections = (0, _event_driven_io_emmett.filterProjections)("inline", options.projections ?? []);
233
+ this.projections = { inline: {
234
+ findOne: this.findOneInlineProjection.bind(this),
235
+ find: this.findInlineProjection.bind(this),
236
+ count: this.countInlineProjection.bind(this)
237
+ } };
238
+ }
239
+ async readStream(streamName, options) {
240
+ const { streamType } = fromStreamName(streamName);
241
+ const expectedStreamVersion = options?.expectedStreamVersion;
242
+ const collection = await this.storage.collectionFor(streamType);
243
+ const filter = { streamName: { $eq: streamName } };
244
+ const eventsSliceArr = [];
245
+ if (options && "from" in options) eventsSliceArr.push(Number(options.from));
246
+ else eventsSliceArr.push(0);
247
+ if (options && "to" in options) eventsSliceArr.push(Number(options.to));
248
+ const eventsSlice = eventsSliceArr.length > 1 ? { $slice: eventsSliceArr } : 1;
249
+ const stream = await collection.findOne(filter, {
250
+ useBigInt64: true,
251
+ projection: {
252
+ metadata: 1,
253
+ messages: eventsSlice
254
+ }
255
+ });
256
+ if (!stream) return {
257
+ events: [],
258
+ currentStreamVersion: MongoDBEventStoreDefaultStreamVersion,
259
+ streamExists: false
260
+ };
261
+ (0, _event_driven_io_emmett.assertExpectedVersionMatchesCurrent)(stream.metadata.streamPosition, expectedStreamVersion, MongoDBEventStoreDefaultStreamVersion);
262
+ return {
263
+ events: (0, _event_driven_io_emmett.upcastRecordedMessages)(stream.messages, options?.schema?.versioning),
264
+ currentStreamVersion: stream.metadata.streamPosition,
265
+ streamExists: true
266
+ };
267
+ }
268
+ async aggregateStream(streamName, options) {
269
+ const stream = await this.readStream(streamName, options?.read);
270
+ const { evolve, initialState } = options;
271
+ return {
272
+ state: stream.events.reduce(evolve, initialState()),
273
+ currentStreamVersion: stream.currentStreamVersion,
274
+ streamExists: stream.streamExists
275
+ };
276
+ }
277
+ async appendToStream(streamName, events, options) {
278
+ const { streamId, streamType } = fromStreamName(streamName);
279
+ const expectedStreamVersion = options?.expectedStreamVersion;
280
+ const collection = await this.storage.collectionFor(streamType);
281
+ const stream = await collection.findOne({ streamName: { $eq: streamName } }, {
282
+ useBigInt64: true,
283
+ projection: {
284
+ "metadata.streamPosition": 1,
285
+ projections: 1
286
+ }
287
+ });
288
+ const currentStreamVersion = stream?.metadata.streamPosition ?? 0n;
289
+ (0, _event_driven_io_emmett.assertExpectedVersionMatchesCurrent)(currentStreamVersion, expectedStreamVersion, MongoDBEventStoreDefaultStreamVersion);
290
+ let streamOffset = currentStreamVersion;
291
+ const eventsToAppend = events.map((event) => {
292
+ const metadata = {
293
+ messageId: (0, uuid.v4)(),
294
+ streamName,
295
+ streamPosition: ++streamOffset
296
+ };
297
+ return (0, _event_driven_io_emmett.downcastRecordedMessage)({
298
+ type: event.type,
299
+ data: event.data,
300
+ metadata: {
301
+ ...metadata,
302
+ ..."metadata" in event ? event.metadata ?? {} : {}
303
+ }
304
+ }, options?.schema?.versioning);
305
+ });
306
+ const now = /* @__PURE__ */ new Date();
307
+ const updates = {
308
+ $push: { messages: { $each: eventsToAppend } },
309
+ $set: {
310
+ "metadata.updatedAt": now,
311
+ "metadata.streamPosition": currentStreamVersion + BigInt(events.length)
312
+ },
313
+ $setOnInsert: {
314
+ streamName,
315
+ "metadata.streamId": streamId,
316
+ "metadata.streamType": streamType,
317
+ "metadata.createdAt": now
318
+ }
319
+ };
320
+ if (this.inlineProjections) await handleInlineProjections({
321
+ readModels: stream?.projections ?? {},
322
+ streamId,
323
+ events: eventsToAppend,
324
+ projections: this.inlineProjections,
325
+ collection,
326
+ updates,
327
+ client: {}
328
+ });
329
+ if (!await collection.updateOne({
330
+ streamName: { $eq: streamName },
331
+ "metadata.streamPosition": currentStreamVersion
332
+ }, updates, {
333
+ useBigInt64: true,
334
+ upsert: true
335
+ })) throw new _event_driven_io_emmett.ExpectedVersionConflictError(currentStreamVersion, options?.expectedStreamVersion ?? 0n);
336
+ await (0, _event_driven_io_emmett.tryPublishMessagesAfterCommit)(eventsToAppend, this.options.hooks);
337
+ return {
338
+ nextExpectedStreamVersion: currentStreamVersion + BigInt(eventsToAppend.length),
339
+ createdNewStream: currentStreamVersion === MongoDBEventStoreDefaultStreamVersion
340
+ };
341
+ }
342
+ async streamExists(streamName) {
343
+ const { streamType } = fromStreamName(streamName);
344
+ const collection = await this.storage.collectionFor(streamType);
345
+ const filter = { streamName: { $eq: streamName } };
346
+ const count = await collection.countDocuments(filter, {
347
+ useBigInt64: true,
348
+ limit: 1
349
+ });
350
+ return Boolean(count > 0);
351
+ }
352
+ collectionFor = async (streamType) => {
353
+ return this.storage.collectionFor(streamType);
354
+ };
355
+ /**
356
+ * Gracefully cleans up managed resources by the MongoDBEventStore.
357
+ * It closes MongoDB client created for the provided connection string
358
+ * through event store options.
359
+ *
360
+ * @memberof Closeable
361
+ */
362
+ close = () => {
363
+ if (this.isClosed) return Promise.resolve();
364
+ this.isClosed = true;
365
+ if (!this.shouldManageClientLifetime) return Promise.resolve();
366
+ return this.client.close();
367
+ };
368
+ async findOneInlineProjection(streamFilter, projectionQuery) {
369
+ const { projectionName, streamName, streamType } = parseSingleProjectionQueryStreamFilter(streamFilter);
370
+ const collection = await this.storage.collectionFor(streamType);
371
+ const query = prependMongoFilterWithProjectionPrefix(projectionQuery, `projections.${projectionName}`);
372
+ const filters = [{ [`projections.${projectionName}`]: { $exists: true } }];
373
+ if (query) filters.push(query);
374
+ if (streamName) filters.push({ streamName: { $eq: streamName } });
375
+ return (await collection.findOne({ $and: filters }, {
376
+ useBigInt64: true,
377
+ projection: { [`projections.${projectionName}`]: 1 }
378
+ }))?.projections?.[projectionName] ?? null;
379
+ }
380
+ async findInlineProjection(streamFilter, projectionQuery, queryOptions) {
381
+ const parsedStreamFilter = parseMultiProjectionQueryStreamFilter(streamFilter);
382
+ if (!parsedStreamFilter) return [];
383
+ const { projectionName, streamNames, streamType } = parsedStreamFilter;
384
+ const collection = await this.storage.collectionFor(streamType);
385
+ const prefix = `projections.${projectionName}`;
386
+ const projectionFilter = prependMongoFilterWithProjectionPrefix(projectionQuery, prefix);
387
+ const filters = [{ [`projections.${projectionName}`]: { $ne: null } }];
388
+ if (projectionFilter) filters.push(projectionFilter);
389
+ if (streamNames) filters.push({ streamName: { $in: streamNames } });
390
+ let query = collection.find({ $and: filters }, {
391
+ useBigInt64: true,
392
+ projection: { [`projections.${projectionName}`]: 1 }
393
+ });
394
+ if (queryOptions?.skip) query = query.skip(queryOptions.skip);
395
+ if (queryOptions?.limit) query = query.limit(queryOptions.limit);
396
+ if (queryOptions?.sort) {
397
+ const sort = prependMongoFilterWithProjectionPrefix(queryOptions.sort, prefix);
398
+ query = query.sort(sort);
399
+ }
400
+ return (await query.toArray()).map((s) => s.projections[projectionName]).filter((p) => !!p);
401
+ }
402
+ async countInlineProjection(streamFilter, projectionQuery) {
403
+ const parsedStreamFilter = parseMultiProjectionQueryStreamFilter(streamFilter);
404
+ if (!parsedStreamFilter) return 0;
405
+ const { projectionName, streamNames, streamType } = parsedStreamFilter;
406
+ const collection = await this.storage.collectionFor(streamType);
407
+ const projectionFilter = prependMongoFilterWithProjectionPrefix(projectionQuery, `projections.${projectionName}`);
408
+ const filters = [{ [`projections.${projectionName}`]: { $ne: null } }];
409
+ if (projectionFilter) filters.push(projectionFilter);
410
+ if (streamNames) filters.push({ streamName: { $in: streamNames } });
411
+ return await collection.countDocuments({ $and: filters });
412
+ }
413
+ getConnectedClient = async () => {
414
+ if (!this.isClosed) await this.client.connect();
415
+ return this.client;
416
+ };
704
417
  };
705
-
706
- // src/eventStore/mongoDBEventStore.ts
707
- var MongoDBEventStoreDefaultStreamVersion = 0n;
708
- var MongoDBEventStoreImplementation = (_class2 = class {
709
-
710
-
711
-
712
- __init() {this.isClosed = false}
713
-
714
-
715
-
716
- constructor(options) {;_class2.prototype.__init.call(this);_class2.prototype.__init2.call(this);_class2.prototype.__init3.call(this);_class2.prototype.__init4.call(this);
717
- this.options = options;
718
- this.client = "client" in options && options.client ? options.client : new (0, _mongodb.MongoClient)(options.connectionString, options.clientOptions);
719
- this.shouldManageClientLifetime = !("client" in options);
720
- this.storage = mongoDBEventStoreStorage({
721
- storage: options.storage,
722
- getConnectedClient: () => this.getConnectedClient()
723
- });
724
- this.inlineProjections = filterProjections(
725
- "inline",
726
- _nullishCoalesce(options.projections, () => ( []))
727
- );
728
- this.projections = {
729
- inline: {
730
- findOne: this.findOneInlineProjection.bind(this),
731
- find: this.findInlineProjection.bind(this),
732
- count: this.countInlineProjection.bind(this)
733
- }
734
- };
735
- }
736
- async readStream(streamName, options) {
737
- const { streamType } = fromStreamName(streamName);
738
- const expectedStreamVersion = _optionalChain([options, 'optionalAccess', _26 => _26.expectedStreamVersion]);
739
- const collection = await this.storage.collectionFor(streamType);
740
- const filter = {
741
- streamName: { $eq: streamName }
742
- };
743
- const eventsSliceArr = [];
744
- if (options && "from" in options) {
745
- eventsSliceArr.push(Number(options.from));
746
- } else {
747
- eventsSliceArr.push(0);
748
- }
749
- if (options && "to" in options) {
750
- eventsSliceArr.push(Number(options.to));
751
- }
752
- const eventsSlice = eventsSliceArr.length > 1 ? { $slice: eventsSliceArr } : 1;
753
- const stream = await collection.findOne(filter, {
754
- useBigInt64: true,
755
- projection: {
756
- metadata: 1,
757
- messages: eventsSlice
758
- }
759
- });
760
- if (!stream) {
761
- return {
762
- events: [],
763
- currentStreamVersion: MongoDBEventStoreDefaultStreamVersion,
764
- streamExists: false
765
- };
766
- }
767
- assertExpectedVersionMatchesCurrent(
768
- stream.metadata.streamPosition,
769
- expectedStreamVersion,
770
- MongoDBEventStoreDefaultStreamVersion
771
- );
772
- const events = upcastRecordedMessages(
773
- stream.messages,
774
- _optionalChain([options, 'optionalAccess', _27 => _27.schema, 'optionalAccess', _28 => _28.versioning])
775
- );
776
- return {
777
- events,
778
- currentStreamVersion: stream.metadata.streamPosition,
779
- streamExists: true
780
- };
781
- }
782
- async aggregateStream(streamName, options) {
783
- const stream = await this.readStream(
784
- streamName,
785
- _optionalChain([options, 'optionalAccess', _29 => _29.read])
786
- );
787
- const { evolve, initialState } = options;
788
- const state = stream.events.reduce(evolve, initialState());
789
- return {
790
- state,
791
- currentStreamVersion: stream.currentStreamVersion,
792
- streamExists: stream.streamExists
793
- };
794
- }
795
- async appendToStream(streamName, events, options) {
796
- const { streamId, streamType } = fromStreamName(streamName);
797
- const expectedStreamVersion = _optionalChain([options, 'optionalAccess', _30 => _30.expectedStreamVersion]);
798
- const collection = await this.storage.collectionFor(streamType);
799
- const stream = await collection.findOne(
800
- { streamName: { $eq: streamName } },
801
- {
802
- useBigInt64: true,
803
- projection: {
804
- "metadata.streamPosition": 1,
805
- projections: 1
806
- }
807
- }
808
- );
809
- const currentStreamVersion = _nullishCoalesce(_optionalChain([stream, 'optionalAccess', _31 => _31.metadata, 'access', _32 => _32.streamPosition]), () => ( MongoDBEventStoreDefaultStreamVersion));
810
- assertExpectedVersionMatchesCurrent(
811
- currentStreamVersion,
812
- expectedStreamVersion,
813
- MongoDBEventStoreDefaultStreamVersion
814
- );
815
- let streamOffset = currentStreamVersion;
816
- const eventsToAppend = events.map((event) => {
817
- const metadata = {
818
- messageId: _uuid.v4.call(void 0, ),
819
- streamName,
820
- streamPosition: ++streamOffset
821
- };
822
- return downcastRecordedMessage(
823
- {
824
- type: event.type,
825
- data: event.data,
826
- metadata: {
827
- ...metadata,
828
- ..."metadata" in event ? _nullishCoalesce(event.metadata, () => ( {})) : {}
829
- }
830
- },
831
- _optionalChain([options, 'optionalAccess', _33 => _33.schema, 'optionalAccess', _34 => _34.versioning])
832
- );
833
- });
834
- const now = /* @__PURE__ */ new Date();
835
- const updates = {
836
- $push: { messages: { $each: eventsToAppend } },
837
- $set: {
838
- "metadata.updatedAt": now,
839
- "metadata.streamPosition": currentStreamVersion + BigInt(events.length)
840
- },
841
- $setOnInsert: {
842
- streamName,
843
- "metadata.streamId": streamId,
844
- "metadata.streamType": streamType,
845
- "metadata.createdAt": now
846
- }
847
- };
848
- if (this.inlineProjections) {
849
- await handleInlineProjections({
850
- readModels: _nullishCoalesce(_optionalChain([stream, 'optionalAccess', _35 => _35.projections]), () => ( {})),
851
- streamId,
852
- events: eventsToAppend,
853
- projections: this.inlineProjections,
854
- collection,
855
- updates,
856
- client: {}
857
- });
858
- }
859
- const updatedStream = await collection.updateOne(
860
- {
861
- streamName: { $eq: streamName },
862
- "metadata.streamPosition": currentStreamVersion
863
- },
864
- updates,
865
- { useBigInt64: true, upsert: true }
866
- );
867
- if (!updatedStream) {
868
- throw new ExpectedVersionConflictError(
869
- currentStreamVersion,
870
- _nullishCoalesce(_optionalChain([options, 'optionalAccess', _36 => _36.expectedStreamVersion]), () => ( 0n))
871
- );
872
- }
873
- await tryPublishMessagesAfterCommit(
874
- eventsToAppend,
875
- this.options.hooks
876
- // {
877
- // TODO: same context as InlineProjectionHandlerContext for mongodb?
878
- // },
879
- );
880
- return {
881
- nextExpectedStreamVersion: currentStreamVersion + BigInt(eventsToAppend.length),
882
- createdNewStream: currentStreamVersion === MongoDBEventStoreDefaultStreamVersion
883
- };
884
- }
885
- async streamExists(streamName) {
886
- const { streamType } = fromStreamName(streamName);
887
- const collection = await this.storage.collectionFor(streamType);
888
- const filter = {
889
- streamName: { $eq: streamName }
890
- };
891
- const count = await collection.countDocuments(filter, {
892
- useBigInt64: true,
893
- limit: 1
894
- });
895
- return Boolean(count > 0);
896
- }
897
- __init2() {this.collectionFor = async (streamType) => {
898
- return this.storage.collectionFor(streamType);
899
- }}
900
- /**
901
- * Gracefully cleans up managed resources by the MongoDBEventStore.
902
- * It closes MongoDB client created for the provided connection string
903
- * through event store options.
904
- *
905
- * @memberof Closeable
906
- */
907
- __init3() {this.close = () => {
908
- if (this.isClosed) return Promise.resolve();
909
- this.isClosed = true;
910
- if (!this.shouldManageClientLifetime) return Promise.resolve();
911
- return this.client.close();
912
- }}
913
- async findOneInlineProjection(streamFilter, projectionQuery) {
914
- const { projectionName, streamName, streamType } = parseSingleProjectionQueryStreamFilter(streamFilter);
915
- const collection = await this.storage.collectionFor(streamType);
916
- const query = prependMongoFilterWithProjectionPrefix(projectionQuery, `projections.${projectionName}`);
917
- const filters = [
918
- { [`projections.${projectionName}`]: { $exists: true } }
919
- ];
920
- if (query) {
921
- filters.push(query);
922
- }
923
- if (streamName) {
924
- filters.push({ streamName: { $eq: streamName } });
925
- }
926
- const result = await collection.findOne(
927
- { $and: filters },
928
- {
929
- useBigInt64: true,
930
- projection: { [`projections.${projectionName}`]: 1 }
931
- }
932
- );
933
- return _nullishCoalesce(_optionalChain([result, 'optionalAccess', _37 => _37.projections, 'optionalAccess', _38 => _38[projectionName]]), () => ( null));
934
- }
935
- async findInlineProjection(streamFilter, projectionQuery, queryOptions) {
936
- const parsedStreamFilter = parseMultiProjectionQueryStreamFilter(streamFilter);
937
- if (!parsedStreamFilter) return [];
938
- const { projectionName, streamNames, streamType } = parsedStreamFilter;
939
- const collection = await this.storage.collectionFor(streamType);
940
- const prefix = `projections.${projectionName}`;
941
- const projectionFilter = prependMongoFilterWithProjectionPrefix(projectionQuery, prefix);
942
- const filters = [
943
- { [`projections.${projectionName}`]: { $ne: null } }
944
- ];
945
- if (projectionFilter) {
946
- filters.push(projectionFilter);
947
- }
948
- if (streamNames) {
949
- filters.push({ streamName: { $in: streamNames } });
950
- }
951
- let query = collection.find(
952
- { $and: filters },
953
- {
954
- useBigInt64: true,
955
- projection: { [`projections.${projectionName}`]: 1 }
956
- }
957
- );
958
- if (_optionalChain([queryOptions, 'optionalAccess', _39 => _39.skip])) {
959
- query = query.skip(queryOptions.skip);
960
- }
961
- if (_optionalChain([queryOptions, 'optionalAccess', _40 => _40.limit])) {
962
- query = query.limit(queryOptions.limit);
963
- }
964
- if (_optionalChain([queryOptions, 'optionalAccess', _41 => _41.sort])) {
965
- const sort = prependMongoFilterWithProjectionPrefix(
966
- queryOptions.sort,
967
- prefix
968
- );
969
- query = query.sort(sort);
970
- }
971
- const streams = await query.toArray();
972
- return streams.map((s) => s.projections[projectionName]).filter((p) => !!p);
973
- }
974
- async countInlineProjection(streamFilter, projectionQuery) {
975
- const parsedStreamFilter = parseMultiProjectionQueryStreamFilter(streamFilter);
976
- if (!parsedStreamFilter) return 0;
977
- const { projectionName, streamNames, streamType } = parsedStreamFilter;
978
- const collection = await this.storage.collectionFor(streamType);
979
- const prefix = `projections.${projectionName}`;
980
- const projectionFilter = prependMongoFilterWithProjectionPrefix(projectionQuery, prefix);
981
- const filters = [
982
- { [`projections.${projectionName}`]: { $ne: null } }
983
- ];
984
- if (projectionFilter) {
985
- filters.push(projectionFilter);
986
- }
987
- if (streamNames) {
988
- filters.push({ streamName: { $in: streamNames } });
989
- }
990
- const total = await collection.countDocuments({ $and: filters });
991
- return total;
992
- }
993
- __init4() {this.getConnectedClient = async () => {
994
- if (!this.isClosed) await this.client.connect();
995
- return this.client;
996
- }}
997
- }, _class2);
998
418
  function parseSingleProjectionQueryStreamFilter(streamFilter) {
999
- const projectionName = _nullishCoalesce(streamFilter.projectionName, () => ( MongoDBDefaultInlineProjectionName));
1000
- if ("streamName" in streamFilter) {
1001
- const { streamType } = fromStreamName(streamFilter.streamName);
1002
- return {
1003
- projectionName,
1004
- streamName: streamFilter.streamName,
1005
- streamType
1006
- };
1007
- }
1008
- if (streamFilter.streamId) {
1009
- const streamName = toStreamName(
1010
- streamFilter.streamType,
1011
- streamFilter.streamId
1012
- );
1013
- return {
1014
- projectionName,
1015
- streamName,
1016
- streamType: streamFilter.streamType
1017
- };
1018
- }
1019
- return {
1020
- projectionName,
1021
- streamType: streamFilter.streamType
1022
- };
419
+ const projectionName = streamFilter.projectionName ?? "_default";
420
+ if ("streamName" in streamFilter) {
421
+ const { streamType } = fromStreamName(streamFilter.streamName);
422
+ return {
423
+ projectionName,
424
+ streamName: streamFilter.streamName,
425
+ streamType
426
+ };
427
+ }
428
+ if (streamFilter.streamId) return {
429
+ projectionName,
430
+ streamName: toStreamName(streamFilter.streamType, streamFilter.streamId),
431
+ streamType: streamFilter.streamType
432
+ };
433
+ return {
434
+ projectionName,
435
+ streamType: streamFilter.streamType
436
+ };
1023
437
  }
1024
438
  function parseMultiProjectionQueryStreamFilter(streamFilter) {
1025
- const projectionName = _nullishCoalesce(streamFilter.projectionName, () => ( MongoDBDefaultInlineProjectionName));
1026
- if ("streamNames" in streamFilter) {
1027
- if (streamFilter.streamNames.length == 0) return null;
1028
- const { streamType } = fromStreamName(streamFilter.streamNames[0]);
1029
- return {
1030
- projectionName,
1031
- streamNames: streamFilter.streamNames,
1032
- streamType
1033
- };
1034
- }
1035
- if (streamFilter.streamIds && streamFilter.streamIds.length > 0) {
1036
- const streamNames = streamFilter.streamIds.map(
1037
- (id) => toStreamName(streamFilter.streamType, id)
1038
- );
1039
- return {
1040
- projectionName,
1041
- streamNames,
1042
- streamType: streamFilter.streamType
1043
- };
1044
- }
1045
- return {
1046
- projectionName,
1047
- streamType: streamFilter.streamType
1048
- };
439
+ const projectionName = streamFilter.projectionName ?? "_default";
440
+ if ("streamNames" in streamFilter) {
441
+ if (streamFilter.streamNames.length == 0) return null;
442
+ const { streamType } = fromStreamName(streamFilter.streamNames[0]);
443
+ return {
444
+ projectionName,
445
+ streamNames: streamFilter.streamNames,
446
+ streamType
447
+ };
448
+ }
449
+ if (streamFilter.streamIds && streamFilter.streamIds.length > 0) return {
450
+ projectionName,
451
+ streamNames: streamFilter.streamIds.map((id) => toStreamName(streamFilter.streamType, id)),
452
+ streamType: streamFilter.streamType
453
+ };
454
+ return {
455
+ projectionName,
456
+ streamType: streamFilter.streamType
457
+ };
1049
458
  }
459
+ /**
460
+ * Prepends `prefix` to all object keys that don't start with a '$'
461
+ */
1050
462
  function prependMongoFilterWithProjectionPrefix(obj, prefix) {
1051
- if (typeof obj !== "object" || obj === null || obj === void 0) {
1052
- return obj;
1053
- }
1054
- if (Array.isArray(obj)) {
1055
- for (let i = 0; i < obj.length; i++) {
1056
- obj[i] = prependMongoFilterWithProjectionPrefix(obj[i], prefix);
1057
- }
1058
- return obj;
1059
- }
1060
- for (const key in obj) {
1061
- const k = addProjectionPrefixToMongoKey(key, prefix);
1062
- if (k !== key) {
1063
- obj[k] = obj[key];
1064
- delete obj[key];
1065
- }
1066
- obj[k] = prependMongoFilterWithProjectionPrefix(obj[k], prefix);
1067
- }
1068
- return obj;
463
+ if (typeof obj !== "object" || obj === null || obj === void 0) return obj;
464
+ if (Array.isArray(obj)) {
465
+ for (let i = 0; i < obj.length; i++) obj[i] = prependMongoFilterWithProjectionPrefix(obj[i], prefix);
466
+ return obj;
467
+ }
468
+ for (const key in obj) {
469
+ const k = addProjectionPrefixToMongoKey(key, prefix);
470
+ if (k !== key) {
471
+ obj[k] = obj[key];
472
+ delete obj[key];
473
+ }
474
+ obj[k] = prependMongoFilterWithProjectionPrefix(obj[k], prefix);
475
+ }
476
+ return obj;
1069
477
  }
1070
478
  function addProjectionPrefixToMongoKey(key, prefix) {
1071
- if (key[0] === "$") {
1072
- return key;
1073
- }
1074
- return `${prefix}${key.length > 0 ? "." : ""}${key}`;
479
+ if (key[0] === "$") return key;
480
+ return `${prefix}${key.length > 0 ? "." : ""}${key}`;
1075
481
  }
1076
482
  function getMongoDBEventStore(options) {
1077
- const impl = new MongoDBEventStoreImplementation(options);
1078
- if ("client" in options && "close" in impl) {
1079
- delete impl.close;
1080
- }
1081
- return impl;
483
+ const impl = new MongoDBEventStoreImplementation(options);
484
+ if ("client" in options && "close" in impl) delete impl.close;
485
+ return impl;
1082
486
  }
487
+ /**
488
+ * Accepts a `streamType` (the type/category of the event stream) and an `streamId`
489
+ * (the individual entity/object or aggregate ID) and combines them to a singular
490
+ * `streamName` which can be used in `EventStore`.
491
+ */
1083
492
  function toStreamName(streamType, streamId) {
1084
- return `${streamType}:${streamId}`;
493
+ return `${streamType}:${streamId}`;
1085
494
  }
495
+ /**
496
+ * Accepts a fully formatted `streamName` and returns the broken down
497
+ * `streamType` and `streamId`.
498
+ */
1086
499
  function fromStreamName(streamName) {
1087
- const parts = streamName.split(":");
1088
- return {
1089
- streamType: parts[0],
1090
- streamId: parts[1]
1091
- };
500
+ const parts = streamName.split(":");
501
+ return {
502
+ streamType: parts[0],
503
+ streamId: parts[1]
504
+ };
1092
505
  }
506
+ /**
507
+ * Accepts a `streamType` (the type/category of the event stream)
508
+ * and combines them to a `collectionName` which can be used in `EventStore`.
509
+ */
1093
510
  function toStreamCollectionName(streamType) {
1094
- return `emt:${streamType}`;
511
+ return `emt:${streamType}`;
1095
512
  }
513
+ /**
514
+ * Accepts a fully formatted `streamCollectionName` and returns the parsed `streamType`.
515
+ */
1096
516
  function fromStreamCollectionName(streamCollectionName) {
1097
- const parts = streamCollectionName.split(":");
1098
- return {
1099
- streamType: parts[1]
1100
- };
517
+ return { streamType: streamCollectionName.split(":")[1] };
1101
518
  }
1102
519
 
1103
-
1104
-
1105
-
1106
-
1107
-
1108
-
1109
-
1110
-
1111
-
1112
-
1113
-
1114
-
1115
-
1116
-
1117
-
1118
-
1119
-
1120
- exports.DefaultMongoDBEventStoreCollectionName = DefaultMongoDBEventStoreCollectionName; exports.DefaultMongoDBEventStoreStorageOptions = DefaultMongoDBEventStoreStorageOptions; exports.MongoDBDefaultInlineProjectionName = MongoDBDefaultInlineProjectionName; exports.MongoDBEventStoreDefaultStreamVersion = MongoDBEventStoreDefaultStreamVersion; exports.MongoDBInlineProjectionSpec = MongoDBInlineProjectionSpec; exports.eventInStream = eventInStream; exports.eventsInStream = eventsInStream; exports.expectInlineReadModel = expectInlineReadModel; exports.fromStreamCollectionName = fromStreamCollectionName; exports.fromStreamName = fromStreamName; exports.getMongoDBEventStore = getMongoDBEventStore; exports.handleInlineProjections = handleInlineProjections; exports.mongoDBEventStoreStorage = mongoDBEventStoreStorage; exports.mongoDBInlineProjection = mongoDBInlineProjection; exports.prependMongoFilterWithProjectionPrefix = prependMongoFilterWithProjectionPrefix; exports.toStreamCollectionName = toStreamCollectionName; exports.toStreamName = toStreamName;
520
+ //#endregion
521
+ exports.DefaultMongoDBEventStoreCollectionName = DefaultMongoDBEventStoreCollectionName;
522
+ exports.DefaultMongoDBEventStoreStorageOptions = DefaultMongoDBEventStoreStorageOptions;
523
+ exports.MongoDBDefaultInlineProjectionName = MongoDBDefaultInlineProjectionName;
524
+ exports.MongoDBEventStoreDefaultStreamVersion = MongoDBEventStoreDefaultStreamVersion;
525
+ exports.MongoDBInlineProjectionSpec = MongoDBInlineProjectionSpec;
526
+ exports.eventInStream = eventInStream;
527
+ exports.eventsInStream = eventsInStream;
528
+ exports.expectInlineReadModel = expectInlineReadModel;
529
+ exports.fromStreamCollectionName = fromStreamCollectionName;
530
+ exports.fromStreamName = fromStreamName;
531
+ exports.getMongoDBEventStore = getMongoDBEventStore;
532
+ exports.handleInlineProjections = handleInlineProjections;
533
+ exports.mongoDBEventStoreStorage = mongoDBEventStoreStorage;
534
+ exports.mongoDBInlineProjection = mongoDBInlineProjection;
535
+ exports.prependMongoFilterWithProjectionPrefix = prependMongoFilterWithProjectionPrefix;
536
+ exports.toStreamCollectionName = toStreamCollectionName;
537
+ exports.toStreamName = toStreamName;
1121
538
  //# sourceMappingURL=index.cjs.map