@powersync/web 1.30.0 → 1.32.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.
Files changed (112) hide show
  1. package/dist/_journeyapps_wa-sqlite-_journeyapps_wa-sqlite_src_examples_AccessHandlePoolVFS_js-_journeyapp-89f0ba.index.umd.js +1867 -0
  2. package/dist/_journeyapps_wa-sqlite-_journeyapps_wa-sqlite_src_examples_AccessHandlePoolVFS_js-_journeyapp-89f0ba.index.umd.js.map +1 -0
  3. package/dist/_journeyapps_wa-sqlite_src_examples_AccessHandlePoolVFS_js-_journeyapps_wa-sqlite_src_example-2530150.index.umd.js +555 -0
  4. package/dist/_journeyapps_wa-sqlite_src_examples_AccessHandlePoolVFS_js-_journeyapps_wa-sqlite_src_example-2530150.index.umd.js.map +1 -0
  5. package/dist/_journeyapps_wa-sqlite_src_examples_AccessHandlePoolVFS_js-_journeyapps_wa-sqlite_src_example-2530151.index.umd.js +555 -0
  6. package/dist/_journeyapps_wa-sqlite_src_examples_AccessHandlePoolVFS_js-_journeyapps_wa-sqlite_src_example-2530151.index.umd.js.map +1 -0
  7. package/dist/index.umd.js +5022 -38504
  8. package/dist/index.umd.js.map +1 -1
  9. package/dist/worker/SharedSyncImplementation.umd.js +819 -2220
  10. package/dist/worker/SharedSyncImplementation.umd.js.map +1 -1
  11. package/dist/worker/WASQLiteDB.umd.js +524 -2121
  12. package/dist/worker/WASQLiteDB.umd.js.map +1 -1
  13. package/dist/worker/{node_modules_bson_lib_bson_mjs.umd.js → node_modules_pnpm_bson_6_10_4_node_modules_bson_lib_bson_mjs.umd.js} +8 -8
  14. package/dist/worker/node_modules_pnpm_bson_6_10_4_node_modules_bson_lib_bson_mjs.umd.js.map +1 -0
  15. package/dist/worker/node_modules_pnpm_journeyapps_wa-sqlite_1_4_1_node_modules_journeyapps_wa-sqlite_dist_mc-wa-s-3a94cf.umd.js +44 -0
  16. package/dist/worker/node_modules_pnpm_journeyapps_wa-sqlite_1_4_1_node_modules_journeyapps_wa-sqlite_dist_mc-wa-s-3a94cf.umd.js.map +1 -0
  17. package/dist/worker/node_modules_pnpm_journeyapps_wa-sqlite_1_4_1_node_modules_journeyapps_wa-sqlite_dist_mc-wa-s-868779.umd.js +44 -0
  18. package/dist/worker/node_modules_pnpm_journeyapps_wa-sqlite_1_4_1_node_modules_journeyapps_wa-sqlite_dist_mc-wa-s-868779.umd.js.map +1 -0
  19. package/dist/worker/node_modules_pnpm_journeyapps_wa-sqlite_1_4_1_node_modules_journeyapps_wa-sqlite_dist_wa-sqli-f60d0d.umd.js +44 -0
  20. package/dist/worker/node_modules_pnpm_journeyapps_wa-sqlite_1_4_1_node_modules_journeyapps_wa-sqlite_dist_wa-sqli-f60d0d.umd.js.map +1 -0
  21. package/dist/worker/node_modules_pnpm_journeyapps_wa-sqlite_1_4_1_node_modules_journeyapps_wa-sqlite_dist_wa-sqlite_mjs.umd.js +44 -0
  22. package/dist/worker/node_modules_pnpm_journeyapps_wa-sqlite_1_4_1_node_modules_journeyapps_wa-sqlite_dist_wa-sqlite_mjs.umd.js.map +1 -0
  23. package/dist/worker/{node_modules_journeyapps_wa-sqlite_src_examples_IDBBatchAtomicVFS_js.umd.js → node_modules_pnpm_journeyapps_wa-sqlite_1_4_1_node_modules_journeyapps_wa-sqlite_src_examples-0d2437.umd.js} +32 -32
  24. package/dist/worker/node_modules_pnpm_journeyapps_wa-sqlite_1_4_1_node_modules_journeyapps_wa-sqlite_src_examples-0d2437.umd.js.map +1 -0
  25. package/dist/worker/{node_modules_journeyapps_wa-sqlite_src_examples_OPFSCoopSyncVFS_js.umd.js → node_modules_pnpm_journeyapps_wa-sqlite_1_4_1_node_modules_journeyapps_wa-sqlite_src_examples-1d4e74.umd.js} +24 -24
  26. package/dist/worker/node_modules_pnpm_journeyapps_wa-sqlite_1_4_1_node_modules_journeyapps_wa-sqlite_src_examples-1d4e74.umd.js.map +1 -0
  27. package/dist/worker/{node_modules_journeyapps_wa-sqlite_src_examples_AccessHandlePoolVFS_js.umd.js → node_modules_pnpm_journeyapps_wa-sqlite_1_4_1_node_modules_journeyapps_wa-sqlite_src_examples-3622cf.umd.js} +24 -24
  28. package/dist/worker/node_modules_pnpm_journeyapps_wa-sqlite_1_4_1_node_modules_journeyapps_wa-sqlite_src_examples-3622cf.umd.js.map +1 -0
  29. package/lib/package.json +27 -23
  30. package/lib/src/db/NavigatorTriggerClaimManager.d.ts +6 -0
  31. package/lib/src/db/NavigatorTriggerClaimManager.js +20 -0
  32. package/lib/src/db/PowerSyncDatabase.d.ts +5 -2
  33. package/lib/src/db/PowerSyncDatabase.js +49 -11
  34. package/lib/src/db/adapters/AbstractWebPowerSyncDatabaseOpenFactory.d.ts +1 -1
  35. package/lib/src/db/adapters/AbstractWebPowerSyncDatabaseOpenFactory.js +1 -1
  36. package/lib/src/db/adapters/AbstractWebSQLOpenFactory.d.ts +2 -2
  37. package/lib/src/db/adapters/AbstractWebSQLOpenFactory.js +2 -2
  38. package/lib/src/db/adapters/AsyncDatabaseConnection.d.ts +1 -1
  39. package/lib/src/db/adapters/LockedAsyncDatabaseAdapter.d.ts +21 -4
  40. package/lib/src/db/adapters/LockedAsyncDatabaseAdapter.js +116 -22
  41. package/lib/src/db/adapters/WebDBAdapter.d.ts +5 -2
  42. package/lib/src/db/adapters/WorkerWrappedAsyncDatabaseConnection.d.ts +7 -3
  43. package/lib/src/db/adapters/WorkerWrappedAsyncDatabaseConnection.js +14 -7
  44. package/lib/src/db/adapters/wa-sqlite/InternalWASQLiteDBAdapter.d.ts +12 -0
  45. package/lib/src/db/adapters/wa-sqlite/InternalWASQLiteDBAdapter.js +19 -0
  46. package/lib/src/db/adapters/wa-sqlite/WASQLiteConnection.d.ts +2 -2
  47. package/lib/src/db/adapters/wa-sqlite/WASQLiteConnection.js +11 -2
  48. package/lib/src/db/adapters/wa-sqlite/WASQLiteDBAdapter.d.ts +4 -4
  49. package/lib/src/db/adapters/wa-sqlite/WASQLiteDBAdapter.js +6 -6
  50. package/lib/src/db/adapters/wa-sqlite/WASQLiteOpenFactory.d.ts +5 -5
  51. package/lib/src/db/adapters/wa-sqlite/WASQLiteOpenFactory.js +7 -7
  52. package/lib/src/db/adapters/wa-sqlite/WASQLitePowerSyncDatabaseOpenFactory.d.ts +1 -1
  53. package/lib/src/db/adapters/wa-sqlite/WASQLitePowerSyncDatabaseOpenFactory.js +3 -3
  54. package/lib/src/db/sync/SharedWebStreamingSyncImplementation.d.ts +6 -9
  55. package/lib/src/db/sync/SharedWebStreamingSyncImplementation.js +54 -38
  56. package/lib/src/db/sync/WebRemote.js +1 -1
  57. package/lib/src/db/sync/WebStreamingSyncImplementation.d.ts +1 -1
  58. package/lib/src/db/sync/WebStreamingSyncImplementation.js +1 -1
  59. package/lib/src/index.d.ts +12 -12
  60. package/lib/src/index.js +12 -12
  61. package/lib/src/worker/db/SharedWASQLiteConnection.d.ts +2 -2
  62. package/lib/src/worker/db/WASQLiteDB.worker.js +3 -3
  63. package/lib/src/worker/db/WorkerWASQLiteConnection.d.ts +2 -2
  64. package/lib/src/worker/db/WorkerWASQLiteConnection.js +1 -1
  65. package/lib/src/worker/db/open-worker-database.d.ts +2 -2
  66. package/lib/src/worker/db/open-worker-database.js +1 -1
  67. package/lib/src/worker/sync/BroadcastLogger.d.ts +1 -1
  68. package/lib/src/worker/sync/SharedSyncImplementation.d.ts +21 -11
  69. package/lib/src/worker/sync/SharedSyncImplementation.js +209 -113
  70. package/lib/src/worker/sync/SharedSyncImplementation.worker.js +3 -3
  71. package/lib/src/worker/sync/WorkerClient.d.ts +4 -5
  72. package/lib/src/worker/sync/WorkerClient.js +8 -10
  73. package/lib/tsconfig.tsbuildinfo +1 -1
  74. package/package.json +23 -19
  75. package/src/db/NavigatorTriggerClaimManager.ts +23 -0
  76. package/src/db/PowerSyncDatabase.ts +64 -22
  77. package/src/db/adapters/AbstractWebPowerSyncDatabaseOpenFactory.ts +1 -1
  78. package/src/db/adapters/AbstractWebSQLOpenFactory.ts +3 -3
  79. package/src/db/adapters/AsyncDatabaseConnection.ts +1 -1
  80. package/src/db/adapters/LockedAsyncDatabaseAdapter.ts +138 -33
  81. package/src/db/adapters/WebDBAdapter.ts +6 -2
  82. package/src/db/adapters/WorkerWrappedAsyncDatabaseConnection.ts +20 -8
  83. package/src/db/adapters/wa-sqlite/InternalWASQLiteDBAdapter.ts +23 -0
  84. package/src/db/adapters/wa-sqlite/WASQLiteConnection.ts +13 -5
  85. package/src/db/adapters/wa-sqlite/WASQLiteDBAdapter.ts +8 -8
  86. package/src/db/adapters/wa-sqlite/WASQLiteOpenFactory.ts +9 -9
  87. package/src/db/adapters/wa-sqlite/WASQLitePowerSyncDatabaseOpenFactory.ts +3 -3
  88. package/src/db/sync/SharedWebStreamingSyncImplementation.ts +69 -51
  89. package/src/db/sync/WebRemote.ts +1 -1
  90. package/src/db/sync/WebStreamingSyncImplementation.ts +2 -2
  91. package/src/index.ts +12 -12
  92. package/src/worker/db/SharedWASQLiteConnection.ts +2 -2
  93. package/src/worker/db/WASQLiteDB.worker.ts +5 -6
  94. package/src/worker/db/WorkerWASQLiteConnection.ts +2 -2
  95. package/src/worker/db/open-worker-database.ts +2 -2
  96. package/src/worker/sync/BroadcastLogger.ts +1 -1
  97. package/src/worker/sync/SharedSyncImplementation.ts +241 -126
  98. package/src/worker/sync/SharedSyncImplementation.worker.ts +3 -3
  99. package/src/worker/sync/WorkerClient.ts +10 -14
  100. package/dist/worker/node_modules_bson_lib_bson_mjs.umd.js.map +0 -1
  101. package/dist/worker/node_modules_journeyapps_wa-sqlite_dist_mc-wa-sqlite-async_mjs.umd.js +0 -44
  102. package/dist/worker/node_modules_journeyapps_wa-sqlite_dist_mc-wa-sqlite-async_mjs.umd.js.map +0 -1
  103. package/dist/worker/node_modules_journeyapps_wa-sqlite_dist_mc-wa-sqlite_mjs.umd.js +0 -44
  104. package/dist/worker/node_modules_journeyapps_wa-sqlite_dist_mc-wa-sqlite_mjs.umd.js.map +0 -1
  105. package/dist/worker/node_modules_journeyapps_wa-sqlite_dist_wa-sqlite-async_mjs.umd.js +0 -44
  106. package/dist/worker/node_modules_journeyapps_wa-sqlite_dist_wa-sqlite-async_mjs.umd.js.map +0 -1
  107. package/dist/worker/node_modules_journeyapps_wa-sqlite_dist_wa-sqlite_mjs.umd.js +0 -44
  108. package/dist/worker/node_modules_journeyapps_wa-sqlite_dist_wa-sqlite_mjs.umd.js.map +0 -1
  109. package/dist/worker/node_modules_journeyapps_wa-sqlite_src_examples_AccessHandlePoolVFS_js.umd.js.map +0 -1
  110. package/dist/worker/node_modules_journeyapps_wa-sqlite_src_examples_IDBBatchAtomicVFS_js.umd.js.map +0 -1
  111. package/dist/worker/node_modules_journeyapps_wa-sqlite_src_examples_OPFSCoopSyncVFS_js.umd.js.map +0 -1
  112. /package/bin/{powersync.js → powersync.cjs} +0 -0
@@ -1,7 +1,7 @@
1
- import { BaseObserver, createLogger } from '@powersync/common';
2
- import { getNavigatorLocks } from '../..//shared/navigator';
3
- import { WorkerWrappedAsyncDatabaseConnection } from './WorkerWrappedAsyncDatabaseConnection';
4
- import { WASQLiteVFS } from './wa-sqlite/WASQLiteConnection';
1
+ import { BaseObserver, ConnectionClosedError, createLogger } from '@powersync/common';
2
+ import { getNavigatorLocks } from '../../shared/navigator.js';
3
+ import { WorkerWrappedAsyncDatabaseConnection } from './WorkerWrappedAsyncDatabaseConnection.js';
4
+ import { WASQLiteVFS } from './wa-sqlite/WASQLiteConnection.js';
5
5
  /**
6
6
  * @internal
7
7
  * Wraps a {@link AsyncDatabaseConnection} and provides exclusive locking functions in
@@ -19,6 +19,7 @@ export class LockedAsyncDatabaseAdapter extends BaseObserver {
19
19
  _config = null;
20
20
  pendingAbortControllers;
21
21
  requiresHolds;
22
+ databaseOpenPromise = null;
22
23
  closing;
23
24
  closed;
24
25
  constructor(options) {
@@ -68,22 +69,79 @@ export class LockedAsyncDatabaseAdapter extends BaseObserver {
68
69
  async init() {
69
70
  return this.initPromise;
70
71
  }
72
+ async openInternalDB() {
73
+ /**
74
+ * Execute opening of the db in a lock in order not to interfere with other operations.
75
+ */
76
+ return this._acquireLock(async () => {
77
+ // Dispose any previous table change listener.
78
+ this._disposeTableChangeListener?.();
79
+ this._disposeTableChangeListener = null;
80
+ this._db?.close().catch((ex) => this.logger.warn(`Error closing database before opening new instance`, ex));
81
+ const isReOpen = !!this._db;
82
+ this._db = null;
83
+ this._db = await this.options.openConnection();
84
+ await this._db.init();
85
+ this._config = await this._db.getConfig();
86
+ await this.registerOnChangeListener(this._db);
87
+ if (isReOpen) {
88
+ this.iterateListeners((cb) => cb.databaseReOpened?.());
89
+ }
90
+ /**
91
+ * This is only required for the long-lived shared IndexedDB connections.
92
+ */
93
+ this.requiresHolds = this._config.vfs == WASQLiteVFS.IDBBatchAtomicVFS;
94
+ });
95
+ }
96
+ _reOpen() {
97
+ this.databaseOpenPromise = this.openInternalDB().finally(() => {
98
+ this.databaseOpenPromise = null;
99
+ });
100
+ return this.databaseOpenPromise;
101
+ }
102
+ /**
103
+ * Re-opens the underlying database.
104
+ * Returns a pending operation if one is already in progress.
105
+ */
106
+ async reOpenInternalDB() {
107
+ if (!this.options.reOpenOnConnectionClosed) {
108
+ throw new Error(`Cannot re-open underlying database, reOpenOnConnectionClosed is not enabled`);
109
+ }
110
+ if (this.databaseOpenPromise) {
111
+ return this.databaseOpenPromise;
112
+ }
113
+ return this._reOpen();
114
+ }
71
115
  async _init() {
72
- this._db = await this.options.openConnection();
73
- await this._db.init();
74
- this._config = await this._db.getConfig();
75
- await this.registerOnChangeListener(this._db);
76
- this.iterateListeners((cb) => cb.initialized?.());
77
116
  /**
78
- * This is only required for the long-lived shared IndexedDB connections.
117
+ * For OPFS, we can see this open call sometimes fail due to NoModificationAllowedError.
118
+ * We should be able to recover from this by re-opening the database.
79
119
  */
80
- this.requiresHolds = this._config.vfs == WASQLiteVFS.IDBBatchAtomicVFS;
120
+ const maxAttempts = 3;
121
+ for (let count = 0; count < maxAttempts; count++) {
122
+ try {
123
+ await this.openInternalDB();
124
+ break;
125
+ }
126
+ catch (ex) {
127
+ if (count == maxAttempts - 1) {
128
+ throw ex;
129
+ }
130
+ this.logger.warn(`Attempt ${count + 1} of ${maxAttempts} to open database failed, retrying in 1 second...`, ex);
131
+ await new Promise((resolve) => setTimeout(resolve, 1000));
132
+ }
133
+ }
134
+ this.iterateListeners((cb) => cb.initialized?.());
81
135
  }
82
136
  getConfiguration() {
83
137
  if (!this._config) {
84
138
  throw new Error(`Cannot get config before initialization is completed`);
85
139
  }
86
- return this._config;
140
+ return {
141
+ ...this._config,
142
+ // This can be overridden by the adapter later
143
+ requiresPersistentTriggers: false
144
+ };
87
145
  }
88
146
  async waitForInitialized() {
89
147
  // Awaiting this will expose errors on function calls like .execute etc
@@ -124,7 +182,14 @@ export class LockedAsyncDatabaseAdapter extends BaseObserver {
124
182
  */
125
183
  async close() {
126
184
  this.closing = true;
127
- this._disposeTableChangeListener?.();
185
+ /**
186
+ * Note that we obtain a reference to the callback to avoid calling the callback with `this` as the context.
187
+ * This is to avoid Comlink attempting to clone `this` when calling the method.
188
+ */
189
+ const dispose = this._disposeTableChangeListener;
190
+ if (dispose) {
191
+ dispose();
192
+ }
128
193
  this.pendingAbortControllers.forEach((controller) => controller.abort('Closed'));
129
194
  await this.baseDB?.close?.();
130
195
  this.closed = true;
@@ -144,24 +209,23 @@ export class LockedAsyncDatabaseAdapter extends BaseObserver {
144
209
  async readLock(fn, options) {
145
210
  await this.waitForInitialized();
146
211
  return this.acquireLock(async () => fn(this.generateDBHelpers({ execute: this._execute, executeRaw: this._executeRaw })), {
147
- timeoutMs: options?.timeoutMs
212
+ timeoutMs: options?.timeoutMs ?? this.options.defaultLockTimeoutMs
148
213
  });
149
214
  }
150
215
  async writeLock(fn, options) {
151
216
  await this.waitForInitialized();
152
217
  return this.acquireLock(async () => fn(this.generateDBHelpers({ execute: this._execute, executeRaw: this._executeRaw })), {
153
- timeoutMs: options?.timeoutMs
218
+ timeoutMs: options?.timeoutMs ?? this.options.defaultLockTimeoutMs
154
219
  });
155
220
  }
156
- async acquireLock(callback, options) {
157
- await this.waitForInitialized();
221
+ async _acquireLock(callback, options) {
158
222
  if (this.closing) {
159
223
  throw new Error(`Cannot acquire lock, the database is closing`);
160
224
  }
161
225
  const abortController = new AbortController();
162
226
  this.pendingAbortControllers.add(abortController);
163
227
  const { timeoutMs } = options ?? {};
164
- const timoutId = timeoutMs
228
+ const timeoutId = timeoutMs
165
229
  ? setTimeout(() => {
166
230
  abortController.abort(`Timeout after ${timeoutMs}ms`);
167
231
  this.pendingAbortControllers.delete(abortController);
@@ -169,19 +233,49 @@ export class LockedAsyncDatabaseAdapter extends BaseObserver {
169
233
  : null;
170
234
  return getNavigatorLocks().request(`db-lock-${this._dbIdentifier}`, { signal: abortController.signal }, async () => {
171
235
  this.pendingAbortControllers.delete(abortController);
172
- if (timoutId) {
173
- clearTimeout(timoutId);
236
+ if (timeoutId) {
237
+ clearTimeout(timeoutId);
174
238
  }
175
- const holdId = this.requiresHolds ? await this.baseDB.markHold() : null;
239
+ return await callback();
240
+ });
241
+ }
242
+ async acquireLock(callback, options) {
243
+ await this.waitForInitialized();
244
+ // The database is being opened in the background. Wait for it here.
245
+ if (this.databaseOpenPromise) {
246
+ await this.databaseOpenPromise;
247
+ }
248
+ return this._acquireLock(async () => {
249
+ let holdId = null;
176
250
  try {
251
+ /**
252
+ * We can't await this since it uses the same lock as we're in now.
253
+ * If there is a pending open, this call will throw.
254
+ * If there is no pending open, but there is also no database - the open
255
+ * might have failed. We need to re-open the database.
256
+ */
257
+ if (this.databaseOpenPromise || !this._db) {
258
+ throw new ConnectionClosedError('Connection is busy re-opening');
259
+ }
260
+ holdId = this.requiresHolds ? await this.baseDB.markHold() : null;
177
261
  return await callback();
178
262
  }
263
+ catch (ex) {
264
+ if (ConnectionClosedError.MATCHES(ex)) {
265
+ if (this.options.reOpenOnConnectionClosed && !this.databaseOpenPromise && !this.closing) {
266
+ // Immediately re-open the database. We need to miss as little table updates as possible.
267
+ // Note, don't await this since it uses the same lock as we're in now.
268
+ this.reOpenInternalDB();
269
+ }
270
+ }
271
+ throw ex;
272
+ }
179
273
  finally {
180
274
  if (holdId) {
181
275
  await this.baseDB.releaseHold(holdId);
182
276
  }
183
277
  }
184
- });
278
+ }, options);
185
279
  }
186
280
  async readTransaction(fn, options) {
187
281
  return this.readLock(this.wrapTransaction(fn));
@@ -1,9 +1,12 @@
1
1
  import { DBAdapter } from '@powersync/common';
2
- import { ResolvedWebSQLOpenOptions } from './web-sql-flags';
2
+ import { ResolvedWebSQLOpenOptions } from './web-sql-flags.js';
3
3
  export type SharedConnectionWorker = {
4
4
  identifier: string;
5
5
  port: MessagePort;
6
6
  };
7
+ export type WebDBAdapterConfiguration = ResolvedWebSQLOpenOptions & {
8
+ requiresPersistentTriggers: boolean;
9
+ };
7
10
  export interface WebDBAdapter extends DBAdapter {
8
11
  /**
9
12
  * Get a MessagePort which can be used to share the internals of this connection.
@@ -13,5 +16,5 @@ export interface WebDBAdapter extends DBAdapter {
13
16
  * Get the config options used to open this connection.
14
17
  * This is useful for sharing connections.
15
18
  */
16
- getConfiguration(): ResolvedWebSQLOpenOptions;
19
+ getConfiguration(): WebDBAdapterConfiguration;
17
20
  }
@@ -1,6 +1,7 @@
1
+ import { BaseObserver } from '@powersync/common';
1
2
  import * as Comlink from 'comlink';
2
- import { AsyncDatabaseConnection, OnTableChangeCallback, OpenAsyncDatabaseConnection, ProxiedQueryResult } from './AsyncDatabaseConnection';
3
- import { ResolvedWebSQLOpenOptions } from './web-sql-flags';
3
+ import { AsyncDatabaseConnection, OnTableChangeCallback, OpenAsyncDatabaseConnection, ProxiedQueryResult } from './AsyncDatabaseConnection.js';
4
+ import { ResolvedWebSQLOpenOptions } from './web-sql-flags.js';
4
5
  export type SharedConnectionWorker = {
5
6
  identifier: string;
6
7
  port: MessagePort;
@@ -15,11 +16,14 @@ export type WrappedWorkerConnectionOptions<Config extends ResolvedWebSQLOpenOpti
15
16
  remote: Comlink.Remote<OpenAsyncDatabaseConnection<Config>>;
16
17
  onClose?: () => void;
17
18
  };
19
+ export type WorkerWrappedAsyncDatabaseConnectionListener = {
20
+ closing: () => void;
21
+ };
18
22
  /**
19
23
  * Wraps a provided instance of {@link AsyncDatabaseConnection}, providing necessary proxy
20
24
  * functions for worker listeners.
21
25
  */
22
- export declare class WorkerWrappedAsyncDatabaseConnection<Config extends ResolvedWebSQLOpenOptions = ResolvedWebSQLOpenOptions> implements AsyncDatabaseConnection {
26
+ export declare class WorkerWrappedAsyncDatabaseConnection<Config extends ResolvedWebSQLOpenOptions = ResolvedWebSQLOpenOptions> extends BaseObserver<WorkerWrappedAsyncDatabaseConnectionListener> implements AsyncDatabaseConnection {
23
27
  protected options: WrappedWorkerConnectionOptions<Config>;
24
28
  protected lockAbortController: AbortController;
25
29
  protected notifyRemoteClosed: AbortController | undefined;
@@ -1,13 +1,15 @@
1
+ import { BaseObserver, ConnectionClosedError } from '@powersync/common';
1
2
  import * as Comlink from 'comlink';
2
3
  /**
3
4
  * Wraps a provided instance of {@link AsyncDatabaseConnection}, providing necessary proxy
4
5
  * functions for worker listeners.
5
6
  */
6
- export class WorkerWrappedAsyncDatabaseConnection {
7
+ export class WorkerWrappedAsyncDatabaseConnection extends BaseObserver {
7
8
  options;
8
9
  lockAbortController = new AbortController();
9
10
  notifyRemoteClosed;
10
11
  constructor(options) {
12
+ super();
11
13
  this.options = options;
12
14
  if (options.remoteCanCloseUnexpectedly) {
13
15
  this.notifyRemoteClosed = new AbortController();
@@ -40,17 +42,20 @@ export class WorkerWrappedAsyncDatabaseConnection {
40
42
  isAutoCommit() {
41
43
  return this.withRemote(() => this.baseConnection.isAutoCommit());
42
44
  }
43
- withRemote(workerPromise) {
45
+ withRemote(workerPromise, fireActionOnAbort = false) {
44
46
  const controller = this.notifyRemoteClosed;
45
47
  if (controller) {
46
48
  return new Promise((resolve, reject) => {
47
49
  if (controller.signal.aborted) {
48
- reject(new Error('Called operation on closed remote'));
49
- // Don't run the operation if we're going to reject
50
- return;
50
+ reject(new ConnectionClosedError('Called operation on closed remote'));
51
+ if (!fireActionOnAbort) {
52
+ // Don't run the operation if we're going to reject
53
+ // We might want to fire-and-forget the operation in some cases (like a close operation)
54
+ return;
55
+ }
51
56
  }
52
57
  function handleAbort() {
53
- reject(new Error('Remote peer closed with request in flight'));
58
+ reject(new ConnectionClosedError('Remote peer closed with request in flight'));
54
59
  }
55
60
  function completePromise(action) {
56
61
  controller.signal.removeEventListener('abort', handleAbort);
@@ -118,11 +123,13 @@ export class WorkerWrappedAsyncDatabaseConnection {
118
123
  // Abort any pending lock requests.
119
124
  this.lockAbortController.abort();
120
125
  try {
121
- await this.withRemote(() => this.baseConnection.close());
126
+ // fire and forget the close operation
127
+ await this.withRemote(() => this.baseConnection.close(), true);
122
128
  }
123
129
  finally {
124
130
  this.options.remote[Comlink.releaseProxy]();
125
131
  this.options.onClose?.();
132
+ this.iterateListeners((l) => l.closing?.());
126
133
  }
127
134
  }
128
135
  execute(sql, params) {
@@ -0,0 +1,12 @@
1
+ import { LockedAsyncDatabaseAdapter } from '../LockedAsyncDatabaseAdapter.js';
2
+ import { WebDBAdapterConfiguration } from '../WebDBAdapter.js';
3
+ /**
4
+ * @internal
5
+ * An intermediary implementation of WASQLiteDBAdapter, which takes the same
6
+ * constructor arguments as {@link LockedAsyncDatabaseAdapter}, but provides some
7
+ * basic WA-SQLite specific functionality.
8
+ * This base class is used to avoid requiring overloading the constructor of {@link WASQLiteDBAdapter}
9
+ */
10
+ export declare class InternalWASQLiteDBAdapter extends LockedAsyncDatabaseAdapter {
11
+ getConfiguration(): WebDBAdapterConfiguration;
12
+ }
@@ -0,0 +1,19 @@
1
+ import { LockedAsyncDatabaseAdapter } from '../LockedAsyncDatabaseAdapter.js';
2
+ import { WASQLiteVFS } from './WASQLiteConnection.js';
3
+ /**
4
+ * @internal
5
+ * An intermediary implementation of WASQLiteDBAdapter, which takes the same
6
+ * constructor arguments as {@link LockedAsyncDatabaseAdapter}, but provides some
7
+ * basic WA-SQLite specific functionality.
8
+ * This base class is used to avoid requiring overloading the constructor of {@link WASQLiteDBAdapter}
9
+ */
10
+ export class InternalWASQLiteDBAdapter extends LockedAsyncDatabaseAdapter {
11
+ getConfiguration() {
12
+ // This is valid since we only handle WASQLite connections
13
+ const baseConfig = super.getConfiguration();
14
+ return {
15
+ ...super.getConfiguration(),
16
+ requiresPersistentTriggers: baseConfig.vfs == WASQLiteVFS.OPFSCoopSyncVFS || baseConfig.vfs == WASQLiteVFS.AccessHandlePoolVFS
17
+ };
18
+ }
19
+ }
@@ -1,8 +1,8 @@
1
1
  import * as SQLite from '@journeyapps/wa-sqlite';
2
2
  import { BaseObserver, BatchedUpdateNotification } from '@powersync/common';
3
3
  import { Mutex } from 'async-mutex';
4
- import { AsyncDatabaseConnection, OnTableChangeCallback, ProxiedQueryResult } from '../AsyncDatabaseConnection';
5
- import { ResolvedWASQLiteOpenFactoryOptions } from './WASQLiteOpenFactory';
4
+ import { AsyncDatabaseConnection, OnTableChangeCallback, ProxiedQueryResult } from '../AsyncDatabaseConnection.js';
5
+ import { ResolvedWASQLiteOpenFactoryOptions } from './WASQLiteOpenFactory.js';
6
6
  /**
7
7
  * List of currently tested virtual filesystems
8
8
  */
@@ -82,9 +82,10 @@ export const DEFAULT_MODULE_FACTORIES = {
82
82
  }
83
83
  // @ts-expect-error The types for this static method are missing upstream
84
84
  const { OPFSCoopSyncVFS } = await import('@journeyapps/wa-sqlite/src/examples/OPFSCoopSyncVFS.js');
85
+ const vfs = await OPFSCoopSyncVFS.create(options.dbFileName, module);
85
86
  return {
86
87
  module,
87
- vfs: await OPFSCoopSyncVFS.create(options.dbFileName, module)
88
+ vfs
88
89
  };
89
90
  }
90
91
  };
@@ -310,7 +311,15 @@ export class WASqliteConnection extends BaseObserver {
310
311
  }
311
312
  async close() {
312
313
  this.broadcastChannel?.close();
313
- await this.sqliteAPI.close(this.dbP);
314
+ await this.acquireExecuteLock(async () => {
315
+ /**
316
+ * Running the close operation inside the same execute mutex prevents errors like:
317
+ * ```
318
+ * unable to close due to unfinalized statements or unfinished backups
319
+ * ```
320
+ */
321
+ await this.sqliteAPI.close(this.dbP);
322
+ });
314
323
  }
315
324
  async registerOnTableChange(callback) {
316
325
  return this.registerListener({
@@ -1,7 +1,7 @@
1
1
  import { type PowerSyncOpenFactoryOptions } from '@powersync/common';
2
- import { LockedAsyncDatabaseAdapter } from '../LockedAsyncDatabaseAdapter';
3
- import { ResolvedWebSQLOpenOptions, TemporaryStorageOption, WebSQLFlags } from '../web-sql-flags';
4
- import { WASQLiteVFS } from './WASQLiteConnection';
2
+ import { ResolvedWebSQLOpenOptions, TemporaryStorageOption, WebSQLFlags } from '../web-sql-flags.js';
3
+ import { InternalWASQLiteDBAdapter } from './InternalWASQLiteDBAdapter.js';
4
+ import { WASQLiteVFS } from './WASQLiteConnection.js';
5
5
  /**
6
6
  * These flags are the same as {@link WebSQLFlags}.
7
7
  * This export is maintained only for API consistency
@@ -27,6 +27,6 @@ export interface WASQLiteDBAdapterOptions extends Omit<PowerSyncOpenFactoryOptio
27
27
  /**
28
28
  * Adapter for WA-SQLite SQLite connections.
29
29
  */
30
- export declare class WASQLiteDBAdapter extends LockedAsyncDatabaseAdapter {
30
+ export declare class WASQLiteDBAdapter extends InternalWASQLiteDBAdapter {
31
31
  constructor(options: WASQLiteDBAdapterOptions);
32
32
  }
@@ -1,13 +1,13 @@
1
1
  import * as Comlink from 'comlink';
2
- import { resolveWebPowerSyncFlags } from '../../PowerSyncDatabase';
3
- import { LockedAsyncDatabaseAdapter } from '../LockedAsyncDatabaseAdapter';
4
- import { DEFAULT_CACHE_SIZE_KB, TemporaryStorageOption } from '../web-sql-flags';
5
- import { WorkerWrappedAsyncDatabaseConnection } from '../WorkerWrappedAsyncDatabaseConnection';
6
- import { WASQLiteOpenFactory } from './WASQLiteOpenFactory';
2
+ import { resolveWebPowerSyncFlags } from '../../PowerSyncDatabase.js';
3
+ import { DEFAULT_CACHE_SIZE_KB, TemporaryStorageOption } from '../web-sql-flags.js';
4
+ import { WorkerWrappedAsyncDatabaseConnection } from '../WorkerWrappedAsyncDatabaseConnection.js';
5
+ import { InternalWASQLiteDBAdapter } from './InternalWASQLiteDBAdapter.js';
6
+ import { WASQLiteOpenFactory } from './WASQLiteOpenFactory.js';
7
7
  /**
8
8
  * Adapter for WA-SQLite SQLite connections.
9
9
  */
10
- export class WASQLiteDBAdapter extends LockedAsyncDatabaseAdapter {
10
+ export class WASQLiteDBAdapter extends InternalWASQLiteDBAdapter {
11
11
  constructor(options) {
12
12
  super({
13
13
  name: options.dbFilename,
@@ -1,8 +1,8 @@
1
- import { type ILogLevel, DBAdapter } from '@powersync/common';
2
- import { AbstractWebSQLOpenFactory } from '../AbstractWebSQLOpenFactory';
3
- import { AsyncDatabaseConnection } from '../AsyncDatabaseConnection';
4
- import { ResolvedWebSQLOpenOptions, WebSQLOpenFactoryOptions } from '../web-sql-flags';
5
- import { WASQLiteVFS } from './WASQLiteConnection';
1
+ import { DBAdapter, type ILogLevel } from '@powersync/common';
2
+ import { AbstractWebSQLOpenFactory } from '../AbstractWebSQLOpenFactory.js';
3
+ import { AsyncDatabaseConnection } from '../AsyncDatabaseConnection.js';
4
+ import { ResolvedWebSQLOpenOptions, WebSQLOpenFactoryOptions } from '../web-sql-flags.js';
5
+ import { WASQLiteVFS } from './WASQLiteConnection.js';
6
6
  export interface WASQLiteOpenFactoryOptions extends WebSQLOpenFactoryOptions {
7
7
  vfs?: WASQLiteVFS;
8
8
  }
@@ -1,10 +1,10 @@
1
1
  import * as Comlink from 'comlink';
2
- import { openWorkerDatabasePort, resolveWorkerDatabasePortFactory } from '../../../worker/db/open-worker-database';
3
- import { AbstractWebSQLOpenFactory } from '../AbstractWebSQLOpenFactory';
4
- import { LockedAsyncDatabaseAdapter } from '../LockedAsyncDatabaseAdapter';
5
- import { DEFAULT_CACHE_SIZE_KB, TemporaryStorageOption } from '../web-sql-flags';
6
- import { WorkerWrappedAsyncDatabaseConnection } from '../WorkerWrappedAsyncDatabaseConnection';
7
- import { WASqliteConnection, WASQLiteVFS } from './WASQLiteConnection';
2
+ import { openWorkerDatabasePort, resolveWorkerDatabasePortFactory } from '../../../worker/db/open-worker-database.js';
3
+ import { AbstractWebSQLOpenFactory } from '../AbstractWebSQLOpenFactory.js';
4
+ import { WorkerWrappedAsyncDatabaseConnection } from '../WorkerWrappedAsyncDatabaseConnection.js';
5
+ import { DEFAULT_CACHE_SIZE_KB, TemporaryStorageOption } from '../web-sql-flags.js';
6
+ import { InternalWASQLiteDBAdapter } from './InternalWASQLiteDBAdapter.js';
7
+ import { WASQLiteVFS, WASqliteConnection } from './WASQLiteConnection.js';
8
8
  /**
9
9
  * Opens a SQLite connection using WA-SQLite.
10
10
  */
@@ -18,7 +18,7 @@ export class WASQLiteOpenFactory extends AbstractWebSQLOpenFactory {
18
18
  return this.options;
19
19
  }
20
20
  openAdapter() {
21
- return new LockedAsyncDatabaseAdapter({
21
+ return new InternalWASQLiteDBAdapter({
22
22
  name: this.options.dbFilename,
23
23
  openConnection: () => this.openConnection(),
24
24
  debugMode: this.options.debugMode,
@@ -1,5 +1,5 @@
1
1
  import { AbstractPowerSyncDatabase, DBAdapter, PowerSyncDatabaseOptions } from '@powersync/common';
2
- import { AbstractWebPowerSyncDatabaseOpenFactory } from '../AbstractWebPowerSyncDatabaseOpenFactory';
2
+ import { AbstractWebPowerSyncDatabaseOpenFactory } from '../AbstractWebPowerSyncDatabaseOpenFactory.js';
3
3
  /**
4
4
  * @deprecated {@link PowerSyncDatabase} can now be constructed directly
5
5
  * @example
@@ -1,6 +1,6 @@
1
- import { PowerSyncDatabase } from '../../../db/PowerSyncDatabase';
2
- import { AbstractWebPowerSyncDatabaseOpenFactory } from '../AbstractWebPowerSyncDatabaseOpenFactory';
3
- import { WASQLiteOpenFactory } from './WASQLiteOpenFactory';
1
+ import { PowerSyncDatabase } from '../../../db/PowerSyncDatabase.js';
2
+ import { AbstractWebPowerSyncDatabaseOpenFactory } from '../AbstractWebPowerSyncDatabaseOpenFactory.js';
3
+ import { WASQLiteOpenFactory } from './WASQLiteOpenFactory.js';
4
4
  /**
5
5
  * @deprecated {@link PowerSyncDatabase} can now be constructed directly
6
6
  * @example
@@ -1,9 +1,9 @@
1
1
  import { PowerSyncConnectionOptions, PowerSyncCredentials, SubscribedStream, SyncStatusOptions } from '@powersync/common';
2
2
  import * as Comlink from 'comlink';
3
- import { AbstractSharedSyncClientProvider } from '../../worker/sync/AbstractSharedSyncClientProvider';
4
- import { WebDBAdapter } from '../adapters/WebDBAdapter';
5
- import { WebStreamingSyncImplementation, WebStreamingSyncImplementationOptions } from './WebStreamingSyncImplementation';
6
- import { WorkerClient } from '../../worker/sync/WorkerClient';
3
+ import { AbstractSharedSyncClientProvider } from '../../worker/sync/AbstractSharedSyncClientProvider.js';
4
+ import { WorkerClient } from '../../worker/sync/WorkerClient.js';
5
+ import { WebDBAdapter } from '../adapters/WebDBAdapter.js';
6
+ import { WebStreamingSyncImplementation, WebStreamingSyncImplementationOptions } from './WebStreamingSyncImplementation.js';
7
7
  /**
8
8
  * The shared worker will trigger methods on this side of the message port
9
9
  * via this client provider.
@@ -17,7 +17,7 @@ declare class SharedSyncClientProvider extends AbstractSharedSyncClientProvider
17
17
  invalidateCredentials(): void;
18
18
  fetchCredentials(): Promise<PowerSyncCredentials | null>;
19
19
  uploadCrud(): Promise<void>;
20
- get logger(): import("js-logger").ILogger | undefined;
20
+ get logger(): import("@powersync/common").ILogger | undefined;
21
21
  trace(...x: any[]): void;
22
22
  debug(...x: any[]): void;
23
23
  info(...x: any[]): void;
@@ -41,6 +41,7 @@ export declare class SharedWebStreamingSyncImplementation extends WebStreamingSy
41
41
  protected dbAdapter: WebDBAdapter;
42
42
  private abortOnClose;
43
43
  constructor(options: SharedWebStreamingSyncImplementationOptions);
44
+ protected _init(): Promise<void>;
44
45
  /**
45
46
  * Starts the sync process, this effectively acts as a call to
46
47
  * `connect` if not yet connected.
@@ -52,9 +53,5 @@ export declare class SharedWebStreamingSyncImplementation extends WebStreamingSy
52
53
  dispose(): Promise<void>;
53
54
  waitForReady(): Promise<void>;
54
55
  updateSubscriptions(subscriptions: SubscribedStream[]): void;
55
- /**
56
- * Used in tests to force a connection states
57
- */
58
- private _testUpdateStatus;
59
56
  }
60
57
  export {};