@powersync/common 0.0.0-dev-20250714151300 → 0.0.0-dev-20250715104704

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.
Files changed (35) hide show
  1. package/dist/bundle.cjs +5 -5
  2. package/dist/bundle.mjs +3 -3
  3. package/lib/client/AbstractPowerSyncDatabase.d.ts +5 -56
  4. package/lib/client/AbstractPowerSyncDatabase.js +32 -96
  5. package/lib/client/ConnectionManager.d.ts +4 -4
  6. package/lib/client/sync/bucket/BucketStorageAdapter.d.ts +2 -2
  7. package/lib/client/sync/stream/AbstractStreamingSyncImplementation.d.ts +9 -5
  8. package/lib/client/sync/stream/AbstractStreamingSyncImplementation.js +21 -18
  9. package/lib/db/schema/RawTable.d.ts +57 -0
  10. package/lib/db/schema/RawTable.js +28 -0
  11. package/lib/db/schema/Schema.d.ts +14 -0
  12. package/lib/db/schema/Schema.js +20 -1
  13. package/lib/index.d.ts +1 -7
  14. package/lib/index.js +1 -7
  15. package/lib/utils/BaseObserver.d.ts +4 -3
  16. package/lib/utils/BaseObserver.js +0 -3
  17. package/package.json +1 -1
  18. package/lib/client/CustomQuery.d.ts +0 -25
  19. package/lib/client/CustomQuery.js +0 -41
  20. package/lib/client/Query.d.ts +0 -79
  21. package/lib/client/Query.js +0 -1
  22. package/lib/client/watched/GetAllQuery.d.ts +0 -32
  23. package/lib/client/watched/GetAllQuery.js +0 -24
  24. package/lib/client/watched/WatchedQuery.d.ts +0 -93
  25. package/lib/client/watched/WatchedQuery.js +0 -12
  26. package/lib/client/watched/processors/AbstractQueryProcessor.d.ts +0 -67
  27. package/lib/client/watched/processors/AbstractQueryProcessor.js +0 -136
  28. package/lib/client/watched/processors/DifferentialQueryProcessor.d.ts +0 -129
  29. package/lib/client/watched/processors/DifferentialQueryProcessor.js +0 -175
  30. package/lib/client/watched/processors/OnChangeQueryProcessor.d.ts +0 -27
  31. package/lib/client/watched/processors/OnChangeQueryProcessor.js +0 -74
  32. package/lib/client/watched/processors/comparators.d.ts +0 -24
  33. package/lib/client/watched/processors/comparators.js +0 -33
  34. package/lib/utils/MetaBaseObserver.d.ts +0 -29
  35. package/lib/utils/MetaBaseObserver.js +0 -50
@@ -6,15 +6,12 @@ import { UploadQueueStats } from '../db/crud/UploadQueueStatus.js';
6
6
  import { Schema } from '../db/schema/Schema.js';
7
7
  import { BaseObserver } from '../utils/BaseObserver.js';
8
8
  import { ConnectionManager } from './ConnectionManager.js';
9
- import { ArrayQueryDefinition, Query } from './Query.js';
10
9
  import { SQLOpenFactory, SQLOpenOptions } from './SQLOpenFactory.js';
11
10
  import { PowerSyncBackendConnector } from './connection/PowerSyncBackendConnector.js';
12
11
  import { BucketStorageAdapter } from './sync/bucket/BucketStorageAdapter.js';
13
12
  import { CrudBatch } from './sync/bucket/CrudBatch.js';
14
13
  import { CrudTransaction } from './sync/bucket/CrudTransaction.js';
15
14
  import { StreamingSyncImplementation, StreamingSyncImplementationListener, type AdditionalConnectionOptions, type PowerSyncConnectionOptions, type RequiredAdditionalConnectionOptions } from './sync/stream/AbstractStreamingSyncImplementation.js';
16
- import { WatchCompatibleQuery } from './watched/WatchedQuery.js';
17
- import { WatchedQueryComparator } from './watched/processors/comparators.js';
18
15
  export interface DisconnectAndClearOptions {
19
16
  /** When set to false, data in local-only tables is preserved. */
20
17
  clearLocal?: boolean;
@@ -47,7 +44,7 @@ export interface PowerSyncDatabaseOptionsWithOpenFactory extends BasePowerSyncDa
47
44
  export interface PowerSyncDatabaseOptionsWithSettings extends BasePowerSyncDatabaseOptions {
48
45
  database: SQLOpenOptions;
49
46
  }
50
- export interface SQLOnChangeOptions {
47
+ export interface SQLWatchOptions {
51
48
  signal?: AbortSignal;
52
49
  tables?: string[];
53
50
  /** The minimum interval between queries. */
@@ -59,17 +56,6 @@ export interface SQLOnChangeOptions {
59
56
  * by not removing PowerSync table name prefixes
60
57
  */
61
58
  rawTableNames?: boolean;
62
- /**
63
- * Emits an empty result set immediately
64
- */
65
- triggerImmediate?: boolean;
66
- }
67
- export interface SQLWatchOptions extends SQLOnChangeOptions {
68
- /**
69
- * Optional comparator which will be used to compare the results of the query.
70
- * The watched query will only yield results if the comparator returns false.
71
- */
72
- comparator?: WatchedQueryComparator<QueryResult>;
73
59
  }
74
60
  export interface WatchOnChangeEvent {
75
61
  changedTables: string[];
@@ -85,8 +71,6 @@ export interface WatchOnChangeHandler {
85
71
  export interface PowerSyncDBListener extends StreamingSyncImplementationListener {
86
72
  initialized: () => void;
87
73
  schemaChanged: (schema: Schema) => void;
88
- closing: () => Promise<void> | void;
89
- closed: () => Promise<void> | void;
90
74
  }
91
75
  export interface PowerSyncCloseOptions {
92
76
  /**
@@ -97,6 +81,7 @@ export interface PowerSyncCloseOptions {
97
81
  disconnect?: boolean;
98
82
  }
99
83
  export declare const DEFAULT_POWERSYNC_CLOSE_OPTIONS: PowerSyncCloseOptions;
84
+ export declare const DEFAULT_WATCH_THROTTLE_MS = 30;
100
85
  export declare const DEFAULT_POWERSYNC_DB_OPTIONS: {
101
86
  retryDelayMs: number;
102
87
  logger: Logger.ILogger;
@@ -405,42 +390,6 @@ export declare abstract class AbstractPowerSyncDatabase extends BaseObserver<Pow
405
390
  * ```
406
391
  */
407
392
  watch(sql: string, parameters?: any[], handler?: WatchHandler, options?: SQLWatchOptions): void;
408
- /**
409
- * Allows defining a query which can be used to build a {@link WatchedQuery}.
410
- * The defined query will be executed with {@link AbstractPowerSyncDatabase#getAll}.
411
- * An optional mapper function can be provided to transform the results.
412
- *
413
- * @example
414
- * ```javascript
415
- * const watchedTodos = powersync.query({
416
- * sql: `SELECT photo_id as id FROM todos WHERE photo_id IS NOT NULL`,
417
- * parameters: [],
418
- * mapper: (row) => ({
419
- * ...row,
420
- * created_at: new Date(row.created_at as string)
421
- * })
422
- * })
423
- * .watch()
424
- * // OR use .differentialWatch() for fine-grained watches.
425
- * ```
426
- */
427
- query<RowType>(query: ArrayQueryDefinition<RowType>): Query<RowType>;
428
- /**
429
- * Allows building a {@link WatchedQuery} using an existing {@link WatchCompatibleQuery}.
430
- * The watched query will use the provided {@link WatchCompatibleQuery.execute} method to query results.
431
- *
432
- * @example
433
- * ```javascript
434
- *
435
- * // Potentially a query from an ORM like Drizzle
436
- * const query = db.select().from(lists);
437
- *
438
- * const watchedTodos = powersync.customQuery(query)
439
- * .watch()
440
- * // OR use .differentialWatch() for fine-grained watches.
441
- * ```
442
- */
443
- customQuery<RowType>(query: WatchCompatibleQuery<RowType[]>): Query<RowType>;
444
393
  /**
445
394
  * Execute a read query every time the source tables are modified.
446
395
  * Use {@link SQLWatchOptions.throttleMs} to specify the minimum interval between queries.
@@ -489,7 +438,7 @@ export declare abstract class AbstractPowerSyncDatabase extends BaseObserver<Pow
489
438
  * }
490
439
  * ```
491
440
  */
492
- onChange(options?: SQLOnChangeOptions): AsyncIterable<WatchOnChangeEvent>;
441
+ onChange(options?: SQLWatchOptions): AsyncIterable<WatchOnChangeEvent>;
493
442
  /**
494
443
  * See {@link onChangeWithCallback}.
495
444
  *
@@ -504,7 +453,7 @@ export declare abstract class AbstractPowerSyncDatabase extends BaseObserver<Pow
504
453
  * }
505
454
  * ```
506
455
  */
507
- onChange(handler?: WatchOnChangeHandler, options?: SQLOnChangeOptions): () => void;
456
+ onChange(handler?: WatchOnChangeHandler, options?: SQLWatchOptions): () => void;
508
457
  /**
509
458
  * Invoke the provided callback on any changes to any of the specified tables.
510
459
  *
@@ -517,7 +466,7 @@ export declare abstract class AbstractPowerSyncDatabase extends BaseObserver<Pow
517
466
  * @param options Options for configuring watch behavior
518
467
  * @returns A dispose function to stop watching for changes
519
468
  */
520
- onChangeWithCallback(handler?: WatchOnChangeHandler, options?: SQLOnChangeOptions): () => void;
469
+ onChangeWithCallback(handler?: WatchOnChangeHandler, options?: SQLWatchOptions): () => void;
521
470
  /**
522
471
  * Create a Stream of changes to any of the specified tables.
523
472
  *
@@ -9,15 +9,13 @@ import { BaseObserver } from '../utils/BaseObserver.js';
9
9
  import { ControlledExecutor } from '../utils/ControlledExecutor.js';
10
10
  import { throttleTrailing } from '../utils/async.js';
11
11
  import { ConnectionManager } from './ConnectionManager.js';
12
- import { CustomQuery } from './CustomQuery.js';
13
12
  import { isDBAdapter, isSQLOpenFactory, isSQLOpenOptions } from './SQLOpenFactory.js';
13
+ import { runOnSchemaChange } from './runOnSchemaChange.js';
14
14
  import { PSInternalTable } from './sync/bucket/BucketStorageAdapter.js';
15
15
  import { CrudBatch } from './sync/bucket/CrudBatch.js';
16
16
  import { CrudEntry } from './sync/bucket/CrudEntry.js';
17
17
  import { CrudTransaction } from './sync/bucket/CrudTransaction.js';
18
18
  import { DEFAULT_CRUD_UPLOAD_THROTTLE_MS, DEFAULT_RETRY_DELAY_MS } from './sync/stream/AbstractStreamingSyncImplementation.js';
19
- import { DEFAULT_WATCH_THROTTLE_MS } from './watched/WatchedQuery.js';
20
- import { OnChangeQueryProcessor } from './watched/processors/OnChangeQueryProcessor.js';
21
19
  const POWERSYNC_TABLE_MATCH = /(^ps_data__|^ps_data_local__)/;
22
20
  const DEFAULT_DISCONNECT_CLEAR_OPTIONS = {
23
21
  clearLocal: true
@@ -25,6 +23,7 @@ const DEFAULT_DISCONNECT_CLEAR_OPTIONS = {
25
23
  export const DEFAULT_POWERSYNC_CLOSE_OPTIONS = {
26
24
  disconnect: true
27
25
  };
26
+ export const DEFAULT_WATCH_THROTTLE_MS = 30;
28
27
  export const DEFAULT_POWERSYNC_DB_OPTIONS = {
29
28
  retryDelayMs: 5000,
30
29
  logger: Logger.get('PowerSyncDatabase'),
@@ -299,7 +298,9 @@ export class AbstractPowerSyncDatabase extends BaseObserver {
299
298
  * Connects to stream of events from the PowerSync instance.
300
299
  */
301
300
  async connect(connector, options) {
302
- return this.connectionManager.connect(connector, options);
301
+ const resolvedOptions = options ?? {};
302
+ resolvedOptions.serializedSchema = this.schema.toJSON();
303
+ return this.connectionManager.connect(connector, resolvedOptions);
303
304
  }
304
305
  /**
305
306
  * Close the sync connection.
@@ -342,7 +343,6 @@ export class AbstractPowerSyncDatabase extends BaseObserver {
342
343
  if (this.closed) {
343
344
  return;
344
345
  }
345
- await this.iterateAsyncListeners(async (cb) => cb.closing?.());
346
346
  const { disconnect } = options;
347
347
  if (disconnect) {
348
348
  await this.disconnect();
@@ -350,7 +350,6 @@ export class AbstractPowerSyncDatabase extends BaseObserver {
350
350
  await this.connectionManager.close();
351
351
  await this.database.close();
352
352
  this.closed = true;
353
- await this.iterateAsyncListeners(async (cb) => cb.closed?.());
354
353
  }
355
354
  /**
356
355
  * Get upload queue size estimate and count.
@@ -597,60 +596,6 @@ export class AbstractPowerSyncDatabase extends BaseObserver {
597
596
  const options = handlerOrOptions;
598
597
  return this.watchWithAsyncGenerator(sql, parameters, options);
599
598
  }
600
- /**
601
- * Allows defining a query which can be used to build a {@link WatchedQuery}.
602
- * The defined query will be executed with {@link AbstractPowerSyncDatabase#getAll}.
603
- * An optional mapper function can be provided to transform the results.
604
- *
605
- * @example
606
- * ```javascript
607
- * const watchedTodos = powersync.query({
608
- * sql: `SELECT photo_id as id FROM todos WHERE photo_id IS NOT NULL`,
609
- * parameters: [],
610
- * mapper: (row) => ({
611
- * ...row,
612
- * created_at: new Date(row.created_at as string)
613
- * })
614
- * })
615
- * .watch()
616
- * // OR use .differentialWatch() for fine-grained watches.
617
- * ```
618
- */
619
- query(query) {
620
- const { sql, parameters = [], mapper } = query;
621
- const compatibleQuery = {
622
- compile: () => ({
623
- sql,
624
- parameters
625
- }),
626
- execute: async ({ sql, parameters }) => {
627
- const result = await this.getAll(sql, parameters);
628
- return mapper ? result.map(mapper) : result;
629
- }
630
- };
631
- return this.customQuery(compatibleQuery);
632
- }
633
- /**
634
- * Allows building a {@link WatchedQuery} using an existing {@link WatchCompatibleQuery}.
635
- * The watched query will use the provided {@link WatchCompatibleQuery.execute} method to query results.
636
- *
637
- * @example
638
- * ```javascript
639
- *
640
- * // Potentially a query from an ORM like Drizzle
641
- * const query = db.select().from(lists);
642
- *
643
- * const watchedTodos = powersync.customQuery(query)
644
- * .watch()
645
- * // OR use .differentialWatch() for fine-grained watches.
646
- * ```
647
- */
648
- customQuery(query) {
649
- return new CustomQuery({
650
- db: this,
651
- query
652
- });
653
- }
654
599
  /**
655
600
  * Execute a read query every time the source tables are modified.
656
601
  * Use {@link SQLWatchOptions.throttleMs} to specify the minimum interval between queries.
@@ -668,41 +613,35 @@ export class AbstractPowerSyncDatabase extends BaseObserver {
668
613
  if (!onResult) {
669
614
  throw new Error('onResult is required');
670
615
  }
671
- const { comparator } = options ?? {};
672
- // This API yields a QueryResult type.
673
- // This is not a standard Array result, which makes it incompatible with the .query API.
674
- const watchedQuery = new OnChangeQueryProcessor({
675
- db: this,
676
- comparator,
677
- placeholderData: null,
678
- watchOptions: {
679
- query: {
680
- compile: () => ({
681
- sql: sql,
682
- parameters: parameters ?? []
683
- }),
684
- execute: () => this.executeReadOnly(sql, parameters)
685
- },
686
- reportFetching: false,
687
- throttleMs: options?.throttleMs ?? DEFAULT_WATCH_THROTTLE_MS
616
+ const watchQuery = async (abortSignal) => {
617
+ try {
618
+ const resolvedTables = await this.resolveTables(sql, parameters, options);
619
+ // Fetch initial data
620
+ const result = await this.executeReadOnly(sql, parameters);
621
+ onResult(result);
622
+ this.onChangeWithCallback({
623
+ onChange: async () => {
624
+ try {
625
+ const result = await this.executeReadOnly(sql, parameters);
626
+ onResult(result);
627
+ }
628
+ catch (error) {
629
+ onError?.(error);
630
+ }
631
+ },
632
+ onError
633
+ }, {
634
+ ...(options ?? {}),
635
+ tables: resolvedTables,
636
+ // Override the abort signal since we intercept it
637
+ signal: abortSignal
638
+ });
688
639
  }
689
- });
690
- const dispose = watchedQuery.registerListener({
691
- onData: (data) => {
692
- if (!data) {
693
- // This should not happen. We only use null for the initial data.
694
- return;
695
- }
696
- onResult(data);
697
- },
698
- onError: (error) => {
699
- onError(error);
640
+ catch (error) {
641
+ onError?.(error);
700
642
  }
701
- });
702
- options?.signal?.addEventListener('abort', () => {
703
- dispose();
704
- watchedQuery.close();
705
- });
643
+ };
644
+ runOnSchemaChange(watchQuery, this, options);
706
645
  }
707
646
  /**
708
647
  * Execute a read query every time the source tables are modified.
@@ -792,9 +731,6 @@ export class AbstractPowerSyncDatabase extends BaseObserver {
792
731
  return;
793
732
  executor.schedule({ changedTables: intersection });
794
733
  }), throttleMs);
795
- if (options?.triggerImmediate) {
796
- executor.schedule({ changedTables: [] });
797
- }
798
734
  const dispose = this.database.registerListener({
799
735
  tablesUpdated: async (update) => {
800
736
  try {
@@ -1,7 +1,7 @@
1
1
  import { ILogger } from 'js-logger';
2
2
  import { BaseListener, BaseObserver } from '../utils/BaseObserver.js';
3
3
  import { PowerSyncBackendConnector } from './connection/PowerSyncBackendConnector.js';
4
- import { PowerSyncConnectionOptions, StreamingSyncImplementation } from './sync/stream/AbstractStreamingSyncImplementation.js';
4
+ import { InternalConnectionOptions, StreamingSyncImplementation } from './sync/stream/AbstractStreamingSyncImplementation.js';
5
5
  /**
6
6
  * @internal
7
7
  */
@@ -17,12 +17,12 @@ export interface ConnectionManagerSyncImplementationResult {
17
17
  * @internal
18
18
  */
19
19
  export interface ConnectionManagerOptions {
20
- createSyncImplementation(connector: PowerSyncBackendConnector, options: PowerSyncConnectionOptions): Promise<ConnectionManagerSyncImplementationResult>;
20
+ createSyncImplementation(connector: PowerSyncBackendConnector, options: InternalConnectionOptions): Promise<ConnectionManagerSyncImplementationResult>;
21
21
  logger: ILogger;
22
22
  }
23
23
  type StoredConnectionOptions = {
24
24
  connector: PowerSyncBackendConnector;
25
- options: PowerSyncConnectionOptions;
25
+ options: InternalConnectionOptions;
26
26
  };
27
27
  /**
28
28
  * @internal
@@ -66,7 +66,7 @@ export declare class ConnectionManager extends BaseObserver<ConnectionManagerLis
66
66
  constructor(options: ConnectionManagerOptions);
67
67
  get logger(): ILogger;
68
68
  close(): Promise<void>;
69
- connect(connector: PowerSyncBackendConnector, options?: PowerSyncConnectionOptions): Promise<void>;
69
+ connect(connector: PowerSyncBackendConnector, options: InternalConnectionOptions): Promise<void>;
70
70
  protected connectInternal(): Promise<void>;
71
71
  /**
72
72
  * Close the sync connection.
@@ -1,4 +1,4 @@
1
- import { BaseListener, BaseObserverInterface, Disposable } from '../../../utils/BaseObserver.js';
1
+ import { BaseListener, BaseObserver, Disposable } from '../../../utils/BaseObserver.js';
2
2
  import { CrudBatch } from './CrudBatch.js';
3
3
  import { CrudEntry, OpId } from './CrudEntry.js';
4
4
  import { SyncDataBatch } from './SyncDataBatch.js';
@@ -62,7 +62,7 @@ export declare enum PowerSyncControlCommand {
62
62
  export interface BucketStorageListener extends BaseListener {
63
63
  crudUpdate: () => void;
64
64
  }
65
- export interface BucketStorageAdapter extends BaseObserverInterface<BucketStorageListener>, Disposable {
65
+ export interface BucketStorageAdapter extends BaseObserver<BucketStorageListener>, Disposable {
66
66
  init(): Promise<void>;
67
67
  saveSyncData(batch: SyncDataBatch, fixedKeyFormat?: boolean): Promise<void>;
68
68
  removeBuckets(buckets: string[]): Promise<void>;
@@ -1,6 +1,6 @@
1
1
  import Logger, { ILogger } from 'js-logger';
2
2
  import { SyncStatus, SyncStatusOptions } from '../../../db/crud/SyncStatus.js';
3
- import { BaseListener, BaseObserver, BaseObserverInterface, Disposable } from '../../../utils/BaseObserver.js';
3
+ import { BaseListener, BaseObserver, Disposable } from '../../../utils/BaseObserver.js';
4
4
  import { BucketStorageAdapter } from '../bucket/BucketStorageAdapter.js';
5
5
  import { AbstractRemote, FetchStrategy } from './AbstractRemote.js';
6
6
  import { StreamingSyncRequestParameterType } from './streaming-sync-types.js';
@@ -88,7 +88,8 @@ export interface StreamingSyncImplementationListener extends BaseListener {
88
88
  * Configurable options to be used when connecting to the PowerSync
89
89
  * backend instance.
90
90
  */
91
- export interface PowerSyncConnectionOptions extends BaseConnectionOptions, AdditionalConnectionOptions {
91
+ export type PowerSyncConnectionOptions = Omit<InternalConnectionOptions, 'serializedSchema'>;
92
+ export interface InternalConnectionOptions extends BaseConnectionOptions, AdditionalConnectionOptions {
92
93
  }
93
94
  /** @internal */
94
95
  export interface BaseConnectionOptions {
@@ -114,6 +115,10 @@ export interface BaseConnectionOptions {
114
115
  * These parameters are passed to the sync rules, and will be available under the`user_parameters` object.
115
116
  */
116
117
  params?: Record<string, StreamingSyncRequestParameterType>;
118
+ /**
119
+ * The serialized schema - mainly used to forward information about raw tables to the sync client.
120
+ */
121
+ serializedSchema?: any;
117
122
  }
118
123
  /** @internal */
119
124
  export interface AdditionalConnectionOptions {
@@ -131,11 +136,11 @@ export interface AdditionalConnectionOptions {
131
136
  }
132
137
  /** @internal */
133
138
  export type RequiredAdditionalConnectionOptions = Required<AdditionalConnectionOptions>;
134
- export interface StreamingSyncImplementation extends BaseObserverInterface<StreamingSyncImplementationListener>, Disposable {
139
+ export interface StreamingSyncImplementation extends BaseObserver<StreamingSyncImplementationListener>, Disposable {
135
140
  /**
136
141
  * Connects to the sync service
137
142
  */
138
- connect(options?: PowerSyncConnectionOptions): Promise<void>;
143
+ connect(options?: InternalConnectionOptions): Promise<void>;
139
144
  /**
140
145
  * Disconnects from the sync services.
141
146
  * @throws if not connected or if abort is not controlled internally
@@ -164,7 +169,6 @@ export declare abstract class AbstractStreamingSyncImplementation extends BaseOb
164
169
  protected _lastSyncedAt: Date | null;
165
170
  protected options: AbstractStreamingSyncImplementationOptions;
166
171
  protected abortController: AbortController | null;
167
- protected uploadAbortController: AbortController | null;
168
172
  protected crudUpdateListener?: () => void;
169
173
  protected streamingSyncPromise?: Promise<void>;
170
174
  private isUploadingCrud;
@@ -1,6 +1,6 @@
1
1
  import Logger from 'js-logger';
2
- import { FULL_SYNC_PRIORITY } from '../../../db/crud/SyncProgress.js';
3
2
  import { SyncStatus } from '../../../db/crud/SyncStatus.js';
3
+ import { FULL_SYNC_PRIORITY } from '../../../db/crud/SyncProgress.js';
4
4
  import { AbortOperation } from '../../../utils/AbortOperation.js';
5
5
  import { BaseObserver } from '../../../utils/BaseObserver.js';
6
6
  import { throttleLeadingTrailing } from '../../../utils/async.js';
@@ -72,7 +72,8 @@ export const DEFAULT_STREAM_CONNECTION_OPTIONS = {
72
72
  connectionMethod: SyncStreamConnectionMethod.WEB_SOCKET,
73
73
  clientImplementation: DEFAULT_SYNC_CLIENT_IMPLEMENTATION,
74
74
  fetchStrategy: FetchStrategy.Buffered,
75
- params: {}
75
+ params: {},
76
+ serializedSchema: undefined
76
77
  };
77
78
  // The priority we assume when we receive checkpoint lines where no priority is set.
78
79
  // This is the default priority used by the sync service, but can be set to an arbitrary
@@ -83,9 +84,6 @@ export class AbstractStreamingSyncImplementation extends BaseObserver {
83
84
  _lastSyncedAt;
84
85
  options;
85
86
  abortController;
86
- // In rare cases, mostly for tests, uploads can be triggered without being properly connected.
87
- // This allows ensuring that all upload processes can be aborted.
88
- uploadAbortController;
89
87
  crudUpdateListener;
90
88
  streamingSyncPromise;
91
89
  isUploadingCrud = false;
@@ -162,10 +160,8 @@ export class AbstractStreamingSyncImplementation extends BaseObserver {
162
160
  return this.options.logger;
163
161
  }
164
162
  async dispose() {
165
- super.dispose();
166
163
  this.crudUpdateListener?.();
167
164
  this.crudUpdateListener = undefined;
168
- this.uploadAbortController?.abort();
169
165
  }
170
166
  async hasCompletedSync() {
171
167
  return this.options.adapter.hasCompletedSync();
@@ -184,12 +180,7 @@ export class AbstractStreamingSyncImplementation extends BaseObserver {
184
180
  * Keep track of the first item in the CRUD queue for the last `uploadCrud` iteration.
185
181
  */
186
182
  let checkedCrudItem;
187
- const controller = new AbortController();
188
- this.uploadAbortController = controller;
189
- this.abortController?.signal.addEventListener('abort', () => {
190
- controller.abort();
191
- }, { once: true });
192
- while (!controller.signal.aborted) {
183
+ while (true) {
193
184
  try {
194
185
  /**
195
186
  * This is the first item in the FIFO CRUD queue.
@@ -230,7 +221,7 @@ The next upload iteration will be delayed.`);
230
221
  uploadError: ex
231
222
  }
232
223
  });
233
- await this.delayRetry(controller.signal);
224
+ await this.delayRetry();
234
225
  if (!this.isConnected) {
235
226
  // Exit the upload loop if the sync stream is no longer connected
236
227
  break;
@@ -245,7 +236,6 @@ The next upload iteration will be delayed.`);
245
236
  });
246
237
  }
247
238
  }
248
- this.uploadAbortController = null;
249
239
  }
250
240
  });
251
241
  }
@@ -526,6 +516,8 @@ The next upload iteration will be delayed.`);
526
516
  }
527
517
  if (isStreamingSyncCheckpoint(line)) {
528
518
  targetCheckpoint = line.checkpoint;
519
+ // New checkpoint - existing validated checkpoint is no longer valid
520
+ pendingValidatedCheckpoint = null;
529
521
  const bucketsToDelete = new Set(bucketMap.keys());
530
522
  const newBuckets = new Map();
531
523
  for (const checksum of line.checkpoint.buckets) {
@@ -549,8 +541,15 @@ The next upload iteration will be delayed.`);
549
541
  return;
550
542
  }
551
543
  else if (!result.applied) {
544
+ // "Could not apply checkpoint due to local data". We need to retry after
545
+ // finishing uploads.
552
546
  pendingValidatedCheckpoint = targetCheckpoint;
553
547
  }
548
+ else {
549
+ // Nothing to retry later. This would likely already be null from the last
550
+ // checksum or checksum_diff operation, but we make sure.
551
+ pendingValidatedCheckpoint = null;
552
+ }
554
553
  }
555
554
  else if (isStreamingSyncCheckpointPartiallyComplete(line)) {
556
555
  const priority = line.partial_checkpoint_complete.priority;
@@ -587,6 +586,8 @@ The next upload iteration will be delayed.`);
587
586
  if (targetCheckpoint == null) {
588
587
  throw new Error('Checkpoint diff without previous checkpoint');
589
588
  }
589
+ // New checkpoint - existing validated checkpoint is no longer valid
590
+ pendingValidatedCheckpoint = null;
590
591
  const diff = line.checkpoint_diff;
591
592
  const newBuckets = new Map();
592
593
  for (const checksum of targetCheckpoint.buckets) {
@@ -826,9 +827,11 @@ The next upload iteration will be delayed.`);
826
827
  }
827
828
  }
828
829
  try {
829
- await control(PowerSyncControlCommand.START, JSON.stringify({
830
- parameters: resolvedOptions.params
831
- }));
830
+ const options = { parameters: resolvedOptions.params };
831
+ if (resolvedOptions.serializedSchema) {
832
+ options.schema = resolvedOptions.serializedSchema;
833
+ }
834
+ await control(PowerSyncControlCommand.START, JSON.stringify(options));
832
835
  this.notifyCompletedUploads = () => {
833
836
  controlInvocations?.enqueueData({ command: PowerSyncControlCommand.NOTIFY_CRUD_UPLOAD_COMPLETED });
834
837
  };
@@ -0,0 +1,57 @@
1
+ /**
2
+ * A pending variant of a {@link RawTable} that doesn't have a name (because it would be inferred when creating the
3
+ * schema).
4
+ */
5
+ export type RawTableType = {
6
+ /**
7
+ * The statement to run when PowerSync detects that a row needs to be inserted or updated.
8
+ */
9
+ put: PendingStatement;
10
+ /**
11
+ * The statement to run when PowerSync detects that a row needs to be deleted.
12
+ */
13
+ delete: PendingStatement;
14
+ };
15
+ /**
16
+ * A parameter to use as part of {@link PendingStatement}.
17
+ *
18
+ * For delete statements, only the `"Id"` value is supported - the sync client will replace it with the id of the row to
19
+ * be synced.
20
+ *
21
+ * For insert and replace operations, the values of columns in the table are available as parameters through
22
+ * `{Column: 'name'}`.
23
+ */
24
+ export type PendingStatementParameter = 'Id' | {
25
+ Column: string;
26
+ };
27
+ /**
28
+ * A statement that the PowerSync client should use to insert or delete data into a table managed by the user.
29
+ */
30
+ export type PendingStatement = {
31
+ sql: string;
32
+ params: PendingStatementParameter[];
33
+ };
34
+ /**
35
+ * Instructs PowerSync to sync data into a "raw" table.
36
+ *
37
+ * Since raw tables are not backed by JSON, running complex queries on them may be more efficient. Further, they allow
38
+ * using client-side table and column constraints.
39
+ *
40
+ * Note that raw tables are only supported when using the new `SyncClientImplementation.rust` sync client.
41
+ *
42
+ * @experimental Please note that this feature is experimental at the moment, and not covered by PowerSync semver or
43
+ * stability guarantees.
44
+ */
45
+ export declare class RawTable implements RawTableType {
46
+ /**
47
+ * The name of the table.
48
+ *
49
+ * This does not have to match the actual table name in the schema - {@link put} and {@link delete} are free to use
50
+ * another table. Instead, this name is used by the sync client to recognize that operations on this table (as it
51
+ * appears in the source / backend database) are to be handled specially.
52
+ */
53
+ name: string;
54
+ put: PendingStatement;
55
+ delete: PendingStatement;
56
+ constructor(name: string, type: RawTableType);
57
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Instructs PowerSync to sync data into a "raw" table.
3
+ *
4
+ * Since raw tables are not backed by JSON, running complex queries on them may be more efficient. Further, they allow
5
+ * using client-side table and column constraints.
6
+ *
7
+ * Note that raw tables are only supported when using the new `SyncClientImplementation.rust` sync client.
8
+ *
9
+ * @experimental Please note that this feature is experimental at the moment, and not covered by PowerSync semver or
10
+ * stability guarantees.
11
+ */
12
+ export class RawTable {
13
+ /**
14
+ * The name of the table.
15
+ *
16
+ * This does not have to match the actual table name in the schema - {@link put} and {@link delete} are free to use
17
+ * another table. Instead, this name is used by the sync client to recognize that operations on this table (as it
18
+ * appears in the source / backend database) are to be handled specially.
19
+ */
20
+ name;
21
+ put;
22
+ delete;
23
+ constructor(name, type) {
24
+ this.name = name;
25
+ this.put = type.put;
26
+ this.delete = type.delete;
27
+ }
28
+ }
@@ -1,3 +1,4 @@
1
+ import { RawTable, RawTableType } from './RawTable.js';
1
2
  import { RowType, Table } from './Table.js';
2
3
  type SchemaType = Record<string, Table<any>>;
3
4
  export type SchemaTableType<S extends SchemaType> = {
@@ -10,7 +11,19 @@ export declare class Schema<S extends SchemaType = SchemaType> {
10
11
  readonly types: SchemaTableType<S>;
11
12
  readonly props: S;
12
13
  readonly tables: Table[];
14
+ readonly rawTables: RawTable[];
13
15
  constructor(tables: Table[] | S);
16
+ /**
17
+ * Adds raw tables to this schema. Raw tables are identified by their name, but entirely managed by the application
18
+ * developer instead of automatically by PowerSync.
19
+ * Since raw tables are not backed by JSON, running complex queries on them may be more efficient. Further, they allow
20
+ * using client-side table and column constraints.
21
+ * Note that raw tables are only supported when using the new `SyncClientImplementation.rust` sync client.
22
+ *
23
+ * @param tables An object of (table name, raw table definition) entries.
24
+ * @experimental Note that the raw tables API is still experimental and may change in the future.
25
+ */
26
+ withRawTables(tables: Record<string, RawTableType>): void;
14
27
  validate(): void;
15
28
  toJSON(): {
16
29
  tables: {
@@ -35,6 +48,7 @@ export declare class Schema<S extends SchemaType = SchemaType> {
35
48
  }[];
36
49
  }[];
37
50
  }[];
51
+ raw_tables: RawTable[];
38
52
  };
39
53
  private convertToClassicTables;
40
54
  }