@powersync/common 0.0.0-dev-20251106124255 → 0.0.0-dev-20251201150812
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 +4560 -184
- package/dist/bundle.cjs.map +1 -1
- package/dist/bundle.mjs +4560 -184
- package/dist/bundle.mjs.map +1 -1
- package/dist/bundle.node.cjs +42 -17
- package/dist/bundle.node.cjs.map +1 -1
- package/dist/bundle.node.mjs +42 -17
- package/dist/bundle.node.mjs.map +1 -1
- package/dist/index.d.cts +226 -162
- package/lib/client/ConnectionManager.d.ts +1 -1
- package/lib/client/ConnectionManager.js.map +1 -1
- package/lib/client/sync/bucket/SqliteBucketStorage.js +2 -2
- package/lib/client/sync/bucket/SqliteBucketStorage.js.map +1 -1
- package/lib/client/sync/stream/AbstractRemote.js +10 -4
- package/lib/client/sync/stream/AbstractRemote.js.map +1 -1
- package/lib/client/sync/stream/AbstractStreamingSyncImplementation.js +4 -4
- package/lib/client/sync/stream/AbstractStreamingSyncImplementation.js.map +1 -1
- package/lib/client/triggers/TriggerManager.d.ts +71 -12
- package/lib/client/triggers/TriggerManagerImpl.js +10 -5
- package/lib/client/triggers/TriggerManagerImpl.js.map +1 -1
- package/lib/db/crud/SyncStatus.d.ts +7 -2
- package/lib/db/crud/SyncStatus.js +15 -1
- package/lib/db/crud/SyncStatus.js.map +1 -1
- package/package.json +2 -2
- package/src/client/ConnectionManager.ts +1 -1
- package/src/client/sync/bucket/SqliteBucketStorage.ts +2 -2
- package/src/client/sync/stream/AbstractRemote.ts +15 -5
- package/src/client/sync/stream/AbstractStreamingSyncImplementation.ts +5 -6
- package/src/client/triggers/TriggerManager.ts +79 -12
- package/src/client/triggers/TriggerManagerImpl.ts +12 -6
- package/src/db/crud/SyncStatus.ts +18 -3
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ILogger } from 'js-logger';
|
|
2
|
+
import { SyncStatus } from '../db/crud/SyncStatus.js';
|
|
2
3
|
import { BaseListener, BaseObserver } from '../utils/BaseObserver.js';
|
|
3
4
|
import { PowerSyncBackendConnector } from './connection/PowerSyncBackendConnector.js';
|
|
4
5
|
import {
|
|
@@ -13,7 +14,6 @@ import {
|
|
|
13
14
|
SyncStreamSubscribeOptions,
|
|
14
15
|
SyncStreamSubscription
|
|
15
16
|
} from './sync/sync-streams.js';
|
|
16
|
-
import { SyncStatus } from '../db/crud/SyncStatus.js';
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* @internal
|
|
@@ -255,7 +255,7 @@ export class SqliteBucketStorage extends BaseObserver<BucketStorageListener> imp
|
|
|
255
255
|
// Nothing to update
|
|
256
256
|
return false;
|
|
257
257
|
}
|
|
258
|
-
const rs = await this.db.getAll<{ seq: number }>("SELECT seq FROM sqlite_sequence WHERE name = 'ps_crud'");
|
|
258
|
+
const rs = await this.db.getAll<{ seq: number }>("SELECT seq FROM main.sqlite_sequence WHERE name = 'ps_crud'");
|
|
259
259
|
if (!rs.length) {
|
|
260
260
|
// Nothing to update
|
|
261
261
|
return false;
|
|
@@ -273,7 +273,7 @@ export class SqliteBucketStorage extends BaseObserver<BucketStorageListener> imp
|
|
|
273
273
|
return false;
|
|
274
274
|
}
|
|
275
275
|
|
|
276
|
-
const rs = await tx.execute("SELECT seq FROM sqlite_sequence WHERE name = 'ps_crud'");
|
|
276
|
+
const rs = await tx.execute("SELECT seq FROM main.sqlite_sequence WHERE name = 'ps_crud'");
|
|
277
277
|
if (!rs.rows?.length) {
|
|
278
278
|
// assert isNotEmpty
|
|
279
279
|
throw new Error('SQLite Sequence should not be empty');
|
|
@@ -6,8 +6,9 @@ import PACKAGE from '../../../../package.json' with { type: 'json' };
|
|
|
6
6
|
import { AbortOperation } from '../../../utils/AbortOperation.js';
|
|
7
7
|
import { DataStream } from '../../../utils/DataStream.js';
|
|
8
8
|
import { PowerSyncCredentials } from '../../connection/PowerSyncCredentials.js';
|
|
9
|
-
import { StreamingSyncRequest } from './streaming-sync-types.js';
|
|
10
9
|
import { WebsocketClientTransport } from './WebsocketClientTransport.js';
|
|
10
|
+
import { StreamingSyncRequest } from './streaming-sync-types.js';
|
|
11
|
+
|
|
11
12
|
|
|
12
13
|
export type BSONImplementation = typeof BSON;
|
|
13
14
|
|
|
@@ -557,9 +558,11 @@ export abstract class AbstractRemote {
|
|
|
557
558
|
// Create a new stream splitting the response at line endings while also handling cancellations
|
|
558
559
|
// by closing the reader.
|
|
559
560
|
const reader = res.body.getReader();
|
|
561
|
+
let readerReleased = false;
|
|
560
562
|
// This will close the network request and read stream
|
|
561
563
|
const closeReader = async () => {
|
|
562
564
|
try {
|
|
565
|
+
readerReleased = true;
|
|
563
566
|
await reader.cancel();
|
|
564
567
|
} catch (ex) {
|
|
565
568
|
// an error will throw if the reader hasn't been used yet
|
|
@@ -567,20 +570,27 @@ export abstract class AbstractRemote {
|
|
|
567
570
|
reader.releaseLock();
|
|
568
571
|
};
|
|
569
572
|
|
|
573
|
+
|
|
574
|
+
const stream = new DataStream<T, string>({
|
|
575
|
+
logger: this.logger,
|
|
576
|
+
mapLine: mapLine
|
|
577
|
+
});
|
|
578
|
+
|
|
570
579
|
abortSignal?.addEventListener('abort', () => {
|
|
571
580
|
closeReader();
|
|
581
|
+
stream.close();
|
|
572
582
|
});
|
|
573
583
|
|
|
574
584
|
const decoder = this.createTextDecoder();
|
|
575
585
|
let buffer = '';
|
|
576
586
|
|
|
577
|
-
|
|
578
|
-
logger: this.logger,
|
|
579
|
-
mapLine: mapLine
|
|
580
|
-
});
|
|
587
|
+
|
|
581
588
|
|
|
582
589
|
const l = stream.registerListener({
|
|
583
590
|
lowWater: async () => {
|
|
591
|
+
if (stream.closed || abortSignal?.aborted || readerReleased) {
|
|
592
|
+
return
|
|
593
|
+
}
|
|
584
594
|
try {
|
|
585
595
|
let didCompleteLine = false;
|
|
586
596
|
while (!didCompleteLine) {
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
import { CrudEntry } from '../bucket/CrudEntry.js';
|
|
17
17
|
import { SyncDataBucket } from '../bucket/SyncDataBucket.js';
|
|
18
18
|
import { AbstractRemote, FetchStrategy, SyncStreamOptions } from './AbstractRemote.js';
|
|
19
|
-
import {
|
|
19
|
+
import { EstablishSyncStream, Instruction, coreStatusToJs } from './core-instruction.js';
|
|
20
20
|
import {
|
|
21
21
|
BucketRequest,
|
|
22
22
|
CrudUploadNotification,
|
|
@@ -429,7 +429,7 @@ The next upload iteration will be delayed.`);
|
|
|
429
429
|
uploadError: ex
|
|
430
430
|
}
|
|
431
431
|
});
|
|
432
|
-
await this.delayRetry(controller.signal);
|
|
432
|
+
await this.delayRetry(controller.signal, this.options.crudUploadThrottleMs);
|
|
433
433
|
if (!this.isConnected) {
|
|
434
434
|
// Exit the upload loop if the sync stream is no longer connected
|
|
435
435
|
break;
|
|
@@ -1216,15 +1216,14 @@ The next upload iteration will be delayed.`);
|
|
|
1216
1216
|
this.iterateListeners((cb) => cb.statusUpdated?.(options));
|
|
1217
1217
|
}
|
|
1218
1218
|
|
|
1219
|
-
private async delayRetry(signal?: AbortSignal): Promise<void> {
|
|
1219
|
+
private async delayRetry(signal?: AbortSignal, delayMs?: number): Promise<void> {
|
|
1220
1220
|
return new Promise((resolve) => {
|
|
1221
1221
|
if (signal?.aborted) {
|
|
1222
1222
|
// If the signal is already aborted, resolve immediately
|
|
1223
1223
|
resolve();
|
|
1224
1224
|
return;
|
|
1225
1225
|
}
|
|
1226
|
-
|
|
1227
|
-
const { retryDelayMs } = this.options;
|
|
1226
|
+
const delay = delayMs ?? this.options.retryDelayMs;
|
|
1228
1227
|
|
|
1229
1228
|
let timeoutId: ReturnType<typeof setTimeout> | undefined;
|
|
1230
1229
|
|
|
@@ -1238,7 +1237,7 @@ The next upload iteration will be delayed.`);
|
|
|
1238
1237
|
};
|
|
1239
1238
|
|
|
1240
1239
|
signal?.addEventListener('abort', endDelay, { once: true });
|
|
1241
|
-
timeoutId = setTimeout(endDelay,
|
|
1240
|
+
timeoutId = setTimeout(endDelay, delay);
|
|
1242
1241
|
});
|
|
1243
1242
|
}
|
|
1244
1243
|
|
|
@@ -14,8 +14,11 @@ export enum DiffTriggerOperation {
|
|
|
14
14
|
* @experimental
|
|
15
15
|
* Diffs created by {@link TriggerManager#createDiffTrigger} are stored in a temporary table.
|
|
16
16
|
* This is the base record structure for all diff records.
|
|
17
|
+
*
|
|
18
|
+
* @template TOperationId - The type for `operation_id`. Defaults to `number` as returned by default SQLite database queries.
|
|
19
|
+
* Use `string` for full 64-bit precision when using `{ castOperationIdAsText: true }` option.
|
|
17
20
|
*/
|
|
18
|
-
export interface BaseTriggerDiffRecord {
|
|
21
|
+
export interface BaseTriggerDiffRecord<TOperationId extends string | number = number> {
|
|
19
22
|
/**
|
|
20
23
|
* The modified row's `id` column value.
|
|
21
24
|
*/
|
|
@@ -24,6 +27,13 @@ export interface BaseTriggerDiffRecord {
|
|
|
24
27
|
* The operation performed which created this record.
|
|
25
28
|
*/
|
|
26
29
|
operation: DiffTriggerOperation;
|
|
30
|
+
/**
|
|
31
|
+
* Auto-incrementing primary key for the operation.
|
|
32
|
+
* Defaults to number as returned by database queries (wa-sqlite returns lower 32 bits).
|
|
33
|
+
* Can be string for full 64-bit precision when using `{ castOperationIdAsText: true }` option.
|
|
34
|
+
*/
|
|
35
|
+
operation_id: TOperationId;
|
|
36
|
+
|
|
27
37
|
/**
|
|
28
38
|
* Time the change operation was recorded.
|
|
29
39
|
* This is in ISO 8601 format, e.g. `2023-10-01T12:00:00.000Z`.
|
|
@@ -37,7 +47,8 @@ export interface BaseTriggerDiffRecord {
|
|
|
37
47
|
* This record contains the new value and optionally the previous value.
|
|
38
48
|
* Values are stored as JSON strings.
|
|
39
49
|
*/
|
|
40
|
-
export interface TriggerDiffUpdateRecord extends
|
|
50
|
+
export interface TriggerDiffUpdateRecord<TOperationId extends string | number = number>
|
|
51
|
+
extends BaseTriggerDiffRecord<TOperationId> {
|
|
41
52
|
operation: DiffTriggerOperation.UPDATE;
|
|
42
53
|
/**
|
|
43
54
|
* The updated state of the row in JSON string format.
|
|
@@ -54,7 +65,8 @@ export interface TriggerDiffUpdateRecord extends BaseTriggerDiffRecord {
|
|
|
54
65
|
* Represents a diff record for a SQLite INSERT operation.
|
|
55
66
|
* This record contains the new value represented as a JSON string.
|
|
56
67
|
*/
|
|
57
|
-
export interface TriggerDiffInsertRecord extends
|
|
68
|
+
export interface TriggerDiffInsertRecord<TOperationId extends string | number = number>
|
|
69
|
+
extends BaseTriggerDiffRecord<TOperationId> {
|
|
58
70
|
operation: DiffTriggerOperation.INSERT;
|
|
59
71
|
/**
|
|
60
72
|
* The value of the row, at the time of INSERT, in JSON string format.
|
|
@@ -67,7 +79,8 @@ export interface TriggerDiffInsertRecord extends BaseTriggerDiffRecord {
|
|
|
67
79
|
* Represents a diff record for a SQLite DELETE operation.
|
|
68
80
|
* This record contains the new value represented as a JSON string.
|
|
69
81
|
*/
|
|
70
|
-
export interface TriggerDiffDeleteRecord extends
|
|
82
|
+
export interface TriggerDiffDeleteRecord<TOperationId extends string | number = number>
|
|
83
|
+
extends BaseTriggerDiffRecord<TOperationId> {
|
|
71
84
|
operation: DiffTriggerOperation.DELETE;
|
|
72
85
|
/**
|
|
73
86
|
* The value of the row, before the DELETE operation, in JSON string format.
|
|
@@ -82,27 +95,53 @@ export interface TriggerDiffDeleteRecord extends BaseTriggerDiffRecord {
|
|
|
82
95
|
*
|
|
83
96
|
* Querying the DIFF table directly with {@link TriggerDiffHandlerContext#withDiff} will return records
|
|
84
97
|
* with the structure of this type.
|
|
98
|
+
*
|
|
99
|
+
* @template TOperationId - The type for `operation_id`. Defaults to `number` as returned by database queries.
|
|
100
|
+
* Use `string` for full 64-bit precision when using `{ castOperationIdAsText: true }` option.
|
|
101
|
+
*
|
|
85
102
|
* @example
|
|
86
103
|
* ```typescript
|
|
104
|
+
* // Default: operation_id is number
|
|
87
105
|
* const diffs = await context.withDiff<TriggerDiffRecord>('SELECT * FROM DIFF');
|
|
88
|
-
*
|
|
106
|
+
*
|
|
107
|
+
* // With string operation_id for full precision
|
|
108
|
+
* const diffsWithString = await context.withDiff<TriggerDiffRecord<string>>(
|
|
109
|
+
* 'SELECT * FROM DIFF',
|
|
110
|
+
* undefined,
|
|
111
|
+
* { castOperationIdAsText: true }
|
|
112
|
+
* );
|
|
89
113
|
* ```
|
|
90
114
|
*/
|
|
91
|
-
export type TriggerDiffRecord
|
|
115
|
+
export type TriggerDiffRecord<TOperationId extends string | number = number> =
|
|
116
|
+
| TriggerDiffUpdateRecord<TOperationId>
|
|
117
|
+
| TriggerDiffInsertRecord<TOperationId>
|
|
118
|
+
| TriggerDiffDeleteRecord<TOperationId>;
|
|
92
119
|
|
|
93
120
|
/**
|
|
94
121
|
* @experimental
|
|
95
122
|
* Querying the DIFF table directly with {@link TriggerDiffHandlerContext#withExtractedDiff} will return records
|
|
96
123
|
* with the tracked columns extracted from the JSON value.
|
|
97
124
|
* This type represents the structure of such records.
|
|
125
|
+
*
|
|
126
|
+
* @template T - The type for the extracted columns from the tracked JSON value.
|
|
127
|
+
* @template TOperationId - The type for `operation_id`. Defaults to `number` as returned by database queries.
|
|
128
|
+
* Use `string` for full 64-bit precision when using `{ castOperationIdAsText: true }` option.
|
|
129
|
+
*
|
|
98
130
|
* @example
|
|
99
131
|
* ```typescript
|
|
132
|
+
* // Default: operation_id is number
|
|
100
133
|
* const diffs = await context.withExtractedDiff<ExtractedTriggerDiffRecord<{id: string, name: string}>>('SELECT * FROM DIFF');
|
|
101
|
-
*
|
|
134
|
+
*
|
|
135
|
+
* // With string operation_id for full precision
|
|
136
|
+
* const diffsWithString = await context.withExtractedDiff<ExtractedTriggerDiffRecord<{id: string, name: string}, string>>(
|
|
137
|
+
* 'SELECT * FROM DIFF',
|
|
138
|
+
* undefined,
|
|
139
|
+
* { castOperationIdAsText: true }
|
|
140
|
+
* );
|
|
102
141
|
* ```
|
|
103
142
|
*/
|
|
104
|
-
export type ExtractedTriggerDiffRecord<T> = T & {
|
|
105
|
-
[K in keyof Omit<BaseTriggerDiffRecord
|
|
143
|
+
export type ExtractedTriggerDiffRecord<T, TOperationId extends string | number = number> = T & {
|
|
144
|
+
[K in keyof Omit<BaseTriggerDiffRecord<TOperationId>, 'id'> as `__${string & K}`]: TriggerDiffRecord<TOperationId>[K];
|
|
106
145
|
} & {
|
|
107
146
|
__previous_value?: string;
|
|
108
147
|
};
|
|
@@ -183,6 +222,21 @@ export interface CreateDiffTriggerOptions extends BaseCreateDiffTriggerOptions {
|
|
|
183
222
|
*/
|
|
184
223
|
export type TriggerRemoveCallback = () => Promise<void>;
|
|
185
224
|
|
|
225
|
+
/**
|
|
226
|
+
* @experimental
|
|
227
|
+
* Options for {@link TriggerDiffHandlerContext#withDiff}.
|
|
228
|
+
*/
|
|
229
|
+
export interface WithDiffOptions {
|
|
230
|
+
/**
|
|
231
|
+
* If true, casts `operation_id` as TEXT in the internal CTE to preserve full 64-bit precision.
|
|
232
|
+
* Use this when you need to ensure `operation_id` is treated as a string to avoid precision loss
|
|
233
|
+
* for values exceeding JavaScript's Number.MAX_SAFE_INTEGER.
|
|
234
|
+
*
|
|
235
|
+
* When enabled, use {@link TriggerDiffRecord}<string> to type the result correctly.
|
|
236
|
+
*/
|
|
237
|
+
castOperationIdAsText?: boolean;
|
|
238
|
+
}
|
|
239
|
+
|
|
186
240
|
/**
|
|
187
241
|
* @experimental
|
|
188
242
|
* Context for the `onChange` handler provided to {@link TriggerManager#trackTableDiff}.
|
|
@@ -200,9 +254,10 @@ export interface TriggerDiffHandlerContext extends LockContext {
|
|
|
200
254
|
* The `DIFF` table is of the form described in {@link TriggerManager#createDiffTrigger}
|
|
201
255
|
* ```sql
|
|
202
256
|
* CREATE TEMP DIFF (
|
|
257
|
+
* operation_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
203
258
|
* id TEXT,
|
|
204
259
|
* operation TEXT,
|
|
205
|
-
* timestamp TEXT
|
|
260
|
+
* timestamp TEXT,
|
|
206
261
|
* value TEXT,
|
|
207
262
|
* previous_value TEXT
|
|
208
263
|
* );
|
|
@@ -222,8 +277,19 @@ export interface TriggerDiffHandlerContext extends LockContext {
|
|
|
222
277
|
* JOIN todos ON DIFF.id = todos.id
|
|
223
278
|
* WHERE json_extract(DIFF.value, '$.status') = 'active'
|
|
224
279
|
* ```
|
|
280
|
+
*
|
|
281
|
+
* @example
|
|
282
|
+
* ```typescript
|
|
283
|
+
* // With operation_id cast as TEXT for full precision
|
|
284
|
+
* const diffs = await context.withDiff<TriggerDiffRecord<string>>(
|
|
285
|
+
* 'SELECT * FROM DIFF',
|
|
286
|
+
* undefined,
|
|
287
|
+
* { castOperationIdAsText: true }
|
|
288
|
+
* );
|
|
289
|
+
* // diffs[0].operation_id is now typed as string
|
|
290
|
+
* ```
|
|
225
291
|
*/
|
|
226
|
-
withDiff: <T = any>(query: string, params?: ReadonlyArray<Readonly<any
|
|
292
|
+
withDiff: <T = any>(query: string, params?: ReadonlyArray<Readonly<any>>, options?: WithDiffOptions) => Promise<T[]>;
|
|
227
293
|
|
|
228
294
|
/**
|
|
229
295
|
* Allows querying the database with access to the table containing diff records.
|
|
@@ -292,9 +358,10 @@ export interface TriggerManager {
|
|
|
292
358
|
*
|
|
293
359
|
* ```sql
|
|
294
360
|
* CREATE TEMP TABLE ${destination} (
|
|
361
|
+
* operation_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
295
362
|
* id TEXT,
|
|
296
363
|
* operation TEXT,
|
|
297
|
-
* timestamp TEXT
|
|
364
|
+
* timestamp TEXT,
|
|
298
365
|
* value TEXT,
|
|
299
366
|
* previous_value TEXT
|
|
300
367
|
* );
|
|
@@ -7,7 +7,8 @@ import {
|
|
|
7
7
|
DiffTriggerOperation,
|
|
8
8
|
TrackDiffOptions,
|
|
9
9
|
TriggerManager,
|
|
10
|
-
TriggerRemoveCallback
|
|
10
|
+
TriggerRemoveCallback,
|
|
11
|
+
WithDiffOptions
|
|
11
12
|
} from './TriggerManager.js';
|
|
12
13
|
|
|
13
14
|
export type TriggerManagerImplOptions = {
|
|
@@ -117,6 +118,7 @@ export class TriggerManagerImpl implements TriggerManager {
|
|
|
117
118
|
await hooks?.beforeCreate?.(tx);
|
|
118
119
|
await tx.execute(/* sql */ `
|
|
119
120
|
CREATE TEMP TABLE ${destination} (
|
|
121
|
+
operation_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
120
122
|
id TEXT,
|
|
121
123
|
operation TEXT,
|
|
122
124
|
timestamp TEXT,
|
|
@@ -243,17 +245,20 @@ export class TriggerManagerImpl implements TriggerManager {
|
|
|
243
245
|
const callbackResult = await options.onChange({
|
|
244
246
|
...tx,
|
|
245
247
|
destinationTable: destination,
|
|
246
|
-
withDiff: async <T>(query, params) => {
|
|
248
|
+
withDiff: async <T>(query, params, options?: WithDiffOptions) => {
|
|
247
249
|
// Wrap the query to expose the destination table
|
|
250
|
+
const operationIdSelect = options?.castOperationIdAsText
|
|
251
|
+
? 'id, operation, CAST(operation_id AS TEXT) as operation_id, timestamp, value, previous_value'
|
|
252
|
+
: '*';
|
|
248
253
|
const wrappedQuery = /* sql */ `
|
|
249
254
|
WITH
|
|
250
255
|
DIFF AS (
|
|
251
256
|
SELECT
|
|
252
|
-
|
|
257
|
+
${operationIdSelect}
|
|
253
258
|
FROM
|
|
254
259
|
${destination}
|
|
255
260
|
ORDER BY
|
|
256
|
-
|
|
261
|
+
operation_id ASC
|
|
257
262
|
) ${query}
|
|
258
263
|
`;
|
|
259
264
|
return tx.getAll<T>(wrappedQuery, params);
|
|
@@ -267,13 +272,14 @@ export class TriggerManagerImpl implements TriggerManager {
|
|
|
267
272
|
id,
|
|
268
273
|
${contextColumns.length > 0
|
|
269
274
|
? `${contextColumns.map((col) => `json_extract(value, '$.${col}') as ${col}`).join(', ')},`
|
|
270
|
-
: ''}
|
|
275
|
+
: ''} operation_id as __operation_id,
|
|
276
|
+
operation as __operation,
|
|
271
277
|
timestamp as __timestamp,
|
|
272
278
|
previous_value as __previous_value
|
|
273
279
|
FROM
|
|
274
280
|
${destination}
|
|
275
281
|
ORDER BY
|
|
276
|
-
|
|
282
|
+
__operation_id ASC
|
|
277
283
|
) ${query}
|
|
278
284
|
`;
|
|
279
285
|
return tx.getAll<T>(wrappedQuery, params);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { CoreStreamSubscription } from '../../client/sync/stream/core-instruction.js';
|
|
2
1
|
import { SyncClientImplementation } from '../../client/sync/stream/AbstractStreamingSyncImplementation.js';
|
|
3
|
-
import {
|
|
2
|
+
import { CoreStreamSubscription } from '../../client/sync/stream/core-instruction.js';
|
|
4
3
|
import { SyncStreamDescription, SyncSubscriptionDescription } from '../../client/sync/sync-streams.js';
|
|
4
|
+
import { InternalProgressInformation, ProgressWithOperations, SyncProgress } from './SyncProgress.js';
|
|
5
5
|
|
|
6
6
|
export type SyncDataFlowStatus = Partial<{
|
|
7
7
|
downloading: boolean;
|
|
@@ -250,13 +250,28 @@ export class SyncStatus {
|
|
|
250
250
|
return {
|
|
251
251
|
connected: this.connected,
|
|
252
252
|
connecting: this.connecting,
|
|
253
|
-
dataFlow:
|
|
253
|
+
dataFlow: {
|
|
254
|
+
...this.dataFlowStatus,
|
|
255
|
+
uploadError: this.serializeError(this.dataFlowStatus.uploadError),
|
|
256
|
+
downloadError: this.serializeError(this.dataFlowStatus.downloadError)
|
|
257
|
+
},
|
|
254
258
|
lastSyncedAt: this.lastSyncedAt,
|
|
255
259
|
hasSynced: this.hasSynced,
|
|
256
260
|
priorityStatusEntries: this.priorityStatusEntries
|
|
257
261
|
};
|
|
258
262
|
}
|
|
259
263
|
|
|
264
|
+
protected serializeError(error?: Error) {
|
|
265
|
+
if (typeof error == 'undefined') {
|
|
266
|
+
return undefined;
|
|
267
|
+
}
|
|
268
|
+
return {
|
|
269
|
+
name: error.name,
|
|
270
|
+
message: error.message,
|
|
271
|
+
stack: error.stack
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
|
|
260
275
|
private static comparePriorities(a: SyncPriorityStatus, b: SyncPriorityStatus) {
|
|
261
276
|
return b.priority - a.priority; // Reverse because higher priorities have lower numbers
|
|
262
277
|
}
|