@powersync/web 1.38.1 → 1.38.3
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/worker/SharedSyncImplementation.umd.js +560 -353
- package/dist/worker/SharedSyncImplementation.umd.js.map +1 -1
- package/dist/worker/WASQLiteDB.umd.js +560 -353
- package/dist/worker/WASQLiteDB.umd.js.map +1 -1
- package/lib/package.json +2 -2
- package/lib/src/attachments/IndexDBFileSystemAdapter.js +1 -0
- package/lib/src/attachments/IndexDBFileSystemAdapter.js.map +1 -0
- package/lib/src/db/NavigatorTriggerClaimManager.js +1 -0
- package/lib/src/db/NavigatorTriggerClaimManager.js.map +1 -0
- package/lib/src/db/PowerSyncDatabase.js +1 -0
- package/lib/src/db/PowerSyncDatabase.js.map +1 -0
- package/lib/src/db/adapters/AbstractWebPowerSyncDatabaseOpenFactory.js +1 -0
- package/lib/src/db/adapters/AbstractWebPowerSyncDatabaseOpenFactory.js.map +1 -0
- package/lib/src/db/adapters/AsyncWebAdapter.js +1 -0
- package/lib/src/db/adapters/AsyncWebAdapter.js.map +1 -0
- package/lib/src/db/adapters/SSRDBAdapter.js +1 -0
- package/lib/src/db/adapters/SSRDBAdapter.js.map +1 -0
- package/lib/src/db/adapters/WebDBAdapter.js +1 -0
- package/lib/src/db/adapters/WebDBAdapter.js.map +1 -0
- package/lib/src/db/adapters/wa-sqlite/ConcurrentConnection.js +1 -0
- package/lib/src/db/adapters/wa-sqlite/ConcurrentConnection.js.map +1 -0
- package/lib/src/db/adapters/wa-sqlite/DatabaseClient.js +1 -0
- package/lib/src/db/adapters/wa-sqlite/DatabaseClient.js.map +1 -0
- package/lib/src/db/adapters/wa-sqlite/DatabaseServer.js +1 -0
- package/lib/src/db/adapters/wa-sqlite/DatabaseServer.js.map +1 -0
- package/lib/src/db/adapters/wa-sqlite/RawSqliteConnection.js +1 -0
- package/lib/src/db/adapters/wa-sqlite/RawSqliteConnection.js.map +1 -0
- package/lib/src/db/adapters/wa-sqlite/WASQLiteOpenFactory.js +1 -0
- package/lib/src/db/adapters/wa-sqlite/WASQLiteOpenFactory.js.map +1 -0
- package/lib/src/db/adapters/wa-sqlite/WASQLitePowerSyncDatabaseOpenFactory.js +1 -0
- package/lib/src/db/adapters/wa-sqlite/WASQLitePowerSyncDatabaseOpenFactory.js.map +1 -0
- package/lib/src/db/adapters/wa-sqlite/vfs.js +1 -0
- package/lib/src/db/adapters/wa-sqlite/vfs.js.map +1 -0
- package/lib/src/db/adapters/web-sql-flags.js +1 -0
- package/lib/src/db/adapters/web-sql-flags.js.map +1 -0
- package/lib/src/db/sync/SSRWebStreamingSyncImplementation.js +1 -0
- package/lib/src/db/sync/SSRWebStreamingSyncImplementation.js.map +1 -0
- package/lib/src/db/sync/SharedWebStreamingSyncImplementation.js +1 -0
- package/lib/src/db/sync/SharedWebStreamingSyncImplementation.js.map +1 -0
- package/lib/src/db/sync/WebRemote.js +1 -0
- package/lib/src/db/sync/WebRemote.js.map +1 -0
- package/lib/src/db/sync/WebStreamingSyncImplementation.js +1 -0
- package/lib/src/db/sync/WebStreamingSyncImplementation.js.map +1 -0
- package/lib/src/db/sync/userAgent.js +1 -0
- package/lib/src/db/sync/userAgent.js.map +1 -0
- package/lib/src/index.js +1 -0
- package/lib/src/index.js.map +1 -0
- package/lib/src/shared/navigator.js +1 -0
- package/lib/src/shared/navigator.js.map +1 -0
- package/lib/src/shared/tab_close_signal.js +1 -0
- package/lib/src/shared/tab_close_signal.js.map +1 -0
- package/lib/src/worker/db/MultiDatabaseServer.js +1 -0
- package/lib/src/worker/db/MultiDatabaseServer.js.map +1 -0
- package/lib/src/worker/db/WASQLiteDB.worker.js +1 -0
- package/lib/src/worker/db/WASQLiteDB.worker.js.map +1 -0
- package/lib/src/worker/db/open-worker-database.js +1 -0
- package/lib/src/worker/db/open-worker-database.js.map +1 -0
- package/lib/src/worker/sync/AbstractSharedSyncClientProvider.js +1 -0
- package/lib/src/worker/sync/AbstractSharedSyncClientProvider.js.map +1 -0
- package/lib/src/worker/sync/BroadcastLogger.js +1 -0
- package/lib/src/worker/sync/BroadcastLogger.js.map +1 -0
- package/lib/src/worker/sync/SharedSyncImplementation.js +1 -0
- package/lib/src/worker/sync/SharedSyncImplementation.js.map +1 -0
- package/lib/src/worker/sync/SharedSyncImplementation.worker.js +1 -0
- package/lib/src/worker/sync/SharedSyncImplementation.worker.js.map +1 -0
- package/lib/src/worker/sync/WorkerClient.js +1 -0
- package/lib/src/worker/sync/WorkerClient.js.map +1 -0
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
|
@@ -1733,7 +1733,10 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
1733
1733
|
/* harmony export */ sanitizeUUID: () => (/* binding */ sanitizeUUID),
|
|
1734
1734
|
/* harmony export */ timeoutSignal: () => (/* binding */ timeoutSignal)
|
|
1735
1735
|
/* harmony export */ });
|
|
1736
|
-
|
|
1736
|
+
/**
|
|
1737
|
+
* @see https://www.sqlite.org/lang_expr.html#castexpr
|
|
1738
|
+
* @public
|
|
1739
|
+
*/
|
|
1737
1740
|
var ColumnType;
|
|
1738
1741
|
(function (ColumnType) {
|
|
1739
1742
|
ColumnType["TEXT"] = "TEXT";
|
|
@@ -1749,14 +1752,24 @@ const integer = {
|
|
|
1749
1752
|
const real = {
|
|
1750
1753
|
type: ColumnType.REAL
|
|
1751
1754
|
};
|
|
1752
|
-
|
|
1753
|
-
|
|
1755
|
+
/**
|
|
1756
|
+
* powersync-sqlite-core limits the number of column per table to 1999, due to internal SQLite limits.
|
|
1757
|
+
* In earlier versions this was limited to 63.
|
|
1758
|
+
*
|
|
1759
|
+
* @internal
|
|
1760
|
+
*/
|
|
1754
1761
|
const MAX_AMOUNT_OF_COLUMNS = 1999;
|
|
1762
|
+
/**
|
|
1763
|
+
* @public
|
|
1764
|
+
*/
|
|
1755
1765
|
const column = {
|
|
1756
1766
|
text,
|
|
1757
1767
|
integer,
|
|
1758
1768
|
real
|
|
1759
1769
|
};
|
|
1770
|
+
/**
|
|
1771
|
+
* @public
|
|
1772
|
+
*/
|
|
1760
1773
|
class Column {
|
|
1761
1774
|
options;
|
|
1762
1775
|
constructor(options) {
|
|
@@ -1776,9 +1789,15 @@ class Column {
|
|
|
1776
1789
|
}
|
|
1777
1790
|
}
|
|
1778
1791
|
|
|
1792
|
+
/**
|
|
1793
|
+
* @internal
|
|
1794
|
+
*/
|
|
1779
1795
|
const DEFAULT_INDEX_COLUMN_OPTIONS = {
|
|
1780
1796
|
ascending: true
|
|
1781
1797
|
};
|
|
1798
|
+
/**
|
|
1799
|
+
* @public
|
|
1800
|
+
*/
|
|
1782
1801
|
class IndexedColumn {
|
|
1783
1802
|
options;
|
|
1784
1803
|
static createAscending(column) {
|
|
@@ -1805,9 +1824,15 @@ class IndexedColumn {
|
|
|
1805
1824
|
}
|
|
1806
1825
|
}
|
|
1807
1826
|
|
|
1827
|
+
/**
|
|
1828
|
+
* @internal
|
|
1829
|
+
*/
|
|
1808
1830
|
const DEFAULT_INDEX_OPTIONS = {
|
|
1809
1831
|
columns: []
|
|
1810
1832
|
};
|
|
1833
|
+
/**
|
|
1834
|
+
* @public
|
|
1835
|
+
*/
|
|
1811
1836
|
class Index {
|
|
1812
1837
|
options;
|
|
1813
1838
|
static createAscending(options, columnNames) {
|
|
@@ -1849,6 +1874,9 @@ function encodeTableOptions(options) {
|
|
|
1849
1874
|
};
|
|
1850
1875
|
}
|
|
1851
1876
|
|
|
1877
|
+
/**
|
|
1878
|
+
* @internal
|
|
1879
|
+
*/
|
|
1852
1880
|
const DEFAULT_TABLE_OPTIONS = {
|
|
1853
1881
|
indexes: [],
|
|
1854
1882
|
insertOnly: false,
|
|
@@ -1857,7 +1885,13 @@ const DEFAULT_TABLE_OPTIONS = {
|
|
|
1857
1885
|
trackMetadata: false,
|
|
1858
1886
|
ignoreEmptyUpdates: false
|
|
1859
1887
|
};
|
|
1888
|
+
/**
|
|
1889
|
+
* @internal
|
|
1890
|
+
*/
|
|
1860
1891
|
const InvalidSQLCharacters = /["'%,.#\s[\]]/;
|
|
1892
|
+
/**
|
|
1893
|
+
* @public
|
|
1894
|
+
*/
|
|
1861
1895
|
class Table {
|
|
1862
1896
|
options;
|
|
1863
1897
|
_mappedColumns;
|
|
@@ -2048,6 +2082,11 @@ class Table {
|
|
|
2048
2082
|
}
|
|
2049
2083
|
}
|
|
2050
2084
|
|
|
2085
|
+
/**
|
|
2086
|
+
* The default name of the local table storing attachment data.
|
|
2087
|
+
*
|
|
2088
|
+
* @alpha
|
|
2089
|
+
*/
|
|
2051
2090
|
const ATTACHMENT_TABLE = 'attachments';
|
|
2052
2091
|
/**
|
|
2053
2092
|
* Maps a database row to an AttachmentRecord.
|
|
@@ -2055,7 +2094,7 @@ const ATTACHMENT_TABLE = 'attachments';
|
|
|
2055
2094
|
* @param row - The database row object
|
|
2056
2095
|
* @returns The corresponding AttachmentRecord
|
|
2057
2096
|
*
|
|
2058
|
-
* @
|
|
2097
|
+
* @alpha
|
|
2059
2098
|
*/
|
|
2060
2099
|
function attachmentFromSql(row) {
|
|
2061
2100
|
return {
|
|
@@ -2073,7 +2112,7 @@ function attachmentFromSql(row) {
|
|
|
2073
2112
|
/**
|
|
2074
2113
|
* AttachmentState represents the current synchronization state of an attachment.
|
|
2075
2114
|
*
|
|
2076
|
-
* @
|
|
2115
|
+
* @alpha
|
|
2077
2116
|
*/
|
|
2078
2117
|
var AttachmentState;
|
|
2079
2118
|
(function (AttachmentState) {
|
|
@@ -2086,7 +2125,7 @@ var AttachmentState;
|
|
|
2086
2125
|
/**
|
|
2087
2126
|
* AttachmentTable defines the schema for the attachment queue table.
|
|
2088
2127
|
*
|
|
2089
|
-
* @
|
|
2128
|
+
* @alpha
|
|
2090
2129
|
*/
|
|
2091
2130
|
class AttachmentTable extends Table {
|
|
2092
2131
|
constructor(options) {
|
|
@@ -2114,7 +2153,8 @@ class AttachmentTable extends Table {
|
|
|
2114
2153
|
* Provides methods to query, insert, update, and delete attachment records with
|
|
2115
2154
|
* proper transaction management through PowerSync.
|
|
2116
2155
|
*
|
|
2117
|
-
* @
|
|
2156
|
+
* @experimental
|
|
2157
|
+
* @alpha
|
|
2118
2158
|
*/
|
|
2119
2159
|
class AttachmentContext {
|
|
2120
2160
|
/** PowerSync database instance for executing queries */
|
|
@@ -2336,6 +2376,9 @@ class AttachmentContext {
|
|
|
2336
2376
|
}
|
|
2337
2377
|
}
|
|
2338
2378
|
|
|
2379
|
+
/**
|
|
2380
|
+
* @public
|
|
2381
|
+
*/
|
|
2339
2382
|
var WatchedQueryListenerEvent;
|
|
2340
2383
|
(function (WatchedQueryListenerEvent) {
|
|
2341
2384
|
WatchedQueryListenerEvent["ON_DATA"] = "onData";
|
|
@@ -2344,176 +2387,18 @@ var WatchedQueryListenerEvent;
|
|
|
2344
2387
|
WatchedQueryListenerEvent["SETTINGS_WILL_UPDATE"] = "settingsWillUpdate";
|
|
2345
2388
|
WatchedQueryListenerEvent["CLOSED"] = "closed";
|
|
2346
2389
|
})(WatchedQueryListenerEvent || (WatchedQueryListenerEvent = {}));
|
|
2390
|
+
/**
|
|
2391
|
+
* @internal
|
|
2392
|
+
*/
|
|
2347
2393
|
const DEFAULT_WATCH_THROTTLE_MS = 30;
|
|
2394
|
+
/**
|
|
2395
|
+
* @internal
|
|
2396
|
+
*/
|
|
2348
2397
|
const DEFAULT_WATCH_QUERY_OPTIONS = {
|
|
2349
2398
|
throttleMs: DEFAULT_WATCH_THROTTLE_MS,
|
|
2350
2399
|
reportFetching: true
|
|
2351
2400
|
};
|
|
2352
2401
|
|
|
2353
|
-
/**
|
|
2354
|
-
* Orchestrates attachment synchronization between local and remote storage.
|
|
2355
|
-
* Handles uploads, downloads, deletions, and state transitions.
|
|
2356
|
-
*
|
|
2357
|
-
* @internal
|
|
2358
|
-
*/
|
|
2359
|
-
class SyncingService {
|
|
2360
|
-
attachmentService;
|
|
2361
|
-
localStorage;
|
|
2362
|
-
remoteStorage;
|
|
2363
|
-
logger;
|
|
2364
|
-
errorHandler;
|
|
2365
|
-
constructor(attachmentService, localStorage, remoteStorage, logger, errorHandler) {
|
|
2366
|
-
this.attachmentService = attachmentService;
|
|
2367
|
-
this.localStorage = localStorage;
|
|
2368
|
-
this.remoteStorage = remoteStorage;
|
|
2369
|
-
this.logger = logger;
|
|
2370
|
-
this.errorHandler = errorHandler;
|
|
2371
|
-
}
|
|
2372
|
-
/**
|
|
2373
|
-
* Processes attachments based on their state (upload, download, or delete).
|
|
2374
|
-
* All updates are saved in a single batch after processing.
|
|
2375
|
-
*
|
|
2376
|
-
* @param attachments - Array of attachment records to process
|
|
2377
|
-
* @param context - Attachment context for database operations
|
|
2378
|
-
* @returns Promise that resolves when all attachments have been processed and saved
|
|
2379
|
-
*/
|
|
2380
|
-
async processAttachments(attachments, context) {
|
|
2381
|
-
const updatedAttachments = [];
|
|
2382
|
-
for (const attachment of attachments) {
|
|
2383
|
-
switch (attachment.state) {
|
|
2384
|
-
case AttachmentState.QUEUED_UPLOAD:
|
|
2385
|
-
const uploaded = await this.uploadAttachment(attachment);
|
|
2386
|
-
updatedAttachments.push(uploaded);
|
|
2387
|
-
break;
|
|
2388
|
-
case AttachmentState.QUEUED_DOWNLOAD:
|
|
2389
|
-
const downloaded = await this.downloadAttachment(attachment);
|
|
2390
|
-
updatedAttachments.push(downloaded);
|
|
2391
|
-
break;
|
|
2392
|
-
case AttachmentState.QUEUED_DELETE:
|
|
2393
|
-
const deleted = await this.deleteAttachment(attachment, context);
|
|
2394
|
-
updatedAttachments.push(deleted);
|
|
2395
|
-
break;
|
|
2396
|
-
}
|
|
2397
|
-
}
|
|
2398
|
-
await context.saveAttachments(updatedAttachments);
|
|
2399
|
-
}
|
|
2400
|
-
/**
|
|
2401
|
-
* Uploads an attachment from local storage to remote storage.
|
|
2402
|
-
* On success, marks as SYNCED. On failure, defers to error handler or archives.
|
|
2403
|
-
*
|
|
2404
|
-
* @param attachment - The attachment record to upload
|
|
2405
|
-
* @returns Updated attachment record with new state
|
|
2406
|
-
* @throws Error if the attachment has no localUri
|
|
2407
|
-
*/
|
|
2408
|
-
async uploadAttachment(attachment) {
|
|
2409
|
-
this.logger.info(`Uploading attachment ${attachment.filename}`);
|
|
2410
|
-
try {
|
|
2411
|
-
if (attachment.localUri == null) {
|
|
2412
|
-
throw new Error(`No localUri for attachment ${attachment.id}`);
|
|
2413
|
-
}
|
|
2414
|
-
const fileBlob = await this.localStorage.readFile(attachment.localUri);
|
|
2415
|
-
await this.remoteStorage.uploadFile(fileBlob, attachment);
|
|
2416
|
-
return {
|
|
2417
|
-
...attachment,
|
|
2418
|
-
state: AttachmentState.SYNCED,
|
|
2419
|
-
hasSynced: true
|
|
2420
|
-
};
|
|
2421
|
-
}
|
|
2422
|
-
catch (error) {
|
|
2423
|
-
const shouldRetry = (await this.errorHandler?.onUploadError(attachment, error)) ?? true;
|
|
2424
|
-
if (!shouldRetry) {
|
|
2425
|
-
return {
|
|
2426
|
-
...attachment,
|
|
2427
|
-
state: AttachmentState.ARCHIVED
|
|
2428
|
-
};
|
|
2429
|
-
}
|
|
2430
|
-
return attachment;
|
|
2431
|
-
}
|
|
2432
|
-
}
|
|
2433
|
-
/**
|
|
2434
|
-
* Downloads an attachment from remote storage to local storage.
|
|
2435
|
-
* Retrieves the file, converts to base64, and saves locally.
|
|
2436
|
-
* On success, marks as SYNCED. On failure, defers to error handler or archives.
|
|
2437
|
-
*
|
|
2438
|
-
* @param attachment - The attachment record to download
|
|
2439
|
-
* @returns Updated attachment record with local URI and new state
|
|
2440
|
-
*/
|
|
2441
|
-
async downloadAttachment(attachment) {
|
|
2442
|
-
this.logger.info(`Downloading attachment ${attachment.filename}`);
|
|
2443
|
-
try {
|
|
2444
|
-
const fileData = await this.remoteStorage.downloadFile(attachment);
|
|
2445
|
-
const localUri = this.localStorage.getLocalUri(attachment.filename);
|
|
2446
|
-
await this.localStorage.saveFile(localUri, fileData);
|
|
2447
|
-
return {
|
|
2448
|
-
...attachment,
|
|
2449
|
-
state: AttachmentState.SYNCED,
|
|
2450
|
-
localUri: localUri,
|
|
2451
|
-
hasSynced: true
|
|
2452
|
-
};
|
|
2453
|
-
}
|
|
2454
|
-
catch (error) {
|
|
2455
|
-
const shouldRetry = (await this.errorHandler?.onDownloadError(attachment, error)) ?? true;
|
|
2456
|
-
if (!shouldRetry) {
|
|
2457
|
-
return {
|
|
2458
|
-
...attachment,
|
|
2459
|
-
state: AttachmentState.ARCHIVED
|
|
2460
|
-
};
|
|
2461
|
-
}
|
|
2462
|
-
return attachment;
|
|
2463
|
-
}
|
|
2464
|
-
}
|
|
2465
|
-
/**
|
|
2466
|
-
* Deletes an attachment from both remote and local storage.
|
|
2467
|
-
* Removes the remote file, local file (if exists), and the attachment record.
|
|
2468
|
-
* On failure, defers to error handler or archives.
|
|
2469
|
-
*
|
|
2470
|
-
* @param attachment - The attachment record to delete
|
|
2471
|
-
* @param context - Attachment context for database operations
|
|
2472
|
-
* @returns Updated attachment record
|
|
2473
|
-
*/
|
|
2474
|
-
async deleteAttachment(attachment, context) {
|
|
2475
|
-
try {
|
|
2476
|
-
await this.remoteStorage.deleteFile(attachment);
|
|
2477
|
-
if (attachment.localUri) {
|
|
2478
|
-
await this.localStorage.deleteFile(attachment.localUri);
|
|
2479
|
-
}
|
|
2480
|
-
await context.deleteAttachment(attachment.id);
|
|
2481
|
-
return {
|
|
2482
|
-
...attachment,
|
|
2483
|
-
state: AttachmentState.ARCHIVED
|
|
2484
|
-
};
|
|
2485
|
-
}
|
|
2486
|
-
catch (error) {
|
|
2487
|
-
const shouldRetry = (await this.errorHandler?.onDeleteError(attachment, error)) ?? true;
|
|
2488
|
-
if (!shouldRetry) {
|
|
2489
|
-
return {
|
|
2490
|
-
...attachment,
|
|
2491
|
-
state: AttachmentState.ARCHIVED
|
|
2492
|
-
};
|
|
2493
|
-
}
|
|
2494
|
-
return attachment;
|
|
2495
|
-
}
|
|
2496
|
-
}
|
|
2497
|
-
/**
|
|
2498
|
-
* Performs cleanup of archived attachments by removing their local files and records.
|
|
2499
|
-
* Errors during local file deletion are logged but do not prevent record deletion.
|
|
2500
|
-
*/
|
|
2501
|
-
async deleteArchivedAttachments(context) {
|
|
2502
|
-
return await context.deleteArchivedAttachments(async (archivedAttachments) => {
|
|
2503
|
-
for (const attachment of archivedAttachments) {
|
|
2504
|
-
if (attachment.localUri) {
|
|
2505
|
-
try {
|
|
2506
|
-
await this.localStorage.deleteFile(attachment.localUri);
|
|
2507
|
-
}
|
|
2508
|
-
catch (error) {
|
|
2509
|
-
this.logger.error('Error deleting local file for archived attachment', error);
|
|
2510
|
-
}
|
|
2511
|
-
}
|
|
2512
|
-
}
|
|
2513
|
-
});
|
|
2514
|
-
}
|
|
2515
|
-
}
|
|
2516
|
-
|
|
2517
2402
|
/**
|
|
2518
2403
|
* A simple fixed-capacity queue implementation.
|
|
2519
2404
|
*
|
|
@@ -2699,6 +2584,9 @@ class Mutex {
|
|
|
2699
2584
|
}
|
|
2700
2585
|
}
|
|
2701
2586
|
}
|
|
2587
|
+
/**
|
|
2588
|
+
* @internal
|
|
2589
|
+
*/
|
|
2702
2590
|
function timeoutSignal(timeout) {
|
|
2703
2591
|
if (timeout == null)
|
|
2704
2592
|
return;
|
|
@@ -2727,36 +2615,200 @@ class AttachmentService {
|
|
|
2727
2615
|
this.context = new AttachmentContext(db, tableName, logger, archivedCacheLimit);
|
|
2728
2616
|
}
|
|
2729
2617
|
/**
|
|
2730
|
-
* Creates a differential watch query for active attachments requiring synchronization.
|
|
2731
|
-
* @returns Watch query that emits changes for queued uploads, downloads, and deletes
|
|
2618
|
+
* Creates a differential watch query for active attachments requiring synchronization.
|
|
2619
|
+
* @returns Watch query that emits changes for queued uploads, downloads, and deletes
|
|
2620
|
+
*/
|
|
2621
|
+
watchActiveAttachments({ throttleMs } = {}) {
|
|
2622
|
+
this.logger.info('Watching active attachments...');
|
|
2623
|
+
const watch = this.db
|
|
2624
|
+
.query({
|
|
2625
|
+
sql: /* sql */ `
|
|
2626
|
+
SELECT
|
|
2627
|
+
*
|
|
2628
|
+
FROM
|
|
2629
|
+
${this.tableName}
|
|
2630
|
+
WHERE
|
|
2631
|
+
state = ?
|
|
2632
|
+
OR state = ?
|
|
2633
|
+
OR state = ?
|
|
2634
|
+
ORDER BY
|
|
2635
|
+
timestamp ASC
|
|
2636
|
+
`,
|
|
2637
|
+
parameters: [AttachmentState.QUEUED_UPLOAD, AttachmentState.QUEUED_DOWNLOAD, AttachmentState.QUEUED_DELETE]
|
|
2638
|
+
})
|
|
2639
|
+
.differentialWatch({ throttleMs });
|
|
2640
|
+
return watch;
|
|
2641
|
+
}
|
|
2642
|
+
/**
|
|
2643
|
+
* Executes a callback with exclusive access to the attachment context.
|
|
2644
|
+
*/
|
|
2645
|
+
async withContext(callback) {
|
|
2646
|
+
return this.mutex.runExclusive(async () => {
|
|
2647
|
+
return callback(this.context);
|
|
2648
|
+
});
|
|
2649
|
+
}
|
|
2650
|
+
}
|
|
2651
|
+
|
|
2652
|
+
/**
|
|
2653
|
+
* Orchestrates attachment synchronization between local and remote storage.
|
|
2654
|
+
* Handles uploads, downloads, deletions, and state transitions.
|
|
2655
|
+
*
|
|
2656
|
+
* @internal
|
|
2657
|
+
*/
|
|
2658
|
+
class SyncingService {
|
|
2659
|
+
attachmentService;
|
|
2660
|
+
localStorage;
|
|
2661
|
+
remoteStorage;
|
|
2662
|
+
logger;
|
|
2663
|
+
errorHandler;
|
|
2664
|
+
constructor(attachmentService, localStorage, remoteStorage, logger, errorHandler) {
|
|
2665
|
+
this.attachmentService = attachmentService;
|
|
2666
|
+
this.localStorage = localStorage;
|
|
2667
|
+
this.remoteStorage = remoteStorage;
|
|
2668
|
+
this.logger = logger;
|
|
2669
|
+
this.errorHandler = errorHandler;
|
|
2670
|
+
}
|
|
2671
|
+
/**
|
|
2672
|
+
* Processes attachments based on their state (upload, download, or delete).
|
|
2673
|
+
* All updates are saved in a single batch after processing.
|
|
2674
|
+
*
|
|
2675
|
+
* @param attachments - Array of attachment records to process
|
|
2676
|
+
* @param context - Attachment context for database operations
|
|
2677
|
+
* @returns Promise that resolves when all attachments have been processed and saved
|
|
2678
|
+
*/
|
|
2679
|
+
async processAttachments(attachments, context) {
|
|
2680
|
+
const updatedAttachments = [];
|
|
2681
|
+
for (const attachment of attachments) {
|
|
2682
|
+
switch (attachment.state) {
|
|
2683
|
+
case AttachmentState.QUEUED_UPLOAD:
|
|
2684
|
+
const uploaded = await this.uploadAttachment(attachment);
|
|
2685
|
+
updatedAttachments.push(uploaded);
|
|
2686
|
+
break;
|
|
2687
|
+
case AttachmentState.QUEUED_DOWNLOAD:
|
|
2688
|
+
const downloaded = await this.downloadAttachment(attachment);
|
|
2689
|
+
updatedAttachments.push(downloaded);
|
|
2690
|
+
break;
|
|
2691
|
+
case AttachmentState.QUEUED_DELETE:
|
|
2692
|
+
const deleted = await this.deleteAttachment(attachment, context);
|
|
2693
|
+
updatedAttachments.push(deleted);
|
|
2694
|
+
break;
|
|
2695
|
+
}
|
|
2696
|
+
}
|
|
2697
|
+
await context.saveAttachments(updatedAttachments);
|
|
2698
|
+
}
|
|
2699
|
+
/**
|
|
2700
|
+
* Uploads an attachment from local storage to remote storage.
|
|
2701
|
+
* On success, marks as SYNCED. On failure, defers to error handler or archives.
|
|
2702
|
+
*
|
|
2703
|
+
* @param attachment - The attachment record to upload
|
|
2704
|
+
* @returns Updated attachment record with new state
|
|
2705
|
+
* @throws Error if the attachment has no localUri
|
|
2706
|
+
*/
|
|
2707
|
+
async uploadAttachment(attachment) {
|
|
2708
|
+
this.logger.info(`Uploading attachment ${attachment.filename}`);
|
|
2709
|
+
try {
|
|
2710
|
+
if (attachment.localUri == null) {
|
|
2711
|
+
throw new Error(`No localUri for attachment ${attachment.id}`);
|
|
2712
|
+
}
|
|
2713
|
+
const fileBlob = await this.localStorage.readFile(attachment.localUri);
|
|
2714
|
+
await this.remoteStorage.uploadFile(fileBlob, attachment);
|
|
2715
|
+
return {
|
|
2716
|
+
...attachment,
|
|
2717
|
+
state: AttachmentState.SYNCED,
|
|
2718
|
+
hasSynced: true
|
|
2719
|
+
};
|
|
2720
|
+
}
|
|
2721
|
+
catch (error) {
|
|
2722
|
+
const shouldRetry = (await this.errorHandler?.onUploadError(attachment, error)) ?? true;
|
|
2723
|
+
if (!shouldRetry) {
|
|
2724
|
+
return {
|
|
2725
|
+
...attachment,
|
|
2726
|
+
state: AttachmentState.ARCHIVED
|
|
2727
|
+
};
|
|
2728
|
+
}
|
|
2729
|
+
return attachment;
|
|
2730
|
+
}
|
|
2731
|
+
}
|
|
2732
|
+
/**
|
|
2733
|
+
* Downloads an attachment from remote storage to local storage.
|
|
2734
|
+
* Retrieves the file, converts to base64, and saves locally.
|
|
2735
|
+
* On success, marks as SYNCED. On failure, defers to error handler or archives.
|
|
2736
|
+
*
|
|
2737
|
+
* @param attachment - The attachment record to download
|
|
2738
|
+
* @returns Updated attachment record with local URI and new state
|
|
2739
|
+
*/
|
|
2740
|
+
async downloadAttachment(attachment) {
|
|
2741
|
+
this.logger.info(`Downloading attachment ${attachment.filename}`);
|
|
2742
|
+
try {
|
|
2743
|
+
const fileData = await this.remoteStorage.downloadFile(attachment);
|
|
2744
|
+
const localUri = this.localStorage.getLocalUri(attachment.filename);
|
|
2745
|
+
await this.localStorage.saveFile(localUri, fileData);
|
|
2746
|
+
return {
|
|
2747
|
+
...attachment,
|
|
2748
|
+
state: AttachmentState.SYNCED,
|
|
2749
|
+
localUri: localUri,
|
|
2750
|
+
hasSynced: true
|
|
2751
|
+
};
|
|
2752
|
+
}
|
|
2753
|
+
catch (error) {
|
|
2754
|
+
const shouldRetry = (await this.errorHandler?.onDownloadError(attachment, error)) ?? true;
|
|
2755
|
+
if (!shouldRetry) {
|
|
2756
|
+
return {
|
|
2757
|
+
...attachment,
|
|
2758
|
+
state: AttachmentState.ARCHIVED
|
|
2759
|
+
};
|
|
2760
|
+
}
|
|
2761
|
+
return attachment;
|
|
2762
|
+
}
|
|
2763
|
+
}
|
|
2764
|
+
/**
|
|
2765
|
+
* Deletes an attachment from both remote and local storage.
|
|
2766
|
+
* Removes the remote file, local file (if exists), and the attachment record.
|
|
2767
|
+
* On failure, defers to error handler or archives.
|
|
2768
|
+
*
|
|
2769
|
+
* @param attachment - The attachment record to delete
|
|
2770
|
+
* @param context - Attachment context for database operations
|
|
2771
|
+
* @returns Updated attachment record
|
|
2732
2772
|
*/
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
.
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2773
|
+
async deleteAttachment(attachment, context) {
|
|
2774
|
+
try {
|
|
2775
|
+
await this.remoteStorage.deleteFile(attachment);
|
|
2776
|
+
if (attachment.localUri) {
|
|
2777
|
+
await this.localStorage.deleteFile(attachment.localUri);
|
|
2778
|
+
}
|
|
2779
|
+
await context.deleteAttachment(attachment.id);
|
|
2780
|
+
return {
|
|
2781
|
+
...attachment,
|
|
2782
|
+
state: AttachmentState.ARCHIVED
|
|
2783
|
+
};
|
|
2784
|
+
}
|
|
2785
|
+
catch (error) {
|
|
2786
|
+
const shouldRetry = (await this.errorHandler?.onDeleteError(attachment, error)) ?? true;
|
|
2787
|
+
if (!shouldRetry) {
|
|
2788
|
+
return {
|
|
2789
|
+
...attachment,
|
|
2790
|
+
state: AttachmentState.ARCHIVED
|
|
2791
|
+
};
|
|
2792
|
+
}
|
|
2793
|
+
return attachment;
|
|
2794
|
+
}
|
|
2753
2795
|
}
|
|
2754
2796
|
/**
|
|
2755
|
-
*
|
|
2797
|
+
* Performs cleanup of archived attachments by removing their local files and records.
|
|
2798
|
+
* Errors during local file deletion are logged but do not prevent record deletion.
|
|
2756
2799
|
*/
|
|
2757
|
-
async
|
|
2758
|
-
return
|
|
2759
|
-
|
|
2800
|
+
async deleteArchivedAttachments(context) {
|
|
2801
|
+
return await context.deleteArchivedAttachments(async (archivedAttachments) => {
|
|
2802
|
+
for (const attachment of archivedAttachments) {
|
|
2803
|
+
if (attachment.localUri) {
|
|
2804
|
+
try {
|
|
2805
|
+
await this.localStorage.deleteFile(attachment.localUri);
|
|
2806
|
+
}
|
|
2807
|
+
catch (error) {
|
|
2808
|
+
this.logger.error('Error deleting local file for archived attachment', error);
|
|
2809
|
+
}
|
|
2810
|
+
}
|
|
2811
|
+
}
|
|
2760
2812
|
});
|
|
2761
2813
|
}
|
|
2762
2814
|
}
|
|
@@ -2817,16 +2869,6 @@ class AttachmentQueue {
|
|
|
2817
2869
|
* Creates a new AttachmentQueue instance.
|
|
2818
2870
|
*
|
|
2819
2871
|
* @param options - Configuration options
|
|
2820
|
-
* @param options.db - PowerSync database instance
|
|
2821
|
-
* @param options.remoteStorage - Remote storage adapter for upload/download operations
|
|
2822
|
-
* @param options.localStorage - Local storage adapter for file persistence
|
|
2823
|
-
* @param options.watchAttachments - Callback for monitoring attachment changes in your data model
|
|
2824
|
-
* @param options.tableName - Name of the table to store attachment records. Default: 'ps_attachment_queue'
|
|
2825
|
-
* @param options.logger - Logger instance. Defaults to db.logger
|
|
2826
|
-
* @param options.syncIntervalMs - Periodic polling interval in milliseconds for retrying failed uploads/downloads. Default: 30000
|
|
2827
|
-
* @param options.syncThrottleDuration - Throttle duration in milliseconds for the reactive watch query that detects attachment changes. Prevents rapid-fire syncs during bulk changes. Default: 30
|
|
2828
|
-
* @param options.downloadAttachments - Whether to automatically download remote attachments. Default: true
|
|
2829
|
-
* @param options.archivedCacheLimit - Maximum archived attachments before cleanup. Default: 100
|
|
2830
2872
|
*/
|
|
2831
2873
|
constructor({ db, localStorage, remoteStorage, watchAttachments, logger, tableName = ATTACHMENT_TABLE, syncIntervalMs = 30 * 1000, syncThrottleDuration = DEFAULT_WATCH_THROTTLE_MS, downloadAttachments = true, archivedCacheLimit = 100, errorHandler }) {
|
|
2832
2874
|
this.db = db;
|
|
@@ -2915,6 +2957,7 @@ class AttachmentQueue {
|
|
|
2915
2957
|
state: AttachmentState.QUEUED_DOWNLOAD,
|
|
2916
2958
|
hasSynced: false,
|
|
2917
2959
|
metaData: watchedAttachment.metaData,
|
|
2960
|
+
mediaType: watchedAttachment.mediaType,
|
|
2918
2961
|
timestamp: new Date().getTime()
|
|
2919
2962
|
});
|
|
2920
2963
|
continue;
|
|
@@ -3002,17 +3045,24 @@ class AttachmentQueue {
|
|
|
3002
3045
|
this.statusListenerDispose = undefined;
|
|
3003
3046
|
}
|
|
3004
3047
|
}
|
|
3048
|
+
/**
|
|
3049
|
+
* Provides an {@link AttachmentContext} to a callback.
|
|
3050
|
+
*
|
|
3051
|
+
* The callback runs while the attachment queue mutex is held. Do not call
|
|
3052
|
+
* other {@link AttachmentQueue} methods from within the callback, as they may
|
|
3053
|
+
* attempt to acquire the same mutex and block indefinitely.
|
|
3054
|
+
*/
|
|
3055
|
+
withAttachmentContext(callback) {
|
|
3056
|
+
/**
|
|
3057
|
+
* AttachmentService is internal and private in this class.
|
|
3058
|
+
* We only need to expose its locking and context functionality for extending classes.
|
|
3059
|
+
*/
|
|
3060
|
+
return this.attachmentService.withContext(callback);
|
|
3061
|
+
}
|
|
3005
3062
|
/**
|
|
3006
3063
|
* Saves a file to local storage and queues it for upload to remote storage.
|
|
3007
3064
|
*
|
|
3008
3065
|
* @param options - File save options
|
|
3009
|
-
* @param options.data - The file data as ArrayBuffer, Blob, or base64 string
|
|
3010
|
-
* @param options.fileExtension - File extension (e.g., 'jpg', 'pdf')
|
|
3011
|
-
* @param options.mediaType - MIME type of the file (e.g., 'image/jpeg')
|
|
3012
|
-
* @param options.metaData - Optional metadata to associate with the attachment
|
|
3013
|
-
* @param options.id - Optional custom ID. If not provided, a UUID will be generated
|
|
3014
|
-
* @param options.updateHook - Optional callback to execute additional database operations
|
|
3015
|
-
* within the same transaction as the attachment creation
|
|
3016
3066
|
* @returns Promise resolving to the created attachment record
|
|
3017
3067
|
*/
|
|
3018
3068
|
async saveFile({ data, fileExtension, mediaType, metaData, id, updateHook }) {
|
|
@@ -3125,6 +3175,9 @@ class AttachmentQueue {
|
|
|
3125
3175
|
}
|
|
3126
3176
|
}
|
|
3127
3177
|
|
|
3178
|
+
/**
|
|
3179
|
+
* @alpha
|
|
3180
|
+
*/
|
|
3128
3181
|
var EncodingType;
|
|
3129
3182
|
(function (EncodingType) {
|
|
3130
3183
|
EncodingType["UTF8"] = "utf8";
|
|
@@ -3588,7 +3641,9 @@ var Logger = /*@__PURE__*/getDefaultExportFromCjs(loggerExports);
|
|
|
3588
3641
|
* different SQLite DB implementations.
|
|
3589
3642
|
*/
|
|
3590
3643
|
/**
|
|
3591
|
-
* Implements {@link DBGetUtils} on a {@link
|
|
3644
|
+
* Implements {@link DBGetUtils} on a {@link SqlExecutor}.
|
|
3645
|
+
*
|
|
3646
|
+
* @internal
|
|
3592
3647
|
*/
|
|
3593
3648
|
function DBGetUtilsDefaultMixin(Base) {
|
|
3594
3649
|
return class extends Base {
|
|
@@ -3632,6 +3687,8 @@ function DBGetUtilsDefaultMixin(Base) {
|
|
|
3632
3687
|
}
|
|
3633
3688
|
/**
|
|
3634
3689
|
* Update table operation numbers from SQLite
|
|
3690
|
+
*
|
|
3691
|
+
* @public
|
|
3635
3692
|
*/
|
|
3636
3693
|
var RowUpdateType;
|
|
3637
3694
|
(function (RowUpdateType) {
|
|
@@ -3640,8 +3697,10 @@ var RowUpdateType;
|
|
|
3640
3697
|
RowUpdateType[RowUpdateType["SQLITE_UPDATE"] = 23] = "SQLITE_UPDATE";
|
|
3641
3698
|
})(RowUpdateType || (RowUpdateType = {}));
|
|
3642
3699
|
/**
|
|
3643
|
-
* A mixin to implement {@link DBAdapter} by delegating to {@link ConnectionPool
|
|
3644
|
-
* {@link ConnectionPool
|
|
3700
|
+
* A mixin to implement {@link DBAdapter} by delegating to {@link ConnectionPool#readLock} and
|
|
3701
|
+
* {@link ConnectionPool#writeLock}.
|
|
3702
|
+
*
|
|
3703
|
+
* @internal
|
|
3645
3704
|
*/
|
|
3646
3705
|
function DBAdapterDefaultMixin(Base) {
|
|
3647
3706
|
return class extends Base {
|
|
@@ -3729,9 +3788,15 @@ class TransactionImplementation extends DBGetUtilsDefaultMixin(BaseTransaction)
|
|
|
3729
3788
|
}
|
|
3730
3789
|
}
|
|
3731
3790
|
}
|
|
3791
|
+
/**
|
|
3792
|
+
* @internal
|
|
3793
|
+
*/
|
|
3732
3794
|
function isBatchedUpdateNotification(update) {
|
|
3733
3795
|
return 'tables' in update;
|
|
3734
3796
|
}
|
|
3797
|
+
/**
|
|
3798
|
+
* @internal
|
|
3799
|
+
*/
|
|
3735
3800
|
function extractTableUpdates(update) {
|
|
3736
3801
|
return isBatchedUpdateNotification(update) ? update.tables : [update.table];
|
|
3737
3802
|
}
|
|
@@ -3759,6 +3824,8 @@ const FULL_SYNC_PRIORITY = 2147483647;
|
|
|
3759
3824
|
*
|
|
3760
3825
|
* Also note that data is downloaded in bulk, which means that individual counters are unlikely
|
|
3761
3826
|
* to be updated one-by-one.
|
|
3827
|
+
*
|
|
3828
|
+
* @public
|
|
3762
3829
|
*/
|
|
3763
3830
|
class SyncProgress {
|
|
3764
3831
|
internal;
|
|
@@ -3797,6 +3864,9 @@ class SyncProgress {
|
|
|
3797
3864
|
}
|
|
3798
3865
|
}
|
|
3799
3866
|
|
|
3867
|
+
/**
|
|
3868
|
+
* @public
|
|
3869
|
+
*/
|
|
3800
3870
|
class SyncStatus {
|
|
3801
3871
|
options;
|
|
3802
3872
|
constructor(options) {
|
|
@@ -3807,6 +3877,8 @@ class SyncStatus {
|
|
|
3807
3877
|
* implementation).
|
|
3808
3878
|
*
|
|
3809
3879
|
* This information is only available after a connection has been requested.
|
|
3880
|
+
*
|
|
3881
|
+
* @deprecated This always returns the Rust client (the only option).
|
|
3810
3882
|
*/
|
|
3811
3883
|
get clientImplementation() {
|
|
3812
3884
|
return this.options.clientImplementation;
|
|
@@ -3814,7 +3886,7 @@ class SyncStatus {
|
|
|
3814
3886
|
/**
|
|
3815
3887
|
* Indicates if the client is currently connected to the PowerSync service.
|
|
3816
3888
|
*
|
|
3817
|
-
* @returns
|
|
3889
|
+
* @returns True if connected, false otherwise. Defaults to false if not specified.
|
|
3818
3890
|
*/
|
|
3819
3891
|
get connected() {
|
|
3820
3892
|
return this.options.connected ?? false;
|
|
@@ -3822,7 +3894,7 @@ class SyncStatus {
|
|
|
3822
3894
|
/**
|
|
3823
3895
|
* Indicates if the client is in the process of establishing a connection to the PowerSync service.
|
|
3824
3896
|
*
|
|
3825
|
-
* @returns
|
|
3897
|
+
* @returns True if connecting, false otherwise. Defaults to false if not specified.
|
|
3826
3898
|
*/
|
|
3827
3899
|
get connecting() {
|
|
3828
3900
|
return this.options.connecting ?? false;
|
|
@@ -3831,7 +3903,7 @@ class SyncStatus {
|
|
|
3831
3903
|
* Time that a last sync has fully completed, if any.
|
|
3832
3904
|
* This timestamp is reset to null after a restart of the PowerSync service.
|
|
3833
3905
|
*
|
|
3834
|
-
* @returns
|
|
3906
|
+
* @returns The timestamp of the last successful sync, or undefined if no sync has completed.
|
|
3835
3907
|
*/
|
|
3836
3908
|
get lastSyncedAt() {
|
|
3837
3909
|
return this.options.lastSyncedAt;
|
|
@@ -3839,7 +3911,7 @@ class SyncStatus {
|
|
|
3839
3911
|
/**
|
|
3840
3912
|
* Indicates whether there has been at least one full sync completed since initialization.
|
|
3841
3913
|
*
|
|
3842
|
-
* @returns
|
|
3914
|
+
* @returns True if at least one sync has completed, false if no sync has completed,
|
|
3843
3915
|
* or undefined when the state is still being loaded from the database.
|
|
3844
3916
|
*/
|
|
3845
3917
|
get hasSynced() {
|
|
@@ -3848,10 +3920,10 @@ class SyncStatus {
|
|
|
3848
3920
|
/**
|
|
3849
3921
|
* Provides the current data flow status regarding uploads and downloads.
|
|
3850
3922
|
*
|
|
3851
|
-
* @returns
|
|
3923
|
+
* @returns An object containing:
|
|
3852
3924
|
* - downloading: True if actively downloading changes (only when connected is also true)
|
|
3853
3925
|
* - uploading: True if actively uploading changes
|
|
3854
|
-
* Defaults to {downloading: false, uploading: false} if not specified.
|
|
3926
|
+
* Defaults to `{downloading: false, uploading: false}` if not specified.
|
|
3855
3927
|
*/
|
|
3856
3928
|
get dataFlowStatus() {
|
|
3857
3929
|
return (this.options.dataFlow ?? {
|
|
@@ -3876,7 +3948,7 @@ class SyncStatus {
|
|
|
3876
3948
|
return this.options.dataFlow?.internalStreamSubscriptions?.map((core) => new SyncStreamStatusView(this, core));
|
|
3877
3949
|
}
|
|
3878
3950
|
/**
|
|
3879
|
-
* If the `stream` appears in {@link syncStreams}, returns the current status for that stream.
|
|
3951
|
+
* If the `stream` appears in {@link SyncStatus.syncStreams}, returns the current status for that stream.
|
|
3880
3952
|
*/
|
|
3881
3953
|
forStream(stream) {
|
|
3882
3954
|
const asJson = JSON.stringify(stream.parameters);
|
|
@@ -3886,7 +3958,7 @@ class SyncStatus {
|
|
|
3886
3958
|
/**
|
|
3887
3959
|
* Provides sync status information for all bucket priorities, sorted by priority (highest first).
|
|
3888
3960
|
*
|
|
3889
|
-
* @returns
|
|
3961
|
+
* @returns An array of status entries for different sync priority levels,
|
|
3890
3962
|
* sorted with highest priorities (lower numbers) first.
|
|
3891
3963
|
*/
|
|
3892
3964
|
get priorityStatusEntries() {
|
|
@@ -3921,8 +3993,8 @@ class SyncStatus {
|
|
|
3921
3993
|
* For example, if PowerSync just finished synchronizing buckets in priority level 3, calling this method
|
|
3922
3994
|
* with a priority of 1 may return information for priority level 3.
|
|
3923
3995
|
*
|
|
3924
|
-
* @param
|
|
3925
|
-
* @returns
|
|
3996
|
+
* @param priority - The bucket priority for which the status should be reported
|
|
3997
|
+
* @returns Status information for the requested priority level or the next higher level with available status
|
|
3926
3998
|
*/
|
|
3927
3999
|
statusForPriority(priority) {
|
|
3928
4000
|
// priorityStatusEntries are sorted by ascending priorities (so higher numbers to lower numbers).
|
|
@@ -3943,8 +4015,8 @@ class SyncStatus {
|
|
|
3943
4015
|
* Compares this SyncStatus instance with another to determine if they are equal.
|
|
3944
4016
|
* Equality is determined by comparing the serialized JSON representation of both instances.
|
|
3945
4017
|
*
|
|
3946
|
-
* @param
|
|
3947
|
-
* @returns
|
|
4018
|
+
* @param status - The SyncStatus instance to compare against
|
|
4019
|
+
* @returns True if the instances are considered equal, false otherwise
|
|
3948
4020
|
*/
|
|
3949
4021
|
isEqual(status) {
|
|
3950
4022
|
/**
|
|
@@ -3967,7 +4039,7 @@ class SyncStatus {
|
|
|
3967
4039
|
* Creates a human-readable string representation of the current sync status.
|
|
3968
4040
|
* Includes information about connection state, sync completion, and data flow.
|
|
3969
4041
|
*
|
|
3970
|
-
* @returns
|
|
4042
|
+
* @returns A string representation of the sync status
|
|
3971
4043
|
*/
|
|
3972
4044
|
getMessage() {
|
|
3973
4045
|
const dataFlow = this.dataFlowStatus;
|
|
@@ -3976,7 +4048,7 @@ class SyncStatus {
|
|
|
3976
4048
|
/**
|
|
3977
4049
|
* Serializes the SyncStatus instance to a plain object.
|
|
3978
4050
|
*
|
|
3979
|
-
* @returns
|
|
4051
|
+
* @returns A plain object representation of the sync status
|
|
3980
4052
|
*/
|
|
3981
4053
|
toJSON() {
|
|
3982
4054
|
return {
|
|
@@ -4042,6 +4114,9 @@ class SyncStreamStatusView {
|
|
|
4042
4114
|
}
|
|
4043
4115
|
}
|
|
4044
4116
|
|
|
4117
|
+
/**
|
|
4118
|
+
* @public
|
|
4119
|
+
*/
|
|
4045
4120
|
class UploadQueueStats {
|
|
4046
4121
|
count;
|
|
4047
4122
|
size;
|
|
@@ -4067,6 +4142,9 @@ class UploadQueueStats {
|
|
|
4067
4142
|
}
|
|
4068
4143
|
}
|
|
4069
4144
|
|
|
4145
|
+
/**
|
|
4146
|
+
* @internal
|
|
4147
|
+
*/
|
|
4070
4148
|
class BaseObserver {
|
|
4071
4149
|
listeners = new Set();
|
|
4072
4150
|
constructor() { }
|
|
@@ -4094,6 +4172,9 @@ class BaseObserver {
|
|
|
4094
4172
|
}
|
|
4095
4173
|
}
|
|
4096
4174
|
|
|
4175
|
+
/**
|
|
4176
|
+
* @internal
|
|
4177
|
+
*/
|
|
4097
4178
|
class ControlledExecutor {
|
|
4098
4179
|
task;
|
|
4099
4180
|
/**
|
|
@@ -4163,30 +4244,44 @@ function throttleTrailing(func, wait) {
|
|
|
4163
4244
|
}
|
|
4164
4245
|
};
|
|
4165
4246
|
}
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
|
|
4177
|
-
|
|
4178
|
-
|
|
4179
|
-
|
|
4180
|
-
|
|
4181
|
-
|
|
4182
|
-
|
|
4183
|
-
|
|
4184
|
-
|
|
4185
|
-
|
|
4186
|
-
|
|
4187
|
-
|
|
4188
|
-
|
|
4189
|
-
|
|
4247
|
+
function asyncNotifier() {
|
|
4248
|
+
let waitingConsumer = null;
|
|
4249
|
+
let hasPendingNotification = false;
|
|
4250
|
+
return {
|
|
4251
|
+
notify() {
|
|
4252
|
+
if (waitingConsumer != null) {
|
|
4253
|
+
waitingConsumer();
|
|
4254
|
+
waitingConsumer = null;
|
|
4255
|
+
}
|
|
4256
|
+
else {
|
|
4257
|
+
hasPendingNotification = true;
|
|
4258
|
+
}
|
|
4259
|
+
},
|
|
4260
|
+
waitForNotification(signal) {
|
|
4261
|
+
return new Promise((resolve) => {
|
|
4262
|
+
if (waitingConsumer != null) {
|
|
4263
|
+
throw new Error('Illegal call to waitForNotification, already has a waiter.');
|
|
4264
|
+
}
|
|
4265
|
+
if (signal.aborted) {
|
|
4266
|
+
resolve();
|
|
4267
|
+
}
|
|
4268
|
+
else if (hasPendingNotification) {
|
|
4269
|
+
resolve();
|
|
4270
|
+
hasPendingNotification = false;
|
|
4271
|
+
}
|
|
4272
|
+
else {
|
|
4273
|
+
function complete() {
|
|
4274
|
+
signal.removeEventListener('abort', onAbort);
|
|
4275
|
+
resolve();
|
|
4276
|
+
}
|
|
4277
|
+
function onAbort() {
|
|
4278
|
+
waitingConsumer = null;
|
|
4279
|
+
resolve();
|
|
4280
|
+
}
|
|
4281
|
+
waitingConsumer = complete;
|
|
4282
|
+
signal.addEventListener('abort', onAbort);
|
|
4283
|
+
}
|
|
4284
|
+
});
|
|
4190
4285
|
}
|
|
4191
4286
|
};
|
|
4192
4287
|
}
|
|
@@ -4347,7 +4442,7 @@ class ConnectionManager extends BaseObserver {
|
|
|
4347
4442
|
/**
|
|
4348
4443
|
* Close the sync connection.
|
|
4349
4444
|
*
|
|
4350
|
-
* Use {@link connect} to connect again.
|
|
4445
|
+
* Use {@link ConnectionManager.connect} to connect again.
|
|
4351
4446
|
*/
|
|
4352
4447
|
async disconnect() {
|
|
4353
4448
|
// This will help abort pending connects
|
|
@@ -4487,6 +4582,8 @@ const _finalizer = 'FinalizationRegistry' in globalThis
|
|
|
4487
4582
|
/**
|
|
4488
4583
|
* An efficient comparator for {@link WatchedQuery} created with {@link Query#watch}. This has the ability to determine if a query
|
|
4489
4584
|
* result has changes without necessarily processing all items in the result.
|
|
4585
|
+
*
|
|
4586
|
+
* @public
|
|
4490
4587
|
*/
|
|
4491
4588
|
class ArrayComparator {
|
|
4492
4589
|
options;
|
|
@@ -4514,6 +4611,8 @@ class ArrayComparator {
|
|
|
4514
4611
|
}
|
|
4515
4612
|
/**
|
|
4516
4613
|
* Watched query comparator that always reports changed result sets.
|
|
4614
|
+
*
|
|
4615
|
+
* @public
|
|
4517
4616
|
*/
|
|
4518
4617
|
const FalsyComparator = {
|
|
4519
4618
|
checkEquality: () => false // Default comparator that always returns false
|
|
@@ -4721,6 +4820,8 @@ class AbstractQueryProcessor extends MetaBaseObserver {
|
|
|
4721
4820
|
/**
|
|
4722
4821
|
* An empty differential result set.
|
|
4723
4822
|
* This is used as the initial state for differential incrementally watched queries.
|
|
4823
|
+
*
|
|
4824
|
+
* @internal
|
|
4724
4825
|
*/
|
|
4725
4826
|
const EMPTY_DIFFERENTIAL = {
|
|
4726
4827
|
added: [],
|
|
@@ -4733,6 +4834,8 @@ const EMPTY_DIFFERENTIAL = {
|
|
|
4733
4834
|
* Default implementation of the {@link DifferentialWatchedQueryComparator} for watched queries.
|
|
4734
4835
|
* It keys items by their `id` property if available, alternatively it uses JSON stringification
|
|
4735
4836
|
* of the entire item for the key and comparison.
|
|
4837
|
+
*
|
|
4838
|
+
* @internal
|
|
4736
4839
|
*/
|
|
4737
4840
|
const DEFAULT_ROW_COMPARATOR = {
|
|
4738
4841
|
keyBy: (item) => {
|
|
@@ -5013,6 +5116,8 @@ class CustomQuery {
|
|
|
5013
5116
|
|
|
5014
5117
|
/**
|
|
5015
5118
|
* Tests if the input is a {@link SQLOpenOptions}
|
|
5119
|
+
*
|
|
5120
|
+
* @internal
|
|
5016
5121
|
*/
|
|
5017
5122
|
const isSQLOpenOptions = (test) => {
|
|
5018
5123
|
// typeof null is `object`, but you cannot use the `in` operator on `null.
|
|
@@ -5020,17 +5125,24 @@ const isSQLOpenOptions = (test) => {
|
|
|
5020
5125
|
};
|
|
5021
5126
|
/**
|
|
5022
5127
|
* Tests if input is a {@link SQLOpenFactory}
|
|
5128
|
+
*
|
|
5129
|
+
* @internal
|
|
5023
5130
|
*/
|
|
5024
5131
|
const isSQLOpenFactory = (test) => {
|
|
5025
5132
|
return typeof test?.openDB == 'function';
|
|
5026
5133
|
};
|
|
5027
5134
|
/**
|
|
5028
5135
|
* Tests if input is a {@link DBAdapter}
|
|
5136
|
+
*
|
|
5137
|
+
* @internal
|
|
5029
5138
|
*/
|
|
5030
5139
|
const isDBAdapter = (test) => {
|
|
5031
5140
|
return typeof test?.writeTransaction == 'function';
|
|
5032
5141
|
};
|
|
5033
5142
|
|
|
5143
|
+
/**
|
|
5144
|
+
* @internal
|
|
5145
|
+
*/
|
|
5034
5146
|
var PSInternalTable;
|
|
5035
5147
|
(function (PSInternalTable) {
|
|
5036
5148
|
PSInternalTable["DATA"] = "ps_data";
|
|
@@ -5039,6 +5151,9 @@ var PSInternalTable;
|
|
|
5039
5151
|
PSInternalTable["OPLOG"] = "ps_oplog";
|
|
5040
5152
|
PSInternalTable["UNTYPED"] = "ps_untyped";
|
|
5041
5153
|
})(PSInternalTable || (PSInternalTable = {}));
|
|
5154
|
+
/**
|
|
5155
|
+
* @internal
|
|
5156
|
+
*/
|
|
5042
5157
|
var PowerSyncControlCommand;
|
|
5043
5158
|
(function (PowerSyncControlCommand) {
|
|
5044
5159
|
PowerSyncControlCommand["PROCESS_TEXT_LINE"] = "line_text";
|
|
@@ -5056,6 +5171,8 @@ var PowerSyncControlCommand;
|
|
|
5056
5171
|
|
|
5057
5172
|
/**
|
|
5058
5173
|
* A batch of client-side changes.
|
|
5174
|
+
*
|
|
5175
|
+
* @public
|
|
5059
5176
|
*/
|
|
5060
5177
|
class CrudBatch {
|
|
5061
5178
|
crud;
|
|
@@ -5082,6 +5199,8 @@ class CrudBatch {
|
|
|
5082
5199
|
|
|
5083
5200
|
/**
|
|
5084
5201
|
* Type of local change.
|
|
5202
|
+
*
|
|
5203
|
+
* @public
|
|
5085
5204
|
*/
|
|
5086
5205
|
var UpdateType;
|
|
5087
5206
|
(function (UpdateType) {
|
|
@@ -5094,6 +5213,8 @@ var UpdateType;
|
|
|
5094
5213
|
})(UpdateType || (UpdateType = {}));
|
|
5095
5214
|
/**
|
|
5096
5215
|
* A single client-side change.
|
|
5216
|
+
*
|
|
5217
|
+
* @public
|
|
5097
5218
|
*/
|
|
5098
5219
|
class CrudEntry {
|
|
5099
5220
|
/**
|
|
@@ -5190,6 +5311,9 @@ class CrudEntry {
|
|
|
5190
5311
|
}
|
|
5191
5312
|
}
|
|
5192
5313
|
|
|
5314
|
+
/**
|
|
5315
|
+
* @public
|
|
5316
|
+
*/
|
|
5193
5317
|
class CrudTransaction extends CrudBatch {
|
|
5194
5318
|
crud;
|
|
5195
5319
|
complete;
|
|
@@ -5218,6 +5342,8 @@ class CrudTransaction extends CrudBatch {
|
|
|
5218
5342
|
* Calls to Abortcontroller.abort(reason: any) will result in the
|
|
5219
5343
|
* `reason` being thrown. This is not necessarily an error,
|
|
5220
5344
|
* but extends error for better logging purposes.
|
|
5345
|
+
*
|
|
5346
|
+
* @internal
|
|
5221
5347
|
*/
|
|
5222
5348
|
class AbortOperation extends Error {
|
|
5223
5349
|
reason;
|
|
@@ -12388,7 +12514,7 @@ function requireDist () {
|
|
|
12388
12514
|
|
|
12389
12515
|
var distExports = requireDist();
|
|
12390
12516
|
|
|
12391
|
-
var version = "1.
|
|
12517
|
+
var version = "1.54.0";
|
|
12392
12518
|
var PACKAGE = {
|
|
12393
12519
|
version: version};
|
|
12394
12520
|
|
|
@@ -12518,7 +12644,8 @@ class WebsocketClientTransport {
|
|
|
12518
12644
|
removeListeners();
|
|
12519
12645
|
resolve(new WebsocketDuplexConnectionExports.WebsocketDuplexConnection(websocket, new distExports.Deserializer(), multiplexerDemultiplexerFactory));
|
|
12520
12646
|
};
|
|
12521
|
-
const errorListener = (
|
|
12647
|
+
const errorListener = (event) => {
|
|
12648
|
+
const ev = event;
|
|
12522
12649
|
removeListeners();
|
|
12523
12650
|
// We add a default error in that case.
|
|
12524
12651
|
if (ev.error != null) {
|
|
@@ -12761,7 +12888,13 @@ const SOCKET_TIMEOUT_MS = 30_000;
|
|
|
12761
12888
|
// If there is a backlog of messages (for example on slow connections), keepalive messages could be delayed
|
|
12762
12889
|
// significantly. Therefore this is longer than the socket timeout.
|
|
12763
12890
|
const KEEP_ALIVE_LIFETIME_MS = 90_000;
|
|
12891
|
+
/**
|
|
12892
|
+
* @internal
|
|
12893
|
+
*/
|
|
12764
12894
|
const DEFAULT_REMOTE_LOGGER = Logger.get('PowerSyncRemote');
|
|
12895
|
+
/**
|
|
12896
|
+
* @public
|
|
12897
|
+
*/
|
|
12765
12898
|
var FetchStrategy;
|
|
12766
12899
|
(function (FetchStrategy) {
|
|
12767
12900
|
/**
|
|
@@ -12780,12 +12913,17 @@ var FetchStrategy;
|
|
|
12780
12913
|
* The class wrapper is used to distinguish the fetchImplementation
|
|
12781
12914
|
* option in [AbstractRemoteOptions] from the general fetch method
|
|
12782
12915
|
* which is typeof "function"
|
|
12916
|
+
*
|
|
12917
|
+
* @internal
|
|
12783
12918
|
*/
|
|
12784
12919
|
class FetchImplementationProvider {
|
|
12785
12920
|
getFetch() {
|
|
12786
12921
|
throw new Error('Unspecified fetch implementation');
|
|
12787
12922
|
}
|
|
12788
12923
|
}
|
|
12924
|
+
/**
|
|
12925
|
+
* @internal
|
|
12926
|
+
*/
|
|
12789
12927
|
const DEFAULT_REMOTE_OPTIONS = {
|
|
12790
12928
|
socketUrlTransformer: (url) => url.replace(/^https?:\/\//, function (match) {
|
|
12791
12929
|
return match === 'https://' ? 'wss://' : 'ws://';
|
|
@@ -12793,6 +12931,9 @@ const DEFAULT_REMOTE_OPTIONS = {
|
|
|
12793
12931
|
fetchImplementation: new FetchImplementationProvider(),
|
|
12794
12932
|
fetchOptions: {}
|
|
12795
12933
|
};
|
|
12934
|
+
/**
|
|
12935
|
+
* @internal
|
|
12936
|
+
*/
|
|
12796
12937
|
class AbstractRemote {
|
|
12797
12938
|
connector;
|
|
12798
12939
|
logger;
|
|
@@ -13202,7 +13343,7 @@ class AbstractRemote {
|
|
|
13202
13343
|
* Posts a `/sync/stream` request.
|
|
13203
13344
|
*
|
|
13204
13345
|
* Depending on the `Content-Type` of the response, this returns strings for sync lines or encoded BSON documents as
|
|
13205
|
-
*
|
|
13346
|
+
* `Uint8Array`s.
|
|
13206
13347
|
*/
|
|
13207
13348
|
async fetchStream(options) {
|
|
13208
13349
|
const { isBson, stream } = await this.fetchStreamRaw(options);
|
|
@@ -13244,16 +13385,26 @@ function isInterruptingInstruction(instruction) {
|
|
|
13244
13385
|
return 'EstablishSyncStream' in instruction || 'CloseSyncStream' in instruction;
|
|
13245
13386
|
}
|
|
13246
13387
|
|
|
13388
|
+
/**
|
|
13389
|
+
* @internal
|
|
13390
|
+
*/
|
|
13247
13391
|
var LockType;
|
|
13248
13392
|
(function (LockType) {
|
|
13249
13393
|
LockType["CRUD"] = "crud";
|
|
13250
13394
|
LockType["SYNC"] = "sync";
|
|
13251
13395
|
})(LockType || (LockType = {}));
|
|
13396
|
+
/**
|
|
13397
|
+
* @public
|
|
13398
|
+
*/
|
|
13252
13399
|
var SyncStreamConnectionMethod;
|
|
13253
13400
|
(function (SyncStreamConnectionMethod) {
|
|
13254
13401
|
SyncStreamConnectionMethod["HTTP"] = "http";
|
|
13255
13402
|
SyncStreamConnectionMethod["WEB_SOCKET"] = "web-socket";
|
|
13256
13403
|
})(SyncStreamConnectionMethod || (SyncStreamConnectionMethod = {}));
|
|
13404
|
+
/**
|
|
13405
|
+
* @deprecated Deprecated since {@link SyncClientImplementation.RUST} is the only option.
|
|
13406
|
+
* @public
|
|
13407
|
+
*/
|
|
13257
13408
|
var SyncClientImplementation;
|
|
13258
13409
|
(function (SyncClientImplementation) {
|
|
13259
13410
|
/**
|
|
@@ -13265,8 +13416,8 @@ var SyncClientImplementation;
|
|
|
13265
13416
|
* ## Compatibility warning
|
|
13266
13417
|
*
|
|
13267
13418
|
* The Rust sync client stores sync data in a format that is slightly different than the one used
|
|
13268
|
-
* by the old JavaScript client. When adopting the {@link RUST} client on existing databases,
|
|
13269
|
-
* migrate the format automatically.
|
|
13419
|
+
* by the old JavaScript client. When adopting the {@link SyncClientImplementation.RUST} client on existing databases,
|
|
13420
|
+
* the PowerSync SDK will migrate the format automatically.
|
|
13270
13421
|
*
|
|
13271
13422
|
* SDK versions supporting both the JavaScript and the Rust client support both formats with the JavaScript client
|
|
13272
13423
|
* implementaiton. However, downgrading to an SDK version that only supports the JavaScript client would not be
|
|
@@ -13276,14 +13427,29 @@ var SyncClientImplementation;
|
|
|
13276
13427
|
})(SyncClientImplementation || (SyncClientImplementation = {}));
|
|
13277
13428
|
/**
|
|
13278
13429
|
* The default {@link SyncClientImplementation} to use, {@link SyncClientImplementation.RUST}.
|
|
13430
|
+
*
|
|
13431
|
+
* @deprecated Deprecated since {@link SyncClientImplementation.RUST} is the only option.
|
|
13432
|
+
* @public
|
|
13279
13433
|
*/
|
|
13280
13434
|
const DEFAULT_SYNC_CLIENT_IMPLEMENTATION = SyncClientImplementation.RUST;
|
|
13435
|
+
/**
|
|
13436
|
+
* @internal
|
|
13437
|
+
*/
|
|
13281
13438
|
const DEFAULT_CRUD_UPLOAD_THROTTLE_MS = 1000;
|
|
13439
|
+
/**
|
|
13440
|
+
* @internal
|
|
13441
|
+
*/
|
|
13282
13442
|
const DEFAULT_RETRY_DELAY_MS = 5000;
|
|
13443
|
+
/**
|
|
13444
|
+
* @internal
|
|
13445
|
+
*/
|
|
13283
13446
|
const DEFAULT_STREAMING_SYNC_OPTIONS = {
|
|
13284
13447
|
retryDelayMs: DEFAULT_RETRY_DELAY_MS,
|
|
13285
13448
|
crudUploadThrottleMs: DEFAULT_CRUD_UPLOAD_THROTTLE_MS
|
|
13286
13449
|
};
|
|
13450
|
+
/**
|
|
13451
|
+
* @internal
|
|
13452
|
+
*/
|
|
13287
13453
|
const DEFAULT_STREAM_CONNECTION_OPTIONS = {
|
|
13288
13454
|
appMetadata: {},
|
|
13289
13455
|
connectionMethod: SyncStreamConnectionMethod.WEB_SOCKET,
|
|
@@ -13293,22 +13459,21 @@ const DEFAULT_STREAM_CONNECTION_OPTIONS = {
|
|
|
13293
13459
|
serializedSchema: undefined,
|
|
13294
13460
|
includeDefaultStreams: true
|
|
13295
13461
|
};
|
|
13462
|
+
/**
|
|
13463
|
+
* @internal
|
|
13464
|
+
*/
|
|
13296
13465
|
class AbstractStreamingSyncImplementation extends BaseObserver {
|
|
13297
13466
|
options;
|
|
13298
13467
|
abortController;
|
|
13299
|
-
// In rare cases, mostly for tests, uploads can be triggered without being properly connected.
|
|
13300
|
-
// This allows ensuring that all upload processes can be aborted.
|
|
13301
|
-
uploadAbortController;
|
|
13302
13468
|
crudUpdateListener;
|
|
13303
13469
|
streamingSyncPromise;
|
|
13304
13470
|
logger;
|
|
13305
13471
|
activeStreams;
|
|
13306
13472
|
connectionMayHaveChanged = false;
|
|
13307
|
-
|
|
13473
|
+
crudUploadNotifier = asyncNotifier();
|
|
13308
13474
|
notifyCompletedUploads;
|
|
13309
13475
|
handleActiveStreamsChange;
|
|
13310
13476
|
syncStatus;
|
|
13311
|
-
triggerCrudUpload;
|
|
13312
13477
|
constructor(options) {
|
|
13313
13478
|
super();
|
|
13314
13479
|
this.options = options;
|
|
@@ -13324,16 +13489,9 @@ class AbstractStreamingSyncImplementation extends BaseObserver {
|
|
|
13324
13489
|
}
|
|
13325
13490
|
});
|
|
13326
13491
|
this.abortController = null;
|
|
13327
|
-
|
|
13328
|
-
|
|
13329
|
-
|
|
13330
|
-
}
|
|
13331
|
-
this.isUploadingCrud = true;
|
|
13332
|
-
this._uploadAllCrud().finally(() => {
|
|
13333
|
-
this.notifyCompletedUploads?.();
|
|
13334
|
-
this.isUploadingCrud = false;
|
|
13335
|
-
});
|
|
13336
|
-
}, this.options.crudUploadThrottleMs);
|
|
13492
|
+
}
|
|
13493
|
+
triggerCrudUpload() {
|
|
13494
|
+
this.crudUploadNotifier.notify();
|
|
13337
13495
|
}
|
|
13338
13496
|
async waitForReady() { }
|
|
13339
13497
|
waitForStatus(status) {
|
|
@@ -13381,7 +13539,6 @@ class AbstractStreamingSyncImplementation extends BaseObserver {
|
|
|
13381
13539
|
super.dispose();
|
|
13382
13540
|
this.crudUpdateListener?.();
|
|
13383
13541
|
this.crudUpdateListener = undefined;
|
|
13384
|
-
this.uploadAbortController?.abort();
|
|
13385
13542
|
}
|
|
13386
13543
|
async getWriteCheckpoint() {
|
|
13387
13544
|
const clientId = await this.options.adapter.getClientId();
|
|
@@ -13391,7 +13548,17 @@ class AbstractStreamingSyncImplementation extends BaseObserver {
|
|
|
13391
13548
|
this.logger.debug(`Created write checkpoint: ${checkpoint}`);
|
|
13392
13549
|
return checkpoint;
|
|
13393
13550
|
}
|
|
13394
|
-
async
|
|
13551
|
+
async crudUploadLoop(signal) {
|
|
13552
|
+
while (!signal.aborted) {
|
|
13553
|
+
await Promise.all([
|
|
13554
|
+
// Start the initial CRUD upload on connect. Then, keep polling until we're done.
|
|
13555
|
+
this._uploadAllCrud(signal),
|
|
13556
|
+
this.delayRetry(signal, this.options.crudUploadThrottleMs)
|
|
13557
|
+
]);
|
|
13558
|
+
await this.crudUploadNotifier.waitForNotification(signal);
|
|
13559
|
+
}
|
|
13560
|
+
}
|
|
13561
|
+
async _uploadAllCrud(signal) {
|
|
13395
13562
|
return this.obtainLock({
|
|
13396
13563
|
type: LockType.CRUD,
|
|
13397
13564
|
callback: async () => {
|
|
@@ -13399,12 +13566,7 @@ class AbstractStreamingSyncImplementation extends BaseObserver {
|
|
|
13399
13566
|
* Keep track of the first item in the CRUD queue for the last `uploadCrud` iteration.
|
|
13400
13567
|
*/
|
|
13401
13568
|
let checkedCrudItem;
|
|
13402
|
-
|
|
13403
|
-
this.uploadAbortController = controller;
|
|
13404
|
-
this.abortController?.signal.addEventListener('abort', () => {
|
|
13405
|
-
controller.abort();
|
|
13406
|
-
}, { once: true });
|
|
13407
|
-
while (!controller.signal.aborted) {
|
|
13569
|
+
while (!signal.aborted) {
|
|
13408
13570
|
try {
|
|
13409
13571
|
/**
|
|
13410
13572
|
* This is the first item in the FIFO CRUD queue.
|
|
@@ -13434,7 +13596,10 @@ The next upload iteration will be delayed.`);
|
|
|
13434
13596
|
else {
|
|
13435
13597
|
// Uploading is completed
|
|
13436
13598
|
const neededUpdate = await this.options.adapter.updateLocalTarget(() => this.getWriteCheckpoint());
|
|
13437
|
-
if (neededUpdate
|
|
13599
|
+
if (neededUpdate) {
|
|
13600
|
+
this.notifyCompletedUploads?.();
|
|
13601
|
+
}
|
|
13602
|
+
else if (checkedCrudItem != null) {
|
|
13438
13603
|
// Only log this if there was something to upload
|
|
13439
13604
|
this.logger.debug('Upload complete, no write checkpoint needed.');
|
|
13440
13605
|
}
|
|
@@ -13449,7 +13614,7 @@ The next upload iteration will be delayed.`);
|
|
|
13449
13614
|
uploadError: ex
|
|
13450
13615
|
}
|
|
13451
13616
|
});
|
|
13452
|
-
await this.delayRetry(
|
|
13617
|
+
await this.delayRetry(signal);
|
|
13453
13618
|
if (!this.isConnected) {
|
|
13454
13619
|
// Exit the upload loop if the sync stream is no longer connected
|
|
13455
13620
|
break;
|
|
@@ -13464,7 +13629,6 @@ The next upload iteration will be delayed.`);
|
|
|
13464
13629
|
});
|
|
13465
13630
|
}
|
|
13466
13631
|
}
|
|
13467
|
-
this.uploadAbortController = undefined;
|
|
13468
13632
|
}
|
|
13469
13633
|
});
|
|
13470
13634
|
}
|
|
@@ -13474,7 +13638,10 @@ The next upload iteration will be delayed.`);
|
|
|
13474
13638
|
}
|
|
13475
13639
|
const controller = new AbortController();
|
|
13476
13640
|
this.abortController = controller;
|
|
13477
|
-
this.streamingSyncPromise =
|
|
13641
|
+
this.streamingSyncPromise = Promise.all([
|
|
13642
|
+
this.crudUploadLoop(controller.signal).catch((ex) => this.logger.error('Error in crud upload loop', ex)),
|
|
13643
|
+
this.streamingSync(controller.signal, options)
|
|
13644
|
+
]);
|
|
13478
13645
|
// Return a promise that resolves when the connection status is updated to indicate that we're connected.
|
|
13479
13646
|
return new Promise((resolve) => {
|
|
13480
13647
|
const disposer = this.registerListener({
|
|
@@ -13512,14 +13679,7 @@ The next upload iteration will be delayed.`);
|
|
|
13512
13679
|
this.abortController = null;
|
|
13513
13680
|
this.updateSyncStatus({ connected: false, connecting: false });
|
|
13514
13681
|
}
|
|
13515
|
-
/**
|
|
13516
|
-
* @deprecated use [connect instead]
|
|
13517
|
-
*/
|
|
13518
13682
|
async streamingSync(signal, options) {
|
|
13519
|
-
if (!signal) {
|
|
13520
|
-
this.abortController = new AbortController();
|
|
13521
|
-
signal = this.abortController.signal;
|
|
13522
|
-
}
|
|
13523
13683
|
/**
|
|
13524
13684
|
* Listen for CRUD updates and trigger upstream uploads
|
|
13525
13685
|
*/
|
|
@@ -13625,7 +13785,7 @@ The next upload iteration will be delayed.`);
|
|
|
13625
13785
|
this.handleActiveStreamsChange?.();
|
|
13626
13786
|
}
|
|
13627
13787
|
/**
|
|
13628
|
-
* Older versions of the JS SDK used to encode subkeys as JSON in
|
|
13788
|
+
* Older versions of the JS SDK used to encode subkeys as JSON in `OplogEntry.toJSON`.
|
|
13629
13789
|
* Because subkeys are always strings, this leads to quotes being added around them in `ps_oplog`.
|
|
13630
13790
|
* While this is not a problem as long as it's done consistently, it causes issues when a database
|
|
13631
13791
|
* created by the JS SDK is used with other SDKs, or (more likely) when the new Rust sync client
|
|
@@ -13635,7 +13795,7 @@ The next upload iteration will be delayed.`);
|
|
|
13635
13795
|
* migration is only triggered when necessary (for now). The function returns whether the new format
|
|
13636
13796
|
* should be used, so that the JS SDK is able to write to updated databases.
|
|
13637
13797
|
*
|
|
13638
|
-
* @param requireFixedKeyFormat Whether we require the new format or also support the old one.
|
|
13798
|
+
* @param requireFixedKeyFormat - Whether we require the new format or also support the old one.
|
|
13639
13799
|
* The Rust client requires the new subkey format.
|
|
13640
13800
|
* @returns Whether the database is now using the new, fixed subkey format.
|
|
13641
13801
|
*/
|
|
@@ -13893,14 +14053,13 @@ The next upload iteration will be delayed.`);
|
|
|
13893
14053
|
// trigger this for all updates
|
|
13894
14054
|
this.iterateListeners((cb) => cb.statusUpdated?.(options));
|
|
13895
14055
|
}
|
|
13896
|
-
async delayRetry(signal) {
|
|
14056
|
+
async delayRetry(signal, delay = this.options.retryDelayMs) {
|
|
13897
14057
|
return new Promise((resolve) => {
|
|
13898
14058
|
if (signal?.aborted) {
|
|
13899
14059
|
// If the signal is already aborted, resolve immediately
|
|
13900
14060
|
resolve();
|
|
13901
14061
|
return;
|
|
13902
14062
|
}
|
|
13903
|
-
const { retryDelayMs } = this.options;
|
|
13904
14063
|
let timeoutId;
|
|
13905
14064
|
const endDelay = () => {
|
|
13906
14065
|
resolve();
|
|
@@ -13911,7 +14070,7 @@ The next upload iteration will be delayed.`);
|
|
|
13911
14070
|
signal?.removeEventListener('abort', endDelay);
|
|
13912
14071
|
};
|
|
13913
14072
|
signal?.addEventListener('abort', endDelay, { once: true });
|
|
13914
|
-
timeoutId = setTimeout(endDelay,
|
|
14073
|
+
timeoutId = setTimeout(endDelay, delay);
|
|
13915
14074
|
});
|
|
13916
14075
|
}
|
|
13917
14076
|
updateSubscriptions(subscriptions) {
|
|
@@ -13943,7 +14102,8 @@ const MEMORY_TRIGGER_CLAIM_MANAGER = {
|
|
|
13943
14102
|
|
|
13944
14103
|
/**
|
|
13945
14104
|
* SQLite operations to track changes for with {@link TriggerManager}
|
|
13946
|
-
*
|
|
14105
|
+
*
|
|
14106
|
+
* @experimental @alpha
|
|
13947
14107
|
*/
|
|
13948
14108
|
var DiffTriggerOperation;
|
|
13949
14109
|
(function (DiffTriggerOperation) {
|
|
@@ -14005,8 +14165,8 @@ class TriggerManagerImpl {
|
|
|
14005
14165
|
get db() {
|
|
14006
14166
|
return this.options.db;
|
|
14007
14167
|
}
|
|
14008
|
-
async getUUID() {
|
|
14009
|
-
const { id: uuid } = await this.db.get(/* sql */ `
|
|
14168
|
+
async getUUID(ctx) {
|
|
14169
|
+
const { id: uuid } = await (ctx ?? this.db).get(/* sql */ `
|
|
14010
14170
|
SELECT
|
|
14011
14171
|
uuid () as id
|
|
14012
14172
|
`);
|
|
@@ -14119,7 +14279,7 @@ class TriggerManagerImpl {
|
|
|
14119
14279
|
const replicatedColumns = columns ?? sourceDefinition.columns.map((col) => col.name);
|
|
14120
14280
|
const internalSource = sourceDefinition.internalName;
|
|
14121
14281
|
const triggerIds = [];
|
|
14122
|
-
const id = await this.getUUID();
|
|
14282
|
+
const id = await this.getUUID(setupContext);
|
|
14123
14283
|
const releaseStorageClaim = useStorage ? await this.options.claimManager.obtainClaim(id) : null;
|
|
14124
14284
|
/**
|
|
14125
14285
|
* We default to replicating all columns if no columns array is provided.
|
|
@@ -14359,18 +14519,29 @@ const POWERSYNC_TABLE_MATCH = /(^ps_data__|^ps_data_local__)/;
|
|
|
14359
14519
|
const DEFAULT_DISCONNECT_CLEAR_OPTIONS = {
|
|
14360
14520
|
clearLocal: true
|
|
14361
14521
|
};
|
|
14522
|
+
/**
|
|
14523
|
+
* @internal
|
|
14524
|
+
*/
|
|
14362
14525
|
const DEFAULT_POWERSYNC_CLOSE_OPTIONS = {
|
|
14363
14526
|
disconnect: true
|
|
14364
14527
|
};
|
|
14528
|
+
/**
|
|
14529
|
+
* @internal
|
|
14530
|
+
*/
|
|
14365
14531
|
const DEFAULT_POWERSYNC_DB_OPTIONS = {
|
|
14366
14532
|
retryDelayMs: 5000,
|
|
14367
14533
|
crudUploadThrottleMs: DEFAULT_CRUD_UPLOAD_THROTTLE_MS
|
|
14368
14534
|
};
|
|
14535
|
+
/**
|
|
14536
|
+
* @internal
|
|
14537
|
+
*/
|
|
14369
14538
|
const DEFAULT_CRUD_BATCH_LIMIT = 100;
|
|
14370
14539
|
/**
|
|
14371
14540
|
* Requesting nested or recursive locks can block the application in some circumstances.
|
|
14372
14541
|
* This default lock timeout will act as a failsafe to throw an error if a lock cannot
|
|
14373
14542
|
* be obtained.
|
|
14543
|
+
*
|
|
14544
|
+
* @internal
|
|
14374
14545
|
*/
|
|
14375
14546
|
const DEFAULT_LOCK_TIMEOUT_MS = 120_000; // 2 mins
|
|
14376
14547
|
/**
|
|
@@ -14380,6 +14551,9 @@ const DEFAULT_LOCK_TIMEOUT_MS = 120_000; // 2 mins
|
|
|
14380
14551
|
const isPowerSyncDatabaseOptionsWithSettings = (test) => {
|
|
14381
14552
|
return typeof test == 'object' && isSQLOpenOptions(test.database);
|
|
14382
14553
|
};
|
|
14554
|
+
/**
|
|
14555
|
+
* @public
|
|
14556
|
+
*/
|
|
14383
14557
|
class AbstractPowerSyncDatabase extends BaseObserver {
|
|
14384
14558
|
options;
|
|
14385
14559
|
/**
|
|
@@ -14537,7 +14711,7 @@ class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
14537
14711
|
/**
|
|
14538
14712
|
* Wait for the first sync operation to complete.
|
|
14539
14713
|
*
|
|
14540
|
-
* @param request Either an abort signal (after which the promise will complete regardless of
|
|
14714
|
+
* @param request - Either an abort signal (after which the promise will complete regardless of
|
|
14541
14715
|
* whether a full sync was completed) or an object providing an abort signal and a priority target.
|
|
14542
14716
|
* When a priority target is set, the promise may complete when all buckets with the given (or higher)
|
|
14543
14717
|
* priorities have been synchronized. This can be earlier than a complete sync.
|
|
@@ -14692,7 +14866,7 @@ class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
14692
14866
|
/**
|
|
14693
14867
|
* Close the sync connection.
|
|
14694
14868
|
*
|
|
14695
|
-
* Use {@link connect} to connect again.
|
|
14869
|
+
* Use {@link AbstractPowerSyncDatabase.connect} to connect again.
|
|
14696
14870
|
*/
|
|
14697
14871
|
async disconnect() {
|
|
14698
14872
|
return this.connectionManager.disconnect();
|
|
@@ -14719,8 +14893,8 @@ class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
14719
14893
|
/**
|
|
14720
14894
|
* Create a sync stream to query its status or to subscribe to it.
|
|
14721
14895
|
*
|
|
14722
|
-
* @param name The name of the stream to subscribe to.
|
|
14723
|
-
* @param params Optional parameters for the stream subscription.
|
|
14896
|
+
* @param name - The name of the stream to subscribe to.
|
|
14897
|
+
* @param params - Optional parameters for the stream subscription.
|
|
14724
14898
|
* @returns A {@link SyncStream} instance that can be subscribed to.
|
|
14725
14899
|
* @experimental Sync streams are currently in alpha.
|
|
14726
14900
|
*/
|
|
@@ -14778,14 +14952,14 @@ class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
14778
14952
|
* Once the data have been successfully uploaded, call {@link CrudBatch.complete} before
|
|
14779
14953
|
* requesting the next batch.
|
|
14780
14954
|
*
|
|
14781
|
-
* Use
|
|
14955
|
+
* Use the `limit` parameter to specify the maximum number of updates to return in a single
|
|
14782
14956
|
* batch.
|
|
14783
14957
|
*
|
|
14784
14958
|
* This method does include transaction ids in the result, but does not group
|
|
14785
14959
|
* data by transaction. One batch may contain data from multiple transactions,
|
|
14786
14960
|
* and a single transaction may be split over multiple batches.
|
|
14787
14961
|
*
|
|
14788
|
-
* @param limit Maximum number of CRUD entries to include in the batch
|
|
14962
|
+
* @param limit - Maximum number of CRUD entries to include in the batch
|
|
14789
14963
|
* @returns A batch of CRUD operations to upload, or null if there are none
|
|
14790
14964
|
*/
|
|
14791
14965
|
async getCrudBatch(limit = DEFAULT_CRUD_BATCH_LIMIT) {
|
|
@@ -14812,7 +14986,7 @@ class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
14812
14986
|
* Once the data have been successfully uploaded, call {@link CrudTransaction.complete} before
|
|
14813
14987
|
* requesting the next transaction.
|
|
14814
14988
|
*
|
|
14815
|
-
* Unlike {@link getCrudBatch}, this only returns data from a single transaction at a time.
|
|
14989
|
+
* Unlike {@link AbstractPowerSyncDatabase.getCrudBatch}, this only returns data from a single transaction at a time.
|
|
14816
14990
|
* All data for the transaction is loaded into memory.
|
|
14817
14991
|
*
|
|
14818
14992
|
* @returns A transaction of CRUD operations to upload, or null if there are none
|
|
@@ -14827,7 +15001,7 @@ class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
14827
15001
|
* This is typically used from the {@link PowerSyncBackendConnector.uploadData} callback. Each entry emitted by the
|
|
14828
15002
|
* returned iterator is a full transaction containing all local writes made while that transaction was active.
|
|
14829
15003
|
*
|
|
14830
|
-
* Unlike {@link getNextCrudTransaction}, which always returns the oldest transaction that hasn't been
|
|
15004
|
+
* Unlike {@link AbstractPowerSyncDatabase.getNextCrudTransaction}, which always returns the oldest transaction that hasn't been
|
|
14831
15005
|
* {@link CrudTransaction.complete}d yet, this iterator can be used to receive multiple transactions. Calling
|
|
14832
15006
|
* {@link CrudTransaction.complete} will mark that and all prior transactions emitted by the iterator as completed.
|
|
14833
15007
|
*
|
|
@@ -14921,8 +15095,8 @@ SELECT * FROM crud_entries;
|
|
|
14921
15095
|
* the returned result's `rowsAffected` may be `0` for successful `UPDATE` and `DELETE` statements.
|
|
14922
15096
|
* Use a `RETURNING` clause and inspect `result.rows` when you need to confirm which rows changed.
|
|
14923
15097
|
*
|
|
14924
|
-
* @param sql The SQL query to execute
|
|
14925
|
-
* @param parameters Optional array of parameters to bind to the query
|
|
15098
|
+
* @param sql - The SQL query to execute
|
|
15099
|
+
* @param parameters - Optional array of parameters to bind to the query
|
|
14926
15100
|
* @returns The query result as an object with structured key-value pairs
|
|
14927
15101
|
*/
|
|
14928
15102
|
async execute(sql, parameters) {
|
|
@@ -14932,8 +15106,8 @@ SELECT * FROM crud_entries;
|
|
|
14932
15106
|
* Execute a SQL write (INSERT/UPDATE/DELETE) query directly on the database without any PowerSync processing.
|
|
14933
15107
|
* This bypasses certain PowerSync abstractions and is useful for accessing the raw database results.
|
|
14934
15108
|
*
|
|
14935
|
-
* @param sql The SQL query to execute
|
|
14936
|
-
* @param parameters Optional array of parameters to bind to the query
|
|
15109
|
+
* @param sql - The SQL query to execute
|
|
15110
|
+
* @param parameters - Optional array of parameters to bind to the query
|
|
14937
15111
|
* @returns The raw query result from the underlying database as a nested array of raw values, where each row is
|
|
14938
15112
|
* represented as an array of column values without field names.
|
|
14939
15113
|
*/
|
|
@@ -14946,8 +15120,8 @@ SELECT * FROM crud_entries;
|
|
|
14946
15120
|
* and optionally return results.
|
|
14947
15121
|
* This is faster than executing separately with each parameter set.
|
|
14948
15122
|
*
|
|
14949
|
-
* @param sql The SQL query to execute
|
|
14950
|
-
* @param parameters Optional 2D array of parameter sets, where each inner array is a set of parameters for one execution
|
|
15123
|
+
* @param sql - The SQL query to execute
|
|
15124
|
+
* @param parameters - Optional 2D array of parameter sets, where each inner array is a set of parameters for one execution
|
|
14951
15125
|
* @returns The query result
|
|
14952
15126
|
*/
|
|
14953
15127
|
async executeBatch(sql, parameters) {
|
|
@@ -14957,8 +15131,8 @@ SELECT * FROM crud_entries;
|
|
|
14957
15131
|
/**
|
|
14958
15132
|
* Execute a read-only query and return results.
|
|
14959
15133
|
*
|
|
14960
|
-
* @param sql The SQL query to execute
|
|
14961
|
-
* @param parameters Optional array of parameters to bind to the query
|
|
15134
|
+
* @param sql - The SQL query to execute
|
|
15135
|
+
* @param parameters - Optional array of parameters to bind to the query
|
|
14962
15136
|
* @returns An array of results
|
|
14963
15137
|
*/
|
|
14964
15138
|
async getAll(sql, parameters) {
|
|
@@ -14968,8 +15142,8 @@ SELECT * FROM crud_entries;
|
|
|
14968
15142
|
/**
|
|
14969
15143
|
* Execute a read-only query and return the first result, or null if the ResultSet is empty.
|
|
14970
15144
|
*
|
|
14971
|
-
* @param sql The SQL query to execute
|
|
14972
|
-
* @param parameters Optional array of parameters to bind to the query
|
|
15145
|
+
* @param sql - The SQL query to execute
|
|
15146
|
+
* @param parameters - Optional array of parameters to bind to the query
|
|
14973
15147
|
* @returns The first result if found, or null if no results are returned
|
|
14974
15148
|
*/
|
|
14975
15149
|
async getOptional(sql, parameters) {
|
|
@@ -14979,8 +15153,8 @@ SELECT * FROM crud_entries;
|
|
|
14979
15153
|
/**
|
|
14980
15154
|
* Execute a read-only query and return the first result, error if the ResultSet is empty.
|
|
14981
15155
|
*
|
|
14982
|
-
* @param sql The SQL query to execute
|
|
14983
|
-
* @param parameters Optional array of parameters to bind to the query
|
|
15156
|
+
* @param sql - The SQL query to execute
|
|
15157
|
+
* @param parameters - Optional array of parameters to bind to the query
|
|
14984
15158
|
* @returns The first result matching the query
|
|
14985
15159
|
* @throws Error if no rows are returned
|
|
14986
15160
|
*/
|
|
@@ -14990,7 +15164,7 @@ SELECT * FROM crud_entries;
|
|
|
14990
15164
|
}
|
|
14991
15165
|
/**
|
|
14992
15166
|
* Takes a read lock, without starting a transaction.
|
|
14993
|
-
* In most cases, {@link readTransaction} should be used instead.
|
|
15167
|
+
* In most cases, {@link AbstractPowerSyncDatabase.readTransaction} should be used instead.
|
|
14994
15168
|
*/
|
|
14995
15169
|
async readLock(callback) {
|
|
14996
15170
|
await this.waitForReady();
|
|
@@ -14998,7 +15172,7 @@ SELECT * FROM crud_entries;
|
|
|
14998
15172
|
}
|
|
14999
15173
|
/**
|
|
15000
15174
|
* Takes a global lock, without starting a transaction.
|
|
15001
|
-
* In most cases, {@link writeTransaction} should be used instead.
|
|
15175
|
+
* In most cases, {@link AbstractPowerSyncDatabase.writeTransaction} should be used instead.
|
|
15002
15176
|
*/
|
|
15003
15177
|
async writeLock(callback) {
|
|
15004
15178
|
await this.waitForReady();
|
|
@@ -15009,8 +15183,8 @@ SELECT * FROM crud_entries;
|
|
|
15009
15183
|
* Read transactions can run concurrently to a write transaction.
|
|
15010
15184
|
* Changes from any write transaction are not visible to read transactions started before it.
|
|
15011
15185
|
*
|
|
15012
|
-
* @param callback Function to execute within the transaction
|
|
15013
|
-
* @param lockTimeout Time in milliseconds to wait for a lock before throwing an error
|
|
15186
|
+
* @param callback - Function to execute within the transaction
|
|
15187
|
+
* @param lockTimeout - Time in milliseconds to wait for a lock before throwing an error
|
|
15014
15188
|
* @returns The result of the callback
|
|
15015
15189
|
* @throws Error if the lock cannot be obtained within the timeout period
|
|
15016
15190
|
*/
|
|
@@ -15027,8 +15201,8 @@ SELECT * FROM crud_entries;
|
|
|
15027
15201
|
* This takes a global lock - only one write transaction can execute against the database at a time.
|
|
15028
15202
|
* Statements within the transaction must be done on the provided {@link Transaction} interface.
|
|
15029
15203
|
*
|
|
15030
|
-
* @param callback Function to execute within the transaction
|
|
15031
|
-
* @param lockTimeout Time in milliseconds to wait for a lock before throwing an error
|
|
15204
|
+
* @param callback - Function to execute within the transaction
|
|
15205
|
+
* @param lockTimeout - Time in milliseconds to wait for a lock before throwing an error
|
|
15032
15206
|
* @returns The result of the callback
|
|
15033
15207
|
* @throws Error if the lock cannot be obtained within the timeout period
|
|
15034
15208
|
*/
|
|
@@ -15105,15 +15279,15 @@ SELECT * FROM crud_entries;
|
|
|
15105
15279
|
}
|
|
15106
15280
|
/**
|
|
15107
15281
|
* Execute a read query every time the source tables are modified.
|
|
15108
|
-
* Use {@link
|
|
15282
|
+
* Use {@link SQLOnChangeOptions.throttleMs} to specify the minimum interval between queries.
|
|
15109
15283
|
* Source tables are automatically detected using `EXPLAIN QUERY PLAN`.
|
|
15110
15284
|
*
|
|
15111
15285
|
* Note that the `onChange` callback member of the handler is required.
|
|
15112
15286
|
*
|
|
15113
|
-
* @param sql The SQL query to execute
|
|
15114
|
-
* @param parameters Optional array of parameters to bind to the query
|
|
15115
|
-
* @param handler Callbacks for handling results and errors
|
|
15116
|
-
* @param options Options for configuring watch behavior
|
|
15287
|
+
* @param sql - The SQL query to execute
|
|
15288
|
+
* @param parameters - Optional array of parameters to bind to the query
|
|
15289
|
+
* @param handler - Callbacks for handling results and errors
|
|
15290
|
+
* @param options - Options for configuring watch behavior
|
|
15117
15291
|
*/
|
|
15118
15292
|
watchWithCallback(sql, parameters, handler, options) {
|
|
15119
15293
|
const { onResult, onError = (e) => this.logger.error(e) } = handler ?? {};
|
|
@@ -15126,7 +15300,7 @@ SELECT * FROM crud_entries;
|
|
|
15126
15300
|
const watchedQuery = new OnChangeQueryProcessor({
|
|
15127
15301
|
db: this,
|
|
15128
15302
|
comparator,
|
|
15129
|
-
placeholderData: null,
|
|
15303
|
+
placeholderData: null, // FIXME
|
|
15130
15304
|
watchOptions: {
|
|
15131
15305
|
query: {
|
|
15132
15306
|
compile: () => ({
|
|
@@ -15159,12 +15333,12 @@ SELECT * FROM crud_entries;
|
|
|
15159
15333
|
}
|
|
15160
15334
|
/**
|
|
15161
15335
|
* Execute a read query every time the source tables are modified.
|
|
15162
|
-
* Use {@link
|
|
15336
|
+
* Use {@link SQLOnChangeOptions.throttleMs} to specify the minimum interval between queries.
|
|
15163
15337
|
* Source tables are automatically detected using `EXPLAIN QUERY PLAN`.
|
|
15164
15338
|
*
|
|
15165
|
-
* @param sql The SQL query to execute
|
|
15166
|
-
* @param parameters Optional array of parameters to bind to the query
|
|
15167
|
-
* @param options Options for configuring watch behavior
|
|
15339
|
+
* @param sql - The SQL query to execute
|
|
15340
|
+
* @param parameters - Optional array of parameters to bind to the query
|
|
15341
|
+
* @param options - Options for configuring watch behavior
|
|
15168
15342
|
* @returns An AsyncIterable that yields QueryResults whenever the data changes
|
|
15169
15343
|
*/
|
|
15170
15344
|
watchWithAsyncGenerator(sql, parameters, options) {
|
|
@@ -15188,9 +15362,9 @@ SELECT * FROM crud_entries;
|
|
|
15188
15362
|
* If tables are specified in the options, those are used directly.
|
|
15189
15363
|
* Otherwise, analyzes the query using EXPLAIN to determine which tables are accessed.
|
|
15190
15364
|
*
|
|
15191
|
-
* @param sql The SQL query to analyze
|
|
15192
|
-
* @param parameters Optional parameters for the SQL query
|
|
15193
|
-
* @param options Optional watch options that may contain explicit table list
|
|
15365
|
+
* @param sql - The SQL query to analyze
|
|
15366
|
+
* @param parameters - Optional parameters for the SQL query
|
|
15367
|
+
* @param options - Optional watch options that may contain explicit table list
|
|
15194
15368
|
* @returns Array of table names that the query depends on
|
|
15195
15369
|
*/
|
|
15196
15370
|
async resolveTables(sql, parameters, options) {
|
|
@@ -15219,13 +15393,13 @@ SELECT * FROM crud_entries;
|
|
|
15219
15393
|
/**
|
|
15220
15394
|
* Invoke the provided callback on any changes to any of the specified tables.
|
|
15221
15395
|
*
|
|
15222
|
-
* This is preferred over {@link watchWithCallback} when multiple queries need to be performed
|
|
15396
|
+
* This is preferred over {@link AbstractPowerSyncDatabase.watchWithCallback} when multiple queries need to be performed
|
|
15223
15397
|
* together when data is changed.
|
|
15224
15398
|
*
|
|
15225
15399
|
* Note that the `onChange` callback member of the handler is required.
|
|
15226
15400
|
*
|
|
15227
|
-
* @param handler Callbacks for handling change events and errors
|
|
15228
|
-
* @param options Options for configuring watch behavior
|
|
15401
|
+
* @param handler - Callbacks for handling change events and errors
|
|
15402
|
+
* @param options - Options for configuring watch behavior
|
|
15229
15403
|
* @returns A dispose function to stop watching for changes
|
|
15230
15404
|
*/
|
|
15231
15405
|
onChangeWithCallback(handler, options) {
|
|
@@ -15268,12 +15442,12 @@ SELECT * FROM crud_entries;
|
|
|
15268
15442
|
/**
|
|
15269
15443
|
* Create a Stream of changes to any of the specified tables.
|
|
15270
15444
|
*
|
|
15271
|
-
* This is preferred over {@link watchWithAsyncGenerator} when multiple queries need to be
|
|
15272
|
-
* together when data is changed.
|
|
15445
|
+
* This is preferred over {@link AbstractPowerSyncDatabase.watchWithAsyncGenerator} when multiple queries need to be
|
|
15446
|
+
* performed together when data is changed.
|
|
15273
15447
|
*
|
|
15274
15448
|
* Note: do not declare this as `async *onChange` as it will not work in React Native.
|
|
15275
15449
|
*
|
|
15276
|
-
* @param options Options for configuring watch behavior
|
|
15450
|
+
* @param options - Options for configuring watch behavior
|
|
15277
15451
|
* @returns An AsyncIterable that yields change events whenever the specified tables change
|
|
15278
15452
|
*/
|
|
15279
15453
|
onChangeWithAsyncGenerator(options) {
|
|
@@ -15311,15 +15485,15 @@ SELECT * FROM crud_entries;
|
|
|
15311
15485
|
changedTables.add(table);
|
|
15312
15486
|
}
|
|
15313
15487
|
}
|
|
15314
|
-
/**
|
|
15315
|
-
* @ignore
|
|
15316
|
-
*/
|
|
15317
15488
|
async executeReadOnly(sql, params) {
|
|
15318
15489
|
await this.waitForReady();
|
|
15319
15490
|
return this.database.readLock((tx) => tx.execute(sql, params));
|
|
15320
15491
|
}
|
|
15321
15492
|
}
|
|
15322
15493
|
|
|
15494
|
+
/**
|
|
15495
|
+
* @internal
|
|
15496
|
+
*/
|
|
15323
15497
|
class AbstractPowerSyncDatabaseOpenFactory {
|
|
15324
15498
|
options;
|
|
15325
15499
|
constructor(options) {
|
|
@@ -15344,6 +15518,9 @@ class AbstractPowerSyncDatabaseOpenFactory {
|
|
|
15344
15518
|
}
|
|
15345
15519
|
}
|
|
15346
15520
|
|
|
15521
|
+
/**
|
|
15522
|
+
* @internal
|
|
15523
|
+
*/
|
|
15347
15524
|
function runOnSchemaChange(callback, db, options) {
|
|
15348
15525
|
const triggerWatchedQuery = () => {
|
|
15349
15526
|
const abortController = new AbortController();
|
|
@@ -15368,6 +15545,9 @@ function runOnSchemaChange(callback, db, options) {
|
|
|
15368
15545
|
triggerWatchedQuery();
|
|
15369
15546
|
}
|
|
15370
15547
|
|
|
15548
|
+
/**
|
|
15549
|
+
* @public
|
|
15550
|
+
*/
|
|
15371
15551
|
function compilableQueryWatch(db, query, handler, options) {
|
|
15372
15552
|
const { onResult, onError = (e) => { } } = handler ?? {};
|
|
15373
15553
|
if (!onResult) {
|
|
@@ -15405,8 +15585,14 @@ function compilableQueryWatch(db, query, handler, options) {
|
|
|
15405
15585
|
runOnSchemaChange(watchQuery, db, options);
|
|
15406
15586
|
}
|
|
15407
15587
|
|
|
15588
|
+
/**
|
|
15589
|
+
* @internal
|
|
15590
|
+
*/
|
|
15408
15591
|
const MAX_OP_ID = '9223372036854775807';
|
|
15409
15592
|
|
|
15593
|
+
/**
|
|
15594
|
+
* @internal
|
|
15595
|
+
*/
|
|
15410
15596
|
class SqliteBucketStorage extends BaseObserver {
|
|
15411
15597
|
db;
|
|
15412
15598
|
logger;
|
|
@@ -15567,6 +15753,8 @@ class SqliteBucketStorage extends BaseObserver {
|
|
|
15567
15753
|
* Thrown when an underlying database connection is closed.
|
|
15568
15754
|
* This is particularly relevant when worker connections are marked as closed while
|
|
15569
15755
|
* operations are still in progress.
|
|
15756
|
+
*
|
|
15757
|
+
* @internal
|
|
15570
15758
|
*/
|
|
15571
15759
|
class ConnectionClosedError extends Error {
|
|
15572
15760
|
static NAME = 'ConnectionClosedError';
|
|
@@ -15586,6 +15774,8 @@ class ConnectionClosedError extends Error {
|
|
|
15586
15774
|
|
|
15587
15775
|
/**
|
|
15588
15776
|
* A schema is a collection of tables. It is used to define the structure of a database.
|
|
15777
|
+
*
|
|
15778
|
+
* @public
|
|
15589
15779
|
*/
|
|
15590
15780
|
class Schema {
|
|
15591
15781
|
/*
|
|
@@ -15622,7 +15812,7 @@ class Schema {
|
|
|
15622
15812
|
* Since raw tables are not backed by JSON, running complex queries on them may be more efficient. Further, they allow
|
|
15623
15813
|
* using client-side table and column constraints.
|
|
15624
15814
|
*
|
|
15625
|
-
* @param tables An object of (table name, raw table definition) entries.
|
|
15815
|
+
* @param tables - An object of (table name, raw table definition) entries.
|
|
15626
15816
|
*/
|
|
15627
15817
|
withRawTables(tables) {
|
|
15628
15818
|
for (const [name, rawTableDefinition] of Object.entries(tables)) {
|
|
@@ -15668,6 +15858,8 @@ class Schema {
|
|
|
15668
15858
|
Generate a new table from the columns and indexes
|
|
15669
15859
|
@deprecated You should use {@link Table} instead as it now allows TableV2 syntax.
|
|
15670
15860
|
This will be removed in the next major release.
|
|
15861
|
+
|
|
15862
|
+
@public
|
|
15671
15863
|
*/
|
|
15672
15864
|
class TableV2 extends Table {
|
|
15673
15865
|
}
|
|
@@ -15678,6 +15870,8 @@ function sanitizeString(input) {
|
|
|
15678
15870
|
/**
|
|
15679
15871
|
* Helper function for sanitizing UUID input strings.
|
|
15680
15872
|
* Typically used with {@link sanitizeSQL}.
|
|
15873
|
+
*
|
|
15874
|
+
* @alpha
|
|
15681
15875
|
*/
|
|
15682
15876
|
function sanitizeUUID(uuid) {
|
|
15683
15877
|
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
@@ -15714,6 +15908,8 @@ function sanitizeUUID(uuid) {
|
|
|
15714
15908
|
* // Incorrect:
|
|
15715
15909
|
* sanitizeSQL`New.id = '${myID}'` // Produces double quotes: New.id = ''O''Reilly''
|
|
15716
15910
|
* ```
|
|
15911
|
+
*
|
|
15912
|
+
* @alpha
|
|
15717
15913
|
*/
|
|
15718
15914
|
function sanitizeSQL(strings, ...values) {
|
|
15719
15915
|
let result = '';
|
|
@@ -15743,6 +15939,8 @@ function sanitizeSQL(strings, ...values) {
|
|
|
15743
15939
|
|
|
15744
15940
|
/**
|
|
15745
15941
|
* Performs a {@link AbstractPowerSyncDatabase.getAll} operation for a watched query.
|
|
15942
|
+
*
|
|
15943
|
+
* @public
|
|
15746
15944
|
*/
|
|
15747
15945
|
class GetAllQuery {
|
|
15748
15946
|
options;
|
|
@@ -15767,6 +15965,9 @@ class GetAllQuery {
|
|
|
15767
15965
|
}
|
|
15768
15966
|
|
|
15769
15967
|
const TypedLogger = Logger;
|
|
15968
|
+
/**
|
|
15969
|
+
* @public
|
|
15970
|
+
*/
|
|
15770
15971
|
const LogLevel = {
|
|
15771
15972
|
TRACE: TypedLogger.TRACE,
|
|
15772
15973
|
DEBUG: TypedLogger.DEBUG,
|
|
@@ -15783,6 +15984,7 @@ const LogLevel = {
|
|
|
15783
15984
|
* across all loggers created with `createLogger`. Adjusting settings on this
|
|
15784
15985
|
* base logger affects all loggers derived from it unless explicitly overridden.
|
|
15785
15986
|
*
|
|
15987
|
+
* @public
|
|
15786
15988
|
*/
|
|
15787
15989
|
function createBaseLogger() {
|
|
15788
15990
|
return Logger;
|
|
@@ -15793,6 +15995,8 @@ function createBaseLogger() {
|
|
|
15793
15995
|
* Named loggers allow specific modules or areas of your application to have
|
|
15794
15996
|
* their own logging levels and behaviors. These loggers inherit configuration
|
|
15795
15997
|
* from the base logger by default but can override settings independently.
|
|
15998
|
+
*
|
|
15999
|
+
* @public
|
|
15796
16000
|
*/
|
|
15797
16001
|
function createLogger(name, options = {}) {
|
|
15798
16002
|
const logger = Logger.get(name);
|
|
@@ -15802,6 +16006,9 @@ function createLogger(name, options = {}) {
|
|
|
15802
16006
|
return logger;
|
|
15803
16007
|
}
|
|
15804
16008
|
|
|
16009
|
+
/**
|
|
16010
|
+
* @internal
|
|
16011
|
+
*/
|
|
15805
16012
|
const parseQuery = (query, parameters) => {
|
|
15806
16013
|
let sqlStatement;
|
|
15807
16014
|
if (typeof query == 'string') {
|