@event-driven-io/emmett-sqlite 0.39.0 → 0.40.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -99,33 +99,47 @@ var rollbackTransaction = (db) => new Promise((resolve, reject) => {
99
99
  });
100
100
  });
101
101
 
102
- // ../emmett/dist/chunk-O2VMBOV4.js
102
+ // ../emmett/dist/chunk-AZDDB5SF.js
103
103
  var isNumber = (val) => typeof val === "number" && val === val;
104
104
  var isBigint = (val) => typeof val === "bigint" && val === val;
105
105
  var isString = (val) => typeof val === "string";
106
+ var isErrorConstructor = (expect) => {
107
+ return typeof expect === "function" && expect.prototype && // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
108
+ expect.prototype.constructor === expect;
109
+ };
106
110
  var EmmettError = class _EmmettError extends Error {
111
+ static Codes = {
112
+ ValidationError: 400,
113
+ IllegalStateError: 403,
114
+ NotFoundError: 404,
115
+ ConcurrencyError: 412,
116
+ InternalServerError: 500
117
+ };
107
118
  errorCode;
108
119
  constructor(options) {
109
- const errorCode = options && typeof options === "object" && "errorCode" in options ? options.errorCode : isNumber(options) ? options : 500;
120
+ const errorCode = options && typeof options === "object" && "errorCode" in options ? options.errorCode : isNumber(options) ? options : _EmmettError.Codes.InternalServerError;
110
121
  const message = options && typeof options === "object" && "message" in options ? options.message : isString(options) ? options : `Error with status code '${errorCode}' ocurred during Emmett processing`;
111
122
  super(message);
112
123
  this.errorCode = errorCode;
113
124
  Object.setPrototypeOf(this, _EmmettError.prototype);
114
125
  }
115
126
  static mapFrom(error) {
116
- if (error instanceof _EmmettError) {
127
+ if (_EmmettError.isInstanceOf(error)) {
117
128
  return error;
118
129
  }
119
130
  return new _EmmettError({
120
- errorCode: "errorCode" in error && error.errorCode !== void 0 && error.errorCode !== null ? error.errorCode : 500,
131
+ errorCode: "errorCode" in error && error.errorCode !== void 0 && error.errorCode !== null ? error.errorCode : _EmmettError.Codes.InternalServerError,
121
132
  message: error.message ?? "An unknown error occurred"
122
133
  });
123
134
  }
135
+ static isInstanceOf(error, errorCode) {
136
+ return typeof error === "object" && error !== null && "errorCode" in error && isNumber(error.errorCode) && (errorCode === void 0 || error.errorCode === errorCode);
137
+ }
124
138
  };
125
139
  var ConcurrencyError = class _ConcurrencyError extends EmmettError {
126
140
  constructor(current, expected, message) {
127
141
  super({
128
- errorCode: 412,
142
+ errorCode: EmmettError.Codes.ConcurrencyError,
129
143
  message: message ?? `Expected version ${expected.toString()} does not match current ${current?.toString()}`
130
144
  });
131
145
  this.current = current;
@@ -159,6 +173,207 @@ var ExpectedVersionConflictError = class _ExpectedVersionConflictError extends C
159
173
  Object.setPrototypeOf(this, _ExpectedVersionConflictError.prototype);
160
174
  }
161
175
  };
176
+ var isPrimitive = (value) => {
177
+ const type = typeof value;
178
+ return value === null || value === void 0 || type === "boolean" || type === "number" || type === "string" || type === "symbol" || type === "bigint";
179
+ };
180
+ var compareArrays = (left, right) => {
181
+ if (left.length !== right.length) {
182
+ return false;
183
+ }
184
+ for (let i = 0; i < left.length; i++) {
185
+ const leftHas = i in left;
186
+ const rightHas = i in right;
187
+ if (leftHas !== rightHas) return false;
188
+ if (leftHas && !deepEquals(left[i], right[i])) return false;
189
+ }
190
+ return true;
191
+ };
192
+ var compareDates = (left, right) => {
193
+ return left.getTime() === right.getTime();
194
+ };
195
+ var compareRegExps = (left, right) => {
196
+ return left.toString() === right.toString();
197
+ };
198
+ var compareErrors = (left, right) => {
199
+ if (left.message !== right.message || left.name !== right.name) {
200
+ return false;
201
+ }
202
+ const leftKeys = Object.keys(left);
203
+ const rightKeys = Object.keys(right);
204
+ if (leftKeys.length !== rightKeys.length) return false;
205
+ const rightKeySet = new Set(rightKeys);
206
+ for (const key of leftKeys) {
207
+ if (!rightKeySet.has(key)) return false;
208
+ if (!deepEquals(left[key], right[key])) return false;
209
+ }
210
+ return true;
211
+ };
212
+ var compareMaps = (left, right) => {
213
+ if (left.size !== right.size) return false;
214
+ for (const [key, value] of left) {
215
+ if (isPrimitive(key)) {
216
+ if (!right.has(key) || !deepEquals(value, right.get(key))) {
217
+ return false;
218
+ }
219
+ } else {
220
+ let found = false;
221
+ for (const [rightKey, rightValue] of right) {
222
+ if (deepEquals(key, rightKey) && deepEquals(value, rightValue)) {
223
+ found = true;
224
+ break;
225
+ }
226
+ }
227
+ if (!found) return false;
228
+ }
229
+ }
230
+ return true;
231
+ };
232
+ var compareSets = (left, right) => {
233
+ if (left.size !== right.size) return false;
234
+ for (const leftItem of left) {
235
+ if (isPrimitive(leftItem)) {
236
+ if (!right.has(leftItem)) return false;
237
+ } else {
238
+ let found = false;
239
+ for (const rightItem of right) {
240
+ if (deepEquals(leftItem, rightItem)) {
241
+ found = true;
242
+ break;
243
+ }
244
+ }
245
+ if (!found) return false;
246
+ }
247
+ }
248
+ return true;
249
+ };
250
+ var compareArrayBuffers = (left, right) => {
251
+ if (left.byteLength !== right.byteLength) return false;
252
+ const leftView = new Uint8Array(left);
253
+ const rightView = new Uint8Array(right);
254
+ for (let i = 0; i < leftView.length; i++) {
255
+ if (leftView[i] !== rightView[i]) return false;
256
+ }
257
+ return true;
258
+ };
259
+ var compareTypedArrays = (left, right) => {
260
+ if (left.constructor !== right.constructor) return false;
261
+ if (left.byteLength !== right.byteLength) return false;
262
+ const leftArray = new Uint8Array(
263
+ left.buffer,
264
+ left.byteOffset,
265
+ left.byteLength
266
+ );
267
+ const rightArray = new Uint8Array(
268
+ right.buffer,
269
+ right.byteOffset,
270
+ right.byteLength
271
+ );
272
+ for (let i = 0; i < leftArray.length; i++) {
273
+ if (leftArray[i] !== rightArray[i]) return false;
274
+ }
275
+ return true;
276
+ };
277
+ var compareObjects = (left, right) => {
278
+ const keys1 = Object.keys(left);
279
+ const keys2 = Object.keys(right);
280
+ if (keys1.length !== keys2.length) {
281
+ return false;
282
+ }
283
+ for (const key of keys1) {
284
+ if (left[key] instanceof Function && right[key] instanceof Function) {
285
+ continue;
286
+ }
287
+ const isEqual = deepEquals(left[key], right[key]);
288
+ if (!isEqual) {
289
+ return false;
290
+ }
291
+ }
292
+ return true;
293
+ };
294
+ var getType = (value) => {
295
+ if (value === null) return "null";
296
+ if (value === void 0) return "undefined";
297
+ const primitiveType = typeof value;
298
+ if (primitiveType !== "object") return primitiveType;
299
+ if (Array.isArray(value)) return "array";
300
+ if (value instanceof Boolean) return "boxed-boolean";
301
+ if (value instanceof Number) return "boxed-number";
302
+ if (value instanceof String) return "boxed-string";
303
+ if (value instanceof Date) return "date";
304
+ if (value instanceof RegExp) return "regexp";
305
+ if (value instanceof Error) return "error";
306
+ if (value instanceof Map) return "map";
307
+ if (value instanceof Set) return "set";
308
+ if (value instanceof ArrayBuffer) return "arraybuffer";
309
+ if (value instanceof DataView) return "dataview";
310
+ if (value instanceof WeakMap) return "weakmap";
311
+ if (value instanceof WeakSet) return "weakset";
312
+ if (ArrayBuffer.isView(value)) return "typedarray";
313
+ return "object";
314
+ };
315
+ var deepEquals = (left, right) => {
316
+ if (left === right) return true;
317
+ if (isEquatable(left)) {
318
+ return left.equals(right);
319
+ }
320
+ const leftType = getType(left);
321
+ const rightType = getType(right);
322
+ if (leftType !== rightType) return false;
323
+ switch (leftType) {
324
+ case "null":
325
+ case "undefined":
326
+ case "boolean":
327
+ case "number":
328
+ case "bigint":
329
+ case "string":
330
+ case "symbol":
331
+ case "function":
332
+ return left === right;
333
+ case "array":
334
+ return compareArrays(left, right);
335
+ case "date":
336
+ return compareDates(left, right);
337
+ case "regexp":
338
+ return compareRegExps(left, right);
339
+ case "error":
340
+ return compareErrors(left, right);
341
+ case "map":
342
+ return compareMaps(
343
+ left,
344
+ right
345
+ );
346
+ case "set":
347
+ return compareSets(left, right);
348
+ case "arraybuffer":
349
+ return compareArrayBuffers(left, right);
350
+ case "dataview":
351
+ case "weakmap":
352
+ case "weakset":
353
+ return false;
354
+ case "typedarray":
355
+ return compareTypedArrays(
356
+ left,
357
+ right
358
+ );
359
+ case "boxed-boolean":
360
+ return left.valueOf() === right.valueOf();
361
+ case "boxed-number":
362
+ return left.valueOf() === right.valueOf();
363
+ case "boxed-string":
364
+ return left.valueOf() === right.valueOf();
365
+ case "object":
366
+ return compareObjects(
367
+ left,
368
+ right
369
+ );
370
+ default:
371
+ return false;
372
+ }
373
+ };
374
+ var isEquatable = (left) => {
375
+ return left !== null && left !== void 0 && typeof left === "object" && "equals" in left && typeof left["equals"] === "function";
376
+ };
162
377
  var ParseError = class extends Error {
163
378
  constructor(text) {
164
379
  super(`Cannot parse! ${text}`);
@@ -180,9 +395,110 @@ var JSONParser = {
180
395
  return options?.map ? options.map(parsed) : parsed;
181
396
  }
182
397
  };
398
+ var AssertionError = class extends Error {
399
+ constructor(message2) {
400
+ super(message2);
401
+ }
402
+ };
403
+ var isSubset = (superObj, subObj) => {
404
+ const sup = superObj;
405
+ const sub = subObj;
406
+ assertOk(sup);
407
+ assertOk(sub);
408
+ return Object.keys(sub).every((ele) => {
409
+ if (typeof sub[ele] == "object") {
410
+ return isSubset(sup[ele], sub[ele]);
411
+ }
412
+ return sub[ele] === sup[ele];
413
+ });
414
+ };
415
+ var assertFails = (message2) => {
416
+ throw new AssertionError(message2 ?? "That should not ever happened, right?");
417
+ };
418
+ function assertTrue(condition, message2) {
419
+ if (condition !== true)
420
+ throw new AssertionError(message2 ?? `Condition is false`);
421
+ }
422
+ function assertOk(obj, message2) {
423
+ if (!obj) throw new AssertionError(message2 ?? `Condition is not truthy`);
424
+ }
425
+ function assertEqual(expected, actual, message2) {
426
+ if (expected !== actual)
427
+ throw new AssertionError(
428
+ `${message2 ?? "Objects are not equal"}:
429
+ Expected: ${JSONParser.stringify(expected)}
430
+ Actual: ${JSONParser.stringify(actual)}`
431
+ );
432
+ }
433
+ function assertNotEqual(obj, other, message2) {
434
+ if (obj === other)
435
+ throw new AssertionError(
436
+ message2 ?? `Objects are equal: ${JSONParser.stringify(obj)}`
437
+ );
438
+ }
439
+ var assertThatArray = (array) => {
440
+ return {
441
+ isEmpty: () => assertEqual(
442
+ array.length,
443
+ 0,
444
+ `Array is not empty ${JSONParser.stringify(array)}`
445
+ ),
446
+ isNotEmpty: () => assertNotEqual(array.length, 0, `Array is empty`),
447
+ hasSize: (length) => assertEqual(array.length, length),
448
+ containsElements: (other) => {
449
+ assertTrue(other.every((ts) => array.some((o) => deepEquals(ts, o))));
450
+ },
451
+ containsElementsMatching: (other) => {
452
+ assertTrue(other.every((ts) => array.some((o) => isSubset(o, ts))));
453
+ },
454
+ containsOnlyElementsMatching: (other) => {
455
+ assertEqual(array.length, other.length, `Arrays lengths don't match`);
456
+ assertTrue(other.every((ts) => array.some((o) => isSubset(o, ts))));
457
+ },
458
+ containsExactlyInAnyOrder: (other) => {
459
+ assertEqual(array.length, other.length);
460
+ assertTrue(array.every((ts) => other.some((o) => deepEquals(ts, o))));
461
+ },
462
+ containsExactlyInAnyOrderElementsOf: (other) => {
463
+ assertEqual(array.length, other.length);
464
+ assertTrue(array.every((ts) => other.some((o) => deepEquals(ts, o))));
465
+ },
466
+ containsExactlyElementsOf: (other) => {
467
+ assertEqual(array.length, other.length);
468
+ for (let i = 0; i < array.length; i++) {
469
+ assertTrue(deepEquals(array[i], other[i]));
470
+ }
471
+ },
472
+ containsExactly: (elem) => {
473
+ assertEqual(array.length, 1);
474
+ assertTrue(deepEquals(array[0], elem));
475
+ },
476
+ contains: (elem) => {
477
+ assertTrue(array.some((a) => deepEquals(a, elem)));
478
+ },
479
+ containsOnlyOnceElementsOf: (other) => {
480
+ assertTrue(
481
+ other.map((o) => array.filter((a) => deepEquals(a, o)).length).filter((a) => a === 1).length === other.length
482
+ );
483
+ },
484
+ containsAnyOf: (other) => {
485
+ assertTrue(array.some((a) => other.some((o) => deepEquals(a, o))));
486
+ },
487
+ allMatch: (matches) => {
488
+ assertTrue(array.every(matches));
489
+ },
490
+ anyMatches: (matches) => {
491
+ assertTrue(array.some(matches));
492
+ },
493
+ allMatchAsync: async (matches) => {
494
+ for (const item of array) {
495
+ assertTrue(await matches(item));
496
+ }
497
+ }
498
+ };
499
+ };
183
500
  var getCheckpoint = (message2) => {
184
- return "checkpoint" in message2.metadata && // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
185
- isBigint(message2.metadata.checkpoint) ? (
501
+ return "checkpoint" in message2.metadata ? (
186
502
  // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
187
503
  message2.metadata.checkpoint
188
504
  ) : "globalPosition" in message2.metadata && // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
@@ -195,9 +511,43 @@ var getCheckpoint = (message2) => {
195
511
  message2.metadata.streamPosition
196
512
  ) : null;
197
513
  };
514
+ var projection = (definition) => definition;
198
515
 
199
- // src/eventStore/schema/appendToStream.ts
200
- import { v4 as uuid4 } from "uuid";
516
+ // src/eventStore/projections/sqliteProjection.ts
517
+ var handleProjections = async (options) => {
518
+ const { projections: allProjections, events, connection } = options;
519
+ const eventTypes = events.map((e) => e.type);
520
+ for (const projection2 of allProjections) {
521
+ if (!projection2.canHandle.some((type) => eventTypes.includes(type))) {
522
+ continue;
523
+ }
524
+ await projection2.handle(events, {
525
+ connection
526
+ });
527
+ }
528
+ };
529
+ var sqliteProjection = (definition) => projection(definition);
530
+ var sqliteRawBatchSQLProjection = (handle, ...canHandle) => sqliteProjection({
531
+ canHandle,
532
+ handle: async (events, context) => {
533
+ const sqls = await handle(events, context);
534
+ for (const sql2 of sqls) await context.connection.command(sql2);
535
+ }
536
+ });
537
+ var sqliteRawSQLProjection = (handle, ...canHandle) => sqliteRawBatchSQLProjection(
538
+ async (events, context) => {
539
+ const sqls = [];
540
+ for (const event of events) {
541
+ sqls.push(await handle(event, context));
542
+ }
543
+ return sqls;
544
+ },
545
+ ...canHandle
546
+ );
547
+
548
+ // src/eventStore/projections/sqliteProjectionSpec.ts
549
+ import "@event-driven-io/dumbo";
550
+ import { v4 as uuid5 } from "uuid";
201
551
 
202
552
  // src/eventStore/schema/typing.ts
203
553
  var emmettPrefix = "emt";
@@ -230,7 +580,196 @@ var subscriptionsTable = {
230
580
  name: `${emmettPrefix}_subscriptions`
231
581
  };
232
582
 
583
+ // src/eventStore/schema/tables.ts
584
+ var sql = (sql2) => sql2;
585
+ var streamsTableSQL = sql(
586
+ `CREATE TABLE IF NOT EXISTS ${streamsTable.name}(
587
+ stream_id TEXT NOT NULL,
588
+ stream_position BIGINT NOT NULL DEFAULT 0,
589
+ partition TEXT NOT NULL DEFAULT '${globalTag}',
590
+ stream_type TEXT NOT NULL,
591
+ stream_metadata JSONB NOT NULL,
592
+ is_archived BOOLEAN NOT NULL DEFAULT FALSE,
593
+ PRIMARY KEY (stream_id, partition, is_archived),
594
+ UNIQUE (stream_id, partition, is_archived)
595
+ );`
596
+ );
597
+ var messagesTableSQL = sql(
598
+ `CREATE TABLE IF NOT EXISTS ${messagesTable.name}(
599
+ stream_id TEXT NOT NULL,
600
+ stream_position BIGINT NOT NULL,
601
+ partition TEXT NOT NULL DEFAULT '${globalTag}',
602
+ message_kind CHAR(1) NOT NULL DEFAULT 'E',
603
+ message_data JSONB NOT NULL,
604
+ message_metadata JSONB NOT NULL,
605
+ message_schema_version TEXT NOT NULL,
606
+ message_type TEXT NOT NULL,
607
+ message_id TEXT NOT NULL,
608
+ is_archived BOOLEAN NOT NULL DEFAULT FALSE,
609
+ global_position INTEGER PRIMARY KEY,
610
+ created DATETIME DEFAULT CURRENT_TIMESTAMP,
611
+ UNIQUE (stream_id, stream_position, partition, is_archived)
612
+ );
613
+ `
614
+ );
615
+ var subscriptionsTableSQL = sql(
616
+ `
617
+ CREATE TABLE IF NOT EXISTS ${subscriptionsTable.name}(
618
+ subscription_id TEXT NOT NULL,
619
+ version INTEGER NOT NULL DEFAULT 1,
620
+ partition TEXT NOT NULL DEFAULT '${globalTag}',
621
+ last_processed_position BIGINT NOT NULL,
622
+ PRIMARY KEY (subscription_id, partition, version)
623
+ );
624
+ `
625
+ );
626
+ var schemaSQL = [
627
+ streamsTableSQL,
628
+ messagesTableSQL,
629
+ subscriptionsTableSQL
630
+ ];
631
+ var createEventStoreSchema = async (db) => {
632
+ for (const sql2 of schemaSQL) {
633
+ await db.command(sql2);
634
+ }
635
+ };
636
+
637
+ // src/eventStore/schema/utils.ts
638
+ var singleOrNull = async (getResult) => {
639
+ const result = await getResult;
640
+ if (result.length > 1) throw new Error("Query had more than one result");
641
+ return result.length > 0 ? result[0] ?? null : null;
642
+ };
643
+
644
+ // src/eventStore/schema/readLastMessageGlobalPosition.ts
645
+ var readLastMessageGlobalPosition = async (db, options) => {
646
+ const result = await singleOrNull(
647
+ db.query(
648
+ sql(
649
+ `SELECT global_position
650
+ FROM ${messagesTable.name}
651
+ WHERE partition = ? AND is_archived = FALSE
652
+ ORDER BY global_position
653
+ LIMIT 1`
654
+ ),
655
+ [options?.partition ?? defaultTag]
656
+ )
657
+ );
658
+ return {
659
+ currentGlobalPosition: result !== null ? BigInt(result.global_position) : null
660
+ };
661
+ };
662
+
663
+ // src/eventStore/schema/readMessagesBatch.ts
664
+ var readMessagesBatch = async (db, options) => {
665
+ const from = "from" in options ? options.from : "after" in options ? options.after + 1n : 0n;
666
+ const batchSize = options && "batchSize" in options ? options.batchSize : options.to - options.from;
667
+ const fromCondition = from !== -0n ? `AND global_position >= ${from}` : "";
668
+ const toCondition = "to" in options ? `AND global_position <= ${options.to}` : "";
669
+ const limitCondition = "batchSize" in options ? `LIMIT ${options.batchSize}` : "";
670
+ const events = (await db.query(
671
+ sql(
672
+ `SELECT stream_id, stream_position, global_position, message_data, message_metadata, message_schema_version, message_type, message_id
673
+ FROM ${messagesTable.name}
674
+ WHERE partition = ? AND is_archived = FALSE ${fromCondition} ${toCondition}
675
+ ORDER BY global_position
676
+ ${limitCondition}`
677
+ ),
678
+ [options?.partition ?? defaultTag]
679
+ )).map((row) => {
680
+ const rawEvent = {
681
+ type: row.message_type,
682
+ data: JSONParser.parse(row.message_data),
683
+ metadata: JSONParser.parse(row.message_metadata)
684
+ };
685
+ const metadata = {
686
+ ..."metadata" in rawEvent ? rawEvent.metadata ?? {} : {},
687
+ messageId: row.message_id,
688
+ streamName: row.stream_id,
689
+ streamPosition: BigInt(row.stream_position),
690
+ globalPosition: BigInt(row.global_position)
691
+ };
692
+ return {
693
+ ...rawEvent,
694
+ kind: "Event",
695
+ metadata
696
+ };
697
+ });
698
+ return events.length > 0 ? {
699
+ currentGlobalPosition: events[events.length - 1].metadata.globalPosition,
700
+ messages: events,
701
+ areEventsLeft: events.length === batchSize
702
+ } : {
703
+ currentGlobalPosition: "from" in options ? options.from : "after" in options ? options.after : 0n,
704
+ messages: [],
705
+ areEventsLeft: false
706
+ };
707
+ };
708
+
709
+ // src/eventStore/consumers/messageBatchProcessing/index.ts
710
+ var DefaultSQLiteEventStoreProcessorBatchSize = 100;
711
+ var DefaultSQLiteEventStoreProcessorPullingFrequencyInMs = 50;
712
+ var sqliteEventStoreMessageBatchPuller = ({
713
+ connection,
714
+ batchSize,
715
+ eachBatch,
716
+ pullingFrequencyInMs
717
+ }) => {
718
+ let isRunning = false;
719
+ let start;
720
+ const pullMessages = async (options) => {
721
+ const after = options.startFrom === "BEGINNING" ? 0n : options.startFrom === "END" ? (await readLastMessageGlobalPosition(connection)).currentGlobalPosition ?? 0n : options.startFrom.globalPosition;
722
+ const readMessagesOptions = {
723
+ after,
724
+ batchSize
725
+ };
726
+ let waitTime = 100;
727
+ do {
728
+ const { messages, currentGlobalPosition, areEventsLeft } = await readMessagesBatch(connection, readMessagesOptions);
729
+ if (messages.length > 0) {
730
+ const result = await eachBatch({ messages });
731
+ if (result && result.type === "STOP") {
732
+ isRunning = false;
733
+ break;
734
+ }
735
+ }
736
+ readMessagesOptions.after = currentGlobalPosition;
737
+ await new Promise((resolve) => setTimeout(resolve, waitTime));
738
+ if (!areEventsLeft) {
739
+ waitTime = Math.min(waitTime * 2, 1e3);
740
+ } else {
741
+ waitTime = pullingFrequencyInMs;
742
+ }
743
+ } while (isRunning);
744
+ };
745
+ return {
746
+ get isRunning() {
747
+ return isRunning;
748
+ },
749
+ start: (options) => {
750
+ if (isRunning) return start;
751
+ start = (async () => {
752
+ isRunning = true;
753
+ return pullMessages(options);
754
+ })();
755
+ return start;
756
+ },
757
+ stop: async () => {
758
+ if (!isRunning) return;
759
+ isRunning = false;
760
+ await start;
761
+ }
762
+ };
763
+ };
764
+ var zipSQLiteEventStoreMessageBatchPullerStartFrom = (options) => {
765
+ if (options.length === 0 || options.some((o) => o === void 0 || o === "BEGINNING"))
766
+ return "BEGINNING";
767
+ if (options.every((o) => o === "END")) return "END";
768
+ return options.filter((o) => o !== void 0 && o !== "BEGINNING" && o !== "END").sort((a, b) => a > b ? 1 : -1)[0];
769
+ };
770
+
233
771
  // src/eventStore/schema/appendToStream.ts
772
+ import { v4 as uuid4 } from "uuid";
234
773
  var appendToStream = async (connection, streamName, streamType, messages, options) => {
235
774
  if (messages.length === 0) return { success: false };
236
775
  const expectedStreamVersion = toExpectedVersion(
@@ -422,103 +961,39 @@ var buildMessageInsertQuery = (messages, expectedStreamVersion, streamId, partit
422
961
  return { sqlString, values: query.values };
423
962
  };
424
963
 
425
- // src/eventStore/schema/tables.ts
426
- var sql = (sql2) => sql2;
427
- var streamsTableSQL = sql(
428
- `CREATE TABLE IF NOT EXISTS ${streamsTable.name}(
429
- stream_id TEXT NOT NULL,
430
- stream_position BIGINT NOT NULL DEFAULT 0,
431
- partition TEXT NOT NULL DEFAULT '${globalTag}',
432
- stream_type TEXT NOT NULL,
433
- stream_metadata JSONB NOT NULL,
434
- is_archived BOOLEAN NOT NULL DEFAULT FALSE,
435
- PRIMARY KEY (stream_id, partition, is_archived),
436
- UNIQUE (stream_id, partition, is_archived)
437
- );`
438
- );
439
- var messagesTableSQL = sql(
440
- `CREATE TABLE IF NOT EXISTS ${messagesTable.name}(
441
- stream_id TEXT NOT NULL,
442
- stream_position BIGINT NOT NULL,
443
- partition TEXT NOT NULL DEFAULT '${globalTag}',
444
- message_kind CHAR(1) NOT NULL DEFAULT 'E',
445
- message_data JSONB NOT NULL,
446
- message_metadata JSONB NOT NULL,
447
- message_schema_version TEXT NOT NULL,
448
- message_type TEXT NOT NULL,
449
- message_id TEXT NOT NULL,
450
- is_archived BOOLEAN NOT NULL DEFAULT FALSE,
451
- global_position INTEGER PRIMARY KEY,
452
- created DATETIME DEFAULT CURRENT_TIMESTAMP,
453
- UNIQUE (stream_id, stream_position, partition, is_archived)
454
- );
455
- `
456
- );
457
- var subscriptionsTableSQL = sql(
458
- `
459
- CREATE TABLE IF NOT EXISTS ${subscriptionsTable.name}(
460
- subscription_id TEXT NOT NULL,
461
- version INTEGER NOT NULL DEFAULT 1,
462
- partition TEXT NOT NULL DEFAULT '${globalTag}',
463
- last_processed_position BIGINT NOT NULL,
464
- PRIMARY KEY (subscription_id, partition, version)
465
- );
466
- `
467
- );
468
- var schemaSQL = [
469
- streamsTableSQL,
470
- messagesTableSQL,
471
- subscriptionsTableSQL
472
- ];
473
- var createEventStoreSchema = async (db) => {
474
- for (const sql2 of schemaSQL) {
475
- await db.command(sql2);
476
- }
477
- };
478
-
479
- // src/eventStore/schema/utils.ts
480
- var singleOrNull = async (getResult) => {
481
- const result = await getResult;
482
- if (result.length > 1) throw new Error("Query had more than one result");
483
- return result.length > 0 ? result[0] ?? null : null;
484
- };
485
-
486
- // src/eventStore/schema/readLastMessageGlobalPosition.ts
487
- var readLastMessageGlobalPosition = async (db, options) => {
964
+ // src/eventStore/schema/readProcessorCheckpoint.ts
965
+ var readProcessorCheckpoint = async (db, options) => {
488
966
  const result = await singleOrNull(
489
967
  db.query(
490
968
  sql(
491
- `SELECT global_position
492
- FROM ${messagesTable.name}
493
- WHERE partition = ? AND is_archived = FALSE
494
- ORDER BY global_position
495
- LIMIT 1`
969
+ `SELECT last_processed_position
970
+ FROM ${subscriptionsTable.name}
971
+ WHERE partition = ? AND subscription_id = ?
972
+ LIMIT 1`
496
973
  ),
497
- [options?.partition ?? defaultTag]
974
+ [options?.partition ?? defaultTag, options.processorId]
498
975
  )
499
976
  );
500
977
  return {
501
- currentGlobalPosition: result !== null ? BigInt(result.global_position) : null
978
+ lastProcessedPosition: result !== null ? BigInt(result.last_processed_position) : null
502
979
  };
503
980
  };
504
981
 
505
- // src/eventStore/schema/readMessagesBatch.ts
506
- var readMessagesBatch = async (db, options) => {
507
- const from = "from" in options ? options.from : "after" in options ? options.after + 1n : 0n;
508
- const batchSize = options && "batchSize" in options ? options.batchSize : options.to - options.from;
509
- const fromCondition = from !== -0n ? `AND global_position >= ${from}` : "";
510
- const toCondition = "to" in options ? `AND global_position <= ${options.to}` : "";
511
- const limitCondition = "batchSize" in options ? `LIMIT ${options.batchSize}` : "";
512
- const events = (await db.query(
513
- sql(
514
- `SELECT stream_id, stream_position, global_position, message_data, message_metadata, message_schema_version, message_type, message_id
982
+ // src/eventStore/schema/readStream.ts
983
+ var readStream = async (db, streamId, options) => {
984
+ const fromCondition = options && "from" in options ? `AND stream_position >= ${options.from}` : "";
985
+ const to = Number(
986
+ options && "to" in options ? options.to : options && "maxCount" in options && options.maxCount ? options.from + options.maxCount : NaN
987
+ );
988
+ const toCondition = !isNaN(to) ? `AND stream_position <= ${to}` : "";
989
+ const results = await db.query(
990
+ `SELECT stream_id, stream_position, global_position, message_data, message_metadata, message_schema_version, message_type, message_id
515
991
  FROM ${messagesTable.name}
516
- WHERE partition = ? AND is_archived = FALSE ${fromCondition} ${toCondition}
517
- ORDER BY global_position
518
- ${limitCondition}`
519
- ),
520
- [options?.partition ?? defaultTag]
521
- )).map((row) => {
992
+ WHERE stream_id = ? AND partition = ? AND is_archived = FALSE ${fromCondition} ${toCondition}
993
+ ORDER BY stream_position ASC`,
994
+ [streamId, options?.partition ?? defaultTag]
995
+ );
996
+ const messages = results.map((row) => {
522
997
  const rawEvent = {
523
998
  type: row.message_type,
524
999
  data: JSONParser.parse(row.message_data),
@@ -527,7 +1002,7 @@ var readMessagesBatch = async (db, options) => {
527
1002
  const metadata = {
528
1003
  ..."metadata" in rawEvent ? rawEvent.metadata ?? {} : {},
529
1004
  messageId: row.message_id,
530
- streamName: row.stream_id,
1005
+ streamName: streamId,
531
1006
  streamPosition: BigInt(row.stream_position),
532
1007
  globalPosition: BigInt(row.global_position)
533
1008
  };
@@ -537,96 +1012,95 @@ var readMessagesBatch = async (db, options) => {
537
1012
  metadata
538
1013
  };
539
1014
  });
540
- return events.length > 0 ? {
541
- currentGlobalPosition: events[events.length - 1].metadata.globalPosition,
542
- messages: events,
543
- areEventsLeft: events.length === batchSize
1015
+ return messages.length > 0 ? {
1016
+ currentStreamVersion: messages[messages.length - 1].metadata.streamPosition,
1017
+ events: messages,
1018
+ streamExists: true
544
1019
  } : {
545
- currentGlobalPosition: "from" in options ? options.from : "after" in options ? options.after : 0n,
546
- messages: [],
547
- areEventsLeft: false
548
- };
549
- };
550
-
551
- // src/eventStore/schema/readProcessorCheckpoint.ts
552
- var readProcessorCheckpoint = async (db, options) => {
553
- const result = await singleOrNull(
554
- db.query(
555
- sql(
556
- `SELECT last_processed_position
557
- FROM ${subscriptionsTable.name}
558
- WHERE partition = ? AND subscription_id = ?
559
- LIMIT 1`
560
- ),
561
- [options?.partition ?? defaultTag, options.processorId]
562
- )
563
- );
564
- return {
565
- lastProcessedPosition: result !== null ? BigInt(result.last_processed_position) : null
566
- };
567
- };
568
-
569
- // src/eventStore/consumers/messageBatchProcessing/index.ts
570
- var DefaultSQLiteEventStoreProcessorBatchSize = 100;
571
- var DefaultSQLiteEventStoreProcessorPullingFrequencyInMs = 50;
572
- var sqliteEventStoreMessageBatchPuller = ({
573
- connection,
574
- batchSize,
575
- eachBatch,
576
- pullingFrequencyInMs
577
- }) => {
578
- let isRunning = false;
579
- let start;
580
- const pullMessages = async (options) => {
581
- const after = options.startFrom === "BEGINNING" ? 0n : options.startFrom === "END" ? (await readLastMessageGlobalPosition(connection)).currentGlobalPosition ?? 0n : options.startFrom.globalPosition;
582
- const readMessagesOptions = {
583
- after,
584
- batchSize
585
- };
586
- let waitTime = 100;
587
- do {
588
- const { messages, currentGlobalPosition, areEventsLeft } = await readMessagesBatch(connection, readMessagesOptions);
589
- if (messages.length > 0) {
590
- const result = await eachBatch({ messages });
591
- if (result && result.type === "STOP") {
592
- isRunning = false;
593
- break;
594
- }
1020
+ currentStreamVersion: SQLiteEventStoreDefaultStreamVersion,
1021
+ events: [],
1022
+ streamExists: false
1023
+ };
1024
+ };
1025
+
1026
+ // src/eventStore/schema/storeProcessorCheckpoint.ts
1027
+ async function storeSubscriptionCheckpointSQLite(db, processorId, version, position, checkPosition, partition) {
1028
+ if (checkPosition !== null) {
1029
+ const updateResult = await db.command(
1030
+ sql(`
1031
+ UPDATE ${subscriptionsTable.name}
1032
+ SET last_processed_position = ?
1033
+ WHERE subscription_id = ?
1034
+ AND last_processed_position = ?
1035
+ AND partition = ?
1036
+ `),
1037
+ [position.toString(), processorId, checkPosition.toString(), partition]
1038
+ );
1039
+ if (updateResult.changes > 0) {
1040
+ return 1;
1041
+ } else {
1042
+ const current_position = await singleOrNull(
1043
+ db.query(
1044
+ sql(
1045
+ `SELECT last_processed_position FROM ${subscriptionsTable.name}
1046
+ WHERE subscription_id = ? AND partition = ?`
1047
+ ),
1048
+ [processorId, partition]
1049
+ )
1050
+ );
1051
+ if (current_position?.last_processed_position === position) {
1052
+ return 0;
1053
+ } else if (position !== null && current_position !== null && current_position?.last_processed_position > position) {
1054
+ return 2;
1055
+ } else {
1056
+ return 2;
595
1057
  }
596
- readMessagesOptions.after = currentGlobalPosition;
597
- await new Promise((resolve) => setTimeout(resolve, waitTime));
598
- if (!areEventsLeft) {
599
- waitTime = Math.min(waitTime * 2, 1e3);
1058
+ }
1059
+ } else {
1060
+ try {
1061
+ await db.command(
1062
+ sql(
1063
+ `INSERT INTO ${subscriptionsTable.name} (subscription_id, version, last_processed_position, partition) VALUES (?, ?, ?, ?)`
1064
+ ),
1065
+ [processorId, version, position.toString(), partition]
1066
+ );
1067
+ return 1;
1068
+ } catch (err) {
1069
+ if (!(isSQLiteError(err) && (err.errno === 19 || err.errno === 2067))) {
1070
+ throw err;
1071
+ }
1072
+ const current = await singleOrNull(
1073
+ db.query(
1074
+ sql(
1075
+ `SELECT last_processed_position FROM ${subscriptionsTable.name} WHERE subscription_id = ? AND partition = ?`
1076
+ ),
1077
+ [processorId, partition]
1078
+ )
1079
+ );
1080
+ if (current?.last_processed_position === position) {
1081
+ return 0;
600
1082
  } else {
601
- waitTime = pullingFrequencyInMs;
1083
+ return 2;
602
1084
  }
603
- } while (isRunning);
604
- };
605
- return {
606
- get isRunning() {
607
- return isRunning;
608
- },
609
- start: (options) => {
610
- if (isRunning) return start;
611
- start = (async () => {
612
- isRunning = true;
613
- return pullMessages(options);
614
- })();
615
- return start;
616
- },
617
- stop: async () => {
618
- if (!isRunning) return;
619
- isRunning = false;
620
- await start;
621
1085
  }
622
- };
623
- };
624
- var zipSQLiteEventStoreMessageBatchPullerStartFrom = (options) => {
625
- if (options.length === 0 || options.some((o) => o === void 0 || o === "BEGINNING"))
626
- return "BEGINNING";
627
- if (options.every((o) => o === "END")) return "END";
628
- return options.filter((o) => o !== void 0 && o !== "BEGINNING" && o !== "END").sort((a, b) => a > b ? 1 : -1)[0];
629
- };
1086
+ }
1087
+ }
1088
+ async function storeProcessorCheckpoint(db, options) {
1089
+ try {
1090
+ const result = await storeSubscriptionCheckpointSQLite(
1091
+ db,
1092
+ options.processorId,
1093
+ options.version ?? 1,
1094
+ options.newPosition,
1095
+ options.lastProcessedPosition,
1096
+ options.partition ?? defaultTag
1097
+ );
1098
+ return result === 1 ? { success: true, newPosition: options.newPosition } : { success: false, reason: result === 0 ? "IGNORED" : "MISMATCH" };
1099
+ } catch (error) {
1100
+ console.log(error);
1101
+ throw error;
1102
+ }
1103
+ }
630
1104
 
631
1105
  // src/eventStore/consumers/sqliteProcessor.ts
632
1106
  var genericSQLiteProcessor = (options) => {
@@ -796,20 +1270,6 @@ var sqliteEventStoreConsumer = (options) => {
796
1270
  };
797
1271
  };
798
1272
 
799
- // src/eventStore/projections/index.ts
800
- var handleProjections = async (options) => {
801
- const { projections: allProjections, events, connection } = options;
802
- const eventTypes = events.map((e) => e.type);
803
- for (const projection2 of allProjections) {
804
- if (!projection2.canHandle.some((type) => eventTypes.includes(type))) {
805
- continue;
806
- }
807
- await projection2.handle(events, {
808
- connection
809
- });
810
- }
811
- };
812
-
813
1273
  // src/eventStore/SQLiteEventStore.ts
814
1274
  var SQLiteEventStoreDefaultStreamVersion = 0n;
815
1275
  var getSQLiteEventStore = (options) => {
@@ -932,142 +1392,141 @@ var getSQLiteEventStore = (options) => {
932
1392
  };
933
1393
  };
934
1394
 
935
- // src/eventStore/schema/readStream.ts
936
- var readStream = async (db, streamId, options) => {
937
- const fromCondition = options && "from" in options ? `AND stream_position >= ${options.from}` : "";
938
- const to = Number(
939
- options && "to" in options ? options.to : options && "maxCount" in options && options.maxCount ? options.from + options.maxCount : NaN
940
- );
941
- const toCondition = !isNaN(to) ? `AND stream_position <= ${to}` : "";
942
- const results = await db.query(
943
- `SELECT stream_id, stream_position, global_position, message_data, message_metadata, message_schema_version, message_type, message_id
944
- FROM ${messagesTable.name}
945
- WHERE stream_id = ? AND partition = ? AND is_archived = FALSE ${fromCondition} ${toCondition}
946
- ORDER BY stream_position ASC`,
947
- [streamId, options?.partition ?? defaultTag]
948
- );
949
- const messages = results.map((row) => {
950
- const rawEvent = {
951
- type: row.message_type,
952
- data: JSONParser.parse(row.message_data),
953
- metadata: JSONParser.parse(row.message_metadata)
954
- };
955
- const metadata = {
956
- ..."metadata" in rawEvent ? rawEvent.metadata ?? {} : {},
957
- messageId: row.message_id,
958
- streamName: streamId,
959
- streamPosition: BigInt(row.stream_position),
960
- globalPosition: BigInt(row.global_position)
961
- };
962
- return {
963
- ...rawEvent,
964
- kind: "Event",
965
- metadata
966
- };
967
- });
968
- return messages.length > 0 ? {
969
- currentStreamVersion: messages[messages.length - 1].metadata.streamPosition,
970
- events: messages,
971
- streamExists: true
972
- } : {
973
- currentStreamVersion: SQLiteEventStoreDefaultStreamVersion,
974
- events: [],
975
- streamExists: false
976
- };
1395
+ // src/eventStore/projections/sqliteProjectionSpec.ts
1396
+ var SQLiteProjectionSpec = {
1397
+ for: (options) => {
1398
+ {
1399
+ const connection = options.connection;
1400
+ const projection2 = options.projection;
1401
+ return (givenEvents) => {
1402
+ return {
1403
+ when: (events, options2) => {
1404
+ const allEvents = [];
1405
+ const run = async (connection2) => {
1406
+ let globalPosition = 0n;
1407
+ const numberOfTimes = options2?.numberOfTimes ?? 1;
1408
+ for (const event of [
1409
+ ...givenEvents,
1410
+ ...Array.from({ length: numberOfTimes }).flatMap(() => events)
1411
+ ]) {
1412
+ const metadata = {
1413
+ globalPosition: ++globalPosition,
1414
+ streamPosition: globalPosition,
1415
+ streamName: `test-${uuid5()}`,
1416
+ messageId: uuid5()
1417
+ };
1418
+ allEvents.push({
1419
+ ...event,
1420
+ kind: "Event",
1421
+ metadata: {
1422
+ ...metadata,
1423
+ ..."metadata" in event ? event.metadata ?? {} : {}
1424
+ }
1425
+ });
1426
+ }
1427
+ await connection2.withTransaction(
1428
+ () => handleProjections({
1429
+ events: allEvents,
1430
+ projections: [projection2],
1431
+ connection: connection2
1432
+ })
1433
+ );
1434
+ };
1435
+ return {
1436
+ then: async (assert, message) => {
1437
+ try {
1438
+ await run(connection);
1439
+ const succeeded = await assert({
1440
+ connection
1441
+ });
1442
+ if (succeeded !== void 0 && succeeded === false)
1443
+ assertFails(
1444
+ message ?? "Projection specification didn't match the criteria"
1445
+ );
1446
+ } finally {
1447
+ connection.close();
1448
+ }
1449
+ },
1450
+ thenThrows: async (...args) => {
1451
+ try {
1452
+ await run(connection);
1453
+ throw new AssertionError("Handler did not fail as expected");
1454
+ } catch (error) {
1455
+ if (error instanceof AssertionError) throw error;
1456
+ if (args.length === 0) return;
1457
+ if (!isErrorConstructor(args[0])) {
1458
+ assertTrue(
1459
+ args[0](error),
1460
+ `Error didn't match the error condition: ${error?.toString()}`
1461
+ );
1462
+ return;
1463
+ }
1464
+ assertTrue(
1465
+ error instanceof args[0],
1466
+ `Caught error is not an instance of the expected type: ${error?.toString()}`
1467
+ );
1468
+ if (args[1]) {
1469
+ assertTrue(
1470
+ args[1](error),
1471
+ `Error didn't match the error condition: ${error?.toString()}`
1472
+ );
1473
+ }
1474
+ } finally {
1475
+ connection.close();
1476
+ }
1477
+ }
1478
+ };
1479
+ }
1480
+ };
1481
+ };
1482
+ }
1483
+ }
977
1484
  };
978
-
979
- // src/eventStore/schema/storeProcessorCheckpoint.ts
980
- async function storeSubscriptionCheckpointSQLite(db, processorId, version, position, checkPosition, partition) {
981
- if (checkPosition !== null) {
982
- const updateResult = await db.command(
983
- sql(`
984
- UPDATE ${subscriptionsTable.name}
985
- SET last_processed_position = ?
986
- WHERE subscription_id = ?
987
- AND last_processed_position = ?
988
- AND partition = ?
989
- `),
990
- [position.toString(), processorId, checkPosition.toString(), partition]
991
- );
992
- if (updateResult.changes > 0) {
993
- return 1;
994
- } else {
995
- const current_position = await singleOrNull(
996
- db.query(
997
- sql(
998
- `SELECT last_processed_position FROM ${subscriptionsTable.name}
999
- WHERE subscription_id = ? AND partition = ?`
1000
- ),
1001
- [processorId, partition]
1002
- )
1003
- );
1004
- if (current_position?.last_processed_position === position) {
1005
- return 0;
1006
- } else if (position !== null && current_position !== null && current_position?.last_processed_position > position) {
1007
- return 2;
1008
- } else {
1009
- return 2;
1010
- }
1485
+ var eventInStream = (streamName, event) => {
1486
+ return {
1487
+ ...event,
1488
+ metadata: {
1489
+ ...event.metadata ?? {},
1490
+ streamName: event.metadata?.streamName ?? streamName
1011
1491
  }
1012
- } else {
1013
- try {
1014
- await db.command(
1015
- sql(
1016
- `INSERT INTO ${subscriptionsTable.name} (subscription_id, version, last_processed_position, partition) VALUES (?, ?, ?, ?)`
1017
- ),
1018
- [processorId, version, position.toString(), partition]
1019
- );
1020
- return 1;
1021
- } catch (err) {
1022
- if (!(isSQLiteError(err) && (err.errno === 19 || err.errno === 2067))) {
1023
- throw err;
1024
- }
1025
- const current = await singleOrNull(
1026
- db.query(
1027
- sql(
1028
- `SELECT last_processed_position FROM ${subscriptionsTable.name} WHERE subscription_id = ? AND partition = ?`
1029
- ),
1030
- [processorId, partition]
1031
- )
1032
- );
1033
- if (current?.last_processed_position === position) {
1034
- return 0;
1035
- } else {
1036
- return 2;
1037
- }
1492
+ };
1493
+ };
1494
+ var eventsInStream = (streamName, events) => {
1495
+ return events.map((e) => eventInStream(streamName, e));
1496
+ };
1497
+ var newEventsInStream = eventsInStream;
1498
+ var assertSQLQueryResultMatches = (sql2, rows) => async ({ connection }) => {
1499
+ const result = await connection.query(sql2);
1500
+ assertThatArray(rows).containsExactlyInAnyOrder(result);
1501
+ };
1502
+ var expectSQL = {
1503
+ query: (sql2) => ({
1504
+ resultRows: {
1505
+ toBeTheSame: (rows) => assertSQLQueryResultMatches(sql2, rows)
1038
1506
  }
1039
- }
1040
- }
1041
- async function storeProcessorCheckpoint(db, options) {
1042
- try {
1043
- const result = await storeSubscriptionCheckpointSQLite(
1044
- db,
1045
- options.processorId,
1046
- options.version ?? 1,
1047
- options.newPosition,
1048
- options.lastProcessedPosition,
1049
- options.partition ?? defaultTag
1050
- );
1051
- return result === 1 ? { success: true, newPosition: options.newPosition } : { success: false, reason: result === 0 ? "IGNORED" : "MISMATCH" };
1052
- } catch (error) {
1053
- console.log(error);
1054
- throw error;
1055
- }
1056
- }
1507
+ })
1508
+ };
1057
1509
  export {
1058
1510
  InMemorySQLiteDatabase,
1059
1511
  InMemorySharedCacheSQLiteDatabase,
1060
1512
  SQLiteEventStoreDefaultStreamVersion,
1513
+ SQLiteProjectionSpec,
1061
1514
  appendToStream,
1515
+ assertSQLQueryResultMatches,
1062
1516
  createEventStoreSchema,
1063
1517
  defaultTag,
1064
1518
  emmettPrefix,
1519
+ eventInStream,
1520
+ eventsInStream,
1521
+ expectSQL,
1065
1522
  getSQLiteEventStore,
1066
1523
  globalNames,
1067
1524
  globalTag,
1525
+ handleProjections,
1068
1526
  isSQLiteError,
1069
1527
  messagesTable,
1070
1528
  messagesTableSQL,
1529
+ newEventsInStream,
1071
1530
  readLastMessageGlobalPosition,
1072
1531
  readMessagesBatch,
1073
1532
  readProcessorCheckpoint,
@@ -1075,6 +1534,9 @@ export {
1075
1534
  schemaSQL,
1076
1535
  sql,
1077
1536
  sqliteConnection,
1537
+ sqliteProjection,
1538
+ sqliteRawBatchSQLProjection,
1539
+ sqliteRawSQLProjection,
1078
1540
  storeProcessorCheckpoint,
1079
1541
  streamsTable,
1080
1542
  streamsTableSQL,