@event-driven-io/emmett-sqlite 0.43.0-alpha.1 → 0.43.0-alpha.4

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
@@ -76,6 +76,10 @@ var ExpectedVersionConflictError = class _ExpectedVersionConflictError extends C
76
76
  Object.setPrototypeOf(this, _ExpectedVersionConflictError.prototype);
77
77
  }
78
78
  };
79
+ var isExpectedVersionConflictError = (error2) => error2 instanceof ExpectedVersionConflictError || EmmettError.isInstanceOf(
80
+ error2,
81
+ ExpectedVersionConflictError.Codes.ConcurrencyError
82
+ );
79
83
  var isPrimitive = (value) => {
80
84
  const type = typeof value;
81
85
  return value === null || value === void 0 || type === "boolean" || type === "number" || type === "string" || type === "symbol" || type === "bigint";
@@ -511,27 +515,142 @@ var sqliteRawSQLProjection = (options) => {
511
515
  };
512
516
 
513
517
  // src/eventStore/projections/sqliteProjectionSpec.ts
518
+ import { dumbo } from "@event-driven-io/dumbo";
519
+ import { v4 as uuid5 } from "uuid";
520
+ var SQLiteProjectionSpec = {
521
+ for: (options) => {
522
+ {
523
+ const pool = options.pool ?? dumbo({
524
+ ...options.driver.mapToDumboOptions(options)
525
+ });
526
+ const projection2 = options.projection;
527
+ let wasInitialized = false;
528
+ return (givenEvents) => {
529
+ return {
530
+ when: (events, options2) => {
531
+ const allEvents = [];
532
+ const run = async (connection) => {
533
+ let globalPosition = 0n;
534
+ const numberOfTimes = options2?.numberOfTimes ?? 1;
535
+ for (const event of [
536
+ ...givenEvents,
537
+ ...Array.from({ length: numberOfTimes }).flatMap(() => events)
538
+ ]) {
539
+ const metadata = {
540
+ globalPosition: ++globalPosition,
541
+ streamPosition: globalPosition,
542
+ streamName: `test-${uuid5()}`,
543
+ messageId: uuid5()
544
+ };
545
+ allEvents.push({
546
+ ...event,
547
+ kind: "Event",
548
+ metadata: {
549
+ ...metadata,
550
+ ..."metadata" in event ? event.metadata ?? {} : {}
551
+ }
552
+ });
553
+ }
554
+ if (!wasInitialized && projection2.init) {
555
+ await projection2.init({
556
+ registrationType: "async",
557
+ status: "active",
558
+ context: { connection },
559
+ version: projection2.version ?? 1
560
+ });
561
+ wasInitialized = true;
562
+ }
563
+ await connection.withTransaction(
564
+ () => handleProjections({
565
+ events: allEvents,
566
+ projections: [projection2],
567
+ connection
568
+ })
569
+ );
570
+ };
571
+ return {
572
+ then: (assert, message) => pool.withConnection(async (connection) => {
573
+ await run(connection);
574
+ const succeeded = await assert({
575
+ connection
576
+ });
577
+ if (succeeded !== void 0 && succeeded === false)
578
+ assertFails(
579
+ message ?? "Projection specification didn't match the criteria"
580
+ );
581
+ }),
582
+ thenThrows: (...args) => pool.withConnection(async (connection) => {
583
+ try {
584
+ await run(connection);
585
+ throw new AssertionError(
586
+ "Handler did not fail as expected"
587
+ );
588
+ } catch (error) {
589
+ if (error instanceof AssertionError) throw error;
590
+ if (args.length === 0) return;
591
+ if (!isErrorConstructor(args[0])) {
592
+ assertTrue(
593
+ args[0](error),
594
+ `Error didn't match the error condition: ${error?.toString()}`
595
+ );
596
+ return;
597
+ }
598
+ assertTrue(
599
+ error instanceof args[0],
600
+ `Caught error is not an instance of the expected type: ${error?.toString()}`
601
+ );
602
+ if (args[1]) {
603
+ assertTrue(
604
+ args[1](error),
605
+ `Error didn't match the error condition: ${error?.toString()}`
606
+ );
607
+ }
608
+ }
609
+ })
610
+ };
611
+ }
612
+ };
613
+ };
614
+ }
615
+ }
616
+ };
617
+ var eventInStream = (streamName, event) => {
618
+ return {
619
+ ...event,
620
+ metadata: {
621
+ ...event.metadata ?? {},
622
+ streamName: event.metadata?.streamName ?? streamName
623
+ }
624
+ };
625
+ };
626
+ var eventsInStream = (streamName, events) => {
627
+ return events.map((e) => eventInStream(streamName, e));
628
+ };
629
+ var newEventsInStream = eventsInStream;
630
+ var assertSQLQueryResultMatches = (sql, rows) => async ({
631
+ connection
632
+ }) => {
633
+ const result = await connection.execute.query(sql);
634
+ assertThatArray(rows).containsExactlyInAnyOrder(result.rows);
635
+ };
636
+ var expectSQL = {
637
+ query: (sql) => ({
638
+ resultRows: {
639
+ toBeTheSame: (rows) => assertSQLQueryResultMatches(sql, rows)
640
+ }
641
+ })
642
+ };
643
+
644
+ // src/eventStore/schema/appendToStream.ts
514
645
  import {
515
- JSONSerializer as JSONSerializer2
646
+ BatchCommandNoChangesError,
647
+ DumboError,
648
+ singleOrNull,
649
+ SQL,
650
+ UniqueConstraintError
516
651
  } from "@event-driven-io/dumbo";
517
- import {
518
- InMemorySQLiteDatabase as InMemorySQLiteDatabase2,
519
- sqlite3Connection as sqlite3Connection2
520
- } from "@event-driven-io/dumbo/sqlite3";
521
652
  import { v4 as uuid6 } from "uuid";
522
653
 
523
- // src/eventStore/SQLiteEventStore.ts
524
- import {
525
- InMemorySQLiteDatabase,
526
- sqlite3Pool as sqlite3Pool2
527
- } from "@event-driven-io/dumbo/sqlite3";
528
-
529
- // src/eventStore/consumers/messageBatchProcessing/index.ts
530
- import "@event-driven-io/dumbo/sqlite3";
531
-
532
- // src/eventStore/schema/readLastMessageGlobalPosition.ts
533
- import { SQL, singleOrNull } from "@event-driven-io/dumbo";
534
-
535
654
  // src/eventStore/schema/typing.ts
536
655
  var emmettPrefix2 = "emt";
537
656
  var globalTag = "global";
@@ -567,156 +686,8 @@ var projectionsTable = {
567
686
  name: `${emmettPrefix2}_projections`
568
687
  };
569
688
 
570
- // src/eventStore/schema/readLastMessageGlobalPosition.ts
571
- var { identifier } = SQL;
572
- var readLastMessageGlobalPosition = async (execute, options) => {
573
- const result = await singleOrNull(
574
- execute.query(
575
- SQL`
576
- SELECT global_position
577
- FROM ${identifier(messagesTable.name)}
578
- WHERE partition = ${options?.partition ?? defaultTag2} AND is_archived = FALSE
579
- ORDER BY global_position
580
- LIMIT 1`
581
- )
582
- );
583
- return {
584
- currentGlobalPosition: result !== null ? BigInt(result.global_position) : null
585
- };
586
- };
587
-
588
- // src/eventStore/schema/readMessagesBatch.ts
589
- import { SQL as SQL2 } from "@event-driven-io/dumbo";
590
- var { identifier: identifier2 } = SQL2;
591
- var readMessagesBatch = async (execute, options) => {
592
- const from = "from" in options ? options.from : "after" in options ? options.after + 1n : 0n;
593
- const batchSize = options && "batchSize" in options ? options.batchSize : options.to - options.from;
594
- const fromCondition = from !== -0n ? SQL2`AND global_position >= ${from}` : "";
595
- const toCondition = "to" in options ? SQL2`AND global_position <= ${options.to}` : SQL2.EMPTY;
596
- const limitCondition = "batchSize" in options ? SQL2`LIMIT ${options.batchSize}` : SQL2.EMPTY;
597
- const events = (await execute.query(
598
- SQL2`SELECT stream_id, stream_position, global_position, message_data, message_metadata, message_schema_version, message_type, message_id
599
- FROM ${identifier2(messagesTable.name)}
600
- WHERE partition = ${options?.partition ?? defaultTag2} AND is_archived = FALSE ${fromCondition} ${toCondition}
601
- ORDER BY global_position
602
- ${limitCondition}`
603
- )).rows.map((row) => {
604
- const rawEvent = {
605
- type: row.message_type,
606
- data: JSONParser.parse(row.message_data),
607
- metadata: JSONParser.parse(row.message_metadata)
608
- };
609
- const metadata = {
610
- ..."metadata" in rawEvent ? rawEvent.metadata ?? {} : {},
611
- messageId: row.message_id,
612
- streamName: row.stream_id,
613
- streamPosition: BigInt(row.stream_position),
614
- globalPosition: BigInt(row.global_position)
615
- };
616
- return {
617
- ...rawEvent,
618
- kind: "Event",
619
- metadata
620
- };
621
- });
622
- return events.length > 0 ? {
623
- currentGlobalPosition: events[events.length - 1].metadata.globalPosition,
624
- messages: events,
625
- areEventsLeft: events.length === batchSize
626
- } : {
627
- currentGlobalPosition: "from" in options ? options.from : "after" in options ? options.after : 0n,
628
- messages: [],
629
- areEventsLeft: false
630
- };
631
- };
632
-
633
- // src/eventStore/consumers/messageBatchProcessing/index.ts
634
- var DefaultSQLiteEventStoreProcessorBatchSize = 100;
635
- var DefaultSQLiteEventStoreProcessorPullingFrequencyInMs = 50;
636
- var sqliteEventStoreMessageBatchPuller = ({
637
- pool,
638
- batchSize,
639
- eachBatch,
640
- pullingFrequencyInMs
641
- }) => {
642
- let isRunning = false;
643
- let start;
644
- const pullMessages = async (options) => {
645
- const after = options.startFrom === "BEGINNING" ? 0n : options.startFrom === "END" ? (await pool.withConnection(
646
- async ({ execute }) => readLastMessageGlobalPosition(execute)
647
- )).currentGlobalPosition ?? 0n : options.startFrom.globalPosition;
648
- const readMessagesOptions = {
649
- after,
650
- batchSize
651
- };
652
- let waitTime = 100;
653
- do {
654
- const { messages, currentGlobalPosition, areEventsLeft } = await pool.withConnection(
655
- ({ execute }) => readMessagesBatch(execute, readMessagesOptions)
656
- );
657
- if (messages.length > 0) {
658
- const result = await eachBatch({ messages });
659
- if (result && result.type === "STOP") {
660
- isRunning = false;
661
- break;
662
- }
663
- }
664
- readMessagesOptions.after = currentGlobalPosition;
665
- await new Promise((resolve) => setTimeout(resolve, waitTime));
666
- if (!areEventsLeft) {
667
- waitTime = Math.min(waitTime * 2, 1e3);
668
- } else {
669
- waitTime = pullingFrequencyInMs;
670
- }
671
- } while (isRunning);
672
- };
673
- return {
674
- get isRunning() {
675
- return isRunning;
676
- },
677
- start: (options) => {
678
- if (isRunning) return start;
679
- start = (async () => {
680
- isRunning = true;
681
- return pullMessages(options);
682
- })();
683
- return start;
684
- },
685
- stop: async () => {
686
- if (!isRunning) return;
687
- isRunning = false;
688
- await start;
689
- }
690
- };
691
- };
692
- var zipSQLiteEventStoreMessageBatchPullerStartFrom = (options) => {
693
- if (options.length === 0 || options.some((o) => o === void 0 || o === "BEGINNING"))
694
- return "BEGINNING";
695
- if (options.every((o) => o === "END")) return "END";
696
- return options.filter((o) => o !== void 0 && o !== "BEGINNING" && o !== "END").sort((a, b) => a > b ? 1 : -1)[0];
697
- };
698
-
699
- // src/eventStore/consumers/sqliteEventStoreConsumer.ts
700
- import {
701
- sqlite3Pool
702
- } from "@event-driven-io/dumbo/sqlite3";
703
-
704
- // src/eventStore/consumers/sqliteProcessor.ts
705
- import { JSONSerializer } from "@event-driven-io/dumbo";
706
- import {
707
- sqlite3Connection
708
- } from "@event-driven-io/dumbo/sqlite3";
709
-
710
689
  // src/eventStore/schema/appendToStream.ts
711
- import {
712
- singleOrNull as singleOrNull2,
713
- SQL as SQL3
714
- } from "@event-driven-io/dumbo";
715
- import {
716
- isSQLiteError
717
- } from "@event-driven-io/dumbo/sqlite3";
718
- import { v4 as uuid5 } from "uuid";
719
- var { identifier: identifier3, merge } = SQL3;
690
+ var { identifier, merge } = SQL;
720
691
  var appendToStream = async (connection, streamName, streamType, messages, options) => {
721
692
  if (messages.length === 0) return { success: false };
722
693
  const expectedStreamVersion = toExpectedVersion(
@@ -728,29 +699,43 @@ var appendToStream = async (connection, streamName, streamType, messages, option
728
699
  kind: m.kind ?? "Event",
729
700
  metadata: {
730
701
  streamName,
731
- messageId: uuid5(),
702
+ messageId: uuid6(),
732
703
  streamPosition: BigInt(i + 1),
733
704
  ..."metadata" in m ? m.metadata ?? {} : {}
734
705
  }
735
706
  })
736
707
  );
737
- return await connection.withTransaction(
738
- async (transaction) => {
739
- const result = await appendToStreamRaw(
740
- transaction.execute,
741
- streamName,
742
- streamType,
743
- downcastRecordedMessages(messagesToAppend, options?.schema?.versioning),
744
- {
745
- expectedStreamVersion
746
- }
747
- );
748
- if (options?.onBeforeCommit)
749
- await options.onBeforeCommit(messagesToAppend, { connection });
750
- return { success: true, result };
708
+ try {
709
+ return await connection.withTransaction(
710
+ async (transaction) => {
711
+ const result = await appendToStreamRaw(
712
+ transaction.execute,
713
+ streamName,
714
+ streamType,
715
+ downcastRecordedMessages(
716
+ messagesToAppend,
717
+ options?.schema?.versioning
718
+ ),
719
+ {
720
+ expectedStreamVersion
721
+ }
722
+ );
723
+ if (options?.onBeforeCommit)
724
+ await options.onBeforeCommit(messagesToAppend, { connection });
725
+ return { success: true, result };
726
+ }
727
+ );
728
+ } catch (err) {
729
+ if (isExpectedVersionConflictError(err) || DumboError.isInstanceOf(err, {
730
+ errorType: UniqueConstraintError.ErrorType
731
+ }) || DumboError.isInstanceOf(err, {
732
+ errorType: BatchCommandNoChangesError.ErrorType
733
+ })) {
734
+ return { success: false };
751
735
  }
752
- );
753
- };
736
+ throw err;
737
+ }
738
+ };
754
739
  var toExpectedVersion = (expected) => {
755
740
  if (expected === void 0) return null;
756
741
  if (expected === NO_CONCURRENCY_CHECK) return null;
@@ -759,22 +744,20 @@ var toExpectedVersion = (expected) => {
759
744
  return expected;
760
745
  };
761
746
  var appendToStreamRaw = async (execute, streamId, streamType, messages, options) => {
762
- let streamPosition;
763
- let globalPosition;
764
- try {
765
- let expectedStreamVersion = options?.expectedStreamVersion ?? null;
766
- if (expectedStreamVersion == null) {
767
- expectedStreamVersion = await getLastStreamPosition(
768
- execute,
769
- streamId,
770
- expectedStreamVersion
771
- );
772
- }
773
- let position;
774
- if (expectedStreamVersion === 0n) {
775
- position = await singleOrNull2(
776
- execute.query(
777
- SQL3`INSERT INTO ${identifier3(streamsTable.name)}
747
+ let expectedStreamVersion = options?.expectedStreamVersion ?? null;
748
+ const currentStreamVersion = await getLastStreamPosition(
749
+ execute,
750
+ streamId,
751
+ expectedStreamVersion
752
+ );
753
+ expectedStreamVersion ??= currentStreamVersion ?? 0n;
754
+ if (expectedStreamVersion !== currentStreamVersion) {
755
+ throw new ExpectedVersionConflictError(
756
+ currentStreamVersion,
757
+ expectedStreamVersion
758
+ );
759
+ }
760
+ const streamSQL = expectedStreamVersion === 0n ? SQL`INSERT INTO ${identifier(streamsTable.name)}
778
761
  (stream_id, stream_position, partition, stream_type, stream_metadata, is_archived)
779
762
  VALUES (
780
763
  ${streamId},
@@ -785,68 +768,37 @@ var appendToStreamRaw = async (execute, streamId, streamType, messages, options)
785
768
  false
786
769
  )
787
770
  RETURNING stream_position;
788
- `
789
- )
790
- );
791
- } else {
792
- position = await singleOrNull2(
793
- execute.query(
794
- SQL3`UPDATE ${identifier3(streamsTable.name)}
771
+ ` : SQL`UPDATE ${identifier(streamsTable.name)}
795
772
  SET stream_position = stream_position + ${messages.length}
796
773
  WHERE stream_id = ${streamId}
774
+ AND stream_position = ${expectedStreamVersion}
797
775
  AND partition = ${options?.partition ?? streamsTable.columns.partition}
798
776
  AND is_archived = false
799
777
  RETURNING stream_position;
800
- `
801
- )
802
- );
803
- }
804
- if (position == null) {
805
- throw new Error("Could not find stream position");
806
- }
807
- streamPosition = BigInt(position.stream_position);
808
- if (expectedStreamVersion != null) {
809
- const expectedStreamPositionAfterSave = BigInt(expectedStreamVersion) + BigInt(messages.length);
810
- if (streamPosition !== expectedStreamPositionAfterSave) {
811
- return {
812
- success: false
813
- };
814
- }
815
- }
816
- const insertSQL = buildMessageInsertQuery(
817
- messages,
818
- expectedStreamVersion,
819
- streamId,
820
- options?.partition?.toString() ?? defaultTag2
821
- );
822
- const { rows: returningIds } = await execute.query(insertSQL);
823
- if (returningIds.length === 0 || !returningIds[returningIds.length - 1]?.global_position) {
824
- throw new Error("Could not find global position");
825
- }
826
- globalPosition = BigInt(
827
- returningIds[returningIds.length - 1].global_position
828
- );
829
- } catch (err) {
830
- if (isSQLiteError(err) && isOptimisticConcurrencyError(err)) {
831
- return {
832
- success: false
833
- };
834
- }
835
- throw err;
836
- }
778
+ `;
779
+ const insertSQL = buildMessageInsertQuery(
780
+ messages,
781
+ expectedStreamVersion,
782
+ streamId,
783
+ options?.partition?.toString() ?? defaultTag2
784
+ );
785
+ const results = await execute.batchCommand([streamSQL, insertSQL], { assertChanges: true });
786
+ const [streamResult, messagesResult] = results;
787
+ const streamPosition = streamResult?.rows[0]?.stream_position;
788
+ const globalPosition = messagesResult?.rows.at(-1)?.global_position;
789
+ if (!streamPosition)
790
+ throw new ExpectedVersionConflictError(0n, expectedStreamVersion ?? 0n);
791
+ if (!globalPosition) throw new Error("Could not find global position");
837
792
  return {
838
793
  success: true,
839
- nextStreamPosition: streamPosition,
840
- lastGlobalPosition: globalPosition
794
+ nextStreamPosition: BigInt(streamPosition),
795
+ lastGlobalPosition: BigInt(globalPosition)
841
796
  };
842
797
  };
843
- var isOptimisticConcurrencyError = (error) => {
844
- return error?.errno !== void 0 && error.errno === 19;
845
- };
846
798
  async function getLastStreamPosition(execute, streamId, expectedStreamVersion) {
847
- const result = await singleOrNull2(
799
+ const result = await singleOrNull(
848
800
  execute.query(
849
- SQL3`SELECT CAST(stream_position AS VARCHAR) AS stream_position FROM ${identifier3(streamsTable.name)} WHERE stream_id = ${streamId}`
801
+ SQL`SELECT CAST(stream_position AS VARCHAR) AS stream_position FROM ${identifier(streamsTable.name)} WHERE stream_id = ${streamId}`
850
802
  )
851
803
  );
852
804
  if (result?.stream_position == null) {
@@ -862,10 +814,10 @@ var buildMessageInsertQuery = (messages, expectedStreamVersion, streamId, partit
862
814
  throw new Error("Stream position is required");
863
815
  }
864
816
  const streamPosition = BigInt(message.metadata.streamPosition) + BigInt(expectedStreamVersion);
865
- return SQL3`(${streamId},${streamPosition ?? 0n},${partition ?? defaultTag2},${message.kind === "Event" ? "E" : "C"},${message.data},${message.metadata},${expectedStreamVersion ?? 0n},${message.type},${message.metadata.messageId},${false})`;
817
+ return SQL`(${streamId},${streamPosition ?? 0n},${partition ?? defaultTag2},${message.kind === "Event" ? "E" : "C"},${message.data},${message.metadata},${expectedStreamVersion ?? 0n},${message.type},${message.metadata.messageId},${false})`;
866
818
  });
867
- return SQL3`
868
- INSERT INTO ${identifier3(messagesTable.name)} (
819
+ return SQL`
820
+ INSERT INTO ${identifier(messagesTable.name)} (
869
821
  stream_id,
870
822
  stream_position,
871
823
  partition,
@@ -883,157 +835,10 @@ var buildMessageInsertQuery = (messages, expectedStreamVersion, streamId, partit
883
835
  `;
884
836
  };
885
837
 
886
- // src/eventStore/schema/readProcessorCheckpoint.ts
887
- import { SQL as SQL4, singleOrNull as singleOrNull3 } from "@event-driven-io/dumbo";
888
- var { identifier: identifier4 } = SQL4;
889
- var readProcessorCheckpoint = async (execute, options) => {
890
- const result = await singleOrNull3(
891
- execute.query(
892
- SQL4`SELECT last_processed_checkpoint
893
- FROM ${identifier4(processorsTable.name)}
894
- WHERE partition = ${options?.partition ?? defaultTag2} AND processor_id = ${options.processorId}
895
- LIMIT 1`
896
- )
897
- );
898
- return {
899
- lastProcessedPosition: result !== null ? BigInt(result.last_processed_checkpoint) : null
900
- };
901
- };
902
-
903
- // src/eventStore/schema/readStream.ts
904
- import { SQL as SQL5 } from "@event-driven-io/dumbo";
905
- var { identifier: identifier5 } = SQL5;
906
- var readStream = async (execute, streamId, options) => {
907
- const fromCondition = options?.from ? SQL5`AND stream_position >= ${options.from}` : SQL5.EMPTY;
908
- const to = Number(
909
- options?.to ?? (options?.maxCount ? (options.from ?? 0n) + options.maxCount : NaN)
910
- );
911
- const toCondition = !isNaN(to) ? SQL5`AND stream_position <= ${to}` : SQL5.EMPTY;
912
- const { rows: results } = await execute.query(
913
- SQL5`SELECT stream_id, stream_position, global_position, message_data, message_metadata, message_schema_version, message_type, message_id
914
- FROM ${identifier5(messagesTable.name)}
915
- WHERE stream_id = ${streamId} AND partition = ${options?.partition ?? defaultTag2} AND is_archived = FALSE ${fromCondition} ${toCondition}
916
- ORDER BY stream_position ASC`
917
- );
918
- const messages = results.map((row) => {
919
- const rawEvent = {
920
- type: row.message_type,
921
- data: JSONParser.parse(row.message_data),
922
- metadata: JSONParser.parse(row.message_metadata)
923
- };
924
- const metadata = {
925
- ..."metadata" in rawEvent ? rawEvent.metadata ?? {} : {},
926
- messageId: row.message_id,
927
- streamName: streamId,
928
- streamPosition: BigInt(row.stream_position),
929
- globalPosition: BigInt(row.global_position)
930
- };
931
- const event = {
932
- ...rawEvent,
933
- kind: "Event",
934
- metadata
935
- };
936
- return upcastRecordedMessage(event, options?.schema?.versioning);
937
- });
938
- return messages.length > 0 ? {
939
- currentStreamVersion: messages[messages.length - 1].metadata.streamPosition,
940
- events: messages,
941
- streamExists: true
942
- } : {
943
- currentStreamVersion: SQLiteEventStoreDefaultStreamVersion,
944
- events: [],
945
- streamExists: false
946
- };
947
- };
948
-
949
- // src/eventStore/schema/storeProcessorCheckpoint.ts
950
- import { singleOrNull as singleOrNull4, SQL as SQL6 } from "@event-driven-io/dumbo";
951
- import { isSQLiteError as isSQLiteError2 } from "@event-driven-io/dumbo/sqlite3";
952
- var { identifier: identifier6 } = SQL6;
953
- async function storeSubscriptionCheckpointSQLite(execute, processorId, version, position, checkPosition, partition, processorInstanceId) {
954
- processorInstanceId ??= unknownTag2;
955
- if (checkPosition !== null) {
956
- const updateResult = await execute.command(
957
- SQL6`
958
- UPDATE ${identifier6(processorsTable.name)}
959
- SET
960
- last_processed_checkpoint = ${position},
961
- processor_instance_id = ${processorInstanceId}
962
- WHERE processor_id = ${processorId}
963
- AND last_processed_checkpoint = ${checkPosition}
964
- AND partition = ${partition}
965
- `
966
- );
967
- if (updateResult.rowCount && updateResult.rowCount > 0) {
968
- return 1;
969
- }
970
- const current_position = await singleOrNull4(
971
- execute.query(
972
- SQL6`
973
- SELECT last_processed_checkpoint FROM ${identifier6(processorsTable.name)}
974
- WHERE processor_id = ${processorId} AND partition = ${partition}`
975
- )
976
- );
977
- const currentPosition = current_position && current_position?.last_processed_checkpoint !== null ? BigInt(current_position.last_processed_checkpoint) : null;
978
- if (currentPosition === position) {
979
- return 0;
980
- } else if (position !== null && currentPosition !== null && currentPosition > position) {
981
- return 2;
982
- } else {
983
- return 2;
984
- }
985
- } else {
986
- try {
987
- await execute.command(
988
- SQL6`INSERT INTO ${identifier6(processorsTable.name)} (processor_id, version, last_processed_checkpoint, partition, processor_instance_id)
989
- VALUES (${processorId}, ${version}, ${position}, ${partition}, ${processorInstanceId})`
990
- );
991
- return 1;
992
- } catch (err) {
993
- if (!(isSQLiteError2(err) && (err.errno === 19 || err.errno === 2067))) {
994
- throw err;
995
- }
996
- const current = await singleOrNull4(
997
- execute.query(
998
- SQL6`
999
- SELECT last_processed_checkpoint FROM ${identifier6(processorsTable.name)}
1000
- WHERE processor_id = ${processorId} AND partition = ${partition}`
1001
- )
1002
- );
1003
- const currentPosition = current && current?.last_processed_checkpoint !== null ? BigInt(current.last_processed_checkpoint) : null;
1004
- if (currentPosition === position) {
1005
- return 0;
1006
- } else {
1007
- return 2;
1008
- }
1009
- }
1010
- }
1011
- }
1012
- async function storeProcessorCheckpoint(execute, options) {
1013
- try {
1014
- const result = await storeSubscriptionCheckpointSQLite(
1015
- execute,
1016
- options.processorId,
1017
- options.version ?? 1,
1018
- options.newPosition,
1019
- options.lastProcessedPosition,
1020
- options.partition ?? defaultTag2
1021
- );
1022
- return result === 1 ? { success: true, newPosition: options.newPosition } : { success: false, reason: result === 0 ? "IGNORED" : "MISMATCH" };
1023
- } catch (error) {
1024
- console.log(error);
1025
- throw error;
1026
- }
1027
- }
1028
-
1029
- // src/eventStore/schema/tables.ts
1030
- import { SQL as SQL10 } from "@event-driven-io/dumbo";
1031
- import "@event-driven-io/dumbo/sqlite3";
1032
-
1033
838
  // src/eventStore/schema/migrations/0_41_0/0_41_0.snapshot.ts
1034
- import { SQL as SQL7 } from "@event-driven-io/dumbo";
839
+ import { SQL as SQL2 } from "@event-driven-io/dumbo";
1035
840
  var schema_0_41_0 = [
1036
- SQL7`CREATE TABLE IF NOT EXISTS emt_streams(
841
+ SQL2`CREATE TABLE IF NOT EXISTS emt_streams(
1037
842
  stream_id TEXT NOT NULL,
1038
843
  stream_position BIGINT NOT NULL DEFAULT 0,
1039
844
  partition TEXT NOT NULL DEFAULT 'global',
@@ -1043,7 +848,7 @@ var schema_0_41_0 = [
1043
848
  PRIMARY KEY (stream_id, partition, is_archived),
1044
849
  UNIQUE (stream_id, partition, is_archived)
1045
850
  )`,
1046
- SQL7`CREATE TABLE IF NOT EXISTS emt_messages(
851
+ SQL2`CREATE TABLE IF NOT EXISTS emt_messages(
1047
852
  stream_id TEXT NOT NULL,
1048
853
  stream_position BIGINT NOT NULL,
1049
854
  partition TEXT NOT NULL DEFAULT 'global',
@@ -1058,7 +863,7 @@ var schema_0_41_0 = [
1058
863
  created DATETIME DEFAULT CURRENT_TIMESTAMP,
1059
864
  UNIQUE (stream_id, stream_position, partition, is_archived)
1060
865
  )`,
1061
- SQL7`CREATE TABLE IF NOT EXISTS emt_subscriptions(
866
+ SQL2`CREATE TABLE IF NOT EXISTS emt_subscriptions(
1062
867
  subscription_id TEXT NOT NULL,
1063
868
  version INTEGER NOT NULL DEFAULT 1,
1064
869
  partition TEXT NOT NULL DEFAULT 'global',
@@ -1068,10 +873,10 @@ var schema_0_41_0 = [
1068
873
  ];
1069
874
 
1070
875
  // src/eventStore/schema/migrations/0_42_0/0_42_0.migration.ts
1071
- import { singleOrNull as singleOrNull5, SQL as SQL8 } from "@event-driven-io/dumbo";
1072
- var { identifier: identifier7, plain } = SQL8;
876
+ import { singleOrNull as singleOrNull2, SQL as SQL3 } from "@event-driven-io/dumbo";
877
+ var { identifier: identifier2, plain } = SQL3;
1073
878
  var migration_0_42_0_SQLs = [
1074
- SQL8`CREATE TABLE IF NOT EXISTS ${identifier7(processorsTable.name)}(
879
+ SQL3`CREATE TABLE IF NOT EXISTS ${identifier2(processorsTable.name)}(
1075
880
  processor_id TEXT NOT NULL,
1076
881
  version INTEGER NOT NULL DEFAULT 1,
1077
882
  partition TEXT NOT NULL DEFAULT '${plain(globalTag)}',
@@ -1080,7 +885,7 @@ var migration_0_42_0_SQLs = [
1080
885
  processor_instance_id TEXT DEFAULT 'emt:unknown',
1081
886
  PRIMARY KEY (processor_id, partition, version)
1082
887
  )`,
1083
- SQL8`CREATE TABLE IF NOT EXISTS ${identifier7(projectionsTable.name)}(
888
+ SQL3`CREATE TABLE IF NOT EXISTS ${identifier2(projectionsTable.name)}(
1084
889
  name TEXT NOT NULL,
1085
890
  version INTEGER NOT NULL DEFAULT 1,
1086
891
  partition TEXT NOT NULL DEFAULT '${plain(globalTag)}',
@@ -1090,7 +895,7 @@ var migration_0_42_0_SQLs = [
1090
895
  definition JSONB NOT NULL DEFAULT '{}',
1091
896
  PRIMARY KEY (name, partition, version)
1092
897
  )`,
1093
- SQL8`INSERT INTO ${identifier7(processorsTable.name)}
898
+ SQL3`INSERT INTO ${identifier2(processorsTable.name)}
1094
899
  (processor_id, version, partition, status, last_processed_checkpoint, processor_instance_id)
1095
900
  SELECT
1096
901
  subscription_id,
@@ -1100,12 +905,12 @@ var migration_0_42_0_SQLs = [
1100
905
  printf('%019d', last_processed_position),
1101
906
  'emt:unknown'
1102
907
  FROM emt_subscriptions`,
1103
- SQL8`DROP TABLE emt_subscriptions`
908
+ SQL3`DROP TABLE emt_subscriptions`
1104
909
  ];
1105
910
  var migration_0_42_0_FromSubscriptionsToProcessors = async (execute) => {
1106
- const tableExists = await singleOrNull5(
911
+ const tableExists = await singleOrNull2(
1107
912
  execute.query(
1108
- SQL8`SELECT name FROM sqlite_master WHERE type='table' AND name='emt_subscriptions'`
913
+ SQL3`SELECT name FROM sqlite_master WHERE type='table' AND name='emt_subscriptions'`
1109
914
  )
1110
915
  );
1111
916
  if (!tableExists) {
@@ -1115,9 +920,9 @@ var migration_0_42_0_FromSubscriptionsToProcessors = async (execute) => {
1115
920
  };
1116
921
 
1117
922
  // src/eventStore/schema/migrations/0_42_0/0_42_0.snapshot.ts
1118
- import { SQL as SQL9 } from "@event-driven-io/dumbo";
923
+ import { SQL as SQL4 } from "@event-driven-io/dumbo";
1119
924
  var schema_0_42_0 = [
1120
- SQL9`CREATE TABLE IF NOT EXISTS emt_streams(
925
+ SQL4`CREATE TABLE IF NOT EXISTS emt_streams(
1121
926
  stream_id TEXT NOT NULL,
1122
927
  stream_position BIGINT NOT NULL DEFAULT 0,
1123
928
  partition TEXT NOT NULL DEFAULT 'global',
@@ -1127,7 +932,7 @@ var schema_0_42_0 = [
1127
932
  PRIMARY KEY (stream_id, partition, is_archived),
1128
933
  UNIQUE (stream_id, partition, is_archived)
1129
934
  )`,
1130
- SQL9`CREATE TABLE IF NOT EXISTS emt_messages(
935
+ SQL4`CREATE TABLE IF NOT EXISTS emt_messages(
1131
936
  stream_id TEXT NOT NULL,
1132
937
  stream_position BIGINT NOT NULL,
1133
938
  partition TEXT NOT NULL DEFAULT 'global',
@@ -1142,7 +947,7 @@ var schema_0_42_0 = [
1142
947
  created DATETIME DEFAULT CURRENT_TIMESTAMP,
1143
948
  UNIQUE (stream_id, stream_position, partition, is_archived)
1144
949
  )`,
1145
- SQL9`CREATE TABLE IF NOT EXISTS emt_processors(
950
+ SQL4`CREATE TABLE IF NOT EXISTS emt_processors(
1146
951
  processor_id TEXT NOT NULL,
1147
952
  version INTEGER NOT NULL DEFAULT 1,
1148
953
  partition TEXT NOT NULL DEFAULT 'global',
@@ -1151,7 +956,7 @@ var schema_0_42_0 = [
1151
956
  processor_instance_id TEXT DEFAULT 'emt:unknown',
1152
957
  PRIMARY KEY (processor_id, partition, version)
1153
958
  )`,
1154
- SQL9`CREATE TABLE IF NOT EXISTS emt_projections(
959
+ SQL4`CREATE TABLE IF NOT EXISTS emt_projections(
1155
960
  name TEXT NOT NULL,
1156
961
  version INTEGER NOT NULL DEFAULT 1,
1157
962
  partition TEXT NOT NULL DEFAULT 'global',
@@ -1163,119 +968,199 @@ var schema_0_42_0 = [
1163
968
  )`
1164
969
  ];
1165
970
 
1166
- // src/eventStore/schema/tables.ts
1167
- var { identifier: identifier8, plain: plain2 } = SQL10;
1168
- var streamsTableSQL = SQL10`CREATE TABLE IF NOT EXISTS ${identifier8(streamsTable.name)}(
1169
- stream_id TEXT NOT NULL,
1170
- stream_position BIGINT NOT NULL DEFAULT 0,
1171
- partition TEXT NOT NULL DEFAULT '${plain2(globalTag)}',
1172
- stream_type TEXT NOT NULL,
1173
- stream_metadata JSONB NOT NULL,
1174
- is_archived BOOLEAN NOT NULL DEFAULT FALSE,
1175
- PRIMARY KEY (stream_id, partition, is_archived),
1176
- UNIQUE (stream_id, partition, is_archived)
1177
- );`;
1178
- var messagesTableSQL = SQL10`CREATE TABLE IF NOT EXISTS ${identifier8(messagesTable.name)}(
1179
- stream_id TEXT NOT NULL,
1180
- stream_position BIGINT NOT NULL,
1181
- partition TEXT NOT NULL DEFAULT '${plain2(globalTag)}',
1182
- message_kind CHAR(1) NOT NULL DEFAULT 'E',
1183
- message_data JSONB NOT NULL,
1184
- message_metadata JSONB NOT NULL,
1185
- message_schema_version TEXT NOT NULL,
1186
- message_type TEXT NOT NULL,
1187
- message_id TEXT NOT NULL,
1188
- is_archived BOOLEAN NOT NULL DEFAULT FALSE,
1189
- global_position INTEGER PRIMARY KEY,
1190
- created DATETIME DEFAULT CURRENT_TIMESTAMP,
1191
- UNIQUE (stream_id, stream_position, partition, is_archived)
1192
- );
1193
- `;
1194
- var processorsTableSQL = SQL10`
1195
- CREATE TABLE IF NOT EXISTS ${SQL10.identifier(processorsTable.name)}(
1196
- processor_id TEXT NOT NULL,
1197
- version INTEGER NOT NULL DEFAULT 1,
1198
- partition TEXT NOT NULL DEFAULT '${plain2(globalTag)}',
1199
- status TEXT NOT NULL DEFAULT 'stopped',
1200
- last_processed_checkpoint TEXT NOT NULL,
1201
- processor_instance_id TEXT DEFAULT '${plain2(unknownTag2)}',
1202
- PRIMARY KEY (processor_id, partition, version)
1203
- );
1204
- `;
1205
- var projectionsTableSQL = SQL10`
1206
- CREATE TABLE IF NOT EXISTS ${SQL10.identifier(projectionsTable.name)}(
1207
- name TEXT NOT NULL,
1208
- version INTEGER NOT NULL DEFAULT 1,
1209
- partition TEXT NOT NULL DEFAULT '${plain2(globalTag)}',
1210
- type CHAR(1) NOT NULL,
1211
- kind TEXT NOT NULL,
1212
- status TEXT NOT NULL,
1213
- definition JSONB NOT NULL DEFAULT '{}',
1214
- PRIMARY KEY (name, partition, version)
971
+ // src/eventStore/schema/readLastMessageGlobalPosition.ts
972
+ import { SQL as SQL5, singleOrNull as singleOrNull3 } from "@event-driven-io/dumbo";
973
+ var { identifier: identifier3 } = SQL5;
974
+ var readLastMessageGlobalPosition = async (execute, options) => {
975
+ const result = await singleOrNull3(
976
+ execute.query(
977
+ SQL5`
978
+ SELECT global_position
979
+ FROM ${identifier3(messagesTable.name)}
980
+ WHERE partition = ${options?.partition ?? defaultTag2} AND is_archived = FALSE
981
+ ORDER BY global_position
982
+ LIMIT 1`
983
+ )
1215
984
  );
1216
- `;
1217
- var schemaSQL = [
1218
- streamsTableSQL,
1219
- messagesTableSQL,
1220
- processorsTableSQL,
1221
- projectionsTableSQL
1222
- ];
1223
- var createEventStoreSchema = async (pool, hooks) => {
1224
- await pool.withTransaction(async (tx) => {
1225
- await migration_0_42_0_FromSubscriptionsToProcessors(tx.execute);
1226
- if (hooks?.onBeforeSchemaCreated) {
1227
- await hooks.onBeforeSchemaCreated({
1228
- connection: tx.connection
1229
- });
1230
- }
1231
- await tx.execute.batchCommand(schemaSQL);
1232
- if (hooks?.onAfterSchemaCreated) {
1233
- await hooks.onAfterSchemaCreated();
1234
- }
1235
- });
985
+ return {
986
+ currentGlobalPosition: result !== null ? BigInt(result.global_position) : null
987
+ };
1236
988
  };
1237
989
 
1238
- // src/eventStore/consumers/sqliteProcessor.ts
1239
- var genericSQLiteProcessor = (options) => {
1240
- const { eachMessage } = options;
1241
- let isActive = true;
1242
- const getDb = (context) => {
1243
- const fileName = context.fileName ?? options.connectionOptions?.fileName;
1244
- if (!fileName)
1245
- throw new EmmettError(
1246
- `SQLite processor '${options.processorId}' is missing file name. Ensure that you passed it through options`
1247
- );
1248
- const connection = context.connection ?? options.connectionOptions?.connection ?? sqlite3Connection({ fileName, serializer: JSONSerializer });
1249
- return { connection, fileName };
1250
- };
1251
- return {
1252
- id: options.processorId,
1253
- start: async ({
1254
- execute
1255
- }) => {
1256
- isActive = true;
1257
- if (options.startFrom !== "CURRENT") return options.startFrom;
1258
- const { lastProcessedPosition } = await readProcessorCheckpoint(execute, {
1259
- processorId: options.processorId,
1260
- partition: options.partition
1261
- });
1262
- if (lastProcessedPosition === null) return "BEGINNING";
1263
- return { globalPosition: lastProcessedPosition };
1264
- },
1265
- get isActive() {
1266
- return isActive;
1267
- },
1268
- handle: async ({ messages }, context) => {
1269
- if (!isActive) return;
1270
- const { connection, fileName } = getDb(context);
990
+ // src/eventStore/schema/readMessagesBatch.ts
991
+ import { SQL as SQL6 } from "@event-driven-io/dumbo";
992
+ var { identifier: identifier4 } = SQL6;
993
+ var readMessagesBatch = async (execute, options) => {
994
+ const from = "from" in options ? options.from : "after" in options ? options.after + 1n : 0n;
995
+ const batchSize = options && "batchSize" in options ? options.batchSize : options.to - options.from;
996
+ const fromCondition = from !== -0n ? SQL6`AND global_position >= ${from}` : "";
997
+ const toCondition = "to" in options ? SQL6`AND global_position <= ${options.to}` : SQL6.EMPTY;
998
+ const limitCondition = "batchSize" in options ? SQL6`LIMIT ${options.batchSize}` : SQL6.EMPTY;
999
+ const events = (await execute.query(
1000
+ SQL6`SELECT stream_id, stream_position, global_position, message_data, message_metadata, message_schema_version, message_type, message_id
1001
+ FROM ${identifier4(messagesTable.name)}
1002
+ WHERE partition = ${options?.partition ?? defaultTag2} AND is_archived = FALSE ${fromCondition} ${toCondition}
1003
+ ORDER BY global_position
1004
+ ${limitCondition}`
1005
+ )).rows.map((row) => {
1006
+ const rawEvent = {
1007
+ type: row.message_type,
1008
+ data: JSONParser.parse(row.message_data),
1009
+ metadata: JSONParser.parse(row.message_metadata)
1010
+ };
1011
+ const metadata = {
1012
+ ..."metadata" in rawEvent ? rawEvent.metadata ?? {} : {},
1013
+ messageId: row.message_id,
1014
+ streamName: row.stream_id,
1015
+ streamPosition: BigInt(row.stream_position),
1016
+ globalPosition: BigInt(row.global_position)
1017
+ };
1018
+ return {
1019
+ ...rawEvent,
1020
+ kind: "Event",
1021
+ metadata
1022
+ };
1023
+ });
1024
+ return events.length > 0 ? {
1025
+ currentGlobalPosition: events[events.length - 1].metadata.globalPosition,
1026
+ messages: events,
1027
+ areEventsLeft: events.length === batchSize
1028
+ } : {
1029
+ currentGlobalPosition: "from" in options ? options.from : "after" in options ? options.after : 0n,
1030
+ messages: [],
1031
+ areEventsLeft: false
1032
+ };
1033
+ };
1034
+
1035
+ // src/eventStore/schema/readProcessorCheckpoint.ts
1036
+ import { SQL as SQL7, singleOrNull as singleOrNull4 } from "@event-driven-io/dumbo";
1037
+ var { identifier: identifier5 } = SQL7;
1038
+ var readProcessorCheckpoint = async (execute, options) => {
1039
+ const result = await singleOrNull4(
1040
+ execute.query(
1041
+ SQL7`SELECT last_processed_checkpoint
1042
+ FROM ${identifier5(processorsTable.name)}
1043
+ WHERE partition = ${options?.partition ?? defaultTag2} AND processor_id = ${options.processorId}
1044
+ LIMIT 1`
1045
+ )
1046
+ );
1047
+ return {
1048
+ lastProcessedPosition: result !== null ? BigInt(result.last_processed_checkpoint) : null
1049
+ };
1050
+ };
1051
+
1052
+ // src/eventStore/schema/readStream.ts
1053
+ import { SQL as SQL8 } from "@event-driven-io/dumbo";
1054
+
1055
+ // src/eventStore/SQLiteEventStore.ts
1056
+ import { dumbo as dumbo3 } from "@event-driven-io/dumbo";
1057
+
1058
+ // src/eventStore/consumers/messageBatchProcessing/index.ts
1059
+ var DefaultSQLiteEventStoreProcessorBatchSize = 100;
1060
+ var DefaultSQLiteEventStoreProcessorPullingFrequencyInMs = 50;
1061
+ var sqliteEventStoreMessageBatchPuller = ({
1062
+ pool,
1063
+ batchSize,
1064
+ eachBatch,
1065
+ pullingFrequencyInMs
1066
+ }) => {
1067
+ let isRunning = false;
1068
+ let start;
1069
+ const pullMessages = async (options) => {
1070
+ const after = options.startFrom === "BEGINNING" ? 0n : options.startFrom === "END" ? (await pool.withConnection(
1071
+ async ({ execute }) => readLastMessageGlobalPosition(execute)
1072
+ )).currentGlobalPosition ?? 0n : options.startFrom.globalPosition;
1073
+ const readMessagesOptions = {
1074
+ after,
1075
+ batchSize
1076
+ };
1077
+ let waitTime = 100;
1078
+ do {
1079
+ const { messages, currentGlobalPosition, areEventsLeft } = await pool.withConnection(
1080
+ ({ execute }) => readMessagesBatch(execute, readMessagesOptions)
1081
+ );
1082
+ if (messages.length > 0) {
1083
+ const result = await eachBatch({ messages });
1084
+ if (result && result.type === "STOP") {
1085
+ isRunning = false;
1086
+ break;
1087
+ }
1088
+ }
1089
+ readMessagesOptions.after = currentGlobalPosition;
1090
+ await new Promise((resolve) => setTimeout(resolve, waitTime));
1091
+ if (!areEventsLeft) {
1092
+ waitTime = Math.min(waitTime * 2, 1e3);
1093
+ } else {
1094
+ waitTime = pullingFrequencyInMs;
1095
+ }
1096
+ } while (isRunning);
1097
+ };
1098
+ return {
1099
+ get isRunning() {
1100
+ return isRunning;
1101
+ },
1102
+ start: (options) => {
1103
+ if (isRunning) return start;
1104
+ start = (async () => {
1105
+ isRunning = true;
1106
+ return pullMessages(options);
1107
+ })();
1108
+ return start;
1109
+ },
1110
+ stop: async () => {
1111
+ if (!isRunning) return;
1112
+ isRunning = false;
1113
+ await start;
1114
+ }
1115
+ };
1116
+ };
1117
+ var zipSQLiteEventStoreMessageBatchPullerStartFrom = (options) => {
1118
+ if (options.length === 0 || options.some((o) => o === void 0 || o === "BEGINNING"))
1119
+ return "BEGINNING";
1120
+ if (options.every((o) => o === "END")) return "END";
1121
+ return options.filter((o) => o !== void 0 && o !== "BEGINNING" && o !== "END").sort((a, b) => a > b ? 1 : -1)[0];
1122
+ };
1123
+
1124
+ // src/eventStore/consumers/sqliteEventStoreConsumer.ts
1125
+ import { dumbo as dumbo2 } from "@event-driven-io/dumbo";
1126
+
1127
+ // src/eventStore/consumers/sqliteProcessor.ts
1128
+ var genericSQLiteProcessor = (options) => {
1129
+ const { eachMessage } = options;
1130
+ let isActive = true;
1131
+ const mapToContext = (context) => {
1132
+ const connection = context.connection ?? options.connectionOptions?.connection;
1133
+ if (!connection)
1134
+ throw new Error("Connection is required in context or options");
1135
+ return { connection };
1136
+ };
1137
+ return {
1138
+ id: options.processorId,
1139
+ start: async ({
1140
+ execute
1141
+ }) => {
1142
+ isActive = true;
1143
+ if (options.startFrom !== "CURRENT") return options.startFrom;
1144
+ const { lastProcessedPosition } = await readProcessorCheckpoint(execute, {
1145
+ processorId: options.processorId,
1146
+ partition: options.partition
1147
+ });
1148
+ if (lastProcessedPosition === null) return "BEGINNING";
1149
+ return { globalPosition: lastProcessedPosition };
1150
+ },
1151
+ get isActive() {
1152
+ return isActive;
1153
+ },
1154
+ handle: async ({ messages }, context) => {
1155
+ if (!isActive) return;
1156
+ const { connection } = mapToContext(context);
1271
1157
  return connection.withTransaction(async (tx) => {
1272
1158
  let result = void 0;
1273
1159
  let lastProcessedPosition = null;
1274
1160
  for (const message of messages) {
1275
1161
  const typedMessage = message;
1276
1162
  const messageProcessingResult = await eachMessage(typedMessage, {
1277
- connection: tx.connection,
1278
- fileName
1163
+ connection: tx.connection
1279
1164
  });
1280
1165
  const newPosition = getCheckpoint(typedMessage);
1281
1166
  await storeProcessorCheckpoint(tx.execute, {
@@ -1329,9 +1214,12 @@ var sqliteEventStoreConsumer = (options) => {
1329
1214
  const processors = options.processors ?? [];
1330
1215
  let start;
1331
1216
  let currentMessagePuller;
1332
- const pool = options.pool ?? sqlite3Pool({
1333
- transactionOptions: { allowNestedTransactions: true },
1334
- ...options
1217
+ const pool = options.pool ?? dumbo2({
1218
+ ...options.driver.mapToDumboOptions(options),
1219
+ transactionOptions: {
1220
+ allowNestedTransactions: true,
1221
+ mode: "session_based"
1222
+ }
1335
1223
  });
1336
1224
  const eachBatch = (messagesBatch) => pool.withConnection(async (connection) => {
1337
1225
  const activeProcessors = processors.filter((s) => s.isActive);
@@ -1343,8 +1231,7 @@ var sqliteEventStoreConsumer = (options) => {
1343
1231
  const result = await Promise.allSettled(
1344
1232
  activeProcessors.map((s) => {
1345
1233
  return s.handle(messagesBatch, {
1346
- connection,
1347
- fileName: options.fileName
1234
+ connection
1348
1235
  });
1349
1236
  })
1350
1237
  );
@@ -1407,30 +1294,16 @@ var sqliteEventStoreConsumer = (options) => {
1407
1294
  };
1408
1295
  };
1409
1296
 
1410
- // src/eventStore/schema/streamExists.ts
1411
- import { exists, SQL as SQL11 } from "@event-driven-io/dumbo";
1412
- var streamExists = (execute, streamId, options) => exists(
1413
- execute.query(
1414
- SQL11`SELECT EXISTS (
1415
- SELECT 1
1416
- from ${SQL11.identifier(streamsTable.name)}
1417
- WHERE stream_id = ${streamId} AND partition = ${options?.partition ?? defaultTag2} AND is_archived = FALSE) as exists
1418
- `
1419
- )
1420
- );
1421
-
1422
1297
  // src/eventStore/SQLiteEventStore.ts
1423
1298
  var SQLiteEventStoreDefaultStreamVersion = 0n;
1424
1299
  var getSQLiteEventStore = (options) => {
1425
1300
  let autoGenerateSchema = false;
1426
- const fileName = options.fileName ?? InMemorySQLiteDatabase;
1427
- const poolOptions = {
1428
- fileName,
1429
- transactionOptions: { allowNestedTransactions: true }
1430
- };
1431
- const pool = options.pool ?? sqlite3Pool2({
1432
- ...poolOptions,
1433
- ...options.connectionOptions ? options.connectionOptions : {}
1301
+ const pool = options.pool ?? dumbo3({
1302
+ ...options.driver.mapToDumboOptions(options),
1303
+ transactionOptions: {
1304
+ allowNestedTransactions: true,
1305
+ mode: "session_based"
1306
+ }
1434
1307
  });
1435
1308
  let migrateSchema = void 0;
1436
1309
  const inlineProjections = (options.projections ?? []).filter(({ type }) => type === "inline").map(({ projection: projection2 }) => projection2);
@@ -1533,9 +1406,9 @@ var getSQLiteEventStore = (options) => {
1533
1406
  ({ execute }) => streamExists(execute, streamName, options2)
1534
1407
  );
1535
1408
  },
1536
- consumer: (options2) => sqliteEventStoreConsumer({
1537
- ...options2 ?? {},
1538
- fileName,
1409
+ consumer: (consumerOptions) => sqliteEventStoreConsumer({
1410
+ ...options ?? {},
1411
+ ...consumerOptions ?? {},
1539
1412
  pool
1540
1413
  }),
1541
1414
  close: () => pool.close(),
@@ -1547,134 +1420,220 @@ var getSQLiteEventStore = (options) => {
1547
1420
  };
1548
1421
  };
1549
1422
 
1550
- // src/eventStore/projections/sqliteProjectionSpec.ts
1551
- var SQLiteProjectionSpec = {
1552
- for: (options) => {
1553
- {
1554
- const connection = options.connection ?? sqlite3Connection2({
1555
- fileName: options.fileName ?? InMemorySQLiteDatabase2,
1556
- serializer: JSONSerializer2
1557
- });
1558
- const projection2 = options.projection;
1559
- let wasInitialized = false;
1560
- return (givenEvents) => {
1561
- return {
1562
- when: (events, options2) => {
1563
- const allEvents = [];
1564
- const run = async (connection2) => {
1565
- let globalPosition = 0n;
1566
- const numberOfTimes = options2?.numberOfTimes ?? 1;
1567
- for (const event of [
1568
- ...givenEvents,
1569
- ...Array.from({ length: numberOfTimes }).flatMap(() => events)
1570
- ]) {
1571
- const metadata = {
1572
- globalPosition: ++globalPosition,
1573
- streamPosition: globalPosition,
1574
- streamName: `test-${uuid6()}`,
1575
- messageId: uuid6()
1576
- };
1577
- allEvents.push({
1578
- ...event,
1579
- kind: "Event",
1580
- metadata: {
1581
- ...metadata,
1582
- ..."metadata" in event ? event.metadata ?? {} : {}
1583
- }
1584
- });
1585
- }
1586
- if (!wasInitialized && projection2.init) {
1587
- await projection2.init({
1588
- registrationType: "async",
1589
- status: "active",
1590
- context: { connection: connection2 },
1591
- version: projection2.version ?? 1
1592
- });
1593
- wasInitialized = true;
1594
- }
1595
- await connection2.withTransaction(
1596
- () => handleProjections({
1597
- events: allEvents,
1598
- projections: [projection2],
1599
- connection: connection2
1600
- })
1601
- );
1602
- };
1603
- return {
1604
- then: async (assert, message) => {
1605
- try {
1606
- await run(connection);
1607
- const succeeded = await assert({
1608
- connection
1609
- });
1610
- if (succeeded !== void 0 && succeeded === false)
1611
- assertFails(
1612
- message ?? "Projection specification didn't match the criteria"
1613
- );
1614
- } finally {
1615
- await connection.close();
1616
- }
1617
- },
1618
- thenThrows: async (...args) => {
1619
- try {
1620
- await run(connection);
1621
- throw new AssertionError("Handler did not fail as expected");
1622
- } catch (error) {
1623
- if (error instanceof AssertionError) throw error;
1624
- if (args.length === 0) return;
1625
- if (!isErrorConstructor(args[0])) {
1626
- assertTrue(
1627
- args[0](error),
1628
- `Error didn't match the error condition: ${error?.toString()}`
1629
- );
1630
- return;
1631
- }
1632
- assertTrue(
1633
- error instanceof args[0],
1634
- `Caught error is not an instance of the expected type: ${error?.toString()}`
1635
- );
1636
- if (args[1]) {
1637
- assertTrue(
1638
- args[1](error),
1639
- `Error didn't match the error condition: ${error?.toString()}`
1640
- );
1641
- }
1642
- } finally {
1643
- await connection.close();
1644
- }
1645
- }
1646
- };
1647
- }
1648
- };
1649
- };
1423
+ // src/eventStore/schema/readStream.ts
1424
+ var { identifier: identifier6 } = SQL8;
1425
+ var readStream = async (execute, streamId, options) => {
1426
+ const fromCondition = options?.from ? SQL8`AND stream_position >= ${options.from}` : SQL8.EMPTY;
1427
+ const to = Number(
1428
+ options?.to ?? (options?.maxCount ? (options.from ?? 0n) + options.maxCount : NaN)
1429
+ );
1430
+ const toCondition = !isNaN(to) ? SQL8`AND stream_position <= ${to}` : SQL8.EMPTY;
1431
+ const { rows: results } = await execute.query(
1432
+ SQL8`SELECT stream_id, stream_position, global_position, message_data, message_metadata, message_schema_version, message_type, message_id
1433
+ FROM ${identifier6(messagesTable.name)}
1434
+ WHERE stream_id = ${streamId} AND partition = ${options?.partition ?? defaultTag2} AND is_archived = FALSE ${fromCondition} ${toCondition}
1435
+ ORDER BY stream_position ASC`
1436
+ );
1437
+ const messages = results.map((row) => {
1438
+ const rawEvent = {
1439
+ type: row.message_type,
1440
+ data: JSONParser.parse(row.message_data),
1441
+ metadata: JSONParser.parse(row.message_metadata)
1442
+ };
1443
+ const metadata = {
1444
+ ..."metadata" in rawEvent ? rawEvent.metadata ?? {} : {},
1445
+ messageId: row.message_id,
1446
+ streamName: streamId,
1447
+ streamPosition: BigInt(row.stream_position),
1448
+ globalPosition: BigInt(row.global_position)
1449
+ };
1450
+ const event = {
1451
+ ...rawEvent,
1452
+ kind: "Event",
1453
+ metadata
1454
+ };
1455
+ return upcastRecordedMessage(event, options?.schema?.versioning);
1456
+ });
1457
+ return messages.length > 0 ? {
1458
+ currentStreamVersion: messages[messages.length - 1].metadata.streamPosition,
1459
+ events: messages,
1460
+ streamExists: true
1461
+ } : {
1462
+ currentStreamVersion: SQLiteEventStoreDefaultStreamVersion,
1463
+ events: [],
1464
+ streamExists: false
1465
+ };
1466
+ };
1467
+
1468
+ // src/eventStore/schema/storeProcessorCheckpoint.ts
1469
+ import {
1470
+ DumboError as DumboError2,
1471
+ singleOrNull as singleOrNull5,
1472
+ SQL as SQL9,
1473
+ UniqueConstraintError as UniqueConstraintError2
1474
+ } from "@event-driven-io/dumbo";
1475
+ var { identifier: identifier7 } = SQL9;
1476
+ async function storeSubscriptionCheckpointSQLite(execute, processorId, version, position, checkPosition, partition, processorInstanceId) {
1477
+ processorInstanceId ??= unknownTag2;
1478
+ if (checkPosition !== null) {
1479
+ const updateResult = await execute.command(
1480
+ SQL9`
1481
+ UPDATE ${identifier7(processorsTable.name)}
1482
+ SET
1483
+ last_processed_checkpoint = ${position},
1484
+ processor_instance_id = ${processorInstanceId}
1485
+ WHERE processor_id = ${processorId}
1486
+ AND last_processed_checkpoint = ${checkPosition}
1487
+ AND partition = ${partition}
1488
+ `
1489
+ );
1490
+ if (updateResult.rowCount && updateResult.rowCount > 0) {
1491
+ return 1;
1492
+ }
1493
+ const current_position = await singleOrNull5(
1494
+ execute.query(
1495
+ SQL9`
1496
+ SELECT last_processed_checkpoint FROM ${identifier7(processorsTable.name)}
1497
+ WHERE processor_id = ${processorId} AND partition = ${partition}`
1498
+ )
1499
+ );
1500
+ const currentPosition = current_position && current_position?.last_processed_checkpoint !== null ? BigInt(current_position.last_processed_checkpoint) : null;
1501
+ if (currentPosition === position) {
1502
+ return 0;
1503
+ } else if (position !== null && currentPosition !== null && currentPosition > position) {
1504
+ return 2;
1505
+ } else {
1506
+ return 2;
1507
+ }
1508
+ } else {
1509
+ try {
1510
+ await execute.command(
1511
+ SQL9`INSERT INTO ${identifier7(processorsTable.name)} (processor_id, version, last_processed_checkpoint, partition, processor_instance_id)
1512
+ VALUES (${processorId}, ${version}, ${position}, ${partition}, ${processorInstanceId})`
1513
+ );
1514
+ return 1;
1515
+ } catch (err) {
1516
+ if (!DumboError2.isInstanceOf(err, {
1517
+ errorType: UniqueConstraintError2.ErrorType
1518
+ })) {
1519
+ throw err;
1520
+ }
1521
+ const current = await singleOrNull5(
1522
+ execute.query(
1523
+ SQL9`
1524
+ SELECT last_processed_checkpoint FROM ${identifier7(processorsTable.name)}
1525
+ WHERE processor_id = ${processorId} AND partition = ${partition}`
1526
+ )
1527
+ );
1528
+ const currentPosition = current && current?.last_processed_checkpoint !== null ? BigInt(current.last_processed_checkpoint) : null;
1529
+ if (currentPosition === position) {
1530
+ return 0;
1531
+ } else {
1532
+ return 2;
1533
+ }
1650
1534
  }
1651
1535
  }
1652
- };
1653
- var eventInStream = (streamName, event) => {
1654
- return {
1655
- ...event,
1656
- metadata: {
1657
- ...event.metadata ?? {},
1658
- streamName: event.metadata?.streamName ?? streamName
1536
+ }
1537
+ async function storeProcessorCheckpoint(execute, options) {
1538
+ try {
1539
+ const result = await storeSubscriptionCheckpointSQLite(
1540
+ execute,
1541
+ options.processorId,
1542
+ options.version ?? 1,
1543
+ options.newPosition,
1544
+ options.lastProcessedPosition,
1545
+ options.partition ?? defaultTag2
1546
+ );
1547
+ return result === 1 ? { success: true, newPosition: options.newPosition } : { success: false, reason: result === 0 ? "IGNORED" : "MISMATCH" };
1548
+ } catch (error) {
1549
+ console.log(error);
1550
+ throw error;
1551
+ }
1552
+ }
1553
+
1554
+ // src/eventStore/schema/streamExists.ts
1555
+ import { exists, SQL as SQL10 } from "@event-driven-io/dumbo";
1556
+ var streamExists = (execute, streamId, options) => exists(
1557
+ execute.query(
1558
+ SQL10`SELECT EXISTS (
1559
+ SELECT 1
1560
+ from ${SQL10.identifier(streamsTable.name)}
1561
+ WHERE stream_id = ${streamId} AND partition = ${options?.partition ?? defaultTag2} AND is_archived = FALSE) as exists
1562
+ `
1563
+ )
1564
+ );
1565
+
1566
+ // src/eventStore/schema/tables.ts
1567
+ import { SQL as SQL11 } from "@event-driven-io/dumbo";
1568
+ var { identifier: identifier8, plain: plain2 } = SQL11;
1569
+ var streamsTableSQL = SQL11`CREATE TABLE IF NOT EXISTS ${identifier8(streamsTable.name)}(
1570
+ stream_id TEXT NOT NULL,
1571
+ stream_position BIGINT NOT NULL DEFAULT 0,
1572
+ partition TEXT NOT NULL DEFAULT '${plain2(globalTag)}',
1573
+ stream_type TEXT NOT NULL,
1574
+ stream_metadata JSONB NOT NULL,
1575
+ is_archived BOOLEAN NOT NULL DEFAULT FALSE,
1576
+ PRIMARY KEY (stream_id, partition, is_archived),
1577
+ UNIQUE (stream_id, partition, is_archived)
1578
+ );`;
1579
+ var messagesTableSQL = SQL11`CREATE TABLE IF NOT EXISTS ${identifier8(messagesTable.name)}(
1580
+ stream_id TEXT NOT NULL,
1581
+ stream_position BIGINT NOT NULL,
1582
+ partition TEXT NOT NULL DEFAULT '${plain2(globalTag)}',
1583
+ message_kind CHAR(1) NOT NULL DEFAULT 'E',
1584
+ message_data JSONB NOT NULL,
1585
+ message_metadata JSONB NOT NULL,
1586
+ message_schema_version TEXT NOT NULL,
1587
+ message_type TEXT NOT NULL,
1588
+ message_id TEXT NOT NULL,
1589
+ is_archived BOOLEAN NOT NULL DEFAULT FALSE,
1590
+ global_position INTEGER PRIMARY KEY,
1591
+ created DATETIME DEFAULT CURRENT_TIMESTAMP,
1592
+ UNIQUE (stream_id, stream_position, partition, is_archived)
1593
+ );
1594
+ `;
1595
+ var processorsTableSQL = SQL11`
1596
+ CREATE TABLE IF NOT EXISTS ${SQL11.identifier(processorsTable.name)}(
1597
+ processor_id TEXT NOT NULL,
1598
+ version INTEGER NOT NULL DEFAULT 1,
1599
+ partition TEXT NOT NULL DEFAULT '${plain2(globalTag)}',
1600
+ status TEXT NOT NULL DEFAULT 'stopped',
1601
+ last_processed_checkpoint TEXT NOT NULL,
1602
+ processor_instance_id TEXT DEFAULT '${plain2(unknownTag2)}',
1603
+ PRIMARY KEY (processor_id, partition, version)
1604
+ );
1605
+ `;
1606
+ var projectionsTableSQL = SQL11`
1607
+ CREATE TABLE IF NOT EXISTS ${SQL11.identifier(projectionsTable.name)}(
1608
+ name TEXT NOT NULL,
1609
+ version INTEGER NOT NULL DEFAULT 1,
1610
+ partition TEXT NOT NULL DEFAULT '${plain2(globalTag)}',
1611
+ type CHAR(1) NOT NULL,
1612
+ kind TEXT NOT NULL,
1613
+ status TEXT NOT NULL,
1614
+ definition JSONB NOT NULL DEFAULT '{}',
1615
+ PRIMARY KEY (name, partition, version)
1616
+ );
1617
+ `;
1618
+ var schemaSQL = [
1619
+ streamsTableSQL,
1620
+ messagesTableSQL,
1621
+ processorsTableSQL,
1622
+ projectionsTableSQL
1623
+ ];
1624
+ var createEventStoreSchema = async (pool, hooks) => {
1625
+ await pool.withTransaction(async (tx) => {
1626
+ await migration_0_42_0_FromSubscriptionsToProcessors(tx.execute);
1627
+ if (hooks?.onBeforeSchemaCreated) {
1628
+ await hooks.onBeforeSchemaCreated({
1629
+ connection: tx.connection
1630
+ });
1659
1631
  }
1660
- };
1661
- };
1662
- var eventsInStream = (streamName, events) => {
1663
- return events.map((e) => eventInStream(streamName, e));
1664
- };
1665
- var newEventsInStream = eventsInStream;
1666
- var assertSQLQueryResultMatches = (sql, rows) => async ({
1667
- connection
1668
- }) => {
1669
- const result = await connection.execute.query(sql);
1670
- assertThatArray(rows).containsExactlyInAnyOrder(result.rows);
1671
- };
1672
- var expectSQL = {
1673
- query: (sql) => ({
1674
- resultRows: {
1675
- toBeTheSame: (rows) => assertSQLQueryResultMatches(sql, rows)
1632
+ await tx.execute.batchCommand(schemaSQL);
1633
+ if (hooks?.onAfterSchemaCreated) {
1634
+ await hooks.onAfterSchemaCreated();
1676
1635
  }
1677
- })
1636
+ });
1678
1637
  };
1679
1638
  export {
1680
1639
  SQLiteEventStoreDefaultStreamVersion,
@@ -1711,6 +1670,7 @@ export {
1711
1670
  sqliteRawBatchSQLProjection,
1712
1671
  sqliteRawSQLProjection,
1713
1672
  storeProcessorCheckpoint,
1673
+ streamExists,
1714
1674
  streamsTable,
1715
1675
  streamsTableSQL,
1716
1676
  unknownTag2 as unknownTag