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