@powersync/common 0.0.0-dev-20240903063630 → 0.0.0-dev-20240920093037

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 (104) hide show
  1. package/dist/bundle.mjs +18 -0
  2. package/dist/bundle.mjs.map +1 -0
  3. package/lib/client/AbstractPowerSyncDatabase.d.ts +22 -15
  4. package/lib/client/AbstractPowerSyncDatabase.js +51 -36
  5. package/lib/client/AbstractPowerSyncOpenFactory.d.ts +5 -5
  6. package/lib/client/AbstractPowerSyncOpenFactory.js +0 -1
  7. package/lib/client/SQLOpenFactory.d.ts +1 -1
  8. package/lib/client/SQLOpenFactory.js +0 -1
  9. package/lib/client/connection/PowerSyncBackendConnector.d.ts +2 -2
  10. package/lib/client/connection/PowerSyncBackendConnector.js +0 -1
  11. package/lib/client/connection/PowerSyncCredentials.js +0 -1
  12. package/lib/client/constants.js +0 -1
  13. package/lib/client/sync/bucket/BucketStorageAdapter.d.ts +8 -4
  14. package/lib/client/sync/bucket/BucketStorageAdapter.js +0 -1
  15. package/lib/client/sync/bucket/CrudBatch.d.ts +1 -1
  16. package/lib/client/sync/bucket/CrudBatch.js +0 -1
  17. package/lib/client/sync/bucket/CrudEntry.js +0 -1
  18. package/lib/client/sync/bucket/CrudTransaction.d.ts +2 -2
  19. package/lib/client/sync/bucket/CrudTransaction.js +1 -2
  20. package/lib/client/sync/bucket/OpType.js +0 -1
  21. package/lib/client/sync/bucket/OplogEntry.d.ts +2 -2
  22. package/lib/client/sync/bucket/OplogEntry.js +1 -2
  23. package/lib/client/sync/bucket/SqliteBucketStorage.d.ts +9 -6
  24. package/lib/client/sync/bucket/SqliteBucketStorage.js +28 -13
  25. package/lib/client/sync/bucket/SyncDataBatch.d.ts +1 -1
  26. package/lib/client/sync/bucket/SyncDataBatch.js +1 -2
  27. package/lib/client/sync/bucket/SyncDataBucket.d.ts +2 -2
  28. package/lib/client/sync/bucket/SyncDataBucket.js +1 -2
  29. package/lib/client/sync/stream/AbstractRemote.d.ts +8 -6
  30. package/lib/client/sync/stream/AbstractRemote.js +17 -7
  31. package/lib/client/sync/stream/AbstractStreamingSyncImplementation.d.ts +8 -8
  32. package/lib/client/sync/stream/AbstractStreamingSyncImplementation.js +14 -11
  33. package/lib/client/sync/stream/streaming-sync-types.d.ts +4 -3
  34. package/lib/client/sync/stream/streaming-sync-types.js +0 -1
  35. package/lib/db/DBAdapter.d.ts +1 -1
  36. package/lib/db/DBAdapter.js +0 -1
  37. package/lib/db/crud/SyncStatus.js +0 -1
  38. package/lib/db/crud/UploadQueueStatus.js +0 -1
  39. package/lib/db/schema/Column.d.ts +30 -0
  40. package/lib/db/{Column.js → schema/Column.js} +17 -1
  41. package/lib/db/schema/Index.d.ts +3 -3
  42. package/lib/db/schema/Index.js +1 -2
  43. package/lib/db/schema/IndexedColumn.d.ts +2 -2
  44. package/lib/db/schema/IndexedColumn.js +1 -2
  45. package/lib/db/schema/Schema.d.ts +6 -7
  46. package/lib/db/schema/Schema.js +11 -3
  47. package/lib/db/schema/Table.d.ts +90 -10
  48. package/lib/db/schema/Table.js +61 -11
  49. package/lib/db/schema/TableV2.d.ts +8 -30
  50. package/lib/db/schema/TableV2.js +5 -62
  51. package/lib/index.d.ts +34 -35
  52. package/lib/index.js +34 -36
  53. package/lib/types/types.js +0 -1
  54. package/lib/utils/AbortOperation.js +0 -1
  55. package/lib/utils/BaseObserver.js +0 -1
  56. package/lib/utils/ControlledExecutor.js +0 -1
  57. package/lib/utils/DataStream.d.ts +1 -1
  58. package/lib/utils/DataStream.js +1 -2
  59. package/lib/utils/mutex.js +0 -1
  60. package/lib/utils/parseQuery.d.ts +1 -1
  61. package/lib/utils/parseQuery.js +0 -1
  62. package/lib/utils/throttle.d.ts +14 -0
  63. package/lib/utils/throttle.js +45 -0
  64. package/package.json +13 -5
  65. package/dist/index.js +0 -16
  66. package/lib/client/AbstractPowerSyncDatabase.js.map +0 -1
  67. package/lib/client/AbstractPowerSyncOpenFactory.js.map +0 -1
  68. package/lib/client/SQLOpenFactory.js.map +0 -1
  69. package/lib/client/connection/PowerSyncBackendConnector.js.map +0 -1
  70. package/lib/client/connection/PowerSyncCredentials.js.map +0 -1
  71. package/lib/client/constants.js.map +0 -1
  72. package/lib/client/sync/bucket/BucketStorageAdapter.js.map +0 -1
  73. package/lib/client/sync/bucket/CrudBatch.js.map +0 -1
  74. package/lib/client/sync/bucket/CrudEntry.js.map +0 -1
  75. package/lib/client/sync/bucket/CrudTransaction.js.map +0 -1
  76. package/lib/client/sync/bucket/OpType.js.map +0 -1
  77. package/lib/client/sync/bucket/OplogEntry.js.map +0 -1
  78. package/lib/client/sync/bucket/SqliteBucketStorage.js.map +0 -1
  79. package/lib/client/sync/bucket/SyncDataBatch.js.map +0 -1
  80. package/lib/client/sync/bucket/SyncDataBucket.js.map +0 -1
  81. package/lib/client/sync/stream/AbstractRemote.js.map +0 -1
  82. package/lib/client/sync/stream/AbstractStreamingSyncImplementation.js.map +0 -1
  83. package/lib/client/sync/stream/streaming-sync-types.js.map +0 -1
  84. package/lib/db/Column.d.ts +0 -19
  85. package/lib/db/Column.js.map +0 -1
  86. package/lib/db/DBAdapter.js.map +0 -1
  87. package/lib/db/crud/SyncStatus.js.map +0 -1
  88. package/lib/db/crud/UploadQueueStatus.js.map +0 -1
  89. package/lib/db/schema/Index.js.map +0 -1
  90. package/lib/db/schema/IndexedColumn.js.map +0 -1
  91. package/lib/db/schema/Schema.js.map +0 -1
  92. package/lib/db/schema/Table.js.map +0 -1
  93. package/lib/db/schema/TableV2.js.map +0 -1
  94. package/lib/index.js.map +0 -1
  95. package/lib/types/types.js.map +0 -1
  96. package/lib/utils/AbortOperation.js.map +0 -1
  97. package/lib/utils/BaseObserver.js.map +0 -1
  98. package/lib/utils/ControlledExecutor.js.map +0 -1
  99. package/lib/utils/DataStream.js.map +0 -1
  100. package/lib/utils/mutex.js.map +0 -1
  101. package/lib/utils/parseQuery.js.map +0 -1
  102. package/lib/utils/strings.d.ts +0 -3
  103. package/lib/utils/strings.js +0 -10
  104. package/lib/utils/strings.js.map +0 -1
@@ -1,16 +1,16 @@
1
1
  import { Mutex } from 'async-mutex';
2
- import { ILogger } from 'js-logger';
3
- import { DBAdapter, QueryResult, Transaction } from '../db/DBAdapter';
4
- import { SyncStatus } from '../db/crud/SyncStatus';
5
- import { UploadQueueStats } from '../db/crud/UploadQueueStatus';
6
- import { Schema } from '../db/schema/Schema';
7
- import { BaseObserver } from '../utils/BaseObserver';
8
- import { SQLOpenFactory, SQLOpenOptions } from './SQLOpenFactory';
9
- import { PowerSyncBackendConnector } from './connection/PowerSyncBackendConnector';
10
- import { BucketStorageAdapter } from './sync/bucket/BucketStorageAdapter';
11
- import { CrudBatch } from './sync/bucket/CrudBatch';
12
- import { CrudTransaction } from './sync/bucket/CrudTransaction';
13
- import { AbstractStreamingSyncImplementation, PowerSyncConnectionOptions, StreamingSyncImplementation, StreamingSyncImplementationListener } from './sync/stream/AbstractStreamingSyncImplementation';
2
+ import Logger, { ILogger } from 'js-logger';
3
+ import { DBAdapter, QueryResult, Transaction } from '../db/DBAdapter.js';
4
+ import { SyncStatus } from '../db/crud/SyncStatus.js';
5
+ import { UploadQueueStats } from '../db/crud/UploadQueueStatus.js';
6
+ import { Schema } from '../db/schema/Schema.js';
7
+ import { BaseObserver } from '../utils/BaseObserver.js';
8
+ import { SQLOpenFactory, SQLOpenOptions } from './SQLOpenFactory.js';
9
+ import { PowerSyncBackendConnector } from './connection/PowerSyncBackendConnector.js';
10
+ import { BucketStorageAdapter } from './sync/bucket/BucketStorageAdapter.js';
11
+ import { CrudBatch } from './sync/bucket/CrudBatch.js';
12
+ import { CrudTransaction } from './sync/bucket/CrudTransaction.js';
13
+ import { PowerSyncConnectionOptions, StreamingSyncImplementation, StreamingSyncImplementationListener } 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;
@@ -87,7 +87,7 @@ export declare const DEFAULT_POWERSYNC_CLOSE_OPTIONS: PowerSyncCloseOptions;
87
87
  export declare const DEFAULT_WATCH_THROTTLE_MS = 30;
88
88
  export declare const DEFAULT_POWERSYNC_DB_OPTIONS: {
89
89
  retryDelay: number;
90
- logger: ILogger;
90
+ logger: Logger.ILogger;
91
91
  crudUploadThrottleMs: number;
92
92
  };
93
93
  /**
@@ -132,7 +132,7 @@ export declare abstract class AbstractPowerSyncDatabase extends BaseObserver<Pow
132
132
  * Schema used for the local database.
133
133
  */
134
134
  get schema(): Schema<{
135
- [x: string]: import("..").TableV2<any>;
135
+ [x: string]: import("../index.js").Table<any>;
136
136
  }>;
137
137
  /**
138
138
  * The underlying database.
@@ -148,7 +148,7 @@ export declare abstract class AbstractPowerSyncDatabase extends BaseObserver<Pow
148
148
  * Opens the DBAdapter given open options using a default open factory
149
149
  */
150
150
  protected abstract openDBAdapter(options: PowerSyncDatabaseOptionsWithSettings): DBAdapter;
151
- protected abstract generateSyncStreamImplementation(connector: PowerSyncBackendConnector): AbstractStreamingSyncImplementation;
151
+ protected abstract generateSyncStreamImplementation(connector: PowerSyncBackendConnector): StreamingSyncImplementation;
152
152
  protected abstract generateBucketStorageAdapter(): BucketStorageAdapter;
153
153
  /**
154
154
  * @returns A promise which will resolve once initialization is completed.
@@ -168,6 +168,7 @@ export declare abstract class AbstractPowerSyncDatabase extends BaseObserver<Pow
168
168
  * This is to be automatically executed in the constructor.
169
169
  */
170
170
  protected initialize(): Promise<void>;
171
+ private _loadVersion;
171
172
  protected updateHasSynced(): Promise<void>;
172
173
  /**
173
174
  * Replace the schema with a new version. This is for advanced use cases - typically the schema should just be specified once in the constructor.
@@ -244,6 +245,12 @@ export declare abstract class AbstractPowerSyncDatabase extends BaseObserver<Pow
244
245
  * All data for the transaction is loaded into memory.
245
246
  */
246
247
  getNextCrudTransaction(): Promise<CrudTransaction | null>;
248
+ /**
249
+ * Get an unique client id for this database.
250
+ *
251
+ * The id is not reset when the database is cleared, only when the database is deleted.
252
+ */
253
+ getClientId(): Promise<string>;
247
254
  private handleCrudCheckpoint;
248
255
  /**
249
256
  * Execute a write (INSERT/UPDATE/DELETE) query
@@ -1,20 +1,19 @@
1
1
  import { Mutex } from 'async-mutex';
2
2
  import { EventIterator } from 'event-iterator';
3
3
  import Logger from 'js-logger';
4
- import throttle from 'lodash/throttle';
5
- import { isBatchedUpdateNotification } from '../db/DBAdapter';
6
- import { SyncStatus } from '../db/crud/SyncStatus';
7
- import { UploadQueueStats } from '../db/crud/UploadQueueStatus';
8
- import { BaseObserver } from '../utils/BaseObserver';
9
- import { ControlledExecutor } from '../utils/ControlledExecutor';
10
- import { mutexRunExclusive } from '../utils/mutex';
11
- import { quoteIdentifier } from '../utils/strings';
12
- import { isDBAdapter, isSQLOpenFactory, isSQLOpenOptions } from './SQLOpenFactory';
13
- import { PSInternalTable } from './sync/bucket/BucketStorageAdapter';
14
- import { CrudBatch } from './sync/bucket/CrudBatch';
15
- import { CrudEntry } from './sync/bucket/CrudEntry';
16
- import { CrudTransaction } from './sync/bucket/CrudTransaction';
17
- import { DEFAULT_CRUD_UPLOAD_THROTTLE_MS } from './sync/stream/AbstractStreamingSyncImplementation';
4
+ import { isBatchedUpdateNotification } from '../db/DBAdapter.js';
5
+ import { SyncStatus } from '../db/crud/SyncStatus.js';
6
+ import { UploadQueueStats } from '../db/crud/UploadQueueStatus.js';
7
+ import { BaseObserver } from '../utils/BaseObserver.js';
8
+ import { ControlledExecutor } from '../utils/ControlledExecutor.js';
9
+ import { mutexRunExclusive } from '../utils/mutex.js';
10
+ import { throttleTrailing } from '../utils/throttle.js';
11
+ import { isDBAdapter, isSQLOpenFactory, isSQLOpenOptions } from './SQLOpenFactory.js';
12
+ import { PSInternalTable } from './sync/bucket/BucketStorageAdapter.js';
13
+ import { CrudBatch } from './sync/bucket/CrudBatch.js';
14
+ import { CrudEntry } from './sync/bucket/CrudEntry.js';
15
+ import { CrudTransaction } from './sync/bucket/CrudTransaction.js';
16
+ import { DEFAULT_CRUD_UPLOAD_THROTTLE_MS } from './sync/stream/AbstractStreamingSyncImplementation.js';
18
17
  const POWERSYNC_TABLE_MATCH = /(^ps_data__|^ps_data_local__)/;
19
18
  const DEFAULT_DISCONNECT_CLEAR_OPTIONS = {
20
19
  clearLocal: true
@@ -151,19 +150,41 @@ export class AbstractPowerSyncDatabase extends BaseObserver {
151
150
  async initialize() {
152
151
  await this._initialize();
153
152
  await this.bucketStorageAdapter.init();
154
- const version = await this.database.execute('SELECT powersync_rs_version()');
155
- this.sdkVersion = version.rows?.item(0)['powersync_rs_version()'] ?? '';
153
+ await this._loadVersion();
156
154
  await this.updateSchema(this.options.schema);
157
155
  await this.updateHasSynced();
158
156
  await this.database.execute('PRAGMA RECURSIVE_TRIGGERS=TRUE');
159
157
  this.ready = true;
160
158
  this.iterateListeners((cb) => cb.initialized?.());
161
159
  }
160
+ async _loadVersion() {
161
+ try {
162
+ const { version } = await this.database.get('SELECT powersync_rs_version() as version');
163
+ this.sdkVersion = version;
164
+ }
165
+ catch (e) {
166
+ throw new Error(`The powersync extension is not loaded correctly. Details: ${e.message}`);
167
+ }
168
+ let versionInts;
169
+ try {
170
+ versionInts = this.sdkVersion.split(/[.\/]/)
171
+ .slice(0, 3)
172
+ .map((n) => parseInt(n));
173
+ }
174
+ catch (e) {
175
+ throw new Error(`Unsupported powersync extension version. Need ^0.2.0, got: ${this.sdkVersion}. Details: ${e.message}`);
176
+ }
177
+ // Validate ^0.2.0
178
+ if (versionInts[0] != 0 || versionInts[1] != 2 || versionInts[2] < 0) {
179
+ throw new Error(`Unsupported powersync extension version. Need ^0.2.0, got: ${this.sdkVersion}`);
180
+ }
181
+ }
162
182
  async updateHasSynced() {
163
- const result = await this.database.getOptional('SELECT 1 FROM ps_buckets WHERE last_applied_op > 0 LIMIT 1');
164
- const hasSynced = !!result;
183
+ const result = await this.database.get('SELECT powersync_last_synced_at() as synced_at');
184
+ const hasSynced = result.synced_at != null;
185
+ const syncedAt = result.synced_at != null ? new Date(result.synced_at + 'Z') : undefined;
165
186
  if (hasSynced != this.currentStatus.hasSynced) {
166
- this.currentStatus = new SyncStatus({ ...this.currentStatus.toJSON(), hasSynced });
187
+ this.currentStatus = new SyncStatus({ ...this.currentStatus.toJSON(), hasSynced, lastSyncedAt: syncedAt });
167
188
  this.iterateListeners((l) => l.statusChanged?.(this.currentStatus));
168
189
  }
169
190
  }
@@ -247,20 +268,7 @@ export class AbstractPowerSyncDatabase extends BaseObserver {
247
268
  const { clearLocal } = options;
248
269
  // TODO DB name, verify this is necessary with extension
249
270
  await this.database.writeTransaction(async (tx) => {
250
- await tx.execute(`DELETE FROM ${PSInternalTable.OPLOG}`);
251
- await tx.execute(`DELETE FROM ${PSInternalTable.CRUD}`);
252
- await tx.execute(`DELETE FROM ${PSInternalTable.BUCKETS}`);
253
- await tx.execute(`DELETE FROM ${PSInternalTable.UNTYPED}`);
254
- const tableGlob = clearLocal ? 'ps_data_*' : 'ps_data__*';
255
- const existingTableRows = await tx.execute(`
256
- SELECT name FROM sqlite_master WHERE type='table' AND name GLOB ?
257
- `, [tableGlob]);
258
- if (!existingTableRows.rows?.length) {
259
- return;
260
- }
261
- for (const row of existingTableRows.rows._array) {
262
- await tx.execute(`DELETE FROM ${quoteIdentifier(row.name)} WHERE 1`);
263
- }
271
+ await tx.execute('SELECT powersync_clear(?)', [clearLocal ? 1 : 0]);
264
272
  });
265
273
  // The data has been deleted - reset the sync status
266
274
  this.currentStatus = new SyncStatus({});
@@ -364,6 +372,14 @@ export class AbstractPowerSyncDatabase extends BaseObserver {
364
372
  return new CrudTransaction(all, async (writeCheckpoint) => this.handleCrudCheckpoint(last.clientId, writeCheckpoint), txId);
365
373
  });
366
374
  }
375
+ /**
376
+ * Get an unique client id for this database.
377
+ *
378
+ * The id is not reset when the database is cleared, only when the database is deleted.
379
+ */
380
+ async getClientId() {
381
+ return this.bucketStorageAdapter.getClientId();
382
+ }
367
383
  async handleCrudCheckpoint(lastClientId, writeCheckpoint) {
368
384
  return this.writeTransaction(async (tx) => {
369
385
  await tx.execute(`DELETE FROM ${PSInternalTable.CRUD} WHERE id <= ?`, [lastClientId]);
@@ -578,11 +594,11 @@ export class AbstractPowerSyncDatabase extends BaseObserver {
578
594
  const executor = new ControlledExecutor(async (e) => {
579
595
  await onChange(e);
580
596
  });
581
- const flushTableUpdates = throttle(() => this.handleTableChanges(changedTables, watchedTables, (intersection) => {
597
+ const flushTableUpdates = throttleTrailing(() => this.handleTableChanges(changedTables, watchedTables, (intersection) => {
582
598
  if (resolvedOptions?.signal?.aborted)
583
599
  return;
584
600
  executor.schedule({ changedTables: intersection });
585
- }), throttleMs, { leading: false, trailing: true });
601
+ }), throttleMs);
586
602
  const dispose = this.database.registerListener({
587
603
  tablesUpdated: async (update) => {
588
604
  try {
@@ -660,4 +676,3 @@ export class AbstractPowerSyncDatabase extends BaseObserver {
660
676
  return this.database.readLock((tx) => tx.execute(sql, params));
661
677
  }
662
678
  }
663
- //# sourceMappingURL=AbstractPowerSyncDatabase.js.map
@@ -1,7 +1,7 @@
1
- import { DBAdapter } from '../db/DBAdapter';
2
- import { Schema } from '../db/schema/Schema';
3
- import { AbstractPowerSyncDatabase, PowerSyncDatabaseOptions } from './AbstractPowerSyncDatabase';
4
- import { SQLOpenOptions } from './SQLOpenFactory';
1
+ import { DBAdapter } from '../db/DBAdapter.js';
2
+ import { Schema } from '../db/schema/Schema.js';
3
+ import { AbstractPowerSyncDatabase, PowerSyncDatabaseOptions } from './AbstractPowerSyncDatabase.js';
4
+ import { SQLOpenOptions } from './SQLOpenFactory.js';
5
5
  export interface PowerSyncOpenFactoryOptions extends Partial<PowerSyncDatabaseOptions>, SQLOpenOptions {
6
6
  /** Schema used for the local database. */
7
7
  schema: Schema;
@@ -13,7 +13,7 @@ export declare abstract class AbstractPowerSyncDatabaseOpenFactory {
13
13
  * Schema used for the local database.
14
14
  */
15
15
  get schema(): Schema<{
16
- [x: string]: import("..").TableV2<any>;
16
+ [x: string]: import("../index.js").Table<any>;
17
17
  }>;
18
18
  protected abstract openDB(): DBAdapter;
19
19
  generateOptions(): PowerSyncDatabaseOptions;
@@ -22,4 +22,3 @@ export class AbstractPowerSyncDatabaseOpenFactory {
22
22
  return this.generateInstance(options);
23
23
  }
24
24
  }
25
- //# sourceMappingURL=AbstractPowerSyncOpenFactory.js.map
@@ -1,4 +1,4 @@
1
- import { DBAdapter } from '../db/DBAdapter';
1
+ import { DBAdapter } from '../db/DBAdapter.js';
2
2
  export interface SQLOpenOptions {
3
3
  /**
4
4
  * Filename for the database.
@@ -17,4 +17,3 @@ export const isSQLOpenFactory = (test) => {
17
17
  export const isDBAdapter = (test) => {
18
18
  return typeof test?.writeTransaction == 'function';
19
19
  };
20
- //# sourceMappingURL=SQLOpenFactory.js.map
@@ -1,5 +1,5 @@
1
- import { PowerSyncCredentials } from './PowerSyncCredentials';
2
- import type { AbstractPowerSyncDatabase } from '../AbstractPowerSyncDatabase';
1
+ import { PowerSyncCredentials } from './PowerSyncCredentials.js';
2
+ import type { AbstractPowerSyncDatabase } from '../AbstractPowerSyncDatabase.js';
3
3
  export interface PowerSyncBackendConnector {
4
4
  /** Allows the PowerSync client to retrieve an authentication token from your backend
5
5
  * which is used to authenticate against the PowerSync service.
@@ -1,2 +1 @@
1
1
  export {};
2
- //# sourceMappingURL=PowerSyncBackendConnector.js.map
@@ -1,2 +1 @@
1
1
  export {};
2
- //# sourceMappingURL=PowerSyncCredentials.js.map
@@ -1,2 +1 @@
1
1
  export const MAX_OP_ID = '9223372036854775807';
2
- //# sourceMappingURL=constants.js.map
@@ -1,7 +1,7 @@
1
- import { BaseListener, BaseObserver, Disposable } from '../../../utils/BaseObserver';
2
- import { CrudBatch } from './CrudBatch';
3
- import { CrudEntry, OpId } from './CrudEntry';
4
- import { SyncDataBatch } from './SyncDataBatch';
1
+ import { BaseListener, BaseObserver, Disposable } from '../../../utils/BaseObserver.js';
2
+ import { CrudBatch } from './CrudBatch.js';
3
+ import { CrudEntry, OpId } from './CrudEntry.js';
4
+ import { SyncDataBatch } from './SyncDataBatch.js';
5
5
  export interface Checkpoint {
6
6
  last_op_id: OpId;
7
7
  buckets: BucketChecksum[];
@@ -70,4 +70,8 @@ export interface BucketStorageAdapter extends BaseObserver<BucketStorageListener
70
70
  */
71
71
  forceCompact(): Promise<void>;
72
72
  getMaxOpId(): string;
73
+ /**
74
+ * Get an unique client id.
75
+ */
76
+ getClientId(): Promise<string>;
73
77
  }
@@ -6,4 +6,3 @@ export var PSInternalTable;
6
6
  PSInternalTable["OPLOG"] = "ps_oplog";
7
7
  PSInternalTable["UNTYPED"] = "ps_untyped";
8
8
  })(PSInternalTable || (PSInternalTable = {}));
9
- //# sourceMappingURL=BucketStorageAdapter.js.map
@@ -1,4 +1,4 @@
1
- import { CrudEntry } from './CrudEntry';
1
+ import { CrudEntry } from './CrudEntry.js';
2
2
  /**
3
3
  * A batch of client-side changes.
4
4
  */
@@ -23,4 +23,3 @@ export class CrudBatch {
23
23
  this.complete = complete;
24
24
  }
25
25
  }
26
- //# sourceMappingURL=CrudBatch.js.map
@@ -82,4 +82,3 @@ export class CrudEntry {
82
82
  return [this.transactionId, this.clientId, this.op, this.table, this.id, this.opData];
83
83
  }
84
84
  }
85
- //# sourceMappingURL=CrudEntry.js.map
@@ -1,5 +1,5 @@
1
- import { CrudBatch } from './CrudBatch';
2
- import { CrudEntry } from './CrudEntry';
1
+ import { CrudBatch } from './CrudBatch.js';
2
+ import { CrudEntry } from './CrudEntry.js';
3
3
  export declare class CrudTransaction extends CrudBatch {
4
4
  /**
5
5
  * List of client-side changes.
@@ -1,4 +1,4 @@
1
- import { CrudBatch } from './CrudBatch';
1
+ import { CrudBatch } from './CrudBatch.js';
2
2
  export class CrudTransaction extends CrudBatch {
3
3
  crud;
4
4
  complete;
@@ -22,4 +22,3 @@ export class CrudTransaction extends CrudBatch {
22
22
  this.transactionId = transactionId;
23
23
  }
24
24
  }
25
- //# sourceMappingURL=CrudTransaction.js.map
@@ -20,4 +20,3 @@ export class OpType {
20
20
  return Object.entries(OpTypeEnum).find(([, value]) => value === this.value)[0];
21
21
  }
22
22
  }
23
- //# sourceMappingURL=OpType.js.map
@@ -1,5 +1,5 @@
1
- import { OpId } from './CrudEntry';
2
- import { OpType, OpTypeJSON } from './OpType';
1
+ import { OpId } from './CrudEntry.js';
2
+ import { OpType, OpTypeJSON } from './OpType.js';
3
3
  export interface OplogEntryJSON {
4
4
  checksum: number;
5
5
  data?: string;
@@ -1,4 +1,4 @@
1
- import { OpType } from './OpType';
1
+ import { OpType } from './OpType.js';
2
2
  export class OplogEntry {
3
3
  op_id;
4
4
  op;
@@ -31,4 +31,3 @@ export class OplogEntry {
31
31
  };
32
32
  }
33
33
  }
34
- //# sourceMappingURL=OplogEntry.js.map
@@ -1,11 +1,11 @@
1
1
  import { Mutex } from 'async-mutex';
2
2
  import { ILogger } from 'js-logger';
3
- import { DBAdapter, Transaction } from '../../../db/DBAdapter';
4
- import { BaseObserver } from '../../../utils/BaseObserver';
5
- import { BucketState, BucketStorageAdapter, BucketStorageListener, Checkpoint, SyncLocalDatabaseResult } from './BucketStorageAdapter';
6
- import { CrudBatch } from './CrudBatch';
7
- import { CrudEntry } from './CrudEntry';
8
- import { SyncDataBatch } from './SyncDataBatch';
3
+ import { DBAdapter, Transaction } from '../../../db/DBAdapter.js';
4
+ import { BaseObserver } from '../../../utils/BaseObserver.js';
5
+ import { BucketState, BucketStorageAdapter, BucketStorageListener, Checkpoint, SyncLocalDatabaseResult } from './BucketStorageAdapter.js';
6
+ import { CrudBatch } from './CrudBatch.js';
7
+ import { CrudEntry } from './CrudEntry.js';
8
+ import { SyncDataBatch } from './SyncDataBatch.js';
9
9
  export declare class SqliteBucketStorage extends BaseObserver<BucketStorageListener> implements BucketStorageAdapter {
10
10
  private db;
11
11
  private mutex;
@@ -14,6 +14,7 @@ export declare class SqliteBucketStorage extends BaseObserver<BucketStorageListe
14
14
  private pendingBucketDeletes;
15
15
  private _hasCompletedSync;
16
16
  private updateListener;
17
+ private _clientId?;
17
18
  /**
18
19
  * Count up, and do a compact on startup.
19
20
  */
@@ -21,6 +22,8 @@ export declare class SqliteBucketStorage extends BaseObserver<BucketStorageListe
21
22
  constructor(db: DBAdapter, mutex: Mutex, logger?: ILogger);
22
23
  init(): Promise<void>;
23
24
  dispose(): Promise<void>;
25
+ _getClientId(): Promise<string>;
26
+ getClientId(): Promise<string>;
24
27
  getMaxOpId(): string;
25
28
  /**
26
29
  * Reset any caches.
@@ -1,9 +1,9 @@
1
1
  import Logger from 'js-logger';
2
- import { extractTableUpdates } from '../../../db/DBAdapter';
3
- import { BaseObserver } from '../../../utils/BaseObserver';
4
- import { PSInternalTable } from './BucketStorageAdapter';
5
- import { MAX_OP_ID } from '../../constants';
6
- import { CrudEntry } from './CrudEntry';
2
+ import { extractTableUpdates } from '../../../db/DBAdapter.js';
3
+ import { BaseObserver } from '../../../utils/BaseObserver.js';
4
+ import { MAX_OP_ID } from '../../constants.js';
5
+ import { PSInternalTable } from './BucketStorageAdapter.js';
6
+ import { CrudEntry } from './CrudEntry.js';
7
7
  const COMPACT_OPERATION_INTERVAL = 1_000;
8
8
  export class SqliteBucketStorage extends BaseObserver {
9
9
  db;
@@ -13,6 +13,7 @@ export class SqliteBucketStorage extends BaseObserver {
13
13
  pendingBucketDeletes;
14
14
  _hasCompletedSync;
15
15
  updateListener;
16
+ _clientId;
16
17
  /**
17
18
  * Count up, and do a compact on startup.
18
19
  */
@@ -44,6 +45,16 @@ export class SqliteBucketStorage extends BaseObserver {
44
45
  async dispose() {
45
46
  this.updateListener?.();
46
47
  }
48
+ async _getClientId() {
49
+ const row = await this.db.get('SELECT powersync_client_id() as client_id');
50
+ return row['client_id'];
51
+ }
52
+ getClientId() {
53
+ if (this._clientId == null) {
54
+ this._clientId = this._getClientId();
55
+ }
56
+ return this._clientId;
57
+ }
47
58
  getMaxOpId() {
48
59
  return MAX_OP_ID;
49
60
  }
@@ -88,8 +99,8 @@ export class SqliteBucketStorage extends BaseObserver {
88
99
  if (this._hasCompletedSync) {
89
100
  return true;
90
101
  }
91
- const r = await this.db.execute(`SELECT name, last_applied_op FROM ps_buckets WHERE last_applied_op > 0 LIMIT 1`);
92
- const completed = !!r.rows?.length;
102
+ const r = await this.db.get(`SELECT powersync_last_synced_at() as synced_at`);
103
+ const completed = r.synced_at != null;
93
104
  if (completed) {
94
105
  this._hasCompletedSync = true;
95
106
  }
@@ -177,8 +188,7 @@ export class SqliteBucketStorage extends BaseObserver {
177
188
  async deletePendingBuckets() {
178
189
  if (this.pendingBucketDeletes !== false) {
179
190
  await this.writeTransaction(async (tx) => {
180
- await tx.execute('DELETE FROM ps_oplog WHERE bucket IN (SELECT name FROM ps_buckets WHERE pending_delete = 1 AND last_applied_op = last_op AND last_op >= target_op)');
181
- await tx.execute('DELETE FROM ps_buckets WHERE pending_delete = 1 AND last_applied_op = last_op AND last_op >= target_op');
191
+ await tx.execute('INSERT INTO powersync_operations(op, data) VALUES (?, ?)', ['delete_pending_buckets', '']);
182
192
  });
183
193
  // Executed once after start-up, and again when there are pending deletes.
184
194
  this.pendingBucketDeletes = false;
@@ -228,7 +238,9 @@ export class SqliteBucketStorage extends BaseObserver {
228
238
  // New crud data may have been uploaded since we got the checkpoint. Abort.
229
239
  return false;
230
240
  }
231
- const response = await tx.execute("UPDATE ps_buckets SET target_op = ? WHERE name='$local'", [opId]);
241
+ const response = await tx.execute("UPDATE ps_buckets SET target_op = CAST(? as INTEGER) WHERE name='$local'", [
242
+ opId
243
+ ]);
232
244
  this.logger.debug(['[updateLocalTarget] Response from updating target_op ', JSON.stringify(response)]);
233
245
  return true;
234
246
  });
@@ -270,11 +282,15 @@ export class SqliteBucketStorage extends BaseObserver {
270
282
  if (writeCheckpoint) {
271
283
  const crudResult = await tx.execute('SELECT 1 FROM ps_crud LIMIT 1');
272
284
  if (crudResult.rows?.length) {
273
- await tx.execute("UPDATE ps_buckets SET target_op = ? WHERE name='$local'", [writeCheckpoint]);
285
+ await tx.execute("UPDATE ps_buckets SET target_op = CAST(? as INTEGER) WHERE name='$local'", [
286
+ writeCheckpoint
287
+ ]);
274
288
  }
275
289
  }
276
290
  else {
277
- await tx.execute("UPDATE ps_buckets SET target_op = ? WHERE name='$local'", [this.getMaxOpId()]);
291
+ await tx.execute("UPDATE ps_buckets SET target_op = CAST(? as INTEGER) WHERE name='$local'", [
292
+ this.getMaxOpId()
293
+ ]);
278
294
  }
279
295
  });
280
296
  }
@@ -290,4 +306,3 @@ export class SqliteBucketStorage extends BaseObserver {
290
306
  // No-op for now
291
307
  }
292
308
  }
293
- //# sourceMappingURL=SqliteBucketStorage.js.map
@@ -1,4 +1,4 @@
1
- import { SyncDataBucket } from './SyncDataBucket';
1
+ import { SyncDataBucket } from './SyncDataBucket.js';
2
2
  export declare class SyncDataBatch {
3
3
  buckets: SyncDataBucket[];
4
4
  static fromJSON(json: any): SyncDataBatch;
@@ -1,4 +1,4 @@
1
- import { SyncDataBucket } from './SyncDataBucket';
1
+ import { SyncDataBucket } from './SyncDataBucket.js';
2
2
  // TODO JSON
3
3
  export class SyncDataBatch {
4
4
  buckets;
@@ -9,4 +9,3 @@ export class SyncDataBatch {
9
9
  this.buckets = buckets;
10
10
  }
11
11
  }
12
- //# sourceMappingURL=SyncDataBatch.js.map
@@ -1,5 +1,5 @@
1
- import { OpId } from './CrudEntry';
2
- import { OplogEntry, OplogEntryJSON } from './OplogEntry';
1
+ import { OpId } from './CrudEntry.js';
2
+ import { OplogEntry, OplogEntryJSON } from './OplogEntry.js';
3
3
  export type SyncDataBucketJSON = {
4
4
  bucket: string;
5
5
  has_more?: boolean;
@@ -1,4 +1,4 @@
1
- import { OplogEntry } from './OplogEntry';
1
+ import { OplogEntry } from './OplogEntry.js';
2
2
  export class SyncDataBucket {
3
3
  bucket;
4
4
  data;
@@ -37,4 +37,3 @@ export class SyncDataBucket {
37
37
  };
38
38
  }
39
39
  }
40
- //# sourceMappingURL=SyncDataBucket.js.map
@@ -1,14 +1,14 @@
1
- import { ILogger } from 'js-logger';
2
- import { type fetch } from 'cross-fetch';
3
- import { PowerSyncCredentials } from '../../connection/PowerSyncCredentials';
4
- import { StreamingSyncLine, StreamingSyncRequest } from './streaming-sync-types';
5
- import { DataStream } from '../../../utils/DataStream';
6
1
  import type { BSON } from 'bson';
2
+ import { type fetch } from 'cross-fetch';
3
+ import Logger, { ILogger } from 'js-logger';
4
+ import { DataStream } from '../../../utils/DataStream.js';
5
+ import { PowerSyncCredentials } from '../../connection/PowerSyncCredentials.js';
6
+ import { StreamingSyncLine, StreamingSyncRequest } from './streaming-sync-types.js';
7
7
  export type BSONImplementation = typeof BSON;
8
8
  export type RemoteConnector = {
9
9
  fetchCredentials: () => Promise<PowerSyncCredentials | null>;
10
10
  };
11
- export declare const DEFAULT_REMOTE_LOGGER: ILogger;
11
+ export declare const DEFAULT_REMOTE_LOGGER: Logger.ILogger;
12
12
  export type SyncStreamOptions = {
13
13
  path: string;
14
14
  data: StreamingSyncRequest;
@@ -53,11 +53,13 @@ export declare abstract class AbstractRemote {
53
53
  */
54
54
  get fetch(): FetchImplementation;
55
55
  getCredentials(): Promise<PowerSyncCredentials | null>;
56
+ getUserAgent(): string;
56
57
  protected buildRequest(path: string): Promise<{
57
58
  url: string;
58
59
  headers: {
59
60
  'content-type': string;
60
61
  Authorization: string;
62
+ 'x-user-agent': string;
61
63
  };
62
64
  }>;
63
65
  post(path: string, data: any, headers?: Record<string, string>): Promise<any>;
@@ -1,10 +1,11 @@
1
- import Logger from 'js-logger';
2
- import { DataStream } from '../../../utils/DataStream';
1
+ import { Buffer } from 'buffer';
3
2
  import ndjsonStream from 'can-ndjson-stream';
3
+ import Logger from 'js-logger';
4
4
  import { RSocketConnector } from 'rsocket-core';
5
5
  import { WebsocketClientTransport } from 'rsocket-websocket-client';
6
- import { AbortOperation } from '../../../utils/AbortOperation';
7
- import { Buffer } from 'buffer';
6
+ import { AbortOperation } from '../../../utils/AbortOperation.js';
7
+ import { DataStream } from '../../../utils/DataStream.js';
8
+ import { version as POWERSYNC_JS_VERSION } from '../../../../package.json';
8
9
  // Refresh at least 30 sec before it expires
9
10
  const REFRESH_CREDENTIALS_SAFETY_PERIOD_MS = 30_000;
10
11
  const SYNC_QUEUE_REQUEST_N = 10;
@@ -62,6 +63,9 @@ export class AbstractRemote {
62
63
  this.credentials = await this.connector.fetchCredentials();
63
64
  return this.credentials;
64
65
  }
66
+ getUserAgent() {
67
+ return `powersync-js/${POWERSYNC_JS_VERSION}`;
68
+ }
65
69
  async buildRequest(path) {
66
70
  const credentials = await this.getCredentials();
67
71
  if (credentials != null && (credentials.endpoint == null || credentials.endpoint == '')) {
@@ -72,11 +76,13 @@ export class AbstractRemote {
72
76
  error.status = 401;
73
77
  throw error;
74
78
  }
79
+ const userAgent = this.getUserAgent();
75
80
  return {
76
81
  url: credentials.endpoint + path,
77
82
  headers: {
78
83
  'content-type': 'application/json',
79
- Authorization: `Token ${credentials.token}`
84
+ Authorization: `Token ${credentials.token}`,
85
+ 'x-user-agent': userAgent
80
86
  }
81
87
  };
82
88
  }
@@ -137,6 +143,10 @@ export class AbstractRemote {
137
143
  const { path } = options;
138
144
  const request = await this.buildRequest(path);
139
145
  const bson = await this.getBSON();
146
+ // Add the user agent in the setup payload - we can't set custom
147
+ // headers with websockets on web. The browser userAgent is however added
148
+ // automatically as a header.
149
+ const userAgent = this.getUserAgent();
140
150
  const connector = new RSocketConnector({
141
151
  transport: new WebsocketClientTransport({
142
152
  url: this.options.socketUrlTransformer(request.url)
@@ -149,7 +159,8 @@ export class AbstractRemote {
149
159
  payload: {
150
160
  data: null,
151
161
  metadata: Buffer.from(bson.serialize({
152
- token: request.headers.Authorization
162
+ token: request.headers.Authorization,
163
+ user_agent: userAgent
153
164
  }))
154
165
  }
155
166
  }
@@ -383,4 +394,3 @@ export class AbstractRemote {
383
394
  return stream;
384
395
  }
385
396
  }
386
- //# sourceMappingURL=AbstractRemote.js.map