@powersync/common 1.45.0 → 1.46.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/dist/bundle.cjs +412 -2067
  2. package/dist/bundle.cjs.map +1 -1
  3. package/dist/bundle.mjs +410 -2068
  4. package/dist/bundle.mjs.map +1 -1
  5. package/dist/bundle.node.cjs +237 -63
  6. package/dist/bundle.node.cjs.map +1 -1
  7. package/dist/bundle.node.mjs +235 -64
  8. package/dist/bundle.node.mjs.map +1 -1
  9. package/dist/index.d.cts +94 -15
  10. package/lib/client/AbstractPowerSyncDatabase.d.ts +9 -2
  11. package/lib/client/AbstractPowerSyncDatabase.js +18 -5
  12. package/lib/client/AbstractPowerSyncDatabase.js.map +1 -1
  13. package/lib/client/sync/stream/AbstractRemote.js +41 -32
  14. package/lib/client/sync/stream/AbstractRemote.js.map +1 -1
  15. package/lib/client/sync/stream/AbstractStreamingSyncImplementation.d.ts +7 -12
  16. package/lib/client/sync/stream/AbstractStreamingSyncImplementation.js +10 -12
  17. package/lib/client/sync/stream/AbstractStreamingSyncImplementation.js.map +1 -1
  18. package/lib/client/triggers/MemoryTriggerClaimManager.d.ts +6 -0
  19. package/lib/client/triggers/MemoryTriggerClaimManager.js +21 -0
  20. package/lib/client/triggers/MemoryTriggerClaimManager.js.map +1 -0
  21. package/lib/client/triggers/TriggerManager.d.ts +37 -0
  22. package/lib/client/triggers/TriggerManagerImpl.d.ts +24 -3
  23. package/lib/client/triggers/TriggerManagerImpl.js +133 -11
  24. package/lib/client/triggers/TriggerManagerImpl.js.map +1 -1
  25. package/lib/index.d.ts +3 -0
  26. package/lib/index.js +3 -0
  27. package/lib/index.js.map +1 -1
  28. package/lib/utils/DataStream.js +11 -2
  29. package/lib/utils/DataStream.js.map +1 -1
  30. package/package.json +4 -3
  31. package/src/client/AbstractPowerSyncDatabase.ts +21 -6
  32. package/src/client/sync/stream/AbstractRemote.ts +47 -35
  33. package/src/client/sync/stream/AbstractStreamingSyncImplementation.ts +11 -14
  34. package/src/client/triggers/MemoryTriggerClaimManager.ts +25 -0
  35. package/src/client/triggers/TriggerManager.ts +50 -6
  36. package/src/client/triggers/TriggerManagerImpl.ts +177 -13
  37. package/src/index.ts +3 -0
  38. package/src/utils/DataStream.ts +13 -2
@@ -6722,7 +6722,7 @@ function requireDist () {
6722
6722
 
6723
6723
  var distExports = requireDist();
6724
6724
 
6725
- var version = "1.45.0";
6725
+ var version = "1.46.0";
6726
6726
  var PACKAGE = {
6727
6727
  version: version};
6728
6728
 
@@ -6793,6 +6793,16 @@ class DataStream extends BaseObserver {
6793
6793
  * @returns a Data payload or Null if the stream closed.
6794
6794
  */
6795
6795
  async read() {
6796
+ if (this.closed) {
6797
+ return null;
6798
+ }
6799
+ // Wait for any pending processing to complete first.
6800
+ // This ensures we register our listener before calling processQueue(),
6801
+ // avoiding a race where processQueue() sees no reader and returns early.
6802
+ if (this.processingPromise) {
6803
+ await this.processingPromise;
6804
+ }
6805
+ // Re-check after await - stream may have closed while we were waiting
6796
6806
  if (this.closed) {
6797
6807
  return null;
6798
6808
  }
@@ -6832,7 +6842,7 @@ class DataStream extends BaseObserver {
6832
6842
  }
6833
6843
  const promise = (this.processingPromise = this._processQueue());
6834
6844
  promise.finally(() => {
6835
- return (this.processingPromise = null);
6845
+ this.processingPromise = null;
6836
6846
  });
6837
6847
  return promise;
6838
6848
  }
@@ -6864,7 +6874,6 @@ class DataStream extends BaseObserver {
6864
6874
  this.notifyDataAdded = null;
6865
6875
  }
6866
6876
  if (this.dataQueue.length > 0) {
6867
- // Next tick
6868
6877
  setTimeout(() => this.processQueue());
6869
6878
  }
6870
6879
  }
@@ -7484,7 +7493,11 @@ class AbstractRemote {
7484
7493
  };
7485
7494
  const stream = new DataStream({
7486
7495
  logger: this.logger,
7487
- mapLine: mapLine
7496
+ mapLine: mapLine,
7497
+ pressure: {
7498
+ highWaterMark: 20,
7499
+ lowWaterMark: 10
7500
+ }
7488
7501
  });
7489
7502
  abortSignal?.addEventListener('abort', () => {
7490
7503
  closeReader();
@@ -7492,42 +7505,47 @@ class AbstractRemote {
7492
7505
  });
7493
7506
  const decoder = this.createTextDecoder();
7494
7507
  let buffer = '';
7495
- const l = stream.registerListener({
7496
- lowWater: async () => {
7497
- if (stream.closed || abortSignal?.aborted || readerReleased) {
7508
+ const consumeStream = async () => {
7509
+ while (!stream.closed && !abortSignal?.aborted && !readerReleased) {
7510
+ const { done, value } = await reader.read();
7511
+ if (done) {
7512
+ const remaining = buffer.trim();
7513
+ if (remaining.length != 0) {
7514
+ stream.enqueueData(remaining);
7515
+ }
7516
+ stream.close();
7517
+ await closeReader();
7498
7518
  return;
7499
7519
  }
7500
- try {
7501
- let didCompleteLine = false;
7502
- while (!didCompleteLine) {
7503
- const { done, value } = await reader.read();
7504
- if (done) {
7505
- const remaining = buffer.trim();
7506
- if (remaining.length != 0) {
7507
- stream.enqueueData(remaining);
7508
- }
7509
- stream.close();
7510
- await closeReader();
7511
- return;
7512
- }
7513
- const data = decoder.decode(value, { stream: true });
7514
- buffer += data;
7515
- const lines = buffer.split('\n');
7516
- for (var i = 0; i < lines.length - 1; i++) {
7517
- var l = lines[i].trim();
7518
- if (l.length > 0) {
7519
- stream.enqueueData(l);
7520
- didCompleteLine = true;
7521
- }
7522
- }
7523
- buffer = lines[lines.length - 1];
7520
+ const data = decoder.decode(value, { stream: true });
7521
+ buffer += data;
7522
+ const lines = buffer.split('\n');
7523
+ for (var i = 0; i < lines.length - 1; i++) {
7524
+ var l = lines[i].trim();
7525
+ if (l.length > 0) {
7526
+ stream.enqueueData(l);
7524
7527
  }
7525
7528
  }
7526
- catch (ex) {
7527
- stream.close();
7528
- throw ex;
7529
+ buffer = lines[lines.length - 1];
7530
+ // Implement backpressure by waiting for the low water mark to be reached
7531
+ if (stream.dataQueue.length > stream.highWatermark) {
7532
+ await new Promise((resolve) => {
7533
+ const dispose = stream.registerListener({
7534
+ lowWater: async () => {
7535
+ resolve();
7536
+ dispose();
7537
+ },
7538
+ closed: () => {
7539
+ resolve();
7540
+ dispose();
7541
+ }
7542
+ });
7543
+ });
7529
7544
  }
7530
- },
7545
+ }
7546
+ };
7547
+ consumeStream().catch(ex => this.logger.error('Error consuming stream', ex));
7548
+ const l = stream.registerListener({
7531
7549
  closed: () => {
7532
7550
  closeReader();
7533
7551
  l?.();
@@ -7606,18 +7624,17 @@ exports.SyncClientImplementation = void 0;
7606
7624
  *
7607
7625
  * This is the default option.
7608
7626
  *
7609
- * @deprecated Don't use {@link SyncClientImplementation.JAVASCRIPT} directly. Instead, use
7610
- * {@link DEFAULT_SYNC_CLIENT_IMPLEMENTATION} or omit the option. The explicit choice to use
7611
- * the JavaScript-based sync implementation will be removed from a future version of the SDK.
7627
+ * @deprecated We recommend the {@link RUST} client implementation for all apps. If you have issues with
7628
+ * the Rust client, please file an issue or reach out to us. The JavaScript client will be removed in a future
7629
+ * version of the PowerSync SDK.
7612
7630
  */
7613
7631
  SyncClientImplementation["JAVASCRIPT"] = "js";
7614
7632
  /**
7615
7633
  * This implementation offloads the sync line decoding and handling into the PowerSync
7616
7634
  * core extension.
7617
7635
  *
7618
- * @experimental
7619
- * While this implementation is more performant than {@link SyncClientImplementation.JAVASCRIPT},
7620
- * it has seen less real-world testing and is marked as __experimental__ at the moment.
7636
+ * This option is more performant than the {@link JAVASCRIPT} client, enabled by default and the
7637
+ * recommended client implementation for all apps.
7621
7638
  *
7622
7639
  * ## Compatibility warning
7623
7640
  *
@@ -7635,13 +7652,9 @@ exports.SyncClientImplementation = void 0;
7635
7652
  SyncClientImplementation["RUST"] = "rust";
7636
7653
  })(exports.SyncClientImplementation || (exports.SyncClientImplementation = {}));
7637
7654
  /**
7638
- * The default {@link SyncClientImplementation} to use.
7639
- *
7640
- * Please use this field instead of {@link SyncClientImplementation.JAVASCRIPT} directly. A future version
7641
- * of the PowerSync SDK will enable {@link SyncClientImplementation.RUST} by default and remove the JavaScript
7642
- * option.
7655
+ * The default {@link SyncClientImplementation} to use, {@link SyncClientImplementation.RUST}.
7643
7656
  */
7644
- const DEFAULT_SYNC_CLIENT_IMPLEMENTATION = exports.SyncClientImplementation.JAVASCRIPT;
7657
+ const DEFAULT_SYNC_CLIENT_IMPLEMENTATION = exports.SyncClientImplementation.RUST;
7645
7658
  const DEFAULT_CRUD_UPLOAD_THROTTLE_MS = 1000;
7646
7659
  const DEFAULT_RETRY_DELAY_MS = 5000;
7647
7660
  const DEFAULT_STREAMING_SYNC_OPTIONS = {
@@ -8051,6 +8064,9 @@ The next upload iteration will be delayed.`);
8051
8064
  if (rawTables != null && rawTables.length) {
8052
8065
  this.logger.warn('Raw tables require the Rust-based sync client. The JS client will ignore them.');
8053
8066
  }
8067
+ if (this.activeStreams.length) {
8068
+ this.logger.error('Sync streams require `clientImplementation: SyncClientImplementation.RUST` when connecting.');
8069
+ }
8054
8070
  this.logger.debug('Streaming sync iteration started');
8055
8071
  this.options.adapter.startSession();
8056
8072
  let [req, bucketMap] = await this.collectLocalBucketState();
@@ -8566,6 +8582,27 @@ The next upload iteration will be delayed.`);
8566
8582
  }
8567
8583
  }
8568
8584
 
8585
+ const CLAIM_STORE = new Map();
8586
+ /**
8587
+ * @internal
8588
+ * @experimental
8589
+ */
8590
+ const MEMORY_TRIGGER_CLAIM_MANAGER = {
8591
+ async obtainClaim(identifier) {
8592
+ if (CLAIM_STORE.has(identifier)) {
8593
+ throw new Error(`A claim is already present for ${identifier}`);
8594
+ }
8595
+ const release = async () => {
8596
+ CLAIM_STORE.delete(identifier);
8597
+ };
8598
+ CLAIM_STORE.set(identifier, release);
8599
+ return release;
8600
+ },
8601
+ async checkClaim(identifier) {
8602
+ return CLAIM_STORE.has(identifier);
8603
+ }
8604
+ };
8605
+
8569
8606
  /**
8570
8607
  * SQLite operations to track changes for with {@link TriggerManager}
8571
8608
  * @experimental
@@ -8577,9 +8614,20 @@ exports.DiffTriggerOperation = void 0;
8577
8614
  DiffTriggerOperation["DELETE"] = "DELETE";
8578
8615
  })(exports.DiffTriggerOperation || (exports.DiffTriggerOperation = {}));
8579
8616
 
8617
+ const DEFAULT_TRIGGER_MANAGER_CONFIGURATION = {
8618
+ useStorageByDefault: false
8619
+ };
8620
+ const TRIGGER_CLEANUP_INTERVAL_MS = 120_000; // 2 minutes
8621
+ /**
8622
+ * @internal
8623
+ * @experimental
8624
+ */
8580
8625
  class TriggerManagerImpl {
8581
8626
  options;
8582
8627
  schema;
8628
+ defaultConfig;
8629
+ cleanupTimeout;
8630
+ isDisposed;
8583
8631
  constructor(options) {
8584
8632
  this.options = options;
8585
8633
  this.schema = options.schema;
@@ -8588,6 +8636,33 @@ class TriggerManagerImpl {
8588
8636
  this.schema = schema;
8589
8637
  }
8590
8638
  });
8639
+ this.isDisposed = false;
8640
+ /**
8641
+ * Configure a cleanup to run on an interval.
8642
+ * The interval is configured using setTimeout to take the async
8643
+ * execution time of the callback into account.
8644
+ */
8645
+ this.defaultConfig = DEFAULT_TRIGGER_MANAGER_CONFIGURATION;
8646
+ const cleanupCallback = async () => {
8647
+ this.cleanupTimeout = null;
8648
+ if (this.isDisposed) {
8649
+ return;
8650
+ }
8651
+ try {
8652
+ await this.cleanupResources();
8653
+ }
8654
+ catch (ex) {
8655
+ this.db.logger.error(`Caught error while attempting to cleanup triggers`, ex);
8656
+ }
8657
+ finally {
8658
+ // if not closed, set another timeout
8659
+ if (this.isDisposed) {
8660
+ return;
8661
+ }
8662
+ this.cleanupTimeout = setTimeout(cleanupCallback, TRIGGER_CLEANUP_INTERVAL_MS);
8663
+ }
8664
+ };
8665
+ this.cleanupTimeout = setTimeout(cleanupCallback, TRIGGER_CLEANUP_INTERVAL_MS);
8591
8666
  }
8592
8667
  get db() {
8593
8668
  return this.options.db;
@@ -8605,13 +8680,95 @@ class TriggerManagerImpl {
8605
8680
  await tx.execute(/* sql */ `DROP TRIGGER IF EXISTS ${triggerId}; `);
8606
8681
  }
8607
8682
  }
8683
+ dispose() {
8684
+ this.isDisposed = true;
8685
+ if (this.cleanupTimeout) {
8686
+ clearTimeout(this.cleanupTimeout);
8687
+ }
8688
+ }
8689
+ /**
8690
+ * Updates default config settings for platform specific use-cases.
8691
+ */
8692
+ updateDefaults(config) {
8693
+ this.defaultConfig = {
8694
+ ...this.defaultConfig,
8695
+ ...config
8696
+ };
8697
+ }
8698
+ generateTriggerName(operation, destinationTable, triggerId) {
8699
+ return `__ps_temp_trigger_${operation.toLowerCase()}__${destinationTable}__${triggerId}`;
8700
+ }
8701
+ /**
8702
+ * Cleanup any SQLite triggers or tables that are no longer in use.
8703
+ */
8704
+ async cleanupResources() {
8705
+ // we use the database here since cleanupResources is called during the PowerSyncDatabase initialization
8706
+ await this.db.database.writeLock(async (ctx) => {
8707
+ /**
8708
+ * Note: We only cleanup persisted triggers. These are tracked in the sqlite_master table.
8709
+ * temporary triggers will not be affected by this.
8710
+ * Query all triggers that match our naming pattern
8711
+ */
8712
+ const triggers = await ctx.getAll(/* sql */ `
8713
+ SELECT
8714
+ name
8715
+ FROM
8716
+ sqlite_master
8717
+ WHERE
8718
+ type = 'trigger'
8719
+ AND name LIKE '__ps_temp_trigger_%'
8720
+ `);
8721
+ /** Use regex to extract table names and IDs from trigger names
8722
+ * Trigger naming convention: __ps_temp_trigger_<operation>__<destination_table>__<id>
8723
+ */
8724
+ const triggerPattern = /^__ps_temp_trigger_(?:insert|update|delete)__(.+)__([a-f0-9_]{36})$/i;
8725
+ const trackedItems = new Map();
8726
+ for (const trigger of triggers) {
8727
+ const match = trigger.name.match(triggerPattern);
8728
+ if (match) {
8729
+ const [, table, id] = match;
8730
+ // Collect all trigger names for each id combo
8731
+ const existing = trackedItems.get(id);
8732
+ if (existing) {
8733
+ existing.triggerNames.push(trigger.name);
8734
+ }
8735
+ else {
8736
+ trackedItems.set(id, { table, id, triggerNames: [trigger.name] });
8737
+ }
8738
+ }
8739
+ }
8740
+ for (const trackedItem of trackedItems.values()) {
8741
+ // check if there is anything holding on to this item
8742
+ const hasClaim = await this.options.claimManager.checkClaim(trackedItem.id);
8743
+ if (hasClaim) {
8744
+ // This does not require cleanup
8745
+ continue;
8746
+ }
8747
+ this.db.logger.debug(`Clearing resources for trigger ${trackedItem.id} with table ${trackedItem.table}`);
8748
+ // We need to delete the triggers and table
8749
+ for (const triggerName of trackedItem.triggerNames) {
8750
+ await ctx.execute(`DROP TRIGGER IF EXISTS ${triggerName}`);
8751
+ }
8752
+ await ctx.execute(`DROP TABLE IF EXISTS ${trackedItem.table}`);
8753
+ }
8754
+ });
8755
+ }
8608
8756
  async createDiffTrigger(options) {
8609
8757
  await this.db.waitForReady();
8610
- const { source, destination, columns, when, hooks } = options;
8758
+ const { source, destination, columns, when, hooks,
8759
+ // Fall back to the provided default if not given on this level
8760
+ useStorage = this.defaultConfig.useStorageByDefault } = options;
8611
8761
  const operations = Object.keys(when);
8612
8762
  if (operations.length == 0) {
8613
8763
  throw new Error('At least one WHEN operation must be specified for the trigger.');
8614
8764
  }
8765
+ /**
8766
+ * The clause to use when executing
8767
+ * CREATE ${tableTriggerTypeClause} TABLE
8768
+ * OR
8769
+ * CREATE ${tableTriggerTypeClause} TRIGGER
8770
+ */
8771
+ const tableTriggerTypeClause = !useStorage ? 'TEMP' : '';
8615
8772
  const whenClauses = Object.fromEntries(Object.entries(when).map(([operation, filter]) => [operation, `WHEN ${filter}`]));
8616
8773
  /**
8617
8774
  * Allow specifying the View name as the source.
@@ -8625,6 +8782,7 @@ class TriggerManagerImpl {
8625
8782
  const internalSource = sourceDefinition.internalName;
8626
8783
  const triggerIds = [];
8627
8784
  const id = await this.getUUID();
8785
+ const releaseStorageClaim = useStorage ? await this.options.claimManager.obtainClaim(id) : null;
8628
8786
  /**
8629
8787
  * We default to replicating all columns if no columns array is provided.
8630
8788
  */
@@ -8657,26 +8815,27 @@ class TriggerManagerImpl {
8657
8815
  return this.db.writeLock(async (tx) => {
8658
8816
  await this.removeTriggers(tx, triggerIds);
8659
8817
  await tx.execute(/* sql */ `DROP TABLE IF EXISTS ${destination};`);
8818
+ await releaseStorageClaim?.();
8660
8819
  });
8661
8820
  };
8662
8821
  const setup = async (tx) => {
8663
8822
  // Allow user code to execute in this lock context before the trigger is created.
8664
8823
  await hooks?.beforeCreate?.(tx);
8665
8824
  await tx.execute(/* sql */ `
8666
- CREATE TEMP TABLE ${destination} (
8825
+ CREATE ${tableTriggerTypeClause} TABLE ${destination} (
8667
8826
  operation_id INTEGER PRIMARY KEY AUTOINCREMENT,
8668
8827
  id TEXT,
8669
8828
  operation TEXT,
8670
8829
  timestamp TEXT,
8671
8830
  value TEXT,
8672
8831
  previous_value TEXT
8673
- );
8832
+ )
8674
8833
  `);
8675
8834
  if (operations.includes(exports.DiffTriggerOperation.INSERT)) {
8676
- const insertTriggerId = `ps_temp_trigger_insert_${id}`;
8835
+ const insertTriggerId = this.generateTriggerName(exports.DiffTriggerOperation.INSERT, destination, id);
8677
8836
  triggerIds.push(insertTriggerId);
8678
8837
  await tx.execute(/* sql */ `
8679
- CREATE TEMP TRIGGER ${insertTriggerId} AFTER INSERT ON ${internalSource} ${whenClauses[exports.DiffTriggerOperation.INSERT]} BEGIN
8838
+ CREATE ${tableTriggerTypeClause} TRIGGER ${insertTriggerId} AFTER INSERT ON ${internalSource} ${whenClauses[exports.DiffTriggerOperation.INSERT]} BEGIN
8680
8839
  INSERT INTO
8681
8840
  ${destination} (id, operation, timestamp, value)
8682
8841
  VALUES
@@ -8687,14 +8846,14 @@ class TriggerManagerImpl {
8687
8846
  ${jsonFragment('NEW')}
8688
8847
  );
8689
8848
 
8690
- END;
8849
+ END
8691
8850
  `);
8692
8851
  }
8693
8852
  if (operations.includes(exports.DiffTriggerOperation.UPDATE)) {
8694
- const updateTriggerId = `ps_temp_trigger_update_${id}`;
8853
+ const updateTriggerId = this.generateTriggerName(exports.DiffTriggerOperation.UPDATE, destination, id);
8695
8854
  triggerIds.push(updateTriggerId);
8696
8855
  await tx.execute(/* sql */ `
8697
- CREATE TEMP TRIGGER ${updateTriggerId} AFTER
8856
+ CREATE ${tableTriggerTypeClause} TRIGGER ${updateTriggerId} AFTER
8698
8857
  UPDATE ON ${internalSource} ${whenClauses[exports.DiffTriggerOperation.UPDATE]} BEGIN
8699
8858
  INSERT INTO
8700
8859
  ${destination} (id, operation, timestamp, value, previous_value)
@@ -8711,11 +8870,11 @@ class TriggerManagerImpl {
8711
8870
  `);
8712
8871
  }
8713
8872
  if (operations.includes(exports.DiffTriggerOperation.DELETE)) {
8714
- const deleteTriggerId = `ps_temp_trigger_delete_${id}`;
8873
+ const deleteTriggerId = this.generateTriggerName(exports.DiffTriggerOperation.DELETE, destination, id);
8715
8874
  triggerIds.push(deleteTriggerId);
8716
8875
  // Create delete trigger for basic JSON
8717
8876
  await tx.execute(/* sql */ `
8718
- CREATE TEMP TRIGGER ${deleteTriggerId} AFTER DELETE ON ${internalSource} ${whenClauses[exports.DiffTriggerOperation.DELETE]} BEGIN
8877
+ CREATE ${tableTriggerTypeClause} TRIGGER ${deleteTriggerId} AFTER DELETE ON ${internalSource} ${whenClauses[exports.DiffTriggerOperation.DELETE]} BEGIN
8719
8878
  INSERT INTO
8720
8879
  ${destination} (id, operation, timestamp, value)
8721
8880
  VALUES
@@ -8759,7 +8918,7 @@ class TriggerManagerImpl {
8759
8918
  // If no array is provided, we use all columns from the source table.
8760
8919
  const contextColumns = columns ?? sourceDefinition.columns.map((col) => col.name);
8761
8920
  const id = await this.getUUID();
8762
- const destination = `ps_temp_track_${source}_${id}`;
8921
+ const destination = `__ps_temp_track_${source}_${id}`;
8763
8922
  // register an onChange before the trigger is created
8764
8923
  const abortController = new AbortController();
8765
8924
  const abortOnChange = () => abortController.abort();
@@ -8914,6 +9073,7 @@ class AbstractPowerSyncDatabase extends BaseObserver {
8914
9073
  * Allows creating SQLite triggers which can be used to track various operations on SQLite tables.
8915
9074
  */
8916
9075
  triggers;
9076
+ triggersImpl;
8917
9077
  logger;
8918
9078
  constructor(options) {
8919
9079
  super();
@@ -8977,9 +9137,10 @@ class AbstractPowerSyncDatabase extends BaseObserver {
8977
9137
  logger: this.logger
8978
9138
  });
8979
9139
  this._isReadyPromise = this.initialize();
8980
- this.triggers = new TriggerManagerImpl({
9140
+ this.triggers = this.triggersImpl = new TriggerManagerImpl({
8981
9141
  db: this,
8982
- schema: this.schema
9142
+ schema: this.schema,
9143
+ ...this.generateTriggerManagerConfig()
8983
9144
  });
8984
9145
  }
8985
9146
  /**
@@ -9005,6 +9166,15 @@ class AbstractPowerSyncDatabase extends BaseObserver {
9005
9166
  get connecting() {
9006
9167
  return this.currentStatus?.connecting || false;
9007
9168
  }
9169
+ /**
9170
+ * Generates a base configuration for {@link TriggerManagerImpl}.
9171
+ * Implementations should override this if necessary.
9172
+ */
9173
+ generateTriggerManagerConfig() {
9174
+ return {
9175
+ claimManager: MEMORY_TRIGGER_CLAIM_MANAGER
9176
+ };
9177
+ }
9008
9178
  /**
9009
9179
  * @returns A promise which will resolve once initialization is completed.
9010
9180
  */
@@ -9065,14 +9235,15 @@ class AbstractPowerSyncDatabase extends BaseObserver {
9065
9235
  async initialize() {
9066
9236
  await this._initialize();
9067
9237
  await this.bucketStorageAdapter.init();
9068
- await this._loadVersion();
9238
+ await this.loadVersion();
9069
9239
  await this.updateSchema(this.options.schema);
9070
9240
  await this.resolveOfflineSyncStatus();
9071
9241
  await this.database.execute('PRAGMA RECURSIVE_TRIGGERS=TRUE');
9242
+ await this.triggersImpl.cleanupResources();
9072
9243
  this.ready = true;
9073
9244
  this.iterateListeners((cb) => cb.initialized?.());
9074
9245
  }
9075
- async _loadVersion() {
9246
+ async loadVersion() {
9076
9247
  try {
9077
9248
  const { version } = await this.database.get('SELECT powersync_rs_version() as version');
9078
9249
  this.sdkVersion = version;
@@ -9188,7 +9359,6 @@ class AbstractPowerSyncDatabase extends BaseObserver {
9188
9359
  await this.disconnect();
9189
9360
  await this.waitForReady();
9190
9361
  const { clearLocal } = options;
9191
- // TODO DB name, verify this is necessary with extension
9192
9362
  await this.database.writeTransaction(async (tx) => {
9193
9363
  await tx.execute('SELECT powersync_clear(?)', [clearLocal ? 1 : 0]);
9194
9364
  });
@@ -9220,6 +9390,7 @@ class AbstractPowerSyncDatabase extends BaseObserver {
9220
9390
  if (this.closed) {
9221
9391
  return;
9222
9392
  }
9393
+ this.triggersImpl.dispose();
9223
9394
  await this.iterateAsyncListeners(async (cb) => cb.closing?.());
9224
9395
  const { disconnect } = options;
9225
9396
  if (disconnect) {
@@ -10831,9 +11002,11 @@ exports.InvalidSQLCharacters = InvalidSQLCharacters;
10831
11002
  exports.LogLevel = LogLevel;
10832
11003
  exports.MAX_AMOUNT_OF_COLUMNS = MAX_AMOUNT_OF_COLUMNS;
10833
11004
  exports.MAX_OP_ID = MAX_OP_ID;
11005
+ exports.MEMORY_TRIGGER_CLAIM_MANAGER = MEMORY_TRIGGER_CLAIM_MANAGER;
10834
11006
  exports.OnChangeQueryProcessor = OnChangeQueryProcessor;
10835
11007
  exports.OpType = OpType;
10836
11008
  exports.OplogEntry = OplogEntry;
11009
+ exports.RawTable = RawTable;
10837
11010
  exports.Schema = Schema;
10838
11011
  exports.SqliteBucketStorage = SqliteBucketStorage;
10839
11012
  exports.SyncDataBatch = SyncDataBatch;
@@ -10842,6 +11015,7 @@ exports.SyncProgress = SyncProgress;
10842
11015
  exports.SyncStatus = SyncStatus;
10843
11016
  exports.Table = Table;
10844
11017
  exports.TableV2 = TableV2;
11018
+ exports.TriggerManagerImpl = TriggerManagerImpl;
10845
11019
  exports.UploadQueueStats = UploadQueueStats;
10846
11020
  exports.column = column;
10847
11021
  exports.compilableQueryWatch = compilableQueryWatch;