@powersync/common 0.0.0-dev-20260414110516 → 0.0.0-dev-20260504100448
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 +33 -665
- package/dist/bundle.cjs.map +1 -1
- package/dist/bundle.mjs +34 -654
- package/dist/bundle.mjs.map +1 -1
- package/dist/bundle.node.cjs +33 -665
- package/dist/bundle.node.cjs.map +1 -1
- package/dist/bundle.node.mjs +34 -654
- package/dist/bundle.node.mjs.map +1 -1
- package/dist/index.d.cts +22 -369
- package/legacy/sync_protocol.d.ts +103 -0
- package/lib/client/sync/bucket/BucketStorageAdapter.d.ts +1 -63
- package/lib/client/sync/bucket/BucketStorageAdapter.js.map +1 -1
- package/lib/client/sync/bucket/SqliteBucketStorage.d.ts +1 -28
- package/lib/client/sync/bucket/SqliteBucketStorage.js +0 -162
- package/lib/client/sync/bucket/SqliteBucketStorage.js.map +1 -1
- package/lib/client/sync/stream/AbstractRemote.d.ts +2 -12
- package/lib/client/sync/stream/AbstractRemote.js +3 -13
- package/lib/client/sync/stream/AbstractRemote.js.map +1 -1
- package/lib/client/sync/stream/AbstractStreamingSyncImplementation.d.ts +12 -35
- package/lib/client/sync/stream/AbstractStreamingSyncImplementation.js +29 -337
- package/lib/client/sync/stream/AbstractStreamingSyncImplementation.js.map +1 -1
- package/lib/client/sync/stream/JsonValue.d.ts +7 -0
- package/lib/client/sync/stream/JsonValue.js +2 -0
- package/lib/client/sync/stream/JsonValue.js.map +1 -0
- package/lib/client/sync/stream/core-instruction.d.ts +1 -2
- package/lib/client/sync/stream/core-instruction.js.map +1 -1
- package/lib/db/crud/SyncStatus.d.ts +0 -4
- package/lib/db/crud/SyncStatus.js +0 -4
- package/lib/db/crud/SyncStatus.js.map +1 -1
- package/lib/db/schema/RawTable.d.ts +0 -5
- package/lib/db/schema/Schema.d.ts +0 -2
- package/lib/db/schema/Schema.js +0 -2
- package/lib/db/schema/Schema.js.map +1 -1
- package/lib/index.d.ts +1 -5
- package/lib/index.js +1 -5
- package/lib/index.js.map +1 -1
- package/package.json +7 -4
- package/src/client/sync/bucket/BucketStorageAdapter.ts +1 -70
- package/src/client/sync/bucket/SqliteBucketStorage.ts +1 -197
- package/src/client/sync/stream/AbstractRemote.ts +5 -27
- package/src/client/sync/stream/AbstractStreamingSyncImplementation.ts +41 -407
- package/src/client/sync/stream/JsonValue.ts +8 -0
- package/src/client/sync/stream/core-instruction.ts +1 -2
- package/src/db/crud/SyncStatus.ts +0 -4
- package/src/db/schema/RawTable.ts +0 -5
- package/src/db/schema/Schema.ts +0 -2
- package/src/index.ts +1 -5
- package/lib/client/sync/bucket/OpType.d.ts +0 -16
- package/lib/client/sync/bucket/OpType.js +0 -23
- package/lib/client/sync/bucket/OpType.js.map +0 -1
- package/lib/client/sync/bucket/OplogEntry.d.ts +0 -23
- package/lib/client/sync/bucket/OplogEntry.js +0 -36
- package/lib/client/sync/bucket/OplogEntry.js.map +0 -1
- package/lib/client/sync/bucket/SyncDataBatch.d.ts +0 -6
- package/lib/client/sync/bucket/SyncDataBatch.js +0 -12
- package/lib/client/sync/bucket/SyncDataBatch.js.map +0 -1
- package/lib/client/sync/bucket/SyncDataBucket.d.ts +0 -40
- package/lib/client/sync/bucket/SyncDataBucket.js +0 -40
- package/lib/client/sync/bucket/SyncDataBucket.js.map +0 -1
- package/lib/client/sync/stream/streaming-sync-types.d.ts +0 -143
- package/lib/client/sync/stream/streaming-sync-types.js +0 -26
- package/lib/client/sync/stream/streaming-sync-types.js.map +0 -1
- package/src/client/sync/bucket/OpType.ts +0 -23
- package/src/client/sync/bucket/OplogEntry.ts +0 -50
- package/src/client/sync/bucket/SyncDataBatch.ts +0 -11
- package/src/client/sync/bucket/SyncDataBucket.ts +0 -49
- package/src/client/sync/stream/streaming-sync-types.ts +0 -210
|
@@ -3,23 +3,16 @@ import { DBAdapter, extractTableUpdates, Transaction } from '../../../db/DBAdapt
|
|
|
3
3
|
import { BaseObserver } from '../../../utils/BaseObserver.js';
|
|
4
4
|
import { MAX_OP_ID } from '../../constants.js';
|
|
5
5
|
import {
|
|
6
|
-
BucketChecksum,
|
|
7
|
-
BucketOperationProgress,
|
|
8
|
-
BucketState,
|
|
9
6
|
BucketStorageAdapter,
|
|
10
7
|
BucketStorageListener,
|
|
11
|
-
Checkpoint,
|
|
12
8
|
PowerSyncControlCommand,
|
|
13
|
-
PSInternalTable
|
|
14
|
-
SyncLocalDatabaseResult
|
|
9
|
+
PSInternalTable
|
|
15
10
|
} from './BucketStorageAdapter.js';
|
|
16
11
|
import { CrudBatch } from './CrudBatch.js';
|
|
17
12
|
import { CrudEntry, CrudEntryJSON } from './CrudEntry.js';
|
|
18
|
-
import { SyncDataBatch } from './SyncDataBatch.js';
|
|
19
13
|
|
|
20
14
|
export class SqliteBucketStorage extends BaseObserver<BucketStorageListener> implements BucketStorageAdapter {
|
|
21
15
|
public tableNames: Set<string>;
|
|
22
|
-
private _hasCompletedSync: boolean;
|
|
23
16
|
private updateListener: () => void;
|
|
24
17
|
private _clientId?: Promise<string>;
|
|
25
18
|
|
|
@@ -28,7 +21,6 @@ export class SqliteBucketStorage extends BaseObserver<BucketStorageListener> imp
|
|
|
28
21
|
private logger: ILogger = Logger.get('SqliteBucketStorage')
|
|
29
22
|
) {
|
|
30
23
|
super();
|
|
31
|
-
this._hasCompletedSync = false;
|
|
32
24
|
this.tableNames = new Set();
|
|
33
25
|
this.updateListener = db.registerListener({
|
|
34
26
|
tablesUpdated: (update) => {
|
|
@@ -41,7 +33,6 @@ export class SqliteBucketStorage extends BaseObserver<BucketStorageListener> imp
|
|
|
41
33
|
}
|
|
42
34
|
|
|
43
35
|
async init() {
|
|
44
|
-
this._hasCompletedSync = false;
|
|
45
36
|
const existingTableRows = await this.db.getAll<{ name: string }>(
|
|
46
37
|
`SELECT name FROM sqlite_master WHERE type='table' AND name GLOB 'ps_data_*'`
|
|
47
38
|
);
|
|
@@ -70,182 +61,6 @@ export class SqliteBucketStorage extends BaseObserver<BucketStorageListener> imp
|
|
|
70
61
|
return MAX_OP_ID;
|
|
71
62
|
}
|
|
72
63
|
|
|
73
|
-
/**
|
|
74
|
-
* Reset any caches.
|
|
75
|
-
*/
|
|
76
|
-
startSession(): void {}
|
|
77
|
-
|
|
78
|
-
async getBucketStates(): Promise<BucketState[]> {
|
|
79
|
-
const result = await this.db.getAll<BucketState>(
|
|
80
|
-
"SELECT name as bucket, cast(last_op as TEXT) as op_id FROM ps_buckets WHERE pending_delete = 0 AND name != '$local'"
|
|
81
|
-
);
|
|
82
|
-
return result;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
async getBucketOperationProgress(): Promise<BucketOperationProgress> {
|
|
86
|
-
const rows = await this.db.getAll<{ name: string; count_at_last: number; count_since_last: number }>(
|
|
87
|
-
'SELECT name, count_at_last, count_since_last FROM ps_buckets'
|
|
88
|
-
);
|
|
89
|
-
return Object.fromEntries(rows.map((r) => [r.name, { atLast: r.count_at_last, sinceLast: r.count_since_last }]));
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
async saveSyncData(batch: SyncDataBatch, fixedKeyFormat: boolean = false) {
|
|
93
|
-
await this.writeTransaction(async (tx) => {
|
|
94
|
-
for (const b of batch.buckets) {
|
|
95
|
-
await tx.execute('INSERT INTO powersync_operations(op, data) VALUES(?, ?)', [
|
|
96
|
-
'save',
|
|
97
|
-
JSON.stringify({ buckets: [b.toJSON(fixedKeyFormat)] })
|
|
98
|
-
]);
|
|
99
|
-
this.logger.debug(`Saved batch of data for bucket: ${b.bucket}, operations: ${b.data.length}`);
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
async removeBuckets(buckets: string[]): Promise<void> {
|
|
105
|
-
for (const bucket of buckets) {
|
|
106
|
-
await this.deleteBucket(bucket);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Mark a bucket for deletion.
|
|
112
|
-
*/
|
|
113
|
-
private async deleteBucket(bucket: string) {
|
|
114
|
-
await this.writeTransaction(async (tx) => {
|
|
115
|
-
await tx.execute('INSERT INTO powersync_operations(op, data) VALUES(?, ?)', ['delete_bucket', bucket]);
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
this.logger.debug(`Done deleting bucket ${bucket}`);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
async hasCompletedSync() {
|
|
122
|
-
if (this._hasCompletedSync) {
|
|
123
|
-
return true;
|
|
124
|
-
}
|
|
125
|
-
const r = await this.db.get<{ synced_at: string | null }>(`SELECT powersync_last_synced_at() as synced_at`);
|
|
126
|
-
const completed = r.synced_at != null;
|
|
127
|
-
if (completed) {
|
|
128
|
-
this._hasCompletedSync = true;
|
|
129
|
-
}
|
|
130
|
-
return completed;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
async syncLocalDatabase(checkpoint: Checkpoint, priority?: number): Promise<SyncLocalDatabaseResult> {
|
|
134
|
-
const r = await this.validateChecksums(checkpoint, priority);
|
|
135
|
-
if (!r.checkpointValid) {
|
|
136
|
-
this.logger.error('Checksums failed for', r.checkpointFailures);
|
|
137
|
-
for (const b of r.checkpointFailures ?? []) {
|
|
138
|
-
await this.deleteBucket(b);
|
|
139
|
-
}
|
|
140
|
-
return { ready: false, checkpointValid: false, checkpointFailures: r.checkpointFailures };
|
|
141
|
-
}
|
|
142
|
-
if (priority == null) {
|
|
143
|
-
this.logger.debug(`Validated checksums checkpoint ${checkpoint.last_op_id}`);
|
|
144
|
-
} else {
|
|
145
|
-
this.logger.debug(`Validated checksums for partial checkpoint ${checkpoint.last_op_id}, priority ${priority}`);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
let buckets = checkpoint.buckets;
|
|
149
|
-
if (priority !== undefined) {
|
|
150
|
-
buckets = buckets.filter((b) => hasMatchingPriority(priority, b));
|
|
151
|
-
}
|
|
152
|
-
const bucketNames = buckets.map((b) => b.bucket);
|
|
153
|
-
await this.writeTransaction(async (tx) => {
|
|
154
|
-
await tx.execute(`UPDATE ps_buckets SET last_op = ? WHERE name IN (SELECT json_each.value FROM json_each(?))`, [
|
|
155
|
-
checkpoint.last_op_id,
|
|
156
|
-
JSON.stringify(bucketNames)
|
|
157
|
-
]);
|
|
158
|
-
|
|
159
|
-
if (priority == null && checkpoint.write_checkpoint) {
|
|
160
|
-
await tx.execute("UPDATE ps_buckets SET last_op = ? WHERE name = '$local'", [checkpoint.write_checkpoint]);
|
|
161
|
-
}
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
const valid = await this.updateObjectsFromBuckets(checkpoint, priority);
|
|
165
|
-
if (!valid) {
|
|
166
|
-
return { ready: false, checkpointValid: true };
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
return {
|
|
170
|
-
ready: true,
|
|
171
|
-
checkpointValid: true
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* Atomically update the local state to the current checkpoint.
|
|
177
|
-
*
|
|
178
|
-
* This includes creating new tables, dropping old tables, and copying data over from the oplog.
|
|
179
|
-
*/
|
|
180
|
-
private async updateObjectsFromBuckets(checkpoint: Checkpoint, priority: number | undefined) {
|
|
181
|
-
let arg = '';
|
|
182
|
-
if (priority !== undefined) {
|
|
183
|
-
const affectedBuckets: string[] = [];
|
|
184
|
-
for (const desc of checkpoint.buckets) {
|
|
185
|
-
if (hasMatchingPriority(priority, desc)) {
|
|
186
|
-
affectedBuckets.push(desc.bucket);
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
arg = JSON.stringify({ priority, buckets: affectedBuckets });
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
return this.writeTransaction(async (tx) => {
|
|
194
|
-
const { insertId: result } = await tx.execute('INSERT INTO powersync_operations(op, data) VALUES(?, ?)', [
|
|
195
|
-
'sync_local',
|
|
196
|
-
arg
|
|
197
|
-
]);
|
|
198
|
-
if (result == 1) {
|
|
199
|
-
if (priority == null) {
|
|
200
|
-
const bucketToCount = Object.fromEntries(checkpoint.buckets.map((b) => [b.bucket, b.count]));
|
|
201
|
-
// The two parameters could be replaced with one, but: https://github.com/powersync-ja/better-sqlite3/pull/6
|
|
202
|
-
const jsonBucketCount = JSON.stringify(bucketToCount);
|
|
203
|
-
await tx.execute(
|
|
204
|
-
"UPDATE ps_buckets SET count_since_last = 0, count_at_last = ?->name WHERE name != '$local' AND ?->name IS NOT NULL",
|
|
205
|
-
[jsonBucketCount, jsonBucketCount]
|
|
206
|
-
);
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
return true;
|
|
210
|
-
} else {
|
|
211
|
-
return false;
|
|
212
|
-
}
|
|
213
|
-
});
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
async validateChecksums(checkpoint: Checkpoint, priority: number | undefined): Promise<SyncLocalDatabaseResult> {
|
|
217
|
-
if (priority !== undefined) {
|
|
218
|
-
// Only validate the buckets within the priority we care about
|
|
219
|
-
const newBuckets = checkpoint.buckets.filter((cs) => hasMatchingPriority(priority, cs));
|
|
220
|
-
checkpoint = { ...checkpoint, buckets: newBuckets };
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
const rs = await this.db.execute('SELECT powersync_validate_checkpoint(?) as result', [
|
|
224
|
-
JSON.stringify({ ...checkpoint })
|
|
225
|
-
]);
|
|
226
|
-
|
|
227
|
-
const resultItem = rs.rows?.item(0);
|
|
228
|
-
if (!resultItem) {
|
|
229
|
-
return {
|
|
230
|
-
checkpointValid: false,
|
|
231
|
-
ready: false,
|
|
232
|
-
checkpointFailures: []
|
|
233
|
-
};
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
const result = JSON.parse(resultItem['result']);
|
|
237
|
-
|
|
238
|
-
if (result['valid']) {
|
|
239
|
-
return { ready: true, checkpointValid: true };
|
|
240
|
-
} else {
|
|
241
|
-
return {
|
|
242
|
-
checkpointValid: false,
|
|
243
|
-
ready: false,
|
|
244
|
-
checkpointFailures: result['failed_buckets']
|
|
245
|
-
};
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
64
|
async updateLocalTarget(cb: () => Promise<string>): Promise<boolean> {
|
|
250
65
|
const rs1 = await this.db.getAll(
|
|
251
66
|
"SELECT target_op FROM ps_buckets WHERE name = '$local' AND target_op = CAST(? as INTEGER)",
|
|
@@ -356,13 +171,6 @@ export class SqliteBucketStorage extends BaseObserver<BucketStorageListener> imp
|
|
|
356
171
|
return this.db.writeTransaction(callback, options);
|
|
357
172
|
}
|
|
358
173
|
|
|
359
|
-
/**
|
|
360
|
-
* Set a target checkpoint.
|
|
361
|
-
*/
|
|
362
|
-
async setTargetCheckpoint(checkpoint: Checkpoint) {
|
|
363
|
-
// No-op for now
|
|
364
|
-
}
|
|
365
|
-
|
|
366
174
|
async control(op: PowerSyncControlCommand, payload: string | Uint8Array | ArrayBuffer | null): Promise<string> {
|
|
367
175
|
return await this.writeTransaction(async (tx) => {
|
|
368
176
|
const [[raw]] = await tx.executeRaw('SELECT powersync_control(?, ?)', [op, payload]);
|
|
@@ -389,7 +197,3 @@ export class SqliteBucketStorage extends BaseObserver<BucketStorageListener> imp
|
|
|
389
197
|
|
|
390
198
|
static _subkeyMigrationKey = 'powersync_js_migrated_subkeys';
|
|
391
199
|
}
|
|
392
|
-
|
|
393
|
-
function hasMatchingPriority(priority: number, bucket: BucketChecksum) {
|
|
394
|
-
return bucket.priority != null && bucket.priority <= priority;
|
|
395
|
-
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { BSON } from 'bson';
|
|
2
1
|
import { type fetch } from 'cross-fetch';
|
|
3
2
|
import Logger, { ILogger } from 'js-logger';
|
|
4
3
|
import { Requestable, RSocket, RSocketConnector } from 'rsocket-core';
|
|
@@ -6,7 +5,6 @@ import PACKAGE from '../../../../package.json' with { type: 'json' };
|
|
|
6
5
|
import { AbortOperation } from '../../../utils/AbortOperation.js';
|
|
7
6
|
import { PowerSyncCredentials } from '../../connection/PowerSyncCredentials.js';
|
|
8
7
|
import { WebsocketClientTransport } from './WebsocketClientTransport.js';
|
|
9
|
-
import { StreamingSyncRequest } from './streaming-sync-types.js';
|
|
10
8
|
import {
|
|
11
9
|
doneResult,
|
|
12
10
|
extractBsonObjects,
|
|
@@ -16,8 +14,6 @@ import {
|
|
|
16
14
|
import { EventIterator } from 'event-iterator';
|
|
17
15
|
import type { Queue } from 'event-iterator/lib/event-iterator.js';
|
|
18
16
|
|
|
19
|
-
export type BSONImplementation = typeof BSON;
|
|
20
|
-
|
|
21
17
|
export type RemoteConnector = {
|
|
22
18
|
fetchCredentials: () => Promise<PowerSyncCredentials | null>;
|
|
23
19
|
invalidateCredentials?: () => void;
|
|
@@ -44,7 +40,7 @@ export const DEFAULT_REMOTE_LOGGER = Logger.get('PowerSyncRemote');
|
|
|
44
40
|
|
|
45
41
|
export type SyncStreamOptions = {
|
|
46
42
|
path: string;
|
|
47
|
-
data:
|
|
43
|
+
data: unknown;
|
|
48
44
|
headers?: Record<string, string>;
|
|
49
45
|
abortSignal: AbortSignal;
|
|
50
46
|
fetchOptions?: Request;
|
|
@@ -265,11 +261,6 @@ export abstract class AbstractRemote {
|
|
|
265
261
|
return res.json();
|
|
266
262
|
}
|
|
267
263
|
|
|
268
|
-
/**
|
|
269
|
-
* Provides a BSON implementation. The import nature of this varies depending on the platform
|
|
270
|
-
*/
|
|
271
|
-
abstract getBSON(): Promise<BSONImplementation>;
|
|
272
|
-
|
|
273
264
|
/**
|
|
274
265
|
* @returns A text decoder decoding UTF-8. This is a method to allow patching it for Hermes which doesn't support the
|
|
275
266
|
* builtin, without forcing us to bundle a polyfill with `@powersync/common`.
|
|
@@ -286,26 +277,13 @@ export abstract class AbstractRemote {
|
|
|
286
277
|
* Returns a data stream of sync line data, fetched via RSocket-over-WebSocket.
|
|
287
278
|
*
|
|
288
279
|
* The only mechanism to abort the returned stream is to use the abort signal in {@link SocketSyncStreamOptions}.
|
|
289
|
-
*
|
|
290
|
-
* @param bson A BSON encoder and decoder. When set, the data stream will be requested with a BSON payload
|
|
291
|
-
* (required for compatibility with older sync services).
|
|
292
280
|
*/
|
|
293
|
-
async socketStreamRaw(
|
|
294
|
-
options: SocketSyncStreamOptions,
|
|
295
|
-
bson?: typeof BSON
|
|
296
|
-
): Promise<SimpleAsyncIterator<Uint8Array>> {
|
|
281
|
+
async socketStreamRaw(options: SocketSyncStreamOptions): Promise<SimpleAsyncIterator<Uint8Array>> {
|
|
297
282
|
const { path, fetchStrategy = FetchStrategy.Buffered } = options;
|
|
298
|
-
const mimeType =
|
|
283
|
+
const mimeType = 'application/json';
|
|
299
284
|
|
|
300
285
|
function toBuffer(js: any): Buffer {
|
|
301
|
-
|
|
302
|
-
if (bson != null) {
|
|
303
|
-
contents = bson.serialize(js);
|
|
304
|
-
} else {
|
|
305
|
-
contents = JSON.stringify(js);
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
return Buffer.from(contents);
|
|
286
|
+
return Buffer.from(JSON.stringify(js));
|
|
309
287
|
}
|
|
310
288
|
|
|
311
289
|
const syncQueueRequestSize = fetchStrategy == FetchStrategy.Buffered ? 10 : 1;
|
|
@@ -575,7 +553,7 @@ export abstract class AbstractRemote {
|
|
|
575
553
|
|
|
576
554
|
const contentType = res.headers.get('content-type');
|
|
577
555
|
responseIsBson = contentType == bson;
|
|
578
|
-
} catch (ex) {
|
|
556
|
+
} catch (ex: any) {
|
|
579
557
|
if (ex.name == 'AbortError') {
|
|
580
558
|
throw new AbortOperation(`Pending fetch request to ${request.url} has been aborted.`);
|
|
581
559
|
}
|