@event-driven-io/emmett-sqlite 0.43.0-beta.12 → 0.43.0-beta.14
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 +493 -0
- package/dist/cli.cjs +0 -1
- package/dist/cli.d.cts +1 -2
- package/dist/cli.d.ts +1 -2
- package/dist/cli.js +1 -1
- package/dist/cloudflare.cjs +16 -16
- package/dist/cloudflare.cjs.map +1 -1
- package/dist/cloudflare.d.cts +9 -10
- package/dist/cloudflare.d.ts +9 -10
- package/dist/cloudflare.js +18 -19
- package/dist/cloudflare.js.map +1 -1
- package/dist/index-2i8q-ZKl.d.ts +415 -0
- package/dist/index-CqxXzmd1.d.cts +415 -0
- package/dist/index.cjs +1164 -2557
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -243
- package/dist/index.d.ts +2 -243
- package/dist/index.js +1090 -2530
- package/dist/index.js.map +1 -1
- package/dist/sqlite3.cjs +14 -14
- package/dist/sqlite3.cjs.map +1 -1
- package/dist/sqlite3.d.cts +8 -9
- package/dist/sqlite3.d.ts +8 -9
- package/dist/sqlite3.js +14 -15
- package/dist/sqlite3.js.map +1 -1
- package/package.json +10 -10
- package/dist/cli.cjs.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/sqliteProjection-BrbKryzB.d.cts +0 -150
- package/dist/sqliteProjection-BrbKryzB.d.ts +0 -150
package/dist/index.js
CHANGED
|
@@ -1,1647 +1,427 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
} from "
|
|
5
|
-
var pongoProjection = ({
|
|
6
|
-
name,
|
|
7
|
-
kind,
|
|
8
|
-
version,
|
|
9
|
-
truncate,
|
|
10
|
-
handle,
|
|
11
|
-
canHandle,
|
|
12
|
-
eventsOptions
|
|
13
|
-
}) => sqliteProjection({
|
|
14
|
-
name,
|
|
15
|
-
version,
|
|
16
|
-
kind: kind ?? "emt:projections:postgresql:pongo:generic",
|
|
17
|
-
canHandle,
|
|
18
|
-
eventsOptions,
|
|
19
|
-
handle: async (events, context) => {
|
|
20
|
-
const { connection } = context;
|
|
21
|
-
const driver = await pongoDriverRegistry.tryResolve(
|
|
22
|
-
context.driverType
|
|
23
|
-
);
|
|
24
|
-
const pongo = pongoClient({
|
|
25
|
-
driver,
|
|
26
|
-
connectionOptions: { connection }
|
|
27
|
-
});
|
|
28
|
-
try {
|
|
29
|
-
await handle(events, {
|
|
30
|
-
...context,
|
|
31
|
-
pongo
|
|
32
|
-
});
|
|
33
|
-
} finally {
|
|
34
|
-
await pongo.close();
|
|
35
|
-
}
|
|
36
|
-
},
|
|
37
|
-
truncate: truncate ? async (context) => {
|
|
38
|
-
const { connection } = context;
|
|
39
|
-
const driver = await pongoDriverRegistry.tryResolve(
|
|
40
|
-
context.driverType
|
|
41
|
-
);
|
|
42
|
-
const pongo = pongoClient({
|
|
43
|
-
driver,
|
|
44
|
-
connectionOptions: { connection }
|
|
45
|
-
});
|
|
46
|
-
try {
|
|
47
|
-
await truncate({
|
|
48
|
-
...context,
|
|
49
|
-
pongo
|
|
50
|
-
});
|
|
51
|
-
} finally {
|
|
52
|
-
await pongo.close();
|
|
53
|
-
}
|
|
54
|
-
} : void 0
|
|
55
|
-
});
|
|
56
|
-
var pongoMultiStreamProjection = (options) => {
|
|
57
|
-
const { collectionName, getDocumentId, canHandle } = options;
|
|
58
|
-
const collectionNameWithVersion = options.version && options.version > 0 ? `${collectionName}_v${options.version}` : collectionName;
|
|
59
|
-
return pongoProjection({
|
|
60
|
-
name: collectionNameWithVersion,
|
|
61
|
-
version: options.version,
|
|
62
|
-
kind: options.kind ?? "emt:projections:postgresql:pongo:multi_stream",
|
|
63
|
-
eventsOptions: options.eventsOptions,
|
|
64
|
-
handle: async (events, { pongo }) => {
|
|
65
|
-
const collection = pongo.db().collection(
|
|
66
|
-
collectionNameWithVersion,
|
|
67
|
-
options.collectionOptions
|
|
68
|
-
);
|
|
69
|
-
for (const event of events) {
|
|
70
|
-
await collection.handle(getDocumentId(event), async (document) => {
|
|
71
|
-
return "initialState" in options ? await options.evolve(
|
|
72
|
-
document ?? options.initialState(),
|
|
73
|
-
event
|
|
74
|
-
) : await options.evolve(
|
|
75
|
-
document,
|
|
76
|
-
event
|
|
77
|
-
);
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
},
|
|
81
|
-
canHandle,
|
|
82
|
-
truncate: async (context) => {
|
|
83
|
-
const { connection } = context;
|
|
84
|
-
const driver = await pongoDriverRegistry.tryResolve(
|
|
85
|
-
context.driverType
|
|
86
|
-
);
|
|
87
|
-
const pongo = pongoClient({
|
|
88
|
-
driver,
|
|
89
|
-
connectionOptions: { connection }
|
|
90
|
-
});
|
|
91
|
-
try {
|
|
92
|
-
await pongo.db().collection(
|
|
93
|
-
collectionNameWithVersion,
|
|
94
|
-
options.collectionOptions
|
|
95
|
-
).deleteMany();
|
|
96
|
-
} finally {
|
|
97
|
-
await pongo.close();
|
|
98
|
-
}
|
|
99
|
-
},
|
|
100
|
-
init: async (context) => {
|
|
101
|
-
const { connection } = context;
|
|
102
|
-
const driver = await pongoDriverRegistry.tryResolve(
|
|
103
|
-
context.driverType
|
|
104
|
-
);
|
|
105
|
-
const pongo = pongoClient({
|
|
106
|
-
connectionOptions: { connection },
|
|
107
|
-
driver
|
|
108
|
-
});
|
|
109
|
-
try {
|
|
110
|
-
await pongo.db().collection(
|
|
111
|
-
collectionNameWithVersion,
|
|
112
|
-
options.collectionOptions
|
|
113
|
-
).schema.migrate();
|
|
114
|
-
} finally {
|
|
115
|
-
await pongo.close();
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
});
|
|
119
|
-
};
|
|
120
|
-
var pongoSingleStreamProjection = (options) => {
|
|
121
|
-
return pongoMultiStreamProjection({
|
|
122
|
-
...options,
|
|
123
|
-
kind: "emt:projections:postgresql:pongo:single_stream",
|
|
124
|
-
getDocumentId: options.getDocumentId ?? ((event) => event.metadata.streamName)
|
|
125
|
-
});
|
|
126
|
-
};
|
|
1
|
+
import { AssertionError, EmmettError, ExpectedVersionConflictError, JSONSerializer, NO_CONCURRENCY_CHECK, STREAM_DOES_NOT_EXIST, STREAM_EXISTS, assertDeepEqual, assertEqual, assertExpectedVersionMatchesCurrent, assertFails, assertIsNotNull, assertIsNull, assertThatArray, assertTrue, asyncAwaiter, bigIntProcessorCheckpoint, defaultProcessorPartition, defaultProcessorVersion, downcastRecordedMessages, getCheckpoint, getProcessorInstanceId, getProjectorId, getWorkflowId, isErrorConstructor, isExpectedVersionConflictError, parseBigIntProcessorCheckpoint, projection, projector, reactor, reduceAsync, upcastRecordedMessage, workflowProcessor } from "@event-driven-io/emmett";
|
|
2
|
+
import { pongoClient } from "@event-driven-io/pongo";
|
|
3
|
+
import { BatchCommandNoChangesError, DumboError, SQL, UniqueConstraintError, dumbo, exists, mapRows, singleOrNull } from "@event-driven-io/dumbo";
|
|
4
|
+
import { v4, v7 } from "uuid";
|
|
127
5
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
6
|
+
//#region src/eventStore/projections/pongo/pongoProjections.ts
|
|
7
|
+
const pongoProjection = ({ name, kind, version, truncate, handle, canHandle, eventsOptions }) => sqliteProjection({
|
|
8
|
+
name,
|
|
9
|
+
version,
|
|
10
|
+
kind: kind ?? "emt:projections:postgresql:pongo:generic",
|
|
11
|
+
canHandle,
|
|
12
|
+
eventsOptions,
|
|
13
|
+
handle: async (events, context) => {
|
|
14
|
+
const { connection } = context;
|
|
15
|
+
const pongo = pongoClient({
|
|
16
|
+
driver: await pongoDriverRegistry.tryResolve(context.driverType),
|
|
17
|
+
connectionOptions: { connection }
|
|
18
|
+
});
|
|
19
|
+
try {
|
|
20
|
+
await handle(events, {
|
|
21
|
+
...context,
|
|
22
|
+
pongo
|
|
23
|
+
});
|
|
24
|
+
} finally {
|
|
25
|
+
await pongo.close();
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
truncate: truncate ? async (context) => {
|
|
29
|
+
const { connection } = context;
|
|
30
|
+
const pongo = pongoClient({
|
|
31
|
+
driver: await pongoDriverRegistry.tryResolve(context.driverType),
|
|
32
|
+
connectionOptions: { connection }
|
|
33
|
+
});
|
|
34
|
+
try {
|
|
35
|
+
await truncate({
|
|
36
|
+
...context,
|
|
37
|
+
pongo
|
|
38
|
+
});
|
|
39
|
+
} finally {
|
|
40
|
+
await pongo.close();
|
|
41
|
+
}
|
|
42
|
+
} : void 0
|
|
43
|
+
});
|
|
44
|
+
const pongoMultiStreamProjection = (options) => {
|
|
45
|
+
const { collectionName, getDocumentId, canHandle } = options;
|
|
46
|
+
const collectionNameWithVersion = options.version && options.version > 0 ? `${collectionName}_v${options.version}` : collectionName;
|
|
47
|
+
return pongoProjection({
|
|
48
|
+
name: collectionNameWithVersion,
|
|
49
|
+
version: options.version,
|
|
50
|
+
kind: options.kind ?? "emt:projections:postgresql:pongo:multi_stream",
|
|
51
|
+
eventsOptions: options.eventsOptions,
|
|
52
|
+
handle: async (events, { pongo }) => {
|
|
53
|
+
const collection = pongo.db().collection(collectionNameWithVersion, options.collectionOptions);
|
|
54
|
+
const eventsByDocumentId = events.map((event) => {
|
|
55
|
+
return {
|
|
56
|
+
documentId: getDocumentId(event),
|
|
57
|
+
event
|
|
58
|
+
};
|
|
59
|
+
}).reduce((acc, { documentId, event }) => {
|
|
60
|
+
if (!acc.has(documentId)) acc.set(documentId, []);
|
|
61
|
+
acc.get(documentId).push(event);
|
|
62
|
+
return acc;
|
|
63
|
+
}, /* @__PURE__ */ new Map());
|
|
64
|
+
await collection.handle([...eventsByDocumentId.keys()], (document, id) => {
|
|
65
|
+
return reduceAsync(eventsByDocumentId.get(id), async (acc, event) => await options.evolve(acc, event), document ?? ("initialState" in options ? options.initialState() : null));
|
|
66
|
+
});
|
|
67
|
+
},
|
|
68
|
+
canHandle,
|
|
69
|
+
truncate: async (context) => {
|
|
70
|
+
const { connection } = context;
|
|
71
|
+
const pongo = pongoClient({
|
|
72
|
+
driver: await pongoDriverRegistry.tryResolve(context.driverType),
|
|
73
|
+
connectionOptions: { connection }
|
|
74
|
+
});
|
|
75
|
+
try {
|
|
76
|
+
await pongo.db().collection(collectionNameWithVersion, options.collectionOptions).deleteMany();
|
|
77
|
+
} finally {
|
|
78
|
+
await pongo.close();
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
init: async (context) => {
|
|
82
|
+
const { connection } = context;
|
|
83
|
+
const driver = await pongoDriverRegistry.tryResolve(context.driverType);
|
|
84
|
+
const pongo = pongoClient({
|
|
85
|
+
connectionOptions: { connection },
|
|
86
|
+
driver
|
|
87
|
+
});
|
|
88
|
+
try {
|
|
89
|
+
await pongo.db().collection(collectionNameWithVersion, options.collectionOptions).schema.migrate();
|
|
90
|
+
} finally {
|
|
91
|
+
await pongo.close();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
};
|
|
96
|
+
const pongoSingleStreamProjection = (options) => {
|
|
97
|
+
return pongoMultiStreamProjection({
|
|
98
|
+
...options,
|
|
99
|
+
kind: "emt:projections:postgresql:pongo:single_stream",
|
|
100
|
+
getDocumentId: options.getDocumentId ?? ((event) => event.metadata.streamName)
|
|
101
|
+
});
|
|
174
102
|
};
|
|
175
103
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
104
|
+
//#endregion
|
|
105
|
+
//#region src/eventStore/projections/pongo/pongoProjectionSpec.ts
|
|
106
|
+
const withCollection = async (handle, options) => {
|
|
107
|
+
const { connection, inDatabase, inCollection } = options;
|
|
108
|
+
const driver = await pongoDriverRegistry.tryResolve(connection.driverType);
|
|
109
|
+
const pongo = pongoClient({
|
|
110
|
+
connectionOptions: { connection },
|
|
111
|
+
driver
|
|
112
|
+
});
|
|
113
|
+
try {
|
|
114
|
+
return handle(pongo.db(inDatabase).collection(inCollection));
|
|
115
|
+
} finally {
|
|
116
|
+
await pongo.close();
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
const withoutIdAndVersion = (doc) => {
|
|
120
|
+
const { _id, _version, ...without } = doc;
|
|
121
|
+
return without;
|
|
122
|
+
};
|
|
123
|
+
const assertDocumentsEqual = (actual, expected) => {
|
|
124
|
+
if ("_id" in expected) assertEqual(expected._id, actual._id, `Document ids are not matching! Expected: ${expected._id}, Actual: ${actual._id}`);
|
|
125
|
+
return assertDeepEqual(withoutIdAndVersion(actual), withoutIdAndVersion(expected));
|
|
126
|
+
};
|
|
127
|
+
const documentExists = (document, options) => (assertOptions) => withCollection(async (collection) => {
|
|
128
|
+
const result = await collection.findOne("withId" in options ? { _id: options.withId } : options.matchingFilter);
|
|
129
|
+
assertIsNotNull(result);
|
|
130
|
+
assertDocumentsEqual(result, document);
|
|
131
|
+
}, {
|
|
132
|
+
...options,
|
|
133
|
+
...assertOptions
|
|
196
134
|
});
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
return current === expected;
|
|
205
|
-
};
|
|
206
|
-
var assertExpectedVersionMatchesCurrent = (current, expected, defaultVersion) => {
|
|
207
|
-
expected ??= NO_CONCURRENCY_CHECK;
|
|
208
|
-
if (!matchesExpectedVersion(current, expected, defaultVersion))
|
|
209
|
-
throw new ExpectedVersionConflictError(current, expected);
|
|
210
|
-
};
|
|
211
|
-
var ExpectedVersionConflictError = class _ExpectedVersionConflictError extends ConcurrencyError {
|
|
212
|
-
constructor(current, expected) {
|
|
213
|
-
super(current?.toString(), expected?.toString());
|
|
214
|
-
Object.setPrototypeOf(this, _ExpectedVersionConflictError.prototype);
|
|
215
|
-
}
|
|
216
|
-
};
|
|
217
|
-
var isExpectedVersionConflictError = (error) => error instanceof ExpectedVersionConflictError || EmmettError.isInstanceOf(
|
|
218
|
-
error,
|
|
219
|
-
ExpectedVersionConflictError.Codes.ConcurrencyError
|
|
220
|
-
);
|
|
221
|
-
var isPrimitive = (value) => {
|
|
222
|
-
const type = typeof value;
|
|
223
|
-
return value === null || value === void 0 || type === "boolean" || type === "number" || type === "string" || type === "symbol" || type === "bigint";
|
|
224
|
-
};
|
|
225
|
-
var compareArrays = (left, right) => {
|
|
226
|
-
if (left.length !== right.length) {
|
|
227
|
-
return false;
|
|
228
|
-
}
|
|
229
|
-
for (let i = 0; i < left.length; i++) {
|
|
230
|
-
const leftHas = i in left;
|
|
231
|
-
const rightHas = i in right;
|
|
232
|
-
if (leftHas !== rightHas) return false;
|
|
233
|
-
if (leftHas && !deepEquals(left[i], right[i])) return false;
|
|
234
|
-
}
|
|
235
|
-
return true;
|
|
236
|
-
};
|
|
237
|
-
var compareDates = (left, right) => {
|
|
238
|
-
return left.getTime() === right.getTime();
|
|
239
|
-
};
|
|
240
|
-
var compareRegExps = (left, right) => {
|
|
241
|
-
return left.toString() === right.toString();
|
|
242
|
-
};
|
|
243
|
-
var compareErrors = (left, right) => {
|
|
244
|
-
if (left.message !== right.message || left.name !== right.name) {
|
|
245
|
-
return false;
|
|
246
|
-
}
|
|
247
|
-
const leftKeys = Object.keys(left);
|
|
248
|
-
const rightKeys = Object.keys(right);
|
|
249
|
-
if (leftKeys.length !== rightKeys.length) return false;
|
|
250
|
-
const rightKeySet = new Set(rightKeys);
|
|
251
|
-
for (const key of leftKeys) {
|
|
252
|
-
if (!rightKeySet.has(key)) return false;
|
|
253
|
-
if (!deepEquals(left[key], right[key])) return false;
|
|
254
|
-
}
|
|
255
|
-
return true;
|
|
256
|
-
};
|
|
257
|
-
var compareMaps = (left, right) => {
|
|
258
|
-
if (left.size !== right.size) return false;
|
|
259
|
-
for (const [key, value] of left) {
|
|
260
|
-
if (isPrimitive(key)) {
|
|
261
|
-
if (!right.has(key) || !deepEquals(value, right.get(key))) {
|
|
262
|
-
return false;
|
|
263
|
-
}
|
|
264
|
-
} else {
|
|
265
|
-
let found = false;
|
|
266
|
-
for (const [rightKey, rightValue] of right) {
|
|
267
|
-
if (deepEquals(key, rightKey) && deepEquals(value, rightValue)) {
|
|
268
|
-
found = true;
|
|
269
|
-
break;
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
if (!found) return false;
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
return true;
|
|
276
|
-
};
|
|
277
|
-
var compareSets = (left, right) => {
|
|
278
|
-
if (left.size !== right.size) return false;
|
|
279
|
-
for (const leftItem of left) {
|
|
280
|
-
if (isPrimitive(leftItem)) {
|
|
281
|
-
if (!right.has(leftItem)) return false;
|
|
282
|
-
} else {
|
|
283
|
-
let found = false;
|
|
284
|
-
for (const rightItem of right) {
|
|
285
|
-
if (deepEquals(leftItem, rightItem)) {
|
|
286
|
-
found = true;
|
|
287
|
-
break;
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
if (!found) return false;
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
return true;
|
|
294
|
-
};
|
|
295
|
-
var compareArrayBuffers = (left, right) => {
|
|
296
|
-
if (left.byteLength !== right.byteLength) return false;
|
|
297
|
-
const leftView = new Uint8Array(left);
|
|
298
|
-
const rightView = new Uint8Array(right);
|
|
299
|
-
for (let i = 0; i < leftView.length; i++) {
|
|
300
|
-
if (leftView[i] !== rightView[i]) return false;
|
|
301
|
-
}
|
|
302
|
-
return true;
|
|
303
|
-
};
|
|
304
|
-
var compareTypedArrays = (left, right) => {
|
|
305
|
-
if (left.constructor !== right.constructor) return false;
|
|
306
|
-
if (left.byteLength !== right.byteLength) return false;
|
|
307
|
-
const leftArray = new Uint8Array(
|
|
308
|
-
left.buffer,
|
|
309
|
-
left.byteOffset,
|
|
310
|
-
left.byteLength
|
|
311
|
-
);
|
|
312
|
-
const rightArray = new Uint8Array(
|
|
313
|
-
right.buffer,
|
|
314
|
-
right.byteOffset,
|
|
315
|
-
right.byteLength
|
|
316
|
-
);
|
|
317
|
-
for (let i = 0; i < leftArray.length; i++) {
|
|
318
|
-
if (leftArray[i] !== rightArray[i]) return false;
|
|
319
|
-
}
|
|
320
|
-
return true;
|
|
321
|
-
};
|
|
322
|
-
var compareObjects = (left, right) => {
|
|
323
|
-
const keys1 = Object.keys(left);
|
|
324
|
-
const keys2 = Object.keys(right);
|
|
325
|
-
if (keys1.length !== keys2.length) {
|
|
326
|
-
return false;
|
|
327
|
-
}
|
|
328
|
-
for (const key of keys1) {
|
|
329
|
-
if (left[key] instanceof Function && right[key] instanceof Function) {
|
|
330
|
-
continue;
|
|
331
|
-
}
|
|
332
|
-
const isEqual = deepEquals(left[key], right[key]);
|
|
333
|
-
if (!isEqual) {
|
|
334
|
-
return false;
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
return true;
|
|
338
|
-
};
|
|
339
|
-
var getType = (value) => {
|
|
340
|
-
if (value === null) return "null";
|
|
341
|
-
if (value === void 0) return "undefined";
|
|
342
|
-
const primitiveType = typeof value;
|
|
343
|
-
if (primitiveType !== "object") return primitiveType;
|
|
344
|
-
if (Array.isArray(value)) return "array";
|
|
345
|
-
if (value instanceof Boolean) return "boxed-boolean";
|
|
346
|
-
if (value instanceof Number) return "boxed-number";
|
|
347
|
-
if (value instanceof String) return "boxed-string";
|
|
348
|
-
if (value instanceof Date) return "date";
|
|
349
|
-
if (value instanceof RegExp) return "regexp";
|
|
350
|
-
if (value instanceof Error) return "error";
|
|
351
|
-
if (value instanceof Map) return "map";
|
|
352
|
-
if (value instanceof Set) return "set";
|
|
353
|
-
if (value instanceof ArrayBuffer) return "arraybuffer";
|
|
354
|
-
if (value instanceof DataView) return "dataview";
|
|
355
|
-
if (value instanceof WeakMap) return "weakmap";
|
|
356
|
-
if (value instanceof WeakSet) return "weakset";
|
|
357
|
-
if (ArrayBuffer.isView(value)) return "typedarray";
|
|
358
|
-
return "object";
|
|
359
|
-
};
|
|
360
|
-
var deepEquals = (left, right) => {
|
|
361
|
-
if (left === right) return true;
|
|
362
|
-
if (isEquatable(left)) {
|
|
363
|
-
return left.equals(right);
|
|
364
|
-
}
|
|
365
|
-
const leftType = getType(left);
|
|
366
|
-
const rightType = getType(right);
|
|
367
|
-
if (leftType !== rightType) return false;
|
|
368
|
-
switch (leftType) {
|
|
369
|
-
case "null":
|
|
370
|
-
case "undefined":
|
|
371
|
-
case "boolean":
|
|
372
|
-
case "number":
|
|
373
|
-
case "bigint":
|
|
374
|
-
case "string":
|
|
375
|
-
case "symbol":
|
|
376
|
-
case "function":
|
|
377
|
-
return left === right;
|
|
378
|
-
case "array":
|
|
379
|
-
return compareArrays(left, right);
|
|
380
|
-
case "date":
|
|
381
|
-
return compareDates(left, right);
|
|
382
|
-
case "regexp":
|
|
383
|
-
return compareRegExps(left, right);
|
|
384
|
-
case "error":
|
|
385
|
-
return compareErrors(left, right);
|
|
386
|
-
case "map":
|
|
387
|
-
return compareMaps(
|
|
388
|
-
left,
|
|
389
|
-
right
|
|
390
|
-
);
|
|
391
|
-
case "set":
|
|
392
|
-
return compareSets(left, right);
|
|
393
|
-
case "arraybuffer":
|
|
394
|
-
return compareArrayBuffers(left, right);
|
|
395
|
-
case "dataview":
|
|
396
|
-
case "weakmap":
|
|
397
|
-
case "weakset":
|
|
398
|
-
return false;
|
|
399
|
-
case "typedarray":
|
|
400
|
-
return compareTypedArrays(
|
|
401
|
-
left,
|
|
402
|
-
right
|
|
403
|
-
);
|
|
404
|
-
case "boxed-boolean":
|
|
405
|
-
return left.valueOf() === right.valueOf();
|
|
406
|
-
case "boxed-number":
|
|
407
|
-
return left.valueOf() === right.valueOf();
|
|
408
|
-
case "boxed-string":
|
|
409
|
-
return left.valueOf() === right.valueOf();
|
|
410
|
-
case "object":
|
|
411
|
-
return compareObjects(
|
|
412
|
-
left,
|
|
413
|
-
right
|
|
414
|
-
);
|
|
415
|
-
default:
|
|
416
|
-
return false;
|
|
417
|
-
}
|
|
418
|
-
};
|
|
419
|
-
var isEquatable = (left) => {
|
|
420
|
-
return left !== null && left !== void 0 && typeof left === "object" && "equals" in left && typeof left["equals"] === "function";
|
|
421
|
-
};
|
|
422
|
-
var toNormalizedString = (value) => value.toString().padStart(19, "0");
|
|
423
|
-
var bigInt = {
|
|
424
|
-
toNormalizedString
|
|
425
|
-
};
|
|
426
|
-
var bigIntReplacer = (_key, value) => {
|
|
427
|
-
return typeof value === "bigint" ? value.toString() : value;
|
|
428
|
-
};
|
|
429
|
-
var dateReplacer = (_key, value) => {
|
|
430
|
-
return value instanceof Date ? value.toISOString() : value;
|
|
431
|
-
};
|
|
432
|
-
var isFirstLetterNumeric = (str) => {
|
|
433
|
-
const c = str.charCodeAt(0);
|
|
434
|
-
return c >= 48 && c <= 57;
|
|
435
|
-
};
|
|
436
|
-
var isFirstLetterNumericOrMinus = (str) => {
|
|
437
|
-
const c = str.charCodeAt(0);
|
|
438
|
-
return c >= 48 && c <= 57 || c === 45;
|
|
439
|
-
};
|
|
440
|
-
var bigIntReviver = (_key, value, context) => {
|
|
441
|
-
if (typeof value === "number" && Number.isInteger(value) && !Number.isSafeInteger(value)) {
|
|
442
|
-
try {
|
|
443
|
-
return BigInt(context?.source ?? value.toString());
|
|
444
|
-
} catch {
|
|
445
|
-
return value;
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
if (typeof value === "string" && value.length > 15) {
|
|
449
|
-
if (isFirstLetterNumericOrMinus(value)) {
|
|
450
|
-
const num = Number(value);
|
|
451
|
-
if (Number.isFinite(num) && !Number.isSafeInteger(num)) {
|
|
452
|
-
try {
|
|
453
|
-
return BigInt(value);
|
|
454
|
-
} catch {
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
return value;
|
|
460
|
-
};
|
|
461
|
-
var dateReviver = (_key, value) => {
|
|
462
|
-
if (typeof value === "string" && value.length === 24 && isFirstLetterNumeric(value) && value[10] === "T" && value[23] === "Z") {
|
|
463
|
-
const date = new Date(value);
|
|
464
|
-
if (!isNaN(date.getTime())) {
|
|
465
|
-
return date;
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
return value;
|
|
469
|
-
};
|
|
470
|
-
var composeJSONReplacers = (...replacers) => {
|
|
471
|
-
const filteredReplacers = replacers.filter((r) => r !== void 0);
|
|
472
|
-
if (filteredReplacers.length === 0) return void 0;
|
|
473
|
-
return (key, value) => (
|
|
474
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
475
|
-
filteredReplacers.reduce(
|
|
476
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
477
|
-
(accValue, replacer) => replacer(key, accValue),
|
|
478
|
-
value
|
|
479
|
-
)
|
|
480
|
-
);
|
|
481
|
-
};
|
|
482
|
-
var composeJSONRevivers = (...revivers) => {
|
|
483
|
-
const filteredRevivers = revivers.filter((r) => r !== void 0);
|
|
484
|
-
if (filteredRevivers.length === 0) return void 0;
|
|
485
|
-
return (key, value, context) => (
|
|
486
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
487
|
-
filteredRevivers.reduce(
|
|
488
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
489
|
-
(accValue, reviver) => reviver(key, accValue, context),
|
|
490
|
-
value
|
|
491
|
-
)
|
|
492
|
-
);
|
|
493
|
-
};
|
|
494
|
-
var JSONReplacer = (opts) => composeJSONReplacers(
|
|
495
|
-
opts?.replacer,
|
|
496
|
-
opts?.failOnBigIntSerialization !== true ? JSONReplacers.bigInt : void 0,
|
|
497
|
-
opts?.useDefaultDateSerialization !== true ? JSONReplacers.date : void 0
|
|
498
|
-
);
|
|
499
|
-
var JSONReviver = (opts) => composeJSONRevivers(
|
|
500
|
-
opts?.reviver,
|
|
501
|
-
opts?.parseBigInts === true ? JSONRevivers.bigInt : void 0,
|
|
502
|
-
opts?.parseDates === true ? JSONRevivers.date : void 0
|
|
503
|
-
);
|
|
504
|
-
var JSONReplacers = {
|
|
505
|
-
bigInt: bigIntReplacer,
|
|
506
|
-
date: dateReplacer
|
|
507
|
-
};
|
|
508
|
-
var JSONRevivers = {
|
|
509
|
-
bigInt: bigIntReviver,
|
|
510
|
-
date: dateReviver
|
|
511
|
-
};
|
|
512
|
-
var jsonSerializer = (options) => {
|
|
513
|
-
const defaultReplacer = JSONReplacer(options);
|
|
514
|
-
const defaultReviver = JSONReviver(options);
|
|
515
|
-
return {
|
|
516
|
-
serialize: (object, serializerOptions) => JSON.stringify(
|
|
517
|
-
object,
|
|
518
|
-
serializerOptions ? JSONReplacer(serializerOptions) : defaultReplacer
|
|
519
|
-
),
|
|
520
|
-
deserialize: (payload, deserializerOptions) => JSON.parse(
|
|
521
|
-
payload,
|
|
522
|
-
deserializerOptions ? JSONReviver(deserializerOptions) : defaultReviver
|
|
523
|
-
)
|
|
524
|
-
};
|
|
525
|
-
};
|
|
526
|
-
var JSONSerializer = Object.assign(jsonSerializer(), {
|
|
527
|
-
from: (options) => options?.serialization?.serializer ?? (options?.serialization?.options ? jsonSerializer(options?.serialization?.options) : JSONSerializer)
|
|
135
|
+
const documentsAreTheSame = (documents, options) => (assertOptions) => withCollection(async (collection) => {
|
|
136
|
+
const result = await collection.find("withId" in options ? { _id: options.withId } : options.matchingFilter);
|
|
137
|
+
assertEqual(documents.length, result.length, "Different Documents Count than expected");
|
|
138
|
+
for (let i = 0; i < documents.length; i++) assertThatArray(result).contains(documents[i]);
|
|
139
|
+
}, {
|
|
140
|
+
...options,
|
|
141
|
+
...assertOptions
|
|
528
142
|
});
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
try {
|
|
535
|
-
const result = await fn();
|
|
536
|
-
if (opts?.shouldRetryResult && opts.shouldRetryResult(result)) {
|
|
537
|
-
throw new EmmettError(
|
|
538
|
-
`Retrying because of result: ${JSONSerializer.serialize(result)}`
|
|
539
|
-
);
|
|
540
|
-
}
|
|
541
|
-
return result;
|
|
542
|
-
} catch (error) {
|
|
543
|
-
if (opts?.shouldRetryError && !opts.shouldRetryError(error)) {
|
|
544
|
-
bail(error);
|
|
545
|
-
return void 0;
|
|
546
|
-
}
|
|
547
|
-
throw error;
|
|
548
|
-
}
|
|
549
|
-
},
|
|
550
|
-
opts ?? { retries: 0 }
|
|
551
|
-
);
|
|
552
|
-
};
|
|
553
|
-
var onShutdown = (handler) => {
|
|
554
|
-
const signals = ["SIGTERM", "SIGINT"];
|
|
555
|
-
if (typeof process !== "undefined" && typeof process.on === "function") {
|
|
556
|
-
for (const signal of signals) {
|
|
557
|
-
process.on(signal, handler);
|
|
558
|
-
}
|
|
559
|
-
return () => {
|
|
560
|
-
for (const signal of signals) {
|
|
561
|
-
process.off(signal, handler);
|
|
562
|
-
}
|
|
563
|
-
};
|
|
564
|
-
}
|
|
565
|
-
const deno = globalThis.Deno;
|
|
566
|
-
if (deno && typeof deno.addSignalListener === "function") {
|
|
567
|
-
for (const signal of signals) {
|
|
568
|
-
deno.addSignalListener(signal, handler);
|
|
569
|
-
}
|
|
570
|
-
return () => {
|
|
571
|
-
for (const signal of signals) {
|
|
572
|
-
deno.removeSignalListener(signal, handler);
|
|
573
|
-
}
|
|
574
|
-
};
|
|
575
|
-
}
|
|
576
|
-
return () => {
|
|
577
|
-
};
|
|
578
|
-
};
|
|
579
|
-
var textEncoder = new TextEncoder();
|
|
580
|
-
var getCheckpoint = (message2) => {
|
|
581
|
-
return message2.metadata.checkpoint;
|
|
582
|
-
};
|
|
583
|
-
var wasMessageHandled = (message2, checkpoint) => {
|
|
584
|
-
const messageCheckpoint = getCheckpoint(message2);
|
|
585
|
-
return messageCheckpoint !== null && messageCheckpoint !== void 0 && checkpoint !== null && checkpoint !== void 0 && messageCheckpoint <= checkpoint;
|
|
586
|
-
};
|
|
587
|
-
var MessageProcessorType = {
|
|
588
|
-
PROJECTOR: "projector",
|
|
589
|
-
REACTOR: "reactor"
|
|
590
|
-
};
|
|
591
|
-
var defaultProcessingMessageProcessingScope = (handler, partialContext) => handler(partialContext);
|
|
592
|
-
var bigIntProcessorCheckpoint = (value) => bigInt.toNormalizedString(value);
|
|
593
|
-
var parseBigIntProcessorCheckpoint = (value) => BigInt(value);
|
|
594
|
-
var defaultProcessorVersion = 1;
|
|
595
|
-
var defaultProcessorPartition = defaultTag;
|
|
596
|
-
var getProcessorInstanceId = (processorId) => `${processorId}:${uuid3()}`;
|
|
597
|
-
var getProjectorId = (options) => `emt:processor:projector:${options.projectionName}`;
|
|
598
|
-
var reactor = (options) => {
|
|
599
|
-
const {
|
|
600
|
-
checkpoints,
|
|
601
|
-
processorId,
|
|
602
|
-
processorInstanceId: instanceId = getProcessorInstanceId(processorId),
|
|
603
|
-
type = MessageProcessorType.REACTOR,
|
|
604
|
-
version = defaultProcessorVersion,
|
|
605
|
-
partition = defaultProcessorPartition,
|
|
606
|
-
hooks = {},
|
|
607
|
-
processingScope = defaultProcessingMessageProcessingScope,
|
|
608
|
-
startFrom,
|
|
609
|
-
canHandle,
|
|
610
|
-
stopAfter
|
|
611
|
-
} = options;
|
|
612
|
-
const eachMessage = "eachMessage" in options && options.eachMessage ? options.eachMessage : () => Promise.resolve();
|
|
613
|
-
let isInitiated = false;
|
|
614
|
-
let isActive = false;
|
|
615
|
-
let lastCheckpoint = null;
|
|
616
|
-
let closeSignal = null;
|
|
617
|
-
const init = async (initOptions) => {
|
|
618
|
-
if (isInitiated) return;
|
|
619
|
-
if (hooks.onInit === void 0) {
|
|
620
|
-
isInitiated = true;
|
|
621
|
-
return;
|
|
622
|
-
}
|
|
623
|
-
return await processingScope(async (context) => {
|
|
624
|
-
await hooks.onInit(context);
|
|
625
|
-
isInitiated = true;
|
|
626
|
-
}, initOptions);
|
|
627
|
-
};
|
|
628
|
-
const close = async (closeOptions) => {
|
|
629
|
-
isActive = false;
|
|
630
|
-
if (closeSignal) {
|
|
631
|
-
closeSignal();
|
|
632
|
-
closeSignal = null;
|
|
633
|
-
}
|
|
634
|
-
if (hooks.onClose) {
|
|
635
|
-
await processingScope(hooks.onClose, closeOptions);
|
|
636
|
-
}
|
|
637
|
-
};
|
|
638
|
-
return {
|
|
639
|
-
// TODO: Consider whether not make it optional or add URN prefix
|
|
640
|
-
id: processorId,
|
|
641
|
-
instanceId,
|
|
642
|
-
type,
|
|
643
|
-
canHandle,
|
|
644
|
-
init,
|
|
645
|
-
start: async (startOptions) => {
|
|
646
|
-
if (isActive) return;
|
|
647
|
-
await init(startOptions);
|
|
648
|
-
isActive = true;
|
|
649
|
-
closeSignal = onShutdown(() => close(startOptions));
|
|
650
|
-
if (lastCheckpoint !== null)
|
|
651
|
-
return {
|
|
652
|
-
lastCheckpoint
|
|
653
|
-
};
|
|
654
|
-
return await processingScope(async (context) => {
|
|
655
|
-
if (hooks.onStart) {
|
|
656
|
-
await hooks.onStart(context);
|
|
657
|
-
}
|
|
658
|
-
if (startFrom && startFrom !== "CURRENT") return startFrom;
|
|
659
|
-
if (checkpoints) {
|
|
660
|
-
const readResult = await checkpoints?.read(
|
|
661
|
-
{
|
|
662
|
-
processorId,
|
|
663
|
-
partition
|
|
664
|
-
},
|
|
665
|
-
{ ...startOptions, ...context }
|
|
666
|
-
);
|
|
667
|
-
lastCheckpoint = readResult.lastCheckpoint;
|
|
668
|
-
}
|
|
669
|
-
if (lastCheckpoint === null) return "BEGINNING";
|
|
670
|
-
return {
|
|
671
|
-
lastCheckpoint
|
|
672
|
-
};
|
|
673
|
-
}, startOptions);
|
|
674
|
-
},
|
|
675
|
-
close,
|
|
676
|
-
get isActive() {
|
|
677
|
-
return isActive;
|
|
678
|
-
},
|
|
679
|
-
handle: async (messages, partialContext) => {
|
|
680
|
-
if (!isActive) return Promise.resolve();
|
|
681
|
-
return await processingScope(async (context) => {
|
|
682
|
-
let result = void 0;
|
|
683
|
-
for (const message2 of messages) {
|
|
684
|
-
if (wasMessageHandled(message2, lastCheckpoint)) continue;
|
|
685
|
-
const upcasted = upcastRecordedMessage(
|
|
686
|
-
// TODO: Make it smarter
|
|
687
|
-
message2,
|
|
688
|
-
options.messageOptions?.schema?.versioning
|
|
689
|
-
);
|
|
690
|
-
if (canHandle !== void 0 && !canHandle.includes(upcasted.type))
|
|
691
|
-
continue;
|
|
692
|
-
const messageProcessingResult = await eachMessage(upcasted, context);
|
|
693
|
-
if (checkpoints) {
|
|
694
|
-
const storeCheckpointResult = await checkpoints.store(
|
|
695
|
-
{
|
|
696
|
-
processorId,
|
|
697
|
-
version,
|
|
698
|
-
message: upcasted,
|
|
699
|
-
lastCheckpoint,
|
|
700
|
-
partition
|
|
701
|
-
},
|
|
702
|
-
context
|
|
703
|
-
);
|
|
704
|
-
if (storeCheckpointResult.success) {
|
|
705
|
-
lastCheckpoint = storeCheckpointResult.newCheckpoint;
|
|
706
|
-
}
|
|
707
|
-
}
|
|
708
|
-
if (messageProcessingResult && messageProcessingResult.type === "STOP") {
|
|
709
|
-
isActive = false;
|
|
710
|
-
result = messageProcessingResult;
|
|
711
|
-
break;
|
|
712
|
-
}
|
|
713
|
-
if (stopAfter && stopAfter(upcasted)) {
|
|
714
|
-
isActive = false;
|
|
715
|
-
result = { type: "STOP", reason: "Stop condition reached" };
|
|
716
|
-
break;
|
|
717
|
-
}
|
|
718
|
-
if (messageProcessingResult && messageProcessingResult.type === "SKIP")
|
|
719
|
-
continue;
|
|
720
|
-
}
|
|
721
|
-
return result;
|
|
722
|
-
}, partialContext);
|
|
723
|
-
}
|
|
724
|
-
};
|
|
725
|
-
};
|
|
726
|
-
var projector = (options) => {
|
|
727
|
-
const {
|
|
728
|
-
projection: projection2,
|
|
729
|
-
processorId = getProjectorId({
|
|
730
|
-
projectionName: projection2.name ?? "unknown"
|
|
731
|
-
}),
|
|
732
|
-
...rest
|
|
733
|
-
} = options;
|
|
734
|
-
return reactor({
|
|
735
|
-
...rest,
|
|
736
|
-
type: MessageProcessorType.PROJECTOR,
|
|
737
|
-
canHandle: projection2.canHandle,
|
|
738
|
-
processorId,
|
|
739
|
-
messageOptions: options.projection.eventsOptions,
|
|
740
|
-
hooks: {
|
|
741
|
-
onInit: options.hooks?.onInit,
|
|
742
|
-
onStart: options.truncateOnStart && options.projection.truncate || options.hooks?.onStart ? async (context) => {
|
|
743
|
-
if (options.truncateOnStart && options.projection.truncate)
|
|
744
|
-
await options.projection.truncate(context);
|
|
745
|
-
if (options.hooks?.onStart) await options.hooks?.onStart(context);
|
|
746
|
-
} : void 0,
|
|
747
|
-
onClose: options.hooks?.onClose
|
|
748
|
-
},
|
|
749
|
-
eachMessage: async (event2, context) => projection2.handle([event2], context)
|
|
750
|
-
});
|
|
751
|
-
};
|
|
752
|
-
var AssertionError = class extends Error {
|
|
753
|
-
constructor(message2) {
|
|
754
|
-
super(message2);
|
|
755
|
-
}
|
|
756
|
-
};
|
|
757
|
-
var isSubset = (superObj, subObj) => {
|
|
758
|
-
const sup = superObj;
|
|
759
|
-
const sub = subObj;
|
|
760
|
-
assertOk(sup);
|
|
761
|
-
assertOk(sub);
|
|
762
|
-
return Object.keys(sub).every((ele) => {
|
|
763
|
-
if (sub[ele] !== null && typeof sub[ele] == "object") {
|
|
764
|
-
return isSubset(sup[ele], sub[ele]);
|
|
765
|
-
}
|
|
766
|
-
return sub[ele] === sup[ele];
|
|
767
|
-
});
|
|
768
|
-
};
|
|
769
|
-
var assertFails = (message2) => {
|
|
770
|
-
throw new AssertionError(message2 ?? "That should not ever happened, right?");
|
|
771
|
-
};
|
|
772
|
-
var assertDeepEqual = (actual, expected, message2) => {
|
|
773
|
-
if (!deepEquals(actual, expected))
|
|
774
|
-
throw new AssertionError(
|
|
775
|
-
message2 ?? `subObj:
|
|
776
|
-
${JSONSerializer.serialize(expected)}
|
|
777
|
-
is not equal to
|
|
778
|
-
${JSONSerializer.serialize(actual)}`
|
|
779
|
-
);
|
|
780
|
-
};
|
|
781
|
-
function assertTrue(condition, message2) {
|
|
782
|
-
if (condition !== true)
|
|
783
|
-
throw new AssertionError(message2 ?? `Condition is false`);
|
|
784
|
-
}
|
|
785
|
-
function assertOk(obj, message2) {
|
|
786
|
-
if (!obj) throw new AssertionError(message2 ?? `Condition is not truthy`);
|
|
787
|
-
}
|
|
788
|
-
function assertEqual(expected, actual, message2) {
|
|
789
|
-
if (expected !== actual)
|
|
790
|
-
throw new AssertionError(
|
|
791
|
-
`${message2 ?? "Objects are not equal"}:
|
|
792
|
-
Expected: ${JSONSerializer.serialize(expected)}
|
|
793
|
-
Actual: ${JSONSerializer.serialize(actual)}`
|
|
794
|
-
);
|
|
795
|
-
}
|
|
796
|
-
function assertNotEqual(obj, other, message2) {
|
|
797
|
-
if (obj === other)
|
|
798
|
-
throw new AssertionError(
|
|
799
|
-
message2 ?? `Objects are equal: ${JSONSerializer.serialize(obj)}`
|
|
800
|
-
);
|
|
801
|
-
}
|
|
802
|
-
function assertIsNotNull(result) {
|
|
803
|
-
assertNotEqual(result, null);
|
|
804
|
-
assertOk(result);
|
|
805
|
-
}
|
|
806
|
-
function assertIsNull(result) {
|
|
807
|
-
assertEqual(result, null);
|
|
808
|
-
}
|
|
809
|
-
var assertThatArray = (array) => {
|
|
810
|
-
return {
|
|
811
|
-
isEmpty: () => assertEqual(
|
|
812
|
-
array.length,
|
|
813
|
-
0,
|
|
814
|
-
`Array is not empty ${JSONSerializer.serialize(array)}`
|
|
815
|
-
),
|
|
816
|
-
isNotEmpty: () => assertNotEqual(array.length, 0, `Array is empty`),
|
|
817
|
-
hasSize: (length) => assertEqual(array.length, length),
|
|
818
|
-
containsElements: (other) => {
|
|
819
|
-
assertTrue(other.every((ts) => array.some((o) => deepEquals(ts, o))));
|
|
820
|
-
},
|
|
821
|
-
containsElementsMatching: (other) => {
|
|
822
|
-
assertTrue(other.every((ts) => array.some((o) => isSubset(o, ts))));
|
|
823
|
-
},
|
|
824
|
-
containsOnlyElementsMatching: (other) => {
|
|
825
|
-
assertEqual(array.length, other.length, `Arrays lengths don't match`);
|
|
826
|
-
assertTrue(other.every((ts) => array.some((o) => isSubset(o, ts))));
|
|
827
|
-
},
|
|
828
|
-
containsExactlyInAnyOrder: (other) => {
|
|
829
|
-
assertEqual(array.length, other.length);
|
|
830
|
-
assertTrue(array.every((ts) => other.some((o) => deepEquals(ts, o))));
|
|
831
|
-
},
|
|
832
|
-
containsExactlyInAnyOrderElementsOf: (other) => {
|
|
833
|
-
assertEqual(array.length, other.length);
|
|
834
|
-
assertTrue(array.every((ts) => other.some((o) => deepEquals(ts, o))));
|
|
835
|
-
},
|
|
836
|
-
containsExactlyElementsOf: (other) => {
|
|
837
|
-
assertEqual(array.length, other.length);
|
|
838
|
-
for (let i = 0; i < array.length; i++) {
|
|
839
|
-
assertTrue(deepEquals(array[i], other[i]));
|
|
840
|
-
}
|
|
841
|
-
},
|
|
842
|
-
containsExactly: (elem) => {
|
|
843
|
-
assertEqual(array.length, 1);
|
|
844
|
-
assertTrue(deepEquals(array[0], elem));
|
|
845
|
-
},
|
|
846
|
-
contains: (elem) => {
|
|
847
|
-
assertTrue(array.some((a) => deepEquals(a, elem)));
|
|
848
|
-
},
|
|
849
|
-
containsOnlyOnceElementsOf: (other) => {
|
|
850
|
-
assertTrue(
|
|
851
|
-
other.map((o) => array.filter((a) => deepEquals(a, o)).length).filter((a) => a === 1).length === other.length
|
|
852
|
-
);
|
|
853
|
-
},
|
|
854
|
-
containsAnyOf: (other) => {
|
|
855
|
-
assertTrue(array.some((a) => other.some((o) => deepEquals(a, o))));
|
|
856
|
-
},
|
|
857
|
-
allMatch: (matches) => {
|
|
858
|
-
assertTrue(array.every(matches));
|
|
859
|
-
},
|
|
860
|
-
anyMatches: (matches) => {
|
|
861
|
-
assertTrue(array.some(matches));
|
|
862
|
-
},
|
|
863
|
-
allMatchAsync: async (matches) => {
|
|
864
|
-
for (const item of array) {
|
|
865
|
-
assertTrue(await matches(item));
|
|
866
|
-
}
|
|
867
|
-
}
|
|
868
|
-
};
|
|
869
|
-
};
|
|
870
|
-
var downcastRecordedMessage = (recordedMessage, options) => {
|
|
871
|
-
if (!options?.downcast)
|
|
872
|
-
return recordedMessage;
|
|
873
|
-
const downcasted = options.downcast(
|
|
874
|
-
recordedMessage
|
|
875
|
-
);
|
|
876
|
-
return {
|
|
877
|
-
...recordedMessage,
|
|
878
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
879
|
-
data: downcasted.data,
|
|
880
|
-
..."metadata" in recordedMessage || "metadata" in downcasted ? {
|
|
881
|
-
metadata: {
|
|
882
|
-
..."metadata" in recordedMessage ? recordedMessage.metadata : {},
|
|
883
|
-
..."metadata" in downcasted ? downcasted.metadata : {}
|
|
884
|
-
}
|
|
885
|
-
} : {}
|
|
886
|
-
};
|
|
887
|
-
};
|
|
888
|
-
var downcastRecordedMessages = (recordedMessages, options) => {
|
|
889
|
-
if (!options?.downcast)
|
|
890
|
-
return recordedMessages;
|
|
891
|
-
return recordedMessages.map(
|
|
892
|
-
(recordedMessage) => downcastRecordedMessage(recordedMessage, options)
|
|
893
|
-
);
|
|
894
|
-
};
|
|
895
|
-
var upcastRecordedMessage = (recordedMessage, options) => {
|
|
896
|
-
if (!options?.upcast)
|
|
897
|
-
return recordedMessage;
|
|
898
|
-
const upcasted = options.upcast(
|
|
899
|
-
recordedMessage
|
|
900
|
-
);
|
|
901
|
-
return {
|
|
902
|
-
...recordedMessage,
|
|
903
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
904
|
-
data: upcasted.data,
|
|
905
|
-
..."metadata" in recordedMessage || "metadata" in upcasted ? {
|
|
906
|
-
metadata: {
|
|
907
|
-
..."metadata" in recordedMessage ? recordedMessage.metadata : {},
|
|
908
|
-
..."metadata" in upcasted ? upcasted.metadata : {}
|
|
909
|
-
}
|
|
910
|
-
} : {}
|
|
911
|
-
};
|
|
912
|
-
};
|
|
913
|
-
var projection = (definition) => definition;
|
|
914
|
-
var WorkflowHandlerStreamVersionConflictRetryOptions = {
|
|
915
|
-
retries: 3,
|
|
916
|
-
minTimeout: 100,
|
|
917
|
-
factor: 1.5,
|
|
918
|
-
shouldRetryError: isExpectedVersionConflictError
|
|
919
|
-
};
|
|
920
|
-
var fromWorkflowHandlerRetryOptions = (retryOptions) => {
|
|
921
|
-
if (retryOptions === void 0) return NoRetries;
|
|
922
|
-
if ("onVersionConflict" in retryOptions) {
|
|
923
|
-
if (typeof retryOptions.onVersionConflict === "boolean")
|
|
924
|
-
return WorkflowHandlerStreamVersionConflictRetryOptions;
|
|
925
|
-
else if (typeof retryOptions.onVersionConflict === "number")
|
|
926
|
-
return {
|
|
927
|
-
...WorkflowHandlerStreamVersionConflictRetryOptions,
|
|
928
|
-
retries: retryOptions.onVersionConflict
|
|
929
|
-
};
|
|
930
|
-
else return retryOptions.onVersionConflict;
|
|
931
|
-
}
|
|
932
|
-
return retryOptions;
|
|
933
|
-
};
|
|
934
|
-
var emptyHandlerResult = (nextExpectedStreamVersion = 0n) => ({
|
|
935
|
-
newMessages: [],
|
|
936
|
-
createdNewStream: false,
|
|
937
|
-
nextExpectedStreamVersion
|
|
143
|
+
const documentsMatchingHaveCount = (expectedCount, options) => (assertOptions) => withCollection(async (collection) => {
|
|
144
|
+
assertEqual(expectedCount, (await collection.find("withId" in options ? { _id: options.withId } : options.matchingFilter)).length, "Different Documents Count than expected");
|
|
145
|
+
}, {
|
|
146
|
+
...options,
|
|
147
|
+
...assertOptions
|
|
938
148
|
});
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
149
|
+
const documentMatchingExists = (options) => (assertOptions) => withCollection(async (collection) => {
|
|
150
|
+
assertThatArray(await collection.find("withId" in options ? { _id: options.withId } : options.matchingFilter)).isNotEmpty();
|
|
151
|
+
}, {
|
|
152
|
+
...options,
|
|
153
|
+
...assertOptions
|
|
943
154
|
});
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
...existingMetadata,
|
|
950
|
-
action
|
|
951
|
-
}
|
|
952
|
-
};
|
|
953
|
-
};
|
|
954
|
-
var createWrappedInitialState = (initialState) => {
|
|
955
|
-
return () => ({
|
|
956
|
-
userState: initialState(),
|
|
957
|
-
processedInputIds: /* @__PURE__ */ new Set()
|
|
958
|
-
});
|
|
959
|
-
};
|
|
960
|
-
var createWrappedEvolve = (evolve, workflowName, separateInputInboxFromProcessing) => {
|
|
961
|
-
return (state, event2) => {
|
|
962
|
-
const metadata = event2.metadata;
|
|
963
|
-
let processedInputIds = state.processedInputIds;
|
|
964
|
-
if (metadata?.input === true && typeof metadata?.originalMessageId === "string") {
|
|
965
|
-
processedInputIds = new Set(state.processedInputIds);
|
|
966
|
-
processedInputIds.add(metadata.originalMessageId);
|
|
967
|
-
}
|
|
968
|
-
if (separateInputInboxFromProcessing && metadata?.input === true) {
|
|
969
|
-
return {
|
|
970
|
-
userState: state.userState,
|
|
971
|
-
processedInputIds
|
|
972
|
-
};
|
|
973
|
-
}
|
|
974
|
-
const eventType = event2.type;
|
|
975
|
-
const eventForEvolve = eventType.startsWith(`${workflowName}:`) ? {
|
|
976
|
-
...event2,
|
|
977
|
-
type: eventType.replace(`${workflowName}:`, "")
|
|
978
|
-
} : event2;
|
|
979
|
-
return {
|
|
980
|
-
userState: evolve(state.userState, eventForEvolve),
|
|
981
|
-
processedInputIds
|
|
982
|
-
};
|
|
983
|
-
};
|
|
984
|
-
};
|
|
985
|
-
var workflowStreamName = ({
|
|
986
|
-
workflowName,
|
|
987
|
-
workflowId
|
|
988
|
-
}) => `emt:workflow:${workflowName}:${workflowId}`;
|
|
989
|
-
var WorkflowHandler = (options) => async (store, message2, handleOptions) => asyncRetry(
|
|
990
|
-
async () => {
|
|
991
|
-
const result = await withSession2(store, async ({ eventStore }) => {
|
|
992
|
-
const {
|
|
993
|
-
workflow: { evolve, initialState, decide, name: workflowName },
|
|
994
|
-
getWorkflowId: getWorkflowId2
|
|
995
|
-
} = options;
|
|
996
|
-
const inputMessageId = (
|
|
997
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
998
|
-
("metadata" in message2 && message2.metadata?.messageId ? (
|
|
999
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
1000
|
-
message2.metadata.messageId
|
|
1001
|
-
) : void 0) ?? uuid6()
|
|
1002
|
-
);
|
|
1003
|
-
const messageWithMetadata = {
|
|
1004
|
-
...message2,
|
|
1005
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
1006
|
-
metadata: {
|
|
1007
|
-
messageId: inputMessageId,
|
|
1008
|
-
...message2.metadata
|
|
1009
|
-
}
|
|
1010
|
-
};
|
|
1011
|
-
const workflowId = getWorkflowId2(messageWithMetadata);
|
|
1012
|
-
if (!workflowId) {
|
|
1013
|
-
return emptyHandlerResult();
|
|
1014
|
-
}
|
|
1015
|
-
const streamName = options.mapWorkflowId ? options.mapWorkflowId(workflowId) : workflowStreamName({ workflowName, workflowId });
|
|
1016
|
-
const messageType = messageWithMetadata.type;
|
|
1017
|
-
const hasWorkflowPrefix = messageType.startsWith(`${workflowName}:`);
|
|
1018
|
-
if (options.separateInputInboxFromProcessing && !hasWorkflowPrefix) {
|
|
1019
|
-
const inputMetadata2 = createInputMetadata(
|
|
1020
|
-
inputMessageId,
|
|
1021
|
-
"InitiatedBy"
|
|
1022
|
-
);
|
|
1023
|
-
const inputToStore2 = {
|
|
1024
|
-
type: `${workflowName}:${messageWithMetadata.type}`,
|
|
1025
|
-
data: messageWithMetadata.data,
|
|
1026
|
-
kind: messageWithMetadata.kind,
|
|
1027
|
-
metadata: inputMetadata2
|
|
1028
|
-
};
|
|
1029
|
-
const appendResult2 = await eventStore.appendToStream(
|
|
1030
|
-
streamName,
|
|
1031
|
-
[inputToStore2],
|
|
1032
|
-
{
|
|
1033
|
-
...handleOptions,
|
|
1034
|
-
expectedStreamVersion: handleOptions?.expectedStreamVersion ?? NO_CONCURRENCY_CHECK
|
|
1035
|
-
}
|
|
1036
|
-
);
|
|
1037
|
-
return {
|
|
1038
|
-
...appendResult2,
|
|
1039
|
-
newMessages: []
|
|
1040
|
-
};
|
|
1041
|
-
}
|
|
1042
|
-
const wrappedInitialState = createWrappedInitialState(initialState);
|
|
1043
|
-
const wrappedEvolve = createWrappedEvolve(
|
|
1044
|
-
evolve,
|
|
1045
|
-
workflowName,
|
|
1046
|
-
options.separateInputInboxFromProcessing ?? false
|
|
1047
|
-
);
|
|
1048
|
-
const aggregationResult = await eventStore.aggregateStream(streamName, {
|
|
1049
|
-
evolve: wrappedEvolve,
|
|
1050
|
-
initialState: wrappedInitialState,
|
|
1051
|
-
read: {
|
|
1052
|
-
...handleOptions,
|
|
1053
|
-
// expected stream version is passed to fail fast
|
|
1054
|
-
// if stream is in the wrong state
|
|
1055
|
-
expectedStreamVersion: handleOptions?.expectedStreamVersion ?? NO_CONCURRENCY_CHECK
|
|
1056
|
-
}
|
|
1057
|
-
});
|
|
1058
|
-
const { currentStreamVersion } = aggregationResult;
|
|
1059
|
-
const { userState: state, processedInputIds } = aggregationResult.state;
|
|
1060
|
-
if (processedInputIds.has(inputMessageId)) {
|
|
1061
|
-
return emptyHandlerResult(currentStreamVersion);
|
|
1062
|
-
}
|
|
1063
|
-
const messageForDecide = hasWorkflowPrefix ? {
|
|
1064
|
-
...messageWithMetadata,
|
|
1065
|
-
type: messageType.replace(`${workflowName}:`, "")
|
|
1066
|
-
} : messageWithMetadata;
|
|
1067
|
-
const result2 = decide(messageForDecide, state);
|
|
1068
|
-
const inputMetadata = createInputMetadata(
|
|
1069
|
-
inputMessageId,
|
|
1070
|
-
aggregationResult.streamExists ? "Received" : "InitiatedBy"
|
|
1071
|
-
);
|
|
1072
|
-
const inputToStore = {
|
|
1073
|
-
type: `${workflowName}:${messageWithMetadata.type}`,
|
|
1074
|
-
data: messageWithMetadata.data,
|
|
1075
|
-
kind: messageWithMetadata.kind,
|
|
1076
|
-
metadata: inputMetadata
|
|
1077
|
-
};
|
|
1078
|
-
const outputMessages = (Array.isArray(result2) ? result2 : [result2]).filter((msg) => msg !== void 0 && msg !== null);
|
|
1079
|
-
const outputCommandTypes = options.outputs?.commands ?? [];
|
|
1080
|
-
const taggedOutputMessages = outputMessages.map((msg) => {
|
|
1081
|
-
const action = outputCommandTypes.includes(
|
|
1082
|
-
msg.type
|
|
1083
|
-
) ? "Sent" : "Published";
|
|
1084
|
-
return tagOutputMessage(msg, action);
|
|
1085
|
-
});
|
|
1086
|
-
const messagesToAppend = options.separateInputInboxFromProcessing && hasWorkflowPrefix ? [...taggedOutputMessages] : [inputToStore, ...taggedOutputMessages];
|
|
1087
|
-
if (messagesToAppend.length === 0) {
|
|
1088
|
-
return emptyHandlerResult(currentStreamVersion);
|
|
1089
|
-
}
|
|
1090
|
-
const expectedStreamVersion = handleOptions?.expectedStreamVersion ?? (aggregationResult.streamExists ? currentStreamVersion : STREAM_DOES_NOT_EXIST);
|
|
1091
|
-
const appendResult = await eventStore.appendToStream(
|
|
1092
|
-
streamName,
|
|
1093
|
-
// TODO: Fix this cast
|
|
1094
|
-
messagesToAppend,
|
|
1095
|
-
{
|
|
1096
|
-
...handleOptions,
|
|
1097
|
-
expectedStreamVersion
|
|
1098
|
-
}
|
|
1099
|
-
);
|
|
1100
|
-
return {
|
|
1101
|
-
...appendResult,
|
|
1102
|
-
newMessages: outputMessages
|
|
1103
|
-
};
|
|
1104
|
-
});
|
|
1105
|
-
return result;
|
|
1106
|
-
},
|
|
1107
|
-
fromWorkflowHandlerRetryOptions(
|
|
1108
|
-
handleOptions && "retry" in handleOptions ? handleOptions.retry : options.retry
|
|
1109
|
-
)
|
|
1110
|
-
);
|
|
1111
|
-
var withSession2 = (eventStore, callback) => {
|
|
1112
|
-
const sessionFactory = canCreateEventStoreSession(eventStore) ? eventStore : nulloSessionFactory(eventStore);
|
|
1113
|
-
return sessionFactory.withSession(callback);
|
|
1114
|
-
};
|
|
1115
|
-
var getWorkflowId = (options) => `emt:processor:workflow:${options.workflowName}`;
|
|
1116
|
-
var workflowProcessor = (options) => {
|
|
1117
|
-
const { workflow, ...rest } = options;
|
|
1118
|
-
const inputs = [...options.inputs.commands, ...options.inputs.events];
|
|
1119
|
-
let canHandle = inputs;
|
|
1120
|
-
if (options.separateInputInboxFromProcessing)
|
|
1121
|
-
canHandle = [
|
|
1122
|
-
...canHandle,
|
|
1123
|
-
...options.inputs.commands.map((t) => `${workflow.name}:${t}`),
|
|
1124
|
-
...options.inputs.events.map((t) => `${workflow.name}:${t}`)
|
|
1125
|
-
];
|
|
1126
|
-
if (options.outputHandler)
|
|
1127
|
-
canHandle = [...canHandle, ...options.outputHandler.canHandle];
|
|
1128
|
-
const handle = WorkflowHandler(options);
|
|
1129
|
-
return reactor({
|
|
1130
|
-
...rest,
|
|
1131
|
-
processorId: options.processorId ?? getWorkflowId({ workflowName: workflow.name }),
|
|
1132
|
-
canHandle,
|
|
1133
|
-
type: MessageProcessorType.PROJECTOR,
|
|
1134
|
-
eachMessage: async (message2, context) => {
|
|
1135
|
-
const messageType = message2.type;
|
|
1136
|
-
const metadata = message2.metadata;
|
|
1137
|
-
const isInput = metadata?.input === true;
|
|
1138
|
-
if (isInput || inputs.includes(messageType)) {
|
|
1139
|
-
const result = await handle(
|
|
1140
|
-
context.connection.messageStore,
|
|
1141
|
-
message2,
|
|
1142
|
-
context
|
|
1143
|
-
);
|
|
1144
|
-
if (options.stopAfter && result.newMessages.length > 0) {
|
|
1145
|
-
for (const outputMessage of result.newMessages) {
|
|
1146
|
-
if (options.stopAfter(
|
|
1147
|
-
outputMessage
|
|
1148
|
-
)) {
|
|
1149
|
-
return { type: "STOP", reason: "Stop condition reached" };
|
|
1150
|
-
}
|
|
1151
|
-
}
|
|
1152
|
-
}
|
|
1153
|
-
return;
|
|
1154
|
-
}
|
|
1155
|
-
if (options.outputHandler?.canHandle.includes(messageType) === true) {
|
|
1156
|
-
const recordedMessage = message2;
|
|
1157
|
-
const handledOutputMessages = options.outputHandler.eachBatch ? await options.outputHandler.eachBatch([recordedMessage], context) : await options.outputHandler.eachMessage(recordedMessage, context);
|
|
1158
|
-
if (handledOutputMessages instanceof EmmettError) {
|
|
1159
|
-
return {
|
|
1160
|
-
type: "STOP",
|
|
1161
|
-
reason: "Routing error",
|
|
1162
|
-
error: handledOutputMessages
|
|
1163
|
-
};
|
|
1164
|
-
}
|
|
1165
|
-
const messagesToAppend = Array.isArray(handledOutputMessages) ? handledOutputMessages : handledOutputMessages ? [handledOutputMessages] : [];
|
|
1166
|
-
if (messagesToAppend.length === 0) {
|
|
1167
|
-
return;
|
|
1168
|
-
}
|
|
1169
|
-
const workflowId = options.getWorkflowId(
|
|
1170
|
-
message2
|
|
1171
|
-
);
|
|
1172
|
-
if (!workflowId) return;
|
|
1173
|
-
const streamName = options.mapWorkflowId ? options.mapWorkflowId(workflowId) : workflowStreamName({
|
|
1174
|
-
workflowName: workflow.name,
|
|
1175
|
-
workflowId
|
|
1176
|
-
});
|
|
1177
|
-
await context.connection.messageStore.appendToStream(
|
|
1178
|
-
streamName,
|
|
1179
|
-
messagesToAppend
|
|
1180
|
-
);
|
|
1181
|
-
return;
|
|
1182
|
-
}
|
|
1183
|
-
return;
|
|
1184
|
-
}
|
|
1185
|
-
});
|
|
1186
|
-
};
|
|
1187
|
-
|
|
1188
|
-
// src/eventStore/projections/pongo/pongoProjectionSpec.ts
|
|
1189
|
-
import {
|
|
1190
|
-
pongoClient as pongoClient2
|
|
1191
|
-
} from "@event-driven-io/pongo";
|
|
1192
|
-
var withCollection = async (handle, options) => {
|
|
1193
|
-
const { connection, inDatabase, inCollection } = options;
|
|
1194
|
-
const driver = await pongoDriverRegistry.tryResolve(
|
|
1195
|
-
connection.driverType
|
|
1196
|
-
);
|
|
1197
|
-
const pongo = pongoClient2({
|
|
1198
|
-
connectionOptions: { connection },
|
|
1199
|
-
driver
|
|
1200
|
-
});
|
|
1201
|
-
try {
|
|
1202
|
-
const collection = pongo.db(inDatabase).collection(inCollection);
|
|
1203
|
-
return handle(collection);
|
|
1204
|
-
} finally {
|
|
1205
|
-
await pongo.close();
|
|
1206
|
-
}
|
|
1207
|
-
};
|
|
1208
|
-
var withoutIdAndVersion = (doc) => {
|
|
1209
|
-
const { _id, _version, ...without } = doc;
|
|
1210
|
-
return without;
|
|
1211
|
-
};
|
|
1212
|
-
var assertDocumentsEqual = (actual, expected) => {
|
|
1213
|
-
if ("_id" in expected)
|
|
1214
|
-
assertEqual(
|
|
1215
|
-
expected._id,
|
|
1216
|
-
actual._id,
|
|
1217
|
-
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
|
1218
|
-
`Document ids are not matching! Expected: ${expected._id}, Actual: ${actual._id}`
|
|
1219
|
-
);
|
|
1220
|
-
return assertDeepEqual(
|
|
1221
|
-
withoutIdAndVersion(actual),
|
|
1222
|
-
withoutIdAndVersion(expected)
|
|
1223
|
-
);
|
|
1224
|
-
};
|
|
1225
|
-
var documentExists = (document, options) => (assertOptions) => withCollection(
|
|
1226
|
-
async (collection) => {
|
|
1227
|
-
const result = await collection.findOne(
|
|
1228
|
-
"withId" in options ? { _id: options.withId } : options.matchingFilter
|
|
1229
|
-
);
|
|
1230
|
-
assertIsNotNull(result);
|
|
1231
|
-
assertDocumentsEqual(result, document);
|
|
1232
|
-
},
|
|
1233
|
-
{ ...options, ...assertOptions }
|
|
1234
|
-
);
|
|
1235
|
-
var documentsAreTheSame = (documents, options) => (assertOptions) => withCollection(
|
|
1236
|
-
async (collection) => {
|
|
1237
|
-
const result = await collection.find(
|
|
1238
|
-
"withId" in options ? { _id: options.withId } : options.matchingFilter
|
|
1239
|
-
);
|
|
1240
|
-
assertEqual(
|
|
1241
|
-
documents.length,
|
|
1242
|
-
result.length,
|
|
1243
|
-
"Different Documents Count than expected"
|
|
1244
|
-
);
|
|
1245
|
-
for (let i = 0; i < documents.length; i++) {
|
|
1246
|
-
assertThatArray(result).contains(documents[i]);
|
|
1247
|
-
}
|
|
1248
|
-
},
|
|
1249
|
-
{ ...options, ...assertOptions }
|
|
1250
|
-
);
|
|
1251
|
-
var documentsMatchingHaveCount = (expectedCount, options) => (assertOptions) => withCollection(
|
|
1252
|
-
async (collection) => {
|
|
1253
|
-
const result = await collection.find(
|
|
1254
|
-
"withId" in options ? { _id: options.withId } : options.matchingFilter
|
|
1255
|
-
);
|
|
1256
|
-
assertEqual(
|
|
1257
|
-
expectedCount,
|
|
1258
|
-
result.length,
|
|
1259
|
-
"Different Documents Count than expected"
|
|
1260
|
-
);
|
|
1261
|
-
},
|
|
1262
|
-
{ ...options, ...assertOptions }
|
|
1263
|
-
);
|
|
1264
|
-
var documentMatchingExists = (options) => (assertOptions) => withCollection(
|
|
1265
|
-
async (collection) => {
|
|
1266
|
-
const result = await collection.find(
|
|
1267
|
-
"withId" in options ? { _id: options.withId } : options.matchingFilter
|
|
1268
|
-
);
|
|
1269
|
-
assertThatArray(result).isNotEmpty();
|
|
1270
|
-
},
|
|
1271
|
-
{ ...options, ...assertOptions }
|
|
1272
|
-
);
|
|
1273
|
-
var documentDoesNotExist = (options) => (assertOptions) => withCollection(
|
|
1274
|
-
async (collection) => {
|
|
1275
|
-
const result = await collection.findOne(
|
|
1276
|
-
"withId" in options ? { _id: options.withId } : options.matchingFilter
|
|
1277
|
-
);
|
|
1278
|
-
assertIsNull(result);
|
|
1279
|
-
},
|
|
1280
|
-
{ ...options, ...assertOptions }
|
|
1281
|
-
);
|
|
1282
|
-
var expectPongoDocuments = {
|
|
1283
|
-
fromCollection: (collectionName) => {
|
|
1284
|
-
return {
|
|
1285
|
-
withId: (id) => {
|
|
1286
|
-
return {
|
|
1287
|
-
toBeEqual: (document) => documentExists(document, {
|
|
1288
|
-
withId: id,
|
|
1289
|
-
inCollection: collectionName
|
|
1290
|
-
}),
|
|
1291
|
-
toExist: () => documentMatchingExists({
|
|
1292
|
-
withId: id,
|
|
1293
|
-
inCollection: collectionName
|
|
1294
|
-
}),
|
|
1295
|
-
notToExist: () => documentDoesNotExist({
|
|
1296
|
-
withId: id,
|
|
1297
|
-
inCollection: collectionName
|
|
1298
|
-
})
|
|
1299
|
-
};
|
|
1300
|
-
},
|
|
1301
|
-
matching: (filter) => {
|
|
1302
|
-
return {
|
|
1303
|
-
toBeTheSame: (documents) => documentsAreTheSame(documents, {
|
|
1304
|
-
matchingFilter: filter,
|
|
1305
|
-
inCollection: collectionName
|
|
1306
|
-
}),
|
|
1307
|
-
toHaveCount: (expectedCount) => documentsMatchingHaveCount(expectedCount, {
|
|
1308
|
-
matchingFilter: filter,
|
|
1309
|
-
inCollection: collectionName
|
|
1310
|
-
}),
|
|
1311
|
-
toExist: () => documentMatchingExists({
|
|
1312
|
-
matchingFilter: filter,
|
|
1313
|
-
inCollection: collectionName
|
|
1314
|
-
}),
|
|
1315
|
-
notToExist: () => documentDoesNotExist({
|
|
1316
|
-
matchingFilter: filter,
|
|
1317
|
-
inCollection: collectionName
|
|
1318
|
-
})
|
|
1319
|
-
};
|
|
1320
|
-
}
|
|
1321
|
-
};
|
|
1322
|
-
}
|
|
1323
|
-
};
|
|
1324
|
-
|
|
1325
|
-
// src/eventStore/projections/sqliteProjection.ts
|
|
1326
|
-
var handleProjections = async (options) => {
|
|
1327
|
-
const {
|
|
1328
|
-
projections: allProjections,
|
|
1329
|
-
events,
|
|
1330
|
-
connection,
|
|
1331
|
-
execute,
|
|
1332
|
-
driverType
|
|
1333
|
-
} = options;
|
|
1334
|
-
const eventTypes = events.map((e) => e.type);
|
|
1335
|
-
for (const projection2 of allProjections) {
|
|
1336
|
-
if (!projection2.canHandle.some((type) => eventTypes.includes(type))) {
|
|
1337
|
-
continue;
|
|
1338
|
-
}
|
|
1339
|
-
await projection2.handle(events, {
|
|
1340
|
-
connection,
|
|
1341
|
-
execute,
|
|
1342
|
-
driverType
|
|
1343
|
-
});
|
|
1344
|
-
}
|
|
1345
|
-
};
|
|
1346
|
-
var sqliteProjection = (definition) => projection(definition);
|
|
1347
|
-
var sqliteRawBatchSQLProjection = (options) => sqliteProjection({
|
|
1348
|
-
name: options.name,
|
|
1349
|
-
kind: options.kind ?? "emt:projections:sqlite:raw_sql:batch",
|
|
1350
|
-
version: options.version,
|
|
1351
|
-
canHandle: options.canHandle,
|
|
1352
|
-
eventsOptions: options.eventsOptions,
|
|
1353
|
-
handle: async (events, context) => {
|
|
1354
|
-
const sqls = await options.evolve(events, context);
|
|
1355
|
-
await context.execute.batchCommand(sqls);
|
|
1356
|
-
},
|
|
1357
|
-
init: async (initOptions) => {
|
|
1358
|
-
const initSQL = options.init ? await options.init(initOptions) : void 0;
|
|
1359
|
-
if (initSQL) {
|
|
1360
|
-
if (Array.isArray(initSQL)) {
|
|
1361
|
-
await initOptions.context.execute.batchCommand(initSQL);
|
|
1362
|
-
} else {
|
|
1363
|
-
await initOptions.context.execute.command(initSQL);
|
|
1364
|
-
}
|
|
1365
|
-
}
|
|
1366
|
-
}
|
|
155
|
+
const documentDoesNotExist = (options) => (assertOptions) => withCollection(async (collection) => {
|
|
156
|
+
assertIsNull(await collection.findOne("withId" in options ? { _id: options.withId } : options.matchingFilter));
|
|
157
|
+
}, {
|
|
158
|
+
...options,
|
|
159
|
+
...assertOptions
|
|
1367
160
|
});
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
161
|
+
const expectPongoDocuments = { fromCollection: (collectionName) => {
|
|
162
|
+
return {
|
|
163
|
+
withId: (id) => {
|
|
164
|
+
return {
|
|
165
|
+
toBeEqual: (document) => documentExists(document, {
|
|
166
|
+
withId: id,
|
|
167
|
+
inCollection: collectionName
|
|
168
|
+
}),
|
|
169
|
+
toExist: () => documentMatchingExists({
|
|
170
|
+
withId: id,
|
|
171
|
+
inCollection: collectionName
|
|
172
|
+
}),
|
|
173
|
+
notToExist: () => documentDoesNotExist({
|
|
174
|
+
withId: id,
|
|
175
|
+
inCollection: collectionName
|
|
176
|
+
})
|
|
177
|
+
};
|
|
178
|
+
},
|
|
179
|
+
matching: (filter) => {
|
|
180
|
+
return {
|
|
181
|
+
toBeTheSame: (documents) => documentsAreTheSame(documents, {
|
|
182
|
+
matchingFilter: filter,
|
|
183
|
+
inCollection: collectionName
|
|
184
|
+
}),
|
|
185
|
+
toHaveCount: (expectedCount) => documentsMatchingHaveCount(expectedCount, {
|
|
186
|
+
matchingFilter: filter,
|
|
187
|
+
inCollection: collectionName
|
|
188
|
+
}),
|
|
189
|
+
toExist: () => documentMatchingExists({
|
|
190
|
+
matchingFilter: filter,
|
|
191
|
+
inCollection: collectionName
|
|
192
|
+
}),
|
|
193
|
+
notToExist: () => documentDoesNotExist({
|
|
194
|
+
matchingFilter: filter,
|
|
195
|
+
inCollection: collectionName
|
|
196
|
+
})
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
} };
|
|
1387
201
|
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
registrationType: "async",
|
|
1435
|
-
status: "active",
|
|
1436
|
-
context: {
|
|
1437
|
-
execute: connection.execute,
|
|
1438
|
-
connection,
|
|
1439
|
-
driverType
|
|
1440
|
-
},
|
|
1441
|
-
version: projection2.version ?? 1
|
|
1442
|
-
});
|
|
1443
|
-
wasInitialized = true;
|
|
1444
|
-
}
|
|
1445
|
-
await connection.withTransaction(
|
|
1446
|
-
() => handleProjections({
|
|
1447
|
-
events: allEvents,
|
|
1448
|
-
projections: [projection2],
|
|
1449
|
-
execute: connection.execute,
|
|
1450
|
-
connection,
|
|
1451
|
-
driverType
|
|
1452
|
-
})
|
|
1453
|
-
);
|
|
1454
|
-
};
|
|
1455
|
-
return {
|
|
1456
|
-
then: (assert, message) => pool.withConnection(async (connection) => {
|
|
1457
|
-
await run(connection);
|
|
1458
|
-
const succeeded = await assert({
|
|
1459
|
-
connection
|
|
1460
|
-
});
|
|
1461
|
-
if (succeeded !== void 0 && succeeded === false)
|
|
1462
|
-
assertFails(
|
|
1463
|
-
message ?? "Projection specification didn't match the criteria"
|
|
1464
|
-
);
|
|
1465
|
-
}),
|
|
1466
|
-
thenThrows: (...args) => pool.withConnection(async (connection) => {
|
|
1467
|
-
try {
|
|
1468
|
-
await run(connection);
|
|
1469
|
-
throw new AssertionError(
|
|
1470
|
-
"Handler did not fail as expected"
|
|
1471
|
-
);
|
|
1472
|
-
} catch (error) {
|
|
1473
|
-
if (error instanceof AssertionError) throw error;
|
|
1474
|
-
if (args.length === 0) return;
|
|
1475
|
-
if (!isErrorConstructor(args[0])) {
|
|
1476
|
-
assertTrue(
|
|
1477
|
-
args[0](error),
|
|
1478
|
-
`Error didn't match the error condition: ${error?.toString()}`
|
|
1479
|
-
);
|
|
1480
|
-
return;
|
|
1481
|
-
}
|
|
1482
|
-
assertTrue(
|
|
1483
|
-
error instanceof args[0],
|
|
1484
|
-
`Caught error is not an instance of the expected type: ${error?.toString()}`
|
|
1485
|
-
);
|
|
1486
|
-
if (args[1]) {
|
|
1487
|
-
assertTrue(
|
|
1488
|
-
args[1](error),
|
|
1489
|
-
`Error didn't match the error condition: ${error?.toString()}`
|
|
1490
|
-
);
|
|
1491
|
-
}
|
|
1492
|
-
}
|
|
1493
|
-
})
|
|
1494
|
-
};
|
|
1495
|
-
}
|
|
1496
|
-
};
|
|
1497
|
-
};
|
|
1498
|
-
}
|
|
1499
|
-
}
|
|
1500
|
-
};
|
|
1501
|
-
var eventInStream = (streamName, event) => {
|
|
1502
|
-
return {
|
|
1503
|
-
...event,
|
|
1504
|
-
metadata: {
|
|
1505
|
-
...event.metadata ?? {},
|
|
1506
|
-
streamName: event.metadata?.streamName ?? streamName
|
|
1507
|
-
}
|
|
1508
|
-
};
|
|
1509
|
-
};
|
|
1510
|
-
var eventsInStream = (streamName, events) => {
|
|
1511
|
-
return events.map((e) => eventInStream(streamName, e));
|
|
1512
|
-
};
|
|
1513
|
-
var newEventsInStream = eventsInStream;
|
|
1514
|
-
var assertSQLQueryResultMatches = (sql, rows) => async ({
|
|
1515
|
-
connection
|
|
1516
|
-
}) => {
|
|
1517
|
-
const result = await connection.execute.query(sql);
|
|
1518
|
-
assertThatArray(rows).containsExactlyInAnyOrder(result.rows);
|
|
1519
|
-
};
|
|
1520
|
-
var expectSQL = {
|
|
1521
|
-
query: (sql) => ({
|
|
1522
|
-
resultRows: {
|
|
1523
|
-
toBeTheSame: (rows) => assertSQLQueryResultMatches(sql, rows)
|
|
1524
|
-
}
|
|
1525
|
-
})
|
|
202
|
+
//#endregion
|
|
203
|
+
//#region src/eventStore/projections/sqliteProjection.ts
|
|
204
|
+
const handleProjections = async (options) => {
|
|
205
|
+
const { projections: allProjections, events, connection, execute, driverType } = options;
|
|
206
|
+
const eventTypes = events.map((e) => e.type);
|
|
207
|
+
for (const projection of allProjections) {
|
|
208
|
+
if (!projection.canHandle.some((type) => eventTypes.includes(type))) continue;
|
|
209
|
+
await projection.handle(events, {
|
|
210
|
+
connection,
|
|
211
|
+
execute,
|
|
212
|
+
driverType
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
const sqliteProjection = (definition) => projection(definition);
|
|
217
|
+
const sqliteRawBatchSQLProjection = (options) => sqliteProjection({
|
|
218
|
+
name: options.name,
|
|
219
|
+
kind: options.kind ?? "emt:projections:sqlite:raw_sql:batch",
|
|
220
|
+
version: options.version,
|
|
221
|
+
canHandle: options.canHandle,
|
|
222
|
+
eventsOptions: options.eventsOptions,
|
|
223
|
+
handle: async (events, context) => {
|
|
224
|
+
const sqls = await options.evolve(events, context);
|
|
225
|
+
await context.execute.batchCommand(sqls);
|
|
226
|
+
},
|
|
227
|
+
init: async (initOptions) => {
|
|
228
|
+
const initSQL = options.init ? await options.init(initOptions) : void 0;
|
|
229
|
+
if (initSQL) if (Array.isArray(initSQL)) await initOptions.context.execute.batchCommand(initSQL);
|
|
230
|
+
else await initOptions.context.execute.command(initSQL);
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
const sqliteRawSQLProjection = (options) => {
|
|
234
|
+
const { evolve, kind, ...rest } = options;
|
|
235
|
+
return sqliteRawBatchSQLProjection({
|
|
236
|
+
kind: kind ?? "emt:projections:sqlite:raw:_sql:single",
|
|
237
|
+
...rest,
|
|
238
|
+
evolve: async (events, context) => {
|
|
239
|
+
const sqls = [];
|
|
240
|
+
for (const event of events) {
|
|
241
|
+
const pendingSqls = await evolve(event, context);
|
|
242
|
+
if (Array.isArray(pendingSqls)) sqls.push(...pendingSqls);
|
|
243
|
+
else sqls.push(pendingSqls);
|
|
244
|
+
}
|
|
245
|
+
return sqls;
|
|
246
|
+
}
|
|
247
|
+
});
|
|
1526
248
|
};
|
|
1527
249
|
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
250
|
+
//#endregion
|
|
251
|
+
//#region src/eventStore/projections/sqliteProjectionSpec.ts
|
|
252
|
+
const SQLiteProjectionSpec = { for: (options) => {
|
|
253
|
+
{
|
|
254
|
+
const driverType = options.driver.driverType;
|
|
255
|
+
const pool = options.pool ?? dumbo({
|
|
256
|
+
serialization: options.serialization,
|
|
257
|
+
transactionOptions: {
|
|
258
|
+
allowNestedTransactions: true,
|
|
259
|
+
mode: "session_based"
|
|
260
|
+
},
|
|
261
|
+
...options.driver.mapToDumboOptions(options)
|
|
262
|
+
});
|
|
263
|
+
const projection = options.projection;
|
|
264
|
+
let wasInitialized = false;
|
|
265
|
+
return (givenEvents) => {
|
|
266
|
+
return { when: (events, options) => {
|
|
267
|
+
const allEvents = [];
|
|
268
|
+
const run = async (connection) => {
|
|
269
|
+
let globalPosition = 0n;
|
|
270
|
+
const numberOfTimes = options?.numberOfTimes ?? 1;
|
|
271
|
+
for (const event of [...givenEvents, ...Array.from({ length: numberOfTimes }).flatMap(() => events)]) {
|
|
272
|
+
const metadata = {
|
|
273
|
+
checkpoint: bigIntProcessorCheckpoint(++globalPosition),
|
|
274
|
+
globalPosition,
|
|
275
|
+
streamPosition: globalPosition,
|
|
276
|
+
streamName: `test-${v4()}`,
|
|
277
|
+
messageId: v4()
|
|
278
|
+
};
|
|
279
|
+
allEvents.push({
|
|
280
|
+
...event,
|
|
281
|
+
kind: "Event",
|
|
282
|
+
metadata: {
|
|
283
|
+
...metadata,
|
|
284
|
+
..."metadata" in event ? event.metadata ?? {} : {}
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
if (!wasInitialized && projection.init) {
|
|
289
|
+
await projection.init({
|
|
290
|
+
registrationType: "async",
|
|
291
|
+
status: "active",
|
|
292
|
+
context: {
|
|
293
|
+
execute: connection.execute,
|
|
294
|
+
connection,
|
|
295
|
+
driverType
|
|
296
|
+
},
|
|
297
|
+
version: projection.version ?? 1
|
|
298
|
+
});
|
|
299
|
+
wasInitialized = true;
|
|
300
|
+
}
|
|
301
|
+
await connection.withTransaction(() => handleProjections({
|
|
302
|
+
events: allEvents,
|
|
303
|
+
projections: [projection],
|
|
304
|
+
execute: connection.execute,
|
|
305
|
+
connection,
|
|
306
|
+
driverType
|
|
307
|
+
}));
|
|
308
|
+
};
|
|
309
|
+
return {
|
|
310
|
+
then: (assert, message) => pool.withConnection(async (connection) => {
|
|
311
|
+
await run(connection);
|
|
312
|
+
const succeeded = await assert({ connection });
|
|
313
|
+
if (succeeded !== void 0 && succeeded === false) assertFails(message ?? "Projection specification didn't match the criteria");
|
|
314
|
+
}),
|
|
315
|
+
thenThrows: (...args) => pool.withConnection(async (connection) => {
|
|
316
|
+
try {
|
|
317
|
+
await run(connection);
|
|
318
|
+
throw new AssertionError("Handler did not fail as expected");
|
|
319
|
+
} catch (error) {
|
|
320
|
+
if (error instanceof AssertionError) throw error;
|
|
321
|
+
if (args.length === 0) return;
|
|
322
|
+
if (!isErrorConstructor(args[0])) {
|
|
323
|
+
assertTrue(args[0](error), `Error didn't match the error condition: ${error?.toString()}`);
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
assertTrue(error instanceof args[0], `Caught error is not an instance of the expected type: ${error?.toString()}`);
|
|
327
|
+
if (args[1]) assertTrue(args[1](error), `Error didn't match the error condition: ${error?.toString()}`);
|
|
328
|
+
}
|
|
329
|
+
})
|
|
330
|
+
};
|
|
331
|
+
} };
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
} };
|
|
335
|
+
const eventInStream = (streamName, event) => {
|
|
336
|
+
return {
|
|
337
|
+
...event,
|
|
338
|
+
metadata: {
|
|
339
|
+
...event.metadata ?? {},
|
|
340
|
+
streamName: event.metadata?.streamName ?? streamName
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
};
|
|
344
|
+
const eventsInStream = (streamName, events) => {
|
|
345
|
+
return events.map((e) => eventInStream(streamName, e));
|
|
346
|
+
};
|
|
347
|
+
const newEventsInStream = eventsInStream;
|
|
348
|
+
const assertSQLQueryResultMatches = (sql, rows) => async ({ connection }) => {
|
|
349
|
+
const result = await connection.execute.query(sql);
|
|
350
|
+
assertThatArray(rows).containsExactlyInAnyOrder(result.rows);
|
|
351
|
+
};
|
|
352
|
+
const expectSQL = { query: (sql) => ({ resultRows: { toBeTheSame: (rows) => assertSQLQueryResultMatches(sql, rows) } }) };
|
|
1537
353
|
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
};
|
|
1566
|
-
var processorsTable = {
|
|
1567
|
-
name: `${emmettPrefix2}_processors`
|
|
1568
|
-
};
|
|
1569
|
-
var projectionsTable = {
|
|
1570
|
-
name: `${emmettPrefix2}_projections`
|
|
1571
|
-
};
|
|
354
|
+
//#endregion
|
|
355
|
+
//#region src/eventStore/schema/typing.ts
|
|
356
|
+
const emmettPrefix = "emt";
|
|
357
|
+
const globalTag = "global";
|
|
358
|
+
const defaultTag = `${"emt"}:default`;
|
|
359
|
+
const unknownTag = `${"emt"}:unknown`;
|
|
360
|
+
const globalNames = { module: `${"emt"}:module:${globalTag}` };
|
|
361
|
+
const columns = {
|
|
362
|
+
partition: { name: "partition" },
|
|
363
|
+
isArchived: { name: "is_archived" }
|
|
364
|
+
};
|
|
365
|
+
const streamsTable = {
|
|
366
|
+
name: `${"emt"}_streams`,
|
|
367
|
+
columns: {
|
|
368
|
+
partition: columns.partition,
|
|
369
|
+
isArchived: columns.isArchived
|
|
370
|
+
}
|
|
371
|
+
};
|
|
372
|
+
const messagesTable = {
|
|
373
|
+
name: `${"emt"}_messages`,
|
|
374
|
+
columns: {
|
|
375
|
+
partition: columns.partition,
|
|
376
|
+
isArchived: columns.isArchived
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
const processorsTable = { name: `${"emt"}_processors` };
|
|
380
|
+
const projectionsTable = { name: `${"emt"}_projections` };
|
|
1572
381
|
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
errorType: BatchCommandNoChangesError.ErrorType
|
|
1617
|
-
})) {
|
|
1618
|
-
return { success: false };
|
|
1619
|
-
}
|
|
1620
|
-
throw err;
|
|
1621
|
-
}
|
|
1622
|
-
};
|
|
1623
|
-
var toExpectedVersion = (expected) => {
|
|
1624
|
-
if (expected === void 0) return null;
|
|
1625
|
-
if (expected === NO_CONCURRENCY_CHECK) return null;
|
|
1626
|
-
if (expected == STREAM_DOES_NOT_EXIST) return null;
|
|
1627
|
-
if (expected == STREAM_EXISTS) return null;
|
|
1628
|
-
return expected;
|
|
1629
|
-
};
|
|
1630
|
-
var appendToStreamRaw = async (execute, streamId, streamType, messages, options) => {
|
|
1631
|
-
let expectedStreamVersion = options?.expectedStreamVersion ?? null;
|
|
1632
|
-
const currentStreamVersion = await getLastStreamPosition(
|
|
1633
|
-
execute,
|
|
1634
|
-
streamId,
|
|
1635
|
-
expectedStreamVersion
|
|
1636
|
-
);
|
|
1637
|
-
expectedStreamVersion ??= currentStreamVersion ?? 0n;
|
|
1638
|
-
if (expectedStreamVersion !== currentStreamVersion) {
|
|
1639
|
-
throw new ExpectedVersionConflictError(
|
|
1640
|
-
currentStreamVersion,
|
|
1641
|
-
expectedStreamVersion
|
|
1642
|
-
);
|
|
1643
|
-
}
|
|
1644
|
-
const streamSQL = expectedStreamVersion === 0n ? SQL`INSERT INTO ${identifier(streamsTable.name)}
|
|
382
|
+
//#endregion
|
|
383
|
+
//#region src/eventStore/schema/appendToStream.ts
|
|
384
|
+
const { identifier: identifier$7, merge } = SQL;
|
|
385
|
+
const appendToStream = async (connection, streamName, streamType, messages, options) => {
|
|
386
|
+
if (messages.length === 0) return { success: false };
|
|
387
|
+
const expectedStreamVersion = toExpectedVersion(options?.expectedStreamVersion);
|
|
388
|
+
const messagesToAppend = messages.map((m, i) => ({
|
|
389
|
+
...m,
|
|
390
|
+
kind: m.kind ?? "Event",
|
|
391
|
+
metadata: {
|
|
392
|
+
streamName,
|
|
393
|
+
messageId: v4(),
|
|
394
|
+
streamPosition: BigInt(i + 1),
|
|
395
|
+
..."metadata" in m ? m.metadata ?? {} : {}
|
|
396
|
+
}
|
|
397
|
+
}));
|
|
398
|
+
try {
|
|
399
|
+
return await connection.withTransaction(async (transaction) => {
|
|
400
|
+
const result = await appendToStreamRaw(transaction.execute, streamName, streamType, downcastRecordedMessages(messagesToAppend, options?.schema?.versioning), { expectedStreamVersion });
|
|
401
|
+
if (options?.onBeforeCommit) await options.onBeforeCommit(messagesToAppend, { connection });
|
|
402
|
+
return {
|
|
403
|
+
success: true,
|
|
404
|
+
result
|
|
405
|
+
};
|
|
406
|
+
});
|
|
407
|
+
} catch (err) {
|
|
408
|
+
if (isExpectedVersionConflictError(err) || DumboError.isInstanceOf(err, { errorType: UniqueConstraintError.ErrorType }) || DumboError.isInstanceOf(err, { errorType: BatchCommandNoChangesError.ErrorType })) return { success: false };
|
|
409
|
+
throw err;
|
|
410
|
+
}
|
|
411
|
+
};
|
|
412
|
+
const toExpectedVersion = (expected) => {
|
|
413
|
+
if (expected === void 0) return null;
|
|
414
|
+
if (expected === NO_CONCURRENCY_CHECK) return null;
|
|
415
|
+
if (expected == STREAM_DOES_NOT_EXIST) return null;
|
|
416
|
+
if (expected == STREAM_EXISTS) return null;
|
|
417
|
+
return expected;
|
|
418
|
+
};
|
|
419
|
+
const appendToStreamRaw = async (execute, streamId, streamType, messages, options) => {
|
|
420
|
+
let expectedStreamVersion = options?.expectedStreamVersion ?? null;
|
|
421
|
+
const currentStreamVersion = await getLastStreamPosition(execute, streamId, expectedStreamVersion);
|
|
422
|
+
expectedStreamVersion ??= currentStreamVersion ?? 0n;
|
|
423
|
+
if (expectedStreamVersion !== currentStreamVersion) throw new ExpectedVersionConflictError(currentStreamVersion, expectedStreamVersion);
|
|
424
|
+
const streamSQL = expectedStreamVersion === 0n ? SQL`INSERT INTO ${identifier$7(streamsTable.name)}
|
|
1645
425
|
(stream_id, stream_position, partition, stream_type, stream_metadata, is_archived)
|
|
1646
426
|
VALUES (
|
|
1647
427
|
${streamId},
|
|
@@ -1652,7 +432,7 @@ var appendToStreamRaw = async (execute, streamId, streamType, messages, options)
|
|
|
1652
432
|
false
|
|
1653
433
|
)
|
|
1654
434
|
RETURNING stream_position;
|
|
1655
|
-
` : SQL`UPDATE ${identifier(streamsTable.name)}
|
|
435
|
+
` : SQL`UPDATE ${identifier$7(streamsTable.name)}
|
|
1656
436
|
SET stream_position = stream_position + ${messages.length}
|
|
1657
437
|
WHERE stream_id = ${streamId}
|
|
1658
438
|
AND stream_position = ${expectedStreamVersion}
|
|
@@ -1660,48 +440,31 @@ var appendToStreamRaw = async (execute, streamId, streamType, messages, options)
|
|
|
1660
440
|
AND is_archived = false
|
|
1661
441
|
RETURNING stream_position;
|
|
1662
442
|
`;
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
throw new ExpectedVersionConflictError(0n, expectedStreamVersion ?? 0n);
|
|
1675
|
-
if (!globalPosition) throw new Error("Could not find global position");
|
|
1676
|
-
return {
|
|
1677
|
-
success: true,
|
|
1678
|
-
nextStreamPosition: BigInt(streamPosition),
|
|
1679
|
-
lastGlobalPosition: BigInt(globalPosition)
|
|
1680
|
-
};
|
|
443
|
+
const insertSQL = buildMessageInsertQuery(messages, expectedStreamVersion, streamId, options?.partition?.toString() ?? defaultTag);
|
|
444
|
+
const [streamResult, messagesResult] = await execute.batchCommand([streamSQL, insertSQL], { assertChanges: true });
|
|
445
|
+
const streamPosition = streamResult?.rows[0]?.stream_position;
|
|
446
|
+
const globalPosition = messagesResult?.rows.at(-1)?.global_position;
|
|
447
|
+
if (!streamPosition) throw new ExpectedVersionConflictError(0n, expectedStreamVersion ?? 0n);
|
|
448
|
+
if (!globalPosition) throw new Error("Could not find global position");
|
|
449
|
+
return {
|
|
450
|
+
success: true,
|
|
451
|
+
nextStreamPosition: BigInt(streamPosition),
|
|
452
|
+
lastGlobalPosition: BigInt(globalPosition)
|
|
453
|
+
};
|
|
1681
454
|
};
|
|
1682
455
|
async function getLastStreamPosition(execute, streamId, expectedStreamVersion) {
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
);
|
|
1688
|
-
if (result?.stream_position == null) {
|
|
1689
|
-
expectedStreamVersion = 0n;
|
|
1690
|
-
} else {
|
|
1691
|
-
expectedStreamVersion = BigInt(result.stream_position);
|
|
1692
|
-
}
|
|
1693
|
-
return expectedStreamVersion;
|
|
456
|
+
const result = await singleOrNull(execute.query(SQL`SELECT CAST(stream_position AS VARCHAR) AS stream_position FROM ${identifier$7(streamsTable.name)} WHERE stream_id = ${streamId}`));
|
|
457
|
+
if (result?.stream_position == null) expectedStreamVersion = 0n;
|
|
458
|
+
else expectedStreamVersion = BigInt(result.stream_position);
|
|
459
|
+
return expectedStreamVersion;
|
|
1694
460
|
}
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
});
|
|
1703
|
-
return SQL`
|
|
1704
|
-
INSERT INTO ${identifier(messagesTable.name)} (
|
|
461
|
+
const buildMessageInsertQuery = (messages, expectedStreamVersion, streamId, partition) => {
|
|
462
|
+
const values = messages.map((message) => {
|
|
463
|
+
if (message.metadata?.streamPosition == null || typeof message.metadata.streamPosition !== "bigint") throw new Error("Stream position is required");
|
|
464
|
+
return SQL`(${streamId},${BigInt(message.metadata.streamPosition) + BigInt(expectedStreamVersion) ?? 0n},${partition ?? defaultTag},${message.kind === "Event" ? "E" : "C"},${message.data},${message.metadata},${expectedStreamVersion ?? 0n},${message.type},${message.metadata.messageId},${false})`;
|
|
465
|
+
});
|
|
466
|
+
return SQL`
|
|
467
|
+
INSERT INTO ${identifier$7(messagesTable.name)} (
|
|
1705
468
|
stream_id,
|
|
1706
469
|
stream_position,
|
|
1707
470
|
partition,
|
|
@@ -1719,10 +482,10 @@ var buildMessageInsertQuery = (messages, expectedStreamVersion, streamId, partit
|
|
|
1719
482
|
`;
|
|
1720
483
|
};
|
|
1721
484
|
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
485
|
+
//#endregion
|
|
486
|
+
//#region src/eventStore/schema/migrations/0_41_0/0_41_0.snapshot.ts
|
|
487
|
+
const schema_0_41_0 = [
|
|
488
|
+
SQL`CREATE TABLE IF NOT EXISTS emt_streams(
|
|
1726
489
|
stream_id TEXT NOT NULL,
|
|
1727
490
|
stream_position BIGINT NOT NULL DEFAULT 0,
|
|
1728
491
|
partition TEXT NOT NULL DEFAULT 'global',
|
|
@@ -1732,7 +495,7 @@ var schema_0_41_0 = [
|
|
|
1732
495
|
PRIMARY KEY (stream_id, partition, is_archived),
|
|
1733
496
|
UNIQUE (stream_id, partition, is_archived)
|
|
1734
497
|
)`,
|
|
1735
|
-
|
|
498
|
+
SQL`CREATE TABLE IF NOT EXISTS emt_messages(
|
|
1736
499
|
stream_id TEXT NOT NULL,
|
|
1737
500
|
stream_position BIGINT NOT NULL,
|
|
1738
501
|
partition TEXT NOT NULL DEFAULT 'global',
|
|
@@ -1747,7 +510,7 @@ var schema_0_41_0 = [
|
|
|
1747
510
|
created DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
1748
511
|
UNIQUE (stream_id, stream_position, partition, is_archived)
|
|
1749
512
|
)`,
|
|
1750
|
-
|
|
513
|
+
SQL`CREATE TABLE IF NOT EXISTS emt_subscriptions(
|
|
1751
514
|
subscription_id TEXT NOT NULL,
|
|
1752
515
|
version INTEGER NOT NULL DEFAULT 1,
|
|
1753
516
|
partition TEXT NOT NULL DEFAULT 'global',
|
|
@@ -1756,30 +519,30 @@ var schema_0_41_0 = [
|
|
|
1756
519
|
)`
|
|
1757
520
|
];
|
|
1758
521
|
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
522
|
+
//#endregion
|
|
523
|
+
//#region src/eventStore/schema/migrations/0_42_0/0_42_0.migration.ts
|
|
524
|
+
const { identifier: identifier$6, plain: plain$1 } = SQL;
|
|
525
|
+
const migration_0_42_0_SQLs = [
|
|
526
|
+
SQL`CREATE TABLE IF NOT EXISTS ${identifier$6(processorsTable.name)}(
|
|
1764
527
|
processor_id TEXT NOT NULL,
|
|
1765
528
|
version INTEGER NOT NULL DEFAULT 1,
|
|
1766
|
-
partition TEXT NOT NULL DEFAULT '${plain(globalTag)}',
|
|
529
|
+
partition TEXT NOT NULL DEFAULT '${plain$1(globalTag)}',
|
|
1767
530
|
status TEXT NOT NULL DEFAULT 'stopped',
|
|
1768
531
|
last_processed_checkpoint TEXT NOT NULL,
|
|
1769
532
|
processor_instance_id TEXT DEFAULT 'emt:unknown',
|
|
1770
533
|
PRIMARY KEY (processor_id, partition, version)
|
|
1771
534
|
)`,
|
|
1772
|
-
|
|
535
|
+
SQL`CREATE TABLE IF NOT EXISTS ${identifier$6(projectionsTable.name)}(
|
|
1773
536
|
name TEXT NOT NULL,
|
|
1774
537
|
version INTEGER NOT NULL DEFAULT 1,
|
|
1775
|
-
partition TEXT NOT NULL DEFAULT '${plain(globalTag)}',
|
|
538
|
+
partition TEXT NOT NULL DEFAULT '${plain$1(globalTag)}',
|
|
1776
539
|
type CHAR(1) NOT NULL,
|
|
1777
540
|
kind TEXT NOT NULL,
|
|
1778
541
|
status TEXT NOT NULL,
|
|
1779
542
|
definition JSONB NOT NULL DEFAULT '{}',
|
|
1780
543
|
PRIMARY KEY (name, partition, version)
|
|
1781
544
|
)`,
|
|
1782
|
-
|
|
545
|
+
SQL`INSERT INTO ${identifier$6(processorsTable.name)}
|
|
1783
546
|
(processor_id, version, partition, status, last_processed_checkpoint, processor_instance_id)
|
|
1784
547
|
SELECT
|
|
1785
548
|
subscription_id,
|
|
@@ -1789,24 +552,17 @@ var migration_0_42_0_SQLs = [
|
|
|
1789
552
|
printf('%019d', last_processed_position),
|
|
1790
553
|
'emt:unknown'
|
|
1791
554
|
FROM emt_subscriptions`,
|
|
1792
|
-
|
|
555
|
+
SQL`DROP TABLE emt_subscriptions`
|
|
1793
556
|
];
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
SQL3`SELECT name FROM sqlite_master WHERE type='table' AND name='emt_subscriptions'`
|
|
1798
|
-
)
|
|
1799
|
-
);
|
|
1800
|
-
if (!tableExists) {
|
|
1801
|
-
return;
|
|
1802
|
-
}
|
|
1803
|
-
await execute.batchCommand(migration_0_42_0_SQLs);
|
|
557
|
+
const migration_0_42_0_FromSubscriptionsToProcessors = async (execute) => {
|
|
558
|
+
if (!await singleOrNull(execute.query(SQL`SELECT name FROM sqlite_master WHERE type='table' AND name='emt_subscriptions'`))) return;
|
|
559
|
+
await execute.batchCommand(migration_0_42_0_SQLs);
|
|
1804
560
|
};
|
|
1805
561
|
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
562
|
+
//#endregion
|
|
563
|
+
//#region src/eventStore/schema/migrations/0_42_0/0_42_0.snapshot.ts
|
|
564
|
+
const schema_0_42_0 = [
|
|
565
|
+
SQL`CREATE TABLE IF NOT EXISTS emt_streams(
|
|
1810
566
|
stream_id TEXT NOT NULL,
|
|
1811
567
|
stream_position BIGINT NOT NULL DEFAULT 0,
|
|
1812
568
|
partition TEXT NOT NULL DEFAULT 'global',
|
|
@@ -1816,7 +572,7 @@ var schema_0_42_0 = [
|
|
|
1816
572
|
PRIMARY KEY (stream_id, partition, is_archived),
|
|
1817
573
|
UNIQUE (stream_id, partition, is_archived)
|
|
1818
574
|
)`,
|
|
1819
|
-
|
|
575
|
+
SQL`CREATE TABLE IF NOT EXISTS emt_messages(
|
|
1820
576
|
stream_id TEXT NOT NULL,
|
|
1821
577
|
stream_position BIGINT NOT NULL,
|
|
1822
578
|
partition TEXT NOT NULL DEFAULT 'global',
|
|
@@ -1831,7 +587,7 @@ var schema_0_42_0 = [
|
|
|
1831
587
|
created DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
1832
588
|
UNIQUE (stream_id, stream_position, partition, is_archived)
|
|
1833
589
|
)`,
|
|
1834
|
-
|
|
590
|
+
SQL`CREATE TABLE IF NOT EXISTS emt_processors(
|
|
1835
591
|
processor_id TEXT NOT NULL,
|
|
1836
592
|
version INTEGER NOT NULL DEFAULT 1,
|
|
1837
593
|
partition TEXT NOT NULL DEFAULT 'global',
|
|
@@ -1840,7 +596,7 @@ var schema_0_42_0 = [
|
|
|
1840
596
|
processor_instance_id TEXT DEFAULT 'emt:unknown',
|
|
1841
597
|
PRIMARY KEY (processor_id, partition, version)
|
|
1842
598
|
)`,
|
|
1843
|
-
|
|
599
|
+
SQL`CREATE TABLE IF NOT EXISTS emt_projections(
|
|
1844
600
|
name TEXT NOT NULL,
|
|
1845
601
|
version INTEGER NOT NULL DEFAULT 1,
|
|
1846
602
|
partition TEXT NOT NULL DEFAULT 'global',
|
|
@@ -1852,801 +608,657 @@ var schema_0_42_0 = [
|
|
|
1852
608
|
)`
|
|
1853
609
|
];
|
|
1854
610
|
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
execute.query(
|
|
1861
|
-
SQL5`
|
|
611
|
+
//#endregion
|
|
612
|
+
//#region src/eventStore/schema/readLastMessageGlobalPosition.ts
|
|
613
|
+
const { identifier: identifier$5 } = SQL;
|
|
614
|
+
const readLastMessageGlobalPosition = async (execute, options) => {
|
|
615
|
+
const result = await singleOrNull(execute.query(SQL`
|
|
1862
616
|
SELECT global_position
|
|
1863
|
-
FROM ${
|
|
1864
|
-
WHERE partition = ${options?.partition ??
|
|
1865
|
-
ORDER BY global_position
|
|
1866
|
-
LIMIT 1`
|
|
1867
|
-
|
|
1868
|
-
);
|
|
1869
|
-
return {
|
|
1870
|
-
currentGlobalPosition: result !== null ? BigInt(result.global_position) : null
|
|
1871
|
-
};
|
|
617
|
+
FROM ${identifier$5(messagesTable.name)}
|
|
618
|
+
WHERE partition = ${options?.partition ?? defaultTag} AND is_archived = FALSE
|
|
619
|
+
ORDER BY global_position DESC
|
|
620
|
+
LIMIT 1`));
|
|
621
|
+
return { currentGlobalPosition: result !== null ? BigInt(result.global_position) : null };
|
|
1872
622
|
};
|
|
1873
623
|
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
FROM ${identifier4(messagesTable.name)}
|
|
1889
|
-
WHERE partition = ${options?.partition ?? defaultTag2} AND is_archived = FALSE ${fromCondition} ${toCondition}
|
|
624
|
+
//#endregion
|
|
625
|
+
//#region src/eventStore/schema/readMessagesBatch.ts
|
|
626
|
+
const { identifier: identifier$4 } = SQL;
|
|
627
|
+
const readMessagesBatch = async (execute, options) => {
|
|
628
|
+
const { serializer } = options;
|
|
629
|
+
const from = "from" in options ? options.from : void 0;
|
|
630
|
+
const after = "after" in options ? options.after : void 0;
|
|
631
|
+
const batchSize = "batchSize" in options ? options.batchSize : options.to - options.from;
|
|
632
|
+
const fromCondition = from !== void 0 ? SQL`AND global_position >= ${from}` : after !== void 0 ? SQL`AND global_position > ${after}` : SQL.EMPTY;
|
|
633
|
+
const toCondition = "to" in options ? SQL`AND global_position <= ${options.to}` : SQL.EMPTY;
|
|
634
|
+
const limitCondition = "batchSize" in options ? SQL`LIMIT ${options.batchSize}` : SQL.EMPTY;
|
|
635
|
+
const messages = await mapRows(execute.query(SQL`SELECT stream_id, stream_position, global_position, message_data, message_metadata, message_schema_version, message_type, message_id
|
|
636
|
+
FROM ${identifier$4(messagesTable.name)}
|
|
637
|
+
WHERE partition = ${options?.partition ?? defaultTag} AND is_archived = FALSE ${fromCondition} ${toCondition}
|
|
1890
638
|
ORDER BY global_position
|
|
1891
|
-
${limitCondition}`
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
messages: [],
|
|
1921
|
-
areMessagesLeft: false
|
|
1922
|
-
};
|
|
639
|
+
${limitCondition}`), (row) => {
|
|
640
|
+
const rawEvent = {
|
|
641
|
+
type: row.message_type,
|
|
642
|
+
data: serializer.deserialize(row.message_data),
|
|
643
|
+
metadata: serializer.deserialize(row.message_metadata)
|
|
644
|
+
};
|
|
645
|
+
const metadata = {
|
|
646
|
+
..."metadata" in rawEvent ? rawEvent.metadata ?? {} : {},
|
|
647
|
+
messageId: row.message_id,
|
|
648
|
+
streamName: row.stream_id,
|
|
649
|
+
streamPosition: BigInt(row.stream_position),
|
|
650
|
+
globalPosition: BigInt(row.global_position),
|
|
651
|
+
checkpoint: bigIntProcessorCheckpoint(BigInt(row.global_position))
|
|
652
|
+
};
|
|
653
|
+
return {
|
|
654
|
+
...rawEvent,
|
|
655
|
+
kind: "Event",
|
|
656
|
+
metadata
|
|
657
|
+
};
|
|
658
|
+
});
|
|
659
|
+
return messages.length > 0 ? {
|
|
660
|
+
currentGlobalPosition: messages[messages.length - 1].metadata.globalPosition,
|
|
661
|
+
messages,
|
|
662
|
+
areMessagesLeft: messages.length === batchSize
|
|
663
|
+
} : {
|
|
664
|
+
currentGlobalPosition: "from" in options ? options.from : "after" in options ? options.after : 0n,
|
|
665
|
+
messages: [],
|
|
666
|
+
areMessagesLeft: false
|
|
667
|
+
};
|
|
1923
668
|
};
|
|
1924
669
|
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
LIMIT 1`
|
|
1935
|
-
)
|
|
1936
|
-
);
|
|
1937
|
-
return {
|
|
1938
|
-
lastProcessedCheckpoint: result !== null ? result.last_processed_checkpoint : null
|
|
1939
|
-
};
|
|
670
|
+
//#endregion
|
|
671
|
+
//#region src/eventStore/schema/readProcessorCheckpoint.ts
|
|
672
|
+
const { identifier: identifier$3 } = SQL;
|
|
673
|
+
const readProcessorCheckpoint = async (execute, options) => {
|
|
674
|
+
const result = await singleOrNull(execute.query(SQL`SELECT last_processed_checkpoint
|
|
675
|
+
FROM ${identifier$3(processorsTable.name)}
|
|
676
|
+
WHERE partition = ${options?.partition ?? defaultTag} AND processor_id = ${options.processorId}
|
|
677
|
+
LIMIT 1`));
|
|
678
|
+
return { lastProcessedCheckpoint: result !== null ? result.last_processed_checkpoint : null };
|
|
1940
679
|
};
|
|
1941
680
|
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
stop: async () => {
|
|
2006
|
-
if (!isRunning) return;
|
|
2007
|
-
isRunning = false;
|
|
2008
|
-
await start;
|
|
2009
|
-
}
|
|
2010
|
-
};
|
|
2011
|
-
};
|
|
2012
|
-
var zipSQLiteEventStoreMessageBatchPullerStartFrom = (options) => {
|
|
2013
|
-
if (options.length === 0 || options.some((o) => o === void 0 || o === "BEGINNING"))
|
|
2014
|
-
return "BEGINNING";
|
|
2015
|
-
if (options.every((o) => o === "END")) return "END";
|
|
2016
|
-
return options.filter((o) => o !== void 0 && o !== "BEGINNING" && o !== "END").sort((a, b) => a > b ? 1 : -1)[0];
|
|
681
|
+
//#endregion
|
|
682
|
+
//#region src/eventStore/consumers/messageBatchProcessing/index.ts
|
|
683
|
+
const sqliteEventStoreMessageBatchPuller = ({ executor, batchSize, eachBatch, pullingFrequencyInMs, stopWhen, signal, serialization }) => {
|
|
684
|
+
let isRunning = false;
|
|
685
|
+
let start;
|
|
686
|
+
const serializer = JSONSerializer.from({ serialization });
|
|
687
|
+
const pullMessages = async (options) => {
|
|
688
|
+
let after;
|
|
689
|
+
try {
|
|
690
|
+
after = options.startFrom === "BEGINNING" ? 0n : options.startFrom === "END" ? (await readLastMessageGlobalPosition(executor)).currentGlobalPosition ?? 0n : parseBigIntProcessorCheckpoint(options.startFrom.lastCheckpoint);
|
|
691
|
+
} catch (error) {
|
|
692
|
+
options.started?.reject(error);
|
|
693
|
+
throw error;
|
|
694
|
+
}
|
|
695
|
+
options.started?.resolve();
|
|
696
|
+
const readMessagesOptions = {
|
|
697
|
+
after,
|
|
698
|
+
batchSize,
|
|
699
|
+
serializer
|
|
700
|
+
};
|
|
701
|
+
let waitTime = 100;
|
|
702
|
+
while (isRunning && !signal?.aborted) {
|
|
703
|
+
const { messages, currentGlobalPosition, areMessagesLeft } = await readMessagesBatch(executor, readMessagesOptions);
|
|
704
|
+
if (messages.length > 0) {
|
|
705
|
+
const result = await eachBatch(messages);
|
|
706
|
+
if (result && result.type === "STOP") {
|
|
707
|
+
isRunning = false;
|
|
708
|
+
break;
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
readMessagesOptions.after = currentGlobalPosition;
|
|
712
|
+
await new Promise((resolve) => setTimeout(resolve, waitTime));
|
|
713
|
+
if (stopWhen?.noMessagesLeft === true && !areMessagesLeft) {
|
|
714
|
+
isRunning = false;
|
|
715
|
+
break;
|
|
716
|
+
}
|
|
717
|
+
if (!areMessagesLeft) waitTime = Math.min(waitTime * 2, 1e3);
|
|
718
|
+
else waitTime = pullingFrequencyInMs;
|
|
719
|
+
}
|
|
720
|
+
};
|
|
721
|
+
return {
|
|
722
|
+
get isRunning() {
|
|
723
|
+
return isRunning;
|
|
724
|
+
},
|
|
725
|
+
start: (options) => {
|
|
726
|
+
if (isRunning) return start;
|
|
727
|
+
isRunning = true;
|
|
728
|
+
start = (async () => {
|
|
729
|
+
return pullMessages(options);
|
|
730
|
+
})();
|
|
731
|
+
return start;
|
|
732
|
+
},
|
|
733
|
+
stop: async () => {
|
|
734
|
+
if (!isRunning) return;
|
|
735
|
+
isRunning = false;
|
|
736
|
+
await start;
|
|
737
|
+
}
|
|
738
|
+
};
|
|
739
|
+
};
|
|
740
|
+
const zipSQLiteEventStoreMessageBatchPullerStartFrom = (options) => {
|
|
741
|
+
if (options.length === 0 || options.some((o) => o === void 0 || o === "BEGINNING")) return "BEGINNING";
|
|
742
|
+
if (options.every((o) => o === "END")) return "END";
|
|
743
|
+
return options.filter((o) => o !== void 0 && o !== "BEGINNING" && o !== "END").sort((a, b) => a > b ? 1 : -1)[0];
|
|
2017
744
|
};
|
|
2018
745
|
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
}
|
|
746
|
+
//#endregion
|
|
747
|
+
//#region src/eventStore/consumers/sqliteCheckpointer.ts
|
|
748
|
+
const sqliteCheckpointer = () => ({
|
|
749
|
+
read: async (options, context) => {
|
|
750
|
+
return { lastCheckpoint: (await readProcessorCheckpoint(context.execute, options))?.lastProcessedCheckpoint };
|
|
751
|
+
},
|
|
752
|
+
store: async (options, context) => {
|
|
753
|
+
const newCheckpoint = getCheckpoint(options.message);
|
|
754
|
+
const result = await storeProcessorCheckpoint(context.execute, {
|
|
755
|
+
lastProcessedCheckpoint: options.lastCheckpoint,
|
|
756
|
+
newCheckpoint,
|
|
757
|
+
processorId: options.processorId,
|
|
758
|
+
partition: options.partition,
|
|
759
|
+
version: options.version
|
|
760
|
+
});
|
|
761
|
+
return result.success ? {
|
|
762
|
+
success: true,
|
|
763
|
+
newCheckpoint: result.newCheckpoint
|
|
764
|
+
} : result;
|
|
765
|
+
}
|
|
2040
766
|
});
|
|
2041
767
|
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
onInit: options.projection.init !== void 0 || options.hooks?.onInit ? async (context) => {
|
|
2134
|
-
if (options.projection.init)
|
|
2135
|
-
await options.projection.init({
|
|
2136
|
-
version: options.projection.version ?? version,
|
|
2137
|
-
status: "active",
|
|
2138
|
-
registrationType: "async",
|
|
2139
|
-
context: {
|
|
2140
|
-
...context,
|
|
2141
|
-
migrationOptions: options.migrationOptions
|
|
2142
|
-
}
|
|
2143
|
-
});
|
|
2144
|
-
if (options.hooks?.onInit)
|
|
2145
|
-
await options.hooks.onInit({
|
|
2146
|
-
...context,
|
|
2147
|
-
migrationOptions: options.migrationOptions
|
|
2148
|
-
});
|
|
2149
|
-
} : options.hooks?.onInit,
|
|
2150
|
-
onClose: options.hooks?.onClose
|
|
2151
|
-
};
|
|
2152
|
-
const processor = projector({
|
|
2153
|
-
...options,
|
|
2154
|
-
processorId,
|
|
2155
|
-
processorInstanceId,
|
|
2156
|
-
version,
|
|
2157
|
-
partition,
|
|
2158
|
-
hooks,
|
|
2159
|
-
processingScope: sqliteProcessingScope(),
|
|
2160
|
-
checkpoints: sqliteCheckpointer()
|
|
2161
|
-
});
|
|
2162
|
-
return processor;
|
|
768
|
+
//#endregion
|
|
769
|
+
//#region src/eventStore/consumers/sqliteProcessor.ts
|
|
770
|
+
const sqliteProcessingScope = () => {
|
|
771
|
+
const processingScope = async (handler, partialContext) => {
|
|
772
|
+
const connection = partialContext?.connection;
|
|
773
|
+
if (!connection) throw new EmmettError("Connection is required in context or options");
|
|
774
|
+
return connection.withTransaction(async (transaction) => {
|
|
775
|
+
return handler({
|
|
776
|
+
...partialContext,
|
|
777
|
+
connection,
|
|
778
|
+
execute: transaction.execute
|
|
779
|
+
});
|
|
780
|
+
});
|
|
781
|
+
};
|
|
782
|
+
return processingScope;
|
|
783
|
+
};
|
|
784
|
+
const sqliteWorkflowProcessingScope = (messageStore) => {
|
|
785
|
+
const processingScope = async (handler, partialContext) => {
|
|
786
|
+
const connection = partialContext?.connection;
|
|
787
|
+
if (!connection) throw new EmmettError("Connection is required in context or options");
|
|
788
|
+
return connection.withTransaction(async (transaction) => {
|
|
789
|
+
return handler({
|
|
790
|
+
...partialContext,
|
|
791
|
+
connection: Object.assign(connection, { messageStore }),
|
|
792
|
+
execute: transaction.execute
|
|
793
|
+
});
|
|
794
|
+
});
|
|
795
|
+
};
|
|
796
|
+
return processingScope;
|
|
797
|
+
};
|
|
798
|
+
const sqliteWorkflowProcessor = (options) => {
|
|
799
|
+
const { processorId = options.processorId ?? getWorkflowId({ workflowName: options.workflow.name ?? "unknown" }), processorInstanceId = getProcessorInstanceId(processorId), version = defaultProcessorVersion, partition = defaultProcessorPartition } = options;
|
|
800
|
+
const hooks = {
|
|
801
|
+
...options.hooks ?? {},
|
|
802
|
+
onClose: options.hooks?.onClose
|
|
803
|
+
};
|
|
804
|
+
return workflowProcessor({
|
|
805
|
+
...options,
|
|
806
|
+
processorId,
|
|
807
|
+
processorInstanceId,
|
|
808
|
+
version,
|
|
809
|
+
partition,
|
|
810
|
+
hooks,
|
|
811
|
+
processingScope: sqliteWorkflowProcessingScope(options.messageStore),
|
|
812
|
+
checkpoints: sqliteCheckpointer()
|
|
813
|
+
});
|
|
814
|
+
};
|
|
815
|
+
const sqliteReactor = (options) => {
|
|
816
|
+
const { processorId = options.processorId, processorInstanceId = getProcessorInstanceId(processorId), version = defaultProcessorVersion, partition = defaultProcessorPartition, hooks } = options;
|
|
817
|
+
return reactor({
|
|
818
|
+
...options,
|
|
819
|
+
processorId,
|
|
820
|
+
processorInstanceId,
|
|
821
|
+
version,
|
|
822
|
+
partition,
|
|
823
|
+
hooks,
|
|
824
|
+
processingScope: sqliteProcessingScope(),
|
|
825
|
+
checkpoints: sqliteCheckpointer()
|
|
826
|
+
});
|
|
827
|
+
};
|
|
828
|
+
const sqliteProjector = (options) => {
|
|
829
|
+
const { processorId = getProjectorId({ projectionName: options.projection.name ?? "unknown" }), processorInstanceId = getProcessorInstanceId(processorId), version = defaultProcessorVersion, partition = defaultProcessorPartition } = options;
|
|
830
|
+
const hooks = {
|
|
831
|
+
...options.hooks ?? {},
|
|
832
|
+
onInit: options.projection.init !== void 0 || options.hooks?.onInit ? async (context) => {
|
|
833
|
+
if (options.projection.init) await options.projection.init({
|
|
834
|
+
version: options.projection.version ?? version,
|
|
835
|
+
status: "active",
|
|
836
|
+
registrationType: "async",
|
|
837
|
+
context: {
|
|
838
|
+
...context,
|
|
839
|
+
migrationOptions: options.migrationOptions
|
|
840
|
+
}
|
|
841
|
+
});
|
|
842
|
+
if (options.hooks?.onInit) await options.hooks.onInit({
|
|
843
|
+
...context,
|
|
844
|
+
migrationOptions: options.migrationOptions
|
|
845
|
+
});
|
|
846
|
+
} : options.hooks?.onInit,
|
|
847
|
+
onClose: options.hooks?.onClose
|
|
848
|
+
};
|
|
849
|
+
return projector({
|
|
850
|
+
...options,
|
|
851
|
+
processorId,
|
|
852
|
+
processorInstanceId,
|
|
853
|
+
version,
|
|
854
|
+
partition,
|
|
855
|
+
hooks,
|
|
856
|
+
processingScope: sqliteProcessingScope(),
|
|
857
|
+
checkpoints: sqliteCheckpointer()
|
|
858
|
+
});
|
|
2163
859
|
};
|
|
2164
860
|
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
})
|
|
2306
|
-
)
|
|
2307
|
-
)
|
|
2308
|
-
);
|
|
2309
|
-
await messagePuller.start({ startFrom });
|
|
2310
|
-
await stopProcessors();
|
|
2311
|
-
isRunning = false;
|
|
2312
|
-
})();
|
|
2313
|
-
return start;
|
|
2314
|
-
},
|
|
2315
|
-
stop,
|
|
2316
|
-
close: async () => {
|
|
2317
|
-
await stop();
|
|
2318
|
-
await pool.close();
|
|
2319
|
-
}
|
|
2320
|
-
};
|
|
861
|
+
//#endregion
|
|
862
|
+
//#region src/eventStore/consumers/sqliteEventStoreConsumer.ts
|
|
863
|
+
const sqliteEventStoreConsumer = (options) => {
|
|
864
|
+
let isRunning = false;
|
|
865
|
+
let isInitialized = false;
|
|
866
|
+
const { pulling } = options;
|
|
867
|
+
const processors = options.processors ?? [];
|
|
868
|
+
let abortController = null;
|
|
869
|
+
let start;
|
|
870
|
+
let messagePuller;
|
|
871
|
+
const startedAwaiter = asyncAwaiter();
|
|
872
|
+
const pool = options.pool ?? dumbo({
|
|
873
|
+
serialization: options.serialization,
|
|
874
|
+
transactionOptions: {
|
|
875
|
+
allowNestedTransactions: true,
|
|
876
|
+
mode: "session_based"
|
|
877
|
+
},
|
|
878
|
+
...options.driver.mapToDumboOptions(options)
|
|
879
|
+
});
|
|
880
|
+
const eachBatch = (messagesBatch) => pool.withConnection(async (connection) => {
|
|
881
|
+
const activeProcessors = processors.filter((s) => s.isActive);
|
|
882
|
+
if (activeProcessors.length === 0) return {
|
|
883
|
+
type: "STOP",
|
|
884
|
+
reason: "No active processors"
|
|
885
|
+
};
|
|
886
|
+
return (await Promise.allSettled(activeProcessors.map(async (s) => {
|
|
887
|
+
return await s.handle(messagesBatch, {
|
|
888
|
+
connection,
|
|
889
|
+
execute: connection.execute
|
|
890
|
+
});
|
|
891
|
+
}))).some((r) => r.status === "fulfilled" && r.value?.type !== "STOP") ? void 0 : { type: "STOP" };
|
|
892
|
+
});
|
|
893
|
+
const processorContext = {
|
|
894
|
+
execute: void 0,
|
|
895
|
+
connection: void 0
|
|
896
|
+
};
|
|
897
|
+
const stopProcessors = () => Promise.all(processors.map((p) => p.close(processorContext)));
|
|
898
|
+
const stop = async () => {
|
|
899
|
+
if (!isRunning) return;
|
|
900
|
+
isRunning = false;
|
|
901
|
+
if (messagePuller) {
|
|
902
|
+
abortController?.abort();
|
|
903
|
+
await messagePuller.stop();
|
|
904
|
+
}
|
|
905
|
+
await start;
|
|
906
|
+
messagePuller = void 0;
|
|
907
|
+
abortController = null;
|
|
908
|
+
await stopProcessors();
|
|
909
|
+
};
|
|
910
|
+
const init = async () => {
|
|
911
|
+
if (isInitialized) return;
|
|
912
|
+
const sqliteProcessors = processors;
|
|
913
|
+
await pool.withConnection(async (connection) => {
|
|
914
|
+
for (const processor of sqliteProcessors) if (processor.init) await processor.init({
|
|
915
|
+
...processorContext,
|
|
916
|
+
connection,
|
|
917
|
+
execute: connection.execute
|
|
918
|
+
});
|
|
919
|
+
});
|
|
920
|
+
isInitialized = true;
|
|
921
|
+
};
|
|
922
|
+
return {
|
|
923
|
+
consumerId: options.consumerId ?? v7(),
|
|
924
|
+
get isRunning() {
|
|
925
|
+
return isRunning;
|
|
926
|
+
},
|
|
927
|
+
whenStarted: () => startedAwaiter.wait,
|
|
928
|
+
processors,
|
|
929
|
+
init,
|
|
930
|
+
reactor: (options) => {
|
|
931
|
+
const processor = sqliteReactor(options);
|
|
932
|
+
processors.push(processor);
|
|
933
|
+
return processor;
|
|
934
|
+
},
|
|
935
|
+
projector: (options) => {
|
|
936
|
+
const processor = sqliteProjector(options);
|
|
937
|
+
processors.push(processor);
|
|
938
|
+
return processor;
|
|
939
|
+
},
|
|
940
|
+
workflowProcessor: (processorOptions) => {
|
|
941
|
+
const messageStore = getSQLiteEventStore({
|
|
942
|
+
...options,
|
|
943
|
+
pool,
|
|
944
|
+
schema: { autoMigration: "None" }
|
|
945
|
+
});
|
|
946
|
+
const processor = sqliteWorkflowProcessor({
|
|
947
|
+
...processorOptions,
|
|
948
|
+
messageStore
|
|
949
|
+
});
|
|
950
|
+
processors.push(processor);
|
|
951
|
+
return processor;
|
|
952
|
+
},
|
|
953
|
+
start: () => {
|
|
954
|
+
if (isRunning) return start;
|
|
955
|
+
startedAwaiter.reset();
|
|
956
|
+
if (processors.length === 0) {
|
|
957
|
+
const error = new EmmettError("Cannot start consumer without at least a single processor");
|
|
958
|
+
startedAwaiter.reject(error);
|
|
959
|
+
return Promise.reject(error);
|
|
960
|
+
}
|
|
961
|
+
isRunning = true;
|
|
962
|
+
abortController = new AbortController();
|
|
963
|
+
start = (async () => {
|
|
964
|
+
if (!isRunning) return;
|
|
965
|
+
try {
|
|
966
|
+
messagePuller = sqliteEventStoreMessageBatchPuller({
|
|
967
|
+
stopWhen: options.stopWhen,
|
|
968
|
+
executor: pool.execute,
|
|
969
|
+
eachBatch,
|
|
970
|
+
batchSize: pulling?.batchSize ?? 100,
|
|
971
|
+
pullingFrequencyInMs: pulling?.pullingFrequencyInMs ?? 50,
|
|
972
|
+
signal: abortController.signal
|
|
973
|
+
});
|
|
974
|
+
if (!isInitialized) await init();
|
|
975
|
+
const startFrom = await pool.withConnection(async (connection) => zipSQLiteEventStoreMessageBatchPullerStartFrom(await Promise.all(processors.map(async (o) => {
|
|
976
|
+
return await o.start({
|
|
977
|
+
execute: connection.execute,
|
|
978
|
+
connection
|
|
979
|
+
});
|
|
980
|
+
}))));
|
|
981
|
+
await messagePuller.start({
|
|
982
|
+
startFrom,
|
|
983
|
+
started: startedAwaiter
|
|
984
|
+
});
|
|
985
|
+
} catch (error) {
|
|
986
|
+
isRunning = false;
|
|
987
|
+
startedAwaiter.reject(error);
|
|
988
|
+
throw error;
|
|
989
|
+
} finally {
|
|
990
|
+
await stopProcessors();
|
|
991
|
+
}
|
|
992
|
+
})();
|
|
993
|
+
return start;
|
|
994
|
+
},
|
|
995
|
+
stop,
|
|
996
|
+
close: async () => {
|
|
997
|
+
await stop();
|
|
998
|
+
await pool.close();
|
|
999
|
+
}
|
|
1000
|
+
};
|
|
2321
1001
|
};
|
|
2322
1002
|
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
serialization: options.serialization
|
|
2461
|
-
}),
|
|
2462
|
-
transactionOptions: {
|
|
2463
|
-
allowNestedTransactions: true,
|
|
2464
|
-
mode: "session_based"
|
|
2465
|
-
},
|
|
2466
|
-
schema: {
|
|
2467
|
-
...options.schema,
|
|
2468
|
-
autoMigration: "None"
|
|
2469
|
-
},
|
|
2470
|
-
serialization: options.serialization
|
|
2471
|
-
});
|
|
2472
|
-
await ensureSchemaExists();
|
|
2473
|
-
return callback({
|
|
2474
|
-
eventStore: sessionStore,
|
|
2475
|
-
close: () => Promise.resolve()
|
|
2476
|
-
});
|
|
2477
|
-
});
|
|
2478
|
-
},
|
|
2479
|
-
close: () => pool.close(),
|
|
2480
|
-
schema: {
|
|
2481
|
-
sql: () => schemaSQL.join(""),
|
|
2482
|
-
print: () => console.log(schemaSQL.join("")),
|
|
2483
|
-
migrate: () => pool.withConnection(migrate)
|
|
2484
|
-
}
|
|
2485
|
-
};
|
|
1003
|
+
//#endregion
|
|
1004
|
+
//#region src/eventStore/SQLiteEventStore.ts
|
|
1005
|
+
const SQLiteEventStoreDefaultStreamVersion = 0n;
|
|
1006
|
+
const getSQLiteEventStore = (options) => {
|
|
1007
|
+
let autoGenerateSchema = false;
|
|
1008
|
+
const serializer = JSONSerializer.from(options);
|
|
1009
|
+
const pool = options.pool ?? dumbo({
|
|
1010
|
+
serialization: options.serialization,
|
|
1011
|
+
transactionOptions: {
|
|
1012
|
+
allowNestedTransactions: true,
|
|
1013
|
+
mode: "session_based"
|
|
1014
|
+
},
|
|
1015
|
+
...options.driver.mapToDumboOptions(options)
|
|
1016
|
+
});
|
|
1017
|
+
let migrateSchema = void 0;
|
|
1018
|
+
const inlineProjections = (options.projections ?? []).filter(({ type }) => type === "inline").map(({ projection }) => projection);
|
|
1019
|
+
const onBeforeCommitHook = options.hooks?.onBeforeCommit;
|
|
1020
|
+
if (options) autoGenerateSchema = options.schema?.autoMigration === void 0 || options.schema?.autoMigration !== "None";
|
|
1021
|
+
const migrate = (connection) => {
|
|
1022
|
+
if (!migrateSchema) migrateSchema = createEventStoreSchema(connection, {
|
|
1023
|
+
onBeforeSchemaCreated: async (context) => {
|
|
1024
|
+
for (const projection of inlineProjections) if (projection.init) await projection.init({
|
|
1025
|
+
version: projection.version ?? 1,
|
|
1026
|
+
registrationType: "async",
|
|
1027
|
+
status: "active",
|
|
1028
|
+
context: {
|
|
1029
|
+
execute: context.connection.execute,
|
|
1030
|
+
connection: context.connection,
|
|
1031
|
+
driverType: options.driver.driverType
|
|
1032
|
+
}
|
|
1033
|
+
});
|
|
1034
|
+
if (options.hooks?.onBeforeSchemaCreated) await options.hooks.onBeforeSchemaCreated(context);
|
|
1035
|
+
},
|
|
1036
|
+
onAfterSchemaCreated: options.hooks?.onAfterSchemaCreated
|
|
1037
|
+
});
|
|
1038
|
+
return migrateSchema;
|
|
1039
|
+
};
|
|
1040
|
+
const ensureSchemaExists = () => {
|
|
1041
|
+
if (!autoGenerateSchema) return Promise.resolve();
|
|
1042
|
+
return pool.withConnection((connection) => migrate(connection));
|
|
1043
|
+
};
|
|
1044
|
+
return {
|
|
1045
|
+
async aggregateStream(streamName, options) {
|
|
1046
|
+
await ensureSchemaExists();
|
|
1047
|
+
const { evolve, initialState, read } = options;
|
|
1048
|
+
const expectedStreamVersion = read?.expectedStreamVersion;
|
|
1049
|
+
let state = initialState();
|
|
1050
|
+
if (typeof streamName !== "string") throw new Error("Stream name is not string");
|
|
1051
|
+
const result = await readStream(pool.execute, streamName, {
|
|
1052
|
+
...read,
|
|
1053
|
+
serializer: read?.serialization?.serializer ?? serializer
|
|
1054
|
+
});
|
|
1055
|
+
const currentStreamVersion = result.currentStreamVersion;
|
|
1056
|
+
assertExpectedVersionMatchesCurrent(currentStreamVersion, expectedStreamVersion, SQLiteEventStoreDefaultStreamVersion);
|
|
1057
|
+
for (const event of result.events) {
|
|
1058
|
+
if (!event) continue;
|
|
1059
|
+
state = evolve(state, event);
|
|
1060
|
+
}
|
|
1061
|
+
return {
|
|
1062
|
+
currentStreamVersion,
|
|
1063
|
+
state,
|
|
1064
|
+
streamExists: result.streamExists
|
|
1065
|
+
};
|
|
1066
|
+
},
|
|
1067
|
+
readStream: async (streamName, readOptions) => {
|
|
1068
|
+
await ensureSchemaExists();
|
|
1069
|
+
return readStream(pool.execute, streamName, {
|
|
1070
|
+
...readOptions,
|
|
1071
|
+
serializer: options.serialization?.serializer ?? serializer
|
|
1072
|
+
});
|
|
1073
|
+
},
|
|
1074
|
+
appendToStream: async (streamName, events, appendOptions) => {
|
|
1075
|
+
await ensureSchemaExists();
|
|
1076
|
+
const [firstPart, ...rest] = streamName.split("-");
|
|
1077
|
+
const streamType = firstPart && rest.length > 0 ? firstPart : unknownTag;
|
|
1078
|
+
const appendResult = await pool.withConnection((connection) => appendToStream(connection, streamName, streamType, events, {
|
|
1079
|
+
...appendOptions,
|
|
1080
|
+
onBeforeCommit: async (messages, context) => {
|
|
1081
|
+
if (inlineProjections.length > 0) await handleProjections({
|
|
1082
|
+
projections: inlineProjections,
|
|
1083
|
+
events: messages,
|
|
1084
|
+
execute: context.connection.execute,
|
|
1085
|
+
connection: context.connection,
|
|
1086
|
+
driverType: options.driver.driverType
|
|
1087
|
+
});
|
|
1088
|
+
if (onBeforeCommitHook) await onBeforeCommitHook(messages, context);
|
|
1089
|
+
}
|
|
1090
|
+
}), { readonly: false });
|
|
1091
|
+
if (!appendResult.success) throw new ExpectedVersionConflictError(-1n, appendOptions?.expectedStreamVersion ?? NO_CONCURRENCY_CHECK);
|
|
1092
|
+
return {
|
|
1093
|
+
nextExpectedStreamVersion: appendResult.nextStreamPosition,
|
|
1094
|
+
lastEventGlobalPosition: appendResult.lastGlobalPosition,
|
|
1095
|
+
createdNewStream: appendResult.nextStreamPosition >= BigInt(events.length)
|
|
1096
|
+
};
|
|
1097
|
+
},
|
|
1098
|
+
async streamExists(streamName, options) {
|
|
1099
|
+
await ensureSchemaExists();
|
|
1100
|
+
return streamExists(pool.execute, streamName, options);
|
|
1101
|
+
},
|
|
1102
|
+
consumer: (consumerOptions) => sqliteEventStoreConsumer({
|
|
1103
|
+
...options ?? {},
|
|
1104
|
+
...consumerOptions ?? {},
|
|
1105
|
+
pool
|
|
1106
|
+
}),
|
|
1107
|
+
async withSession(callback) {
|
|
1108
|
+
return await pool.withConnection(async (connection) => {
|
|
1109
|
+
const sessionStore = getSQLiteEventStore({
|
|
1110
|
+
...options,
|
|
1111
|
+
pool: dumbo({
|
|
1112
|
+
...options.driver.mapToDumboOptions(options),
|
|
1113
|
+
connection,
|
|
1114
|
+
serialization: options.serialization
|
|
1115
|
+
}),
|
|
1116
|
+
transactionOptions: {
|
|
1117
|
+
allowNestedTransactions: true,
|
|
1118
|
+
mode: "session_based"
|
|
1119
|
+
},
|
|
1120
|
+
schema: {
|
|
1121
|
+
...options.schema,
|
|
1122
|
+
autoMigration: "None"
|
|
1123
|
+
},
|
|
1124
|
+
serialization: options.serialization
|
|
1125
|
+
});
|
|
1126
|
+
await ensureSchemaExists();
|
|
1127
|
+
return callback({
|
|
1128
|
+
eventStore: sessionStore,
|
|
1129
|
+
close: () => Promise.resolve()
|
|
1130
|
+
});
|
|
1131
|
+
});
|
|
1132
|
+
},
|
|
1133
|
+
close: () => pool.close(),
|
|
1134
|
+
schema: {
|
|
1135
|
+
sql: () => schemaSQL.join(""),
|
|
1136
|
+
print: () => console.log(schemaSQL.join("")),
|
|
1137
|
+
migrate: () => pool.withConnection(migrate)
|
|
1138
|
+
}
|
|
1139
|
+
};
|
|
2486
1140
|
};
|
|
2487
1141
|
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
currentStreamVersion: SQLiteEventStoreDefaultStreamVersion,
|
|
2530
|
-
events: [],
|
|
2531
|
-
streamExists: false
|
|
2532
|
-
};
|
|
1142
|
+
//#endregion
|
|
1143
|
+
//#region src/eventStore/schema/readStream.ts
|
|
1144
|
+
const { identifier: identifier$2 } = SQL;
|
|
1145
|
+
const readStream = async (execute, streamId, options) => {
|
|
1146
|
+
const { serializer } = options;
|
|
1147
|
+
const fromCondition = options.from ? SQL`AND stream_position >= ${options.from}` : SQL.EMPTY;
|
|
1148
|
+
const to = Number(options?.to ?? (options?.maxCount ? (options.from ?? 0n) + options.maxCount : NaN));
|
|
1149
|
+
const toCondition = !isNaN(to) ? SQL`AND stream_position <= ${to}` : SQL.EMPTY;
|
|
1150
|
+
const { rows: results } = await execute.query(SQL`SELECT stream_id, stream_position, global_position, message_data, message_metadata, message_schema_version, message_type, message_id
|
|
1151
|
+
FROM ${identifier$2(messagesTable.name)}
|
|
1152
|
+
WHERE stream_id = ${streamId} AND partition = ${options?.partition ?? defaultTag} AND is_archived = FALSE ${fromCondition} ${toCondition}
|
|
1153
|
+
ORDER BY stream_position ASC`);
|
|
1154
|
+
const messages = results.map((row) => {
|
|
1155
|
+
const rawEvent = {
|
|
1156
|
+
type: row.message_type,
|
|
1157
|
+
data: serializer.deserialize(row.message_data),
|
|
1158
|
+
metadata: serializer.deserialize(row.message_metadata)
|
|
1159
|
+
};
|
|
1160
|
+
const metadata = {
|
|
1161
|
+
..."metadata" in rawEvent ? rawEvent.metadata ?? {} : {},
|
|
1162
|
+
messageId: row.message_id,
|
|
1163
|
+
streamName: streamId,
|
|
1164
|
+
streamPosition: BigInt(row.stream_position),
|
|
1165
|
+
globalPosition: BigInt(row.global_position),
|
|
1166
|
+
checkpoint: bigIntProcessorCheckpoint(BigInt(row.global_position))
|
|
1167
|
+
};
|
|
1168
|
+
return upcastRecordedMessage({
|
|
1169
|
+
...rawEvent,
|
|
1170
|
+
kind: "Event",
|
|
1171
|
+
metadata
|
|
1172
|
+
}, options?.schema?.versioning);
|
|
1173
|
+
});
|
|
1174
|
+
return messages.length > 0 ? {
|
|
1175
|
+
currentStreamVersion: messages[messages.length - 1].metadata.streamPosition,
|
|
1176
|
+
events: messages,
|
|
1177
|
+
streamExists: true
|
|
1178
|
+
} : {
|
|
1179
|
+
currentStreamVersion: SQLiteEventStoreDefaultStreamVersion,
|
|
1180
|
+
events: [],
|
|
1181
|
+
streamExists: false
|
|
1182
|
+
};
|
|
2533
1183
|
};
|
|
2534
1184
|
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
singleOrNull as singleOrNull5,
|
|
2539
|
-
SQL as SQL9,
|
|
2540
|
-
UniqueConstraintError as UniqueConstraintError2
|
|
2541
|
-
} from "@event-driven-io/dumbo";
|
|
2542
|
-
var { identifier: identifier7 } = SQL9;
|
|
1185
|
+
//#endregion
|
|
1186
|
+
//#region src/eventStore/schema/storeProcessorCheckpoint.ts
|
|
1187
|
+
const { identifier: identifier$1 } = SQL;
|
|
2543
1188
|
async function storeSubscriptionCheckpointSQLite(execute, processorId, version, position, checkPosition, partition, processorInstanceId) {
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
UPDATE ${identifier7(processorsTable.name)}
|
|
1189
|
+
processorInstanceId ??= unknownTag;
|
|
1190
|
+
if (checkPosition !== null) {
|
|
1191
|
+
const updateResult = await execute.command(SQL`
|
|
1192
|
+
UPDATE ${identifier$1(processorsTable.name)}
|
|
2549
1193
|
SET
|
|
2550
1194
|
last_processed_checkpoint = ${position},
|
|
2551
1195
|
processor_instance_id = ${processorInstanceId}
|
|
2552
1196
|
WHERE processor_id = ${processorId}
|
|
2553
1197
|
AND last_processed_checkpoint = ${checkPosition}
|
|
2554
1198
|
AND partition = ${partition}
|
|
2555
|
-
`
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
try {
|
|
2577
|
-
await execute.command(
|
|
2578
|
-
SQL9`INSERT INTO ${identifier7(processorsTable.name)} (processor_id, version, last_processed_checkpoint, partition, processor_instance_id)
|
|
2579
|
-
VALUES (${processorId}, ${version}, ${position}, ${partition}, ${processorInstanceId})`
|
|
2580
|
-
);
|
|
2581
|
-
return 1;
|
|
2582
|
-
} catch (err) {
|
|
2583
|
-
if (!DumboError2.isInstanceOf(err, {
|
|
2584
|
-
errorType: UniqueConstraintError2.ErrorType
|
|
2585
|
-
})) {
|
|
2586
|
-
throw err;
|
|
2587
|
-
}
|
|
2588
|
-
const current = await singleOrNull5(
|
|
2589
|
-
execute.query(
|
|
2590
|
-
SQL9`
|
|
2591
|
-
SELECT last_processed_checkpoint FROM ${identifier7(processorsTable.name)}
|
|
2592
|
-
WHERE processor_id = ${processorId} AND partition = ${partition}`
|
|
2593
|
-
)
|
|
2594
|
-
);
|
|
2595
|
-
const currentPosition = current && current?.last_processed_checkpoint !== null ? BigInt(current.last_processed_checkpoint) : null;
|
|
2596
|
-
if (currentPosition === position) {
|
|
2597
|
-
return 0;
|
|
2598
|
-
} else {
|
|
2599
|
-
return 2;
|
|
2600
|
-
}
|
|
2601
|
-
}
|
|
2602
|
-
}
|
|
1199
|
+
`);
|
|
1200
|
+
if (updateResult.rowCount && updateResult.rowCount > 0) return 1;
|
|
1201
|
+
const current_position = await singleOrNull(execute.query(SQL`
|
|
1202
|
+
SELECT last_processed_checkpoint FROM ${identifier$1(processorsTable.name)}
|
|
1203
|
+
WHERE processor_id = ${processorId} AND partition = ${partition}`));
|
|
1204
|
+
const currentPosition = current_position && current_position?.last_processed_checkpoint !== null ? current_position.last_processed_checkpoint : null;
|
|
1205
|
+
if (currentPosition === position) return 0;
|
|
1206
|
+
else if (position !== null && currentPosition !== null && currentPosition > position) return 2;
|
|
1207
|
+
else return 2;
|
|
1208
|
+
} else try {
|
|
1209
|
+
await execute.command(SQL`INSERT INTO ${identifier$1(processorsTable.name)} (processor_id, version, last_processed_checkpoint, partition, processor_instance_id)
|
|
1210
|
+
VALUES (${processorId}, ${version}, ${position}, ${partition}, ${processorInstanceId})`);
|
|
1211
|
+
return 1;
|
|
1212
|
+
} catch (err) {
|
|
1213
|
+
if (!DumboError.isInstanceOf(err, { errorType: UniqueConstraintError.ErrorType })) throw err;
|
|
1214
|
+
const current = await singleOrNull(execute.query(SQL`
|
|
1215
|
+
SELECT last_processed_checkpoint FROM ${identifier$1(processorsTable.name)}
|
|
1216
|
+
WHERE processor_id = ${processorId} AND partition = ${partition}`));
|
|
1217
|
+
if ((current && current?.last_processed_checkpoint !== null ? BigInt(current.last_processed_checkpoint) : null) === position) return 0;
|
|
1218
|
+
else return 2;
|
|
1219
|
+
}
|
|
2603
1220
|
}
|
|
2604
1221
|
async function storeProcessorCheckpoint(execute, options) {
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
}
|
|
1222
|
+
try {
|
|
1223
|
+
const result = await storeSubscriptionCheckpointSQLite(execute, options.processorId, options.version ?? 1, options.newCheckpoint, options.lastProcessedCheckpoint, options.partition ?? defaultTag);
|
|
1224
|
+
return result === 1 ? {
|
|
1225
|
+
success: true,
|
|
1226
|
+
newCheckpoint: options.newCheckpoint
|
|
1227
|
+
} : {
|
|
1228
|
+
success: false,
|
|
1229
|
+
reason: result === 0 ? "IGNORED" : "MISMATCH"
|
|
1230
|
+
};
|
|
1231
|
+
} catch (error) {
|
|
1232
|
+
console.log(error);
|
|
1233
|
+
throw error;
|
|
1234
|
+
}
|
|
2619
1235
|
}
|
|
2620
1236
|
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
execute.query(
|
|
2625
|
-
SQL10`SELECT EXISTS (
|
|
1237
|
+
//#endregion
|
|
1238
|
+
//#region src/eventStore/schema/streamExists.ts
|
|
1239
|
+
const streamExists = (execute, streamId, options) => exists(execute.query(SQL`SELECT EXISTS (
|
|
2626
1240
|
SELECT 1
|
|
2627
|
-
from ${
|
|
2628
|
-
WHERE stream_id = ${streamId} AND partition = ${options?.partition ??
|
|
2629
|
-
`
|
|
2630
|
-
)
|
|
2631
|
-
);
|
|
1241
|
+
from ${SQL.identifier(streamsTable.name)}
|
|
1242
|
+
WHERE stream_id = ${streamId} AND partition = ${options?.partition ?? defaultTag} AND is_archived = FALSE) as exists
|
|
1243
|
+
`));
|
|
2632
1244
|
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
1245
|
+
//#endregion
|
|
1246
|
+
//#region src/eventStore/schema/tables.ts
|
|
1247
|
+
const { identifier, plain } = SQL;
|
|
1248
|
+
const streamsTableSQL = SQL`CREATE TABLE IF NOT EXISTS ${identifier(streamsTable.name)}(
|
|
2637
1249
|
stream_id TEXT NOT NULL,
|
|
2638
1250
|
stream_position BIGINT NOT NULL DEFAULT 0,
|
|
2639
|
-
partition TEXT NOT NULL DEFAULT '${
|
|
1251
|
+
partition TEXT NOT NULL DEFAULT '${plain(globalTag)}',
|
|
2640
1252
|
stream_type TEXT NOT NULL,
|
|
2641
1253
|
stream_metadata JSONB NOT NULL,
|
|
2642
1254
|
is_archived BOOLEAN NOT NULL DEFAULT FALSE,
|
|
2643
1255
|
PRIMARY KEY (stream_id, partition, is_archived),
|
|
2644
1256
|
UNIQUE (stream_id, partition, is_archived)
|
|
2645
1257
|
);`;
|
|
2646
|
-
|
|
1258
|
+
const messagesTableSQL = SQL`CREATE TABLE IF NOT EXISTS ${identifier(messagesTable.name)}(
|
|
2647
1259
|
stream_id TEXT NOT NULL,
|
|
2648
1260
|
stream_position BIGINT NOT NULL,
|
|
2649
|
-
partition TEXT NOT NULL DEFAULT '${
|
|
1261
|
+
partition TEXT NOT NULL DEFAULT '${plain(globalTag)}',
|
|
2650
1262
|
message_kind CHAR(1) NOT NULL DEFAULT 'E',
|
|
2651
1263
|
message_data JSONB NOT NULL,
|
|
2652
1264
|
message_metadata JSONB NOT NULL,
|
|
@@ -2659,22 +1271,22 @@ var messagesTableSQL = SQL11`CREATE TABLE IF NOT EXISTS ${identifier8(messagesTa
|
|
|
2659
1271
|
UNIQUE (stream_id, stream_position, partition, is_archived)
|
|
2660
1272
|
);
|
|
2661
1273
|
`;
|
|
2662
|
-
|
|
2663
|
-
CREATE TABLE IF NOT EXISTS ${
|
|
1274
|
+
const processorsTableSQL = SQL`
|
|
1275
|
+
CREATE TABLE IF NOT EXISTS ${SQL.identifier(processorsTable.name)}(
|
|
2664
1276
|
processor_id TEXT NOT NULL,
|
|
2665
1277
|
version INTEGER NOT NULL DEFAULT 1,
|
|
2666
|
-
partition TEXT NOT NULL DEFAULT '${
|
|
1278
|
+
partition TEXT NOT NULL DEFAULT '${plain(globalTag)}',
|
|
2667
1279
|
status TEXT NOT NULL DEFAULT 'stopped',
|
|
2668
1280
|
last_processed_checkpoint TEXT NOT NULL,
|
|
2669
|
-
processor_instance_id TEXT DEFAULT '${
|
|
1281
|
+
processor_instance_id TEXT DEFAULT '${plain(unknownTag)}',
|
|
2670
1282
|
PRIMARY KEY (processor_id, partition, version)
|
|
2671
1283
|
);
|
|
2672
1284
|
`;
|
|
2673
|
-
|
|
2674
|
-
CREATE TABLE IF NOT EXISTS ${
|
|
1285
|
+
const projectionsTableSQL = SQL`
|
|
1286
|
+
CREATE TABLE IF NOT EXISTS ${SQL.identifier(projectionsTable.name)}(
|
|
2675
1287
|
name TEXT NOT NULL,
|
|
2676
1288
|
version INTEGER NOT NULL DEFAULT 1,
|
|
2677
|
-
partition TEXT NOT NULL DEFAULT '${
|
|
1289
|
+
partition TEXT NOT NULL DEFAULT '${plain(globalTag)}',
|
|
2678
1290
|
type CHAR(1) NOT NULL,
|
|
2679
1291
|
kind TEXT NOT NULL,
|
|
2680
1292
|
status TEXT NOT NULL,
|
|
@@ -2682,73 +1294,21 @@ var projectionsTableSQL = SQL11`
|
|
|
2682
1294
|
PRIMARY KEY (name, partition, version)
|
|
2683
1295
|
);
|
|
2684
1296
|
`;
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
1297
|
+
const schemaSQL = [
|
|
1298
|
+
streamsTableSQL,
|
|
1299
|
+
messagesTableSQL,
|
|
1300
|
+
processorsTableSQL,
|
|
1301
|
+
projectionsTableSQL
|
|
2690
1302
|
];
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
}
|
|
2699
|
-
await tx.execute.batchCommand(schemaSQL);
|
|
2700
|
-
if (hooks?.onAfterSchemaCreated) {
|
|
2701
|
-
await hooks.onAfterSchemaCreated();
|
|
2702
|
-
}
|
|
2703
|
-
});
|
|
2704
|
-
};
|
|
2705
|
-
export {
|
|
2706
|
-
SQLiteEventStoreDefaultStreamVersion,
|
|
2707
|
-
SQLiteProjectionSpec,
|
|
2708
|
-
appendToStream,
|
|
2709
|
-
assertSQLQueryResultMatches,
|
|
2710
|
-
createEventStoreSchema,
|
|
2711
|
-
defaultTag2 as defaultTag,
|
|
2712
|
-
documentDoesNotExist,
|
|
2713
|
-
documentExists,
|
|
2714
|
-
documentMatchingExists,
|
|
2715
|
-
documentsAreTheSame,
|
|
2716
|
-
documentsMatchingHaveCount,
|
|
2717
|
-
emmettPrefix2 as emmettPrefix,
|
|
2718
|
-
eventInStream,
|
|
2719
|
-
eventsInStream,
|
|
2720
|
-
expectPongoDocuments,
|
|
2721
|
-
expectSQL,
|
|
2722
|
-
getSQLiteEventStore,
|
|
2723
|
-
globalNames,
|
|
2724
|
-
globalTag,
|
|
2725
|
-
handleProjections,
|
|
2726
|
-
messagesTable,
|
|
2727
|
-
messagesTableSQL,
|
|
2728
|
-
migration_0_42_0_FromSubscriptionsToProcessors,
|
|
2729
|
-
migration_0_42_0_SQLs,
|
|
2730
|
-
newEventsInStream,
|
|
2731
|
-
pongoMultiStreamProjection,
|
|
2732
|
-
pongoProjection,
|
|
2733
|
-
pongoSingleStreamProjection,
|
|
2734
|
-
processorsTable,
|
|
2735
|
-
processorsTableSQL,
|
|
2736
|
-
projectionsTable,
|
|
2737
|
-
projectionsTableSQL,
|
|
2738
|
-
readLastMessageGlobalPosition,
|
|
2739
|
-
readMessagesBatch,
|
|
2740
|
-
readProcessorCheckpoint,
|
|
2741
|
-
readStream,
|
|
2742
|
-
schemaSQL,
|
|
2743
|
-
schema_0_41_0,
|
|
2744
|
-
schema_0_42_0,
|
|
2745
|
-
sqliteProjection,
|
|
2746
|
-
sqliteRawBatchSQLProjection,
|
|
2747
|
-
sqliteRawSQLProjection,
|
|
2748
|
-
storeProcessorCheckpoint,
|
|
2749
|
-
streamExists,
|
|
2750
|
-
streamsTable,
|
|
2751
|
-
streamsTableSQL,
|
|
2752
|
-
unknownTag2 as unknownTag
|
|
1303
|
+
const createEventStoreSchema = async (pool, hooks) => {
|
|
1304
|
+
await pool.withTransaction(async (tx) => {
|
|
1305
|
+
await migration_0_42_0_FromSubscriptionsToProcessors(tx.execute);
|
|
1306
|
+
if (hooks?.onBeforeSchemaCreated) await hooks.onBeforeSchemaCreated({ connection: tx.connection });
|
|
1307
|
+
await tx.execute.batchCommand(schemaSQL);
|
|
1308
|
+
if (hooks?.onAfterSchemaCreated) await hooks.onAfterSchemaCreated();
|
|
1309
|
+
});
|
|
2753
1310
|
};
|
|
1311
|
+
|
|
1312
|
+
//#endregion
|
|
1313
|
+
export { SQLiteEventStoreDefaultStreamVersion, SQLiteProjectionSpec, appendToStream, assertSQLQueryResultMatches, createEventStoreSchema, defaultTag, documentDoesNotExist, documentExists, documentMatchingExists, documentsAreTheSame, documentsMatchingHaveCount, emmettPrefix, eventInStream, eventsInStream, expectPongoDocuments, expectSQL, getSQLiteEventStore, globalNames, globalTag, handleProjections, messagesTable, messagesTableSQL, migration_0_42_0_FromSubscriptionsToProcessors, migration_0_42_0_SQLs, newEventsInStream, pongoMultiStreamProjection, pongoProjection, pongoSingleStreamProjection, processorsTable, processorsTableSQL, projectionsTable, projectionsTableSQL, readLastMessageGlobalPosition, readMessagesBatch, readProcessorCheckpoint, readStream, schemaSQL, schema_0_41_0, schema_0_42_0, sqliteProjection, sqliteRawBatchSQLProjection, sqliteRawSQLProjection, storeProcessorCheckpoint, streamExists, streamsTable, streamsTableSQL, unknownTag };
|
|
2754
1314
|
//# sourceMappingURL=index.js.map
|