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