@event-driven-io/emmett-sqlite 0.39.1 → 0.40.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -103,6 +103,10 @@ var rollbackTransaction = (db) => new Promise((resolve, reject) => {
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 = class _EmmettError extends Error {
107
111
  static __initStatic() {this.Codes = {
108
112
  ValidationError: 400,
@@ -169,6 +173,207 @@ var ExpectedVersionConflictError = class _ExpectedVersionConflictError extends C
169
173
  Object.setPrototypeOf(this, _ExpectedVersionConflictError.prototype);
170
174
  }
171
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
+ };
172
377
  var ParseError = class extends Error {
173
378
  constructor(text) {
174
379
  super(`Cannot parse! ${text}`);
@@ -190,9 +395,110 @@ var JSONParser = {
190
395
  return _optionalChain([options, 'optionalAccess', _13 => _13.map]) ? options.map(parsed) : parsed;
191
396
  }
192
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(_nullishCoalesce(message2, () => ( "That should not ever happened, right?")));
417
+ };
418
+ function assertTrue(condition, message2) {
419
+ if (condition !== true)
420
+ throw new AssertionError(_nullishCoalesce(message2, () => ( `Condition is false`)));
421
+ }
422
+ function assertOk(obj, message2) {
423
+ if (!obj) throw new AssertionError(_nullishCoalesce(message2, () => ( `Condition is not truthy`)));
424
+ }
425
+ function assertEqual(expected, actual, message2) {
426
+ if (expected !== actual)
427
+ throw new AssertionError(
428
+ `${_nullishCoalesce(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
+ _nullishCoalesce(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
+ };
193
500
  var getCheckpoint = (message2) => {
194
- return "checkpoint" in message2.metadata && // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
195
- isBigint(message2.metadata.checkpoint) ? (
501
+ return "checkpoint" in message2.metadata ? (
196
502
  // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
197
503
  message2.metadata.checkpoint
198
504
  ) : "globalPosition" in message2.metadata && // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
@@ -205,8 +511,41 @@ var getCheckpoint = (message2) => {
205
511
  message2.metadata.streamPosition
206
512
  ) : null;
207
513
  };
514
+ var projection = (definition) => definition;
208
515
 
209
- // src/eventStore/schema/appendToStream.ts
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
210
549
 
211
550
 
212
551
  // src/eventStore/schema/typing.ts
@@ -240,11 +579,200 @@ var subscriptionsTable = {
240
579
  name: `${emmettPrefix}_subscriptions`
241
580
  };
242
581
 
582
+ // src/eventStore/schema/tables.ts
583
+ var sql = (sql2) => sql2;
584
+ var streamsTableSQL = sql(
585
+ `CREATE TABLE IF NOT EXISTS ${streamsTable.name}(
586
+ stream_id TEXT NOT NULL,
587
+ stream_position BIGINT NOT NULL DEFAULT 0,
588
+ partition TEXT NOT NULL DEFAULT '${globalTag}',
589
+ stream_type TEXT NOT NULL,
590
+ stream_metadata JSONB NOT NULL,
591
+ is_archived BOOLEAN NOT NULL DEFAULT FALSE,
592
+ PRIMARY KEY (stream_id, partition, is_archived),
593
+ UNIQUE (stream_id, partition, is_archived)
594
+ );`
595
+ );
596
+ var messagesTableSQL = sql(
597
+ `CREATE TABLE IF NOT EXISTS ${messagesTable.name}(
598
+ stream_id TEXT NOT NULL,
599
+ stream_position BIGINT NOT NULL,
600
+ partition TEXT NOT NULL DEFAULT '${globalTag}',
601
+ message_kind CHAR(1) NOT NULL DEFAULT 'E',
602
+ message_data JSONB NOT NULL,
603
+ message_metadata JSONB NOT NULL,
604
+ message_schema_version TEXT NOT NULL,
605
+ message_type TEXT NOT NULL,
606
+ message_id TEXT NOT NULL,
607
+ is_archived BOOLEAN NOT NULL DEFAULT FALSE,
608
+ global_position INTEGER PRIMARY KEY,
609
+ created DATETIME DEFAULT CURRENT_TIMESTAMP,
610
+ UNIQUE (stream_id, stream_position, partition, is_archived)
611
+ );
612
+ `
613
+ );
614
+ var subscriptionsTableSQL = sql(
615
+ `
616
+ CREATE TABLE IF NOT EXISTS ${subscriptionsTable.name}(
617
+ subscription_id TEXT NOT NULL,
618
+ version INTEGER NOT NULL DEFAULT 1,
619
+ partition TEXT NOT NULL DEFAULT '${globalTag}',
620
+ last_processed_position BIGINT NOT NULL,
621
+ PRIMARY KEY (subscription_id, partition, version)
622
+ );
623
+ `
624
+ );
625
+ var schemaSQL = [
626
+ streamsTableSQL,
627
+ messagesTableSQL,
628
+ subscriptionsTableSQL
629
+ ];
630
+ var createEventStoreSchema = async (db) => {
631
+ for (const sql2 of schemaSQL) {
632
+ await db.command(sql2);
633
+ }
634
+ };
635
+
636
+ // src/eventStore/schema/utils.ts
637
+ var singleOrNull = async (getResult) => {
638
+ const result = await getResult;
639
+ if (result.length > 1) throw new Error("Query had more than one result");
640
+ return result.length > 0 ? _nullishCoalesce(result[0], () => ( null)) : null;
641
+ };
642
+
643
+ // src/eventStore/schema/readLastMessageGlobalPosition.ts
644
+ var readLastMessageGlobalPosition = async (db, options) => {
645
+ const result = await singleOrNull(
646
+ db.query(
647
+ sql(
648
+ `SELECT global_position
649
+ FROM ${messagesTable.name}
650
+ WHERE partition = ? AND is_archived = FALSE
651
+ ORDER BY global_position
652
+ LIMIT 1`
653
+ ),
654
+ [_nullishCoalesce(_optionalChain([options, 'optionalAccess', _14 => _14.partition]), () => ( defaultTag))]
655
+ )
656
+ );
657
+ return {
658
+ currentGlobalPosition: result !== null ? BigInt(result.global_position) : null
659
+ };
660
+ };
661
+
662
+ // src/eventStore/schema/readMessagesBatch.ts
663
+ var readMessagesBatch = async (db, options) => {
664
+ const from = "from" in options ? options.from : "after" in options ? options.after + 1n : 0n;
665
+ const batchSize = options && "batchSize" in options ? options.batchSize : options.to - options.from;
666
+ const fromCondition = from !== -0n ? `AND global_position >= ${from}` : "";
667
+ const toCondition = "to" in options ? `AND global_position <= ${options.to}` : "";
668
+ const limitCondition = "batchSize" in options ? `LIMIT ${options.batchSize}` : "";
669
+ const events = (await db.query(
670
+ sql(
671
+ `SELECT stream_id, stream_position, global_position, message_data, message_metadata, message_schema_version, message_type, message_id
672
+ FROM ${messagesTable.name}
673
+ WHERE partition = ? AND is_archived = FALSE ${fromCondition} ${toCondition}
674
+ ORDER BY global_position
675
+ ${limitCondition}`
676
+ ),
677
+ [_nullishCoalesce(_optionalChain([options, 'optionalAccess', _15 => _15.partition]), () => ( defaultTag))]
678
+ )).map((row) => {
679
+ const rawEvent = {
680
+ type: row.message_type,
681
+ data: JSONParser.parse(row.message_data),
682
+ metadata: JSONParser.parse(row.message_metadata)
683
+ };
684
+ const metadata = {
685
+ ..."metadata" in rawEvent ? _nullishCoalesce(rawEvent.metadata, () => ( {})) : {},
686
+ messageId: row.message_id,
687
+ streamName: row.stream_id,
688
+ streamPosition: BigInt(row.stream_position),
689
+ globalPosition: BigInt(row.global_position)
690
+ };
691
+ return {
692
+ ...rawEvent,
693
+ kind: "Event",
694
+ metadata
695
+ };
696
+ });
697
+ return events.length > 0 ? {
698
+ currentGlobalPosition: events[events.length - 1].metadata.globalPosition,
699
+ messages: events,
700
+ areEventsLeft: events.length === batchSize
701
+ } : {
702
+ currentGlobalPosition: "from" in options ? options.from : "after" in options ? options.after : 0n,
703
+ messages: [],
704
+ areEventsLeft: false
705
+ };
706
+ };
707
+
708
+ // src/eventStore/consumers/messageBatchProcessing/index.ts
709
+ var DefaultSQLiteEventStoreProcessorBatchSize = 100;
710
+ var DefaultSQLiteEventStoreProcessorPullingFrequencyInMs = 50;
711
+ var sqliteEventStoreMessageBatchPuller = ({
712
+ connection,
713
+ batchSize,
714
+ eachBatch,
715
+ pullingFrequencyInMs
716
+ }) => {
717
+ let isRunning = false;
718
+ let start;
719
+ const pullMessages = async (options) => {
720
+ const after = options.startFrom === "BEGINNING" ? 0n : options.startFrom === "END" ? await _asyncNullishCoalesce((await readLastMessageGlobalPosition(connection)).currentGlobalPosition, async () => ( 0n)) : options.startFrom.globalPosition;
721
+ const readMessagesOptions = {
722
+ after,
723
+ batchSize
724
+ };
725
+ let waitTime = 100;
726
+ do {
727
+ const { messages, currentGlobalPosition, areEventsLeft } = await readMessagesBatch(connection, readMessagesOptions);
728
+ if (messages.length > 0) {
729
+ const result = await eachBatch({ messages });
730
+ if (result && result.type === "STOP") {
731
+ isRunning = false;
732
+ break;
733
+ }
734
+ }
735
+ readMessagesOptions.after = currentGlobalPosition;
736
+ await new Promise((resolve) => setTimeout(resolve, waitTime));
737
+ if (!areEventsLeft) {
738
+ waitTime = Math.min(waitTime * 2, 1e3);
739
+ } else {
740
+ waitTime = pullingFrequencyInMs;
741
+ }
742
+ } while (isRunning);
743
+ };
744
+ return {
745
+ get isRunning() {
746
+ return isRunning;
747
+ },
748
+ start: (options) => {
749
+ if (isRunning) return start;
750
+ start = (async () => {
751
+ isRunning = true;
752
+ return pullMessages(options);
753
+ })();
754
+ return start;
755
+ },
756
+ stop: async () => {
757
+ if (!isRunning) return;
758
+ isRunning = false;
759
+ await start;
760
+ }
761
+ };
762
+ };
763
+ var zipSQLiteEventStoreMessageBatchPullerStartFrom = (options) => {
764
+ if (options.length === 0 || options.some((o) => o === void 0 || o === "BEGINNING"))
765
+ return "BEGINNING";
766
+ if (options.every((o) => o === "END")) return "END";
767
+ return options.filter((o) => o !== void 0 && o !== "BEGINNING" && o !== "END").sort((a, b) => a > b ? 1 : -1)[0];
768
+ };
769
+
243
770
  // src/eventStore/schema/appendToStream.ts
771
+
244
772
  var appendToStream = async (connection, streamName, streamType, messages, options) => {
245
773
  if (messages.length === 0) return { success: false };
246
774
  const expectedStreamVersion = toExpectedVersion(
247
- _optionalChain([options, 'optionalAccess', _14 => _14.expectedStreamVersion])
775
+ _optionalChain([options, 'optionalAccess', _16 => _16.expectedStreamVersion])
248
776
  );
249
777
  const messagesToAppend = messages.map(
250
778
  (m, i) => ({
@@ -269,7 +797,7 @@ var appendToStream = async (connection, streamName, streamType, messages, option
269
797
  expectedStreamVersion
270
798
  }
271
799
  );
272
- if (_optionalChain([options, 'optionalAccess', _15 => _15.onBeforeCommit]))
800
+ if (_optionalChain([options, 'optionalAccess', _17 => _17.onBeforeCommit]))
273
801
  await options.onBeforeCommit(messagesToAppend, { connection });
274
802
  return result;
275
803
  });
@@ -285,7 +813,7 @@ var appendToStreamRaw = async (connection, streamId, streamType, messages, optio
285
813
  let streamPosition;
286
814
  let globalPosition;
287
815
  try {
288
- let expectedStreamVersion = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _16 => _16.expectedStreamVersion]), () => ( null));
816
+ let expectedStreamVersion = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _18 => _18.expectedStreamVersion]), () => ( null));
289
817
  if (expectedStreamVersion == null) {
290
818
  expectedStreamVersion = await getLastStreamPosition(
291
819
  connection,
@@ -311,7 +839,7 @@ var appendToStreamRaw = async (connection, streamId, streamType, messages, optio
311
839
  [
312
840
  streamId,
313
841
  messages.length,
314
- _nullishCoalesce(_optionalChain([options, 'optionalAccess', _17 => _17.partition]), () => ( streamsTable.columns.partition)),
842
+ _nullishCoalesce(_optionalChain([options, 'optionalAccess', _19 => _19.partition]), () => ( streamsTable.columns.partition)),
315
843
  streamType
316
844
  ]
317
845
  );
@@ -327,7 +855,7 @@ var appendToStreamRaw = async (connection, streamId, streamType, messages, optio
327
855
  [
328
856
  messages.length,
329
857
  streamId,
330
- _nullishCoalesce(_optionalChain([options, 'optionalAccess', _18 => _18.partition]), () => ( streamsTable.columns.partition))
858
+ _nullishCoalesce(_optionalChain([options, 'optionalAccess', _20 => _20.partition]), () => ( streamsTable.columns.partition))
331
859
  ]
332
860
  );
333
861
  }
@@ -347,10 +875,10 @@ var appendToStreamRaw = async (connection, streamId, streamType, messages, optio
347
875
  messages,
348
876
  expectedStreamVersion,
349
877
  streamId,
350
- _nullishCoalesce(_optionalChain([options, 'optionalAccess', _19 => _19.partition, 'optionalAccess', _20 => _20.toString, 'call', _21 => _21()]), () => ( defaultTag))
878
+ _nullishCoalesce(_optionalChain([options, 'optionalAccess', _21 => _21.partition, 'optionalAccess', _22 => _22.toString, 'call', _23 => _23()]), () => ( defaultTag))
351
879
  );
352
880
  const returningIds = await connection.query(sqlString, values);
353
- if (returningIds.length === 0 || !_optionalChain([returningIds, 'access', _22 => _22[returningIds.length - 1], 'optionalAccess', _23 => _23.global_position])) {
881
+ if (returningIds.length === 0 || !_optionalChain([returningIds, 'access', _24 => _24[returningIds.length - 1], 'optionalAccess', _25 => _25.global_position])) {
354
882
  throw new Error("Could not find global position");
355
883
  }
356
884
  globalPosition = BigInt(
@@ -371,14 +899,14 @@ var appendToStreamRaw = async (connection, streamId, streamType, messages, optio
371
899
  };
372
900
  };
373
901
  var isOptimisticConcurrencyError = (error) => {
374
- return _optionalChain([error, 'optionalAccess', _24 => _24.errno]) !== void 0 && error.errno === 19;
902
+ return _optionalChain([error, 'optionalAccess', _26 => _26.errno]) !== void 0 && error.errno === 19;
375
903
  };
376
904
  async function getLastStreamPosition(connection, streamId, expectedStreamVersion) {
377
905
  const result = await connection.querySingle(
378
906
  `SELECT CAST(stream_position AS VARCHAR) AS stream_position FROM ${streamsTable.name} WHERE stream_id = ?`,
379
907
  [streamId]
380
908
  );
381
- if (_optionalChain([result, 'optionalAccess', _25 => _25.stream_position]) == null) {
909
+ if (_optionalChain([result, 'optionalAccess', _27 => _27.stream_position]) == null) {
382
910
  expectedStreamVersion = 0n;
383
911
  } else {
384
912
  expectedStreamVersion = BigInt(result.stream_position);
@@ -388,7 +916,7 @@ async function getLastStreamPosition(connection, streamId, expectedStreamVersion
388
916
  var buildMessageInsertQuery = (messages, expectedStreamVersion, streamId, partition) => {
389
917
  const query = messages.reduce(
390
918
  (queryBuilder, message) => {
391
- if (_optionalChain([message, 'access', _26 => _26.metadata, 'optionalAccess', _27 => _27.streamPosition]) == null || typeof message.metadata.streamPosition !== "bigint") {
919
+ if (_optionalChain([message, 'access', _28 => _28.metadata, 'optionalAccess', _29 => _29.streamPosition]) == null || typeof message.metadata.streamPosition !== "bigint") {
392
920
  throw new Error("Stream position is required");
393
921
  }
394
922
  const streamPosition = BigInt(message.metadata.streamPosition) + BigInt(expectedStreamVersion);
@@ -400,7 +928,7 @@ var buildMessageInsertQuery = (messages, expectedStreamVersion, streamId, partit
400
928
  message.kind === "Event" ? "E" : "C",
401
929
  JSONParser.stringify(message.data),
402
930
  JSONParser.stringify(message.metadata),
403
- _nullishCoalesce(_optionalChain([expectedStreamVersion, 'optionalAccess', _28 => _28.toString, 'call', _29 => _29()]), () => ( 0)),
931
+ _nullishCoalesce(_optionalChain([expectedStreamVersion, 'optionalAccess', _30 => _30.toString, 'call', _31 => _31()]), () => ( 0)),
404
932
  message.type,
405
933
  message.metadata.messageId,
406
934
  false
@@ -432,103 +960,39 @@ var buildMessageInsertQuery = (messages, expectedStreamVersion, streamId, partit
432
960
  return { sqlString, values: query.values };
433
961
  };
434
962
 
435
- // src/eventStore/schema/tables.ts
436
- var sql = (sql2) => sql2;
437
- var streamsTableSQL = sql(
438
- `CREATE TABLE IF NOT EXISTS ${streamsTable.name}(
439
- stream_id TEXT NOT NULL,
440
- stream_position BIGINT NOT NULL DEFAULT 0,
441
- partition TEXT NOT NULL DEFAULT '${globalTag}',
442
- stream_type TEXT NOT NULL,
443
- stream_metadata JSONB NOT NULL,
444
- is_archived BOOLEAN NOT NULL DEFAULT FALSE,
445
- PRIMARY KEY (stream_id, partition, is_archived),
446
- UNIQUE (stream_id, partition, is_archived)
447
- );`
448
- );
449
- var messagesTableSQL = sql(
450
- `CREATE TABLE IF NOT EXISTS ${messagesTable.name}(
451
- stream_id TEXT NOT NULL,
452
- stream_position BIGINT NOT NULL,
453
- partition TEXT NOT NULL DEFAULT '${globalTag}',
454
- message_kind CHAR(1) NOT NULL DEFAULT 'E',
455
- message_data JSONB NOT NULL,
456
- message_metadata JSONB NOT NULL,
457
- message_schema_version TEXT NOT NULL,
458
- message_type TEXT NOT NULL,
459
- message_id TEXT NOT NULL,
460
- is_archived BOOLEAN NOT NULL DEFAULT FALSE,
461
- global_position INTEGER PRIMARY KEY,
462
- created DATETIME DEFAULT CURRENT_TIMESTAMP,
463
- UNIQUE (stream_id, stream_position, partition, is_archived)
464
- );
465
- `
466
- );
467
- var subscriptionsTableSQL = sql(
468
- `
469
- CREATE TABLE IF NOT EXISTS ${subscriptionsTable.name}(
470
- subscription_id TEXT NOT NULL,
471
- version INTEGER NOT NULL DEFAULT 1,
472
- partition TEXT NOT NULL DEFAULT '${globalTag}',
473
- last_processed_position BIGINT NOT NULL,
474
- PRIMARY KEY (subscription_id, partition, version)
475
- );
476
- `
477
- );
478
- var schemaSQL = [
479
- streamsTableSQL,
480
- messagesTableSQL,
481
- subscriptionsTableSQL
482
- ];
483
- var createEventStoreSchema = async (db) => {
484
- for (const sql2 of schemaSQL) {
485
- await db.command(sql2);
486
- }
487
- };
488
-
489
- // src/eventStore/schema/utils.ts
490
- var singleOrNull = async (getResult) => {
491
- const result = await getResult;
492
- if (result.length > 1) throw new Error("Query had more than one result");
493
- return result.length > 0 ? _nullishCoalesce(result[0], () => ( null)) : null;
494
- };
495
-
496
- // src/eventStore/schema/readLastMessageGlobalPosition.ts
497
- var readLastMessageGlobalPosition = async (db, options) => {
963
+ // src/eventStore/schema/readProcessorCheckpoint.ts
964
+ var readProcessorCheckpoint = async (db, options) => {
498
965
  const result = await singleOrNull(
499
966
  db.query(
500
967
  sql(
501
- `SELECT global_position
502
- FROM ${messagesTable.name}
503
- WHERE partition = ? AND is_archived = FALSE
504
- ORDER BY global_position
505
- LIMIT 1`
968
+ `SELECT last_processed_position
969
+ FROM ${subscriptionsTable.name}
970
+ WHERE partition = ? AND subscription_id = ?
971
+ LIMIT 1`
506
972
  ),
507
- [_nullishCoalesce(_optionalChain([options, 'optionalAccess', _30 => _30.partition]), () => ( defaultTag))]
973
+ [_nullishCoalesce(_optionalChain([options, 'optionalAccess', _32 => _32.partition]), () => ( defaultTag)), options.processorId]
508
974
  )
509
975
  );
510
976
  return {
511
- currentGlobalPosition: result !== null ? BigInt(result.global_position) : null
977
+ lastProcessedPosition: result !== null ? BigInt(result.last_processed_position) : null
512
978
  };
513
979
  };
514
980
 
515
- // src/eventStore/schema/readMessagesBatch.ts
516
- var readMessagesBatch = async (db, options) => {
517
- const from = "from" in options ? options.from : "after" in options ? options.after + 1n : 0n;
518
- const batchSize = options && "batchSize" in options ? options.batchSize : options.to - options.from;
519
- const fromCondition = from !== -0n ? `AND global_position >= ${from}` : "";
520
- const toCondition = "to" in options ? `AND global_position <= ${options.to}` : "";
521
- const limitCondition = "batchSize" in options ? `LIMIT ${options.batchSize}` : "";
522
- const events = (await db.query(
523
- sql(
524
- `SELECT stream_id, stream_position, global_position, message_data, message_metadata, message_schema_version, message_type, message_id
981
+ // src/eventStore/schema/readStream.ts
982
+ var readStream = async (db, streamId, options) => {
983
+ const fromCondition = options && "from" in options ? `AND stream_position >= ${options.from}` : "";
984
+ const to = Number(
985
+ options && "to" in options ? options.to : options && "maxCount" in options && options.maxCount ? options.from + options.maxCount : NaN
986
+ );
987
+ const toCondition = !isNaN(to) ? `AND stream_position <= ${to}` : "";
988
+ const results = await db.query(
989
+ `SELECT stream_id, stream_position, global_position, message_data, message_metadata, message_schema_version, message_type, message_id
525
990
  FROM ${messagesTable.name}
526
- WHERE partition = ? AND is_archived = FALSE ${fromCondition} ${toCondition}
527
- ORDER BY global_position
528
- ${limitCondition}`
529
- ),
530
- [_nullishCoalesce(_optionalChain([options, 'optionalAccess', _31 => _31.partition]), () => ( defaultTag))]
531
- )).map((row) => {
991
+ WHERE stream_id = ? AND partition = ? AND is_archived = FALSE ${fromCondition} ${toCondition}
992
+ ORDER BY stream_position ASC`,
993
+ [streamId, _nullishCoalesce(_optionalChain([options, 'optionalAccess', _33 => _33.partition]), () => ( defaultTag))]
994
+ );
995
+ const messages = results.map((row) => {
532
996
  const rawEvent = {
533
997
  type: row.message_type,
534
998
  data: JSONParser.parse(row.message_data),
@@ -537,7 +1001,7 @@ var readMessagesBatch = async (db, options) => {
537
1001
  const metadata = {
538
1002
  ..."metadata" in rawEvent ? _nullishCoalesce(rawEvent.metadata, () => ( {})) : {},
539
1003
  messageId: row.message_id,
540
- streamName: row.stream_id,
1004
+ streamName: streamId,
541
1005
  streamPosition: BigInt(row.stream_position),
542
1006
  globalPosition: BigInt(row.global_position)
543
1007
  };
@@ -547,108 +1011,107 @@ var readMessagesBatch = async (db, options) => {
547
1011
  metadata
548
1012
  };
549
1013
  });
550
- return events.length > 0 ? {
551
- currentGlobalPosition: events[events.length - 1].metadata.globalPosition,
552
- messages: events,
553
- areEventsLeft: events.length === batchSize
1014
+ return messages.length > 0 ? {
1015
+ currentStreamVersion: messages[messages.length - 1].metadata.streamPosition,
1016
+ events: messages,
1017
+ streamExists: true
554
1018
  } : {
555
- currentGlobalPosition: "from" in options ? options.from : "after" in options ? options.after : 0n,
556
- messages: [],
557
- areEventsLeft: false
558
- };
559
- };
560
-
561
- // src/eventStore/schema/readProcessorCheckpoint.ts
562
- var readProcessorCheckpoint = async (db, options) => {
563
- const result = await singleOrNull(
564
- db.query(
565
- sql(
566
- `SELECT last_processed_position
567
- FROM ${subscriptionsTable.name}
568
- WHERE partition = ? AND subscription_id = ?
569
- LIMIT 1`
570
- ),
571
- [_nullishCoalesce(_optionalChain([options, 'optionalAccess', _32 => _32.partition]), () => ( defaultTag)), options.processorId]
572
- )
573
- );
574
- return {
575
- lastProcessedPosition: result !== null ? BigInt(result.last_processed_position) : null
576
- };
577
- };
578
-
579
- // src/eventStore/consumers/messageBatchProcessing/index.ts
580
- var DefaultSQLiteEventStoreProcessorBatchSize = 100;
581
- var DefaultSQLiteEventStoreProcessorPullingFrequencyInMs = 50;
582
- var sqliteEventStoreMessageBatchPuller = ({
583
- connection,
584
- batchSize,
585
- eachBatch,
586
- pullingFrequencyInMs
587
- }) => {
588
- let isRunning = false;
589
- let start;
590
- const pullMessages = async (options) => {
591
- const after = options.startFrom === "BEGINNING" ? 0n : options.startFrom === "END" ? await _asyncNullishCoalesce((await readLastMessageGlobalPosition(connection)).currentGlobalPosition, async () => ( 0n)) : options.startFrom.globalPosition;
592
- const readMessagesOptions = {
593
- after,
594
- batchSize
595
- };
596
- let waitTime = 100;
597
- do {
598
- const { messages, currentGlobalPosition, areEventsLeft } = await readMessagesBatch(connection, readMessagesOptions);
599
- if (messages.length > 0) {
600
- const result = await eachBatch({ messages });
601
- if (result && result.type === "STOP") {
602
- isRunning = false;
603
- break;
604
- }
1019
+ currentStreamVersion: SQLiteEventStoreDefaultStreamVersion,
1020
+ events: [],
1021
+ streamExists: false
1022
+ };
1023
+ };
1024
+
1025
+ // src/eventStore/schema/storeProcessorCheckpoint.ts
1026
+ async function storeSubscriptionCheckpointSQLite(db, processorId, version, position, checkPosition, partition) {
1027
+ if (checkPosition !== null) {
1028
+ const updateResult = await db.command(
1029
+ sql(`
1030
+ UPDATE ${subscriptionsTable.name}
1031
+ SET last_processed_position = ?
1032
+ WHERE subscription_id = ?
1033
+ AND last_processed_position = ?
1034
+ AND partition = ?
1035
+ `),
1036
+ [position.toString(), processorId, checkPosition.toString(), partition]
1037
+ );
1038
+ if (updateResult.changes > 0) {
1039
+ return 1;
1040
+ } else {
1041
+ const current_position = await singleOrNull(
1042
+ db.query(
1043
+ sql(
1044
+ `SELECT last_processed_position FROM ${subscriptionsTable.name}
1045
+ WHERE subscription_id = ? AND partition = ?`
1046
+ ),
1047
+ [processorId, partition]
1048
+ )
1049
+ );
1050
+ if (_optionalChain([current_position, 'optionalAccess', _34 => _34.last_processed_position]) === position) {
1051
+ return 0;
1052
+ } else if (position !== null && current_position !== null && _optionalChain([current_position, 'optionalAccess', _35 => _35.last_processed_position]) > position) {
1053
+ return 2;
1054
+ } else {
1055
+ return 2;
605
1056
  }
606
- readMessagesOptions.after = currentGlobalPosition;
607
- await new Promise((resolve) => setTimeout(resolve, waitTime));
608
- if (!areEventsLeft) {
609
- waitTime = Math.min(waitTime * 2, 1e3);
1057
+ }
1058
+ } else {
1059
+ try {
1060
+ await db.command(
1061
+ sql(
1062
+ `INSERT INTO ${subscriptionsTable.name} (subscription_id, version, last_processed_position, partition) VALUES (?, ?, ?, ?)`
1063
+ ),
1064
+ [processorId, version, position.toString(), partition]
1065
+ );
1066
+ return 1;
1067
+ } catch (err) {
1068
+ if (!(isSQLiteError(err) && (err.errno === 19 || err.errno === 2067))) {
1069
+ throw err;
1070
+ }
1071
+ const current = await singleOrNull(
1072
+ db.query(
1073
+ sql(
1074
+ `SELECT last_processed_position FROM ${subscriptionsTable.name} WHERE subscription_id = ? AND partition = ?`
1075
+ ),
1076
+ [processorId, partition]
1077
+ )
1078
+ );
1079
+ if (_optionalChain([current, 'optionalAccess', _36 => _36.last_processed_position]) === position) {
1080
+ return 0;
610
1081
  } else {
611
- waitTime = pullingFrequencyInMs;
1082
+ return 2;
612
1083
  }
613
- } while (isRunning);
614
- };
615
- return {
616
- get isRunning() {
617
- return isRunning;
618
- },
619
- start: (options) => {
620
- if (isRunning) return start;
621
- start = (async () => {
622
- isRunning = true;
623
- return pullMessages(options);
624
- })();
625
- return start;
626
- },
627
- stop: async () => {
628
- if (!isRunning) return;
629
- isRunning = false;
630
- await start;
631
1084
  }
632
- };
633
- };
634
- var zipSQLiteEventStoreMessageBatchPullerStartFrom = (options) => {
635
- if (options.length === 0 || options.some((o) => o === void 0 || o === "BEGINNING"))
636
- return "BEGINNING";
637
- if (options.every((o) => o === "END")) return "END";
638
- return options.filter((o) => o !== void 0 && o !== "BEGINNING" && o !== "END").sort((a, b) => a > b ? 1 : -1)[0];
639
- };
1085
+ }
1086
+ }
1087
+ async function storeProcessorCheckpoint(db, options) {
1088
+ try {
1089
+ const result = await storeSubscriptionCheckpointSQLite(
1090
+ db,
1091
+ options.processorId,
1092
+ _nullishCoalesce(options.version, () => ( 1)),
1093
+ options.newPosition,
1094
+ options.lastProcessedPosition,
1095
+ _nullishCoalesce(options.partition, () => ( defaultTag))
1096
+ );
1097
+ return result === 1 ? { success: true, newPosition: options.newPosition } : { success: false, reason: result === 0 ? "IGNORED" : "MISMATCH" };
1098
+ } catch (error) {
1099
+ console.log(error);
1100
+ throw error;
1101
+ }
1102
+ }
640
1103
 
641
1104
  // src/eventStore/consumers/sqliteProcessor.ts
642
1105
  var genericSQLiteProcessor = (options) => {
643
1106
  const { eachMessage } = options;
644
1107
  let isActive = true;
645
1108
  const getDb = (context) => {
646
- const fileName = _nullishCoalesce(context.fileName, () => ( _optionalChain([options, 'access', _33 => _33.connectionOptions, 'optionalAccess', _34 => _34.fileName])));
1109
+ const fileName = _nullishCoalesce(context.fileName, () => ( _optionalChain([options, 'access', _37 => _37.connectionOptions, 'optionalAccess', _38 => _38.fileName])));
647
1110
  if (!fileName)
648
1111
  throw new EmmettError(
649
1112
  `SQLite processor '${options.processorId}' is missing file name. Ensure that you passed it through options`
650
1113
  );
651
- const connection = _nullishCoalesce(_nullishCoalesce(context.connection, () => ( _optionalChain([options, 'access', _35 => _35.connectionOptions, 'optionalAccess', _36 => _36.connection]))), () => ( sqliteConnection({ fileName })));
1114
+ const connection = _nullishCoalesce(_nullishCoalesce(context.connection, () => ( _optionalChain([options, 'access', _39 => _39.connectionOptions, 'optionalAccess', _40 => _40.connection]))), () => ( sqliteConnection({ fileName })));
652
1115
  return { connection, fileName };
653
1116
  };
654
1117
  return {
@@ -750,7 +1213,7 @@ var sqliteEventStoreConsumer = (options) => {
750
1213
  })
751
1214
  );
752
1215
  return result.some(
753
- (r) => r.status === "fulfilled" && _optionalChain([r, 'access', _37 => _37.value, 'optionalAccess', _38 => _38.type]) !== "STOP"
1216
+ (r) => r.status === "fulfilled" && _optionalChain([r, 'access', _41 => _41.value, 'optionalAccess', _42 => _42.type]) !== "STOP"
754
1217
  ) ? void 0 : {
755
1218
  type: "STOP"
756
1219
  };
@@ -758,8 +1221,8 @@ var sqliteEventStoreConsumer = (options) => {
758
1221
  const messagePooler = currentMessagePuller = sqliteEventStoreMessageBatchPuller({
759
1222
  connection,
760
1223
  eachBatch,
761
- batchSize: _nullishCoalesce(_optionalChain([pulling, 'optionalAccess', _39 => _39.batchSize]), () => ( DefaultSQLiteEventStoreProcessorBatchSize)),
762
- pullingFrequencyInMs: _nullishCoalesce(_optionalChain([pulling, 'optionalAccess', _40 => _40.pullingFrequencyInMs]), () => ( DefaultSQLiteEventStoreProcessorPullingFrequencyInMs))
1224
+ batchSize: _nullishCoalesce(_optionalChain([pulling, 'optionalAccess', _43 => _43.batchSize]), () => ( DefaultSQLiteEventStoreProcessorBatchSize)),
1225
+ pullingFrequencyInMs: _nullishCoalesce(_optionalChain([pulling, 'optionalAccess', _44 => _44.pullingFrequencyInMs]), () => ( DefaultSQLiteEventStoreProcessorPullingFrequencyInMs))
763
1226
  });
764
1227
  const stop = async () => {
765
1228
  if (!isRunning) return;
@@ -806,20 +1269,6 @@ var sqliteEventStoreConsumer = (options) => {
806
1269
  };
807
1270
  };
808
1271
 
809
- // src/eventStore/projections/index.ts
810
- var handleProjections = async (options) => {
811
- const { projections: allProjections, events, connection } = options;
812
- const eventTypes = events.map((e) => e.type);
813
- for (const projection2 of allProjections) {
814
- if (!projection2.canHandle.some((type) => eventTypes.includes(type))) {
815
- continue;
816
- }
817
- await projection2.handle(events, {
818
- connection
819
- });
820
- }
821
- };
822
-
823
1272
  // src/eventStore/SQLiteEventStore.ts
824
1273
  var SQLiteEventStoreDefaultStreamVersion = 0n;
825
1274
  var getSQLiteEventStore = (options) => {
@@ -829,7 +1278,7 @@ var getSQLiteEventStore = (options) => {
829
1278
  const fileName = _nullishCoalesce(options.fileName, () => ( InMemorySQLiteDatabase));
830
1279
  const isInMemory = fileName === InMemorySQLiteDatabase || fileName === InMemorySharedCacheSQLiteDatabase;
831
1280
  const inlineProjections = (_nullishCoalesce(options.projections, () => ( []))).filter(({ type }) => type === "inline").map(({ projection: projection2 }) => projection2);
832
- const onBeforeCommitHook = _optionalChain([options, 'access', _41 => _41.hooks, 'optionalAccess', _42 => _42.onBeforeCommit]);
1281
+ const onBeforeCommitHook = _optionalChain([options, 'access', _45 => _45.hooks, 'optionalAccess', _46 => _46.onBeforeCommit]);
833
1282
  const createConnection = () => {
834
1283
  if (database != null) {
835
1284
  return database;
@@ -859,7 +1308,7 @@ var getSQLiteEventStore = (options) => {
859
1308
  }
860
1309
  };
861
1310
  if (options) {
862
- autoGenerateSchema = _optionalChain([options, 'access', _43 => _43.schema, 'optionalAccess', _44 => _44.autoMigration]) === void 0 || _optionalChain([options, 'access', _45 => _45.schema, 'optionalAccess', _46 => _46.autoMigration]) !== "None";
1311
+ autoGenerateSchema = _optionalChain([options, 'access', _47 => _47.schema, 'optionalAccess', _48 => _48.autoMigration]) === void 0 || _optionalChain([options, 'access', _49 => _49.schema, 'optionalAccess', _50 => _50.autoMigration]) !== "None";
863
1312
  }
864
1313
  const ensureSchemaExists = async (connection) => {
865
1314
  if (!autoGenerateSchema) return Promise.resolve();
@@ -872,7 +1321,7 @@ var getSQLiteEventStore = (options) => {
872
1321
  return {
873
1322
  async aggregateStream(streamName, options2) {
874
1323
  const { evolve, initialState, read } = options2;
875
- const expectedStreamVersion = _optionalChain([read, 'optionalAccess', _47 => _47.expectedStreamVersion]);
1324
+ const expectedStreamVersion = _optionalChain([read, 'optionalAccess', _51 => _51.expectedStreamVersion]);
876
1325
  let state = initialState();
877
1326
  if (typeof streamName !== "string") {
878
1327
  throw new Error("Stream name is not string");
@@ -926,7 +1375,7 @@ var getSQLiteEventStore = (options) => {
926
1375
  throw new ExpectedVersionConflictError(
927
1376
  -1n,
928
1377
  //TODO: Return actual version in case of error
929
- _nullishCoalesce(_optionalChain([options2, 'optionalAccess', _48 => _48.expectedStreamVersion]), () => ( NO_CONCURRENCY_CHECK))
1378
+ _nullishCoalesce(_optionalChain([options2, 'optionalAccess', _52 => _52.expectedStreamVersion]), () => ( NO_CONCURRENCY_CHECK))
930
1379
  );
931
1380
  return {
932
1381
  nextExpectedStreamVersion: appendResult.nextStreamPosition,
@@ -942,128 +1391,130 @@ var getSQLiteEventStore = (options) => {
942
1391
  };
943
1392
  };
944
1393
 
945
- // src/eventStore/schema/readStream.ts
946
- var readStream = async (db, streamId, options) => {
947
- const fromCondition = options && "from" in options ? `AND stream_position >= ${options.from}` : "";
948
- const to = Number(
949
- options && "to" in options ? options.to : options && "maxCount" in options && options.maxCount ? options.from + options.maxCount : NaN
950
- );
951
- const toCondition = !isNaN(to) ? `AND stream_position <= ${to}` : "";
952
- const results = await db.query(
953
- `SELECT stream_id, stream_position, global_position, message_data, message_metadata, message_schema_version, message_type, message_id
954
- FROM ${messagesTable.name}
955
- WHERE stream_id = ? AND partition = ? AND is_archived = FALSE ${fromCondition} ${toCondition}
956
- ORDER BY stream_position ASC`,
957
- [streamId, _nullishCoalesce(_optionalChain([options, 'optionalAccess', _49 => _49.partition]), () => ( defaultTag))]
958
- );
959
- const messages = results.map((row) => {
960
- const rawEvent = {
961
- type: row.message_type,
962
- data: JSONParser.parse(row.message_data),
963
- metadata: JSONParser.parse(row.message_metadata)
964
- };
965
- const metadata = {
966
- ..."metadata" in rawEvent ? _nullishCoalesce(rawEvent.metadata, () => ( {})) : {},
967
- messageId: row.message_id,
968
- streamName: streamId,
969
- streamPosition: BigInt(row.stream_position),
970
- globalPosition: BigInt(row.global_position)
971
- };
972
- return {
973
- ...rawEvent,
974
- kind: "Event",
975
- metadata
976
- };
977
- });
978
- return messages.length > 0 ? {
979
- currentStreamVersion: messages[messages.length - 1].metadata.streamPosition,
980
- events: messages,
981
- streamExists: true
982
- } : {
983
- currentStreamVersion: SQLiteEventStoreDefaultStreamVersion,
984
- events: [],
985
- streamExists: false
986
- };
1394
+ // src/eventStore/projections/sqliteProjectionSpec.ts
1395
+ var SQLiteProjectionSpec = {
1396
+ for: (options) => {
1397
+ {
1398
+ const connection = options.connection;
1399
+ const projection2 = options.projection;
1400
+ return (givenEvents) => {
1401
+ return {
1402
+ when: (events, options2) => {
1403
+ const allEvents = [];
1404
+ const run = async (connection2) => {
1405
+ let globalPosition = 0n;
1406
+ const numberOfTimes = _nullishCoalesce(_optionalChain([options2, 'optionalAccess', _53 => _53.numberOfTimes]), () => ( 1));
1407
+ for (const event of [
1408
+ ...givenEvents,
1409
+ ...Array.from({ length: numberOfTimes }).flatMap(() => events)
1410
+ ]) {
1411
+ const metadata = {
1412
+ globalPosition: ++globalPosition,
1413
+ streamPosition: globalPosition,
1414
+ streamName: `test-${_uuid.v4.call(void 0, )}`,
1415
+ messageId: _uuid.v4.call(void 0, )
1416
+ };
1417
+ allEvents.push({
1418
+ ...event,
1419
+ kind: "Event",
1420
+ metadata: {
1421
+ ...metadata,
1422
+ ..."metadata" in event ? _nullishCoalesce(event.metadata, () => ( {})) : {}
1423
+ }
1424
+ });
1425
+ }
1426
+ await connection2.withTransaction(
1427
+ () => handleProjections({
1428
+ events: allEvents,
1429
+ projections: [projection2],
1430
+ connection: connection2
1431
+ })
1432
+ );
1433
+ };
1434
+ return {
1435
+ then: async (assert, message) => {
1436
+ try {
1437
+ await run(connection);
1438
+ const succeeded = await assert({
1439
+ connection
1440
+ });
1441
+ if (succeeded !== void 0 && succeeded === false)
1442
+ assertFails(
1443
+ _nullishCoalesce(message, () => ( "Projection specification didn't match the criteria"))
1444
+ );
1445
+ } finally {
1446
+ connection.close();
1447
+ }
1448
+ },
1449
+ thenThrows: async (...args) => {
1450
+ try {
1451
+ await run(connection);
1452
+ throw new AssertionError("Handler did not fail as expected");
1453
+ } catch (error) {
1454
+ if (error instanceof AssertionError) throw error;
1455
+ if (args.length === 0) return;
1456
+ if (!isErrorConstructor(args[0])) {
1457
+ assertTrue(
1458
+ args[0](error),
1459
+ `Error didn't match the error condition: ${_optionalChain([error, 'optionalAccess', _54 => _54.toString, 'call', _55 => _55()])}`
1460
+ );
1461
+ return;
1462
+ }
1463
+ assertTrue(
1464
+ error instanceof args[0],
1465
+ `Caught error is not an instance of the expected type: ${_optionalChain([error, 'optionalAccess', _56 => _56.toString, 'call', _57 => _57()])}`
1466
+ );
1467
+ if (args[1]) {
1468
+ assertTrue(
1469
+ args[1](error),
1470
+ `Error didn't match the error condition: ${_optionalChain([error, 'optionalAccess', _58 => _58.toString, 'call', _59 => _59()])}`
1471
+ );
1472
+ }
1473
+ } finally {
1474
+ connection.close();
1475
+ }
1476
+ }
1477
+ };
1478
+ }
1479
+ };
1480
+ };
1481
+ }
1482
+ }
987
1483
  };
988
-
989
- // src/eventStore/schema/storeProcessorCheckpoint.ts
990
- async function storeSubscriptionCheckpointSQLite(db, processorId, version, position, checkPosition, partition) {
991
- if (checkPosition !== null) {
992
- const updateResult = await db.command(
993
- sql(`
994
- UPDATE ${subscriptionsTable.name}
995
- SET last_processed_position = ?
996
- WHERE subscription_id = ?
997
- AND last_processed_position = ?
998
- AND partition = ?
999
- `),
1000
- [position.toString(), processorId, checkPosition.toString(), partition]
1001
- );
1002
- if (updateResult.changes > 0) {
1003
- return 1;
1004
- } else {
1005
- const current_position = await singleOrNull(
1006
- db.query(
1007
- sql(
1008
- `SELECT last_processed_position FROM ${subscriptionsTable.name}
1009
- WHERE subscription_id = ? AND partition = ?`
1010
- ),
1011
- [processorId, partition]
1012
- )
1013
- );
1014
- if (_optionalChain([current_position, 'optionalAccess', _50 => _50.last_processed_position]) === position) {
1015
- return 0;
1016
- } else if (position !== null && current_position !== null && _optionalChain([current_position, 'optionalAccess', _51 => _51.last_processed_position]) > position) {
1017
- return 2;
1018
- } else {
1019
- return 2;
1020
- }
1484
+ var eventInStream = (streamName, event) => {
1485
+ return {
1486
+ ...event,
1487
+ metadata: {
1488
+ ..._nullishCoalesce(event.metadata, () => ( {})),
1489
+ streamName: _nullishCoalesce(_optionalChain([event, 'access', _60 => _60.metadata, 'optionalAccess', _61 => _61.streamName]), () => ( streamName))
1021
1490
  }
1022
- } else {
1023
- try {
1024
- await db.command(
1025
- sql(
1026
- `INSERT INTO ${subscriptionsTable.name} (subscription_id, version, last_processed_position, partition) VALUES (?, ?, ?, ?)`
1027
- ),
1028
- [processorId, version, position.toString(), partition]
1029
- );
1030
- return 1;
1031
- } catch (err) {
1032
- if (!(isSQLiteError(err) && (err.errno === 19 || err.errno === 2067))) {
1033
- throw err;
1034
- }
1035
- const current = await singleOrNull(
1036
- db.query(
1037
- sql(
1038
- `SELECT last_processed_position FROM ${subscriptionsTable.name} WHERE subscription_id = ? AND partition = ?`
1039
- ),
1040
- [processorId, partition]
1041
- )
1042
- );
1043
- if (_optionalChain([current, 'optionalAccess', _52 => _52.last_processed_position]) === position) {
1044
- return 0;
1045
- } else {
1046
- return 2;
1047
- }
1491
+ };
1492
+ };
1493
+ var eventsInStream = (streamName, events) => {
1494
+ return events.map((e) => eventInStream(streamName, e));
1495
+ };
1496
+ var newEventsInStream = eventsInStream;
1497
+ var assertSQLQueryResultMatches = (sql2, rows) => async ({ connection }) => {
1498
+ const result = await connection.query(sql2);
1499
+ assertThatArray(rows).containsExactlyInAnyOrder(result);
1500
+ };
1501
+ var expectSQL = {
1502
+ query: (sql2) => ({
1503
+ resultRows: {
1504
+ toBeTheSame: (rows) => assertSQLQueryResultMatches(sql2, rows)
1048
1505
  }
1049
- }
1050
- }
1051
- async function storeProcessorCheckpoint(db, options) {
1052
- try {
1053
- const result = await storeSubscriptionCheckpointSQLite(
1054
- db,
1055
- options.processorId,
1056
- _nullishCoalesce(options.version, () => ( 1)),
1057
- options.newPosition,
1058
- options.lastProcessedPosition,
1059
- _nullishCoalesce(options.partition, () => ( defaultTag))
1060
- );
1061
- return result === 1 ? { success: true, newPosition: options.newPosition } : { success: false, reason: result === 0 ? "IGNORED" : "MISMATCH" };
1062
- } catch (error) {
1063
- console.log(error);
1064
- throw error;
1065
- }
1066
- }
1506
+ })
1507
+ };
1508
+
1509
+
1510
+
1511
+
1512
+
1513
+
1514
+
1515
+
1516
+
1517
+
1067
1518
 
1068
1519
 
1069
1520
 
@@ -1090,5 +1541,5 @@ async function storeProcessorCheckpoint(db, options) {
1090
1541
 
1091
1542
 
1092
1543
 
1093
- exports.InMemorySQLiteDatabase = InMemorySQLiteDatabase; exports.InMemorySharedCacheSQLiteDatabase = InMemorySharedCacheSQLiteDatabase; exports.SQLiteEventStoreDefaultStreamVersion = SQLiteEventStoreDefaultStreamVersion; exports.appendToStream = appendToStream; exports.createEventStoreSchema = createEventStoreSchema; exports.defaultTag = defaultTag; exports.emmettPrefix = emmettPrefix; exports.getSQLiteEventStore = getSQLiteEventStore; exports.globalNames = globalNames; exports.globalTag = globalTag; exports.isSQLiteError = isSQLiteError; exports.messagesTable = messagesTable; exports.messagesTableSQL = messagesTableSQL; exports.readLastMessageGlobalPosition = readLastMessageGlobalPosition; exports.readMessagesBatch = readMessagesBatch; exports.readProcessorCheckpoint = readProcessorCheckpoint; exports.readStream = readStream; exports.schemaSQL = schemaSQL; exports.sql = sql; exports.sqliteConnection = sqliteConnection; exports.storeProcessorCheckpoint = storeProcessorCheckpoint; exports.streamsTable = streamsTable; exports.streamsTableSQL = streamsTableSQL; exports.subscriptionsTable = subscriptionsTable; exports.subscriptionsTableSQL = subscriptionsTableSQL;
1544
+ exports.InMemorySQLiteDatabase = InMemorySQLiteDatabase; exports.InMemorySharedCacheSQLiteDatabase = InMemorySharedCacheSQLiteDatabase; exports.SQLiteEventStoreDefaultStreamVersion = SQLiteEventStoreDefaultStreamVersion; exports.SQLiteProjectionSpec = SQLiteProjectionSpec; exports.appendToStream = appendToStream; exports.assertSQLQueryResultMatches = assertSQLQueryResultMatches; exports.createEventStoreSchema = createEventStoreSchema; exports.defaultTag = defaultTag; exports.emmettPrefix = emmettPrefix; exports.eventInStream = eventInStream; exports.eventsInStream = eventsInStream; exports.expectSQL = expectSQL; exports.getSQLiteEventStore = getSQLiteEventStore; exports.globalNames = globalNames; exports.globalTag = globalTag; exports.handleProjections = handleProjections; exports.isSQLiteError = isSQLiteError; exports.messagesTable = messagesTable; exports.messagesTableSQL = messagesTableSQL; exports.newEventsInStream = newEventsInStream; exports.readLastMessageGlobalPosition = readLastMessageGlobalPosition; exports.readMessagesBatch = readMessagesBatch; exports.readProcessorCheckpoint = readProcessorCheckpoint; exports.readStream = readStream; exports.schemaSQL = schemaSQL; exports.sql = sql; exports.sqliteConnection = sqliteConnection; exports.sqliteProjection = sqliteProjection; exports.sqliteRawBatchSQLProjection = sqliteRawBatchSQLProjection; exports.sqliteRawSQLProjection = sqliteRawSQLProjection; exports.storeProcessorCheckpoint = storeProcessorCheckpoint; exports.streamsTable = streamsTable; exports.streamsTableSQL = streamsTableSQL; exports.subscriptionsTable = subscriptionsTable; exports.subscriptionsTableSQL = subscriptionsTableSQL;
1094
1545
  //# sourceMappingURL=index.cjs.map