@event-driven-io/emmett-sqlite 0.34.0 → 0.35.0-alpha.2

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
@@ -1,4 +1,4 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3;// src/connection/sqliteConnection.ts
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } async function _asyncNullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return await rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3;// src/connection/sqliteConnection.ts
2
2
  var _sqlite3 = require('sqlite3'); var _sqlite32 = _interopRequireDefault(_sqlite3);
3
3
  var isSQLiteError = (error) => {
4
4
  if (error instanceof Error && "code" in error) {
@@ -6,19 +6,35 @@ var isSQLiteError = (error) => {
6
6
  }
7
7
  return false;
8
8
  };
9
+ var InMemorySharedCacheSQLiteDatabase = "file::memory:?cache=shared";
9
10
  var InMemorySQLiteDatabase = ":memory:";
10
11
  var sqliteConnection = (options) => {
11
- const db = new _sqlite32.default.Database(_nullishCoalesce(options.fileName, () => ( InMemorySQLiteDatabase)));
12
+ const fileName = _nullishCoalesce(options.fileName, () => ( InMemorySQLiteDatabase));
13
+ let db;
14
+ if (fileName.startsWith("file:")) {
15
+ db = new _sqlite32.default.Database(
16
+ fileName,
17
+ _sqlite32.default.OPEN_URI | _sqlite32.default.OPEN_READWRITE | _sqlite32.default.OPEN_CREATE
18
+ );
19
+ } else {
20
+ db = new _sqlite32.default.Database(fileName);
21
+ }
22
+ db.run("PRAGMA journal_mode = WAL;");
23
+ let transactionNesting = 0;
12
24
  return {
13
25
  close: () => db.close(),
14
26
  command: (sql2, params) => new Promise((resolve, reject) => {
15
- db.run(sql2, _nullishCoalesce(params, () => ( [])), (err) => {
16
- if (err) {
17
- reject(err);
18
- return;
27
+ db.run(
28
+ sql2,
29
+ _nullishCoalesce(params, () => ( [])),
30
+ function(err) {
31
+ if (err) {
32
+ reject(err);
33
+ return;
34
+ }
35
+ resolve(this);
19
36
  }
20
- resolve();
21
- });
37
+ );
22
38
  }),
23
39
  query: (sql2, params) => new Promise((resolve, reject) => {
24
40
  db.all(sql2, _nullishCoalesce(params, () => ( [])), (err, result) => {
@@ -37,11 +53,53 @@ var sqliteConnection = (options) => {
37
53
  }
38
54
  resolve(result);
39
55
  });
40
- })
56
+ }),
57
+ withTransaction: async (fn) => {
58
+ try {
59
+ if (transactionNesting++ == 0) {
60
+ await beginTransaction(db);
61
+ }
62
+ const result = await fn();
63
+ if (transactionNesting === 1) await commitTransaction(db);
64
+ transactionNesting--;
65
+ return result;
66
+ } catch (err) {
67
+ console.log(err);
68
+ if (--transactionNesting === 0) await rollbackTransaction(db);
69
+ throw err;
70
+ }
71
+ }
41
72
  };
42
73
  };
74
+ var beginTransaction = (db) => new Promise((resolve, reject) => {
75
+ db.run("BEGIN IMMEDIATE TRANSACTION", (err) => {
76
+ if (err) {
77
+ reject(err);
78
+ return;
79
+ }
80
+ resolve();
81
+ });
82
+ });
83
+ var commitTransaction = (db) => new Promise((resolve, reject) => {
84
+ db.run("COMMIT", (err) => {
85
+ if (err) {
86
+ reject(err);
87
+ return;
88
+ }
89
+ resolve();
90
+ });
91
+ });
92
+ var rollbackTransaction = (db) => new Promise((resolve, reject) => {
93
+ db.run("ROLLBACK", (err) => {
94
+ if (err) {
95
+ reject(err);
96
+ return;
97
+ }
98
+ resolve();
99
+ });
100
+ });
43
101
 
44
- // ../emmett/dist/chunk-4E7QLAH5.js
102
+ // ../emmett/dist/chunk-BQYVGGNE.js
45
103
  var isNumber = (val) => typeof val === "number" && val === val;
46
104
  var isString = (val) => typeof val === "string";
47
105
  var EmmettError = class _EmmettError extends Error {
@@ -86,6 +144,7 @@ var _asyncretry = require('async-retry'); var _asyncretry2 = _interopRequireDefa
86
144
 
87
145
 
88
146
 
147
+
89
148
  var STREAM_EXISTS = "STREAM_EXISTS";
90
149
  var STREAM_DOES_NOT_EXIST = "STREAM_DOES_NOT_EXIST";
91
150
  var NO_CONCURRENCY_CHECK = "NO_CONCURRENCY_CHECK";
@@ -353,6 +412,9 @@ var messagesTable = {
353
412
  isArchived: columns.isArchived
354
413
  }
355
414
  };
415
+ var subscriptionsTable = {
416
+ name: `${emmettPrefix}_subscriptions`
417
+ };
356
418
 
357
419
  // src/eventStore/schema/appendToStream.ts
358
420
  var appendToStream = async (db, streamName, streamType, messages, options) => {
@@ -373,8 +435,7 @@ var appendToStream = async (db, streamName, streamType, messages, options) => {
373
435
  })
374
436
  );
375
437
  let result;
376
- await db.command(`BEGIN TRANSACTION`);
377
- try {
438
+ return await db.withTransaction(async () => {
378
439
  result = await appendToStreamRaw(
379
440
  db,
380
441
  streamName,
@@ -385,17 +446,9 @@ var appendToStream = async (db, streamName, streamType, messages, options) => {
385
446
  }
386
447
  );
387
448
  if (_optionalChain([options, 'optionalAccess', _18 => _18.onBeforeCommit]))
388
- await options.onBeforeCommit(messagesToAppend, { connection: db });
389
- } catch (err) {
390
- await db.command(`ROLLBACK`);
391
- throw err;
392
- }
393
- if (result.success == null || !result.success) {
394
- await db.command(`ROLLBACK`);
449
+ await options.onBeforeCommit(messagesToAppend, { db });
395
450
  return result;
396
- }
397
- await db.command(`COMMIT`);
398
- return result;
451
+ });
399
452
  };
400
453
  var toExpectedVersion = (expected) => {
401
454
  if (expected === void 0) return null;
@@ -472,11 +525,13 @@ var appendToStreamRaw = async (db, streamId, streamType, messages, options) => {
472
525
  streamId,
473
526
  _nullishCoalesce(_optionalChain([options, 'optionalAccess', _22 => _22.partition, 'optionalAccess', _23 => _23.toString, 'call', _24 => _24()]), () => ( defaultTag))
474
527
  );
475
- const returningId = await db.querySingle(sqlString, values);
476
- if (_optionalChain([returningId, 'optionalAccess', _25 => _25.global_position]) == null) {
528
+ const returningIds = await db.query(sqlString, values);
529
+ if (returningIds.length === 0 || !_optionalChain([returningIds, 'access', _25 => _25[returningIds.length - 1], 'optionalAccess', _26 => _26.global_position])) {
477
530
  throw new Error("Could not find global position");
478
531
  }
479
- globalPosition = BigInt(returningId.global_position);
532
+ globalPosition = BigInt(
533
+ returningIds[returningIds.length - 1].global_position
534
+ );
480
535
  } catch (err) {
481
536
  if (isSQLiteError(err) && isOptimisticConcurrencyError(err)) {
482
537
  return {
@@ -492,14 +547,14 @@ var appendToStreamRaw = async (db, streamId, streamType, messages, options) => {
492
547
  };
493
548
  };
494
549
  var isOptimisticConcurrencyError = (error) => {
495
- return _optionalChain([error, 'optionalAccess', _26 => _26.errno]) !== void 0 && error.errno === 19;
550
+ return _optionalChain([error, 'optionalAccess', _27 => _27.errno]) !== void 0 && error.errno === 19;
496
551
  };
497
552
  async function getLastStreamPosition(db, streamId, expectedStreamVersion) {
498
553
  const result = await db.querySingle(
499
554
  `SELECT CAST(stream_position AS VARCHAR) AS stream_position FROM ${streamsTable.name} WHERE stream_id = ?`,
500
555
  [streamId]
501
556
  );
502
- if (_optionalChain([result, 'optionalAccess', _27 => _27.stream_position]) == null) {
557
+ if (_optionalChain([result, 'optionalAccess', _28 => _28.stream_position]) == null) {
503
558
  expectedStreamVersion = 0n;
504
559
  } else {
505
560
  expectedStreamVersion = BigInt(result.stream_position);
@@ -509,7 +564,7 @@ async function getLastStreamPosition(db, streamId, expectedStreamVersion) {
509
564
  var buildMessageInsertQuery = (messages, expectedStreamVersion, streamId, partition) => {
510
565
  const query = messages.reduce(
511
566
  (queryBuilder, message) => {
512
- if (_optionalChain([message, 'access', _28 => _28.metadata, 'optionalAccess', _29 => _29.streamPosition]) == null || typeof message.metadata.streamPosition !== "bigint") {
567
+ if (_optionalChain([message, 'access', _29 => _29.metadata, 'optionalAccess', _30 => _30.streamPosition]) == null || typeof message.metadata.streamPosition !== "bigint") {
513
568
  throw new Error("Stream position is required");
514
569
  }
515
570
  const streamPosition = BigInt(message.metadata.streamPosition) + BigInt(expectedStreamVersion);
@@ -521,7 +576,7 @@ var buildMessageInsertQuery = (messages, expectedStreamVersion, streamId, partit
521
576
  message.kind === "Event" ? "E" : "C",
522
577
  JSONParser.stringify(message.data),
523
578
  JSONParser.stringify(message.metadata),
524
- _nullishCoalesce(_optionalChain([expectedStreamVersion, 'optionalAccess', _30 => _30.toString, 'call', _31 => _31()]), () => ( 0)),
579
+ _nullishCoalesce(_optionalChain([expectedStreamVersion, 'optionalAccess', _31 => _31.toString, 'call', _32 => _32()]), () => ( 0)),
525
580
  message.type,
526
581
  message.metadata.messageId,
527
582
  false
@@ -553,16 +608,383 @@ var buildMessageInsertQuery = (messages, expectedStreamVersion, streamId, partit
553
608
  return { sqlString, values: query.values };
554
609
  };
555
610
 
611
+ // src/eventStore/schema/tables.ts
612
+ var sql = (sql2) => sql2;
613
+ var streamsTableSQL = sql(
614
+ `CREATE TABLE IF NOT EXISTS ${streamsTable.name}(
615
+ stream_id TEXT NOT NULL,
616
+ stream_position BIGINT NOT NULL DEFAULT 0,
617
+ partition TEXT NOT NULL DEFAULT '${globalTag}',
618
+ stream_type TEXT NOT NULL,
619
+ stream_metadata JSONB NOT NULL,
620
+ is_archived BOOLEAN NOT NULL DEFAULT FALSE,
621
+ PRIMARY KEY (stream_id, stream_position, partition, is_archived),
622
+ UNIQUE (stream_id, partition, is_archived)
623
+ );`
624
+ );
625
+ var messagesTableSQL = sql(
626
+ `CREATE TABLE IF NOT EXISTS ${messagesTable.name}(
627
+ stream_id TEXT NOT NULL,
628
+ stream_position BIGINT NOT NULL,
629
+ partition TEXT NOT NULL DEFAULT '${globalTag}',
630
+ message_kind CHAR(1) NOT NULL DEFAULT 'E',
631
+ message_data JSONB NOT NULL,
632
+ message_metadata JSONB NOT NULL,
633
+ message_schema_version TEXT NOT NULL,
634
+ message_type TEXT NOT NULL,
635
+ message_id TEXT NOT NULL,
636
+ is_archived BOOLEAN NOT NULL DEFAULT FALSE,
637
+ global_position INTEGER PRIMARY KEY,
638
+ created DATETIME DEFAULT CURRENT_TIMESTAMP,
639
+ UNIQUE (stream_id, stream_position, partition, is_archived)
640
+ );
641
+ `
642
+ );
643
+ var subscriptionsTableSQL = sql(
644
+ `
645
+ CREATE TABLE IF NOT EXISTS ${subscriptionsTable.name}(
646
+ subscription_id TEXT NOT NULL,
647
+ version INTEGER NOT NULL DEFAULT 1,
648
+ partition TEXT NOT NULL DEFAULT '${globalTag}',
649
+ last_processed_position BIGINT NOT NULL,
650
+ PRIMARY KEY (subscription_id, partition, version)
651
+ );
652
+ `
653
+ );
654
+ var schemaSQL = [
655
+ streamsTableSQL,
656
+ messagesTableSQL,
657
+ subscriptionsTableSQL
658
+ ];
659
+ var createEventStoreSchema = async (db) => {
660
+ for (const sql2 of schemaSQL) {
661
+ await db.command(sql2);
662
+ }
663
+ };
664
+
665
+ // src/eventStore/schema/utils.ts
666
+ var singleOrNull = async (getResult) => {
667
+ const result = await getResult;
668
+ if (result.length > 1) throw new Error("Query had more than one result");
669
+ return result.length > 0 ? _nullishCoalesce(result[0], () => ( null)) : null;
670
+ };
671
+
672
+ // src/eventStore/schema/readLastMessageGlobalPosition.ts
673
+ var readLastMessageGlobalPosition = async (db, options) => {
674
+ const result = await singleOrNull(
675
+ db.query(
676
+ sql(
677
+ `SELECT global_position
678
+ FROM ${messagesTable.name}
679
+ WHERE partition = ? AND is_archived = FALSE
680
+ ORDER BY global_position
681
+ LIMIT 1`
682
+ ),
683
+ [_nullishCoalesce(_optionalChain([options, 'optionalAccess', _33 => _33.partition]), () => ( defaultTag))]
684
+ )
685
+ );
686
+ return {
687
+ currentGlobalPosition: result !== null ? BigInt(result.global_position) : null
688
+ };
689
+ };
690
+
691
+ // src/eventStore/schema/readMessagesBatch.ts
692
+ var readMessagesBatch = async (db, options) => {
693
+ const from = "from" in options ? options.from : "after" in options ? options.after + 1n : 0n;
694
+ const batchSize = options && "batchSize" in options ? options.batchSize : options.to - options.from;
695
+ const fromCondition = from !== -0n ? `AND global_position >= ${from}` : "";
696
+ const toCondition = "to" in options ? `AND global_position <= ${options.to}` : "";
697
+ const limitCondition = "batchSize" in options ? `LIMIT ${options.batchSize}` : "";
698
+ const events = (await db.query(
699
+ sql(
700
+ `SELECT stream_id, stream_position, global_position, message_data, message_metadata, message_schema_version, message_type, message_id
701
+ FROM ${messagesTable.name}
702
+ WHERE partition = ? AND is_archived = FALSE ${fromCondition} ${toCondition}
703
+ ORDER BY global_position
704
+ ${limitCondition}`
705
+ ),
706
+ [_nullishCoalesce(_optionalChain([options, 'optionalAccess', _34 => _34.partition]), () => ( defaultTag))]
707
+ )).map((row) => {
708
+ const rawEvent = {
709
+ type: row.message_type,
710
+ data: JSONParser.parse(row.message_data),
711
+ metadata: JSONParser.parse(row.message_metadata)
712
+ };
713
+ const metadata = {
714
+ ..."metadata" in rawEvent ? _nullishCoalesce(rawEvent.metadata, () => ( {})) : {},
715
+ messageId: row.message_id,
716
+ streamName: row.stream_id,
717
+ streamPosition: BigInt(row.stream_position),
718
+ globalPosition: BigInt(row.global_position)
719
+ };
720
+ return {
721
+ ...rawEvent,
722
+ kind: "Event",
723
+ metadata
724
+ };
725
+ });
726
+ return events.length > 0 ? {
727
+ currentGlobalPosition: events[events.length - 1].metadata.globalPosition,
728
+ messages: events,
729
+ areEventsLeft: events.length === batchSize
730
+ } : {
731
+ currentGlobalPosition: "from" in options ? options.from : "after" in options ? options.after : 0n,
732
+ messages: [],
733
+ areEventsLeft: false
734
+ };
735
+ };
736
+
737
+ // src/eventStore/schema/readProcessorCheckpoint.ts
738
+ var readProcessorCheckpoint = async (db, options) => {
739
+ const result = await singleOrNull(
740
+ db.query(
741
+ sql(
742
+ `SELECT last_processed_position
743
+ FROM ${subscriptionsTable.name}
744
+ WHERE partition = ? AND subscription_id = ?
745
+ LIMIT 1`
746
+ ),
747
+ [_nullishCoalesce(_optionalChain([options, 'optionalAccess', _35 => _35.partition]), () => ( defaultTag)), options.processorId]
748
+ )
749
+ );
750
+ return {
751
+ lastProcessedPosition: result !== null ? BigInt(result.last_processed_position) : null
752
+ };
753
+ };
754
+
755
+ // src/eventStore/consumers/messageBatchProcessing/index.ts
756
+ var DefaultSQLiteEventStoreProcessorBatchSize = 100;
757
+ var DefaultSQLiteEventStoreProcessorPullingFrequencyInMs = 50;
758
+ var sqliteEventStoreMessageBatchPuller = ({
759
+ db,
760
+ batchSize,
761
+ eachBatch,
762
+ pullingFrequencyInMs
763
+ }) => {
764
+ let isRunning = false;
765
+ let start;
766
+ const pullMessages = async (options) => {
767
+ const after = options.startFrom === "BEGINNING" ? 0n : options.startFrom === "END" ? await _asyncNullishCoalesce((await readLastMessageGlobalPosition(db)).currentGlobalPosition, async () => ( 0n)) : options.startFrom.globalPosition;
768
+ const readMessagesOptions = {
769
+ after,
770
+ batchSize
771
+ };
772
+ let waitTime = 100;
773
+ do {
774
+ const { messages, currentGlobalPosition, areEventsLeft } = await readMessagesBatch(db, readMessagesOptions);
775
+ if (messages.length > 0) {
776
+ const result = await eachBatch({ messages });
777
+ if (result && result.type === "STOP") {
778
+ isRunning = false;
779
+ break;
780
+ }
781
+ }
782
+ readMessagesOptions.after = currentGlobalPosition;
783
+ await new Promise((resolve) => setTimeout(resolve, waitTime));
784
+ if (!areEventsLeft) {
785
+ waitTime = Math.min(waitTime * 2, 1e3);
786
+ } else {
787
+ waitTime = pullingFrequencyInMs;
788
+ }
789
+ } while (isRunning);
790
+ };
791
+ return {
792
+ get isRunning() {
793
+ return isRunning;
794
+ },
795
+ start: (options) => {
796
+ if (isRunning) return start;
797
+ start = (async () => {
798
+ isRunning = true;
799
+ return pullMessages(options);
800
+ })();
801
+ return start;
802
+ },
803
+ stop: async () => {
804
+ if (!isRunning) return;
805
+ isRunning = false;
806
+ await start;
807
+ }
808
+ };
809
+ };
810
+ var zipSQLiteEventStoreMessageBatchPullerStartFrom = (options) => {
811
+ if (options.length === 0 || options.some((o) => o === void 0 || o === "BEGINNING"))
812
+ return "BEGINNING";
813
+ if (options.every((o) => o === "END")) return "END";
814
+ return options.filter((o) => o !== void 0 && o !== "BEGINNING" && o !== "END").sort((a, b) => a > b ? 1 : -1)[0];
815
+ };
816
+
817
+ // src/eventStore/consumers/sqliteProcessor.ts
818
+ var genericSQLiteProcessor = (options) => {
819
+ const { eachMessage } = options;
820
+ let isActive = true;
821
+ const getDb = (context) => {
822
+ const fileName = _nullishCoalesce(context.fileName, () => ( _optionalChain([options, 'access', _36 => _36.connectionOptions, 'optionalAccess', _37 => _37.fileName])));
823
+ if (!fileName)
824
+ throw new EmmettError(
825
+ `SQLite processor '${options.processorId}' is missing file name. Ensure that you passed it through options`
826
+ );
827
+ const db = _nullishCoalesce(_nullishCoalesce(context.db, () => ( _optionalChain([options, 'access', _38 => _38.connectionOptions, 'optionalAccess', _39 => _39.db]))), () => ( sqliteConnection({ fileName })));
828
+ return { db, fileName };
829
+ };
830
+ return {
831
+ id: options.processorId,
832
+ start: async (db) => {
833
+ isActive = true;
834
+ if (options.startFrom !== "CURRENT") return options.startFrom;
835
+ const { lastProcessedPosition } = await readProcessorCheckpoint(db, {
836
+ processorId: options.processorId,
837
+ partition: options.partition
838
+ });
839
+ if (lastProcessedPosition === null) return "BEGINNING";
840
+ return { globalPosition: lastProcessedPosition };
841
+ },
842
+ get isActive() {
843
+ return isActive;
844
+ },
845
+ handle: async ({ messages }, context) => {
846
+ if (!isActive) return;
847
+ const { db, fileName } = getDb(context);
848
+ return db.withTransaction(async () => {
849
+ let result = void 0;
850
+ let lastProcessedPosition = null;
851
+ for (const message of messages) {
852
+ const typedMessage = message;
853
+ const messageProcessingResult = await eachMessage(typedMessage, {
854
+ db,
855
+ fileName
856
+ });
857
+ await storeProcessorCheckpoint(db, {
858
+ processorId: options.processorId,
859
+ version: options.version,
860
+ lastProcessedPosition,
861
+ newPosition: typedMessage.metadata.globalPosition,
862
+ partition: options.partition
863
+ });
864
+ lastProcessedPosition = typedMessage.metadata.globalPosition;
865
+ if (messageProcessingResult && messageProcessingResult.type === "STOP") {
866
+ isActive = false;
867
+ result = messageProcessingResult;
868
+ break;
869
+ }
870
+ if (options.stopAfter && options.stopAfter(typedMessage)) {
871
+ isActive = false;
872
+ result = { type: "STOP", reason: "Stop condition reached" };
873
+ break;
874
+ }
875
+ if (messageProcessingResult && messageProcessingResult.type === "SKIP")
876
+ continue;
877
+ }
878
+ return result;
879
+ });
880
+ }
881
+ };
882
+ };
883
+ var sqliteProjectionProcessor = (options) => {
884
+ const projection2 = options.projection;
885
+ return genericSQLiteProcessor({
886
+ processorId: _nullishCoalesce(options.processorId, () => ( `projection:${projection2.name}`)),
887
+ eachMessage: async (event, context) => {
888
+ if (!projection2.canHandle.includes(event.type)) return;
889
+ await projection2.handle([event], context);
890
+ },
891
+ ...options
892
+ });
893
+ };
894
+ var sqliteProcessor = (options) => {
895
+ if ("projection" in options) {
896
+ return sqliteProjectionProcessor(options);
897
+ }
898
+ return genericSQLiteProcessor(options);
899
+ };
900
+
901
+ // src/eventStore/consumers/sqliteEventStoreConsumer.ts
902
+ var sqliteEventStoreConsumer = (options) => {
903
+ let isRunning = false;
904
+ const { pulling } = options;
905
+ const processors = _nullishCoalesce(options.processors, () => ( []));
906
+ let start;
907
+ let currentMessagePuller;
908
+ const db = _nullishCoalesce(options.db, () => ( sqliteConnection({ fileName: options.fileName })));
909
+ const eachBatch = async (messagesBatch) => {
910
+ const activeProcessors = processors.filter((s) => s.isActive);
911
+ if (activeProcessors.length === 0)
912
+ return {
913
+ type: "STOP",
914
+ reason: "No active processors"
915
+ };
916
+ const result = await Promise.allSettled(
917
+ activeProcessors.map((s) => {
918
+ return s.handle(messagesBatch, { db, fileName: options.fileName });
919
+ })
920
+ );
921
+ return result.some(
922
+ (r) => r.status === "fulfilled" && _optionalChain([r, 'access', _40 => _40.value, 'optionalAccess', _41 => _41.type]) !== "STOP"
923
+ ) ? void 0 : {
924
+ type: "STOP"
925
+ };
926
+ };
927
+ const messagePooler = currentMessagePuller = sqliteEventStoreMessageBatchPuller({
928
+ db,
929
+ eachBatch,
930
+ batchSize: _nullishCoalesce(_optionalChain([pulling, 'optionalAccess', _42 => _42.batchSize]), () => ( DefaultSQLiteEventStoreProcessorBatchSize)),
931
+ pullingFrequencyInMs: _nullishCoalesce(_optionalChain([pulling, 'optionalAccess', _43 => _43.pullingFrequencyInMs]), () => ( DefaultSQLiteEventStoreProcessorPullingFrequencyInMs))
932
+ });
933
+ const stop = async () => {
934
+ if (!isRunning) return;
935
+ isRunning = false;
936
+ if (currentMessagePuller) {
937
+ await currentMessagePuller.stop();
938
+ currentMessagePuller = void 0;
939
+ }
940
+ await start;
941
+ };
942
+ return {
943
+ processors,
944
+ get isRunning() {
945
+ return isRunning;
946
+ },
947
+ processor: (options2) => {
948
+ const processor = sqliteProcessor(options2);
949
+ processors.push(processor);
950
+ return processor;
951
+ },
952
+ start: () => {
953
+ if (isRunning) return start;
954
+ start = (async () => {
955
+ if (processors.length === 0)
956
+ return Promise.reject(
957
+ new EmmettError(
958
+ "Cannot start consumer without at least a single processor"
959
+ )
960
+ );
961
+ isRunning = true;
962
+ const startFrom = zipSQLiteEventStoreMessageBatchPullerStartFrom(
963
+ await Promise.all(processors.map((o) => o.start(db)))
964
+ );
965
+ return messagePooler.start({ startFrom });
966
+ })();
967
+ return start;
968
+ },
969
+ stop,
970
+ close: async () => {
971
+ await stop();
972
+ db.close();
973
+ await new Promise((resolve) => setTimeout(resolve, 250));
974
+ }
975
+ };
976
+ };
977
+
556
978
  // src/eventStore/projections/index.ts
557
979
  var handleProjections = async (options) => {
558
- const { projections: allProjections, events, connection } = options;
980
+ const { projections: allProjections, events, db } = options;
559
981
  const eventTypes = events.map((e) => e.type);
560
982
  const projections = allProjections.filter(
561
983
  (p) => p.canHandle.some((type) => eventTypes.includes(type))
562
984
  );
563
985
  for (const projection2 of projections) {
564
986
  await projection2.handle(events, {
565
- connection
987
+ db
566
988
  });
567
989
  }
568
990
  };
@@ -574,9 +996,9 @@ var getSQLiteEventStore = (options) => {
574
996
  let autoGenerateSchema = false;
575
997
  let database;
576
998
  const fileName = _nullishCoalesce(options.fileName, () => ( InMemorySQLiteDatabase));
577
- const isInMemory = fileName === InMemorySQLiteDatabase;
999
+ const isInMemory = fileName === InMemorySQLiteDatabase || fileName === InMemorySharedCacheSQLiteDatabase;
578
1000
  const inlineProjections = (_nullishCoalesce(options.projections, () => ( []))).filter(({ type }) => type === "inline").map(({ projection: projection2 }) => projection2);
579
- const onBeforeCommitHook = _optionalChain([options, 'access', _32 => _32.hooks, 'optionalAccess', _33 => _33.onBeforeCommit]);
1001
+ const onBeforeCommitHook = _optionalChain([options, 'access', _44 => _44.hooks, 'optionalAccess', _45 => _45.onBeforeCommit]);
580
1002
  const createConnection = () => {
581
1003
  if (database != null) {
582
1004
  return database;
@@ -606,12 +1028,12 @@ var getSQLiteEventStore = (options) => {
606
1028
  }
607
1029
  };
608
1030
  if (options) {
609
- autoGenerateSchema = _optionalChain([options, 'access', _34 => _34.schema, 'optionalAccess', _35 => _35.autoMigration]) === void 0 || _optionalChain([options, 'access', _36 => _36.schema, 'optionalAccess', _37 => _37.autoMigration]) !== "None";
1031
+ autoGenerateSchema = _optionalChain([options, 'access', _46 => _46.schema, 'optionalAccess', _47 => _47.autoMigration]) === void 0 || _optionalChain([options, 'access', _48 => _48.schema, 'optionalAccess', _49 => _49.autoMigration]) !== "None";
610
1032
  }
611
- const ensureSchemaExists = async (connection) => {
1033
+ const ensureSchemaExists = async (db) => {
612
1034
  if (!autoGenerateSchema) return Promise.resolve();
613
1035
  if (!schemaMigrated) {
614
- await createEventStoreSchema(connection);
1036
+ await createEventStoreSchema(db);
615
1037
  schemaMigrated = true;
616
1038
  }
617
1039
  return Promise.resolve();
@@ -619,7 +1041,7 @@ var getSQLiteEventStore = (options) => {
619
1041
  return {
620
1042
  async aggregateStream(streamName, options2) {
621
1043
  const { evolve, initialState, read } = options2;
622
- const expectedStreamVersion = _optionalChain([read, 'optionalAccess', _38 => _38.expectedStreamVersion]);
1044
+ const expectedStreamVersion = _optionalChain([read, 'optionalAccess', _50 => _50.expectedStreamVersion]);
623
1045
  let state = initialState();
624
1046
  if (typeof streamName !== "string") {
625
1047
  throw new Error("Stream name is not string");
@@ -671,14 +1093,19 @@ var getSQLiteEventStore = (options) => {
671
1093
  throw new ExpectedVersionConflictError(
672
1094
  -1n,
673
1095
  //TODO: Return actual version in case of error
674
- _nullishCoalesce(_optionalChain([options2, 'optionalAccess', _39 => _39.expectedStreamVersion]), () => ( NO_CONCURRENCY_CHECK))
1096
+ _nullishCoalesce(_optionalChain([options2, 'optionalAccess', _51 => _51.expectedStreamVersion]), () => ( NO_CONCURRENCY_CHECK))
675
1097
  );
676
1098
  return {
677
1099
  nextExpectedStreamVersion: appendResult.nextStreamPosition,
678
1100
  lastEventGlobalPosition: appendResult.lastGlobalPosition,
679
1101
  createdNewStream: appendResult.nextStreamPosition >= BigInt(events.length)
680
1102
  };
681
- }
1103
+ },
1104
+ consumer: (options2) => sqliteEventStoreConsumer({
1105
+ ..._nullishCoalesce(options2, () => ( {})),
1106
+ fileName,
1107
+ db: _nullishCoalesce(database, () => ( void 0))
1108
+ })
682
1109
  };
683
1110
  };
684
1111
 
@@ -693,7 +1120,7 @@ var readStream = async (db, streamId, options) => {
693
1120
  `SELECT stream_id, stream_position, global_position, message_data, message_metadata, message_schema_version, message_type, message_id
694
1121
  FROM ${messagesTable.name}
695
1122
  WHERE stream_id = ? AND partition = ? AND is_archived = FALSE ${fromCondition} ${toCondition}`,
696
- [streamId, _nullishCoalesce(_optionalChain([options, 'optionalAccess', _40 => _40.partition]), () => ( defaultTag))]
1123
+ [streamId, _nullishCoalesce(_optionalChain([options, 'optionalAccess', _52 => _52.partition]), () => ( defaultTag))]
697
1124
  );
698
1125
  const messages = results.map((row) => {
699
1126
  const rawEvent = {
@@ -725,44 +1152,91 @@ var readStream = async (db, streamId, options) => {
725
1152
  };
726
1153
  };
727
1154
 
728
- // src/eventStore/schema/tables.ts
729
- var sql = (sql2) => sql2;
730
- var streamsTableSQL = sql(
731
- `CREATE TABLE IF NOT EXISTS ${streamsTable.name}(
732
- stream_id TEXT NOT NULL,
733
- stream_position BIGINT NOT NULL DEFAULT 0,
734
- partition TEXT NOT NULL DEFAULT '${globalTag}',
735
- stream_type TEXT NOT NULL,
736
- stream_metadata JSONB NOT NULL,
737
- is_archived BOOLEAN NOT NULL DEFAULT FALSE,
738
- PRIMARY KEY (stream_id, stream_position, partition, is_archived),
739
- UNIQUE (stream_id, partition, is_archived)
740
- );`
741
- );
742
- var messagesTableSQL = sql(
743
- `CREATE TABLE IF NOT EXISTS ${messagesTable.name}(
744
- stream_id TEXT NOT NULL,
745
- stream_position BIGINT NOT NULL,
746
- partition TEXT NOT NULL DEFAULT '${globalTag}',
747
- message_kind CHAR(1) NOT NULL DEFAULT 'E',
748
- message_data JSONB NOT NULL,
749
- message_metadata JSONB NOT NULL,
750
- message_schema_version TEXT NOT NULL,
751
- message_type TEXT NOT NULL,
752
- message_id TEXT NOT NULL,
753
- is_archived BOOLEAN NOT NULL DEFAULT FALSE,
754
- global_position INTEGER PRIMARY KEY,
755
- created DATETIME DEFAULT CURRENT_TIMESTAMP,
756
- UNIQUE (stream_id, stream_position, partition, is_archived)
757
- );
758
- `
759
- );
760
- var schemaSQL = [streamsTableSQL, messagesTableSQL];
761
- var createEventStoreSchema = async (db) => {
762
- for (const sql2 of schemaSQL) {
763
- await db.command(sql2);
1155
+ // src/eventStore/schema/storeProcessorCheckpoint.ts
1156
+ async function storeSubscriptionCheckpointSQLite(db, processorId, version, position, checkPosition, partition) {
1157
+ if (checkPosition !== null) {
1158
+ const updateResult = await db.command(
1159
+ sql(`
1160
+ UPDATE ${subscriptionsTable.name}
1161
+ SET last_processed_position = ?
1162
+ WHERE subscription_id = ?
1163
+ AND last_processed_position = ?
1164
+ AND partition = ?
1165
+ `),
1166
+ [position.toString(), processorId, checkPosition.toString(), partition]
1167
+ );
1168
+ if (updateResult.changes > 0) {
1169
+ return 1;
1170
+ } else {
1171
+ const current_position = await singleOrNull(
1172
+ db.query(
1173
+ sql(
1174
+ `SELECT last_processed_position FROM ${subscriptionsTable.name}
1175
+ WHERE subscription_id = ? AND partition = ?`
1176
+ ),
1177
+ [processorId, partition]
1178
+ )
1179
+ );
1180
+ if (_optionalChain([current_position, 'optionalAccess', _53 => _53.last_processed_position]) === position) {
1181
+ return 0;
1182
+ } else if (position !== null && current_position !== null && _optionalChain([current_position, 'optionalAccess', _54 => _54.last_processed_position]) > position) {
1183
+ return 2;
1184
+ } else {
1185
+ return 2;
1186
+ }
1187
+ }
1188
+ } else {
1189
+ try {
1190
+ await db.command(
1191
+ sql(
1192
+ `INSERT INTO ${subscriptionsTable.name} (subscription_id, version, last_processed_position, partition) VALUES (?, ?, ?, ?)`
1193
+ ),
1194
+ [processorId, version, position.toString(), partition]
1195
+ );
1196
+ return 1;
1197
+ } catch (err) {
1198
+ if (!(isSQLiteError(err) && (err.errno === 19 || err.errno === 2067))) {
1199
+ throw err;
1200
+ }
1201
+ const current = await singleOrNull(
1202
+ db.query(
1203
+ sql(
1204
+ `SELECT last_processed_position FROM ${subscriptionsTable.name} WHERE subscription_id = ? AND partition = ?`
1205
+ ),
1206
+ [processorId, partition]
1207
+ )
1208
+ );
1209
+ if (_optionalChain([current, 'optionalAccess', _55 => _55.last_processed_position]) === position) {
1210
+ return 0;
1211
+ } else {
1212
+ return 2;
1213
+ }
1214
+ }
764
1215
  }
765
- };
1216
+ }
1217
+ async function storeProcessorCheckpoint(db, options) {
1218
+ try {
1219
+ const result = await storeSubscriptionCheckpointSQLite(
1220
+ db,
1221
+ options.processorId,
1222
+ _nullishCoalesce(options.version, () => ( 1)),
1223
+ options.newPosition,
1224
+ options.lastProcessedPosition,
1225
+ _nullishCoalesce(options.partition, () => ( defaultTag))
1226
+ );
1227
+ return result === 1 ? { success: true, newPosition: options.newPosition } : { success: false, reason: result === 0 ? "IGNORED" : "MISMATCH" };
1228
+ } catch (error) {
1229
+ console.log(error);
1230
+ throw error;
1231
+ }
1232
+ }
1233
+
1234
+
1235
+
1236
+
1237
+
1238
+
1239
+
766
1240
 
767
1241
 
768
1242
 
@@ -782,5 +1256,5 @@ var createEventStoreSchema = async (db) => {
782
1256
 
783
1257
 
784
1258
 
785
- exports.InMemorySQLiteDatabase = InMemorySQLiteDatabase; 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.readStream = readStream; exports.schemaSQL = schemaSQL; exports.sql = sql; exports.sqliteConnection = sqliteConnection; exports.streamsTable = streamsTable; exports.streamsTableSQL = streamsTableSQL;
1259
+ 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;
786
1260
  //# sourceMappingURL=index.cjs.map