@powersync/common 1.56.0 → 1.57.1
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 +105 -40
- package/dist/bundle.cjs.map +1 -1
- package/dist/bundle.mjs +105 -41
- package/dist/bundle.mjs.map +1 -1
- package/dist/bundle.node.cjs +105 -40
- package/dist/bundle.node.cjs.map +1 -1
- package/dist/bundle.node.mjs +105 -41
- package/dist/bundle.node.mjs.map +1 -1
- package/dist/index.d.cts +55 -9
- package/lib/attachments/AttachmentQueue.d.ts +18 -1
- package/lib/attachments/AttachmentQueue.js +43 -7
- package/lib/attachments/AttachmentQueue.js.map +1 -1
- package/lib/attachments/Schema.d.ts +21 -2
- package/lib/attachments/Schema.js +14 -10
- package/lib/attachments/Schema.js.map +1 -1
- package/lib/attachments/SyncingService.d.ts +14 -4
- package/lib/attachments/SyncingService.js +39 -21
- package/lib/attachments/SyncingService.js.map +1 -1
- package/lib/client/triggers/TriggerManagerImpl.js +6 -1
- package/lib/client/triggers/TriggerManagerImpl.js.map +1 -1
- package/lib/db/crud/SyncStatus.d.ts +1 -1
- package/lib/db/crud/SyncStatus.js +1 -1
- package/package.json +1 -1
- package/src/attachments/AttachmentQueue.ts +46 -7
- package/src/attachments/Schema.ts +29 -20
- package/src/attachments/SyncingService.ts +46 -23
- package/src/client/triggers/TriggerManagerImpl.ts +6 -1
- package/src/db/crud/SyncStatus.ts +1 -1
package/dist/bundle.node.cjs
CHANGED
|
@@ -391,6 +391,19 @@ exports.AttachmentState = void 0;
|
|
|
391
391
|
AttachmentState[AttachmentState["SYNCED"] = 3] = "SYNCED";
|
|
392
392
|
AttachmentState[AttachmentState["ARCHIVED"] = 4] = "ARCHIVED"; // Attachment has been orphaned, i.e. the associated record has been deleted
|
|
393
393
|
})(exports.AttachmentState || (exports.AttachmentState = {}));
|
|
394
|
+
/**
|
|
395
|
+
* @alpha
|
|
396
|
+
*/
|
|
397
|
+
const ATTACHMENT_TABLE_COLUMNS = {
|
|
398
|
+
filename: column.text,
|
|
399
|
+
local_uri: column.text,
|
|
400
|
+
timestamp: column.integer,
|
|
401
|
+
size: column.integer,
|
|
402
|
+
media_type: column.text,
|
|
403
|
+
state: column.integer, // Corresponds to AttachmentState
|
|
404
|
+
has_synced: column.integer,
|
|
405
|
+
meta_data: column.text
|
|
406
|
+
};
|
|
394
407
|
/**
|
|
395
408
|
* AttachmentTable defines the schema for the attachment queue table.
|
|
396
409
|
*
|
|
@@ -398,16 +411,7 @@ exports.AttachmentState = void 0;
|
|
|
398
411
|
*/
|
|
399
412
|
class AttachmentTable extends Table {
|
|
400
413
|
constructor(options) {
|
|
401
|
-
super({
|
|
402
|
-
filename: column.text,
|
|
403
|
-
local_uri: column.text,
|
|
404
|
-
timestamp: column.integer,
|
|
405
|
-
size: column.integer,
|
|
406
|
-
media_type: column.text,
|
|
407
|
-
state: column.integer, // Corresponds to AttachmentState
|
|
408
|
-
has_synced: column.integer,
|
|
409
|
-
meta_data: column.text
|
|
410
|
-
}, {
|
|
414
|
+
super(ATTACHMENT_TABLE_COLUMNS, {
|
|
411
415
|
...options,
|
|
412
416
|
viewName: options?.viewName ?? ATTACHMENT_TABLE,
|
|
413
417
|
localOnly: true,
|
|
@@ -939,31 +943,51 @@ class SyncingService {
|
|
|
939
943
|
}
|
|
940
944
|
/**
|
|
941
945
|
* Processes attachments based on their state (upload, download, or delete).
|
|
942
|
-
*
|
|
946
|
+
*
|
|
947
|
+
* Each attachment's I/O runs outside the attachment-service mutex, and the row's
|
|
948
|
+
* state transition is persisted immediately after it completes. This keeps the
|
|
949
|
+
* mutex available to concurrent `saveFile` / `deleteFile` / watched-attachment
|
|
950
|
+
* processing while a batch is in flight, and means consumer queries against the
|
|
951
|
+
* attachments queue see incremental progress instead of one atomic commit at the
|
|
952
|
+
* end of the batch.
|
|
943
953
|
*
|
|
944
954
|
* @param attachments - Array of attachment records to process
|
|
945
|
-
* @param
|
|
946
|
-
*
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
955
|
+
* @param options - Optional controls. Pass `signal` (an `AbortSignal`) to interrupt
|
|
956
|
+
* the batch: it is checked between attachments and, once aborted, the
|
|
957
|
+
* loop exits early — letting `stopSync` stop a running batch within
|
|
958
|
+
* one attachment's processing time.
|
|
959
|
+
*/
|
|
960
|
+
async processAttachments(attachments, options) {
|
|
961
|
+
const signal = options?.signal;
|
|
962
|
+
this.logger.info(`Starting processAttachments with ${attachments.length} attachments`);
|
|
950
963
|
for (const attachment of attachments) {
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
+
if (signal?.aborted) {
|
|
965
|
+
this.logger.info('Sync cancelled; stopping iteration early');
|
|
966
|
+
return;
|
|
967
|
+
}
|
|
968
|
+
try {
|
|
969
|
+
let updated;
|
|
970
|
+
switch (attachment.state) {
|
|
971
|
+
case exports.AttachmentState.QUEUED_UPLOAD:
|
|
972
|
+
updated = await this.uploadAttachment(attachment);
|
|
973
|
+
break;
|
|
974
|
+
case exports.AttachmentState.QUEUED_DOWNLOAD:
|
|
975
|
+
updated = await this.downloadAttachment(attachment);
|
|
976
|
+
break;
|
|
977
|
+
case exports.AttachmentState.QUEUED_DELETE:
|
|
978
|
+
// `deleteAttachment` needs a context (it removes the row in a
|
|
979
|
+
// transaction); briefly re-acquire the mutex for just this row.
|
|
980
|
+
updated = await this.attachmentService.withContext((ctx) => this.deleteAttachment(attachment, ctx));
|
|
981
|
+
break;
|
|
982
|
+
default:
|
|
983
|
+
continue;
|
|
984
|
+
}
|
|
985
|
+
await this.attachmentService.withContext((ctx) => ctx.saveAttachments([updated]));
|
|
986
|
+
}
|
|
987
|
+
catch (error) {
|
|
988
|
+
this.logger.warn(`Error during sync for ${attachment.id}`, error);
|
|
964
989
|
}
|
|
965
990
|
}
|
|
966
|
-
await context.saveAttachments(updatedAttachments);
|
|
967
991
|
}
|
|
968
992
|
/**
|
|
969
993
|
* Uploads an attachment from local storage to remote storage.
|
|
@@ -1134,6 +1158,19 @@ class AttachmentQueue {
|
|
|
1134
1158
|
statusListenerDispose;
|
|
1135
1159
|
watchActiveAttachments;
|
|
1136
1160
|
watchAttachmentsAbortController;
|
|
1161
|
+
/**
|
|
1162
|
+
* Serializes concurrent `syncStorage()` triggers (periodic timer, watch-onDiff,
|
|
1163
|
+
* status-changed). Held across the whole batch, but only contended by other
|
|
1164
|
+
* sync triggers — foreground `saveFile` / `deleteFile` / watched-attachment
|
|
1165
|
+
* processing don't take this lock and proceed in parallel via the
|
|
1166
|
+
* `AttachmentService` mutex, which is acquired only briefly per row.
|
|
1167
|
+
*/
|
|
1168
|
+
syncLoopMutex = new Mutex();
|
|
1169
|
+
/**
|
|
1170
|
+
* Aborted by `stopSync()` to interrupt an in-flight batch within one
|
|
1171
|
+
* attachment's processing time. Polled between rows by `SyncingService`.
|
|
1172
|
+
*/
|
|
1173
|
+
syncAbortController;
|
|
1137
1174
|
/**
|
|
1138
1175
|
* Creates a new AttachmentQueue instance.
|
|
1139
1176
|
*
|
|
@@ -1173,6 +1210,7 @@ class AttachmentQueue {
|
|
|
1173
1210
|
*/
|
|
1174
1211
|
async startSync() {
|
|
1175
1212
|
await this.stopSync();
|
|
1213
|
+
this.syncAbortController = new AbortController();
|
|
1176
1214
|
this.watchActiveAttachments = this.attachmentService.watchActiveAttachments({
|
|
1177
1215
|
throttleMs: this.syncThrottleDuration
|
|
1178
1216
|
});
|
|
@@ -1287,23 +1325,44 @@ class AttachmentQueue {
|
|
|
1287
1325
|
*
|
|
1288
1326
|
* This is called automatically at regular intervals when sync is started,
|
|
1289
1327
|
* but can also be called manually to trigger an immediate sync.
|
|
1328
|
+
*
|
|
1329
|
+
* Concurrent invocations are serialized via `syncLoopMutex`.
|
|
1290
1330
|
*/
|
|
1291
1331
|
async syncStorage() {
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
await this.
|
|
1297
|
-
|
|
1332
|
+
const signal = this.syncAbortController?.signal;
|
|
1333
|
+
if (signal?.aborted)
|
|
1334
|
+
return;
|
|
1335
|
+
try {
|
|
1336
|
+
await this.syncLoopMutex.runExclusive(async () => {
|
|
1337
|
+
const activeAttachments = await this.attachmentService.withContext((ctx) => ctx.getActiveAttachments());
|
|
1338
|
+
await this.localStorage.initialize();
|
|
1339
|
+
await this.syncingService.processAttachments(activeAttachments, { signal });
|
|
1340
|
+
if (signal?.aborted)
|
|
1341
|
+
return;
|
|
1342
|
+
await this.attachmentService.withContext((ctx) => this.syncingService.deleteArchivedAttachments(ctx));
|
|
1343
|
+
}, signal);
|
|
1344
|
+
}
|
|
1345
|
+
catch (error) {
|
|
1346
|
+
// A queued batch's acquire rejects when `stopSync` aborts — expected, not an error.
|
|
1347
|
+
if (signal?.aborted)
|
|
1348
|
+
return;
|
|
1349
|
+
throw error;
|
|
1350
|
+
}
|
|
1298
1351
|
}
|
|
1299
1352
|
/**
|
|
1300
1353
|
* Stops the attachment synchronization process.
|
|
1301
1354
|
*
|
|
1302
|
-
* Clears the periodic sync timer
|
|
1355
|
+
* Clears the periodic sync timer, closes all active attachment watchers, and
|
|
1356
|
+
* aborts any in-flight `syncStorage()` call so it exits within one
|
|
1357
|
+
* attachment's processing time instead of running the batch to completion.
|
|
1303
1358
|
*/
|
|
1304
1359
|
async stopSync() {
|
|
1305
1360
|
clearInterval(this.periodicSyncTimer);
|
|
1306
1361
|
this.periodicSyncTimer = undefined;
|
|
1362
|
+
if (this.syncAbortController) {
|
|
1363
|
+
this.syncAbortController.abort();
|
|
1364
|
+
this.syncAbortController = undefined;
|
|
1365
|
+
}
|
|
1307
1366
|
if (this.watchActiveAttachments)
|
|
1308
1367
|
await this.watchActiveAttachments.close();
|
|
1309
1368
|
if (this.watchAttachmentsAbortController) {
|
|
@@ -2053,7 +2112,7 @@ class SyncStatus {
|
|
|
2053
2112
|
});
|
|
2054
2113
|
}
|
|
2055
2114
|
/**
|
|
2056
|
-
* All sync streams currently being tracked in
|
|
2115
|
+
* All sync streams currently being tracked in the database.
|
|
2057
2116
|
*
|
|
2058
2117
|
* This returns null when the database is currently being opened and we don't have reliable information about all
|
|
2059
2118
|
* included streams yet.
|
|
@@ -8547,7 +8606,7 @@ function requireDist () {
|
|
|
8547
8606
|
|
|
8548
8607
|
var distExports = requireDist();
|
|
8549
8608
|
|
|
8550
|
-
var version = "1.
|
|
8609
|
+
var version = "1.57.1";
|
|
8551
8610
|
var PACKAGE = {
|
|
8552
8611
|
version: version};
|
|
8553
8612
|
|
|
@@ -10298,7 +10357,12 @@ class TriggerManagerImpl {
|
|
|
10298
10357
|
// destination table consistent.
|
|
10299
10358
|
await this.db.writeTransaction(async (tx) => {
|
|
10300
10359
|
const callbackResult = await options.onChange({
|
|
10301
|
-
|
|
10360
|
+
execute: (query, params) => tx.execute(query, params),
|
|
10361
|
+
executeBatch: (query, params) => tx.executeBatch(query, params),
|
|
10362
|
+
executeRaw: (query, params) => tx.executeRaw(query, params),
|
|
10363
|
+
get: (query, params) => tx.get(query, params),
|
|
10364
|
+
getAll: (query, params) => tx.getAll(query, params),
|
|
10365
|
+
getOptional: (query, params) => tx.getOptional(query, params),
|
|
10302
10366
|
destinationTable: destination,
|
|
10303
10367
|
withDiff: async (query, params, options) => {
|
|
10304
10368
|
// Wrap the query to expose the destination table
|
|
@@ -11875,6 +11939,7 @@ const parseQuery = (query, parameters) => {
|
|
|
11875
11939
|
};
|
|
11876
11940
|
|
|
11877
11941
|
exports.ATTACHMENT_TABLE = ATTACHMENT_TABLE;
|
|
11942
|
+
exports.ATTACHMENT_TABLE_COLUMNS = ATTACHMENT_TABLE_COLUMNS;
|
|
11878
11943
|
exports.AbortOperation = AbortOperation;
|
|
11879
11944
|
exports.AbstractPowerSyncDatabase = AbstractPowerSyncDatabase;
|
|
11880
11945
|
exports.AbstractPowerSyncDatabaseOpenFactory = AbstractPowerSyncDatabaseOpenFactory;
|