@powersync/common 0.0.0-dev-20241107150304 → 0.0.0-dev-20250207081035
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.mjs +3 -3
- package/lib/client/AbstractPowerSyncDatabase.d.ts +8 -12
- package/lib/client/AbstractPowerSyncDatabase.js +39 -18
- package/lib/client/compilableQueryWatch.d.ts +7 -0
- package/lib/client/compilableQueryWatch.js +37 -0
- package/lib/client/runOnSchemaChange.d.ts +2 -0
- package/lib/client/runOnSchemaChange.js +23 -0
- package/lib/client/sync/bucket/SqliteBucketStorage.js +2 -4
- package/lib/client/sync/stream/AbstractRemote.js +3 -3
- package/lib/client/sync/stream/AbstractStreamingSyncImplementation.d.ts +24 -5
- package/lib/client/sync/stream/AbstractStreamingSyncImplementation.js +11 -5
- package/lib/db/DBAdapter.d.ts +4 -0
- package/lib/db/crud/SyncStatus.d.ts +2 -0
- package/lib/db/crud/SyncStatus.js +5 -1
- package/lib/db/schema/Column.d.ts +1 -1
- package/lib/db/schema/Column.js +3 -3
- package/lib/db/schema/Schema.d.ts +1 -1
- package/lib/db/schema/Schema.js +10 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.js +2 -0
- package/package.json +2 -2
|
@@ -10,25 +10,18 @@ import { PowerSyncBackendConnector } from './connection/PowerSyncBackendConnecto
|
|
|
10
10
|
import { BucketStorageAdapter } from './sync/bucket/BucketStorageAdapter.js';
|
|
11
11
|
import { CrudBatch } from './sync/bucket/CrudBatch.js';
|
|
12
12
|
import { CrudTransaction } from './sync/bucket/CrudTransaction.js';
|
|
13
|
-
import { PowerSyncConnectionOptions, StreamingSyncImplementation, StreamingSyncImplementationListener } from './sync/stream/AbstractStreamingSyncImplementation.js';
|
|
13
|
+
import { type AdditionalConnectionOptions, type PowerSyncConnectionOptions, StreamingSyncImplementation, StreamingSyncImplementationListener, type RequiredAdditionalConnectionOptions } from './sync/stream/AbstractStreamingSyncImplementation.js';
|
|
14
14
|
export interface DisconnectAndClearOptions {
|
|
15
15
|
/** When set to false, data in local-only tables is preserved. */
|
|
16
16
|
clearLocal?: boolean;
|
|
17
17
|
}
|
|
18
|
-
export interface BasePowerSyncDatabaseOptions {
|
|
18
|
+
export interface BasePowerSyncDatabaseOptions extends AdditionalConnectionOptions {
|
|
19
19
|
/** Schema used for the local database. */
|
|
20
20
|
schema: Schema;
|
|
21
21
|
/**
|
|
22
|
-
*
|
|
23
|
-
* from the PowerSync backend after an error occurs.
|
|
22
|
+
* @deprecated Use {@link retryDelayMs} instead as this will be removed in future releases.
|
|
24
23
|
*/
|
|
25
24
|
retryDelay?: number;
|
|
26
|
-
/**
|
|
27
|
-
* Backend Connector CRUD operations are throttled
|
|
28
|
-
* to occur at most every `crudUploadThrottleMs`
|
|
29
|
-
* milliseconds.
|
|
30
|
-
*/
|
|
31
|
-
crudUploadThrottleMs?: number;
|
|
32
25
|
logger?: ILogger;
|
|
33
26
|
}
|
|
34
27
|
export interface PowerSyncDatabaseOptions extends BasePowerSyncDatabaseOptions {
|
|
@@ -76,6 +69,7 @@ export interface WatchOnChangeHandler {
|
|
|
76
69
|
}
|
|
77
70
|
export interface PowerSyncDBListener extends StreamingSyncImplementationListener {
|
|
78
71
|
initialized: () => void;
|
|
72
|
+
schemaChanged: (schema: Schema) => void;
|
|
79
73
|
}
|
|
80
74
|
export interface PowerSyncCloseOptions {
|
|
81
75
|
/**
|
|
@@ -88,7 +82,7 @@ export interface PowerSyncCloseOptions {
|
|
|
88
82
|
export declare const DEFAULT_POWERSYNC_CLOSE_OPTIONS: PowerSyncCloseOptions;
|
|
89
83
|
export declare const DEFAULT_WATCH_THROTTLE_MS = 30;
|
|
90
84
|
export declare const DEFAULT_POWERSYNC_DB_OPTIONS: {
|
|
91
|
-
|
|
85
|
+
retryDelayMs: number;
|
|
92
86
|
logger: Logger.ILogger;
|
|
93
87
|
crudUploadThrottleMs: number;
|
|
94
88
|
};
|
|
@@ -147,11 +141,12 @@ export declare abstract class AbstractPowerSyncDatabase extends BaseObserver<Pow
|
|
|
147
141
|
* Whether a connection to the PowerSync service is currently open.
|
|
148
142
|
*/
|
|
149
143
|
get connected(): boolean;
|
|
144
|
+
get connecting(): boolean;
|
|
150
145
|
/**
|
|
151
146
|
* Opens the DBAdapter given open options using a default open factory
|
|
152
147
|
*/
|
|
153
148
|
protected abstract openDBAdapter(options: PowerSyncDatabaseOptionsWithSettings): DBAdapter;
|
|
154
|
-
protected abstract generateSyncStreamImplementation(connector: PowerSyncBackendConnector): StreamingSyncImplementation;
|
|
149
|
+
protected abstract generateSyncStreamImplementation(connector: PowerSyncBackendConnector, options: RequiredAdditionalConnectionOptions): StreamingSyncImplementation;
|
|
155
150
|
protected abstract generateBucketStorageAdapter(): BucketStorageAdapter;
|
|
156
151
|
/**
|
|
157
152
|
* @returns A promise which will resolve once initialization is completed.
|
|
@@ -184,6 +179,7 @@ export declare abstract class AbstractPowerSyncDatabase extends BaseObserver<Pow
|
|
|
184
179
|
* While initializing is automatic, this helps to catch and report initialization errors.
|
|
185
180
|
*/
|
|
186
181
|
init(): Promise<void>;
|
|
182
|
+
resolvedConnectionOptions(options?: PowerSyncConnectionOptions): RequiredAdditionalConnectionOptions;
|
|
187
183
|
/**
|
|
188
184
|
* Connects to stream of events from the PowerSync instance.
|
|
189
185
|
*/
|
|
@@ -13,7 +13,8 @@ import { PSInternalTable } from './sync/bucket/BucketStorageAdapter.js';
|
|
|
13
13
|
import { CrudBatch } from './sync/bucket/CrudBatch.js';
|
|
14
14
|
import { CrudEntry } from './sync/bucket/CrudEntry.js';
|
|
15
15
|
import { CrudTransaction } from './sync/bucket/CrudTransaction.js';
|
|
16
|
-
import { DEFAULT_CRUD_UPLOAD_THROTTLE_MS } from './sync/stream/AbstractStreamingSyncImplementation.js';
|
|
16
|
+
import { DEFAULT_CRUD_UPLOAD_THROTTLE_MS, DEFAULT_RETRY_DELAY_MS } from './sync/stream/AbstractStreamingSyncImplementation.js';
|
|
17
|
+
import { runOnSchemaChange } from './runOnSchemaChange.js';
|
|
17
18
|
const POWERSYNC_TABLE_MATCH = /(^ps_data__|^ps_data_local__)/;
|
|
18
19
|
const DEFAULT_DISCONNECT_CLEAR_OPTIONS = {
|
|
19
20
|
clearLocal: true
|
|
@@ -23,7 +24,7 @@ export const DEFAULT_POWERSYNC_CLOSE_OPTIONS = {
|
|
|
23
24
|
};
|
|
24
25
|
export const DEFAULT_WATCH_THROTTLE_MS = 30;
|
|
25
26
|
export const DEFAULT_POWERSYNC_DB_OPTIONS = {
|
|
26
|
-
|
|
27
|
+
retryDelayMs: 5000,
|
|
27
28
|
logger: Logger.get('PowerSyncDatabase'),
|
|
28
29
|
crudUploadThrottleMs: DEFAULT_CRUD_UPLOAD_THROTTLE_MS
|
|
29
30
|
};
|
|
@@ -113,6 +114,9 @@ export class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
113
114
|
get connected() {
|
|
114
115
|
return this.currentStatus?.connected || false;
|
|
115
116
|
}
|
|
117
|
+
get connecting() {
|
|
118
|
+
return this.currentStatus?.connecting || false;
|
|
119
|
+
}
|
|
116
120
|
/**
|
|
117
121
|
* @returns A promise which will resolve once initialization is completed.
|
|
118
122
|
*/
|
|
@@ -211,6 +215,8 @@ export class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
211
215
|
}
|
|
212
216
|
this._schema = schema;
|
|
213
217
|
await this.database.execute('SELECT powersync_replace_schema(?)', [JSON.stringify(this.schema.toJSON())]);
|
|
218
|
+
await this.database.refreshSchema();
|
|
219
|
+
this.iterateListeners(async (cb) => cb.schemaChanged?.(schema));
|
|
214
220
|
}
|
|
215
221
|
/**
|
|
216
222
|
* Wait for initialization to complete.
|
|
@@ -219,6 +225,13 @@ export class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
219
225
|
async init() {
|
|
220
226
|
return this.waitForReady();
|
|
221
227
|
}
|
|
228
|
+
// Use the options passed in during connect, or fallback to the options set during database creation or fallback to the default options
|
|
229
|
+
resolvedConnectionOptions(options) {
|
|
230
|
+
return {
|
|
231
|
+
retryDelayMs: options?.retryDelayMs ?? this.options.retryDelayMs ?? this.options.retryDelay ?? DEFAULT_RETRY_DELAY_MS,
|
|
232
|
+
crudUploadThrottleMs: options?.crudUploadThrottleMs ?? this.options.crudUploadThrottleMs ?? DEFAULT_CRUD_UPLOAD_THROTTLE_MS
|
|
233
|
+
};
|
|
234
|
+
}
|
|
222
235
|
/**
|
|
223
236
|
* Connects to stream of events from the PowerSync instance.
|
|
224
237
|
*/
|
|
@@ -229,7 +242,11 @@ export class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
229
242
|
if (this.closed) {
|
|
230
243
|
throw new Error('Cannot connect using a closed client');
|
|
231
244
|
}
|
|
232
|
-
|
|
245
|
+
const { retryDelayMs, crudUploadThrottleMs } = this.resolvedConnectionOptions(options);
|
|
246
|
+
this.syncStreamImplementation = this.generateSyncStreamImplementation(connector, {
|
|
247
|
+
retryDelayMs,
|
|
248
|
+
crudUploadThrottleMs,
|
|
249
|
+
});
|
|
233
250
|
this.syncStatusListenerDisposer = this.syncStreamImplementation.registerListener({
|
|
234
251
|
statusChanged: (status) => {
|
|
235
252
|
this.currentStatus = new SyncStatus({
|
|
@@ -387,13 +404,13 @@ export class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
387
404
|
if (writeCheckpoint) {
|
|
388
405
|
const check = await tx.execute(`SELECT 1 FROM ${PSInternalTable.CRUD} LIMIT 1`);
|
|
389
406
|
if (!check.rows?.length) {
|
|
390
|
-
await tx.execute(`UPDATE ${PSInternalTable.BUCKETS} SET target_op = ? WHERE name='$local'`, [
|
|
407
|
+
await tx.execute(`UPDATE ${PSInternalTable.BUCKETS} SET target_op = CAST(? as INTEGER) WHERE name='$local'`, [
|
|
391
408
|
writeCheckpoint
|
|
392
409
|
]);
|
|
393
410
|
}
|
|
394
411
|
}
|
|
395
412
|
else {
|
|
396
|
-
await tx.execute(`UPDATE ${PSInternalTable.BUCKETS} SET target_op = ? WHERE name='$local'`, [
|
|
413
|
+
await tx.execute(`UPDATE ${PSInternalTable.BUCKETS} SET target_op = CAST(? as INTEGER) WHERE name='$local'`, [
|
|
397
414
|
this.bucketStorageAdapter.getMaxOpId()
|
|
398
415
|
]);
|
|
399
416
|
}
|
|
@@ -503,7 +520,7 @@ export class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
503
520
|
if (!onResult) {
|
|
504
521
|
throw new Error('onResult is required');
|
|
505
522
|
}
|
|
506
|
-
|
|
523
|
+
const watchQuery = async (abortSignal) => {
|
|
507
524
|
try {
|
|
508
525
|
const resolvedTables = await this.resolveTables(sql, parameters, options);
|
|
509
526
|
// Fetch initial data
|
|
@@ -522,13 +539,16 @@ export class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
522
539
|
onError
|
|
523
540
|
}, {
|
|
524
541
|
...(options ?? {}),
|
|
525
|
-
tables: resolvedTables
|
|
542
|
+
tables: resolvedTables,
|
|
543
|
+
// Override the abort signal since we intercept it
|
|
544
|
+
signal: abortSignal
|
|
526
545
|
});
|
|
527
546
|
}
|
|
528
547
|
catch (error) {
|
|
529
548
|
onError?.(error);
|
|
530
549
|
}
|
|
531
|
-
}
|
|
550
|
+
};
|
|
551
|
+
runOnSchemaChange(watchQuery, this, options);
|
|
532
552
|
}
|
|
533
553
|
/**
|
|
534
554
|
* Execute a read query every time the source tables are modified.
|
|
@@ -537,17 +557,18 @@ export class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
537
557
|
*/
|
|
538
558
|
watchWithAsyncGenerator(sql, parameters, options) {
|
|
539
559
|
return new EventIterator((eventOptions) => {
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
tables: resolvedTables
|
|
547
|
-
})) {
|
|
548
|
-
eventOptions.push(await this.executeReadOnly(sql, parameters));
|
|
560
|
+
const handler = {
|
|
561
|
+
onResult: (result) => {
|
|
562
|
+
eventOptions.push(result);
|
|
563
|
+
},
|
|
564
|
+
onError: (error) => {
|
|
565
|
+
eventOptions.fail(error);
|
|
549
566
|
}
|
|
550
|
-
}
|
|
567
|
+
};
|
|
568
|
+
this.watchWithCallback(sql, parameters, handler, options);
|
|
569
|
+
options?.signal?.addEventListener('abort', () => {
|
|
570
|
+
eventOptions.stop();
|
|
571
|
+
});
|
|
551
572
|
});
|
|
552
573
|
}
|
|
553
574
|
async resolveTables(sql, parameters, options) {
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { CompilableQuery } from './../types/types.js';
|
|
2
|
+
import { AbstractPowerSyncDatabase, SQLWatchOptions } from './AbstractPowerSyncDatabase.js';
|
|
3
|
+
export interface CompilableQueryWatchHandler<T> {
|
|
4
|
+
onResult: (results: T[]) => void;
|
|
5
|
+
onError?: (error: Error) => void;
|
|
6
|
+
}
|
|
7
|
+
export declare function compilableQueryWatch<T>(db: AbstractPowerSyncDatabase, query: CompilableQuery<T>, handler: CompilableQueryWatchHandler<T>, options?: SQLWatchOptions): void;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { runOnSchemaChange } from './runOnSchemaChange.js';
|
|
2
|
+
export function compilableQueryWatch(db, query, handler, options) {
|
|
3
|
+
const { onResult, onError = (e) => { } } = handler ?? {};
|
|
4
|
+
if (!onResult) {
|
|
5
|
+
throw new Error('onResult is required');
|
|
6
|
+
}
|
|
7
|
+
const watchQuery = async (abortSignal) => {
|
|
8
|
+
try {
|
|
9
|
+
const toSql = query.compile();
|
|
10
|
+
const resolvedTables = await db.resolveTables(toSql.sql, toSql.parameters, options);
|
|
11
|
+
// Fetch initial data
|
|
12
|
+
const result = await query.execute();
|
|
13
|
+
onResult(result);
|
|
14
|
+
db.onChangeWithCallback({
|
|
15
|
+
onChange: async () => {
|
|
16
|
+
try {
|
|
17
|
+
const result = await query.execute();
|
|
18
|
+
onResult(result);
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
onError(error);
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
onError
|
|
25
|
+
}, {
|
|
26
|
+
...(options ?? {}),
|
|
27
|
+
tables: resolvedTables,
|
|
28
|
+
// Override the abort signal since we intercept it
|
|
29
|
+
signal: abortSignal
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
onError(error);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
runOnSchemaChange(watchQuery, db, options);
|
|
37
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export function runOnSchemaChange(callback, db, options) {
|
|
2
|
+
const triggerWatchedQuery = () => {
|
|
3
|
+
const abortController = new AbortController();
|
|
4
|
+
let disposeSchemaListener = null;
|
|
5
|
+
const stopWatching = () => {
|
|
6
|
+
abortController.abort('Abort triggered');
|
|
7
|
+
disposeSchemaListener?.();
|
|
8
|
+
disposeSchemaListener = null;
|
|
9
|
+
// Stop listening to upstream abort for this watch
|
|
10
|
+
options?.signal?.removeEventListener('abort', stopWatching);
|
|
11
|
+
};
|
|
12
|
+
options?.signal?.addEventListener('abort', stopWatching);
|
|
13
|
+
disposeSchemaListener = db.registerListener({
|
|
14
|
+
schemaChanged: async () => {
|
|
15
|
+
stopWatching();
|
|
16
|
+
// Re trigger the watched query (recursively), setTimeout ensures that we don't modify the list of listeners while iterating through them
|
|
17
|
+
setTimeout(() => triggerWatchedQuery(), 0);
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
callback(abortController.signal);
|
|
21
|
+
};
|
|
22
|
+
triggerWatchedQuery();
|
|
23
|
+
}
|
|
@@ -63,7 +63,7 @@ export class SqliteBucketStorage extends BaseObserver {
|
|
|
63
63
|
*/
|
|
64
64
|
startSession() { }
|
|
65
65
|
async getBucketStates() {
|
|
66
|
-
const result = await this.db.getAll(
|
|
66
|
+
const result = await this.db.getAll("SELECT name as bucket, cast(last_op as TEXT) as op_id FROM ps_buckets WHERE pending_delete = 0 AND name != '$local'");
|
|
67
67
|
return result;
|
|
68
68
|
}
|
|
69
69
|
async saveSyncData(batch) {
|
|
@@ -204,9 +204,7 @@ export class SqliteBucketStorage extends BaseObserver {
|
|
|
204
204
|
this.compactCounter = 0;
|
|
205
205
|
}
|
|
206
206
|
async updateLocalTarget(cb) {
|
|
207
|
-
const rs1 = await this.db.getAll("SELECT target_op FROM ps_buckets WHERE name = '$local' AND target_op = ?", [
|
|
208
|
-
MAX_OP_ID
|
|
209
|
-
]);
|
|
207
|
+
const rs1 = await this.db.getAll("SELECT target_op FROM ps_buckets WHERE name = '$local' AND target_op = CAST(? as INTEGER)", [MAX_OP_ID]);
|
|
210
208
|
if (!rs1.length) {
|
|
211
209
|
// Nothing to update
|
|
212
210
|
return false;
|
|
@@ -9,12 +9,12 @@ import { version as POWERSYNC_JS_VERSION } from '../../../../package.json';
|
|
|
9
9
|
const POWERSYNC_TRAILING_SLASH_MATCH = /\/+$/;
|
|
10
10
|
// Refresh at least 30 sec before it expires
|
|
11
11
|
const REFRESH_CREDENTIALS_SAFETY_PERIOD_MS = 30_000;
|
|
12
|
-
const SYNC_QUEUE_REQUEST_N =
|
|
12
|
+
const SYNC_QUEUE_REQUEST_N = 1;
|
|
13
13
|
const SYNC_QUEUE_REQUEST_LOW_WATER = 5;
|
|
14
14
|
// Keep alive message is sent every period
|
|
15
|
-
const KEEP_ALIVE_MS =
|
|
15
|
+
const KEEP_ALIVE_MS = 60_000;
|
|
16
16
|
// The ACK must be received in this period
|
|
17
|
-
const KEEP_ALIVE_LIFETIME_MS =
|
|
17
|
+
const KEEP_ALIVE_LIFETIME_MS = 90_000;
|
|
18
18
|
export const DEFAULT_REMOTE_LOGGER = Logger.get('PowerSyncRemote');
|
|
19
19
|
/**
|
|
20
20
|
* Class wrapper for providing a fetch implementation.
|
|
@@ -20,10 +20,9 @@ export interface LockOptions<T> {
|
|
|
20
20
|
type: LockType;
|
|
21
21
|
signal?: AbortSignal;
|
|
22
22
|
}
|
|
23
|
-
export interface AbstractStreamingSyncImplementationOptions {
|
|
23
|
+
export interface AbstractStreamingSyncImplementationOptions extends AdditionalConnectionOptions {
|
|
24
24
|
adapter: BucketStorageAdapter;
|
|
25
25
|
uploadCrud: () => Promise<void>;
|
|
26
|
-
crudUploadThrottleMs?: number;
|
|
27
26
|
/**
|
|
28
27
|
* An identifier for which PowerSync DB this sync implementation is
|
|
29
28
|
* linked to. Most commonly DB name, but not restricted to DB name.
|
|
@@ -31,7 +30,6 @@ export interface AbstractStreamingSyncImplementationOptions {
|
|
|
31
30
|
identifier?: string;
|
|
32
31
|
logger?: ILogger;
|
|
33
32
|
remote: AbstractRemote;
|
|
34
|
-
retryDelayMs?: number;
|
|
35
33
|
}
|
|
36
34
|
export interface StreamingSyncImplementationListener extends BaseListener {
|
|
37
35
|
/**
|
|
@@ -48,7 +46,10 @@ export interface StreamingSyncImplementationListener extends BaseListener {
|
|
|
48
46
|
* Configurable options to be used when connecting to the PowerSync
|
|
49
47
|
* backend instance.
|
|
50
48
|
*/
|
|
51
|
-
export interface PowerSyncConnectionOptions {
|
|
49
|
+
export interface PowerSyncConnectionOptions extends BaseConnectionOptions, AdditionalConnectionOptions {
|
|
50
|
+
}
|
|
51
|
+
/** @internal */
|
|
52
|
+
export interface BaseConnectionOptions {
|
|
52
53
|
/**
|
|
53
54
|
* The connection method to use when streaming updates from
|
|
54
55
|
* the PowerSync backend instance.
|
|
@@ -60,6 +61,22 @@ export interface PowerSyncConnectionOptions {
|
|
|
60
61
|
*/
|
|
61
62
|
params?: Record<string, StreamingSyncRequestParameterType>;
|
|
62
63
|
}
|
|
64
|
+
/** @internal */
|
|
65
|
+
export interface AdditionalConnectionOptions {
|
|
66
|
+
/**
|
|
67
|
+
* Delay for retrying sync streaming operations
|
|
68
|
+
* from the PowerSync backend after an error occurs.
|
|
69
|
+
*/
|
|
70
|
+
retryDelayMs?: number;
|
|
71
|
+
/**
|
|
72
|
+
* Backend Connector CRUD operations are throttled
|
|
73
|
+
* to occur at most every `crudUploadThrottleMs`
|
|
74
|
+
* milliseconds.
|
|
75
|
+
*/
|
|
76
|
+
crudUploadThrottleMs?: number;
|
|
77
|
+
}
|
|
78
|
+
/** @internal */
|
|
79
|
+
export type RequiredAdditionalConnectionOptions = Required<AdditionalConnectionOptions>;
|
|
63
80
|
export interface StreamingSyncImplementation extends BaseObserver<StreamingSyncImplementationListener>, Disposable {
|
|
64
81
|
/**
|
|
65
82
|
* Connects to the sync service
|
|
@@ -80,12 +97,14 @@ export interface StreamingSyncImplementation extends BaseObserver<StreamingSyncI
|
|
|
80
97
|
waitForStatus(status: SyncStatusOptions): Promise<void>;
|
|
81
98
|
}
|
|
82
99
|
export declare const DEFAULT_CRUD_UPLOAD_THROTTLE_MS = 1000;
|
|
100
|
+
export declare const DEFAULT_RETRY_DELAY_MS = 5000;
|
|
83
101
|
export declare const DEFAULT_STREAMING_SYNC_OPTIONS: {
|
|
84
102
|
retryDelayMs: number;
|
|
85
103
|
logger: Logger.ILogger;
|
|
86
104
|
crudUploadThrottleMs: number;
|
|
87
105
|
};
|
|
88
|
-
export
|
|
106
|
+
export type RequiredPowerSyncConnectionOptions = Required<BaseConnectionOptions>;
|
|
107
|
+
export declare const DEFAULT_STREAM_CONNECTION_OPTIONS: RequiredPowerSyncConnectionOptions;
|
|
89
108
|
export declare abstract class AbstractStreamingSyncImplementation extends BaseObserver<StreamingSyncImplementationListener> implements StreamingSyncImplementation {
|
|
90
109
|
protected _lastSyncedAt: Date | null;
|
|
91
110
|
protected options: AbstractStreamingSyncImplementationOptions;
|
|
@@ -16,8 +16,9 @@ export var SyncStreamConnectionMethod;
|
|
|
16
16
|
SyncStreamConnectionMethod["WEB_SOCKET"] = "web-socket";
|
|
17
17
|
})(SyncStreamConnectionMethod || (SyncStreamConnectionMethod = {}));
|
|
18
18
|
export const DEFAULT_CRUD_UPLOAD_THROTTLE_MS = 1000;
|
|
19
|
+
export const DEFAULT_RETRY_DELAY_MS = 5000;
|
|
19
20
|
export const DEFAULT_STREAMING_SYNC_OPTIONS = {
|
|
20
|
-
retryDelayMs:
|
|
21
|
+
retryDelayMs: DEFAULT_RETRY_DELAY_MS,
|
|
21
22
|
logger: Logger.get('PowerSyncStream'),
|
|
22
23
|
crudUploadThrottleMs: DEFAULT_CRUD_UPLOAD_THROTTLE_MS
|
|
23
24
|
};
|
|
@@ -38,6 +39,7 @@ export class AbstractStreamingSyncImplementation extends BaseObserver {
|
|
|
38
39
|
this.options = { ...DEFAULT_STREAMING_SYNC_OPTIONS, ...options };
|
|
39
40
|
this.syncStatus = new SyncStatus({
|
|
40
41
|
connected: false,
|
|
42
|
+
connecting: false,
|
|
41
43
|
lastSyncedAt: undefined,
|
|
42
44
|
dataFlow: {
|
|
43
45
|
uploading: false,
|
|
@@ -207,7 +209,7 @@ The next upload iteration will be delayed.`);
|
|
|
207
209
|
}
|
|
208
210
|
this.streamingSyncPromise = undefined;
|
|
209
211
|
this.abortController = null;
|
|
210
|
-
this.updateSyncStatus({ connected: false });
|
|
212
|
+
this.updateSyncStatus({ connected: false, connecting: false });
|
|
211
213
|
}
|
|
212
214
|
/**
|
|
213
215
|
* @deprecated use [connect instead]
|
|
@@ -238,6 +240,7 @@ The next upload iteration will be delayed.`);
|
|
|
238
240
|
this.crudUpdateListener = undefined;
|
|
239
241
|
this.updateSyncStatus({
|
|
240
242
|
connected: false,
|
|
243
|
+
connecting: false,
|
|
241
244
|
dataFlow: {
|
|
242
245
|
downloading: false
|
|
243
246
|
}
|
|
@@ -250,6 +253,7 @@ The next upload iteration will be delayed.`);
|
|
|
250
253
|
* - Close any sync stream ReadableStreams (which will also close any established network requests)
|
|
251
254
|
*/
|
|
252
255
|
while (true) {
|
|
256
|
+
this.updateSyncStatus({ connecting: true });
|
|
253
257
|
try {
|
|
254
258
|
if (signal?.aborted) {
|
|
255
259
|
break;
|
|
@@ -280,6 +284,7 @@ The next upload iteration will be delayed.`);
|
|
|
280
284
|
else {
|
|
281
285
|
this.logger.error(ex);
|
|
282
286
|
}
|
|
287
|
+
// On error, wait a little before retrying
|
|
283
288
|
await this.delayRetry();
|
|
284
289
|
}
|
|
285
290
|
finally {
|
|
@@ -288,13 +293,13 @@ The next upload iteration will be delayed.`);
|
|
|
288
293
|
nestedAbortController = new AbortController();
|
|
289
294
|
}
|
|
290
295
|
this.updateSyncStatus({
|
|
291
|
-
connected: false
|
|
296
|
+
connected: false,
|
|
297
|
+
connecting: true // May be unnecessary
|
|
292
298
|
});
|
|
293
|
-
// On error, wait a little before retrying
|
|
294
299
|
}
|
|
295
300
|
}
|
|
296
301
|
// Mark as disconnected if here
|
|
297
|
-
this.updateSyncStatus({ connected: false });
|
|
302
|
+
this.updateSyncStatus({ connected: false, connecting: false });
|
|
298
303
|
}
|
|
299
304
|
async streamingSyncIteration(signal, options) {
|
|
300
305
|
return await this.obtainLock({
|
|
@@ -489,6 +494,7 @@ The next upload iteration will be delayed.`);
|
|
|
489
494
|
updateSyncStatus(options) {
|
|
490
495
|
const updatedStatus = new SyncStatus({
|
|
491
496
|
connected: options.connected ?? this.syncStatus.connected,
|
|
497
|
+
connecting: !options.connected && (options.connecting ?? this.syncStatus.connecting),
|
|
492
498
|
lastSyncedAt: options.lastSyncedAt ?? this.syncStatus.lastSyncedAt,
|
|
493
499
|
dataFlow: {
|
|
494
500
|
...this.syncStatus.dataFlowStatus,
|
package/lib/db/DBAdapter.d.ts
CHANGED
|
@@ -90,6 +90,10 @@ export interface DBAdapter extends BaseObserverInterface<DBAdapterListener>, DBG
|
|
|
90
90
|
readTransaction: <T>(fn: (tx: Transaction) => Promise<T>, options?: DBLockOptions) => Promise<T>;
|
|
91
91
|
writeLock: <T>(fn: (tx: LockContext) => Promise<T>, options?: DBLockOptions) => Promise<T>;
|
|
92
92
|
writeTransaction: <T>(fn: (tx: Transaction) => Promise<T>, options?: DBLockOptions) => Promise<T>;
|
|
93
|
+
/**
|
|
94
|
+
* This method refreshes the schema information across all connections. This is for advanced use cases, and should generally not be needed.
|
|
95
|
+
*/
|
|
96
|
+
refreshSchema: () => Promise<void>;
|
|
93
97
|
}
|
|
94
98
|
export declare function isBatchedUpdateNotification(update: BatchedUpdateNotification | UpdateNotification): update is BatchedUpdateNotification;
|
|
95
99
|
export declare function extractTableUpdates(update: BatchedUpdateNotification | UpdateNotification): string[];
|
|
@@ -4,6 +4,7 @@ export type SyncDataFlowStatus = Partial<{
|
|
|
4
4
|
}>;
|
|
5
5
|
export type SyncStatusOptions = {
|
|
6
6
|
connected?: boolean;
|
|
7
|
+
connecting?: boolean;
|
|
7
8
|
dataFlow?: SyncDataFlowStatus;
|
|
8
9
|
lastSyncedAt?: Date;
|
|
9
10
|
hasSynced?: boolean;
|
|
@@ -15,6 +16,7 @@ export declare class SyncStatus {
|
|
|
15
16
|
* true if currently connected.
|
|
16
17
|
*/
|
|
17
18
|
get connected(): boolean;
|
|
19
|
+
get connecting(): boolean;
|
|
18
20
|
/**
|
|
19
21
|
* Time that a last sync has fully completed, if any.
|
|
20
22
|
* Currently this is reset to null after a restart.
|
|
@@ -9,6 +9,9 @@ export class SyncStatus {
|
|
|
9
9
|
get connected() {
|
|
10
10
|
return this.options.connected ?? false;
|
|
11
11
|
}
|
|
12
|
+
get connecting() {
|
|
13
|
+
return this.options.connecting ?? false;
|
|
14
|
+
}
|
|
12
15
|
/**
|
|
13
16
|
* Time that a last sync has fully completed, if any.
|
|
14
17
|
* Currently this is reset to null after a restart.
|
|
@@ -44,11 +47,12 @@ export class SyncStatus {
|
|
|
44
47
|
}
|
|
45
48
|
getMessage() {
|
|
46
49
|
const dataFlow = this.dataFlowStatus;
|
|
47
|
-
return `SyncStatus<connected: ${this.connected} lastSyncedAt: ${this.lastSyncedAt} hasSynced: ${this.hasSynced}. Downloading: ${dataFlow.downloading}. Uploading: ${dataFlow.uploading}`;
|
|
50
|
+
return `SyncStatus<connected: ${this.connected} connecting: ${this.connecting} lastSyncedAt: ${this.lastSyncedAt} hasSynced: ${this.hasSynced}. Downloading: ${dataFlow.downloading}. Uploading: ${dataFlow.uploading}`;
|
|
48
51
|
}
|
|
49
52
|
toJSON() {
|
|
50
53
|
return {
|
|
51
54
|
connected: this.connected,
|
|
55
|
+
connecting: this.connecting,
|
|
52
56
|
dataFlow: this.dataFlowStatus,
|
|
53
57
|
lastSyncedAt: this.lastSyncedAt,
|
|
54
58
|
hasSynced: this.hasSynced
|
|
@@ -12,7 +12,7 @@ export type BaseColumnType<T extends number | string | null> = {
|
|
|
12
12
|
};
|
|
13
13
|
export type ColumnsType = Record<string, BaseColumnType<any>>;
|
|
14
14
|
export type ExtractColumnValueType<T extends BaseColumnType<any>> = T extends BaseColumnType<infer R> ? R : unknown;
|
|
15
|
-
export declare const MAX_AMOUNT_OF_COLUMNS =
|
|
15
|
+
export declare const MAX_AMOUNT_OF_COLUMNS = 1999;
|
|
16
16
|
export declare const column: {
|
|
17
17
|
text: BaseColumnType<string | null>;
|
|
18
18
|
integer: BaseColumnType<number | null>;
|
package/lib/db/schema/Column.js
CHANGED
|
@@ -14,9 +14,9 @@ const integer = {
|
|
|
14
14
|
const real = {
|
|
15
15
|
type: ColumnType.REAL
|
|
16
16
|
};
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
export const MAX_AMOUNT_OF_COLUMNS =
|
|
17
|
+
// powersync-sqlite-core limits the number of column per table to 1999, due to internal SQLite limits.
|
|
18
|
+
// In earlier versions this was limited to 63.
|
|
19
|
+
export const MAX_AMOUNT_OF_COLUMNS = 1999;
|
|
20
20
|
export const column = {
|
|
21
21
|
text,
|
|
22
22
|
integer,
|
package/lib/db/schema/Schema.js
CHANGED
|
@@ -11,6 +11,16 @@ export class Schema {
|
|
|
11
11
|
tables;
|
|
12
12
|
constructor(tables) {
|
|
13
13
|
if (Array.isArray(tables)) {
|
|
14
|
+
/*
|
|
15
|
+
We need to validate that the tables have a name here because a user could pass in an array
|
|
16
|
+
of Tables that don't have a name because they are using the V2 syntax.
|
|
17
|
+
Therefore, 'convertToClassicTables' won't be called on the tables resulting in a runtime error.
|
|
18
|
+
*/
|
|
19
|
+
for (const table of tables) {
|
|
20
|
+
if (table.name === '') {
|
|
21
|
+
throw new Error("It appears you are trying to create a new Schema with an array instead of an object. Passing in an object instead of an array into 'new Schema()' may resolve your issue.");
|
|
22
|
+
}
|
|
23
|
+
}
|
|
14
24
|
this.tables = tables;
|
|
15
25
|
}
|
|
16
26
|
else {
|
package/lib/index.d.ts
CHANGED
|
@@ -4,6 +4,8 @@ export * from './client/SQLOpenFactory.js';
|
|
|
4
4
|
export * from './client/connection/PowerSyncBackendConnector.js';
|
|
5
5
|
export * from './client/connection/PowerSyncCredentials.js';
|
|
6
6
|
export * from './client/sync/bucket/BucketStorageAdapter.js';
|
|
7
|
+
export { runOnSchemaChange } from './client/runOnSchemaChange.js';
|
|
8
|
+
export { CompilableQueryWatchHandler, compilableQueryWatch } from './client/compilableQueryWatch.js';
|
|
7
9
|
export { UpdateType, CrudEntry, OpId } from './client/sync/bucket/CrudEntry.js';
|
|
8
10
|
export * from './client/sync/bucket/SqliteBucketStorage.js';
|
|
9
11
|
export * from './client/sync/bucket/CrudBatch.js';
|
package/lib/index.js
CHANGED
|
@@ -4,6 +4,8 @@ export * from './client/SQLOpenFactory.js';
|
|
|
4
4
|
export * from './client/connection/PowerSyncBackendConnector.js';
|
|
5
5
|
export * from './client/connection/PowerSyncCredentials.js';
|
|
6
6
|
export * from './client/sync/bucket/BucketStorageAdapter.js';
|
|
7
|
+
export { runOnSchemaChange } from './client/runOnSchemaChange.js';
|
|
8
|
+
export { compilableQueryWatch } from './client/compilableQueryWatch.js';
|
|
7
9
|
export { UpdateType, CrudEntry } from './client/sync/bucket/CrudEntry.js';
|
|
8
10
|
export * from './client/sync/bucket/SqliteBucketStorage.js';
|
|
9
11
|
export * from './client/sync/bucket/CrudBatch.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@powersync/common",
|
|
3
|
-
"version": "0.0.0-dev-
|
|
3
|
+
"version": "0.0.0-dev-20250207081035",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"registry": "https://registry.npmjs.org/",
|
|
6
6
|
"access": "public"
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"scripts": {
|
|
60
60
|
"build": "tsc -b && rollup -c rollup.config.mjs",
|
|
61
61
|
"build:prod": "tsc -b --sourceMap false && rollup -c rollup.config.mjs --sourceMap false",
|
|
62
|
-
"clean": "rm -rf lib dist tsconfig.tsbuildinfo",
|
|
62
|
+
"clean": "rm -rf lib dist tsconfig.tsbuildinfo node_modules",
|
|
63
63
|
"test": "vitest"
|
|
64
64
|
}
|
|
65
65
|
}
|