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

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.
@@ -299,7 +299,9 @@ export class AbstractPowerSyncDatabase extends BaseObserver {
299
299
  * Connects to stream of events from the PowerSync instance.
300
300
  */
301
301
  async connect(connector, options) {
302
- return this.connectionManager.connect(connector, options);
302
+ const resolvedOptions = options ?? {};
303
+ resolvedOptions.serializedSchema = this.schema.toJSON();
304
+ return this.connectionManager.connect(connector, resolvedOptions);
303
305
  }
304
306
  /**
305
307
  * Close the sync connection.
@@ -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.
@@ -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 {
@@ -135,7 +140,7 @@ export interface StreamingSyncImplementation extends BaseObserverInterface<Strea
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
@@ -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
@@ -526,6 +527,8 @@ The next upload iteration will be delayed.`);
526
527
  }
527
528
  if (isStreamingSyncCheckpoint(line)) {
528
529
  targetCheckpoint = line.checkpoint;
530
+ // New checkpoint - existing validated checkpoint is no longer valid
531
+ pendingValidatedCheckpoint = null;
529
532
  const bucketsToDelete = new Set(bucketMap.keys());
530
533
  const newBuckets = new Map();
531
534
  for (const checksum of line.checkpoint.buckets) {
@@ -549,8 +552,15 @@ The next upload iteration will be delayed.`);
549
552
  return;
550
553
  }
551
554
  else if (!result.applied) {
555
+ // "Could not apply checkpoint due to local data". We need to retry after
556
+ // finishing uploads.
552
557
  pendingValidatedCheckpoint = targetCheckpoint;
553
558
  }
559
+ else {
560
+ // Nothing to retry later. This would likely already be null from the last
561
+ // checksum or checksum_diff operation, but we make sure.
562
+ pendingValidatedCheckpoint = null;
563
+ }
554
564
  }
555
565
  else if (isStreamingSyncCheckpointPartiallyComplete(line)) {
556
566
  const priority = line.partial_checkpoint_complete.priority;
@@ -587,6 +597,8 @@ The next upload iteration will be delayed.`);
587
597
  if (targetCheckpoint == null) {
588
598
  throw new Error('Checkpoint diff without previous checkpoint');
589
599
  }
600
+ // New checkpoint - existing validated checkpoint is no longer valid
601
+ pendingValidatedCheckpoint = null;
590
602
  const diff = line.checkpoint_diff;
591
603
  const newBuckets = new Map();
592
604
  for (const checksum of targetCheckpoint.buckets) {
@@ -826,9 +838,11 @@ The next upload iteration will be delayed.`);
826
838
  }
827
839
  }
828
840
  try {
829
- await control(PowerSyncControlCommand.START, JSON.stringify({
830
- parameters: resolvedOptions.params
831
- }));
841
+ const options = { parameters: resolvedOptions.params };
842
+ if (resolvedOptions.serializedSchema) {
843
+ options.schema = resolvedOptions.serializedSchema;
844
+ }
845
+ await control(PowerSyncControlCommand.START, JSON.stringify(options));
832
846
  this.notifyCompletedUploads = () => {
833
847
  controlInvocations?.enqueueData({ command: PowerSyncControlCommand.NOTIFY_CRUD_UPLOAD_COMPLETED });
834
848
  };
@@ -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
  }
@@ -1,3 +1,4 @@
1
+ import { RawTable } from './RawTable.js';
1
2
  /**
2
3
  * A schema is a collection of tables. It is used to define the structure of a database.
3
4
  */
@@ -8,6 +9,7 @@ export class Schema {
8
9
  types;
9
10
  props;
10
11
  tables;
12
+ rawTables;
11
13
  constructor(tables) {
12
14
  if (Array.isArray(tables)) {
13
15
  /*
@@ -26,6 +28,22 @@ export class Schema {
26
28
  this.props = tables;
27
29
  this.tables = this.convertToClassicTables(this.props);
28
30
  }
31
+ this.rawTables = [];
32
+ }
33
+ /**
34
+ * Adds raw tables to this schema. Raw tables are identified by their name, but entirely managed by the application
35
+ * developer instead of automatically by PowerSync.
36
+ * Since raw tables are not backed by JSON, running complex queries on them may be more efficient. Further, they allow
37
+ * using client-side table and column constraints.
38
+ * Note that raw tables are only supported when using the new `SyncClientImplementation.rust` sync client.
39
+ *
40
+ * @param tables An object of (table name, raw table definition) entries.
41
+ * @experimental Note that the raw tables API is still experimental and may change in the future.
42
+ */
43
+ withRawTables(tables) {
44
+ for (const [name, rawTableDefinition] of Object.entries(tables)) {
45
+ this.rawTables.push(new RawTable(name, rawTableDefinition));
46
+ }
29
47
  }
30
48
  validate() {
31
49
  for (const table of this.tables) {
@@ -35,7 +53,8 @@ export class Schema {
35
53
  toJSON() {
36
54
  return {
37
55
  // This is required because "name" field is not present in TableV2
38
- tables: this.tables.map((t) => t.toJSON())
56
+ tables: this.tables.map((t) => t.toJSON()),
57
+ raw_tables: this.rawTables
39
58
  };
40
59
  }
41
60
  convertToClassicTables(props) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@powersync/common",
3
- "version": "0.0.0-dev-20250714151300",
3
+ "version": "0.0.0-dev-20250715080712",
4
4
  "publishConfig": {
5
5
  "registry": "https://registry.npmjs.org/",
6
6
  "access": "public"