@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.node.mjs
CHANGED
|
@@ -389,6 +389,19 @@ var AttachmentState;
|
|
|
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
|
})(AttachmentState || (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 @@ var AttachmentState;
|
|
|
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 AttachmentState.QUEUED_UPLOAD:
|
|
970
|
+
updated = await this.uploadAttachment(attachment);
|
|
971
|
+
break;
|
|
972
|
+
case AttachmentState.QUEUED_DOWNLOAD:
|
|
973
|
+
updated = await this.downloadAttachment(attachment);
|
|
974
|
+
break;
|
|
975
|
+
case 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
|
|
@@ -8542,7 +8604,7 @@ function requireDist () {
|
|
|
8542
8604
|
|
|
8543
8605
|
var distExports = requireDist();
|
|
8544
8606
|
|
|
8545
|
-
var version = "1.
|
|
8607
|
+
var version = "1.57.0";
|
|
8546
8608
|
var PACKAGE = {
|
|
8547
8609
|
version: version};
|
|
8548
8610
|
|
|
@@ -10673,7 +10735,7 @@ class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
10673
10735
|
this.logger.warn('Schema validation failed. Unexpected behaviour could occur', ex);
|
|
10674
10736
|
}
|
|
10675
10737
|
this._schema = schema;
|
|
10676
|
-
await this.database.execute('SELECT powersync_replace_schema(?)', [JSON.stringify(this.schema.toJSON())]);
|
|
10738
|
+
await this.database.writeTransaction((tx) => tx.execute('SELECT powersync_replace_schema(?)', [JSON.stringify(this.schema.toJSON())]));
|
|
10677
10739
|
await this.database.refreshSchema();
|
|
10678
10740
|
this.iterateListeners(async (cb) => cb.schemaChanged?.(schema));
|
|
10679
10741
|
}
|
|
@@ -11869,5 +11931,5 @@ const parseQuery = (query, parameters) => {
|
|
|
11869
11931
|
return { sqlStatement, parameters: parameters };
|
|
11870
11932
|
};
|
|
11871
11933
|
|
|
11872
|
-
export { ATTACHMENT_TABLE, AbortOperation, AbstractPowerSyncDatabase, AbstractPowerSyncDatabaseOpenFactory, AbstractQueryProcessor, AbstractRemote, AbstractStreamingSyncImplementation, ArrayComparator, AttachmentContext, AttachmentQueue, AttachmentService, AttachmentState, AttachmentTable, BaseObserver, Column, ColumnType, ConnectionClosedError, ConnectionManager, ControlledExecutor, CrudBatch, CrudEntry, CrudTransaction, DBAdapterDefaultMixin, DBGetUtilsDefaultMixin, DEFAULT_CRUD_BATCH_LIMIT, DEFAULT_CRUD_UPLOAD_THROTTLE_MS, DEFAULT_INDEX_COLUMN_OPTIONS, DEFAULT_INDEX_OPTIONS, DEFAULT_LOCK_TIMEOUT_MS, DEFAULT_POWERSYNC_CLOSE_OPTIONS, DEFAULT_POWERSYNC_DB_OPTIONS, DEFAULT_REMOTE_LOGGER, DEFAULT_REMOTE_OPTIONS, DEFAULT_RETRY_DELAY_MS, DEFAULT_ROW_COMPARATOR, DEFAULT_STREAMING_SYNC_OPTIONS, DEFAULT_STREAM_CONNECTION_OPTIONS, DEFAULT_SYNC_CLIENT_IMPLEMENTATION, DEFAULT_TABLE_OPTIONS, DEFAULT_WATCH_QUERY_OPTIONS, DEFAULT_WATCH_THROTTLE_MS, DiffTriggerOperation, DifferentialQueryProcessor, EMPTY_DIFFERENTIAL, EncodingType, FalsyComparator, FetchImplementationProvider, FetchStrategy, GetAllQuery, Index, IndexedColumn, InvalidSQLCharacters, LockType, LogLevel, MAX_AMOUNT_OF_COLUMNS, MAX_OP_ID, MEMORY_TRIGGER_CLAIM_MANAGER, Mutex, OnChangeQueryProcessor, PSInternalTable, PowerSyncControlCommand, RowUpdateType, Schema, Semaphore, SqliteBucketStorage, SyncClientImplementation, SyncProgress, SyncStatus, SyncStreamConnectionMethod, SyncingService, Table, TableV2, TriggerManagerImpl, UpdateType, UploadQueueStats, WatchedQueryListenerEvent, attachmentFromSql, column, compilableQueryWatch, createBaseLogger, createLogger, extractTableUpdates, isBatchedUpdateNotification, isDBAdapter, isPowerSyncDatabaseOptionsWithSettings, isSQLOpenFactory, isSQLOpenOptions, parseQuery, runOnSchemaChange, sanitizeSQL, sanitizeUUID, timeoutSignal };
|
|
11934
|
+
export { ATTACHMENT_TABLE, ATTACHMENT_TABLE_COLUMNS, AbortOperation, AbstractPowerSyncDatabase, AbstractPowerSyncDatabaseOpenFactory, AbstractQueryProcessor, AbstractRemote, AbstractStreamingSyncImplementation, ArrayComparator, AttachmentContext, AttachmentQueue, AttachmentService, AttachmentState, AttachmentTable, BaseObserver, Column, ColumnType, ConnectionClosedError, ConnectionManager, ControlledExecutor, CrudBatch, CrudEntry, CrudTransaction, DBAdapterDefaultMixin, DBGetUtilsDefaultMixin, DEFAULT_CRUD_BATCH_LIMIT, DEFAULT_CRUD_UPLOAD_THROTTLE_MS, DEFAULT_INDEX_COLUMN_OPTIONS, DEFAULT_INDEX_OPTIONS, DEFAULT_LOCK_TIMEOUT_MS, DEFAULT_POWERSYNC_CLOSE_OPTIONS, DEFAULT_POWERSYNC_DB_OPTIONS, DEFAULT_REMOTE_LOGGER, DEFAULT_REMOTE_OPTIONS, DEFAULT_RETRY_DELAY_MS, DEFAULT_ROW_COMPARATOR, DEFAULT_STREAMING_SYNC_OPTIONS, DEFAULT_STREAM_CONNECTION_OPTIONS, DEFAULT_SYNC_CLIENT_IMPLEMENTATION, DEFAULT_TABLE_OPTIONS, DEFAULT_WATCH_QUERY_OPTIONS, DEFAULT_WATCH_THROTTLE_MS, DiffTriggerOperation, DifferentialQueryProcessor, EMPTY_DIFFERENTIAL, EncodingType, FalsyComparator, FetchImplementationProvider, FetchStrategy, GetAllQuery, Index, IndexedColumn, InvalidSQLCharacters, LockType, LogLevel, MAX_AMOUNT_OF_COLUMNS, MAX_OP_ID, MEMORY_TRIGGER_CLAIM_MANAGER, Mutex, OnChangeQueryProcessor, PSInternalTable, PowerSyncControlCommand, RowUpdateType, Schema, Semaphore, SqliteBucketStorage, SyncClientImplementation, SyncProgress, SyncStatus, SyncStreamConnectionMethod, SyncingService, Table, TableV2, TriggerManagerImpl, UpdateType, UploadQueueStats, WatchedQueryListenerEvent, attachmentFromSql, column, compilableQueryWatch, createBaseLogger, createLogger, extractTableUpdates, isBatchedUpdateNotification, isDBAdapter, isPowerSyncDatabaseOptionsWithSettings, isSQLOpenFactory, isSQLOpenOptions, parseQuery, runOnSchemaChange, sanitizeSQL, sanitizeUUID, timeoutSignal };
|
|
11873
11935
|
//# sourceMappingURL=bundle.node.mjs.map
|