@powersync/web 1.36.0 → 1.37.1

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 (91) hide show
  1. package/dist/index.umd.js +1127 -1235
  2. package/dist/index.umd.js.map +1 -1
  3. package/dist/worker/SharedSyncImplementation.umd.js +550 -3089
  4. package/dist/worker/SharedSyncImplementation.umd.js.map +1 -1
  5. package/dist/worker/WASQLiteDB.umd.js +797 -854
  6. package/dist/worker/WASQLiteDB.umd.js.map +1 -1
  7. package/lib/package.json +2 -3
  8. package/lib/src/db/PowerSyncDatabase.d.ts +1 -2
  9. package/lib/src/db/PowerSyncDatabase.js +3 -4
  10. package/lib/src/db/adapters/AsyncWebAdapter.d.ts +40 -0
  11. package/lib/src/db/adapters/AsyncWebAdapter.js +69 -0
  12. package/lib/src/db/adapters/SSRDBAdapter.d.ts +1 -2
  13. package/lib/src/db/adapters/SSRDBAdapter.js +5 -6
  14. package/lib/src/db/adapters/wa-sqlite/ConcurrentConnection.d.ts +56 -0
  15. package/lib/src/db/adapters/wa-sqlite/ConcurrentConnection.js +121 -0
  16. package/lib/src/db/adapters/wa-sqlite/DatabaseClient.d.ts +54 -0
  17. package/lib/src/db/adapters/wa-sqlite/DatabaseClient.js +227 -0
  18. package/lib/src/db/adapters/wa-sqlite/DatabaseServer.d.ts +47 -0
  19. package/lib/src/db/adapters/wa-sqlite/DatabaseServer.js +146 -0
  20. package/lib/src/db/adapters/wa-sqlite/RawSqliteConnection.d.ts +46 -0
  21. package/lib/src/db/adapters/wa-sqlite/RawSqliteConnection.js +147 -0
  22. package/lib/src/db/adapters/wa-sqlite/WASQLiteOpenFactory.d.ts +14 -6
  23. package/lib/src/db/adapters/wa-sqlite/WASQLiteOpenFactory.js +66 -39
  24. package/lib/src/db/adapters/wa-sqlite/vfs.d.ts +61 -0
  25. package/lib/src/db/adapters/wa-sqlite/vfs.js +91 -0
  26. package/lib/src/db/adapters/web-sql-flags.d.ts +5 -0
  27. package/lib/src/db/sync/SSRWebStreamingSyncImplementation.d.ts +1 -2
  28. package/lib/src/db/sync/SSRWebStreamingSyncImplementation.js +2 -3
  29. package/lib/src/db/sync/SharedWebStreamingSyncImplementation.js +4 -19
  30. package/lib/src/index.d.ts +1 -4
  31. package/lib/src/index.js +1 -4
  32. package/lib/src/shared/tab_close_signal.d.ts +11 -0
  33. package/lib/src/shared/tab_close_signal.js +26 -0
  34. package/lib/src/worker/db/MultiDatabaseServer.d.ts +17 -0
  35. package/lib/src/worker/db/MultiDatabaseServer.js +86 -0
  36. package/lib/src/worker/db/WASQLiteDB.worker.js +9 -48
  37. package/lib/src/worker/db/open-worker-database.d.ts +3 -3
  38. package/lib/src/worker/db/open-worker-database.js +1 -1
  39. package/lib/src/worker/sync/SharedSyncImplementation.d.ts +5 -6
  40. package/lib/src/worker/sync/SharedSyncImplementation.js +92 -54
  41. package/lib/tsconfig.tsbuildinfo +1 -1
  42. package/package.json +3 -4
  43. package/src/db/PowerSyncDatabase.ts +3 -3
  44. package/src/db/adapters/AsyncWebAdapter.ts +91 -0
  45. package/src/db/adapters/SSRDBAdapter.ts +7 -7
  46. package/src/db/adapters/wa-sqlite/ConcurrentConnection.ts +137 -0
  47. package/src/db/adapters/wa-sqlite/DatabaseClient.ts +325 -0
  48. package/src/db/adapters/wa-sqlite/DatabaseServer.ts +201 -0
  49. package/src/db/adapters/wa-sqlite/RawSqliteConnection.ts +191 -0
  50. package/src/db/adapters/wa-sqlite/WASQLiteOpenFactory.ts +87 -43
  51. package/src/db/adapters/wa-sqlite/vfs.ts +112 -0
  52. package/src/db/adapters/web-sql-flags.ts +6 -0
  53. package/src/db/sync/SSRWebStreamingSyncImplementation.ts +2 -3
  54. package/src/db/sync/SharedWebStreamingSyncImplementation.ts +4 -20
  55. package/src/index.ts +1 -4
  56. package/src/shared/tab_close_signal.ts +28 -0
  57. package/src/worker/db/MultiDatabaseServer.ts +104 -0
  58. package/src/worker/db/WASQLiteDB.worker.ts +10 -57
  59. package/src/worker/db/open-worker-database.ts +3 -3
  60. package/src/worker/sync/SharedSyncImplementation.ts +118 -58
  61. package/dist/_journeyapps_wa-sqlite-_journeyapps_wa-sqlite_src_examples_AccessHandlePoolVFS_js-_journeyapp-89f0ba.index.umd.js +0 -1881
  62. package/dist/_journeyapps_wa-sqlite-_journeyapps_wa-sqlite_src_examples_AccessHandlePoolVFS_js-_journeyapp-89f0ba.index.umd.js.map +0 -1
  63. package/dist/_journeyapps_wa-sqlite_src_examples_AccessHandlePoolVFS_js-_journeyapps_wa-sqlite_src_example-97ebe9.index.umd.js +0 -555
  64. package/dist/_journeyapps_wa-sqlite_src_examples_AccessHandlePoolVFS_js-_journeyapps_wa-sqlite_src_example-97ebe9.index.umd.js.map +0 -1
  65. package/lib/src/db/adapters/AbstractWebSQLOpenFactory.d.ts +0 -17
  66. package/lib/src/db/adapters/AbstractWebSQLOpenFactory.js +0 -33
  67. package/lib/src/db/adapters/AsyncDatabaseConnection.d.ts +0 -49
  68. package/lib/src/db/adapters/AsyncDatabaseConnection.js +0 -1
  69. package/lib/src/db/adapters/LockedAsyncDatabaseAdapter.d.ts +0 -109
  70. package/lib/src/db/adapters/LockedAsyncDatabaseAdapter.js +0 -404
  71. package/lib/src/db/adapters/WorkerWrappedAsyncDatabaseConnection.d.ts +0 -59
  72. package/lib/src/db/adapters/WorkerWrappedAsyncDatabaseConnection.js +0 -147
  73. package/lib/src/db/adapters/wa-sqlite/InternalWASQLiteDBAdapter.d.ts +0 -12
  74. package/lib/src/db/adapters/wa-sqlite/InternalWASQLiteDBAdapter.js +0 -19
  75. package/lib/src/db/adapters/wa-sqlite/WASQLiteConnection.d.ts +0 -155
  76. package/lib/src/db/adapters/wa-sqlite/WASQLiteConnection.js +0 -401
  77. package/lib/src/db/adapters/wa-sqlite/WASQLiteDBAdapter.d.ts +0 -32
  78. package/lib/src/db/adapters/wa-sqlite/WASQLiteDBAdapter.js +0 -49
  79. package/lib/src/worker/db/SharedWASQLiteConnection.d.ts +0 -42
  80. package/lib/src/worker/db/SharedWASQLiteConnection.js +0 -90
  81. package/lib/src/worker/db/WorkerWASQLiteConnection.d.ts +0 -9
  82. package/lib/src/worker/db/WorkerWASQLiteConnection.js +0 -12
  83. package/src/db/adapters/AbstractWebSQLOpenFactory.ts +0 -48
  84. package/src/db/adapters/AsyncDatabaseConnection.ts +0 -55
  85. package/src/db/adapters/LockedAsyncDatabaseAdapter.ts +0 -489
  86. package/src/db/adapters/WorkerWrappedAsyncDatabaseConnection.ts +0 -201
  87. package/src/db/adapters/wa-sqlite/InternalWASQLiteDBAdapter.ts +0 -23
  88. package/src/db/adapters/wa-sqlite/WASQLiteConnection.ts +0 -497
  89. package/src/db/adapters/wa-sqlite/WASQLiteDBAdapter.ts +0 -86
  90. package/src/worker/db/SharedWASQLiteConnection.ts +0 -131
  91. package/src/worker/db/WorkerWASQLiteConnection.ts +0 -14
package/dist/index.umd.js CHANGED
@@ -1,13 +1,13 @@
1
1
  (function webpackUniversalModuleDefinition(root, factory) {
2
2
  if(typeof exports === 'object' && typeof module === 'object')
3
- module.exports = factory(require("@powersync/common"), require("async-mutex"), require("comlink"), require("@journeyapps/wa-sqlite"), require("@journeyapps/wa-sqlite/src/examples/OPFSCoopSyncVFS.js"), require("@journeyapps/wa-sqlite/src/examples/AccessHandlePoolVFS.js"), require("@journeyapps/wa-sqlite/src/examples/IDBBatchAtomicVFS.js"));
3
+ module.exports = factory(require("@powersync/common"), require("comlink"), require("@journeyapps/wa-sqlite"), require("@journeyapps/wa-sqlite/src/examples/OPFSCoopSyncVFS.js"), require("@journeyapps/wa-sqlite/src/examples/AccessHandlePoolVFS.js"), require("@journeyapps/wa-sqlite/src/examples/IDBBatchAtomicVFS.js"));
4
4
  else if(typeof define === 'function' && define.amd)
5
- define(["@powersync/common", "async-mutex", "comlink", "@journeyapps/wa-sqlite", "@journeyapps/wa-sqlite/src/examples/OPFSCoopSyncVFS.js", "@journeyapps/wa-sqlite/src/examples/AccessHandlePoolVFS.js", "@journeyapps/wa-sqlite/src/examples/IDBBatchAtomicVFS.js"], factory);
5
+ define(["@powersync/common", "comlink", "@journeyapps/wa-sqlite", "@journeyapps/wa-sqlite/src/examples/OPFSCoopSyncVFS.js", "@journeyapps/wa-sqlite/src/examples/AccessHandlePoolVFS.js", "@journeyapps/wa-sqlite/src/examples/IDBBatchAtomicVFS.js"], factory);
6
6
  else if(typeof exports === 'object')
7
- exports["sdk_web"] = factory(require("@powersync/common"), require("async-mutex"), require("comlink"), require("@journeyapps/wa-sqlite"), require("@journeyapps/wa-sqlite/src/examples/OPFSCoopSyncVFS.js"), require("@journeyapps/wa-sqlite/src/examples/AccessHandlePoolVFS.js"), require("@journeyapps/wa-sqlite/src/examples/IDBBatchAtomicVFS.js"));
7
+ exports["sdk_web"] = factory(require("@powersync/common"), require("comlink"), require("@journeyapps/wa-sqlite"), require("@journeyapps/wa-sqlite/src/examples/OPFSCoopSyncVFS.js"), require("@journeyapps/wa-sqlite/src/examples/AccessHandlePoolVFS.js"), require("@journeyapps/wa-sqlite/src/examples/IDBBatchAtomicVFS.js"));
8
8
  else
9
- root["sdk_web"] = factory(root["@powersync/common"], root["async-mutex"], root["comlink"], root["@journeyapps/wa-sqlite"], root["@journeyapps/wa-sqlite/src/examples/OPFSCoopSyncVFS.js"], root["@journeyapps/wa-sqlite/src/examples/AccessHandlePoolVFS.js"], root["@journeyapps/wa-sqlite/src/examples/IDBBatchAtomicVFS.js"]);
10
- })(self, (__WEBPACK_EXTERNAL_MODULE__powersync_common__, __WEBPACK_EXTERNAL_MODULE_async_mutex__, __WEBPACK_EXTERNAL_MODULE_comlink__, __WEBPACK_EXTERNAL_MODULE__journeyapps_wa_sqlite__, __WEBPACK_EXTERNAL_MODULE__journeyapps_wa_sqlite_src_examples_OPFSCoopSyncVFS_js__, __WEBPACK_EXTERNAL_MODULE__journeyapps_wa_sqlite_src_examples_AccessHandlePoolVFS_js__, __WEBPACK_EXTERNAL_MODULE__journeyapps_wa_sqlite_src_examples_IDBBatchAtomicVFS_js__) => {
9
+ root["sdk_web"] = factory(root["@powersync/common"], root["comlink"], root["@journeyapps/wa-sqlite"], root["@journeyapps/wa-sqlite/src/examples/OPFSCoopSyncVFS.js"], root["@journeyapps/wa-sqlite/src/examples/AccessHandlePoolVFS.js"], root["@journeyapps/wa-sqlite/src/examples/IDBBatchAtomicVFS.js"]);
10
+ })(self, (__WEBPACK_EXTERNAL_MODULE__powersync_common__, __WEBPACK_EXTERNAL_MODULE_comlink__, __WEBPACK_EXTERNAL_MODULE__journeyapps_wa_sqlite__, __WEBPACK_EXTERNAL_MODULE__journeyapps_wa_sqlite_src_examples_OPFSCoopSyncVFS_js__, __WEBPACK_EXTERNAL_MODULE__journeyapps_wa_sqlite_src_examples_AccessHandlePoolVFS_js__, __WEBPACK_EXTERNAL_MODULE__journeyapps_wa_sqlite_src_examples_IDBBatchAtomicVFS_js__) => {
11
11
  return /******/ (() => { // webpackBootstrap
12
12
  /******/ "use strict";
13
13
  /******/ var __webpack_modules__ = ({
@@ -102,16 +102,6 @@ module.exports = __WEBPACK_EXTERNAL_MODULE__powersync_common__;
102
102
 
103
103
  /***/ },
104
104
 
105
- /***/ "async-mutex"
106
- /*!******************************!*\
107
- !*** external "async-mutex" ***!
108
- \******************************/
109
- (module) {
110
-
111
- module.exports = __WEBPACK_EXTERNAL_MODULE_async_mutex__;
112
-
113
- /***/ },
114
-
115
105
  /***/ "comlink"
116
106
  /*!**************************!*\
117
107
  !*** external "comlink" ***!
@@ -4993,17 +4983,15 @@ __webpack_require__.r(__webpack_exports__);
4993
4983
  /* harmony export */ resolveWebPowerSyncFlags: () => (/* binding */ resolveWebPowerSyncFlags)
4994
4984
  /* harmony export */ });
4995
4985
  /* harmony import */ var _powersync_common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @powersync/common */ "@powersync/common");
4996
- /* harmony import */ var async_mutex__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! async-mutex */ "async-mutex");
4997
- /* harmony import */ var _shared_navigator_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../shared/navigator.js */ "./lib/src/shared/navigator.js");
4998
- /* harmony import */ var _NavigatorTriggerClaimManager_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./NavigatorTriggerClaimManager.js */ "./lib/src/db/NavigatorTriggerClaimManager.js");
4999
- /* harmony import */ var _adapters_LockedAsyncDatabaseAdapter_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./adapters/LockedAsyncDatabaseAdapter.js */ "./lib/src/db/adapters/LockedAsyncDatabaseAdapter.js");
5000
- /* harmony import */ var _adapters_wa_sqlite_WASQLiteOpenFactory_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./adapters/wa-sqlite/WASQLiteOpenFactory.js */ "./lib/src/db/adapters/wa-sqlite/WASQLiteOpenFactory.js");
5001
- /* harmony import */ var _adapters_web_sql_flags_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./adapters/web-sql-flags.js */ "./lib/src/db/adapters/web-sql-flags.js");
5002
- /* harmony import */ var _sync_SSRWebStreamingSyncImplementation_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./sync/SSRWebStreamingSyncImplementation.js */ "./lib/src/db/sync/SSRWebStreamingSyncImplementation.js");
5003
- /* harmony import */ var _sync_SharedWebStreamingSyncImplementation_js__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./sync/SharedWebStreamingSyncImplementation.js */ "./lib/src/db/sync/SharedWebStreamingSyncImplementation.js");
5004
- /* harmony import */ var _sync_WebRemote_js__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./sync/WebRemote.js */ "./lib/src/db/sync/WebRemote.js");
5005
- /* harmony import */ var _sync_WebStreamingSyncImplementation_js__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./sync/WebStreamingSyncImplementation.js */ "./lib/src/db/sync/WebStreamingSyncImplementation.js");
5006
-
4986
+ /* harmony import */ var _shared_navigator_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../shared/navigator.js */ "./lib/src/shared/navigator.js");
4987
+ /* harmony import */ var _NavigatorTriggerClaimManager_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./NavigatorTriggerClaimManager.js */ "./lib/src/db/NavigatorTriggerClaimManager.js");
4988
+ /* harmony import */ var _adapters_wa_sqlite_WASQLiteOpenFactory_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./adapters/wa-sqlite/WASQLiteOpenFactory.js */ "./lib/src/db/adapters/wa-sqlite/WASQLiteOpenFactory.js");
4989
+ /* harmony import */ var _adapters_web_sql_flags_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./adapters/web-sql-flags.js */ "./lib/src/db/adapters/web-sql-flags.js");
4990
+ /* harmony import */ var _sync_SSRWebStreamingSyncImplementation_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./sync/SSRWebStreamingSyncImplementation.js */ "./lib/src/db/sync/SSRWebStreamingSyncImplementation.js");
4991
+ /* harmony import */ var _sync_SharedWebStreamingSyncImplementation_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./sync/SharedWebStreamingSyncImplementation.js */ "./lib/src/db/sync/SharedWebStreamingSyncImplementation.js");
4992
+ /* harmony import */ var _sync_WebRemote_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./sync/WebRemote.js */ "./lib/src/db/sync/WebRemote.js");
4993
+ /* harmony import */ var _sync_WebStreamingSyncImplementation_js__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./sync/WebStreamingSyncImplementation.js */ "./lib/src/db/sync/WebStreamingSyncImplementation.js");
4994
+ /* harmony import */ var _adapters_AsyncWebAdapter_js__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./adapters/AsyncWebAdapter.js */ "./lib/src/db/adapters/AsyncWebAdapter.js");
5007
4995
 
5008
4996
 
5009
4997
 
@@ -5015,14 +5003,14 @@ __webpack_require__.r(__webpack_exports__);
5015
5003
 
5016
5004
 
5017
5005
  const DEFAULT_POWERSYNC_FLAGS = {
5018
- ..._adapters_web_sql_flags_js__WEBPACK_IMPORTED_MODULE_6__.DEFAULT_WEB_SQL_FLAGS,
5006
+ ..._adapters_web_sql_flags_js__WEBPACK_IMPORTED_MODULE_4__.DEFAULT_WEB_SQL_FLAGS,
5019
5007
  externallyUnload: false
5020
5008
  };
5021
5009
  const resolveWebPowerSyncFlags = (flags) => {
5022
5010
  return {
5023
5011
  ...DEFAULT_POWERSYNC_FLAGS,
5024
5012
  ...flags,
5025
- ...(0,_adapters_web_sql_flags_js__WEBPACK_IMPORTED_MODULE_6__.resolveWebSQLFlags)(flags)
5013
+ ...(0,_adapters_web_sql_flags_js__WEBPACK_IMPORTED_MODULE_4__.resolveWebSQLFlags)(flags)
5026
5014
  };
5027
5015
  };
5028
5016
  /**
@@ -5052,7 +5040,7 @@ function assertValidDatabaseOptions(options) {
5052
5040
  */
5053
5041
  class PowerSyncDatabase extends _powersync_common__WEBPACK_IMPORTED_MODULE_0__.AbstractPowerSyncDatabase {
5054
5042
  options;
5055
- static SHARED_MUTEX = new async_mutex__WEBPACK_IMPORTED_MODULE_1__.Mutex();
5043
+ static SHARED_MUTEX = new _powersync_common__WEBPACK_IMPORTED_MODULE_0__.Mutex();
5056
5044
  unloadListener;
5057
5045
  resolvedFlags;
5058
5046
  constructor(options) {
@@ -5066,7 +5054,7 @@ class PowerSyncDatabase extends _powersync_common__WEBPACK_IMPORTED_MODULE_0__.A
5066
5054
  }
5067
5055
  }
5068
5056
  async _initialize() {
5069
- if (this.database instanceof _adapters_LockedAsyncDatabaseAdapter_js__WEBPACK_IMPORTED_MODULE_4__.LockedAsyncDatabaseAdapter) {
5057
+ if (this.database instanceof _adapters_AsyncWebAdapter_js__WEBPACK_IMPORTED_MODULE_9__.AsyncDbAdapter) {
5070
5058
  /**
5071
5059
  * While init is done automatically,
5072
5060
  * LockedAsyncDatabaseAdapter only exposes config after init.
@@ -5087,11 +5075,11 @@ class PowerSyncDatabase extends _powersync_common__WEBPACK_IMPORTED_MODULE_0__.A
5087
5075
  generateTriggerManagerConfig() {
5088
5076
  return {
5089
5077
  // We need to share hold information between tabs for web
5090
- claimManager: _NavigatorTriggerClaimManager_js__WEBPACK_IMPORTED_MODULE_3__.NAVIGATOR_TRIGGER_CLAIM_MANAGER
5078
+ claimManager: _NavigatorTriggerClaimManager_js__WEBPACK_IMPORTED_MODULE_2__.NAVIGATOR_TRIGGER_CLAIM_MANAGER
5091
5079
  };
5092
5080
  }
5093
5081
  openDBAdapter(options) {
5094
- const defaultFactory = new _adapters_wa_sqlite_WASQLiteOpenFactory_js__WEBPACK_IMPORTED_MODULE_5__.WASQLiteOpenFactory({
5082
+ const defaultFactory = new _adapters_wa_sqlite_WASQLiteOpenFactory_js__WEBPACK_IMPORTED_MODULE_3__.WASQLiteOpenFactory({
5095
5083
  ...options.database,
5096
5084
  flags: resolveWebPowerSyncFlags(options.flags),
5097
5085
  encryptionKey: options.encryptionKey
@@ -5113,13 +5101,13 @@ class PowerSyncDatabase extends _powersync_common__WEBPACK_IMPORTED_MODULE_0__.A
5113
5101
  });
5114
5102
  }
5115
5103
  async loadVersion() {
5116
- if ((0,_adapters_web_sql_flags_js__WEBPACK_IMPORTED_MODULE_6__.isServerSide)()) {
5104
+ if ((0,_adapters_web_sql_flags_js__WEBPACK_IMPORTED_MODULE_4__.isServerSide)()) {
5117
5105
  return;
5118
5106
  }
5119
5107
  return super.loadVersion();
5120
5108
  }
5121
5109
  async resolveOfflineSyncStatus() {
5122
- if ((0,_adapters_web_sql_flags_js__WEBPACK_IMPORTED_MODULE_6__.isServerSide)()) {
5110
+ if ((0,_adapters_web_sql_flags_js__WEBPACK_IMPORTED_MODULE_4__.isServerSide)()) {
5123
5111
  return;
5124
5112
  }
5125
5113
  return super.resolveOfflineSyncStatus();
@@ -5131,10 +5119,10 @@ class PowerSyncDatabase extends _powersync_common__WEBPACK_IMPORTED_MODULE_0__.A
5131
5119
  if (this.resolvedFlags.ssrMode) {
5132
5120
  return PowerSyncDatabase.SHARED_MUTEX.runExclusive(cb);
5133
5121
  }
5134
- return (0,_shared_navigator_js__WEBPACK_IMPORTED_MODULE_2__.getNavigatorLocks)().request(`lock-${this.database.name}`, cb);
5122
+ return (0,_shared_navigator_js__WEBPACK_IMPORTED_MODULE_1__.getNavigatorLocks)().request(`lock-${this.database.name}`, cb);
5135
5123
  }
5136
5124
  generateSyncStreamImplementation(connector, options) {
5137
- const remote = new _sync_WebRemote_js__WEBPACK_IMPORTED_MODULE_9__.WebRemote(connector, this.logger);
5125
+ const remote = new _sync_WebRemote_js__WEBPACK_IMPORTED_MODULE_7__.WebRemote(connector, this.logger);
5138
5126
  const syncOptions = {
5139
5127
  ...this.options,
5140
5128
  ...options,
@@ -5150,7 +5138,7 @@ class PowerSyncDatabase extends _powersync_common__WEBPACK_IMPORTED_MODULE_0__.A
5150
5138
  };
5151
5139
  switch (true) {
5152
5140
  case this.resolvedFlags.ssrMode:
5153
- return new _sync_SSRWebStreamingSyncImplementation_js__WEBPACK_IMPORTED_MODULE_7__.SSRStreamingSyncImplementation(syncOptions);
5141
+ return new _sync_SSRWebStreamingSyncImplementation_js__WEBPACK_IMPORTED_MODULE_5__.SSRStreamingSyncImplementation(syncOptions);
5154
5142
  case this.resolvedFlags.enableMultiTabs:
5155
5143
  if (!this.resolvedFlags.broadcastLogs) {
5156
5144
  const warning = `
@@ -5160,12 +5148,12 @@ class PowerSyncDatabase extends _powersync_common__WEBPACK_IMPORTED_MODULE_0__.A
5160
5148
  const logger = this.options.logger;
5161
5149
  logger ? logger.warn(warning) : console.warn(warning);
5162
5150
  }
5163
- return new _sync_SharedWebStreamingSyncImplementation_js__WEBPACK_IMPORTED_MODULE_8__.SharedWebStreamingSyncImplementation({
5151
+ return new _sync_SharedWebStreamingSyncImplementation_js__WEBPACK_IMPORTED_MODULE_6__.SharedWebStreamingSyncImplementation({
5164
5152
  ...syncOptions,
5165
5153
  db: this.database // This should always be the case
5166
5154
  });
5167
5155
  default:
5168
- return new _sync_WebStreamingSyncImplementation_js__WEBPACK_IMPORTED_MODULE_10__.WebStreamingSyncImplementation(syncOptions);
5156
+ return new _sync_WebStreamingSyncImplementation_js__WEBPACK_IMPORTED_MODULE_8__.WebStreamingSyncImplementation(syncOptions);
5169
5157
  }
5170
5158
  }
5171
5159
  }
@@ -5215,485 +5203,85 @@ class AbstractWebPowerSyncDatabaseOpenFactory extends _powersync_common__WEBPACK
5215
5203
 
5216
5204
  /***/ },
5217
5205
 
5218
- /***/ "./lib/src/db/adapters/AbstractWebSQLOpenFactory.js"
5219
- /*!**********************************************************!*\
5220
- !*** ./lib/src/db/adapters/AbstractWebSQLOpenFactory.js ***!
5221
- \**********************************************************/
5222
- (__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) {
5223
-
5224
- __webpack_require__.r(__webpack_exports__);
5225
- /* harmony export */ __webpack_require__.d(__webpack_exports__, {
5226
- /* harmony export */ AbstractWebSQLOpenFactory: () => (/* binding */ AbstractWebSQLOpenFactory)
5227
- /* harmony export */ });
5228
- /* harmony import */ var _powersync_common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @powersync/common */ "@powersync/common");
5229
- /* harmony import */ var _SSRDBAdapter_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./SSRDBAdapter.js */ "./lib/src/db/adapters/SSRDBAdapter.js");
5230
- /* harmony import */ var _web_sql_flags_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./web-sql-flags.js */ "./lib/src/db/adapters/web-sql-flags.js");
5231
-
5232
-
5233
-
5234
- class AbstractWebSQLOpenFactory {
5235
- options;
5236
- resolvedFlags;
5237
- logger;
5238
- constructor(options) {
5239
- this.options = options;
5240
- this.resolvedFlags = (0,_web_sql_flags_js__WEBPACK_IMPORTED_MODULE_2__.resolveWebSQLFlags)(options.flags);
5241
- this.logger = options.logger ?? (0,_powersync_common__WEBPACK_IMPORTED_MODULE_0__.createLogger)(`AbstractWebSQLOpenFactory - ${this.options.dbFilename}`);
5242
- }
5243
- /**
5244
- * Opens a {@link DBAdapter} using resolved flags.
5245
- * A SSR implementation is loaded if SSR mode is detected.
5246
- */
5247
- openDB() {
5248
- const { resolvedFlags: { disableSSRWarning, enableMultiTabs, ssrMode = (0,_web_sql_flags_js__WEBPACK_IMPORTED_MODULE_2__.isServerSide)() } } = this;
5249
- if (ssrMode && !disableSSRWarning) {
5250
- this.logger.warn(`
5251
- Running PowerSync in SSR mode.
5252
- Only empty query results will be returned.
5253
- Disable this warning by setting 'disableSSRWarning: true' in options.`);
5254
- }
5255
- if (!enableMultiTabs) {
5256
- this.logger.warn('Multiple tab support is not enabled. Using this site across multiple tabs may not function correctly.');
5257
- }
5258
- if (ssrMode) {
5259
- return new _SSRDBAdapter_js__WEBPACK_IMPORTED_MODULE_1__.SSRDBAdapter();
5260
- }
5261
- return this.openAdapter();
5262
- }
5263
- }
5264
-
5265
-
5266
- /***/ },
5267
-
5268
- /***/ "./lib/src/db/adapters/AsyncDatabaseConnection.js"
5269
- /*!********************************************************!*\
5270
- !*** ./lib/src/db/adapters/AsyncDatabaseConnection.js ***!
5271
- \********************************************************/
5272
- (__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) {
5273
-
5274
- __webpack_require__.r(__webpack_exports__);
5275
-
5276
-
5277
-
5278
- /***/ },
5279
-
5280
- /***/ "./lib/src/db/adapters/LockedAsyncDatabaseAdapter.js"
5281
- /*!***********************************************************!*\
5282
- !*** ./lib/src/db/adapters/LockedAsyncDatabaseAdapter.js ***!
5283
- \***********************************************************/
5206
+ /***/ "./lib/src/db/adapters/AsyncWebAdapter.js"
5207
+ /*!************************************************!*\
5208
+ !*** ./lib/src/db/adapters/AsyncWebAdapter.js ***!
5209
+ \************************************************/
5284
5210
  (__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) {
5285
5211
 
5286
5212
  __webpack_require__.r(__webpack_exports__);
5287
5213
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
5288
- /* harmony export */ LockedAsyncDatabaseAdapter: () => (/* binding */ LockedAsyncDatabaseAdapter)
5214
+ /* harmony export */ AsyncDbAdapter: () => (/* binding */ AsyncDbAdapter)
5289
5215
  /* harmony export */ });
5290
5216
  /* harmony import */ var _powersync_common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @powersync/common */ "@powersync/common");
5291
- /* harmony import */ var _shared_navigator_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../shared/navigator.js */ "./lib/src/shared/navigator.js");
5292
- /* harmony import */ var _WorkerWrappedAsyncDatabaseConnection_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./WorkerWrappedAsyncDatabaseConnection.js */ "./lib/src/db/adapters/WorkerWrappedAsyncDatabaseConnection.js");
5293
- /* harmony import */ var _wa_sqlite_WASQLiteConnection_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./wa-sqlite/WASQLiteConnection.js */ "./lib/src/db/adapters/wa-sqlite/WASQLiteConnection.js");
5294
-
5295
-
5296
-
5297
5217
 
5298
5218
  /**
5299
- * @internal
5300
- * Wraps a {@link AsyncDatabaseConnection} and provides exclusive locking functions in
5301
- * order to implement {@link DBAdapter}.
5219
+ * A connection pool implementation delegating to another pool opened asynchronnously.
5302
5220
  */
5303
- class LockedAsyncDatabaseAdapter extends _powersync_common__WEBPACK_IMPORTED_MODULE_0__.BaseObserver {
5304
- options;
5305
- logger;
5306
- dbGetHelpers;
5307
- debugMode;
5308
- _dbIdentifier;
5309
- initPromise;
5310
- _db = null;
5311
- _disposeTableChangeListener = null;
5312
- _config = null;
5313
- pendingAbortControllers;
5314
- requiresHolds;
5315
- databaseOpenPromise = null;
5316
- closing;
5317
- closed;
5318
- constructor(options) {
5319
- super();
5320
- this.options = options;
5321
- this._dbIdentifier = options.name;
5322
- this.logger = options.logger ?? (0,_powersync_common__WEBPACK_IMPORTED_MODULE_0__.createLogger)(`LockedAsyncDatabaseAdapter - ${this._dbIdentifier}`);
5323
- this.pendingAbortControllers = new Set();
5324
- this.closed = false;
5325
- this.closing = false;
5326
- this.requiresHolds = null;
5327
- // Set the name if provided. We can query for the name if not available yet
5328
- this.debugMode = options.debugMode ?? false;
5329
- if (this.debugMode) {
5330
- const originalExecute = this._execute.bind(this);
5331
- this._execute = async (sql, bindings) => {
5332
- const start = performance.now();
5333
- try {
5334
- const r = await originalExecute(sql, bindings);
5335
- performance.measure(`[SQL] ${sql}`, { start });
5336
- return r;
5337
- }
5338
- catch (e) {
5339
- performance.measure(`[SQL] [ERROR: ${e.message}] ${sql}`, { start });
5340
- throw e;
5341
- }
5342
- };
5343
- }
5344
- this.dbGetHelpers = this.generateDBHelpers({
5345
- execute: (query, params) => this.acquireLock(() => this._execute(query, params)),
5346
- executeRaw: (query, params) => this.acquireLock(() => this._executeRaw(query, params)),
5347
- executeBatch: (query, params) => this.acquireLock(() => this._executeBatch(query, params))
5221
+ class AsyncConnectionPool {
5222
+ name;
5223
+ inner;
5224
+ resolvedClient;
5225
+ pendingListeners = new Set();
5226
+ constructor(inner, name) {
5227
+ this.name = name;
5228
+ this.inner = inner.then((client) => {
5229
+ for (const pending of this.pendingListeners) {
5230
+ pending.closeAfterRegisteredOnResolvedPool = client.registerListener(pending.listener);
5231
+ }
5232
+ this.pendingListeners.clear();
5233
+ this.resolvedClient = client;
5234
+ return client;
5348
5235
  });
5349
- this.initPromise = this._init();
5350
- }
5351
- get baseDB() {
5352
- if (!this._db) {
5353
- throw new Error(`Initialization has not completed yet. Cannot access base db`);
5354
- }
5355
- return this._db;
5356
- }
5357
- get name() {
5358
- return this._dbIdentifier;
5359
5236
  }
5360
- /**
5361
- * Init is automatic, this helps catch errors or explicitly await initialization
5362
- */
5363
5237
  async init() {
5364
- return this.initPromise;
5365
- }
5366
- async openInternalDB() {
5367
- /**
5368
- * Execute opening of the db in a lock in order not to interfere with other operations.
5369
- */
5370
- return this._acquireLock(async () => {
5371
- // Dispose any previous table change listener.
5372
- this._disposeTableChangeListener?.();
5373
- this._disposeTableChangeListener = null;
5374
- this._db?.close().catch((ex) => this.logger.warn(`Error closing database before opening new instance`, ex));
5375
- const isReOpen = !!this._db;
5376
- this._db = null;
5377
- this._db = await this.options.openConnection();
5378
- await this._db.init();
5379
- this._config = await this._db.getConfig();
5380
- await this.registerOnChangeListener(this._db);
5381
- if (isReOpen) {
5382
- this.iterateListeners((cb) => cb.databaseReOpened?.());
5383
- }
5384
- /**
5385
- * This is only required for the long-lived shared IndexedDB connections.
5386
- */
5387
- this.requiresHolds = this._config.vfs == _wa_sqlite_WASQLiteConnection_js__WEBPACK_IMPORTED_MODULE_3__.WASQLiteVFS.IDBBatchAtomicVFS;
5388
- });
5389
- }
5390
- _reOpen() {
5391
- this.databaseOpenPromise = this.openInternalDB().finally(() => {
5392
- this.databaseOpenPromise = null;
5393
- });
5394
- return this.databaseOpenPromise;
5395
- }
5396
- /**
5397
- * Re-opens the underlying database.
5398
- * Returns a pending operation if one is already in progress.
5399
- */
5400
- async reOpenInternalDB() {
5401
- if (this.closing || !this.options.reOpenOnConnectionClosed) {
5402
- // No-op
5403
- return;
5404
- }
5405
- else if (this.databaseOpenPromise) {
5406
- // Already busy opening
5407
- return this.databaseOpenPromise;
5408
- }
5409
- else {
5410
- return this._reOpen();
5411
- }
5412
- }
5413
- async _init() {
5414
- /**
5415
- * For OPFS, we can see this open call sometimes fail due to NoModificationAllowedError.
5416
- * We should be able to recover from this by re-opening the database.
5417
- */
5418
- const maxAttempts = 3;
5419
- for (let count = 0; count < maxAttempts; count++) {
5420
- try {
5421
- await this.openInternalDB();
5422
- break;
5423
- }
5424
- catch (ex) {
5425
- if (count == maxAttempts - 1) {
5426
- throw ex;
5427
- }
5428
- this.logger.warn(`Attempt ${count + 1} of ${maxAttempts} to open database failed, retrying in 1 second...`, ex);
5429
- await new Promise((resolve) => setTimeout(resolve, 1000));
5430
- }
5431
- }
5432
- this.iterateListeners((cb) => cb.initialized?.());
5433
- }
5434
- getConfiguration() {
5435
- if (!this._config) {
5436
- throw new Error(`Cannot get config before initialization is completed`);
5437
- }
5438
- return {
5439
- ...this._config,
5440
- // This can be overridden by the adapter later
5441
- requiresPersistentTriggers: false
5442
- };
5443
- }
5444
- async waitForInitialized() {
5445
- // Awaiting this will expose errors on function calls like .execute etc
5446
- await this.initPromise;
5447
- }
5448
- async shareConnection() {
5449
- if (false == this._db instanceof _WorkerWrappedAsyncDatabaseConnection_js__WEBPACK_IMPORTED_MODULE_2__.WorkerWrappedAsyncDatabaseConnection) {
5450
- throw new Error(`Only worker connections can be shared`);
5451
- }
5452
- return this._db.shareConnection();
5453
- }
5454
- /**
5455
- * Registers a table change notification callback with the base database.
5456
- * This can be extended by custom implementations in order to handle proxy events.
5457
- */
5458
- async registerOnChangeListener(db) {
5459
- this._disposeTableChangeListener = await db.registerOnTableChange((event) => {
5460
- this.iterateListeners((cb) => cb.tablesUpdated?.(event));
5461
- });
5462
- }
5463
- /**
5464
- * This is currently a no-op on web
5465
- */
5466
- async refreshSchema() { }
5467
- async execute(query, params) {
5468
- return this.writeLock((ctx) => ctx.execute(query, params));
5469
- }
5470
- async executeRaw(query, params) {
5471
- return this.writeLock((ctx) => ctx.executeRaw(query, params));
5238
+ await this.inner;
5472
5239
  }
5473
- async executeBatch(query, params) {
5474
- return this.writeLock((ctx) => this._executeBatch(query, params));
5475
- }
5476
- /**
5477
- * Attempts to close the connection.
5478
- * Shared workers might not actually close the connection if other
5479
- * tabs are still using it.
5480
- */
5481
5240
  async close() {
5482
- this.closing = true;
5483
- /**
5484
- * Note that we obtain a reference to the callback to avoid calling the callback with `this` as the context.
5485
- * This is to avoid Comlink attempting to clone `this` when calling the method.
5486
- */
5487
- const dispose = this._disposeTableChangeListener;
5488
- if (dispose) {
5489
- dispose();
5490
- }
5491
- this.pendingAbortControllers.forEach((controller) => controller.abort('Closed'));
5492
- await this.baseDB?.close?.();
5493
- this.closed = true;
5494
- }
5495
- async getAll(sql, parameters) {
5496
- await this.waitForInitialized();
5497
- return this.dbGetHelpers.getAll(sql, parameters);
5498
- }
5499
- async getOptional(sql, parameters) {
5500
- await this.waitForInitialized();
5501
- return this.dbGetHelpers.getOptional(sql, parameters);
5502
- }
5503
- async get(sql, parameters) {
5504
- await this.waitForInitialized();
5505
- return this.dbGetHelpers.get(sql, parameters);
5241
+ const inner = await this.inner;
5242
+ return await inner.close();
5506
5243
  }
5507
5244
  async readLock(fn, options) {
5508
- // Read and write locks are the same because we only have one underlying connection.
5509
- return this.writeLock(fn, options);
5245
+ const inner = await this.inner;
5246
+ return await inner.readLock(fn, options);
5510
5247
  }
5511
5248
  async writeLock(fn, options) {
5512
- await this.waitForInitialized();
5513
- return this.acquireLock(async () => fn(this.generateDBHelpers({
5514
- execute: this._execute,
5515
- executeRaw: this._executeRaw,
5516
- executeBatch: this._executeBatch
5517
- })), {
5518
- timeoutMs: options?.timeoutMs ?? this.options.defaultLockTimeoutMs
5519
- });
5249
+ const inner = await this.inner;
5250
+ return await inner.writeLock(fn, options);
5520
5251
  }
5521
- async _acquireLock(callback, options) {
5522
- if (this.closing) {
5523
- throw new Error(`Cannot acquire lock, the database is closing`);
5524
- }
5525
- const abortController = new AbortController();
5526
- this.pendingAbortControllers.add(abortController);
5527
- const { timeoutMs } = options ?? {};
5528
- const timeoutId = timeoutMs
5529
- ? setTimeout(() => {
5530
- abortController.abort(`Timeout after ${timeoutMs}ms`);
5531
- this.pendingAbortControllers.delete(abortController);
5532
- }, timeoutMs)
5533
- : null;
5534
- return (0,_shared_navigator_js__WEBPACK_IMPORTED_MODULE_1__.getNavigatorLocks)().request(`db-lock-${this._dbIdentifier}`, { signal: abortController.signal }, async () => {
5535
- this.pendingAbortControllers.delete(abortController);
5536
- if (timeoutId) {
5537
- clearTimeout(timeoutId);
5538
- }
5539
- return await callback();
5540
- });
5252
+ async refreshSchema() {
5253
+ await (await this.inner).refreshSchema();
5541
5254
  }
5542
- async acquireLock(callback, options) {
5543
- await this.waitForInitialized();
5544
- // The database is being (re)opened in the background. Wait for it here.
5545
- if (this.databaseOpenPromise) {
5546
- await this.databaseOpenPromise;
5547
- }
5548
- else if (!this._db) {
5549
- /**
5550
- * The database is not open anymore, we might need to re-open it.
5551
- * Typically, _db, can be `null` if we tried to reOpen the database, but failed to succeed in re-opening.
5552
- * This can happen when disconnecting the client.
5553
- * Note: It is safe to re-enter this method multiple times.
5554
- */
5555
- await this.reOpenInternalDB();
5255
+ registerListener(listener) {
5256
+ if (this.resolvedClient) {
5257
+ return this.resolvedClient.registerListener(listener);
5556
5258
  }
5557
- return this._acquireLock(async () => {
5558
- let holdId = null;
5559
- try {
5560
- /**
5561
- * We can't await this since it uses the same lock as we're in now.
5562
- * If there is a pending open, this call will throw.
5563
- * If there is no pending open, but there is also no database - the open
5564
- * might have failed. We need to re-open the database.
5565
- */
5566
- if (this.databaseOpenPromise || !this._db) {
5567
- throw new _powersync_common__WEBPACK_IMPORTED_MODULE_0__.ConnectionClosedError('Connection is busy re-opening');
5568
- }
5569
- holdId = this.requiresHolds ? await this.baseDB.markHold() : null;
5570
- return await callback();
5571
- }
5572
- catch (ex) {
5573
- if (_powersync_common__WEBPACK_IMPORTED_MODULE_0__.ConnectionClosedError.MATCHES(ex)) {
5574
- // Immediately re-open the database. We need to miss as little table updates as possible.
5575
- // Note, don't await this since it uses the same lock as we're in now.
5576
- this.reOpenInternalDB();
5259
+ else {
5260
+ const pending = { listener };
5261
+ this.pendingListeners.add(pending);
5262
+ return () => {
5263
+ if (pending.closeAfterRegisteredOnResolvedPool) {
5264
+ return pending.closeAfterRegisteredOnResolvedPool();
5577
5265
  }
5578
- throw ex;
5579
- }
5580
- finally {
5581
- if (holdId) {
5582
- await this.baseDB.releaseHold(holdId);
5266
+ else {
5267
+ // Has not been registered yet, we can just remove the pending listener.
5268
+ this.pendingListeners.delete(pending);
5583
5269
  }
5584
- }
5585
- }, options);
5586
- }
5587
- async readTransaction(fn, options) {
5588
- return this.readLock(this.wrapTransaction(fn));
5589
- }
5590
- writeTransaction(fn, options) {
5591
- return this.writeLock(this.wrapTransaction(fn, true));
5270
+ };
5271
+ }
5592
5272
  }
5593
- generateDBHelpers(tx) {
5594
- return {
5595
- ...tx,
5596
- /**
5597
- * Execute a read-only query and return results
5598
- */
5599
- async getAll(sql, parameters) {
5600
- const res = await tx.execute(sql, parameters);
5601
- return res.rows?._array ?? [];
5602
- },
5603
- /**
5604
- * Execute a read-only query and return the first result, or null if the ResultSet is empty.
5605
- */
5606
- async getOptional(sql, parameters) {
5607
- const res = await tx.execute(sql, parameters);
5608
- return res.rows?.item(0) ?? null;
5609
- },
5610
- /**
5611
- * Execute a read-only query and return the first result, error if the ResultSet is empty.
5612
- */
5613
- async get(sql, parameters) {
5614
- const res = await tx.execute(sql, parameters);
5615
- const first = res.rows?.item(0);
5616
- if (!first) {
5617
- throw new Error('Result set is empty');
5618
- }
5619
- return first;
5620
- }
5621
- };
5273
+ }
5274
+ class AsyncDbAdapter extends (0,_powersync_common__WEBPACK_IMPORTED_MODULE_0__.DBAdapterDefaultMixin)(AsyncConnectionPool) {
5275
+ async shareConnection() {
5276
+ const inner = await this.inner;
5277
+ return inner.shareConnection();
5622
5278
  }
5623
- /**
5624
- * Wraps a lock context into a transaction context
5625
- */
5626
- wrapTransaction(cb, write = false) {
5627
- return async (tx) => {
5628
- await this._execute(write ? 'BEGIN EXCLUSIVE' : 'BEGIN');
5629
- let finalized = false;
5630
- const commit = async () => {
5631
- if (finalized) {
5632
- return { rowsAffected: 0 };
5633
- }
5634
- finalized = true;
5635
- return this._execute('COMMIT');
5636
- };
5637
- const rollback = () => {
5638
- finalized = true;
5639
- return this._execute('ROLLBACK');
5640
- };
5641
- try {
5642
- const result = await cb({
5643
- ...tx,
5644
- commit,
5645
- rollback
5646
- });
5647
- if (!finalized) {
5648
- await commit();
5649
- }
5650
- return result;
5651
- }
5652
- catch (ex) {
5653
- this.logger.debug('Caught ex in transaction', ex);
5654
- try {
5655
- await rollback();
5656
- }
5657
- catch (ex2) {
5658
- // In rare cases, a rollback may fail.
5659
- // Safe to ignore.
5660
- }
5661
- throw ex;
5662
- }
5663
- };
5279
+ getConfiguration() {
5280
+ if (this.resolvedClient) {
5281
+ return this.resolvedClient.getConfiguration();
5282
+ }
5283
+ throw new Error('AsyncDbAdapter.getConfiguration() can only be called after initializing it.');
5664
5284
  }
5665
- /**
5666
- * Wraps the worker execute function, awaiting for it to be available
5667
- */
5668
- _execute = async (sql, bindings) => {
5669
- await this.waitForInitialized();
5670
- const result = await this.baseDB.execute(sql, bindings);
5671
- return {
5672
- ...result,
5673
- rows: {
5674
- ...result.rows,
5675
- item: (idx) => result.rows._array[idx]
5676
- }
5677
- };
5678
- };
5679
- /**
5680
- * Wraps the worker executeRaw function, awaiting for it to be available
5681
- */
5682
- _executeRaw = async (sql, bindings) => {
5683
- await this.waitForInitialized();
5684
- return await this.baseDB.executeRaw(sql, bindings);
5685
- };
5686
- /**
5687
- * Wraps the worker executeBatch function, awaiting for it to be available
5688
- */
5689
- _executeBatch = async (query, params) => {
5690
- await this.waitForInitialized();
5691
- const result = await this.baseDB.executeBatch(query, params);
5692
- return {
5693
- ...result,
5694
- rows: undefined
5695
- };
5696
- };
5697
5285
  }
5698
5286
 
5699
5287
 
@@ -5710,8 +5298,6 @@ __webpack_require__.r(__webpack_exports__);
5710
5298
  /* harmony export */ SSRDBAdapter: () => (/* binding */ SSRDBAdapter)
5711
5299
  /* harmony export */ });
5712
5300
  /* harmony import */ var _powersync_common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @powersync/common */ "@powersync/common");
5713
- /* harmony import */ var async_mutex__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! async-mutex */ "async-mutex");
5714
-
5715
5301
 
5716
5302
  const MOCK_QUERY_RESPONSE = {
5717
5303
  rowsAffected: 0
@@ -5728,21 +5314,21 @@ class SSRDBAdapter extends _powersync_common__WEBPACK_IMPORTED_MODULE_0__.BaseOb
5728
5314
  constructor() {
5729
5315
  super();
5730
5316
  this.name = 'SSR DB';
5731
- this.readMutex = new async_mutex__WEBPACK_IMPORTED_MODULE_1__.Mutex();
5732
- this.writeMutex = new async_mutex__WEBPACK_IMPORTED_MODULE_1__.Mutex();
5317
+ this.readMutex = new _powersync_common__WEBPACK_IMPORTED_MODULE_0__.Mutex();
5318
+ this.writeMutex = new _powersync_common__WEBPACK_IMPORTED_MODULE_0__.Mutex();
5733
5319
  }
5734
5320
  close() { }
5735
5321
  async readLock(fn, options) {
5736
- return this.readMutex.runExclusive(() => fn(this));
5322
+ return this.readMutex.runExclusive(() => fn(this), (0,_powersync_common__WEBPACK_IMPORTED_MODULE_0__.timeoutSignal)(options?.timeoutMs));
5737
5323
  }
5738
5324
  async readTransaction(fn, options) {
5739
- return this.readLock(() => fn(this.generateMockTransactionContext()));
5325
+ return this.readLock(() => fn(this.generateMockTransactionContext()), options);
5740
5326
  }
5741
5327
  async writeLock(fn, options) {
5742
- return this.writeMutex.runExclusive(() => fn(this));
5328
+ return this.writeMutex.runExclusive(() => fn(this), (0,_powersync_common__WEBPACK_IMPORTED_MODULE_0__.timeoutSignal)(options?.timeoutMs));
5743
5329
  }
5744
5330
  async writeTransaction(fn, options) {
5745
- return this.writeLock(() => fn(this.generateMockTransactionContext()));
5331
+ return this.writeLock(() => fn(this.generateMockTransactionContext()), options);
5746
5332
  }
5747
5333
  async execute(query, params) {
5748
5334
  return this.writeMutex.runExclusive(async () => MOCK_QUERY_RESPONSE);
@@ -5796,116 +5382,256 @@ __webpack_require__.r(__webpack_exports__);
5796
5382
 
5797
5383
  /***/ },
5798
5384
 
5799
- /***/ "./lib/src/db/adapters/WorkerWrappedAsyncDatabaseConnection.js"
5800
- /*!*********************************************************************!*\
5801
- !*** ./lib/src/db/adapters/WorkerWrappedAsyncDatabaseConnection.js ***!
5802
- \*********************************************************************/
5385
+ /***/ "./lib/src/db/adapters/wa-sqlite/ConcurrentConnection.js"
5386
+ /*!***************************************************************!*\
5387
+ !*** ./lib/src/db/adapters/wa-sqlite/ConcurrentConnection.js ***!
5388
+ \***************************************************************/
5803
5389
  (__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) {
5804
5390
 
5805
5391
  __webpack_require__.r(__webpack_exports__);
5806
5392
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
5807
- /* harmony export */ WorkerWrappedAsyncDatabaseConnection: () => (/* binding */ WorkerWrappedAsyncDatabaseConnection)
5393
+ /* harmony export */ ConcurrentSqliteConnection: () => (/* binding */ ConcurrentSqliteConnection),
5394
+ /* harmony export */ ConnectionLeaseToken: () => (/* binding */ ConnectionLeaseToken)
5808
5395
  /* harmony export */ });
5809
5396
  /* harmony import */ var _powersync_common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @powersync/common */ "@powersync/common");
5810
- /* harmony import */ var comlink__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! comlink */ "comlink");
5811
-
5812
5397
 
5813
5398
  /**
5814
- * Wraps a provided instance of {@link AsyncDatabaseConnection}, providing necessary proxy
5815
- * functions for worker listeners.
5399
+ * A wrapper around a {@link RawSqliteConnection} allowing multiple tabs to access it.
5400
+ *
5401
+ * To allow potentially concurrent accesses from different clients, this requires a local mutex implementation here.
5402
+ *
5403
+ * Note that instances of this class are not safe to proxy across context boundaries with comlink! We need to be able to
5404
+ * rely on mutexes being returned reliably, so additional checks to detect say a client tab closing are required to
5405
+ * avoid deadlocks.
5816
5406
  */
5817
- class WorkerWrappedAsyncDatabaseConnection extends _powersync_common__WEBPACK_IMPORTED_MODULE_0__.BaseObserver {
5818
- options;
5819
- lockAbortController = new AbortController();
5820
- notifyRemoteClosed;
5821
- constructor(options) {
5822
- super();
5823
- this.options = options;
5824
- if (options.remoteCanCloseUnexpectedly) {
5825
- this.notifyRemoteClosed = new AbortController();
5826
- }
5827
- }
5828
- get baseConnection() {
5829
- return this.options.baseConnection;
5830
- }
5831
- init() {
5832
- return this.baseConnection.init();
5833
- }
5407
+ class ConcurrentSqliteConnection {
5408
+ inner;
5834
5409
  /**
5835
- * Marks the remote as closed.
5410
+ * An outer mutex ensuring at most one {@link ConnectionLeaseToken} can exist for this connection at a time.
5836
5411
  *
5837
- * This can sometimes happen outside of our control, e.g. when a shared worker requests a connection from a tab. When
5838
- * it happens, all methods on the {@link baseConnection} would never resolve. To avoid livelocks in this scenario, we
5839
- * throw on all outstanding promises and forbid new calls.
5412
+ * If null, we'll use navigator locks instead.
5840
5413
  */
5841
- markRemoteClosed() {
5842
- // Can non-null assert here because this function is only supposed to be called when remoteCanCloseUnexpectedly was
5843
- // set.
5844
- this.notifyRemoteClosed.abort();
5414
+ leaseMutex;
5415
+ /**
5416
+ * @param needsNavigatorLocks Whether access to the database needs an additional navigator lock guard.
5417
+ *
5418
+ * While {@link ConcurrentSqliteConnection} prevents concurrent access to a database _connection_, it's possible we
5419
+ * might have multiple connections to the same physical database (e.g. if multiple tabs use dedicated workers).
5420
+ * In those setups, we use navigator locks instead of an internal mutex to guard access..
5421
+ */
5422
+ constructor(inner, needsNavigatorLocks) {
5423
+ this.inner = inner;
5424
+ this.leaseMutex = needsNavigatorLocks ? null : new _powersync_common__WEBPACK_IMPORTED_MODULE_0__.Mutex();
5425
+ }
5426
+ get options() {
5427
+ return this.inner.options;
5845
5428
  }
5846
- markHold() {
5847
- return this.withRemote(() => this.baseConnection.markHold());
5429
+ acquireMutex(abort) {
5430
+ if (this.leaseMutex) {
5431
+ return this.leaseMutex.acquire(abort);
5432
+ }
5433
+ return new Promise((resolve, reject) => {
5434
+ const options = { signal: abort };
5435
+ navigator.locks
5436
+ .request(`db-lock-${this.options.dbFilename}`, options, (_) => {
5437
+ return new Promise((returnLock) => {
5438
+ return resolve(() => {
5439
+ returnLock();
5440
+ });
5441
+ });
5442
+ })
5443
+ .catch(reject);
5444
+ });
5848
5445
  }
5849
- releaseHold(holdId) {
5850
- return this.withRemote(() => this.baseConnection.releaseHold(holdId));
5446
+ // Unsafe, unguarded access to the SQLite connection.
5447
+ unsafeUseInner() {
5448
+ return this.inner;
5851
5449
  }
5852
- isAutoCommit() {
5853
- return this.withRemote(() => this.baseConnection.isAutoCommit());
5854
- }
5855
- withRemote(workerPromise, fireActionOnAbort = false) {
5856
- const controller = this.notifyRemoteClosed;
5857
- if (controller) {
5858
- return new Promise((resolve, reject) => {
5859
- if (controller.signal.aborted) {
5860
- reject(new _powersync_common__WEBPACK_IMPORTED_MODULE_0__.ConnectionClosedError('Called operation on closed remote'));
5861
- if (!fireActionOnAbort) {
5862
- // Don't run the operation if we're going to reject
5863
- // We might want to fire-and-forget the operation in some cases (like a close operation)
5864
- return;
5865
- }
5866
- }
5867
- function handleAbort() {
5868
- reject(new _powersync_common__WEBPACK_IMPORTED_MODULE_0__.ConnectionClosedError('Remote peer closed with request in flight'));
5869
- }
5870
- function completePromise(action) {
5871
- controller.signal.removeEventListener('abort', handleAbort);
5872
- action();
5873
- }
5874
- controller.signal.addEventListener('abort', handleAbort);
5875
- workerPromise()
5876
- .then((data) => completePromise(() => resolve(data)))
5877
- .catch((e) => completePromise(() => reject(e)));
5878
- });
5450
+ /**
5451
+ * @returns A {@link ConnectionLeaseToken}. Until that token is returned, no other client can use the database.
5452
+ */
5453
+ async acquireConnection(abort) {
5454
+ const returnMutex = await this.acquireMutex(abort);
5455
+ const token = new ConnectionLeaseToken(returnMutex, this.inner);
5456
+ try {
5457
+ // Assert that the inner connection is initialized at this point, fail early if it's not.
5458
+ this.inner.requireSqlite();
5459
+ // If a previous client was interrupted in the middle of a transaction AND this is a shared worker, it's possible
5460
+ // for the connection to still be in a transaction. To avoid inconsistent state, we roll back connection leases
5461
+ // that haven't been comitted.
5462
+ if (!this.inner.isAutoCommit()) {
5463
+ await this.inner.executeRaw('ROLLBACK');
5464
+ }
5879
5465
  }
5880
- else {
5881
- // Can't close, so just return the inner worker promise unguarded.
5882
- return workerPromise();
5466
+ catch (e) {
5467
+ returnMutex();
5468
+ throw e;
5469
+ }
5470
+ return token;
5471
+ }
5472
+ async close() {
5473
+ const returnMutex = await this.acquireMutex();
5474
+ try {
5475
+ await this.inner.close();
5476
+ }
5477
+ finally {
5478
+ returnMutex();
5883
5479
  }
5884
5480
  }
5481
+ }
5482
+ /**
5483
+ * An instance representing temporary exclusive access to a {@link ConcurrentSqliteConnection}.
5484
+ */
5485
+ class ConnectionLeaseToken {
5486
+ returnMutex;
5487
+ connection;
5488
+ /** Ensures that the client with access to this token can't run statements concurrently. */
5489
+ useMutex = new _powersync_common__WEBPACK_IMPORTED_MODULE_0__.Mutex();
5490
+ closed = false;
5491
+ constructor(returnMutex, connection) {
5492
+ this.returnMutex = returnMutex;
5493
+ this.connection = connection;
5494
+ }
5885
5495
  /**
5886
- * Get a MessagePort which can be used to share the internals of this connection.
5496
+ * Returns this lease, allowing another client to use the database connection.
5887
5497
  */
5888
- async shareConnection() {
5889
- const { identifier, remote } = this.options;
5890
- /**
5891
- * Hold a navigator lock in order to avoid features such as Chrome's frozen tabs,
5892
- * or Edge's sleeping tabs from pausing the thread for this connection.
5893
- * This promise resolves once a lock is obtained.
5498
+ async returnLease() {
5499
+ await this.useMutex.runExclusive(async () => {
5500
+ if (!this.closed) {
5501
+ this.closed = true;
5502
+ this.returnMutex();
5503
+ }
5504
+ });
5505
+ }
5506
+ /**
5507
+ * This should only be used internally, since the callback must not use the raw connection after resolving.
5508
+ */
5509
+ async use(callback) {
5510
+ return await this.useMutex.runExclusive(async () => {
5511
+ if (this.closed) {
5512
+ throw new Error('lease token has already been closed');
5513
+ }
5514
+ return await callback(this.connection);
5515
+ });
5516
+ }
5517
+ }
5518
+
5519
+
5520
+ /***/ },
5521
+
5522
+ /***/ "./lib/src/db/adapters/wa-sqlite/DatabaseClient.js"
5523
+ /*!*********************************************************!*\
5524
+ !*** ./lib/src/db/adapters/wa-sqlite/DatabaseClient.js ***!
5525
+ \*********************************************************/
5526
+ (__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) {
5527
+
5528
+ __webpack_require__.r(__webpack_exports__);
5529
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
5530
+ /* harmony export */ DatabaseClient: () => (/* binding */ DatabaseClient)
5531
+ /* harmony export */ });
5532
+ /* harmony import */ var _powersync_common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @powersync/common */ "@powersync/common");
5533
+ /* harmony import */ var comlink__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! comlink */ "comlink");
5534
+
5535
+
5536
+ /**
5537
+ * A single-connection {@link ConnectionPool} implementation based on a worker connection.
5538
+ */
5539
+ class DatabaseClient extends _powersync_common__WEBPACK_IMPORTED_MODULE_0__.BaseObserver {
5540
+ options;
5541
+ config;
5542
+ #connection;
5543
+ #shareConnectionAbortController = new AbortController();
5544
+ #receiveTableUpdates;
5545
+ constructor(options, config) {
5546
+ super();
5547
+ this.options = options;
5548
+ this.config = config;
5549
+ this.#connection = {
5550
+ connection: options.connection,
5551
+ notifyRemoteClosed: options.remoteCanCloseUnexpectedly ? new AbortController() : undefined,
5552
+ traceQueries: config.debugMode === true
5553
+ };
5554
+ const { port1, port2 } = new MessageChannel();
5555
+ options.connection.setUpdateListener(comlink__WEBPACK_IMPORTED_MODULE_1__.transfer(port1, [port1]));
5556
+ this.#receiveTableUpdates = port2;
5557
+ port2.onmessage = (event) => {
5558
+ const tables = event.data;
5559
+ const notification = {
5560
+ tables,
5561
+ groupedUpdates: {},
5562
+ rawUpdates: []
5563
+ };
5564
+ this.iterateListeners((l) => {
5565
+ l.tablesUpdated && l.tablesUpdated(notification);
5566
+ });
5567
+ };
5568
+ }
5569
+ get name() {
5570
+ return this.config.dbFilename;
5571
+ }
5572
+ /**
5573
+ * Marks the remote as closed.
5574
+ *
5575
+ * This can sometimes happen outside of our control, e.g. when a shared worker requests a connection from a tab. When
5576
+ * it happens, all outstanding requests on this pool would never resolve. To avoid livelocks in this scenario, we
5577
+ * throw on all outstanding promises and forbid new calls.
5578
+ */
5579
+ markRemoteClosed() {
5580
+ // Can non-null assert here because this function is only supposed to be called when remoteCanCloseUnexpectedly was
5581
+ // set.
5582
+ this.#connection.notifyRemoteClosed.abort();
5583
+ }
5584
+ async close() {
5585
+ // This connection is no longer shared, so we can close locks held for shareConnection calls.
5586
+ this.#shareConnectionAbortController.abort();
5587
+ this.#receiveTableUpdates.close();
5588
+ await useConnectionState(this.#connection, (c) => c.close(), true);
5589
+ this.options.onClose?.();
5590
+ this.options.source?.[comlink__WEBPACK_IMPORTED_MODULE_1__.releaseProxy]();
5591
+ }
5592
+ readLock(fn, options) {
5593
+ return this.#lock(false, fn, options);
5594
+ }
5595
+ writeLock(fn, options) {
5596
+ return this.#lock(true, fn, options);
5597
+ }
5598
+ async #lock(write, fn, options) {
5599
+ const token = await useConnectionState(this.#connection, (c) => c.requestAccess(write, options?.timeoutMs));
5600
+ try {
5601
+ return await fn(new ClientLockContext(this.#connection, token));
5602
+ }
5603
+ finally {
5604
+ await useConnectionState(this.#connection, (c) => c.completeAccess(token));
5605
+ }
5606
+ }
5607
+ async refreshSchema() {
5608
+ // Currently a no-op on the web.
5609
+ }
5610
+ async shareConnection() {
5611
+ /**
5612
+ * Hold a navigator lock in order to avoid features such as Chrome's frozen tabs,
5613
+ * or Edge's sleeping tabs from pausing the thread for this connection.
5614
+ * This promise resolves once a lock is obtained.
5894
5615
  * This lock will be held as long as this connection is open.
5895
5616
  * The `shareConnection` method should not be called on multiple tabs concurrently.
5896
5617
  */
5618
+ const abort = this.#shareConnectionAbortController;
5619
+ const source = this.options.source;
5620
+ if (source == null) {
5621
+ throw new Error(`shareConnection() is only available for connections based by workers.`);
5622
+ }
5897
5623
  await new Promise((resolve, reject) => navigator.locks
5898
- .request(`shared-connection-${this.options.identifier}-${Date.now()}-${Math.round(Math.random() * 10000)}`, {
5899
- signal: this.lockAbortController.signal
5624
+ .request(`shared-connection-${this.name}-${Date.now()}-${Math.round(Math.random() * 10000)}`, {
5625
+ signal: abort.signal
5900
5626
  }, async () => {
5901
5627
  resolve();
5902
5628
  // Free the lock when the connection is already closed.
5903
- if (this.lockAbortController.signal.aborted) {
5629
+ if (abort.signal.aborted) {
5904
5630
  return;
5905
5631
  }
5906
5632
  // Hold the lock while the shared connection is in use.
5907
5633
  await new Promise((releaseLock) => {
5908
- this.lockAbortController.signal.addEventListener('abort', () => {
5634
+ abort.signal.addEventListener('abort', () => {
5909
5635
  releaseLock();
5910
5636
  });
5911
5637
  });
@@ -5919,280 +5645,335 @@ class WorkerWrappedAsyncDatabaseConnection extends _powersync_common__WEBPACK_IM
5919
5645
  reject(ex);
5920
5646
  }
5921
5647
  }));
5922
- const newPort = await remote[comlink__WEBPACK_IMPORTED_MODULE_1__.createEndpoint]();
5923
- return { port: newPort, identifier };
5648
+ const newPort = await source[comlink__WEBPACK_IMPORTED_MODULE_1__.createEndpoint]();
5649
+ return { port: newPort, identifier: this.name };
5650
+ }
5651
+ getConfiguration() {
5652
+ return this.config;
5653
+ }
5654
+ }
5655
+ /**
5656
+ * A {@link SqlExecutor} implemented by sending commands to a worker.
5657
+ *
5658
+ * While an instance is active, it has exclusive access to the underlying database connection (as represented by its
5659
+ * token).
5660
+ */
5661
+ class ClientSqlExecutor {
5662
+ #connection;
5663
+ #token;
5664
+ constructor(connection, token) {
5665
+ this.#connection = connection;
5666
+ this.#token = token;
5924
5667
  }
5925
5668
  /**
5926
- * Registers a table change notification callback with the base database.
5927
- * This can be extended by custom implementations in order to handle proxy events.
5669
+ * Requests an operation from the worker, potentially tracing it if that option has been enabled.
5928
5670
  */
5929
- async registerOnTableChange(callback) {
5930
- return this.baseConnection.registerOnTableChange(comlink__WEBPACK_IMPORTED_MODULE_1__.proxy(callback));
5931
- }
5932
- async close() {
5933
- // Abort any pending lock requests.
5934
- this.lockAbortController.abort();
5935
- try {
5936
- // fire and forget the close operation
5937
- await this.withRemote(() => this.baseConnection.close(), true);
5671
+ async maybeTrace(fn, describeForTrace) {
5672
+ if (this.#connection.traceQueries) {
5673
+ const start = performance.now();
5674
+ const description = describeForTrace();
5675
+ try {
5676
+ const r = await useConnectionState(this.#connection, fn);
5677
+ performance.measure(`[SQL] ${description}`, { start });
5678
+ return r;
5679
+ }
5680
+ catch (e) {
5681
+ performance.measure(`[SQL] [ERROR: ${e.message}] ${description}`, { start });
5682
+ throw e;
5683
+ }
5938
5684
  }
5939
- finally {
5940
- this.options.remote[comlink__WEBPACK_IMPORTED_MODULE_1__.releaseProxy]();
5941
- this.options.onClose?.();
5942
- this.iterateListeners((l) => l.closing?.());
5685
+ else {
5686
+ return useConnectionState(this.#connection, fn);
5687
+ }
5688
+ }
5689
+ async execute(query, params) {
5690
+ const rs = await this.#executeOnWorker(query, params);
5691
+ let rows;
5692
+ if (rs.resultSet) {
5693
+ const resultSet = rs.resultSet;
5694
+ function rowToJavaScriptObject(row) {
5695
+ const obj = {};
5696
+ resultSet.columns.forEach((key, idx) => (obj[key] = row[idx]));
5697
+ return obj;
5698
+ }
5699
+ const mapped = resultSet.rows.map(rowToJavaScriptObject);
5700
+ rows = {
5701
+ _array: mapped,
5702
+ length: mapped.length,
5703
+ item: (idx) => mapped[idx]
5704
+ };
5943
5705
  }
5706
+ return {
5707
+ rowsAffected: rs.changes,
5708
+ insertId: rs.lastInsertRowId,
5709
+ rows
5710
+ };
5944
5711
  }
5945
- execute(sql, params) {
5946
- return this.withRemote(() => this.baseConnection.execute(sql, params));
5712
+ async executeRaw(query, params) {
5713
+ const rs = await this.#executeOnWorker(query, params);
5714
+ return rs.resultSet?.rows ?? [];
5947
5715
  }
5948
- executeRaw(sql, params) {
5949
- return this.withRemote(() => this.baseConnection.executeRaw(sql, params));
5716
+ async #executeOnWorker(query, params) {
5717
+ return this.maybeTrace((c) => c.execute(this.#token, query, params), () => query);
5950
5718
  }
5951
- executeBatch(sql, params) {
5952
- return this.withRemote(() => this.baseConnection.executeBatch(sql, params));
5719
+ async executeBatch(query, params = []) {
5720
+ const results = await this.maybeTrace((c) => c.executeBatch(this.#token, query, params), () => `${query} (batch of ${params.length})`);
5721
+ const result = { insertId: undefined, rowsAffected: 0 };
5722
+ for (const source of results) {
5723
+ result.insertId = source.lastInsertRowId;
5724
+ result.rowsAffected += source.changes;
5725
+ }
5726
+ return result;
5727
+ }
5728
+ }
5729
+ class ClientLockContext extends (0,_powersync_common__WEBPACK_IMPORTED_MODULE_0__.DBGetUtilsDefaultMixin)(ClientSqlExecutor) {
5730
+ }
5731
+ async function useConnectionState(state, workerPromise, fireActionOnAbort = false) {
5732
+ const controller = state.notifyRemoteClosed;
5733
+ if (controller) {
5734
+ return new Promise((resolve, reject) => {
5735
+ if (controller.signal.aborted) {
5736
+ reject(new _powersync_common__WEBPACK_IMPORTED_MODULE_0__.ConnectionClosedError('Called operation on closed remote'));
5737
+ if (!fireActionOnAbort) {
5738
+ // Don't run the operation if we're going to reject
5739
+ // We might want to fire-and-forget the operation in some cases (like a close operation)
5740
+ return;
5741
+ }
5742
+ }
5743
+ function handleAbort() {
5744
+ reject(new _powersync_common__WEBPACK_IMPORTED_MODULE_0__.ConnectionClosedError('Remote peer closed with request in flight'));
5745
+ }
5746
+ function completePromise(action) {
5747
+ controller.signal.removeEventListener('abort', handleAbort);
5748
+ action();
5749
+ }
5750
+ controller.signal.addEventListener('abort', handleAbort);
5751
+ workerPromise(state.connection)
5752
+ .then((data) => completePromise(() => resolve(data)))
5753
+ .catch((e) => completePromise(() => reject(e)));
5754
+ });
5953
5755
  }
5954
- getConfig() {
5955
- return this.withRemote(() => this.baseConnection.getConfig());
5756
+ else {
5757
+ // Can't close, so just return the inner worker promise unguarded.
5758
+ return workerPromise(state.connection);
5956
5759
  }
5957
5760
  }
5958
5761
 
5959
5762
 
5960
5763
  /***/ },
5961
5764
 
5962
- /***/ "./lib/src/db/adapters/wa-sqlite/InternalWASQLiteDBAdapter.js"
5963
- /*!********************************************************************!*\
5964
- !*** ./lib/src/db/adapters/wa-sqlite/InternalWASQLiteDBAdapter.js ***!
5965
- \********************************************************************/
5765
+ /***/ "./lib/src/db/adapters/wa-sqlite/DatabaseServer.js"
5766
+ /*!*********************************************************!*\
5767
+ !*** ./lib/src/db/adapters/wa-sqlite/DatabaseServer.js ***!
5768
+ \*********************************************************/
5966
5769
  (__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) {
5967
5770
 
5968
5771
  __webpack_require__.r(__webpack_exports__);
5969
5772
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
5970
- /* harmony export */ InternalWASQLiteDBAdapter: () => (/* binding */ InternalWASQLiteDBAdapter)
5773
+ /* harmony export */ DatabaseServer: () => (/* binding */ DatabaseServer)
5971
5774
  /* harmony export */ });
5972
- /* harmony import */ var _LockedAsyncDatabaseAdapter_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../LockedAsyncDatabaseAdapter.js */ "./lib/src/db/adapters/LockedAsyncDatabaseAdapter.js");
5973
- /* harmony import */ var _WASQLiteConnection_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./WASQLiteConnection.js */ "./lib/src/db/adapters/wa-sqlite/WASQLiteConnection.js");
5974
-
5975
-
5976
5775
  /**
5977
- * @internal
5978
- * An intermediary implementation of WASQLiteDBAdapter, which takes the same
5979
- * constructor arguments as {@link LockedAsyncDatabaseAdapter}, but provides some
5980
- * basic WA-SQLite specific functionality.
5981
- * This base class is used to avoid requiring overloading the constructor of {@link WASQLiteDBAdapter}
5776
+ * Access to a WA-sqlite connection that can be shared with multiple clients sending queries over an RPC protocol built
5777
+ * with the Comlink package.
5982
5778
  */
5983
- class InternalWASQLiteDBAdapter extends _LockedAsyncDatabaseAdapter_js__WEBPACK_IMPORTED_MODULE_0__.LockedAsyncDatabaseAdapter {
5984
- getConfiguration() {
5985
- // This is valid since we only handle WASQLite connections
5986
- const baseConfig = super.getConfiguration();
5779
+ class DatabaseServer {
5780
+ #options;
5781
+ #nextClientId = 0;
5782
+ #activeClients = new Set();
5783
+ // TODO: Don't use a broadcast channel for connections managed by a shared worker.
5784
+ #updateBroadcastChannel;
5785
+ #clientTableListeners = new Set();
5786
+ constructor(options) {
5787
+ this.#options = options;
5788
+ const inner = options.inner;
5789
+ this.#updateBroadcastChannel = new BroadcastChannel(`${inner.options.dbFilename}-table-updates`);
5790
+ this.#updateBroadcastChannel.onmessage = ({ data }) => {
5791
+ this.#pushTableUpdateToClients(data);
5792
+ };
5793
+ }
5794
+ #pushTableUpdateToClients(changedTables) {
5795
+ for (const listener of this.#clientTableListeners) {
5796
+ listener.postMessage(changedTables);
5797
+ }
5798
+ }
5799
+ get #inner() {
5800
+ return this.#options.inner;
5801
+ }
5802
+ get #logger() {
5803
+ return this.#options.logger;
5804
+ }
5805
+ /**
5806
+ * Called by clients when they wish to connect to this database.
5807
+ *
5808
+ * @param lockName A lock that is currently held by the client. When the lock is returned, we know the client is gone
5809
+ * and that we need to clean up resources.
5810
+ */
5811
+ async connect(lockName) {
5812
+ let isOpen = true;
5813
+ const clientId = this.#nextClientId++;
5814
+ this.#activeClients.add(clientId);
5815
+ let connectionLeases = new Map();
5816
+ let currentTableListener;
5817
+ function requireOpen() {
5818
+ if (!isOpen) {
5819
+ throw new Error('Client has already been closed');
5820
+ }
5821
+ }
5822
+ function requireOpenAndLease(lease) {
5823
+ requireOpen();
5824
+ const token = connectionLeases.get(lease);
5825
+ if (!token) {
5826
+ throw new Error('Attempted to use a connection lease that has already been returned.');
5827
+ }
5828
+ return token;
5829
+ }
5830
+ const close = async () => {
5831
+ if (isOpen) {
5832
+ isOpen = false;
5833
+ if (currentTableListener) {
5834
+ this.#clientTableListeners.delete(currentTableListener);
5835
+ }
5836
+ // If the client holds a connection lease it hasn't returned, return that now.
5837
+ for (const { lease } of connectionLeases.values()) {
5838
+ this.#logger.debug(`Closing connection lease that hasn't been returned.`);
5839
+ await lease.returnLease();
5840
+ }
5841
+ this.#activeClients.delete(clientId);
5842
+ if (this.#activeClients.size == 0) {
5843
+ await this.forceClose();
5844
+ }
5845
+ else {
5846
+ this.#logger.debug('Keeping underlying connection active since its used by other clients.');
5847
+ }
5848
+ }
5849
+ };
5850
+ if (lockName) {
5851
+ navigator.locks.request(lockName, {}, () => {
5852
+ close();
5853
+ });
5854
+ }
5987
5855
  return {
5988
- ...super.getConfiguration(),
5989
- requiresPersistentTriggers: baseConfig.vfs == _WASQLiteConnection_js__WEBPACK_IMPORTED_MODULE_1__.WASQLiteVFS.OPFSCoopSyncVFS || baseConfig.vfs == _WASQLiteConnection_js__WEBPACK_IMPORTED_MODULE_1__.WASQLiteVFS.AccessHandlePoolVFS
5856
+ close,
5857
+ debugIsAutoCommit: async () => {
5858
+ return this.#inner.unsafeUseInner().isAutoCommit();
5859
+ },
5860
+ requestAccess: async (write, timeoutMs) => {
5861
+ requireOpen();
5862
+ // TODO: Support timeouts, they don't seem to be supported by the async-mutex package.
5863
+ const lease = await this.#inner.acquireConnection();
5864
+ if (!isOpen) {
5865
+ // Race between requestAccess and close(), the connection was closed while we tried to acquire a lease.
5866
+ await lease.returnLease();
5867
+ return requireOpen();
5868
+ }
5869
+ const token = crypto.randomUUID();
5870
+ connectionLeases.set(token, { lease, write });
5871
+ return token;
5872
+ },
5873
+ completeAccess: async (token) => {
5874
+ const lease = requireOpenAndLease(token);
5875
+ connectionLeases.delete(token);
5876
+ try {
5877
+ if (lease.write) {
5878
+ // Collect update hooks invoked while the client had the write connection.
5879
+ const { resultSet } = await lease.lease.use((conn) => conn.execute(`SELECT powersync_update_hooks('get')`));
5880
+ if (resultSet) {
5881
+ const updatedTables = JSON.parse(resultSet.rows[0][0]);
5882
+ if (updatedTables.length) {
5883
+ this.#updateBroadcastChannel.postMessage(updatedTables);
5884
+ this.#pushTableUpdateToClients(updatedTables);
5885
+ }
5886
+ }
5887
+ }
5888
+ }
5889
+ finally {
5890
+ await lease.lease.returnLease();
5891
+ }
5892
+ },
5893
+ execute: async (token, sql, params) => {
5894
+ const { lease } = requireOpenAndLease(token);
5895
+ return await lease.use((db) => db.execute(sql, params));
5896
+ },
5897
+ executeBatch: async (token, sql, params) => {
5898
+ const { lease } = requireOpenAndLease(token);
5899
+ return await lease.use((db) => db.executeBatch(sql, params));
5900
+ },
5901
+ setUpdateListener: async (listener) => {
5902
+ requireOpen();
5903
+ if (currentTableListener) {
5904
+ this.#clientTableListeners.delete(currentTableListener);
5905
+ }
5906
+ currentTableListener = listener;
5907
+ if (listener) {
5908
+ this.#clientTableListeners.add(listener);
5909
+ }
5910
+ }
5990
5911
  };
5991
5912
  }
5913
+ async forceClose() {
5914
+ this.#logger.debug(`Closing connection to ${this.#inner.options}.`);
5915
+ const connection = this.#inner;
5916
+ this.#options.onClose();
5917
+ this.#updateBroadcastChannel.close();
5918
+ await connection.close();
5919
+ }
5992
5920
  }
5993
5921
 
5994
5922
 
5995
5923
  /***/ },
5996
5924
 
5997
- /***/ "./lib/src/db/adapters/wa-sqlite/WASQLiteConnection.js"
5998
- /*!*************************************************************!*\
5999
- !*** ./lib/src/db/adapters/wa-sqlite/WASQLiteConnection.js ***!
6000
- \*************************************************************/
5925
+ /***/ "./lib/src/db/adapters/wa-sqlite/RawSqliteConnection.js"
5926
+ /*!**************************************************************!*\
5927
+ !*** ./lib/src/db/adapters/wa-sqlite/RawSqliteConnection.js ***!
5928
+ \**************************************************************/
6001
5929
  (__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) {
6002
5930
 
6003
5931
  __webpack_require__.r(__webpack_exports__);
6004
5932
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
6005
- /* harmony export */ AsyncWASQLiteModuleFactory: () => (/* binding */ AsyncWASQLiteModuleFactory),
6006
- /* harmony export */ DEFAULT_MODULE_FACTORIES: () => (/* binding */ DEFAULT_MODULE_FACTORIES),
6007
- /* harmony export */ MultiCipherAsyncWASQLiteModuleFactory: () => (/* binding */ MultiCipherAsyncWASQLiteModuleFactory),
6008
- /* harmony export */ MultiCipherSyncWASQLiteModuleFactory: () => (/* binding */ MultiCipherSyncWASQLiteModuleFactory),
6009
- /* harmony export */ SyncWASQLiteModuleFactory: () => (/* binding */ SyncWASQLiteModuleFactory),
6010
- /* harmony export */ WASQLiteVFS: () => (/* binding */ WASQLiteVFS),
6011
- /* harmony export */ WASqliteConnection: () => (/* binding */ WASqliteConnection)
5933
+ /* harmony export */ RawSqliteConnection: () => (/* binding */ RawSqliteConnection)
6012
5934
  /* harmony export */ });
6013
5935
  /* harmony import */ var _journeyapps_wa_sqlite__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @journeyapps/wa-sqlite */ "@journeyapps/wa-sqlite");
6014
- /* harmony import */ var _powersync_common__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @powersync/common */ "@powersync/common");
6015
- /* harmony import */ var async_mutex__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! async-mutex */ "async-mutex");
6016
-
5936
+ /* harmony import */ var _vfs_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./vfs.js */ "./lib/src/db/adapters/wa-sqlite/vfs.js");
6017
5937
 
6018
5938
 
6019
5939
  /**
6020
- * List of currently tested virtual filesystems
6021
- */
6022
- var WASQLiteVFS;
6023
- (function (WASQLiteVFS) {
6024
- WASQLiteVFS["IDBBatchAtomicVFS"] = "IDBBatchAtomicVFS";
6025
- WASQLiteVFS["OPFSCoopSyncVFS"] = "OPFSCoopSyncVFS";
6026
- WASQLiteVFS["AccessHandlePoolVFS"] = "AccessHandlePoolVFS";
6027
- })(WASQLiteVFS || (WASQLiteVFS = {}));
6028
- /**
6029
- * @internal
6030
- */
6031
- const AsyncWASQLiteModuleFactory = async () => {
6032
- const { default: factory } = await Promise.resolve(/*! import() */).then(__webpack_require__.bind(__webpack_require__, /*! @journeyapps/wa-sqlite/dist/wa-sqlite-async.mjs */ "../../node_modules/.pnpm/@journeyapps+wa-sqlite@1.5.0/node_modules/@journeyapps/wa-sqlite/dist/wa-sqlite-async.mjs"));
6033
- return factory();
6034
- };
6035
- /**
6036
- * @internal
6037
- */
6038
- const MultiCipherAsyncWASQLiteModuleFactory = async () => {
6039
- const { default: factory } = await Promise.resolve(/*! import() */).then(__webpack_require__.bind(__webpack_require__, /*! @journeyapps/wa-sqlite/dist/mc-wa-sqlite-async.mjs */ "../../node_modules/.pnpm/@journeyapps+wa-sqlite@1.5.0/node_modules/@journeyapps/wa-sqlite/dist/mc-wa-sqlite-async.mjs"));
6040
- return factory();
6041
- };
6042
- /**
6043
- * @internal
6044
- */
6045
- const SyncWASQLiteModuleFactory = async () => {
6046
- const { default: factory } = await Promise.resolve(/*! import() */).then(__webpack_require__.bind(__webpack_require__, /*! @journeyapps/wa-sqlite/dist/wa-sqlite.mjs */ "../../node_modules/.pnpm/@journeyapps+wa-sqlite@1.5.0/node_modules/@journeyapps/wa-sqlite/dist/wa-sqlite.mjs"));
6047
- return factory();
6048
- };
6049
- /**
6050
- * @internal
6051
- */
6052
- const MultiCipherSyncWASQLiteModuleFactory = async () => {
6053
- const { default: factory } = await Promise.resolve(/*! import() */).then(__webpack_require__.bind(__webpack_require__, /*! @journeyapps/wa-sqlite/dist/mc-wa-sqlite.mjs */ "../../node_modules/.pnpm/@journeyapps+wa-sqlite@1.5.0/node_modules/@journeyapps/wa-sqlite/dist/mc-wa-sqlite.mjs"));
6054
- return factory();
6055
- };
6056
- /**
6057
- * @internal
6058
- */
6059
- const DEFAULT_MODULE_FACTORIES = {
6060
- [WASQLiteVFS.IDBBatchAtomicVFS]: async (options) => {
6061
- let module;
6062
- if (options.encryptionKey) {
6063
- module = await MultiCipherAsyncWASQLiteModuleFactory();
6064
- }
6065
- else {
6066
- module = await AsyncWASQLiteModuleFactory();
6067
- }
6068
- const { IDBBatchAtomicVFS } = await Promise.resolve(/*! import() */).then(__webpack_require__.t.bind(__webpack_require__, /*! @journeyapps/wa-sqlite/src/examples/IDBBatchAtomicVFS.js */ "@journeyapps/wa-sqlite/src/examples/IDBBatchAtomicVFS.js", 19));
6069
- return {
6070
- module,
6071
- // @ts-expect-error The types for this static method are missing upstream
6072
- vfs: await IDBBatchAtomicVFS.create(options.dbFileName, module, { lockPolicy: 'exclusive' })
6073
- };
6074
- },
6075
- [WASQLiteVFS.AccessHandlePoolVFS]: async (options) => {
6076
- let module;
6077
- if (options.encryptionKey) {
6078
- module = await MultiCipherSyncWASQLiteModuleFactory();
6079
- }
6080
- else {
6081
- module = await SyncWASQLiteModuleFactory();
6082
- }
6083
- // @ts-expect-error The types for this static method are missing upstream
6084
- const { AccessHandlePoolVFS } = await Promise.resolve(/*! import() */).then(__webpack_require__.t.bind(__webpack_require__, /*! @journeyapps/wa-sqlite/src/examples/AccessHandlePoolVFS.js */ "@journeyapps/wa-sqlite/src/examples/AccessHandlePoolVFS.js", 19));
6085
- return {
6086
- module,
6087
- vfs: await AccessHandlePoolVFS.create(options.dbFileName, module)
6088
- };
6089
- },
6090
- [WASQLiteVFS.OPFSCoopSyncVFS]: async (options) => {
6091
- let module;
6092
- if (options.encryptionKey) {
6093
- module = await MultiCipherSyncWASQLiteModuleFactory();
6094
- }
6095
- else {
6096
- module = await SyncWASQLiteModuleFactory();
6097
- }
6098
- // @ts-expect-error The types for this static method are missing upstream
6099
- const { OPFSCoopSyncVFS } = await Promise.resolve(/*! import() */).then(__webpack_require__.t.bind(__webpack_require__, /*! @journeyapps/wa-sqlite/src/examples/OPFSCoopSyncVFS.js */ "@journeyapps/wa-sqlite/src/examples/OPFSCoopSyncVFS.js", 19));
6100
- const vfs = await OPFSCoopSyncVFS.create(options.dbFileName, module);
6101
- return {
6102
- module,
6103
- vfs
6104
- };
6105
- }
6106
- };
6107
- /**
6108
- * @internal
6109
- * WA-SQLite connection which directly interfaces with WA-SQLite.
6110
- * This is usually instantiated inside a worker.
5940
+ * A small wrapper around WA-sqlite to help with opening databases and running statements by preparing them internally.
5941
+ *
5942
+ * This is an internal class, and it must never be used directly. Wrappers are required to ensure raw connections aren't
5943
+ * used concurrently across tabs.
6111
5944
  */
6112
- class WASqliteConnection extends _powersync_common__WEBPACK_IMPORTED_MODULE_1__.BaseObserver {
5945
+ class RawSqliteConnection {
6113
5946
  options;
6114
5947
  _sqliteAPI = null;
6115
- _dbP = null;
6116
- _moduleFactory;
6117
- updatedTables;
6118
- updateTimer;
6119
- statementMutex;
6120
- broadcastChannel;
6121
5948
  /**
6122
- * Unique id for this specific connection. This is used to prevent broadcast table change
6123
- * notification loops.
5949
+ * The `sqlite3*` connection pointer.
6124
5950
  */
6125
- connectionId;
6126
- _holdCounter;
6127
- _holdId;
5951
+ db = 0;
5952
+ _moduleFactory;
6128
5953
  constructor(options) {
6129
- super();
6130
5954
  this.options = options;
6131
- this.updatedTables = new Set();
6132
- this.updateTimer = null;
6133
- this.broadcastChannel = null;
6134
- this.connectionId = new Date().valueOf() + Math.random();
6135
- this.statementMutex = new async_mutex__WEBPACK_IMPORTED_MODULE_2__.Mutex();
6136
- this._moduleFactory = DEFAULT_MODULE_FACTORIES[this.options.vfs];
6137
- this._holdCounter = 0;
6138
- this._holdId = null;
5955
+ this._moduleFactory = _vfs_js__WEBPACK_IMPORTED_MODULE_1__.DEFAULT_MODULE_FACTORIES[this.options.vfs];
6139
5956
  }
6140
- /**
6141
- * Gets the id for the current hold.
6142
- * This can be used to check for invalid states.
6143
- */
6144
- get currentHoldId() {
6145
- return this._holdId;
5957
+ get isOpen() {
5958
+ return this.db != 0;
6146
5959
  }
6147
- get sqliteAPI() {
6148
- if (!this._sqliteAPI) {
6149
- throw new Error(`Initialization has not completed`);
6150
- }
6151
- return this._sqliteAPI;
6152
- }
6153
- get dbP() {
6154
- if (!this._dbP) {
6155
- throw new Error(`Initialization has not completed`);
6156
- }
6157
- return this._dbP;
6158
- }
6159
- /**
6160
- * Checks if the database connection is in autocommit mode.
6161
- * @returns true if in autocommit mode, false if in a transaction
6162
- */
6163
- async isAutoCommit() {
6164
- return this.sqliteAPI.get_autocommit(this.dbP) != 0;
6165
- }
6166
- async markHold() {
6167
- const previousHoldId = this._holdId;
6168
- this._holdId = `${++this._holdCounter}`;
6169
- if (previousHoldId) {
6170
- await this.iterateAsyncListeners(async (cb) => cb.holdOverwritten?.(previousHoldId));
6171
- }
6172
- return this._holdId;
6173
- }
6174
- async releaseHold(holdId) {
6175
- if (holdId != this._holdId) {
6176
- throw new Error(`Invalid hold state, expected ${this._holdId} but got ${holdId}`);
6177
- }
6178
- this._holdId = null;
6179
- }
6180
- async openDB() {
6181
- this._dbP = await this.sqliteAPI.open_v2(this.options.dbFilename);
6182
- return this._dbP;
6183
- }
6184
- async executeEncryptionPragma() {
5960
+ async init() {
5961
+ const api = (this._sqliteAPI = await this.openSQLiteAPI());
5962
+ this.db = await api.open_v2(this.options.dbFilename);
5963
+ await this.executeRaw(`PRAGMA temp_store = ${this.options.temporaryStorage};`);
6185
5964
  if (this.options.encryptionKey) {
6186
- await this.executeSingleStatement(`PRAGMA key = "${this.options.encryptionKey}"`);
5965
+ const escapedKey = this.options.encryptionKey.replace("'", "''");
5966
+ await this.executeRaw(`PRAGMA key = '${escapedKey}'`);
6187
5967
  }
6188
- return;
5968
+ await this.executeRaw(`PRAGMA cache_size = -${this.options.cacheSizeKb};`);
5969
+ await this.executeRaw(`SELECT powersync_update_hooks('install');`);
6189
5970
  }
6190
5971
  async openSQLiteAPI() {
6191
5972
  const { module, vfs } = await this._moduleFactory({
6192
5973
  dbFileName: this.options.dbFilename,
6193
5974
  encryptionKey: this.options.encryptionKey
6194
5975
  });
6195
- const sqlite3 = _journeyapps_wa_sqlite__WEBPACK_IMPORTED_MODULE_0__.Factory(module);
5976
+ const sqlite3 = (0,_journeyapps_wa_sqlite__WEBPACK_IMPORTED_MODULE_0__.Factory)(module);
6196
5977
  sqlite3.vfs_register(vfs, true);
6197
5978
  /**
6198
5979
  * Register the PowerSync core SQLite extension
@@ -6209,278 +5990,95 @@ class WASqliteConnection extends _powersync_common__WEBPACK_IMPORTED_MODULE_1__.
6209
5990
  }
6210
5991
  return sqlite3;
6211
5992
  }
6212
- registerBroadcastListeners() {
6213
- this.broadcastChannel = new BroadcastChannel(`${this.options.dbFilename}-table-updates`);
6214
- this.broadcastChannel.addEventListener('message', (event) => {
6215
- const data = event.data;
6216
- if (this.connectionId == data.connectionId) {
6217
- // Ignore messages from the same connection
6218
- return;
6219
- }
6220
- // Ensuring that we don't rebroadcast the same message
6221
- this.queueTableUpdate(data.changedTables, false);
6222
- });
6223
- }
6224
- queueTableUpdate(tableNames, shouldBroadcast = true) {
6225
- tableNames.forEach((tableName) => this.updatedTables.add(tableName));
6226
- if (this.updateTimer == null) {
6227
- this.updateTimer = setTimeout(() => this.fireUpdates(shouldBroadcast), 0);
6228
- }
6229
- }
6230
- async init() {
6231
- this._sqliteAPI = await this.openSQLiteAPI();
6232
- await this.openDB();
6233
- this.registerBroadcastListeners();
6234
- await this.executeSingleStatement(`PRAGMA temp_store = ${this.options.temporaryStorage};`);
6235
- await this.executeEncryptionPragma();
6236
- await this.executeSingleStatement(`PRAGMA cache_size = -${this.options.cacheSizeKb};`);
6237
- this.sqliteAPI.update_hook(this.dbP, (updateType, dbName, tableName) => {
6238
- if (!tableName) {
6239
- return;
6240
- }
6241
- const changedTables = new Set([tableName]);
6242
- this.queueTableUpdate(changedTables);
6243
- });
6244
- }
6245
- async getConfig() {
6246
- return this.options;
6247
- }
6248
- fireUpdates(shouldBroadcast = true) {
6249
- this.updateTimer = null;
6250
- const event = { tables: [...this.updatedTables], groupedUpdates: {}, rawUpdates: [] };
6251
- // Share to other connections
6252
- if (shouldBroadcast) {
6253
- this.broadcastChannel.postMessage({
6254
- changedTables: this.updatedTables,
6255
- connectionId: this.connectionId
6256
- });
5993
+ requireSqlite() {
5994
+ if (!this._sqliteAPI) {
5995
+ throw new Error(`Initialization has not completed`);
6257
5996
  }
6258
- this.updatedTables.clear();
6259
- this.iterateListeners((cb) => cb.tablesUpdated?.(event));
5997
+ return this._sqliteAPI;
6260
5998
  }
6261
5999
  /**
6262
- * This executes SQL statements in a batch.
6000
+ * Checks if the database connection is in autocommit mode.
6001
+ * @returns true if in autocommit mode, false if in a transaction
6263
6002
  */
6264
- async executeBatch(sql, bindings) {
6265
- return this.acquireExecuteLock(async () => {
6266
- let affectedRows = 0;
6267
- try {
6268
- await this.executeSingleStatement('BEGIN TRANSACTION');
6269
- const wrappedBindings = bindings ? bindings : [];
6270
- for await (const stmt of this.sqliteAPI.statements(this.dbP, sql)) {
6271
- if (stmt === null) {
6272
- return {
6273
- rowsAffected: 0,
6274
- rows: { _array: [], length: 0 }
6275
- };
6276
- }
6277
- //Prepare statement once
6278
- for (const binding of wrappedBindings) {
6279
- // TODO not sure why this is needed currently, but booleans break
6280
- for (let i = 0; i < binding.length; i++) {
6281
- const b = binding[i];
6282
- if (typeof b == 'boolean') {
6283
- binding[i] = b ? 1 : 0;
6284
- }
6285
- }
6286
- if (bindings) {
6287
- this.sqliteAPI.bind_collection(stmt, binding);
6288
- }
6289
- const result = await this.sqliteAPI.step(stmt);
6290
- if (result === _journeyapps_wa_sqlite__WEBPACK_IMPORTED_MODULE_0__.SQLITE_DONE) {
6291
- //The value returned by sqlite3_changes() immediately after an INSERT, UPDATE or DELETE statement run on a view is always zero.
6292
- affectedRows += this.sqliteAPI.changes(this.dbP);
6293
- }
6294
- this.sqliteAPI.reset(stmt);
6295
- }
6296
- }
6297
- await this.executeSingleStatement('COMMIT');
6298
- }
6299
- catch (err) {
6300
- await this.executeSingleStatement('ROLLBACK');
6301
- return {
6302
- rowsAffected: 0,
6303
- rows: { _array: [], length: 0 }
6304
- };
6305
- }
6306
- const result = {
6307
- rowsAffected: affectedRows,
6308
- rows: { _array: [], length: 0 }
6309
- };
6310
- return result;
6311
- });
6003
+ isAutoCommit() {
6004
+ return this.requireSqlite().get_autocommit(this.db) != 0;
6312
6005
  }
6313
- /**
6314
- * This executes single SQL statements inside a requested lock.
6315
- */
6316
6006
  async execute(sql, bindings) {
6317
- // Running multiple statements on the same connection concurrently should not be allowed
6318
- return this.acquireExecuteLock(async () => {
6319
- return this.executeSingleStatement(sql, bindings);
6320
- });
6321
- }
6322
- async executeRaw(sql, bindings) {
6323
- return this.acquireExecuteLock(async () => {
6324
- return this.executeSingleStatementRaw(sql, bindings);
6325
- });
6326
- }
6327
- async close() {
6328
- this.broadcastChannel?.close();
6329
- await this.acquireExecuteLock(async () => {
6330
- /**
6331
- * Running the close operation inside the same execute mutex prevents errors like:
6332
- * ```
6333
- * unable to close due to unfinalized statements or unfinished backups
6334
- * ```
6335
- */
6336
- await this.sqliteAPI.close(this.dbP);
6337
- });
6338
- }
6339
- async registerOnTableChange(callback) {
6340
- return this.registerListener({
6341
- tablesUpdated: (event) => callback(event)
6342
- });
6007
+ const resultSet = await this.executeSingleStatementRaw(sql, bindings);
6008
+ return this.wrapQueryResults(this.requireSqlite(), resultSet);
6343
6009
  }
6344
- /**
6345
- * This requests a lock for executing statements.
6346
- * Should only be used internally.
6347
- */
6348
- acquireExecuteLock = (callback) => {
6349
- return this.statementMutex.runExclusive(callback);
6350
- };
6351
- /**
6352
- * This executes a single statement using SQLite3.
6353
- */
6354
- async executeSingleStatement(sql, bindings) {
6355
- const results = await this._execute(sql, bindings);
6356
- const rows = [];
6357
- for (const resultSet of results) {
6358
- for (const row of resultSet.rows) {
6359
- const outRow = {};
6360
- resultSet.columns.forEach((key, index) => {
6361
- outRow[key] = row[index];
6362
- });
6363
- rows.push(outRow);
6010
+ async executeBatch(sql, bindings) {
6011
+ const results = [];
6012
+ const api = this.requireSqlite();
6013
+ for await (const stmt of api.statements(this.db, sql)) {
6014
+ let columns;
6015
+ for (const parameterSet of bindings) {
6016
+ const rs = await this.stepThroughStatement(api, stmt, parameterSet, columns, false);
6017
+ results.push(this.wrapQueryResults(api, rs));
6364
6018
  }
6019
+ // executeBatch can only use a single statement
6020
+ break;
6365
6021
  }
6366
- const result = {
6367
- insertId: this.sqliteAPI.last_insert_id(this.dbP),
6368
- rowsAffected: this.sqliteAPI.changes(this.dbP),
6369
- rows: {
6370
- _array: rows,
6371
- length: rows.length
6372
- }
6022
+ return results;
6023
+ }
6024
+ wrapQueryResults(api, rs) {
6025
+ return {
6026
+ changes: api.changes(this.db),
6027
+ lastInsertRowId: api.last_insert_id(this.db),
6028
+ autocommit: api.get_autocommit(this.db) != 0,
6029
+ resultSet: rs
6373
6030
  };
6374
- return result;
6375
6031
  }
6376
6032
  /**
6377
- * This executes a single statement using SQLite3 and returns the results as an array of arrays.
6033
+ * This executes a single statement using SQLite3 and returns the results as a {@link RawResultSet}.
6378
6034
  */
6379
6035
  async executeSingleStatementRaw(sql, bindings) {
6380
- const results = await this._execute(sql, bindings);
6381
- return results.flatMap((resultset) => resultset.rows.map((row) => resultset.columns.map((_, index) => row[index])));
6036
+ const results = await this.executeRaw(sql, bindings);
6037
+ return results.length ? results[0] : undefined;
6382
6038
  }
6383
- async _execute(sql, bindings) {
6039
+ async executeRaw(sql, bindings) {
6384
6040
  const results = [];
6385
- for await (const stmt of this.sqliteAPI.statements(this.dbP, sql)) {
6041
+ const api = this.requireSqlite();
6042
+ for await (const stmt of api.statements(this.db, sql)) {
6386
6043
  let columns;
6387
- const wrappedBindings = bindings ? [bindings] : [[]];
6388
- for (const binding of wrappedBindings) {
6389
- // TODO not sure why this is needed currently, but booleans break
6390
- binding.forEach((b, index, arr) => {
6391
- if (typeof b == 'boolean') {
6392
- arr[index] = b ? 1 : 0;
6393
- }
6394
- });
6395
- this.sqliteAPI.reset(stmt);
6396
- if (bindings) {
6397
- this.sqliteAPI.bind_collection(stmt, binding);
6398
- }
6399
- const rows = [];
6400
- while ((await this.sqliteAPI.step(stmt)) === _journeyapps_wa_sqlite__WEBPACK_IMPORTED_MODULE_0__.SQLITE_ROW) {
6401
- const row = this.sqliteAPI.row(stmt);
6402
- rows.push(row);
6403
- }
6404
- columns = columns ?? this.sqliteAPI.column_names(stmt);
6405
- if (columns.length) {
6406
- results.push({ columns, rows });
6407
- }
6044
+ const rs = await this.stepThroughStatement(api, stmt, bindings ?? [], columns);
6045
+ columns = rs.columns;
6046
+ if (columns.length) {
6047
+ results.push(rs);
6408
6048
  }
6409
6049
  // When binding parameters, only a single statement is executed.
6410
6050
  if (bindings) {
6411
6051
  break;
6412
- }
6413
- }
6414
- return results;
6415
- }
6416
- }
6417
-
6418
-
6419
- /***/ },
6420
-
6421
- /***/ "./lib/src/db/adapters/wa-sqlite/WASQLiteDBAdapter.js"
6422
- /*!************************************************************!*\
6423
- !*** ./lib/src/db/adapters/wa-sqlite/WASQLiteDBAdapter.js ***!
6424
- \************************************************************/
6425
- (__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) {
6426
-
6427
- __webpack_require__.r(__webpack_exports__);
6428
- /* harmony export */ __webpack_require__.d(__webpack_exports__, {
6429
- /* harmony export */ WASQLiteDBAdapter: () => (/* binding */ WASQLiteDBAdapter)
6430
- /* harmony export */ });
6431
- /* harmony import */ var comlink__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! comlink */ "comlink");
6432
- /* harmony import */ var _PowerSyncDatabase_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../PowerSyncDatabase.js */ "./lib/src/db/PowerSyncDatabase.js");
6433
- /* harmony import */ var _web_sql_flags_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../web-sql-flags.js */ "./lib/src/db/adapters/web-sql-flags.js");
6434
- /* harmony import */ var _WorkerWrappedAsyncDatabaseConnection_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../WorkerWrappedAsyncDatabaseConnection.js */ "./lib/src/db/adapters/WorkerWrappedAsyncDatabaseConnection.js");
6435
- /* harmony import */ var _InternalWASQLiteDBAdapter_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./InternalWASQLiteDBAdapter.js */ "./lib/src/db/adapters/wa-sqlite/InternalWASQLiteDBAdapter.js");
6436
- /* harmony import */ var _WASQLiteOpenFactory_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./WASQLiteOpenFactory.js */ "./lib/src/db/adapters/wa-sqlite/WASQLiteOpenFactory.js");
6437
-
6438
-
6439
-
6440
-
6441
-
6442
-
6443
- /**
6444
- * Adapter for WA-SQLite SQLite connections.
6445
- */
6446
- class WASQLiteDBAdapter extends _InternalWASQLiteDBAdapter_js__WEBPACK_IMPORTED_MODULE_4__.InternalWASQLiteDBAdapter {
6447
- constructor(options) {
6448
- super({
6449
- name: options.dbFilename,
6450
- openConnection: async () => {
6451
- const { workerPort, temporaryStorage, cacheSizeKb } = options;
6452
- if (workerPort) {
6453
- const remote = comlink__WEBPACK_IMPORTED_MODULE_0__.wrap(workerPort);
6454
- return new _WorkerWrappedAsyncDatabaseConnection_js__WEBPACK_IMPORTED_MODULE_3__.WorkerWrappedAsyncDatabaseConnection({
6455
- remote,
6456
- remoteCanCloseUnexpectedly: false,
6457
- identifier: options.dbFilename,
6458
- baseConnection: await remote({
6459
- ...options,
6460
- temporaryStorage: temporaryStorage ?? _web_sql_flags_js__WEBPACK_IMPORTED_MODULE_2__.TemporaryStorageOption.MEMORY,
6461
- cacheSizeKb: cacheSizeKb ?? _web_sql_flags_js__WEBPACK_IMPORTED_MODULE_2__.DEFAULT_CACHE_SIZE_KB,
6462
- flags: (0,_PowerSyncDatabase_js__WEBPACK_IMPORTED_MODULE_1__.resolveWebPowerSyncFlags)(options.flags),
6463
- encryptionKey: options.encryptionKey
6464
- })
6465
- });
6466
- }
6467
- const openFactory = new _WASQLiteOpenFactory_js__WEBPACK_IMPORTED_MODULE_5__.WASQLiteOpenFactory({
6468
- dbFilename: options.dbFilename,
6469
- dbLocation: options.dbLocation,
6470
- debugMode: options.debugMode,
6471
- flags: options.flags,
6472
- temporaryStorage,
6473
- cacheSizeKb,
6474
- logger: options.logger,
6475
- vfs: options.vfs,
6476
- encryptionKey: options.encryptionKey,
6477
- worker: options.worker
6478
- });
6479
- return openFactory.openConnection();
6480
- },
6481
- debugMode: options.debugMode,
6482
- logger: options.logger
6052
+ }
6053
+ }
6054
+ return results;
6055
+ }
6056
+ async stepThroughStatement(api, stmt, bindings, knownColumns, includeResults = true) {
6057
+ // TODO not sure why this is needed currently, but booleans break
6058
+ bindings.forEach((b, index, arr) => {
6059
+ if (typeof b == 'boolean') {
6060
+ arr[index] = b ? 1 : 0;
6061
+ }
6483
6062
  });
6063
+ api.reset(stmt);
6064
+ if (bindings) {
6065
+ api.bind_collection(stmt, bindings);
6066
+ }
6067
+ const rows = [];
6068
+ while ((await api.step(stmt)) === _journeyapps_wa_sqlite__WEBPACK_IMPORTED_MODULE_0__.SQLITE_ROW) {
6069
+ if (includeResults) {
6070
+ const row = api.row(stmt);
6071
+ rows.push(row);
6072
+ }
6073
+ }
6074
+ knownColumns ??= api.column_names(stmt);
6075
+ return { columns: knownColumns, rows };
6076
+ }
6077
+ async close() {
6078
+ if (this.isOpen) {
6079
+ await this.requireSqlite().close(this.db);
6080
+ this.db = 0;
6081
+ }
6484
6082
  }
6485
6083
  }
6486
6084
 
@@ -6497,13 +6095,19 @@ __webpack_require__.r(__webpack_exports__);
6497
6095
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
6498
6096
  /* harmony export */ WASQLiteOpenFactory: () => (/* binding */ WASQLiteOpenFactory)
6499
6097
  /* harmony export */ });
6500
- /* harmony import */ var comlink__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! comlink */ "comlink");
6501
- /* harmony import */ var _worker_db_open_worker_database_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../../worker/db/open-worker-database.js */ "./lib/src/worker/db/open-worker-database.js");
6502
- /* harmony import */ var _AbstractWebSQLOpenFactory_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../AbstractWebSQLOpenFactory.js */ "./lib/src/db/adapters/AbstractWebSQLOpenFactory.js");
6503
- /* harmony import */ var _WorkerWrappedAsyncDatabaseConnection_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../WorkerWrappedAsyncDatabaseConnection.js */ "./lib/src/db/adapters/WorkerWrappedAsyncDatabaseConnection.js");
6504
- /* harmony import */ var _web_sql_flags_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../web-sql-flags.js */ "./lib/src/db/adapters/web-sql-flags.js");
6505
- /* harmony import */ var _InternalWASQLiteDBAdapter_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./InternalWASQLiteDBAdapter.js */ "./lib/src/db/adapters/wa-sqlite/InternalWASQLiteDBAdapter.js");
6506
- /* harmony import */ var _WASQLiteConnection_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./WASQLiteConnection.js */ "./lib/src/db/adapters/wa-sqlite/WASQLiteConnection.js");
6098
+ /* harmony import */ var _powersync_common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @powersync/common */ "@powersync/common");
6099
+ /* harmony import */ var comlink__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! comlink */ "comlink");
6100
+ /* harmony import */ var _worker_db_open_worker_database_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../../worker/db/open-worker-database.js */ "./lib/src/worker/db/open-worker-database.js");
6101
+ /* harmony import */ var _web_sql_flags_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../web-sql-flags.js */ "./lib/src/db/adapters/web-sql-flags.js");
6102
+ /* harmony import */ var _SSRDBAdapter_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../SSRDBAdapter.js */ "./lib/src/db/adapters/SSRDBAdapter.js");
6103
+ /* harmony import */ var _vfs_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./vfs.js */ "./lib/src/db/adapters/wa-sqlite/vfs.js");
6104
+ /* harmony import */ var _worker_db_MultiDatabaseServer_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../../../worker/db/MultiDatabaseServer.js */ "./lib/src/worker/db/MultiDatabaseServer.js");
6105
+ /* harmony import */ var _DatabaseClient_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./DatabaseClient.js */ "./lib/src/db/adapters/wa-sqlite/DatabaseClient.js");
6106
+ /* harmony import */ var _shared_tab_close_signal_js__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../../../shared/tab_close_signal.js */ "./lib/src/shared/tab_close_signal.js");
6107
+ /* harmony import */ var _AsyncWebAdapter_js__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../AsyncWebAdapter.js */ "./lib/src/db/adapters/AsyncWebAdapter.js");
6108
+
6109
+
6110
+
6507
6111
 
6508
6112
 
6509
6113
 
@@ -6514,56 +6118,82 @@ __webpack_require__.r(__webpack_exports__);
6514
6118
  /**
6515
6119
  * Opens a SQLite connection using WA-SQLite.
6516
6120
  */
6517
- class WASQLiteOpenFactory extends _AbstractWebSQLOpenFactory_js__WEBPACK_IMPORTED_MODULE_2__.AbstractWebSQLOpenFactory {
6121
+ class WASQLiteOpenFactory {
6122
+ options;
6123
+ resolvedFlags;
6124
+ logger;
6518
6125
  constructor(options) {
6519
- super(options);
6126
+ this.options = options;
6520
6127
  assertValidWASQLiteOpenFactoryOptions(options);
6128
+ this.resolvedFlags = (0,_web_sql_flags_js__WEBPACK_IMPORTED_MODULE_3__.resolveWebSQLFlags)(options.flags);
6129
+ this.logger = options.logger ?? (0,_powersync_common__WEBPACK_IMPORTED_MODULE_0__.createLogger)(`WASQLiteOpenFactory - ${this.options.dbFilename}`);
6521
6130
  }
6522
6131
  get waOptions() {
6523
6132
  // Cast to extended type
6524
6133
  return this.options;
6525
6134
  }
6526
6135
  openAdapter() {
6527
- return new _InternalWASQLiteDBAdapter_js__WEBPACK_IMPORTED_MODULE_5__.InternalWASQLiteDBAdapter({
6528
- name: this.options.dbFilename,
6529
- openConnection: () => this.openConnection(),
6530
- debugMode: this.options.debugMode,
6531
- logger: this.logger
6532
- });
6136
+ return new _AsyncWebAdapter_js__WEBPACK_IMPORTED_MODULE_9__.AsyncDbAdapter(this.openConnection(), this.options.dbFilename);
6137
+ }
6138
+ openDB() {
6139
+ const { resolvedFlags: { disableSSRWarning, enableMultiTabs, ssrMode = (0,_web_sql_flags_js__WEBPACK_IMPORTED_MODULE_3__.isServerSide)() } } = this;
6140
+ if (ssrMode) {
6141
+ if (!disableSSRWarning) {
6142
+ this.logger.warn(`
6143
+ Running PowerSync in SSR mode.
6144
+ Only empty query results will be returned.
6145
+ Disable this warning by setting 'disableSSRWarning: true' in options.`);
6146
+ }
6147
+ return new _SSRDBAdapter_js__WEBPACK_IMPORTED_MODULE_4__.SSRDBAdapter();
6148
+ }
6149
+ if (!enableMultiTabs) {
6150
+ this.logger.warn('Multiple tab support is not enabled. Using this site across multiple tabs may not function correctly.');
6151
+ }
6152
+ return this.openAdapter();
6533
6153
  }
6534
6154
  async openConnection() {
6535
6155
  const { enableMultiTabs, useWebWorker } = this.resolvedFlags;
6536
- const { vfs = _WASQLiteConnection_js__WEBPACK_IMPORTED_MODULE_6__.WASQLiteVFS.IDBBatchAtomicVFS, temporaryStorage = _web_sql_flags_js__WEBPACK_IMPORTED_MODULE_4__.TemporaryStorageOption.MEMORY, cacheSizeKb = _web_sql_flags_js__WEBPACK_IMPORTED_MODULE_4__.DEFAULT_CACHE_SIZE_KB, encryptionKey } = this.waOptions;
6156
+ const { vfs = _vfs_js__WEBPACK_IMPORTED_MODULE_5__.WASQLiteVFS.IDBBatchAtomicVFS, temporaryStorage = _web_sql_flags_js__WEBPACK_IMPORTED_MODULE_3__.TemporaryStorageOption.MEMORY, cacheSizeKb = _web_sql_flags_js__WEBPACK_IMPORTED_MODULE_3__.DEFAULT_CACHE_SIZE_KB, encryptionKey } = this.waOptions;
6537
6157
  if (!enableMultiTabs) {
6538
6158
  this.logger.warn('Multiple tabs are not enabled in this browser');
6539
6159
  }
6160
+ const resolvedOptions = {
6161
+ dbFilename: this.options.dbFilename,
6162
+ dbLocation: this.options.dbLocation,
6163
+ debugMode: this.options.debugMode,
6164
+ vfs,
6165
+ temporaryStorage,
6166
+ cacheSizeKb,
6167
+ flags: this.resolvedFlags,
6168
+ encryptionKey: encryptionKey
6169
+ };
6170
+ let clientOptions;
6171
+ let requiresPersistentTriggers = (0,_vfs_js__WEBPACK_IMPORTED_MODULE_5__.vfsRequiresDedicatedWorkers)(vfs);
6540
6172
  if (useWebWorker) {
6541
6173
  const optionsDbWorker = this.options.worker;
6542
6174
  const workerPort = typeof optionsDbWorker == 'function'
6543
- ? (0,_worker_db_open_worker_database_js__WEBPACK_IMPORTED_MODULE_1__.resolveWorkerDatabasePortFactory)(() => optionsDbWorker({
6175
+ ? (0,_worker_db_open_worker_database_js__WEBPACK_IMPORTED_MODULE_2__.resolveWorkerDatabasePortFactory)(() => optionsDbWorker({
6544
6176
  ...this.options,
6545
6177
  temporaryStorage,
6546
6178
  cacheSizeKb,
6547
6179
  flags: this.resolvedFlags,
6548
6180
  encryptionKey
6549
6181
  }))
6550
- : (0,_worker_db_open_worker_database_js__WEBPACK_IMPORTED_MODULE_1__.openWorkerDatabasePort)(this.options.dbFilename, enableMultiTabs, optionsDbWorker, this.waOptions.vfs);
6551
- const workerDBOpener = comlink__WEBPACK_IMPORTED_MODULE_0__.wrap(workerPort);
6552
- return new _WorkerWrappedAsyncDatabaseConnection_js__WEBPACK_IMPORTED_MODULE_3__.WorkerWrappedAsyncDatabaseConnection({
6553
- remote: workerDBOpener,
6182
+ : (0,_worker_db_open_worker_database_js__WEBPACK_IMPORTED_MODULE_2__.openWorkerDatabasePort)(this.options.dbFilename, enableMultiTabs, optionsDbWorker, this.waOptions.vfs);
6183
+ const source = comlink__WEBPACK_IMPORTED_MODULE_1__.wrap(workerPort);
6184
+ const closeSignal = new AbortController();
6185
+ const connection = await source.connect({
6186
+ ...resolvedOptions,
6187
+ logLevel: this.logger.getLevel(),
6188
+ lockName: await (0,_shared_tab_close_signal_js__WEBPACK_IMPORTED_MODULE_8__.generateTabCloseSignal)(closeSignal.signal)
6189
+ });
6190
+ clientOptions = {
6191
+ connection,
6192
+ source,
6554
6193
  // This tab owns the worker, so we're guaranteed to outlive it.
6555
6194
  remoteCanCloseUnexpectedly: false,
6556
- baseConnection: await workerDBOpener({
6557
- dbFilename: this.options.dbFilename,
6558
- vfs,
6559
- temporaryStorage,
6560
- cacheSizeKb,
6561
- flags: this.resolvedFlags,
6562
- encryptionKey: encryptionKey,
6563
- logLevel: this.logger.getLevel()
6564
- }),
6565
- identifier: this.options.dbFilename,
6566
6195
  onClose: () => {
6196
+ closeSignal.abort();
6567
6197
  if (workerPort instanceof Worker) {
6568
6198
  workerPort.terminate();
6569
6199
  }
@@ -6571,21 +6201,19 @@ class WASQLiteOpenFactory extends _AbstractWebSQLOpenFactory_js__WEBPACK_IMPORTE
6571
6201
  workerPort.close();
6572
6202
  }
6573
6203
  }
6574
- });
6204
+ };
6575
6205
  }
6576
6206
  else {
6577
- // Don't use a web worker
6578
- return new _WASQLiteConnection_js__WEBPACK_IMPORTED_MODULE_6__.WASqliteConnection({
6579
- dbFilename: this.options.dbFilename,
6580
- dbLocation: this.options.dbLocation,
6581
- debugMode: this.options.debugMode,
6582
- vfs,
6583
- temporaryStorage,
6584
- cacheSizeKb,
6585
- flags: this.resolvedFlags,
6586
- encryptionKey: encryptionKey
6587
- });
6588
- }
6207
+ // Don't use a web worker. Instead, open the MultiDatabaseServer a worker would use locally.
6208
+ const localServer = new _worker_db_MultiDatabaseServer_js__WEBPACK_IMPORTED_MODULE_6__.MultiDatabaseServer(this.logger);
6209
+ requiresPersistentTriggers = true;
6210
+ const connection = await localServer.openConnectionLocally(resolvedOptions);
6211
+ clientOptions = { connection, source: null, remoteCanCloseUnexpectedly: false };
6212
+ }
6213
+ return new _DatabaseClient_js__WEBPACK_IMPORTED_MODULE_7__.DatabaseClient(clientOptions, {
6214
+ ...resolvedOptions,
6215
+ requiresPersistentTriggers
6216
+ });
6589
6217
  }
6590
6218
  }
6591
6219
  /**
@@ -6595,7 +6223,7 @@ function assertValidWASQLiteOpenFactoryOptions(options) {
6595
6223
  // The OPFS VFS only works in dedicated web workers.
6596
6224
  if ('vfs' in options && 'flags' in options) {
6597
6225
  const { vfs, flags = {} } = options;
6598
- if (vfs !== _WASQLiteConnection_js__WEBPACK_IMPORTED_MODULE_6__.WASQLiteVFS.IDBBatchAtomicVFS && 'useWebWorker' in flags && !flags.useWebWorker) {
6226
+ if (vfs && (0,_vfs_js__WEBPACK_IMPORTED_MODULE_5__.vfsRequiresDedicatedWorkers)(vfs) && 'useWebWorker' in flags && !flags.useWebWorker) {
6599
6227
  throw new Error(`Invalid configuration: The 'useWebWorker' flag must be true when using an OPFS-based VFS (${vfs}).`);
6600
6228
  }
6601
6229
  }
@@ -6640,6 +6268,117 @@ class WASQLitePowerSyncDatabaseOpenFactory extends _AbstractWebPowerSyncDatabase
6640
6268
  }
6641
6269
 
6642
6270
 
6271
+ /***/ },
6272
+
6273
+ /***/ "./lib/src/db/adapters/wa-sqlite/vfs.js"
6274
+ /*!**********************************************!*\
6275
+ !*** ./lib/src/db/adapters/wa-sqlite/vfs.js ***!
6276
+ \**********************************************/
6277
+ (__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) {
6278
+
6279
+ __webpack_require__.r(__webpack_exports__);
6280
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
6281
+ /* harmony export */ AsyncWASQLiteModuleFactory: () => (/* binding */ AsyncWASQLiteModuleFactory),
6282
+ /* harmony export */ DEFAULT_MODULE_FACTORIES: () => (/* binding */ DEFAULT_MODULE_FACTORIES),
6283
+ /* harmony export */ MultiCipherAsyncWASQLiteModuleFactory: () => (/* binding */ MultiCipherAsyncWASQLiteModuleFactory),
6284
+ /* harmony export */ MultiCipherSyncWASQLiteModuleFactory: () => (/* binding */ MultiCipherSyncWASQLiteModuleFactory),
6285
+ /* harmony export */ SyncWASQLiteModuleFactory: () => (/* binding */ SyncWASQLiteModuleFactory),
6286
+ /* harmony export */ WASQLiteVFS: () => (/* binding */ WASQLiteVFS),
6287
+ /* harmony export */ vfsRequiresDedicatedWorkers: () => (/* binding */ vfsRequiresDedicatedWorkers)
6288
+ /* harmony export */ });
6289
+ /**
6290
+ * List of currently tested virtual filesystems
6291
+ */
6292
+ var WASQLiteVFS;
6293
+ (function (WASQLiteVFS) {
6294
+ WASQLiteVFS["IDBBatchAtomicVFS"] = "IDBBatchAtomicVFS";
6295
+ WASQLiteVFS["OPFSCoopSyncVFS"] = "OPFSCoopSyncVFS";
6296
+ WASQLiteVFS["AccessHandlePoolVFS"] = "AccessHandlePoolVFS";
6297
+ })(WASQLiteVFS || (WASQLiteVFS = {}));
6298
+ function vfsRequiresDedicatedWorkers(vfs) {
6299
+ return vfs != WASQLiteVFS.IDBBatchAtomicVFS;
6300
+ }
6301
+ /**
6302
+ * @internal
6303
+ */
6304
+ const AsyncWASQLiteModuleFactory = async () => {
6305
+ const { default: factory } = await Promise.resolve(/*! import() */).then(__webpack_require__.bind(__webpack_require__, /*! @journeyapps/wa-sqlite/dist/wa-sqlite-async.mjs */ "../../node_modules/.pnpm/@journeyapps+wa-sqlite@1.5.0/node_modules/@journeyapps/wa-sqlite/dist/wa-sqlite-async.mjs"));
6306
+ return factory();
6307
+ };
6308
+ /**
6309
+ * @internal
6310
+ */
6311
+ const MultiCipherAsyncWASQLiteModuleFactory = async () => {
6312
+ const { default: factory } = await Promise.resolve(/*! import() */).then(__webpack_require__.bind(__webpack_require__, /*! @journeyapps/wa-sqlite/dist/mc-wa-sqlite-async.mjs */ "../../node_modules/.pnpm/@journeyapps+wa-sqlite@1.5.0/node_modules/@journeyapps/wa-sqlite/dist/mc-wa-sqlite-async.mjs"));
6313
+ return factory();
6314
+ };
6315
+ /**
6316
+ * @internal
6317
+ */
6318
+ const SyncWASQLiteModuleFactory = async () => {
6319
+ const { default: factory } = await Promise.resolve(/*! import() */).then(__webpack_require__.bind(__webpack_require__, /*! @journeyapps/wa-sqlite/dist/wa-sqlite.mjs */ "../../node_modules/.pnpm/@journeyapps+wa-sqlite@1.5.0/node_modules/@journeyapps/wa-sqlite/dist/wa-sqlite.mjs"));
6320
+ return factory();
6321
+ };
6322
+ /**
6323
+ * @internal
6324
+ */
6325
+ const MultiCipherSyncWASQLiteModuleFactory = async () => {
6326
+ const { default: factory } = await Promise.resolve(/*! import() */).then(__webpack_require__.bind(__webpack_require__, /*! @journeyapps/wa-sqlite/dist/mc-wa-sqlite.mjs */ "../../node_modules/.pnpm/@journeyapps+wa-sqlite@1.5.0/node_modules/@journeyapps/wa-sqlite/dist/mc-wa-sqlite.mjs"));
6327
+ return factory();
6328
+ };
6329
+ /**
6330
+ * @internal
6331
+ */
6332
+ const DEFAULT_MODULE_FACTORIES = {
6333
+ [WASQLiteVFS.IDBBatchAtomicVFS]: async (options) => {
6334
+ let module;
6335
+ if (options.encryptionKey) {
6336
+ module = await MultiCipherAsyncWASQLiteModuleFactory();
6337
+ }
6338
+ else {
6339
+ module = await AsyncWASQLiteModuleFactory();
6340
+ }
6341
+ const { IDBBatchAtomicVFS } = await Promise.resolve(/*! import() */).then(__webpack_require__.t.bind(__webpack_require__, /*! @journeyapps/wa-sqlite/src/examples/IDBBatchAtomicVFS.js */ "@journeyapps/wa-sqlite/src/examples/IDBBatchAtomicVFS.js", 19));
6342
+ return {
6343
+ module,
6344
+ // @ts-expect-error The types for this static method are missing upstream
6345
+ vfs: await IDBBatchAtomicVFS.create(options.dbFileName, module, { lockPolicy: 'exclusive' })
6346
+ };
6347
+ },
6348
+ [WASQLiteVFS.AccessHandlePoolVFS]: async (options) => {
6349
+ let module;
6350
+ if (options.encryptionKey) {
6351
+ module = await MultiCipherSyncWASQLiteModuleFactory();
6352
+ }
6353
+ else {
6354
+ module = await SyncWASQLiteModuleFactory();
6355
+ }
6356
+ // @ts-expect-error The types for this static method are missing upstream
6357
+ const { AccessHandlePoolVFS } = await Promise.resolve(/*! import() */).then(__webpack_require__.t.bind(__webpack_require__, /*! @journeyapps/wa-sqlite/src/examples/AccessHandlePoolVFS.js */ "@journeyapps/wa-sqlite/src/examples/AccessHandlePoolVFS.js", 19));
6358
+ return {
6359
+ module,
6360
+ vfs: await AccessHandlePoolVFS.create(options.dbFileName, module)
6361
+ };
6362
+ },
6363
+ [WASQLiteVFS.OPFSCoopSyncVFS]: async (options) => {
6364
+ let module;
6365
+ if (options.encryptionKey) {
6366
+ module = await MultiCipherSyncWASQLiteModuleFactory();
6367
+ }
6368
+ else {
6369
+ module = await SyncWASQLiteModuleFactory();
6370
+ }
6371
+ // @ts-expect-error The types for this static method are missing upstream
6372
+ const { OPFSCoopSyncVFS } = await Promise.resolve(/*! import() */).then(__webpack_require__.t.bind(__webpack_require__, /*! @journeyapps/wa-sqlite/src/examples/OPFSCoopSyncVFS.js */ "@journeyapps/wa-sqlite/src/examples/OPFSCoopSyncVFS.js", 19));
6373
+ const vfs = await OPFSCoopSyncVFS.create(options.dbFileName, module);
6374
+ return {
6375
+ module,
6376
+ vfs
6377
+ };
6378
+ }
6379
+ };
6380
+
6381
+
6643
6382
  /***/ },
6644
6383
 
6645
6384
  /***/ "./lib/src/db/adapters/web-sql-flags.js"
@@ -6707,8 +6446,6 @@ __webpack_require__.r(__webpack_exports__);
6707
6446
  /* harmony export */ SSRStreamingSyncImplementation: () => (/* binding */ SSRStreamingSyncImplementation)
6708
6447
  /* harmony export */ });
6709
6448
  /* harmony import */ var _powersync_common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @powersync/common */ "@powersync/common");
6710
- /* harmony import */ var async_mutex__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! async-mutex */ "async-mutex");
6711
-
6712
6449
 
6713
6450
  class SSRStreamingSyncImplementation extends _powersync_common__WEBPACK_IMPORTED_MODULE_0__.BaseObserver {
6714
6451
  syncMutex;
@@ -6718,14 +6455,14 @@ class SSRStreamingSyncImplementation extends _powersync_common__WEBPACK_IMPORTED
6718
6455
  syncStatus;
6719
6456
  constructor(options) {
6720
6457
  super();
6721
- this.syncMutex = new async_mutex__WEBPACK_IMPORTED_MODULE_1__.Mutex();
6722
- this.crudMutex = new async_mutex__WEBPACK_IMPORTED_MODULE_1__.Mutex();
6458
+ this.syncMutex = new _powersync_common__WEBPACK_IMPORTED_MODULE_0__.Mutex();
6459
+ this.crudMutex = new _powersync_common__WEBPACK_IMPORTED_MODULE_0__.Mutex();
6723
6460
  this.syncStatus = new _powersync_common__WEBPACK_IMPORTED_MODULE_0__.SyncStatus({});
6724
6461
  this.isConnected = false;
6725
6462
  }
6726
6463
  obtainLock(lockOptions) {
6727
6464
  const mutex = lockOptions.type == _powersync_common__WEBPACK_IMPORTED_MODULE_0__.LockType.CRUD ? this.crudMutex : this.syncMutex;
6728
- return mutex.runExclusive(lockOptions.callback);
6465
+ return mutex.runExclusive(lockOptions.callback, lockOptions.signal);
6729
6466
  }
6730
6467
  /**
6731
6468
  * This is a no-op in SSR mode
@@ -6788,11 +6525,11 @@ __webpack_require__.r(__webpack_exports__);
6788
6525
  /* harmony export */ SharedWebStreamingSyncImplementation: () => (/* binding */ SharedWebStreamingSyncImplementation)
6789
6526
  /* harmony export */ });
6790
6527
  /* harmony import */ var comlink__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! comlink */ "comlink");
6791
- /* harmony import */ var _shared_navigator_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../shared/navigator.js */ "./lib/src/shared/navigator.js");
6792
- /* harmony import */ var _worker_sync_AbstractSharedSyncClientProvider_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../worker/sync/AbstractSharedSyncClientProvider.js */ "./lib/src/worker/sync/AbstractSharedSyncClientProvider.js");
6793
- /* harmony import */ var _worker_sync_SharedSyncImplementation_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../worker/sync/SharedSyncImplementation.js */ "./lib/src/worker/sync/SharedSyncImplementation.js");
6794
- /* harmony import */ var _adapters_web_sql_flags_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../adapters/web-sql-flags.js */ "./lib/src/db/adapters/web-sql-flags.js");
6795
- /* harmony import */ var _WebStreamingSyncImplementation_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./WebStreamingSyncImplementation.js */ "./lib/src/db/sync/WebStreamingSyncImplementation.js");
6528
+ /* harmony import */ var _worker_sync_AbstractSharedSyncClientProvider_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../worker/sync/AbstractSharedSyncClientProvider.js */ "./lib/src/worker/sync/AbstractSharedSyncClientProvider.js");
6529
+ /* harmony import */ var _worker_sync_SharedSyncImplementation_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../worker/sync/SharedSyncImplementation.js */ "./lib/src/worker/sync/SharedSyncImplementation.js");
6530
+ /* harmony import */ var _adapters_web_sql_flags_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../adapters/web-sql-flags.js */ "./lib/src/db/adapters/web-sql-flags.js");
6531
+ /* harmony import */ var _WebStreamingSyncImplementation_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./WebStreamingSyncImplementation.js */ "./lib/src/db/sync/WebStreamingSyncImplementation.js");
6532
+ /* harmony import */ var _shared_tab_close_signal_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../shared/tab_close_signal.js */ "./lib/src/shared/tab_close_signal.js");
6796
6533
 
6797
6534
 
6798
6535
 
@@ -6803,7 +6540,7 @@ __webpack_require__.r(__webpack_exports__);
6803
6540
  * The shared worker will trigger methods on this side of the message port
6804
6541
  * via this client provider.
6805
6542
  */
6806
- class SharedSyncClientProvider extends _worker_sync_AbstractSharedSyncClientProvider_js__WEBPACK_IMPORTED_MODULE_2__.AbstractSharedSyncClientProvider {
6543
+ class SharedSyncClientProvider extends _worker_sync_AbstractSharedSyncClientProvider_js__WEBPACK_IMPORTED_MODULE_1__.AbstractSharedSyncClientProvider {
6807
6544
  options;
6808
6545
  statusChanged;
6809
6546
  webDB;
@@ -6874,7 +6611,7 @@ class SharedSyncClientProvider extends _worker_sync_AbstractSharedSyncClientProv
6874
6611
  /**
6875
6612
  * The local part of the sync implementation on the web, which talks to a sync implementation hosted in a shared worker.
6876
6613
  */
6877
- class SharedWebStreamingSyncImplementation extends _WebStreamingSyncImplementation_js__WEBPACK_IMPORTED_MODULE_5__.WebStreamingSyncImplementation {
6614
+ class SharedWebStreamingSyncImplementation extends _WebStreamingSyncImplementation_js__WEBPACK_IMPORTED_MODULE_4__.WebStreamingSyncImplementation {
6878
6615
  syncManager;
6879
6616
  clientProvider;
6880
6617
  messagePort;
@@ -6890,10 +6627,10 @@ class SharedWebStreamingSyncImplementation extends _WebStreamingSyncImplementati
6890
6627
  */
6891
6628
  const resolvedWorkerOptions = {
6892
6629
  dbFilename: this.options.identifier,
6893
- temporaryStorage: _adapters_web_sql_flags_js__WEBPACK_IMPORTED_MODULE_4__.TemporaryStorageOption.MEMORY,
6894
- cacheSizeKb: _adapters_web_sql_flags_js__WEBPACK_IMPORTED_MODULE_4__.DEFAULT_CACHE_SIZE_KB,
6630
+ temporaryStorage: _adapters_web_sql_flags_js__WEBPACK_IMPORTED_MODULE_3__.TemporaryStorageOption.MEMORY,
6631
+ cacheSizeKb: _adapters_web_sql_flags_js__WEBPACK_IMPORTED_MODULE_3__.DEFAULT_CACHE_SIZE_KB,
6895
6632
  ...options,
6896
- flags: (0,_adapters_web_sql_flags_js__WEBPACK_IMPORTED_MODULE_4__.resolveWebSQLFlags)(options.flags)
6633
+ flags: (0,_adapters_web_sql_flags_js__WEBPACK_IMPORTED_MODULE_3__.resolveWebSQLFlags)(options.flags)
6897
6634
  };
6898
6635
  const syncWorker = options.sync?.worker;
6899
6636
  if (syncWorker) {
@@ -6908,7 +6645,7 @@ class SharedWebStreamingSyncImplementation extends _WebStreamingSyncImplementati
6908
6645
  }
6909
6646
  }
6910
6647
  else {
6911
- this.messagePort = new SharedWorker(new URL(/* worker import */ __webpack_require__.p + __webpack_require__.u("_journeyapps_wa-sqlite-_journeyapps_wa-sqlite_src_examples_AccessHandlePoolVFS_js-_journeyapp-89f0ba"), __webpack_require__.b), {
6648
+ this.messagePort = new SharedWorker(new URL(/* worker import */ __webpack_require__.p + __webpack_require__.u("lib_src_worker_sync_SharedSyncImplementation_worker_js"), __webpack_require__.b), {
6912
6649
  /* @vite-ignore */
6913
6650
  name: `shared-sync-${this.webOptions.identifier}`,
6914
6651
  type: undefined
@@ -6951,24 +6688,9 @@ class SharedWebStreamingSyncImplementation extends _WebStreamingSyncImplementati
6951
6688
  * - We resolve the top-level promise after the lock has been registered with the shared worker.
6952
6689
  * - The client sends the params to the shared worker after locks have been registered.
6953
6690
  */
6954
- await new Promise((resolve) => {
6955
- // Request a random lock until this client is disposed. The name of the lock is sent to the shared worker, which
6956
- // will also attempt to acquire it. Since the lock is returned when the tab is closed, this allows the share worker
6957
- // to free resources associated with this tab.
6958
- // We take hold of this lock as soon-as-possible in order to cater for potentially closed tabs.
6959
- (0,_shared_navigator_js__WEBPACK_IMPORTED_MODULE_1__.getNavigatorLocks)().request(`tab-close-signal-${crypto.randomUUID()}`, async (lock) => {
6960
- if (this.abortOnClose.signal.aborted) {
6961
- return;
6962
- }
6963
- // Awaiting here ensures the worker is waiting for the lock
6964
- await this.syncManager.addLockBasedCloseSignal(lock.name);
6965
- // The lock has been registered, we can continue with the initialization
6966
- resolve();
6967
- await new Promise((r) => {
6968
- this.abortOnClose.signal.onabort = () => r();
6969
- });
6970
- });
6971
- });
6691
+ const closeSignal = await (0,_shared_tab_close_signal_js__WEBPACK_IMPORTED_MODULE_5__.generateTabCloseSignal)(this.abortOnClose.signal);
6692
+ // Awaiting here ensures the worker is waiting for the lock
6693
+ await this.syncManager.addLockBasedCloseSignal(closeSignal);
6972
6694
  const { crudUploadThrottleMs, identifier, retryDelayMs } = this.options;
6973
6695
  const flags = { ...this.webOptions.flags, workers: undefined };
6974
6696
  await this.syncManager.setParams({
@@ -7006,13 +6728,13 @@ class SharedWebStreamingSyncImplementation extends _WebStreamingSyncImplementati
7006
6728
  // Listen for the close acknowledgment from the worker
7007
6729
  this.messagePort.addEventListener('message', (event) => {
7008
6730
  const payload = event.data;
7009
- if (payload?.event === _worker_sync_SharedSyncImplementation_js__WEBPACK_IMPORTED_MODULE_3__.SharedSyncClientEvent.CLOSE_ACK) {
6731
+ if (payload?.event === _worker_sync_SharedSyncImplementation_js__WEBPACK_IMPORTED_MODULE_2__.SharedSyncClientEvent.CLOSE_ACK) {
7010
6732
  resolve();
7011
6733
  }
7012
6734
  });
7013
6735
  // Signal the shared worker that this client is closing its connection to the worker
7014
6736
  const closeMessagePayload = {
7015
- event: _worker_sync_SharedSyncImplementation_js__WEBPACK_IMPORTED_MODULE_3__.SharedSyncClientEvent.CLOSE_CLIENT,
6737
+ event: _worker_sync_SharedSyncImplementation_js__WEBPACK_IMPORTED_MODULE_2__.SharedSyncClientEvent.CLOSE_CLIENT,
7016
6738
  data: {}
7017
6739
  };
7018
6740
  this.messagePort.postMessage(closeMessagePayload);
@@ -7224,6 +6946,153 @@ const getNavigatorLocks = () => {
7224
6946
  };
7225
6947
 
7226
6948
 
6949
+ /***/ },
6950
+
6951
+ /***/ "./lib/src/shared/tab_close_signal.js"
6952
+ /*!********************************************!*\
6953
+ !*** ./lib/src/shared/tab_close_signal.js ***!
6954
+ \********************************************/
6955
+ (__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) {
6956
+
6957
+ __webpack_require__.r(__webpack_exports__);
6958
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
6959
+ /* harmony export */ generateTabCloseSignal: () => (/* binding */ generateTabCloseSignal)
6960
+ /* harmony export */ });
6961
+ /* harmony import */ var _navigator_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./navigator.js */ "./lib/src/shared/navigator.js");
6962
+
6963
+ /**
6964
+ * Requests a random lock that will be released once the optional signal is aborted (or, if no signal is given, when the
6965
+ * tab is closed).
6966
+ *
6967
+ * This allows sending the name of the lock to another context (e.g. a shared worker), which will also attempt to
6968
+ * acquire it. Since the lock is returned when the tab is closed, this allows the shared worker to free resources
6969
+ * assocatiated with this tab.
6970
+ *
6971
+ * We take hold of this lock as soon-as-possible in order to cater for potentially closed tabs.
6972
+ */
6973
+ function generateTabCloseSignal(abort) {
6974
+ return new Promise((resolve, reject) => {
6975
+ const options = { signal: abort };
6976
+ (0,_navigator_js__WEBPACK_IMPORTED_MODULE_0__.getNavigatorLocks)()
6977
+ .request(`tab-close-signal-${crypto.randomUUID()}`, options, (lock) => {
6978
+ resolve(lock.name);
6979
+ return new Promise((resolve) => {
6980
+ if (abort) {
6981
+ abort.addEventListener('abort', () => resolve());
6982
+ }
6983
+ });
6984
+ })
6985
+ .catch(reject);
6986
+ });
6987
+ }
6988
+
6989
+
6990
+ /***/ },
6991
+
6992
+ /***/ "./lib/src/worker/db/MultiDatabaseServer.js"
6993
+ /*!**************************************************!*\
6994
+ !*** ./lib/src/worker/db/MultiDatabaseServer.js ***!
6995
+ \**************************************************/
6996
+ (__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) {
6997
+
6998
+ __webpack_require__.r(__webpack_exports__);
6999
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
7000
+ /* harmony export */ MultiDatabaseServer: () => (/* binding */ MultiDatabaseServer),
7001
+ /* harmony export */ isSharedWorker: () => (/* binding */ isSharedWorker)
7002
+ /* harmony export */ });
7003
+ /* harmony import */ var comlink__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! comlink */ "comlink");
7004
+ /* harmony import */ var _db_adapters_wa_sqlite_DatabaseServer_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../db/adapters/wa-sqlite/DatabaseServer.js */ "./lib/src/db/adapters/wa-sqlite/DatabaseServer.js");
7005
+ /* harmony import */ var _shared_navigator_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../shared/navigator.js */ "./lib/src/shared/navigator.js");
7006
+ /* harmony import */ var _db_adapters_wa_sqlite_RawSqliteConnection_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../db/adapters/wa-sqlite/RawSqliteConnection.js */ "./lib/src/db/adapters/wa-sqlite/RawSqliteConnection.js");
7007
+ /* harmony import */ var _db_adapters_wa_sqlite_ConcurrentConnection_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../db/adapters/wa-sqlite/ConcurrentConnection.js */ "./lib/src/db/adapters/wa-sqlite/ConcurrentConnection.js");
7008
+
7009
+
7010
+
7011
+
7012
+
7013
+ const OPEN_DB_LOCK = 'open-wasqlite-db';
7014
+ /**
7015
+ * Shared state to manage multiple database connections hosted by a worker.
7016
+ */
7017
+ class MultiDatabaseServer {
7018
+ logger;
7019
+ activeDatabases = new Map();
7020
+ constructor(logger) {
7021
+ this.logger = logger;
7022
+ }
7023
+ async handleConnection(options) {
7024
+ this.logger.setLevel(options.logLevel);
7025
+ return comlink__WEBPACK_IMPORTED_MODULE_0__.proxy(await this.openConnectionLocally(options, options.lockName));
7026
+ }
7027
+ async connectToExisting(name, lockName) {
7028
+ return (0,_shared_navigator_js__WEBPACK_IMPORTED_MODULE_2__.getNavigatorLocks)().request(OPEN_DB_LOCK, async () => {
7029
+ const server = this.activeDatabases.get(name);
7030
+ if (server == null) {
7031
+ throw new Error(`connectToExisting(${name}) failed because the worker doesn't own a database with that name.`);
7032
+ }
7033
+ return comlink__WEBPACK_IMPORTED_MODULE_0__.proxy(await server.connect(lockName));
7034
+ });
7035
+ }
7036
+ async openConnectionLocally(options, lockName) {
7037
+ // Especially on Firefox, we're sometimes seeing "NoModificationAllowedError"s when opening OPFS databases we can
7038
+ // work around by retrying.
7039
+ const maxAttempts = 3;
7040
+ let server;
7041
+ for (let count = 0; count < maxAttempts - 1; count++) {
7042
+ try {
7043
+ server = await this.databaseOpenAttempt(options);
7044
+ }
7045
+ catch (ex) {
7046
+ this.logger.warn(`Attempt ${count + 1} of ${maxAttempts} to open database failed, retrying in 1 second...`, ex);
7047
+ await new Promise((resolve) => setTimeout(resolve, 1000));
7048
+ }
7049
+ }
7050
+ // Final attempt if we haven't been able to open the server - rethrow errors if we still can't open.
7051
+ server ??= await this.databaseOpenAttempt(options);
7052
+ return server.connect(lockName);
7053
+ }
7054
+ async databaseOpenAttempt(options) {
7055
+ return (0,_shared_navigator_js__WEBPACK_IMPORTED_MODULE_2__.getNavigatorLocks)().request(OPEN_DB_LOCK, async () => {
7056
+ const { dbFilename } = options;
7057
+ let server = this.activeDatabases.get(dbFilename);
7058
+ if (server == null) {
7059
+ const needsNavigatorLocks = !isSharedWorker;
7060
+ const connection = new _db_adapters_wa_sqlite_RawSqliteConnection_js__WEBPACK_IMPORTED_MODULE_3__.RawSqliteConnection(options);
7061
+ const withSafeConcurrency = new _db_adapters_wa_sqlite_ConcurrentConnection_js__WEBPACK_IMPORTED_MODULE_4__.ConcurrentSqliteConnection(connection, needsNavigatorLocks);
7062
+ // Initializing the RawSqliteConnection will run some pragmas that might write to the database file, so we want
7063
+ // to do that in an exclusive lock. Note that OPEN_DB_LOCK is not enough for that, as another tab might have
7064
+ // already created a connection (and is thus outside of OPEN_DB_LOCK) while currently writing to it.
7065
+ const returnLease = await withSafeConcurrency.acquireMutex();
7066
+ try {
7067
+ await connection.init();
7068
+ }
7069
+ catch (e) {
7070
+ returnLease();
7071
+ await connection.close();
7072
+ throw e;
7073
+ }
7074
+ returnLease();
7075
+ const onClose = () => this.activeDatabases.delete(dbFilename);
7076
+ server = new _db_adapters_wa_sqlite_DatabaseServer_js__WEBPACK_IMPORTED_MODULE_1__.DatabaseServer({
7077
+ inner: withSafeConcurrency,
7078
+ logger: this.logger,
7079
+ onClose
7080
+ });
7081
+ this.activeDatabases.set(dbFilename, server);
7082
+ }
7083
+ return server;
7084
+ });
7085
+ }
7086
+ closeAll() {
7087
+ const existingDatabases = [...this.activeDatabases.values()];
7088
+ return Promise.all(existingDatabases.map((db) => {
7089
+ db.forceClose();
7090
+ }));
7091
+ }
7092
+ }
7093
+ const isSharedWorker = 'SharedWorkerGlobalScope' in globalThis;
7094
+
7095
+
7227
7096
  /***/ },
7228
7097
 
7229
7098
  /***/ "./lib/src/worker/db/open-worker-database.js"
@@ -7240,14 +7109,14 @@ __webpack_require__.r(__webpack_exports__);
7240
7109
  /* harmony export */ resolveWorkerDatabasePortFactory: () => (/* binding */ resolveWorkerDatabasePortFactory)
7241
7110
  /* harmony export */ });
7242
7111
  /* harmony import */ var comlink__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! comlink */ "comlink");
7243
- /* harmony import */ var _db_adapters_wa_sqlite_WASQLiteConnection_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../db/adapters/wa-sqlite/WASQLiteConnection.js */ "./lib/src/db/adapters/wa-sqlite/WASQLiteConnection.js");
7112
+ /* harmony import */ var _db_adapters_wa_sqlite_vfs_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../db/adapters/wa-sqlite/vfs.js */ "./lib/src/db/adapters/wa-sqlite/vfs.js");
7244
7113
 
7245
7114
 
7246
7115
  /**
7247
7116
  * Opens a shared or dedicated worker which exposes opening of database connections
7248
7117
  */
7249
7118
  function openWorkerDatabasePort(workerIdentifier, multipleTabs = true, worker = '', vfs) {
7250
- const needsDedicated = vfs == _db_adapters_wa_sqlite_WASQLiteConnection_js__WEBPACK_IMPORTED_MODULE_1__.WASQLiteVFS.AccessHandlePoolVFS || vfs == _db_adapters_wa_sqlite_WASQLiteConnection_js__WEBPACK_IMPORTED_MODULE_1__.WASQLiteVFS.OPFSCoopSyncVFS;
7119
+ const needsDedicated = vfs == _db_adapters_wa_sqlite_vfs_js__WEBPACK_IMPORTED_MODULE_1__.WASQLiteVFS.AccessHandlePoolVFS || vfs == _db_adapters_wa_sqlite_vfs_js__WEBPACK_IMPORTED_MODULE_1__.WASQLiteVFS.OPFSCoopSyncVFS;
7251
7120
  if (worker) {
7252
7121
  return !needsDedicated && multipleTabs
7253
7122
  ? new SharedWorker(`${worker}`, {
@@ -7267,12 +7136,12 @@ function openWorkerDatabasePort(workerIdentifier, multipleTabs = true, worker =
7267
7136
  * (in the case of Android)
7268
7137
  */
7269
7138
  return !needsDedicated && multipleTabs
7270
- ? new SharedWorker(new URL(/* worker import */ __webpack_require__.p + __webpack_require__.u("_journeyapps_wa-sqlite_src_examples_AccessHandlePoolVFS_js-_journeyapps_wa-sqlite_src_example-97ebe9"), __webpack_require__.b), {
7139
+ ? new SharedWorker(new URL(/* worker import */ __webpack_require__.p + __webpack_require__.u("lib_src_worker_db_WASQLiteDB_worker_js"), __webpack_require__.b), {
7271
7140
  /* @vite-ignore */
7272
7141
  name: `shared-DB-worker-${workerIdentifier}`,
7273
7142
  type: undefined
7274
7143
  }).port
7275
- : new Worker(new URL(/* worker import */ __webpack_require__.p + __webpack_require__.u("_journeyapps_wa-sqlite_src_examples_AccessHandlePoolVFS_js-_journeyapps_wa-sqlite_src_example-97ebe9"), __webpack_require__.b), {
7144
+ : new Worker(new URL(/* worker import */ __webpack_require__.p + __webpack_require__.u("lib_src_worker_db_WASQLiteDB_worker_js"), __webpack_require__.b), {
7276
7145
  /* @vite-ignore */
7277
7146
  name: `DB-worker-${workerIdentifier}`,
7278
7147
  type: undefined
@@ -7471,14 +7340,12 @@ __webpack_require__.r(__webpack_exports__);
7471
7340
  /* harmony export */ SharedSyncImplementation: () => (/* binding */ SharedSyncImplementation)
7472
7341
  /* harmony export */ });
7473
7342
  /* harmony import */ var _powersync_common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @powersync/common */ "@powersync/common");
7474
- /* harmony import */ var async_mutex__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! async-mutex */ "async-mutex");
7475
- /* harmony import */ var comlink__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! comlink */ "comlink");
7476
- /* harmony import */ var _db_sync_WebRemote_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../db/sync/WebRemote.js */ "./lib/src/db/sync/WebRemote.js");
7477
- /* harmony import */ var _db_sync_WebStreamingSyncImplementation_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../db/sync/WebStreamingSyncImplementation.js */ "./lib/src/db/sync/WebStreamingSyncImplementation.js");
7478
- /* harmony import */ var _db_adapters_LockedAsyncDatabaseAdapter_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../db/adapters/LockedAsyncDatabaseAdapter.js */ "./lib/src/db/adapters/LockedAsyncDatabaseAdapter.js");
7479
- /* harmony import */ var _db_adapters_WorkerWrappedAsyncDatabaseConnection_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../../db/adapters/WorkerWrappedAsyncDatabaseConnection.js */ "./lib/src/db/adapters/WorkerWrappedAsyncDatabaseConnection.js");
7480
- /* harmony import */ var _BroadcastLogger_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./BroadcastLogger.js */ "./lib/src/worker/sync/BroadcastLogger.js");
7481
-
7343
+ /* harmony import */ var comlink__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! comlink */ "comlink");
7344
+ /* harmony import */ var _db_sync_WebRemote_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../db/sync/WebRemote.js */ "./lib/src/db/sync/WebRemote.js");
7345
+ /* harmony import */ var _db_sync_WebStreamingSyncImplementation_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../db/sync/WebStreamingSyncImplementation.js */ "./lib/src/db/sync/WebStreamingSyncImplementation.js");
7346
+ /* harmony import */ var _BroadcastLogger_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./BroadcastLogger.js */ "./lib/src/worker/sync/BroadcastLogger.js");
7347
+ /* harmony import */ var _db_adapters_wa_sqlite_DatabaseClient_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../db/adapters/wa-sqlite/DatabaseClient.js */ "./lib/src/db/adapters/wa-sqlite/DatabaseClient.js");
7348
+ /* harmony import */ var _shared_tab_close_signal_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../../shared/tab_close_signal.js */ "./lib/src/shared/tab_close_signal.js");
7482
7349
 
7483
7350
 
7484
7351
 
@@ -7523,14 +7390,14 @@ class SharedSyncImplementation extends _powersync_common__WEBPACK_IMPORTED_MODUL
7523
7390
  connectionManager;
7524
7391
  syncStatus;
7525
7392
  broadCastLogger;
7526
- distributedDB;
7393
+ database = this.generateReconnectableDatabase();
7527
7394
  constructor() {
7528
7395
  super();
7529
7396
  this.ports = [];
7530
7397
  this.syncParams = null;
7531
7398
  this.logger = (0,_powersync_common__WEBPACK_IMPORTED_MODULE_0__.createLogger)('shared-sync');
7532
7399
  this.lastConnectOptions = undefined;
7533
- this.portMutex = new async_mutex__WEBPACK_IMPORTED_MODULE_1__.Mutex();
7400
+ this.portMutex = new _powersync_common__WEBPACK_IMPORTED_MODULE_0__.Mutex();
7534
7401
  this.isInitialized = new Promise((resolve) => {
7535
7402
  const callback = this.registerListener({
7536
7403
  initialized: () => {
@@ -7539,10 +7406,8 @@ class SharedSyncImplementation extends _powersync_common__WEBPACK_IMPORTED_MODUL
7539
7406
  }
7540
7407
  });
7541
7408
  });
7542
- // Should be configured once we get params
7543
- this.distributedDB = null;
7544
7409
  this.syncStatus = new _powersync_common__WEBPACK_IMPORTED_MODULE_0__.SyncStatus({});
7545
- this.broadCastLogger = new _BroadcastLogger_js__WEBPACK_IMPORTED_MODULE_7__.BroadcastLogger(this.ports);
7410
+ this.broadCastLogger = new _BroadcastLogger_js__WEBPACK_IMPORTED_MODULE_4__.BroadcastLogger(this.ports);
7546
7411
  this.connectionManager = new _powersync_common__WEBPACK_IMPORTED_MODULE_0__.ConnectionManager({
7547
7412
  createSyncImplementation: async () => {
7548
7413
  await this.waitForReady();
@@ -7640,38 +7505,8 @@ class SharedSyncImplementation extends _powersync_common__WEBPACK_IMPORTED_MODUL
7640
7505
  if (params.streamOptions?.flags?.broadcastLogs) {
7641
7506
  this.logger = this.broadCastLogger;
7642
7507
  }
7643
- const lockedAdapter = new _db_adapters_LockedAsyncDatabaseAdapter_js__WEBPACK_IMPORTED_MODULE_5__.LockedAsyncDatabaseAdapter({
7644
- name: params.dbParams.dbFilename,
7645
- openConnection: async () => {
7646
- // Gets a connection from the clients when a new connection is requested.
7647
- const db = await this.openInternalDB();
7648
- db.registerListener({
7649
- closing: () => {
7650
- lockedAdapter.reOpenInternalDB();
7651
- }
7652
- });
7653
- return db;
7654
- },
7655
- logger: this.logger,
7656
- reOpenOnConnectionClosed: true
7657
- });
7658
- this.distributedDB = lockedAdapter;
7659
- await lockedAdapter.init();
7660
- lockedAdapter.registerListener({
7661
- databaseReOpened: () => {
7662
- // We may have missed some table updates while the database was closed.
7663
- // We can poke the crud in case we missed any updates.
7664
- this.connectionManager.syncStreamImplementation?.triggerCrudUpload();
7665
- /**
7666
- * FIXME or IMPROVE ME
7667
- * The Rust client implementation stores sync state on the connection level.
7668
- * Reopening the database causes a state machine error which should cause the
7669
- * StreamingSyncImplementation to reconnect. It would be nicer if we could trigger
7670
- * this reconnect earlier.
7671
- * This reconnect is not required for IndexedDB.
7672
- */
7673
- }
7674
- });
7508
+ // Ensure we have a usable database connection, the reconnectable database will connect lazily on first use.
7509
+ await this.database.readLock(async () => { });
7675
7510
  self.onerror = (event) => {
7676
7511
  // Share any uncaught events on the broadcast logger
7677
7512
  this.logger.error('Uncaught exception in PowerSync shared sync worker', event);
@@ -7703,7 +7538,7 @@ class SharedSyncImplementation extends _powersync_common__WEBPACK_IMPORTED_MODUL
7703
7538
  return await this.portMutex.runExclusive(() => {
7704
7539
  const portProvider = {
7705
7540
  port,
7706
- clientProvider: comlink__WEBPACK_IMPORTED_MODULE_2__.wrap(port),
7541
+ clientProvider: comlink__WEBPACK_IMPORTED_MODULE_1__.wrap(port),
7707
7542
  currentSubscriptions: [],
7708
7543
  closeListeners: [],
7709
7544
  isClosing: false
@@ -7750,7 +7585,7 @@ class SharedSyncImplementation extends _powersync_common__WEBPACK_IMPORTED_MODUL
7750
7585
  await closeListener();
7751
7586
  }
7752
7587
  this.collectActiveSubscriptions();
7753
- return () => trackedPort.clientProvider[comlink__WEBPACK_IMPORTED_MODULE_2__.releaseProxy]();
7588
+ return () => trackedPort.clientProvider[comlink__WEBPACK_IMPORTED_MODULE_1__.releaseProxy]();
7754
7589
  });
7755
7590
  }
7756
7591
  triggerCrudUpload() {
@@ -7787,9 +7622,9 @@ class SharedSyncImplementation extends _powersync_common__WEBPACK_IMPORTED_MODUL
7787
7622
  // This should only be called after initialization has completed
7788
7623
  const syncParams = this.syncParams;
7789
7624
  // Create a new StreamingSyncImplementation for each connect call. This is usually done is all SDKs.
7790
- return new _db_sync_WebStreamingSyncImplementation_js__WEBPACK_IMPORTED_MODULE_4__.WebStreamingSyncImplementation({
7791
- adapter: new _powersync_common__WEBPACK_IMPORTED_MODULE_0__.SqliteBucketStorage(this.distributedDB, this.logger),
7792
- remote: new _db_sync_WebRemote_js__WEBPACK_IMPORTED_MODULE_3__.WebRemote({
7625
+ return new _db_sync_WebStreamingSyncImplementation_js__WEBPACK_IMPORTED_MODULE_3__.WebStreamingSyncImplementation({
7626
+ adapter: new _powersync_common__WEBPACK_IMPORTED_MODULE_0__.SqliteBucketStorage(this.database, this.logger),
7627
+ remote: new _db_sync_WebRemote_js__WEBPACK_IMPORTED_MODULE_2__.WebRemote({
7793
7628
  invalidateCredentials: async () => {
7794
7629
  const lastPort = await this.getLastWrappedPort();
7795
7630
  if (!lastPort) {
@@ -7859,9 +7694,9 @@ class SharedSyncImplementation extends _powersync_common__WEBPACK_IMPORTED_MODUL
7859
7694
  });
7860
7695
  }
7861
7696
  /**
7862
- * Opens a worker wrapped database connection. Using the last connected client port.
7697
+ * Requests a random client to share its database connection with us.
7863
7698
  */
7864
- async openInternalDB() {
7699
+ async openInternalDB(handleClosed) {
7865
7700
  const client = await this.getRandomWrappedPort();
7866
7701
  if (!client) {
7867
7702
  // Should not really happen in practice
@@ -7895,8 +7730,9 @@ class SharedSyncImplementation extends _powersync_common__WEBPACK_IMPORTED_MODUL
7895
7730
  removeCloseListener();
7896
7731
  throw ex;
7897
7732
  });
7898
- const remote = comlink__WEBPACK_IMPORTED_MODULE_2__.wrap(workerPort);
7733
+ const remote = comlink__WEBPACK_IMPORTED_MODULE_1__.wrap(workerPort);
7899
7734
  const identifier = this.syncParams.dbParams.dbFilename;
7735
+ const clientLockName = await (0,_shared_tab_close_signal_js__WEBPACK_IMPORTED_MODULE_6__.generateTabCloseSignal)();
7900
7736
  /**
7901
7737
  * The open could fail if the tab is closed while we're busy opening the database.
7902
7738
  * This operation is typically executed inside an exclusive portMutex lock.
@@ -7904,7 +7740,16 @@ class SharedSyncImplementation extends _powersync_common__WEBPACK_IMPORTED_MODUL
7904
7740
  * We can't rely on the closeListeners to abort the operation if the tab is closed.
7905
7741
  */
7906
7742
  const db = await withAbort({
7907
- action: () => remote(this.syncParams.dbParams),
7743
+ action: async () => {
7744
+ const clientView = await remote.connectToExisting({ identifier, lockName: clientLockName });
7745
+ return new _db_adapters_wa_sqlite_DatabaseClient_js__WEBPACK_IMPORTED_MODULE_5__.DatabaseClient({
7746
+ connection: clientView,
7747
+ source: remote,
7748
+ // It's possible for this worker to outlive the client hosting the database for us. We need to be prepared for
7749
+ // that and ensure pending requests are aborted when the tab is closed.
7750
+ remoteCanCloseUnexpectedly: true
7751
+ }, this.syncParams.dbParams);
7752
+ },
7908
7753
  signal: abortController.signal,
7909
7754
  cleanupOnAbort: (db) => {
7910
7755
  db.close();
@@ -7914,25 +7759,86 @@ class SharedSyncImplementation extends _powersync_common__WEBPACK_IMPORTED_MODUL
7914
7759
  removeCloseListener();
7915
7760
  });
7916
7761
  clearTimeout(timeout);
7917
- const wrapped = new _db_adapters_WorkerWrappedAsyncDatabaseConnection_js__WEBPACK_IMPORTED_MODULE_6__.WorkerWrappedAsyncDatabaseConnection({
7918
- remote,
7919
- baseConnection: db,
7920
- identifier,
7921
- // It's possible for this worker to outlive the client hosting the database for us. We need to be prepared for
7922
- // that and ensure pending requests are aborted when the tab is closed.
7923
- remoteCanCloseUnexpectedly: true
7924
- });
7925
7762
  client.closeListeners.push(async () => {
7926
7763
  this.logger.info('Aborting open connection because associated tab closed.');
7764
+ handleClosed(db);
7927
7765
  /**
7928
7766
  * Don't await this close operation. It might never resolve if the tab is closed.
7929
7767
  * We mark the remote as closed first, this will reject any pending requests.
7930
7768
  * We then call close. The close operation is configured to fire-and-forget, the main promise will reject immediately.
7931
7769
  */
7932
- wrapped.markRemoteClosed();
7933
- wrapped.close().catch((ex) => this.logger.warn('error closing database connection', ex));
7770
+ db.markRemoteClosed();
7771
+ db.close().catch((ex) => this.logger.warn('error closing database connection', ex));
7934
7772
  });
7935
- return wrapped;
7773
+ return db;
7774
+ }
7775
+ generateReconnectableDatabase() {
7776
+ const syncParams = this.syncParams;
7777
+ const sharedSync = this;
7778
+ class ReconnectPool extends _powersync_common__WEBPACK_IMPORTED_MODULE_0__.BaseObserver {
7779
+ connectionState = null;
7780
+ get name() {
7781
+ return syncParams?.dbParams.dbFilename;
7782
+ }
7783
+ async connect() {
7784
+ if (this.connectionState == null) {
7785
+ const handleClosed = this.handleClientClosed.bind(this);
7786
+ this.connectionState = (async () => {
7787
+ try {
7788
+ const db = await sharedSync.openInternalDB(handleClosed);
7789
+ db.registerListener({
7790
+ tablesUpdated: (notification) => {
7791
+ this.iterateListeners((l) => l.tablesUpdated?.(notification));
7792
+ }
7793
+ });
7794
+ this.connectionState = db;
7795
+ return db;
7796
+ }
7797
+ catch (e) {
7798
+ // Allow reconnecting when the database is used again.
7799
+ this.connectionState = null;
7800
+ throw e;
7801
+ }
7802
+ })();
7803
+ }
7804
+ return await this.connectionState;
7805
+ }
7806
+ async close() {
7807
+ if (this.connectionState != null) {
7808
+ await (await this.connectionState).close();
7809
+ }
7810
+ }
7811
+ handleClientClosed(client) {
7812
+ if (client === this.connectionState) {
7813
+ this.connectionState = null;
7814
+ // We may have missed some table updates while the database was closed.
7815
+ // We can poke the crud in case we missed any updates.
7816
+ const impl = sharedSync.connectionManager.syncStreamImplementation;
7817
+ impl?.triggerCrudUpload();
7818
+ /**
7819
+ * FIXME or IMPROVE ME
7820
+ * The Rust client implementation stores sync state on the connection level.
7821
+ * Reopening the database causes a state machine error which should cause the
7822
+ * StreamingSyncImplementation to reconnect. It would be nicer if we could trigger
7823
+ * this reconnect earlier.
7824
+ * This reconnect is not required for IndexedDB.
7825
+ */
7826
+ }
7827
+ }
7828
+ async readLock(fn, options) {
7829
+ const db = await this.connect();
7830
+ return db.readLock(fn, options);
7831
+ }
7832
+ async writeLock(fn, options) {
7833
+ const db = await this.connect();
7834
+ return db.writeLock(fn, options);
7835
+ }
7836
+ async refreshSchema() {
7837
+ // Not used by sync client.
7838
+ }
7839
+ }
7840
+ const Adapter = (0,_powersync_common__WEBPACK_IMPORTED_MODULE_0__.DBAdapterDefaultMixin)(ReconnectPool);
7841
+ return new Adapter();
7936
7842
  }
7937
7843
  /**
7938
7844
  * A method to update the all shared statuses for each
@@ -8151,51 +8057,37 @@ var __webpack_exports__ = {};
8151
8057
  __webpack_require__.r(__webpack_exports__);
8152
8058
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
8153
8059
  /* harmony export */ AbstractWebPowerSyncDatabaseOpenFactory: () => (/* reexport safe */ _db_adapters_AbstractWebPowerSyncDatabaseOpenFactory_js__WEBPACK_IMPORTED_MODULE_2__.AbstractWebPowerSyncDatabaseOpenFactory),
8154
- /* harmony export */ AbstractWebSQLOpenFactory: () => (/* reexport safe */ _db_adapters_AbstractWebSQLOpenFactory_js__WEBPACK_IMPORTED_MODULE_3__.AbstractWebSQLOpenFactory),
8155
- /* harmony export */ AsyncWASQLiteModuleFactory: () => (/* reexport safe */ _db_adapters_wa_sqlite_WASQLiteConnection_js__WEBPACK_IMPORTED_MODULE_5__.AsyncWASQLiteModuleFactory),
8156
- /* harmony export */ DEFAULT_CACHE_SIZE_KB: () => (/* reexport safe */ _db_adapters_web_sql_flags_js__WEBPACK_IMPORTED_MODULE_9__.DEFAULT_CACHE_SIZE_KB),
8157
- /* harmony export */ DEFAULT_MODULE_FACTORIES: () => (/* reexport safe */ _db_adapters_wa_sqlite_WASQLiteConnection_js__WEBPACK_IMPORTED_MODULE_5__.DEFAULT_MODULE_FACTORIES),
8158
- /* harmony export */ DEFAULT_POWERSYNC_FLAGS: () => (/* reexport safe */ _db_PowerSyncDatabase_js__WEBPACK_IMPORTED_MODULE_10__.DEFAULT_POWERSYNC_FLAGS),
8159
- /* harmony export */ DEFAULT_WEB_SQL_FLAGS: () => (/* reexport safe */ _db_adapters_web_sql_flags_js__WEBPACK_IMPORTED_MODULE_9__.DEFAULT_WEB_SQL_FLAGS),
8060
+ /* harmony export */ DEFAULT_CACHE_SIZE_KB: () => (/* reexport safe */ _db_adapters_web_sql_flags_js__WEBPACK_IMPORTED_MODULE_6__.DEFAULT_CACHE_SIZE_KB),
8061
+ /* harmony export */ DEFAULT_POWERSYNC_FLAGS: () => (/* reexport safe */ _db_PowerSyncDatabase_js__WEBPACK_IMPORTED_MODULE_7__.DEFAULT_POWERSYNC_FLAGS),
8062
+ /* harmony export */ DEFAULT_WEB_SQL_FLAGS: () => (/* reexport safe */ _db_adapters_web_sql_flags_js__WEBPACK_IMPORTED_MODULE_6__.DEFAULT_WEB_SQL_FLAGS),
8160
8063
  /* harmony export */ IndexDBFileSystemStorageAdapter: () => (/* reexport safe */ _attachments_IndexDBFileSystemAdapter_js__WEBPACK_IMPORTED_MODULE_1__.IndexDBFileSystemStorageAdapter),
8161
- /* harmony export */ MultiCipherAsyncWASQLiteModuleFactory: () => (/* reexport safe */ _db_adapters_wa_sqlite_WASQLiteConnection_js__WEBPACK_IMPORTED_MODULE_5__.MultiCipherAsyncWASQLiteModuleFactory),
8162
- /* harmony export */ MultiCipherSyncWASQLiteModuleFactory: () => (/* reexport safe */ _db_adapters_wa_sqlite_WASQLiteConnection_js__WEBPACK_IMPORTED_MODULE_5__.MultiCipherSyncWASQLiteModuleFactory),
8163
- /* harmony export */ PowerSyncDatabase: () => (/* reexport safe */ _db_PowerSyncDatabase_js__WEBPACK_IMPORTED_MODULE_10__.PowerSyncDatabase),
8164
- /* harmony export */ SharedWebStreamingSyncImplementation: () => (/* reexport safe */ _db_sync_SharedWebStreamingSyncImplementation_js__WEBPACK_IMPORTED_MODULE_11__.SharedWebStreamingSyncImplementation),
8165
- /* harmony export */ SyncWASQLiteModuleFactory: () => (/* reexport safe */ _db_adapters_wa_sqlite_WASQLiteConnection_js__WEBPACK_IMPORTED_MODULE_5__.SyncWASQLiteModuleFactory),
8166
- /* harmony export */ TemporaryStorageOption: () => (/* reexport safe */ _db_adapters_web_sql_flags_js__WEBPACK_IMPORTED_MODULE_9__.TemporaryStorageOption),
8167
- /* harmony export */ WASQLiteDBAdapter: () => (/* reexport safe */ _db_adapters_wa_sqlite_WASQLiteDBAdapter_js__WEBPACK_IMPORTED_MODULE_6__.WASQLiteDBAdapter),
8168
- /* harmony export */ WASQLiteOpenFactory: () => (/* reexport safe */ _db_adapters_wa_sqlite_WASQLiteOpenFactory_js__WEBPACK_IMPORTED_MODULE_7__.WASQLiteOpenFactory),
8169
- /* harmony export */ WASQLitePowerSyncDatabaseOpenFactory: () => (/* reexport safe */ _db_adapters_wa_sqlite_WASQLitePowerSyncDatabaseOpenFactory_js__WEBPACK_IMPORTED_MODULE_8__.WASQLitePowerSyncDatabaseOpenFactory),
8170
- /* harmony export */ WASQLiteVFS: () => (/* reexport safe */ _db_adapters_wa_sqlite_WASQLiteConnection_js__WEBPACK_IMPORTED_MODULE_5__.WASQLiteVFS),
8171
- /* harmony export */ WASqliteConnection: () => (/* reexport safe */ _db_adapters_wa_sqlite_WASQLiteConnection_js__WEBPACK_IMPORTED_MODULE_5__.WASqliteConnection),
8172
- /* harmony export */ WebRemote: () => (/* reexport safe */ _db_sync_WebRemote_js__WEBPACK_IMPORTED_MODULE_12__.WebRemote),
8173
- /* harmony export */ WebStreamingSyncImplementation: () => (/* reexport safe */ _db_sync_WebStreamingSyncImplementation_js__WEBPACK_IMPORTED_MODULE_13__.WebStreamingSyncImplementation),
8174
- /* harmony export */ isServerSide: () => (/* reexport safe */ _db_adapters_web_sql_flags_js__WEBPACK_IMPORTED_MODULE_9__.isServerSide),
8175
- /* harmony export */ resolveWebPowerSyncFlags: () => (/* reexport safe */ _db_PowerSyncDatabase_js__WEBPACK_IMPORTED_MODULE_10__.resolveWebPowerSyncFlags),
8176
- /* harmony export */ resolveWebSQLFlags: () => (/* reexport safe */ _db_adapters_web_sql_flags_js__WEBPACK_IMPORTED_MODULE_9__.resolveWebSQLFlags)
8064
+ /* harmony export */ PowerSyncDatabase: () => (/* reexport safe */ _db_PowerSyncDatabase_js__WEBPACK_IMPORTED_MODULE_7__.PowerSyncDatabase),
8065
+ /* harmony export */ SharedWebStreamingSyncImplementation: () => (/* reexport safe */ _db_sync_SharedWebStreamingSyncImplementation_js__WEBPACK_IMPORTED_MODULE_8__.SharedWebStreamingSyncImplementation),
8066
+ /* harmony export */ TemporaryStorageOption: () => (/* reexport safe */ _db_adapters_web_sql_flags_js__WEBPACK_IMPORTED_MODULE_6__.TemporaryStorageOption),
8067
+ /* harmony export */ WASQLiteOpenFactory: () => (/* reexport safe */ _db_adapters_wa_sqlite_WASQLiteOpenFactory_js__WEBPACK_IMPORTED_MODULE_4__.WASQLiteOpenFactory),
8068
+ /* harmony export */ WASQLitePowerSyncDatabaseOpenFactory: () => (/* reexport safe */ _db_adapters_wa_sqlite_WASQLitePowerSyncDatabaseOpenFactory_js__WEBPACK_IMPORTED_MODULE_5__.WASQLitePowerSyncDatabaseOpenFactory),
8069
+ /* harmony export */ WASQLiteVFS: () => (/* reexport safe */ _db_adapters_wa_sqlite_vfs_js__WEBPACK_IMPORTED_MODULE_3__.WASQLiteVFS),
8070
+ /* harmony export */ WebRemote: () => (/* reexport safe */ _db_sync_WebRemote_js__WEBPACK_IMPORTED_MODULE_9__.WebRemote),
8071
+ /* harmony export */ WebStreamingSyncImplementation: () => (/* reexport safe */ _db_sync_WebStreamingSyncImplementation_js__WEBPACK_IMPORTED_MODULE_10__.WebStreamingSyncImplementation),
8072
+ /* harmony export */ isServerSide: () => (/* reexport safe */ _db_adapters_web_sql_flags_js__WEBPACK_IMPORTED_MODULE_6__.isServerSide),
8073
+ /* harmony export */ resolveWebPowerSyncFlags: () => (/* reexport safe */ _db_PowerSyncDatabase_js__WEBPACK_IMPORTED_MODULE_7__.resolveWebPowerSyncFlags),
8074
+ /* harmony export */ resolveWebSQLFlags: () => (/* reexport safe */ _db_adapters_web_sql_flags_js__WEBPACK_IMPORTED_MODULE_6__.resolveWebSQLFlags)
8177
8075
  /* harmony export */ });
8178
8076
  /* harmony import */ var _powersync_common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @powersync/common */ "@powersync/common");
8179
8077
  /* harmony reexport (unknown) */ var __WEBPACK_REEXPORT_OBJECT__ = {};
8180
- /* harmony reexport (unknown) */ for(const __WEBPACK_IMPORT_KEY__ in _powersync_common__WEBPACK_IMPORTED_MODULE_0__) if(__WEBPACK_IMPORT_KEY__ !== "default") __WEBPACK_REEXPORT_OBJECT__[__WEBPACK_IMPORT_KEY__] = () => _powersync_common__WEBPACK_IMPORTED_MODULE_0__[__WEBPACK_IMPORT_KEY__]
8078
+ /* harmony reexport (unknown) */ for(const __WEBPACK_IMPORT_KEY__ in _powersync_common__WEBPACK_IMPORTED_MODULE_0__) if(["default","WASQLiteVFS"].indexOf(__WEBPACK_IMPORT_KEY__) < 0) __WEBPACK_REEXPORT_OBJECT__[__WEBPACK_IMPORT_KEY__] = () => _powersync_common__WEBPACK_IMPORTED_MODULE_0__[__WEBPACK_IMPORT_KEY__]
8181
8079
  /* harmony reexport (unknown) */ __webpack_require__.d(__webpack_exports__, __WEBPACK_REEXPORT_OBJECT__);
8182
8080
  /* harmony import */ var _attachments_IndexDBFileSystemAdapter_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./attachments/IndexDBFileSystemAdapter.js */ "./lib/src/attachments/IndexDBFileSystemAdapter.js");
8183
8081
  /* harmony import */ var _db_adapters_AbstractWebPowerSyncDatabaseOpenFactory_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./db/adapters/AbstractWebPowerSyncDatabaseOpenFactory.js */ "./lib/src/db/adapters/AbstractWebPowerSyncDatabaseOpenFactory.js");
8184
- /* harmony import */ var _db_adapters_AbstractWebSQLOpenFactory_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./db/adapters/AbstractWebSQLOpenFactory.js */ "./lib/src/db/adapters/AbstractWebSQLOpenFactory.js");
8185
- /* harmony import */ var _db_adapters_AsyncDatabaseConnection_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./db/adapters/AsyncDatabaseConnection.js */ "./lib/src/db/adapters/AsyncDatabaseConnection.js");
8186
- /* harmony import */ var _db_adapters_wa_sqlite_WASQLiteConnection_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./db/adapters/wa-sqlite/WASQLiteConnection.js */ "./lib/src/db/adapters/wa-sqlite/WASQLiteConnection.js");
8187
- /* harmony import */ var _db_adapters_wa_sqlite_WASQLiteDBAdapter_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./db/adapters/wa-sqlite/WASQLiteDBAdapter.js */ "./lib/src/db/adapters/wa-sqlite/WASQLiteDBAdapter.js");
8188
- /* harmony import */ var _db_adapters_wa_sqlite_WASQLiteOpenFactory_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./db/adapters/wa-sqlite/WASQLiteOpenFactory.js */ "./lib/src/db/adapters/wa-sqlite/WASQLiteOpenFactory.js");
8189
- /* harmony import */ var _db_adapters_wa_sqlite_WASQLitePowerSyncDatabaseOpenFactory_js__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./db/adapters/wa-sqlite/WASQLitePowerSyncDatabaseOpenFactory.js */ "./lib/src/db/adapters/wa-sqlite/WASQLitePowerSyncDatabaseOpenFactory.js");
8190
- /* harmony import */ var _db_adapters_web_sql_flags_js__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./db/adapters/web-sql-flags.js */ "./lib/src/db/adapters/web-sql-flags.js");
8191
- /* harmony import */ var _db_PowerSyncDatabase_js__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./db/PowerSyncDatabase.js */ "./lib/src/db/PowerSyncDatabase.js");
8192
- /* harmony import */ var _db_sync_SharedWebStreamingSyncImplementation_js__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./db/sync/SharedWebStreamingSyncImplementation.js */ "./lib/src/db/sync/SharedWebStreamingSyncImplementation.js");
8193
- /* harmony import */ var _db_sync_WebRemote_js__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./db/sync/WebRemote.js */ "./lib/src/db/sync/WebRemote.js");
8194
- /* harmony import */ var _db_sync_WebStreamingSyncImplementation_js__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./db/sync/WebStreamingSyncImplementation.js */ "./lib/src/db/sync/WebStreamingSyncImplementation.js");
8195
- /* harmony import */ var _db_adapters_WebDBAdapter_js__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./db/adapters/WebDBAdapter.js */ "./lib/src/db/adapters/WebDBAdapter.js");
8196
-
8197
-
8198
-
8082
+ /* harmony import */ var _db_adapters_wa_sqlite_vfs_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./db/adapters/wa-sqlite/vfs.js */ "./lib/src/db/adapters/wa-sqlite/vfs.js");
8083
+ /* harmony import */ var _db_adapters_wa_sqlite_WASQLiteOpenFactory_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./db/adapters/wa-sqlite/WASQLiteOpenFactory.js */ "./lib/src/db/adapters/wa-sqlite/WASQLiteOpenFactory.js");
8084
+ /* harmony import */ var _db_adapters_wa_sqlite_WASQLitePowerSyncDatabaseOpenFactory_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./db/adapters/wa-sqlite/WASQLitePowerSyncDatabaseOpenFactory.js */ "./lib/src/db/adapters/wa-sqlite/WASQLitePowerSyncDatabaseOpenFactory.js");
8085
+ /* harmony import */ var _db_adapters_web_sql_flags_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./db/adapters/web-sql-flags.js */ "./lib/src/db/adapters/web-sql-flags.js");
8086
+ /* harmony import */ var _db_PowerSyncDatabase_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./db/PowerSyncDatabase.js */ "./lib/src/db/PowerSyncDatabase.js");
8087
+ /* harmony import */ var _db_sync_SharedWebStreamingSyncImplementation_js__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./db/sync/SharedWebStreamingSyncImplementation.js */ "./lib/src/db/sync/SharedWebStreamingSyncImplementation.js");
8088
+ /* harmony import */ var _db_sync_WebRemote_js__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./db/sync/WebRemote.js */ "./lib/src/db/sync/WebRemote.js");
8089
+ /* harmony import */ var _db_sync_WebStreamingSyncImplementation_js__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./db/sync/WebStreamingSyncImplementation.js */ "./lib/src/db/sync/WebStreamingSyncImplementation.js");
8090
+ /* harmony import */ var _db_adapters_WebDBAdapter_js__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./db/adapters/WebDBAdapter.js */ "./lib/src/db/adapters/WebDBAdapter.js");
8199
8091
 
8200
8092
 
8201
8093