@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.
- package/dist/bundle.cjs +412 -2067
- package/dist/bundle.cjs.map +1 -1
- package/dist/bundle.mjs +410 -2068
- package/dist/bundle.mjs.map +1 -1
- package/dist/bundle.node.cjs +237 -63
- package/dist/bundle.node.cjs.map +1 -1
- package/dist/bundle.node.mjs +235 -64
- package/dist/bundle.node.mjs.map +1 -1
- package/dist/index.d.cts +94 -15
- package/lib/client/AbstractPowerSyncDatabase.d.ts +9 -2
- package/lib/client/AbstractPowerSyncDatabase.js +18 -5
- package/lib/client/AbstractPowerSyncDatabase.js.map +1 -1
- package/lib/client/sync/stream/AbstractRemote.js +41 -32
- package/lib/client/sync/stream/AbstractRemote.js.map +1 -1
- package/lib/client/sync/stream/AbstractStreamingSyncImplementation.d.ts +7 -12
- package/lib/client/sync/stream/AbstractStreamingSyncImplementation.js +10 -12
- package/lib/client/sync/stream/AbstractStreamingSyncImplementation.js.map +1 -1
- package/lib/client/triggers/MemoryTriggerClaimManager.d.ts +6 -0
- package/lib/client/triggers/MemoryTriggerClaimManager.js +21 -0
- package/lib/client/triggers/MemoryTriggerClaimManager.js.map +1 -0
- package/lib/client/triggers/TriggerManager.d.ts +37 -0
- package/lib/client/triggers/TriggerManagerImpl.d.ts +24 -3
- package/lib/client/triggers/TriggerManagerImpl.js +133 -11
- package/lib/client/triggers/TriggerManagerImpl.js.map +1 -1
- package/lib/index.d.ts +3 -0
- package/lib/index.js +3 -0
- package/lib/index.js.map +1 -1
- package/lib/utils/DataStream.js +11 -2
- package/lib/utils/DataStream.js.map +1 -1
- package/package.json +4 -3
- package/src/client/AbstractPowerSyncDatabase.ts +21 -6
- package/src/client/sync/stream/AbstractRemote.ts +47 -35
- package/src/client/sync/stream/AbstractStreamingSyncImplementation.ts +11 -14
- package/src/client/triggers/MemoryTriggerClaimManager.ts +25 -0
- package/src/client/triggers/TriggerManager.ts +50 -6
- package/src/client/triggers/TriggerManagerImpl.ts +177 -13
- package/src/index.ts +3 -0
- package/src/utils/DataStream.ts +13 -2
package/dist/bundle.node.mjs
CHANGED
|
@@ -6720,7 +6720,7 @@ function requireDist () {
|
|
|
6720
6720
|
|
|
6721
6721
|
var distExports = requireDist();
|
|
6722
6722
|
|
|
6723
|
-
var version = "1.
|
|
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
|
-
|
|
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
|
|
7494
|
-
|
|
7495
|
-
|
|
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
|
-
|
|
7499
|
-
|
|
7500
|
-
|
|
7501
|
-
|
|
7502
|
-
|
|
7503
|
-
|
|
7504
|
-
|
|
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
|
-
|
|
7525
|
-
|
|
7526
|
-
|
|
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
|
|
7608
|
-
*
|
|
7609
|
-
*
|
|
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
|
-
* @
|
|
7617
|
-
*
|
|
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.
|
|
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
|
|
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
|
|
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 =
|
|
8833
|
+
const insertTriggerId = this.generateTriggerName(DiffTriggerOperation.INSERT, destination, id);
|
|
8675
8834
|
triggerIds.push(insertTriggerId);
|
|
8676
8835
|
await tx.execute(/* sql */ `
|
|
8677
|
-
CREATE
|
|
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 =
|
|
8851
|
+
const updateTriggerId = this.generateTriggerName(DiffTriggerOperation.UPDATE, destination, id);
|
|
8693
8852
|
triggerIds.push(updateTriggerId);
|
|
8694
8853
|
await tx.execute(/* sql */ `
|
|
8695
|
-
CREATE
|
|
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 =
|
|
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
|
|
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 = `
|
|
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.
|
|
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
|
|
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
|