@powersync/common 1.20.2 → 1.21.0

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.
@@ -76,6 +76,7 @@ export interface WatchOnChangeHandler {
76
76
  }
77
77
  export interface PowerSyncDBListener extends StreamingSyncImplementationListener {
78
78
  initialized: () => void;
79
+ schemaChanged: (schema: Schema) => void;
79
80
  }
80
81
  export interface PowerSyncCloseOptions {
81
82
  /**
@@ -14,6 +14,7 @@ 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
16
  import { DEFAULT_CRUD_UPLOAD_THROTTLE_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
@@ -211,6 +212,8 @@ export class AbstractPowerSyncDatabase extends BaseObserver {
211
212
  }
212
213
  this._schema = schema;
213
214
  await this.database.execute('SELECT powersync_replace_schema(?)', [JSON.stringify(this.schema.toJSON())]);
215
+ await this.database.refreshSchema();
216
+ this.iterateListeners(async (cb) => cb.schemaChanged?.(schema));
214
217
  }
215
218
  /**
216
219
  * Wait for initialization to complete.
@@ -503,7 +506,7 @@ export class AbstractPowerSyncDatabase extends BaseObserver {
503
506
  if (!onResult) {
504
507
  throw new Error('onResult is required');
505
508
  }
506
- (async () => {
509
+ const watchQuery = async (abortSignal) => {
507
510
  try {
508
511
  const resolvedTables = await this.resolveTables(sql, parameters, options);
509
512
  // Fetch initial data
@@ -522,13 +525,16 @@ export class AbstractPowerSyncDatabase extends BaseObserver {
522
525
  onError
523
526
  }, {
524
527
  ...(options ?? {}),
525
- tables: resolvedTables
528
+ tables: resolvedTables,
529
+ // Override the abort signal since we intercept it
530
+ signal: abortSignal
526
531
  });
527
532
  }
528
533
  catch (error) {
529
534
  onError?.(error);
530
535
  }
531
- })();
536
+ };
537
+ runOnSchemaChange(watchQuery, this, options);
532
538
  }
533
539
  /**
534
540
  * Execute a read query every time the source tables are modified.
@@ -537,17 +543,18 @@ export class AbstractPowerSyncDatabase extends BaseObserver {
537
543
  */
538
544
  watchWithAsyncGenerator(sql, parameters, options) {
539
545
  return new EventIterator((eventOptions) => {
540
- (async () => {
541
- const resolvedTables = await this.resolveTables(sql, parameters, options);
542
- // Fetch initial data
543
- eventOptions.push(await this.executeReadOnly(sql, parameters));
544
- for await (const event of this.onChangeWithAsyncGenerator({
545
- ...(options ?? {}),
546
- tables: resolvedTables
547
- })) {
548
- eventOptions.push(await this.executeReadOnly(sql, parameters));
546
+ const handler = {
547
+ onResult: (result) => {
548
+ eventOptions.push(result);
549
+ },
550
+ onError: (error) => {
551
+ eventOptions.fail(error);
549
552
  }
550
- })();
553
+ };
554
+ this.watchWithCallback(sql, parameters, handler, options);
555
+ options?.signal?.addEventListener('abort', () => {
556
+ eventOptions.stop();
557
+ });
551
558
  });
552
559
  }
553
560
  async resolveTables(sql, parameters, options) {
@@ -0,0 +1,2 @@
1
+ import { AbstractPowerSyncDatabase, SQLWatchOptions } from './AbstractPowerSyncDatabase.js';
2
+ export declare function runOnSchemaChange(callback: (signal: AbortSignal) => void, db: AbstractPowerSyncDatabase, options?: SQLWatchOptions): void;
@@ -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
+ }
@@ -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[];
package/lib/index.d.ts CHANGED
@@ -4,6 +4,7 @@ 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';
7
8
  export { UpdateType, CrudEntry, OpId } from './client/sync/bucket/CrudEntry.js';
8
9
  export * from './client/sync/bucket/SqliteBucketStorage.js';
9
10
  export * from './client/sync/bucket/CrudBatch.js';
package/lib/index.js CHANGED
@@ -4,6 +4,7 @@ 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';
7
8
  export { UpdateType, CrudEntry } from './client/sync/bucket/CrudEntry.js';
8
9
  export * from './client/sync/bucket/SqliteBucketStorage.js';
9
10
  export * from './client/sync/bucket/CrudBatch.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@powersync/common",
3
- "version": "1.20.2",
3
+ "version": "1.21.0",
4
4
  "publishConfig": {
5
5
  "registry": "https://registry.npmjs.org/",
6
6
  "access": "public"