@powersync/common 1.55.0 → 1.57.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bundle.cjs +108 -45
- package/dist/bundle.cjs.map +1 -1
- package/dist/bundle.mjs +108 -46
- package/dist/bundle.mjs.map +1 -1
- package/dist/bundle.node.cjs +108 -45
- package/dist/bundle.node.cjs.map +1 -1
- package/dist/bundle.node.mjs +108 -46
- package/dist/bundle.node.mjs.map +1 -1
- package/dist/index.d.cts +55 -13
- 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/AbstractPowerSyncDatabase.js +1 -1
- package/lib/client/AbstractPowerSyncDatabase.js.map +1 -1
- package/lib/db/crud/SyncStatus.d.ts +1 -5
- package/lib/db/crud/SyncStatus.js +9 -6
- package/lib/db/crud/SyncStatus.js.map +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/AbstractPowerSyncDatabase.ts +3 -1
- package/src/db/crud/SyncStatus.ts +10 -7
package/dist/bundle.cjs
CHANGED
|
@@ -389,6 +389,19 @@ exports.AttachmentState = void 0;
|
|
|
389
389
|
AttachmentState[AttachmentState["SYNCED"] = 3] = "SYNCED";
|
|
390
390
|
AttachmentState[AttachmentState["ARCHIVED"] = 4] = "ARCHIVED"; // Attachment has been orphaned, i.e. the associated record has been deleted
|
|
391
391
|
})(exports.AttachmentState || (exports.AttachmentState = {}));
|
|
392
|
+
/**
|
|
393
|
+
* @alpha
|
|
394
|
+
*/
|
|
395
|
+
const ATTACHMENT_TABLE_COLUMNS = {
|
|
396
|
+
filename: column.text,
|
|
397
|
+
local_uri: column.text,
|
|
398
|
+
timestamp: column.integer,
|
|
399
|
+
size: column.integer,
|
|
400
|
+
media_type: column.text,
|
|
401
|
+
state: column.integer, // Corresponds to AttachmentState
|
|
402
|
+
has_synced: column.integer,
|
|
403
|
+
meta_data: column.text
|
|
404
|
+
};
|
|
392
405
|
/**
|
|
393
406
|
* AttachmentTable defines the schema for the attachment queue table.
|
|
394
407
|
*
|
|
@@ -396,16 +409,7 @@ exports.AttachmentState = void 0;
|
|
|
396
409
|
*/
|
|
397
410
|
class AttachmentTable extends Table {
|
|
398
411
|
constructor(options) {
|
|
399
|
-
super({
|
|
400
|
-
filename: column.text,
|
|
401
|
-
local_uri: column.text,
|
|
402
|
-
timestamp: column.integer,
|
|
403
|
-
size: column.integer,
|
|
404
|
-
media_type: column.text,
|
|
405
|
-
state: column.integer, // Corresponds to AttachmentState
|
|
406
|
-
has_synced: column.integer,
|
|
407
|
-
meta_data: column.text
|
|
408
|
-
}, {
|
|
412
|
+
super(ATTACHMENT_TABLE_COLUMNS, {
|
|
409
413
|
...options,
|
|
410
414
|
viewName: options?.viewName ?? ATTACHMENT_TABLE,
|
|
411
415
|
localOnly: true,
|
|
@@ -937,31 +941,51 @@ class SyncingService {
|
|
|
937
941
|
}
|
|
938
942
|
/**
|
|
939
943
|
* Processes attachments based on their state (upload, download, or delete).
|
|
940
|
-
*
|
|
944
|
+
*
|
|
945
|
+
* Each attachment's I/O runs outside the attachment-service mutex, and the row's
|
|
946
|
+
* state transition is persisted immediately after it completes. This keeps the
|
|
947
|
+
* mutex available to concurrent `saveFile` / `deleteFile` / watched-attachment
|
|
948
|
+
* processing while a batch is in flight, and means consumer queries against the
|
|
949
|
+
* attachments queue see incremental progress instead of one atomic commit at the
|
|
950
|
+
* end of the batch.
|
|
941
951
|
*
|
|
942
952
|
* @param attachments - Array of attachment records to process
|
|
943
|
-
* @param
|
|
944
|
-
*
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
953
|
+
* @param options - Optional controls. Pass `signal` (an `AbortSignal`) to interrupt
|
|
954
|
+
* the batch: it is checked between attachments and, once aborted, the
|
|
955
|
+
* loop exits early — letting `stopSync` stop a running batch within
|
|
956
|
+
* one attachment's processing time.
|
|
957
|
+
*/
|
|
958
|
+
async processAttachments(attachments, options) {
|
|
959
|
+
const signal = options?.signal;
|
|
960
|
+
this.logger.info(`Starting processAttachments with ${attachments.length} attachments`);
|
|
948
961
|
for (const attachment of attachments) {
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
+
if (signal?.aborted) {
|
|
963
|
+
this.logger.info('Sync cancelled; stopping iteration early');
|
|
964
|
+
return;
|
|
965
|
+
}
|
|
966
|
+
try {
|
|
967
|
+
let updated;
|
|
968
|
+
switch (attachment.state) {
|
|
969
|
+
case exports.AttachmentState.QUEUED_UPLOAD:
|
|
970
|
+
updated = await this.uploadAttachment(attachment);
|
|
971
|
+
break;
|
|
972
|
+
case exports.AttachmentState.QUEUED_DOWNLOAD:
|
|
973
|
+
updated = await this.downloadAttachment(attachment);
|
|
974
|
+
break;
|
|
975
|
+
case exports.AttachmentState.QUEUED_DELETE:
|
|
976
|
+
// `deleteAttachment` needs a context (it removes the row in a
|
|
977
|
+
// transaction); briefly re-acquire the mutex for just this row.
|
|
978
|
+
updated = await this.attachmentService.withContext((ctx) => this.deleteAttachment(attachment, ctx));
|
|
979
|
+
break;
|
|
980
|
+
default:
|
|
981
|
+
continue;
|
|
982
|
+
}
|
|
983
|
+
await this.attachmentService.withContext((ctx) => ctx.saveAttachments([updated]));
|
|
984
|
+
}
|
|
985
|
+
catch (error) {
|
|
986
|
+
this.logger.warn(`Error during sync for ${attachment.id}`, error);
|
|
962
987
|
}
|
|
963
988
|
}
|
|
964
|
-
await context.saveAttachments(updatedAttachments);
|
|
965
989
|
}
|
|
966
990
|
/**
|
|
967
991
|
* Uploads an attachment from local storage to remote storage.
|
|
@@ -1132,6 +1156,19 @@ class AttachmentQueue {
|
|
|
1132
1156
|
statusListenerDispose;
|
|
1133
1157
|
watchActiveAttachments;
|
|
1134
1158
|
watchAttachmentsAbortController;
|
|
1159
|
+
/**
|
|
1160
|
+
* Serializes concurrent `syncStorage()` triggers (periodic timer, watch-onDiff,
|
|
1161
|
+
* status-changed). Held across the whole batch, but only contended by other
|
|
1162
|
+
* sync triggers — foreground `saveFile` / `deleteFile` / watched-attachment
|
|
1163
|
+
* processing don't take this lock and proceed in parallel via the
|
|
1164
|
+
* `AttachmentService` mutex, which is acquired only briefly per row.
|
|
1165
|
+
*/
|
|
1166
|
+
syncLoopMutex = new Mutex();
|
|
1167
|
+
/**
|
|
1168
|
+
* Aborted by `stopSync()` to interrupt an in-flight batch within one
|
|
1169
|
+
* attachment's processing time. Polled between rows by `SyncingService`.
|
|
1170
|
+
*/
|
|
1171
|
+
syncAbortController;
|
|
1135
1172
|
/**
|
|
1136
1173
|
* Creates a new AttachmentQueue instance.
|
|
1137
1174
|
*
|
|
@@ -1171,6 +1208,7 @@ class AttachmentQueue {
|
|
|
1171
1208
|
*/
|
|
1172
1209
|
async startSync() {
|
|
1173
1210
|
await this.stopSync();
|
|
1211
|
+
this.syncAbortController = new AbortController();
|
|
1174
1212
|
this.watchActiveAttachments = this.attachmentService.watchActiveAttachments({
|
|
1175
1213
|
throttleMs: this.syncThrottleDuration
|
|
1176
1214
|
});
|
|
@@ -1285,23 +1323,44 @@ class AttachmentQueue {
|
|
|
1285
1323
|
*
|
|
1286
1324
|
* This is called automatically at regular intervals when sync is started,
|
|
1287
1325
|
* but can also be called manually to trigger an immediate sync.
|
|
1326
|
+
*
|
|
1327
|
+
* Concurrent invocations are serialized via `syncLoopMutex`.
|
|
1288
1328
|
*/
|
|
1289
1329
|
async syncStorage() {
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
await this.
|
|
1295
|
-
|
|
1330
|
+
const signal = this.syncAbortController?.signal;
|
|
1331
|
+
if (signal?.aborted)
|
|
1332
|
+
return;
|
|
1333
|
+
try {
|
|
1334
|
+
await this.syncLoopMutex.runExclusive(async () => {
|
|
1335
|
+
const activeAttachments = await this.attachmentService.withContext((ctx) => ctx.getActiveAttachments());
|
|
1336
|
+
await this.localStorage.initialize();
|
|
1337
|
+
await this.syncingService.processAttachments(activeAttachments, { signal });
|
|
1338
|
+
if (signal?.aborted)
|
|
1339
|
+
return;
|
|
1340
|
+
await this.attachmentService.withContext((ctx) => this.syncingService.deleteArchivedAttachments(ctx));
|
|
1341
|
+
}, signal);
|
|
1342
|
+
}
|
|
1343
|
+
catch (error) {
|
|
1344
|
+
// A queued batch's acquire rejects when `stopSync` aborts — expected, not an error.
|
|
1345
|
+
if (signal?.aborted)
|
|
1346
|
+
return;
|
|
1347
|
+
throw error;
|
|
1348
|
+
}
|
|
1296
1349
|
}
|
|
1297
1350
|
/**
|
|
1298
1351
|
* Stops the attachment synchronization process.
|
|
1299
1352
|
*
|
|
1300
|
-
* Clears the periodic sync timer
|
|
1353
|
+
* Clears the periodic sync timer, closes all active attachment watchers, and
|
|
1354
|
+
* aborts any in-flight `syncStorage()` call so it exits within one
|
|
1355
|
+
* attachment's processing time instead of running the batch to completion.
|
|
1301
1356
|
*/
|
|
1302
1357
|
async stopSync() {
|
|
1303
1358
|
clearInterval(this.periodicSyncTimer);
|
|
1304
1359
|
this.periodicSyncTimer = undefined;
|
|
1360
|
+
if (this.syncAbortController) {
|
|
1361
|
+
this.syncAbortController.abort();
|
|
1362
|
+
this.syncAbortController = undefined;
|
|
1363
|
+
}
|
|
1305
1364
|
if (this.watchActiveAttachments)
|
|
1306
1365
|
await this.watchActiveAttachments.close();
|
|
1307
1366
|
if (this.watchAttachmentsAbortController) {
|
|
@@ -2137,11 +2196,7 @@ class SyncStatus {
|
|
|
2137
2196
|
*/
|
|
2138
2197
|
const replacer = (_, value) => {
|
|
2139
2198
|
if (value instanceof Error) {
|
|
2140
|
-
return
|
|
2141
|
-
name: value.name,
|
|
2142
|
-
message: value.message,
|
|
2143
|
-
stack: value.stack
|
|
2144
|
-
};
|
|
2199
|
+
return this.serializeError(value);
|
|
2145
2200
|
}
|
|
2146
2201
|
return value;
|
|
2147
2202
|
};
|
|
@@ -2184,11 +2239,18 @@ class SyncStatus {
|
|
|
2184
2239
|
if (typeof error == 'undefined') {
|
|
2185
2240
|
return undefined;
|
|
2186
2241
|
}
|
|
2187
|
-
|
|
2242
|
+
const serialized = {
|
|
2188
2243
|
name: error.name,
|
|
2189
2244
|
message: error.message,
|
|
2190
2245
|
stack: error.stack
|
|
2191
2246
|
};
|
|
2247
|
+
// `Error.cause` can be any value (the spec types it as unknown). Preserve it
|
|
2248
|
+
// so consumers reading uploadError/downloadError keep the failure context.
|
|
2249
|
+
// Recurse for Error causes so the whole chain is flattened the same way.
|
|
2250
|
+
if (typeof error.cause != 'undefined') {
|
|
2251
|
+
serialized.cause = error.cause instanceof Error ? this.serializeError(error.cause) : error.cause;
|
|
2252
|
+
}
|
|
2253
|
+
return serialized;
|
|
2192
2254
|
}
|
|
2193
2255
|
static comparePriorities(a, b) {
|
|
2194
2256
|
return b.priority - a.priority; // Reverse because higher priorities have lower numbers
|
|
@@ -10913,7 +10975,7 @@ function requireDist () {
|
|
|
10913
10975
|
|
|
10914
10976
|
var distExports = requireDist();
|
|
10915
10977
|
|
|
10916
|
-
var version = "1.
|
|
10978
|
+
var version = "1.57.0";
|
|
10917
10979
|
var PACKAGE = {
|
|
10918
10980
|
version: version};
|
|
10919
10981
|
|
|
@@ -13044,7 +13106,7 @@ class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
13044
13106
|
this.logger.warn('Schema validation failed. Unexpected behaviour could occur', ex);
|
|
13045
13107
|
}
|
|
13046
13108
|
this._schema = schema;
|
|
13047
|
-
await this.database.execute('SELECT powersync_replace_schema(?)', [JSON.stringify(this.schema.toJSON())]);
|
|
13109
|
+
await this.database.writeTransaction((tx) => tx.execute('SELECT powersync_replace_schema(?)', [JSON.stringify(this.schema.toJSON())]));
|
|
13048
13110
|
await this.database.refreshSchema();
|
|
13049
13111
|
this.iterateListeners(async (cb) => cb.schemaChanged?.(schema));
|
|
13050
13112
|
}
|
|
@@ -14241,6 +14303,7 @@ const parseQuery = (query, parameters) => {
|
|
|
14241
14303
|
};
|
|
14242
14304
|
|
|
14243
14305
|
exports.ATTACHMENT_TABLE = ATTACHMENT_TABLE;
|
|
14306
|
+
exports.ATTACHMENT_TABLE_COLUMNS = ATTACHMENT_TABLE_COLUMNS;
|
|
14244
14307
|
exports.AbortOperation = AbortOperation;
|
|
14245
14308
|
exports.AbstractPowerSyncDatabase = AbstractPowerSyncDatabase;
|
|
14246
14309
|
exports.AbstractPowerSyncDatabaseOpenFactory = AbstractPowerSyncDatabaseOpenFactory;
|