@powersync/web 1.38.4 → 1.38.6
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 +108 -45
- package/dist/worker/SharedSyncImplementation.umd.js.map +1 -1
- package/dist/worker/WASQLiteDB.umd.js +107 -44
- package/dist/worker/WASQLiteDB.umd.js.map +1 -1
- package/lib/package.json +2 -2
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
|
@@ -1641,6 +1641,7 @@ class WorkerClient {
|
|
|
1641
1641
|
__webpack_require__.r(__webpack_exports__);
|
|
1642
1642
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
1643
1643
|
/* harmony export */ ATTACHMENT_TABLE: () => (/* binding */ ATTACHMENT_TABLE),
|
|
1644
|
+
/* harmony export */ ATTACHMENT_TABLE_COLUMNS: () => (/* binding */ ATTACHMENT_TABLE_COLUMNS),
|
|
1644
1645
|
/* harmony export */ AbortOperation: () => (/* binding */ AbortOperation),
|
|
1645
1646
|
/* harmony export */ AbstractPowerSyncDatabase: () => (/* binding */ AbstractPowerSyncDatabase),
|
|
1646
1647
|
/* harmony export */ AbstractPowerSyncDatabaseOpenFactory: () => (/* binding */ AbstractPowerSyncDatabaseOpenFactory),
|
|
@@ -2122,6 +2123,19 @@ var AttachmentState;
|
|
|
2122
2123
|
AttachmentState[AttachmentState["SYNCED"] = 3] = "SYNCED";
|
|
2123
2124
|
AttachmentState[AttachmentState["ARCHIVED"] = 4] = "ARCHIVED"; // Attachment has been orphaned, i.e. the associated record has been deleted
|
|
2124
2125
|
})(AttachmentState || (AttachmentState = {}));
|
|
2126
|
+
/**
|
|
2127
|
+
* @alpha
|
|
2128
|
+
*/
|
|
2129
|
+
const ATTACHMENT_TABLE_COLUMNS = {
|
|
2130
|
+
filename: column.text,
|
|
2131
|
+
local_uri: column.text,
|
|
2132
|
+
timestamp: column.integer,
|
|
2133
|
+
size: column.integer,
|
|
2134
|
+
media_type: column.text,
|
|
2135
|
+
state: column.integer, // Corresponds to AttachmentState
|
|
2136
|
+
has_synced: column.integer,
|
|
2137
|
+
meta_data: column.text
|
|
2138
|
+
};
|
|
2125
2139
|
/**
|
|
2126
2140
|
* AttachmentTable defines the schema for the attachment queue table.
|
|
2127
2141
|
*
|
|
@@ -2129,16 +2143,7 @@ var AttachmentState;
|
|
|
2129
2143
|
*/
|
|
2130
2144
|
class AttachmentTable extends Table {
|
|
2131
2145
|
constructor(options) {
|
|
2132
|
-
super({
|
|
2133
|
-
filename: column.text,
|
|
2134
|
-
local_uri: column.text,
|
|
2135
|
-
timestamp: column.integer,
|
|
2136
|
-
size: column.integer,
|
|
2137
|
-
media_type: column.text,
|
|
2138
|
-
state: column.integer, // Corresponds to AttachmentState
|
|
2139
|
-
has_synced: column.integer,
|
|
2140
|
-
meta_data: column.text
|
|
2141
|
-
}, {
|
|
2146
|
+
super(ATTACHMENT_TABLE_COLUMNS, {
|
|
2142
2147
|
...options,
|
|
2143
2148
|
viewName: options?.viewName ?? ATTACHMENT_TABLE,
|
|
2144
2149
|
localOnly: true,
|
|
@@ -2670,31 +2675,51 @@ class SyncingService {
|
|
|
2670
2675
|
}
|
|
2671
2676
|
/**
|
|
2672
2677
|
* Processes attachments based on their state (upload, download, or delete).
|
|
2673
|
-
*
|
|
2678
|
+
*
|
|
2679
|
+
* Each attachment's I/O runs outside the attachment-service mutex, and the row's
|
|
2680
|
+
* state transition is persisted immediately after it completes. This keeps the
|
|
2681
|
+
* mutex available to concurrent `saveFile` / `deleteFile` / watched-attachment
|
|
2682
|
+
* processing while a batch is in flight, and means consumer queries against the
|
|
2683
|
+
* attachments queue see incremental progress instead of one atomic commit at the
|
|
2684
|
+
* end of the batch.
|
|
2674
2685
|
*
|
|
2675
2686
|
* @param attachments - Array of attachment records to process
|
|
2676
|
-
* @param
|
|
2677
|
-
*
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2687
|
+
* @param options - Optional controls. Pass `signal` (an `AbortSignal`) to interrupt
|
|
2688
|
+
* the batch: it is checked between attachments and, once aborted, the
|
|
2689
|
+
* loop exits early — letting `stopSync` stop a running batch within
|
|
2690
|
+
* one attachment's processing time.
|
|
2691
|
+
*/
|
|
2692
|
+
async processAttachments(attachments, options) {
|
|
2693
|
+
const signal = options?.signal;
|
|
2694
|
+
this.logger.info(`Starting processAttachments with ${attachments.length} attachments`);
|
|
2681
2695
|
for (const attachment of attachments) {
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2696
|
+
if (signal?.aborted) {
|
|
2697
|
+
this.logger.info('Sync cancelled; stopping iteration early');
|
|
2698
|
+
return;
|
|
2699
|
+
}
|
|
2700
|
+
try {
|
|
2701
|
+
let updated;
|
|
2702
|
+
switch (attachment.state) {
|
|
2703
|
+
case AttachmentState.QUEUED_UPLOAD:
|
|
2704
|
+
updated = await this.uploadAttachment(attachment);
|
|
2705
|
+
break;
|
|
2706
|
+
case AttachmentState.QUEUED_DOWNLOAD:
|
|
2707
|
+
updated = await this.downloadAttachment(attachment);
|
|
2708
|
+
break;
|
|
2709
|
+
case AttachmentState.QUEUED_DELETE:
|
|
2710
|
+
// `deleteAttachment` needs a context (it removes the row in a
|
|
2711
|
+
// transaction); briefly re-acquire the mutex for just this row.
|
|
2712
|
+
updated = await this.attachmentService.withContext((ctx) => this.deleteAttachment(attachment, ctx));
|
|
2713
|
+
break;
|
|
2714
|
+
default:
|
|
2715
|
+
continue;
|
|
2716
|
+
}
|
|
2717
|
+
await this.attachmentService.withContext((ctx) => ctx.saveAttachments([updated]));
|
|
2718
|
+
}
|
|
2719
|
+
catch (error) {
|
|
2720
|
+
this.logger.warn(`Error during sync for ${attachment.id}`, error);
|
|
2695
2721
|
}
|
|
2696
2722
|
}
|
|
2697
|
-
await context.saveAttachments(updatedAttachments);
|
|
2698
2723
|
}
|
|
2699
2724
|
/**
|
|
2700
2725
|
* Uploads an attachment from local storage to remote storage.
|
|
@@ -2865,6 +2890,19 @@ class AttachmentQueue {
|
|
|
2865
2890
|
statusListenerDispose;
|
|
2866
2891
|
watchActiveAttachments;
|
|
2867
2892
|
watchAttachmentsAbortController;
|
|
2893
|
+
/**
|
|
2894
|
+
* Serializes concurrent `syncStorage()` triggers (periodic timer, watch-onDiff,
|
|
2895
|
+
* status-changed). Held across the whole batch, but only contended by other
|
|
2896
|
+
* sync triggers — foreground `saveFile` / `deleteFile` / watched-attachment
|
|
2897
|
+
* processing don't take this lock and proceed in parallel via the
|
|
2898
|
+
* `AttachmentService` mutex, which is acquired only briefly per row.
|
|
2899
|
+
*/
|
|
2900
|
+
syncLoopMutex = new Mutex();
|
|
2901
|
+
/**
|
|
2902
|
+
* Aborted by `stopSync()` to interrupt an in-flight batch within one
|
|
2903
|
+
* attachment's processing time. Polled between rows by `SyncingService`.
|
|
2904
|
+
*/
|
|
2905
|
+
syncAbortController;
|
|
2868
2906
|
/**
|
|
2869
2907
|
* Creates a new AttachmentQueue instance.
|
|
2870
2908
|
*
|
|
@@ -2904,6 +2942,7 @@ class AttachmentQueue {
|
|
|
2904
2942
|
*/
|
|
2905
2943
|
async startSync() {
|
|
2906
2944
|
await this.stopSync();
|
|
2945
|
+
this.syncAbortController = new AbortController();
|
|
2907
2946
|
this.watchActiveAttachments = this.attachmentService.watchActiveAttachments({
|
|
2908
2947
|
throttleMs: this.syncThrottleDuration
|
|
2909
2948
|
});
|
|
@@ -3018,23 +3057,44 @@ class AttachmentQueue {
|
|
|
3018
3057
|
*
|
|
3019
3058
|
* This is called automatically at regular intervals when sync is started,
|
|
3020
3059
|
* but can also be called manually to trigger an immediate sync.
|
|
3060
|
+
*
|
|
3061
|
+
* Concurrent invocations are serialized via `syncLoopMutex`.
|
|
3021
3062
|
*/
|
|
3022
3063
|
async syncStorage() {
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
await this.
|
|
3028
|
-
|
|
3064
|
+
const signal = this.syncAbortController?.signal;
|
|
3065
|
+
if (signal?.aborted)
|
|
3066
|
+
return;
|
|
3067
|
+
try {
|
|
3068
|
+
await this.syncLoopMutex.runExclusive(async () => {
|
|
3069
|
+
const activeAttachments = await this.attachmentService.withContext((ctx) => ctx.getActiveAttachments());
|
|
3070
|
+
await this.localStorage.initialize();
|
|
3071
|
+
await this.syncingService.processAttachments(activeAttachments, { signal });
|
|
3072
|
+
if (signal?.aborted)
|
|
3073
|
+
return;
|
|
3074
|
+
await this.attachmentService.withContext((ctx) => this.syncingService.deleteArchivedAttachments(ctx));
|
|
3075
|
+
}, signal);
|
|
3076
|
+
}
|
|
3077
|
+
catch (error) {
|
|
3078
|
+
// A queued batch's acquire rejects when `stopSync` aborts — expected, not an error.
|
|
3079
|
+
if (signal?.aborted)
|
|
3080
|
+
return;
|
|
3081
|
+
throw error;
|
|
3082
|
+
}
|
|
3029
3083
|
}
|
|
3030
3084
|
/**
|
|
3031
3085
|
* Stops the attachment synchronization process.
|
|
3032
3086
|
*
|
|
3033
|
-
* Clears the periodic sync timer
|
|
3087
|
+
* Clears the periodic sync timer, closes all active attachment watchers, and
|
|
3088
|
+
* aborts any in-flight `syncStorage()` call so it exits within one
|
|
3089
|
+
* attachment's processing time instead of running the batch to completion.
|
|
3034
3090
|
*/
|
|
3035
3091
|
async stopSync() {
|
|
3036
3092
|
clearInterval(this.periodicSyncTimer);
|
|
3037
3093
|
this.periodicSyncTimer = undefined;
|
|
3094
|
+
if (this.syncAbortController) {
|
|
3095
|
+
this.syncAbortController.abort();
|
|
3096
|
+
this.syncAbortController = undefined;
|
|
3097
|
+
}
|
|
3038
3098
|
if (this.watchActiveAttachments)
|
|
3039
3099
|
await this.watchActiveAttachments.close();
|
|
3040
3100
|
if (this.watchAttachmentsAbortController) {
|
|
@@ -3870,11 +3930,7 @@ class SyncStatus {
|
|
|
3870
3930
|
*/
|
|
3871
3931
|
const replacer = (_, value) => {
|
|
3872
3932
|
if (value instanceof Error) {
|
|
3873
|
-
return
|
|
3874
|
-
name: value.name,
|
|
3875
|
-
message: value.message,
|
|
3876
|
-
stack: value.stack
|
|
3877
|
-
};
|
|
3933
|
+
return this.serializeError(value);
|
|
3878
3934
|
}
|
|
3879
3935
|
return value;
|
|
3880
3936
|
};
|
|
@@ -3917,11 +3973,18 @@ class SyncStatus {
|
|
|
3917
3973
|
if (typeof error == 'undefined') {
|
|
3918
3974
|
return undefined;
|
|
3919
3975
|
}
|
|
3920
|
-
|
|
3976
|
+
const serialized = {
|
|
3921
3977
|
name: error.name,
|
|
3922
3978
|
message: error.message,
|
|
3923
3979
|
stack: error.stack
|
|
3924
3980
|
};
|
|
3981
|
+
// `Error.cause` can be any value (the spec types it as unknown). Preserve it
|
|
3982
|
+
// so consumers reading uploadError/downloadError keep the failure context.
|
|
3983
|
+
// Recurse for Error causes so the whole chain is flattened the same way.
|
|
3984
|
+
if (typeof error.cause != 'undefined') {
|
|
3985
|
+
serialized.cause = error.cause instanceof Error ? this.serializeError(error.cause) : error.cause;
|
|
3986
|
+
}
|
|
3987
|
+
return serialized;
|
|
3925
3988
|
}
|
|
3926
3989
|
static comparePriorities(a, b) {
|
|
3927
3990
|
return b.priority - a.priority; // Reverse because higher priorities have lower numbers
|
|
@@ -12646,7 +12709,7 @@ function requireDist () {
|
|
|
12646
12709
|
|
|
12647
12710
|
var distExports = requireDist();
|
|
12648
12711
|
|
|
12649
|
-
var version = "1.
|
|
12712
|
+
var version = "1.57.0";
|
|
12650
12713
|
var PACKAGE = {
|
|
12651
12714
|
version: version};
|
|
12652
12715
|
|
|
@@ -14777,7 +14840,7 @@ class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
14777
14840
|
this.logger.warn('Schema validation failed. Unexpected behaviour could occur', ex);
|
|
14778
14841
|
}
|
|
14779
14842
|
this._schema = schema;
|
|
14780
|
-
await this.database.execute('SELECT powersync_replace_schema(?)', [JSON.stringify(this.schema.toJSON())]);
|
|
14843
|
+
await this.database.writeTransaction((tx) => tx.execute('SELECT powersync_replace_schema(?)', [JSON.stringify(this.schema.toJSON())]));
|
|
14781
14844
|
await this.database.refreshSchema();
|
|
14782
14845
|
this.iterateListeners(async (cb) => cb.schemaChanged?.(schema));
|
|
14783
14846
|
}
|