@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
@@ -6720,7 +6720,7 @@ function requireDist () {
6720
6720
 
6721
6721
  var distExports = requireDist();
6722
6722
 
6723
- var version = "1.45.0";
6723
+ var version = "1.46.0";
6724
6724
  var PACKAGE = {
6725
6725
  version: version};
6726
6726
 
@@ -6791,6 +6791,16 @@ class DataStream extends BaseObserver {
6791
6791
  * @returns a Data payload or Null if the stream closed.
6792
6792
  */
6793
6793
  async read() {
6794
+ if (this.closed) {
6795
+ return null;
6796
+ }
6797
+ // Wait for any pending processing to complete first.
6798
+ // This ensures we register our listener before calling processQueue(),
6799
+ // avoiding a race where processQueue() sees no reader and returns early.
6800
+ if (this.processingPromise) {
6801
+ await this.processingPromise;
6802
+ }
6803
+ // Re-check after await - stream may have closed while we were waiting
6794
6804
  if (this.closed) {
6795
6805
  return null;
6796
6806
  }
@@ -6830,7 +6840,7 @@ class DataStream extends BaseObserver {
6830
6840
  }
6831
6841
  const promise = (this.processingPromise = this._processQueue());
6832
6842
  promise.finally(() => {
6833
- return (this.processingPromise = null);
6843
+ this.processingPromise = null;
6834
6844
  });
6835
6845
  return promise;
6836
6846
  }
@@ -6862,7 +6872,6 @@ class DataStream extends BaseObserver {
6862
6872
  this.notifyDataAdded = null;
6863
6873
  }
6864
6874
  if (this.dataQueue.length > 0) {
6865
- // Next tick
6866
6875
  setTimeout(() => this.processQueue());
6867
6876
  }
6868
6877
  }
@@ -7482,7 +7491,11 @@ class AbstractRemote {
7482
7491
  };
7483
7492
  const stream = new DataStream({
7484
7493
  logger: this.logger,
7485
- mapLine: mapLine
7494
+ mapLine: mapLine,
7495
+ pressure: {
7496
+ highWaterMark: 20,
7497
+ lowWaterMark: 10
7498
+ }
7486
7499
  });
7487
7500
  abortSignal?.addEventListener('abort', () => {
7488
7501
  closeReader();
@@ -7490,42 +7503,47 @@ class AbstractRemote {
7490
7503
  });
7491
7504
  const decoder = this.createTextDecoder();
7492
7505
  let buffer = '';
7493
- const l = stream.registerListener({
7494
- lowWater: async () => {
7495
- if (stream.closed || abortSignal?.aborted || readerReleased) {
7506
+ const consumeStream = async () => {
7507
+ while (!stream.closed && !abortSignal?.aborted && !readerReleased) {
7508
+ const { done, value } = await reader.read();
7509
+ if (done) {
7510
+ const remaining = buffer.trim();
7511
+ if (remaining.length != 0) {
7512
+ stream.enqueueData(remaining);
7513
+ }
7514
+ stream.close();
7515
+ await closeReader();
7496
7516
  return;
7497
7517
  }
7498
- try {
7499
- let didCompleteLine = false;
7500
- while (!didCompleteLine) {
7501
- const { done, value } = await reader.read();
7502
- if (done) {
7503
- const remaining = buffer.trim();
7504
- if (remaining.length != 0) {
7505
- stream.enqueueData(remaining);
7506
- }
7507
- stream.close();
7508
- await closeReader();
7509
- return;
7510
- }
7511
- const data = decoder.decode(value, { stream: true });
7512
- buffer += data;
7513
- const lines = buffer.split('\n');
7514
- for (var i = 0; i < lines.length - 1; i++) {
7515
- var l = lines[i].trim();
7516
- if (l.length > 0) {
7517
- stream.enqueueData(l);
7518
- didCompleteLine = true;
7519
- }
7520
- }
7521
- buffer = lines[lines.length - 1];
7518
+ const data = decoder.decode(value, { stream: true });
7519
+ buffer += data;
7520
+ const lines = buffer.split('\n');
7521
+ for (var i = 0; i < lines.length - 1; i++) {
7522
+ var l = lines[i].trim();
7523
+ if (l.length > 0) {
7524
+ stream.enqueueData(l);
7522
7525
  }
7523
7526
  }
7524
- catch (ex) {
7525
- stream.close();
7526
- throw ex;
7527
+ buffer = lines[lines.length - 1];
7528
+ // Implement backpressure by waiting for the low water mark to be reached
7529
+ if (stream.dataQueue.length > stream.highWatermark) {
7530
+ await new Promise((resolve) => {
7531
+ const dispose = stream.registerListener({
7532
+ lowWater: async () => {
7533
+ resolve();
7534
+ dispose();
7535
+ },
7536
+ closed: () => {
7537
+ resolve();
7538
+ dispose();
7539
+ }
7540
+ });
7541
+ });
7527
7542
  }
7528
- },
7543
+ }
7544
+ };
7545
+ consumeStream().catch(ex => this.logger.error('Error consuming stream', ex));
7546
+ const l = stream.registerListener({
7529
7547
  closed: () => {
7530
7548
  closeReader();
7531
7549
  l?.();
@@ -7604,18 +7622,17 @@ var SyncClientImplementation;
7604
7622
  *
7605
7623
  * This is the default option.
7606
7624
  *
7607
- * @deprecated Don't use {@link SyncClientImplementation.JAVASCRIPT} directly. Instead, use
7608
- * {@link DEFAULT_SYNC_CLIENT_IMPLEMENTATION} or omit the option. The explicit choice to use
7609
- * the JavaScript-based sync implementation will be removed from a future version of the SDK.
7625
+ * @deprecated We recommend the {@link RUST} client implementation for all apps. If you have issues with
7626
+ * the Rust client, please file an issue or reach out to us. The JavaScript client will be removed in a future
7627
+ * version of the PowerSync SDK.
7610
7628
  */
7611
7629
  SyncClientImplementation["JAVASCRIPT"] = "js";
7612
7630
  /**
7613
7631
  * This implementation offloads the sync line decoding and handling into the PowerSync
7614
7632
  * core extension.
7615
7633
  *
7616
- * @experimental
7617
- * While this implementation is more performant than {@link SyncClientImplementation.JAVASCRIPT},
7618
- * it has seen less real-world testing and is marked as __experimental__ at the moment.
7634
+ * This option is more performant than the {@link JAVASCRIPT} client, enabled by default and the
7635
+ * recommended client implementation for all apps.
7619
7636
  *
7620
7637
  * ## Compatibility warning
7621
7638
  *
@@ -7633,13 +7650,9 @@ var SyncClientImplementation;
7633
7650
  SyncClientImplementation["RUST"] = "rust";
7634
7651
  })(SyncClientImplementation || (SyncClientImplementation = {}));
7635
7652
  /**
7636
- * The default {@link SyncClientImplementation} to use.
7637
- *
7638
- * Please use this field instead of {@link SyncClientImplementation.JAVASCRIPT} directly. A future version
7639
- * of the PowerSync SDK will enable {@link SyncClientImplementation.RUST} by default and remove the JavaScript
7640
- * option.
7653
+ * The default {@link SyncClientImplementation} to use, {@link SyncClientImplementation.RUST}.
7641
7654
  */
7642
- const DEFAULT_SYNC_CLIENT_IMPLEMENTATION = SyncClientImplementation.JAVASCRIPT;
7655
+ const DEFAULT_SYNC_CLIENT_IMPLEMENTATION = SyncClientImplementation.RUST;
7643
7656
  const DEFAULT_CRUD_UPLOAD_THROTTLE_MS = 1000;
7644
7657
  const DEFAULT_RETRY_DELAY_MS = 5000;
7645
7658
  const DEFAULT_STREAMING_SYNC_OPTIONS = {
@@ -8049,6 +8062,9 @@ The next upload iteration will be delayed.`);
8049
8062
  if (rawTables != null && rawTables.length) {
8050
8063
  this.logger.warn('Raw tables require the Rust-based sync client. The JS client will ignore them.');
8051
8064
  }
8065
+ if (this.activeStreams.length) {
8066
+ this.logger.error('Sync streams require `clientImplementation: SyncClientImplementation.RUST` when connecting.');
8067
+ }
8052
8068
  this.logger.debug('Streaming sync iteration started');
8053
8069
  this.options.adapter.startSession();
8054
8070
  let [req, bucketMap] = await this.collectLocalBucketState();
@@ -8564,6 +8580,27 @@ The next upload iteration will be delayed.`);
8564
8580
  }
8565
8581
  }
8566
8582
 
8583
+ const CLAIM_STORE = new Map();
8584
+ /**
8585
+ * @internal
8586
+ * @experimental
8587
+ */
8588
+ const MEMORY_TRIGGER_CLAIM_MANAGER = {
8589
+ async obtainClaim(identifier) {
8590
+ if (CLAIM_STORE.has(identifier)) {
8591
+ throw new Error(`A claim is already present for ${identifier}`);
8592
+ }
8593
+ const release = async () => {
8594
+ CLAIM_STORE.delete(identifier);
8595
+ };
8596
+ CLAIM_STORE.set(identifier, release);
8597
+ return release;
8598
+ },
8599
+ async checkClaim(identifier) {
8600
+ return CLAIM_STORE.has(identifier);
8601
+ }
8602
+ };
8603
+
8567
8604
  /**
8568
8605
  * SQLite operations to track changes for with {@link TriggerManager}
8569
8606
  * @experimental
@@ -8575,9 +8612,20 @@ var DiffTriggerOperation;
8575
8612
  DiffTriggerOperation["DELETE"] = "DELETE";
8576
8613
  })(DiffTriggerOperation || (DiffTriggerOperation = {}));
8577
8614
 
8615
+ const DEFAULT_TRIGGER_MANAGER_CONFIGURATION = {
8616
+ useStorageByDefault: false
8617
+ };
8618
+ const TRIGGER_CLEANUP_INTERVAL_MS = 120_000; // 2 minutes
8619
+ /**
8620
+ * @internal
8621
+ * @experimental
8622
+ */
8578
8623
  class TriggerManagerImpl {
8579
8624
  options;
8580
8625
  schema;
8626
+ defaultConfig;
8627
+ cleanupTimeout;
8628
+ isDisposed;
8581
8629
  constructor(options) {
8582
8630
  this.options = options;
8583
8631
  this.schema = options.schema;
@@ -8586,6 +8634,33 @@ class TriggerManagerImpl {
8586
8634
  this.schema = schema;
8587
8635
  }
8588
8636
  });
8637
+ this.isDisposed = false;
8638
+ /**
8639
+ * Configure a cleanup to run on an interval.
8640
+ * The interval is configured using setTimeout to take the async
8641
+ * execution time of the callback into account.
8642
+ */
8643
+ this.defaultConfig = DEFAULT_TRIGGER_MANAGER_CONFIGURATION;
8644
+ const cleanupCallback = async () => {
8645
+ this.cleanupTimeout = null;
8646
+ if (this.isDisposed) {
8647
+ return;
8648
+ }
8649
+ try {
8650
+ await this.cleanupResources();
8651
+ }
8652
+ catch (ex) {
8653
+ this.db.logger.error(`Caught error while attempting to cleanup triggers`, ex);
8654
+ }
8655
+ finally {
8656
+ // if not closed, set another timeout
8657
+ if (this.isDisposed) {
8658
+ return;
8659
+ }
8660
+ this.cleanupTimeout = setTimeout(cleanupCallback, TRIGGER_CLEANUP_INTERVAL_MS);
8661
+ }
8662
+ };
8663
+ this.cleanupTimeout = setTimeout(cleanupCallback, TRIGGER_CLEANUP_INTERVAL_MS);
8589
8664
  }
8590
8665
  get db() {
8591
8666
  return this.options.db;
@@ -8603,13 +8678,95 @@ class TriggerManagerImpl {
8603
8678
  await tx.execute(/* sql */ `DROP TRIGGER IF EXISTS ${triggerId}; `);
8604
8679
  }
8605
8680
  }
8681
+ dispose() {
8682
+ this.isDisposed = true;
8683
+ if (this.cleanupTimeout) {
8684
+ clearTimeout(this.cleanupTimeout);
8685
+ }
8686
+ }
8687
+ /**
8688
+ * Updates default config settings for platform specific use-cases.
8689
+ */
8690
+ updateDefaults(config) {
8691
+ this.defaultConfig = {
8692
+ ...this.defaultConfig,
8693
+ ...config
8694
+ };
8695
+ }
8696
+ generateTriggerName(operation, destinationTable, triggerId) {
8697
+ return `__ps_temp_trigger_${operation.toLowerCase()}__${destinationTable}__${triggerId}`;
8698
+ }
8699
+ /**
8700
+ * Cleanup any SQLite triggers or tables that are no longer in use.
8701
+ */
8702
+ async cleanupResources() {
8703
+ // we use the database here since cleanupResources is called during the PowerSyncDatabase initialization
8704
+ await this.db.database.writeLock(async (ctx) => {
8705
+ /**
8706
+ * Note: We only cleanup persisted triggers. These are tracked in the sqlite_master table.
8707
+ * temporary triggers will not be affected by this.
8708
+ * Query all triggers that match our naming pattern
8709
+ */
8710
+ const triggers = await ctx.getAll(/* sql */ `
8711
+ SELECT
8712
+ name
8713
+ FROM
8714
+ sqlite_master
8715
+ WHERE
8716
+ type = 'trigger'
8717
+ AND name LIKE '__ps_temp_trigger_%'
8718
+ `);
8719
+ /** Use regex to extract table names and IDs from trigger names
8720
+ * Trigger naming convention: __ps_temp_trigger_<operation>__<destination_table>__<id>
8721
+ */
8722
+ const triggerPattern = /^__ps_temp_trigger_(?:insert|update|delete)__(.+)__([a-f0-9_]{36})$/i;
8723
+ const trackedItems = new Map();
8724
+ for (const trigger of triggers) {
8725
+ const match = trigger.name.match(triggerPattern);
8726
+ if (match) {
8727
+ const [, table, id] = match;
8728
+ // Collect all trigger names for each id combo
8729
+ const existing = trackedItems.get(id);
8730
+ if (existing) {
8731
+ existing.triggerNames.push(trigger.name);
8732
+ }
8733
+ else {
8734
+ trackedItems.set(id, { table, id, triggerNames: [trigger.name] });
8735
+ }
8736
+ }
8737
+ }
8738
+ for (const trackedItem of trackedItems.values()) {
8739
+ // check if there is anything holding on to this item
8740
+ const hasClaim = await this.options.claimManager.checkClaim(trackedItem.id);
8741
+ if (hasClaim) {
8742
+ // This does not require cleanup
8743
+ continue;
8744
+ }
8745
+ this.db.logger.debug(`Clearing resources for trigger ${trackedItem.id} with table ${trackedItem.table}`);
8746
+ // We need to delete the triggers and table
8747
+ for (const triggerName of trackedItem.triggerNames) {
8748
+ await ctx.execute(`DROP TRIGGER IF EXISTS ${triggerName}`);
8749
+ }
8750
+ await ctx.execute(`DROP TABLE IF EXISTS ${trackedItem.table}`);
8751
+ }
8752
+ });
8753
+ }
8606
8754
  async createDiffTrigger(options) {
8607
8755
  await this.db.waitForReady();
8608
- const { source, destination, columns, when, hooks } = options;
8756
+ const { source, destination, columns, when, hooks,
8757
+ // Fall back to the provided default if not given on this level
8758
+ useStorage = this.defaultConfig.useStorageByDefault } = options;
8609
8759
  const operations = Object.keys(when);
8610
8760
  if (operations.length == 0) {
8611
8761
  throw new Error('At least one WHEN operation must be specified for the trigger.');
8612
8762
  }
8763
+ /**
8764
+ * The clause to use when executing
8765
+ * CREATE ${tableTriggerTypeClause} TABLE
8766
+ * OR
8767
+ * CREATE ${tableTriggerTypeClause} TRIGGER
8768
+ */
8769
+ const tableTriggerTypeClause = !useStorage ? 'TEMP' : '';
8613
8770
  const whenClauses = Object.fromEntries(Object.entries(when).map(([operation, filter]) => [operation, `WHEN ${filter}`]));
8614
8771
  /**
8615
8772
  * Allow specifying the View name as the source.
@@ -8623,6 +8780,7 @@ class TriggerManagerImpl {
8623
8780
  const internalSource = sourceDefinition.internalName;
8624
8781
  const triggerIds = [];
8625
8782
  const id = await this.getUUID();
8783
+ const releaseStorageClaim = useStorage ? await this.options.claimManager.obtainClaim(id) : null;
8626
8784
  /**
8627
8785
  * We default to replicating all columns if no columns array is provided.
8628
8786
  */
@@ -8655,26 +8813,27 @@ class TriggerManagerImpl {
8655
8813
  return this.db.writeLock(async (tx) => {
8656
8814
  await this.removeTriggers(tx, triggerIds);
8657
8815
  await tx.execute(/* sql */ `DROP TABLE IF EXISTS ${destination};`);
8816
+ await releaseStorageClaim?.();
8658
8817
  });
8659
8818
  };
8660
8819
  const setup = async (tx) => {
8661
8820
  // Allow user code to execute in this lock context before the trigger is created.
8662
8821
  await hooks?.beforeCreate?.(tx);
8663
8822
  await tx.execute(/* sql */ `
8664
- CREATE TEMP TABLE ${destination} (
8823
+ CREATE ${tableTriggerTypeClause} TABLE ${destination} (
8665
8824
  operation_id INTEGER PRIMARY KEY AUTOINCREMENT,
8666
8825
  id TEXT,
8667
8826
  operation TEXT,
8668
8827
  timestamp TEXT,
8669
8828
  value TEXT,
8670
8829
  previous_value TEXT
8671
- );
8830
+ )
8672
8831
  `);
8673
8832
  if (operations.includes(DiffTriggerOperation.INSERT)) {
8674
- const insertTriggerId = `ps_temp_trigger_insert_${id}`;
8833
+ const insertTriggerId = this.generateTriggerName(DiffTriggerOperation.INSERT, destination, id);
8675
8834
  triggerIds.push(insertTriggerId);
8676
8835
  await tx.execute(/* sql */ `
8677
- CREATE TEMP TRIGGER ${insertTriggerId} AFTER INSERT ON ${internalSource} ${whenClauses[DiffTriggerOperation.INSERT]} BEGIN
8836
+ CREATE ${tableTriggerTypeClause} TRIGGER ${insertTriggerId} AFTER INSERT ON ${internalSource} ${whenClauses[DiffTriggerOperation.INSERT]} BEGIN
8678
8837
  INSERT INTO
8679
8838
  ${destination} (id, operation, timestamp, value)
8680
8839
  VALUES
@@ -8685,14 +8844,14 @@ class TriggerManagerImpl {
8685
8844
  ${jsonFragment('NEW')}
8686
8845
  );
8687
8846
 
8688
- END;
8847
+ END
8689
8848
  `);
8690
8849
  }
8691
8850
  if (operations.includes(DiffTriggerOperation.UPDATE)) {
8692
- const updateTriggerId = `ps_temp_trigger_update_${id}`;
8851
+ const updateTriggerId = this.generateTriggerName(DiffTriggerOperation.UPDATE, destination, id);
8693
8852
  triggerIds.push(updateTriggerId);
8694
8853
  await tx.execute(/* sql */ `
8695
- CREATE TEMP TRIGGER ${updateTriggerId} AFTER
8854
+ CREATE ${tableTriggerTypeClause} TRIGGER ${updateTriggerId} AFTER
8696
8855
  UPDATE ON ${internalSource} ${whenClauses[DiffTriggerOperation.UPDATE]} BEGIN
8697
8856
  INSERT INTO
8698
8857
  ${destination} (id, operation, timestamp, value, previous_value)
@@ -8709,11 +8868,11 @@ class TriggerManagerImpl {
8709
8868
  `);
8710
8869
  }
8711
8870
  if (operations.includes(DiffTriggerOperation.DELETE)) {
8712
- const deleteTriggerId = `ps_temp_trigger_delete_${id}`;
8871
+ const deleteTriggerId = this.generateTriggerName(DiffTriggerOperation.DELETE, destination, id);
8713
8872
  triggerIds.push(deleteTriggerId);
8714
8873
  // Create delete trigger for basic JSON
8715
8874
  await tx.execute(/* sql */ `
8716
- CREATE TEMP TRIGGER ${deleteTriggerId} AFTER DELETE ON ${internalSource} ${whenClauses[DiffTriggerOperation.DELETE]} BEGIN
8875
+ CREATE ${tableTriggerTypeClause} TRIGGER ${deleteTriggerId} AFTER DELETE ON ${internalSource} ${whenClauses[DiffTriggerOperation.DELETE]} BEGIN
8717
8876
  INSERT INTO
8718
8877
  ${destination} (id, operation, timestamp, value)
8719
8878
  VALUES
@@ -8757,7 +8916,7 @@ class TriggerManagerImpl {
8757
8916
  // If no array is provided, we use all columns from the source table.
8758
8917
  const contextColumns = columns ?? sourceDefinition.columns.map((col) => col.name);
8759
8918
  const id = await this.getUUID();
8760
- const destination = `ps_temp_track_${source}_${id}`;
8919
+ const destination = `__ps_temp_track_${source}_${id}`;
8761
8920
  // register an onChange before the trigger is created
8762
8921
  const abortController = new AbortController();
8763
8922
  const abortOnChange = () => abortController.abort();
@@ -8912,6 +9071,7 @@ class AbstractPowerSyncDatabase extends BaseObserver {
8912
9071
  * Allows creating SQLite triggers which can be used to track various operations on SQLite tables.
8913
9072
  */
8914
9073
  triggers;
9074
+ triggersImpl;
8915
9075
  logger;
8916
9076
  constructor(options) {
8917
9077
  super();
@@ -8975,9 +9135,10 @@ class AbstractPowerSyncDatabase extends BaseObserver {
8975
9135
  logger: this.logger
8976
9136
  });
8977
9137
  this._isReadyPromise = this.initialize();
8978
- this.triggers = new TriggerManagerImpl({
9138
+ this.triggers = this.triggersImpl = new TriggerManagerImpl({
8979
9139
  db: this,
8980
- schema: this.schema
9140
+ schema: this.schema,
9141
+ ...this.generateTriggerManagerConfig()
8981
9142
  });
8982
9143
  }
8983
9144
  /**
@@ -9003,6 +9164,15 @@ class AbstractPowerSyncDatabase extends BaseObserver {
9003
9164
  get connecting() {
9004
9165
  return this.currentStatus?.connecting || false;
9005
9166
  }
9167
+ /**
9168
+ * Generates a base configuration for {@link TriggerManagerImpl}.
9169
+ * Implementations should override this if necessary.
9170
+ */
9171
+ generateTriggerManagerConfig() {
9172
+ return {
9173
+ claimManager: MEMORY_TRIGGER_CLAIM_MANAGER
9174
+ };
9175
+ }
9006
9176
  /**
9007
9177
  * @returns A promise which will resolve once initialization is completed.
9008
9178
  */
@@ -9063,14 +9233,15 @@ class AbstractPowerSyncDatabase extends BaseObserver {
9063
9233
  async initialize() {
9064
9234
  await this._initialize();
9065
9235
  await this.bucketStorageAdapter.init();
9066
- await this._loadVersion();
9236
+ await this.loadVersion();
9067
9237
  await this.updateSchema(this.options.schema);
9068
9238
  await this.resolveOfflineSyncStatus();
9069
9239
  await this.database.execute('PRAGMA RECURSIVE_TRIGGERS=TRUE');
9240
+ await this.triggersImpl.cleanupResources();
9070
9241
  this.ready = true;
9071
9242
  this.iterateListeners((cb) => cb.initialized?.());
9072
9243
  }
9073
- async _loadVersion() {
9244
+ async loadVersion() {
9074
9245
  try {
9075
9246
  const { version } = await this.database.get('SELECT powersync_rs_version() as version');
9076
9247
  this.sdkVersion = version;
@@ -9186,7 +9357,6 @@ class AbstractPowerSyncDatabase extends BaseObserver {
9186
9357
  await this.disconnect();
9187
9358
  await this.waitForReady();
9188
9359
  const { clearLocal } = options;
9189
- // TODO DB name, verify this is necessary with extension
9190
9360
  await this.database.writeTransaction(async (tx) => {
9191
9361
  await tx.execute('SELECT powersync_clear(?)', [clearLocal ? 1 : 0]);
9192
9362
  });
@@ -9218,6 +9388,7 @@ class AbstractPowerSyncDatabase extends BaseObserver {
9218
9388
  if (this.closed) {
9219
9389
  return;
9220
9390
  }
9391
+ this.triggersImpl.dispose();
9221
9392
  await this.iterateAsyncListeners(async (cb) => cb.closing?.());
9222
9393
  const { disconnect } = options;
9223
9394
  if (disconnect) {
@@ -10784,5 +10955,5 @@ const parseQuery = (query, parameters) => {
10784
10955
  return { sqlStatement, parameters: parameters };
10785
10956
  };
10786
10957
 
10787
- export { AbortOperation, AbstractPowerSyncDatabase, AbstractPowerSyncDatabaseOpenFactory, AbstractQueryProcessor, AbstractRemote, AbstractStreamingSyncImplementation, ArrayComparator, BaseObserver, Column, ColumnType, ConnectionClosedError, ConnectionManager, ControlledExecutor, CrudBatch, CrudEntry, CrudTransaction, DEFAULT_CRUD_BATCH_LIMIT, DEFAULT_CRUD_UPLOAD_THROTTLE_MS, DEFAULT_INDEX_COLUMN_OPTIONS, DEFAULT_INDEX_OPTIONS, DEFAULT_LOCK_TIMEOUT_MS, DEFAULT_POWERSYNC_CLOSE_OPTIONS, DEFAULT_POWERSYNC_DB_OPTIONS, DEFAULT_PRESSURE_LIMITS, DEFAULT_REMOTE_LOGGER, DEFAULT_REMOTE_OPTIONS, DEFAULT_RETRY_DELAY_MS, DEFAULT_ROW_COMPARATOR, DEFAULT_STREAMING_SYNC_OPTIONS, DEFAULT_STREAM_CONNECTION_OPTIONS, DEFAULT_SYNC_CLIENT_IMPLEMENTATION, DEFAULT_TABLE_OPTIONS, DEFAULT_WATCH_QUERY_OPTIONS, DEFAULT_WATCH_THROTTLE_MS, DataStream, DiffTriggerOperation, DifferentialQueryProcessor, EMPTY_DIFFERENTIAL, FalsyComparator, FetchImplementationProvider, FetchStrategy, GetAllQuery, Index, IndexedColumn, InvalidSQLCharacters, LockType, LogLevel, MAX_AMOUNT_OF_COLUMNS, MAX_OP_ID, OnChangeQueryProcessor, OpType, OpTypeEnum, OplogEntry, PSInternalTable, PowerSyncControlCommand, RowUpdateType, Schema, SqliteBucketStorage, SyncClientImplementation, SyncDataBatch, SyncDataBucket, SyncProgress, SyncStatus, SyncStreamConnectionMethod, Table, TableV2, UpdateType, UploadQueueStats, WatchedQueryListenerEvent, column, compilableQueryWatch, createBaseLogger, createLogger, extractTableUpdates, isBatchedUpdateNotification, isContinueCheckpointRequest, isDBAdapter, isPowerSyncDatabaseOptionsWithSettings, isSQLOpenFactory, isSQLOpenOptions, isStreamingKeepalive, isStreamingSyncCheckpoint, isStreamingSyncCheckpointComplete, isStreamingSyncCheckpointDiff, isStreamingSyncCheckpointPartiallyComplete, isStreamingSyncData, isSyncNewCheckpointRequest, parseQuery, runOnSchemaChange, sanitizeSQL, sanitizeUUID };
10958
+ export { AbortOperation, AbstractPowerSyncDatabase, AbstractPowerSyncDatabaseOpenFactory, AbstractQueryProcessor, AbstractRemote, AbstractStreamingSyncImplementation, ArrayComparator, BaseObserver, Column, ColumnType, ConnectionClosedError, ConnectionManager, ControlledExecutor, CrudBatch, CrudEntry, CrudTransaction, DEFAULT_CRUD_BATCH_LIMIT, DEFAULT_CRUD_UPLOAD_THROTTLE_MS, DEFAULT_INDEX_COLUMN_OPTIONS, DEFAULT_INDEX_OPTIONS, DEFAULT_LOCK_TIMEOUT_MS, DEFAULT_POWERSYNC_CLOSE_OPTIONS, DEFAULT_POWERSYNC_DB_OPTIONS, DEFAULT_PRESSURE_LIMITS, DEFAULT_REMOTE_LOGGER, DEFAULT_REMOTE_OPTIONS, DEFAULT_RETRY_DELAY_MS, DEFAULT_ROW_COMPARATOR, DEFAULT_STREAMING_SYNC_OPTIONS, DEFAULT_STREAM_CONNECTION_OPTIONS, DEFAULT_SYNC_CLIENT_IMPLEMENTATION, DEFAULT_TABLE_OPTIONS, DEFAULT_WATCH_QUERY_OPTIONS, DEFAULT_WATCH_THROTTLE_MS, DataStream, DiffTriggerOperation, DifferentialQueryProcessor, EMPTY_DIFFERENTIAL, FalsyComparator, FetchImplementationProvider, FetchStrategy, GetAllQuery, Index, IndexedColumn, InvalidSQLCharacters, LockType, LogLevel, MAX_AMOUNT_OF_COLUMNS, MAX_OP_ID, MEMORY_TRIGGER_CLAIM_MANAGER, OnChangeQueryProcessor, OpType, OpTypeEnum, OplogEntry, PSInternalTable, PowerSyncControlCommand, RawTable, RowUpdateType, Schema, SqliteBucketStorage, SyncClientImplementation, SyncDataBatch, SyncDataBucket, SyncProgress, SyncStatus, SyncStreamConnectionMethod, Table, TableV2, TriggerManagerImpl, UpdateType, UploadQueueStats, WatchedQueryListenerEvent, column, compilableQueryWatch, createBaseLogger, createLogger, extractTableUpdates, isBatchedUpdateNotification, isContinueCheckpointRequest, isDBAdapter, isPowerSyncDatabaseOptionsWithSettings, isSQLOpenFactory, isSQLOpenOptions, isStreamingKeepalive, isStreamingSyncCheckpoint, isStreamingSyncCheckpointComplete, isStreamingSyncCheckpointDiff, isStreamingSyncCheckpointPartiallyComplete, isStreamingSyncData, isSyncNewCheckpointRequest, parseQuery, runOnSchemaChange, sanitizeSQL, sanitizeUUID };
10788
10959
  //# sourceMappingURL=bundle.node.mjs.map