@powersync/web 1.35.0 → 1.37.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/dist/index.umd.js +1126 -1231
  2. package/dist/index.umd.js.map +1 -1
  3. package/dist/worker/SharedSyncImplementation.umd.js +599 -3086
  4. package/dist/worker/SharedSyncImplementation.umd.js.map +1 -1
  5. package/dist/worker/WASQLiteDB.umd.js +860 -868
  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 -1878
  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 -401
  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 -490
  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,482 +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))
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;
5347
5235
  });
5348
- this.initPromise = this._init();
5349
- }
5350
- get baseDB() {
5351
- if (!this._db) {
5352
- throw new Error(`Initialization has not completed yet. Cannot access base db`);
5353
- }
5354
- return this._db;
5355
- }
5356
- get name() {
5357
- return this._dbIdentifier;
5358
5236
  }
5359
- /**
5360
- * Init is automatic, this helps catch errors or explicitly await initialization
5361
- */
5362
5237
  async init() {
5363
- return this.initPromise;
5364
- }
5365
- async openInternalDB() {
5366
- /**
5367
- * Execute opening of the db in a lock in order not to interfere with other operations.
5368
- */
5369
- return this._acquireLock(async () => {
5370
- // Dispose any previous table change listener.
5371
- this._disposeTableChangeListener?.();
5372
- this._disposeTableChangeListener = null;
5373
- this._db?.close().catch((ex) => this.logger.warn(`Error closing database before opening new instance`, ex));
5374
- const isReOpen = !!this._db;
5375
- this._db = null;
5376
- this._db = await this.options.openConnection();
5377
- await this._db.init();
5378
- this._config = await this._db.getConfig();
5379
- await this.registerOnChangeListener(this._db);
5380
- if (isReOpen) {
5381
- this.iterateListeners((cb) => cb.databaseReOpened?.());
5382
- }
5383
- /**
5384
- * This is only required for the long-lived shared IndexedDB connections.
5385
- */
5386
- this.requiresHolds = this._config.vfs == _wa_sqlite_WASQLiteConnection_js__WEBPACK_IMPORTED_MODULE_3__.WASQLiteVFS.IDBBatchAtomicVFS;
5387
- });
5388
- }
5389
- _reOpen() {
5390
- this.databaseOpenPromise = this.openInternalDB().finally(() => {
5391
- this.databaseOpenPromise = null;
5392
- });
5393
- return this.databaseOpenPromise;
5394
- }
5395
- /**
5396
- * Re-opens the underlying database.
5397
- * Returns a pending operation if one is already in progress.
5398
- */
5399
- async reOpenInternalDB() {
5400
- if (this.closing || !this.options.reOpenOnConnectionClosed) {
5401
- // No-op
5402
- return;
5403
- }
5404
- else if (this.databaseOpenPromise) {
5405
- // Already busy opening
5406
- return this.databaseOpenPromise;
5407
- }
5408
- else {
5409
- return this._reOpen();
5410
- }
5411
- }
5412
- async _init() {
5413
- /**
5414
- * For OPFS, we can see this open call sometimes fail due to NoModificationAllowedError.
5415
- * We should be able to recover from this by re-opening the database.
5416
- */
5417
- const maxAttempts = 3;
5418
- for (let count = 0; count < maxAttempts; count++) {
5419
- try {
5420
- await this.openInternalDB();
5421
- break;
5422
- }
5423
- catch (ex) {
5424
- if (count == maxAttempts - 1) {
5425
- throw ex;
5426
- }
5427
- this.logger.warn(`Attempt ${count + 1} of ${maxAttempts} to open database failed, retrying in 1 second...`, ex);
5428
- await new Promise((resolve) => setTimeout(resolve, 1000));
5429
- }
5430
- }
5431
- this.iterateListeners((cb) => cb.initialized?.());
5432
- }
5433
- getConfiguration() {
5434
- if (!this._config) {
5435
- throw new Error(`Cannot get config before initialization is completed`);
5436
- }
5437
- return {
5438
- ...this._config,
5439
- // This can be overridden by the adapter later
5440
- requiresPersistentTriggers: false
5441
- };
5442
- }
5443
- async waitForInitialized() {
5444
- // Awaiting this will expose errors on function calls like .execute etc
5445
- await this.initPromise;
5446
- }
5447
- async shareConnection() {
5448
- if (false == this._db instanceof _WorkerWrappedAsyncDatabaseConnection_js__WEBPACK_IMPORTED_MODULE_2__.WorkerWrappedAsyncDatabaseConnection) {
5449
- throw new Error(`Only worker connections can be shared`);
5450
- }
5451
- return this._db.shareConnection();
5452
- }
5453
- /**
5454
- * Registers a table change notification callback with the base database.
5455
- * This can be extended by custom implementations in order to handle proxy events.
5456
- */
5457
- async registerOnChangeListener(db) {
5458
- this._disposeTableChangeListener = await db.registerOnTableChange((event) => {
5459
- this.iterateListeners((cb) => cb.tablesUpdated?.(event));
5460
- });
5461
- }
5462
- /**
5463
- * This is currently a no-op on web
5464
- */
5465
- async refreshSchema() { }
5466
- async execute(query, params) {
5467
- return this.writeLock((ctx) => ctx.execute(query, params));
5468
- }
5469
- async executeRaw(query, params) {
5470
- return this.writeLock((ctx) => ctx.executeRaw(query, params));
5238
+ await this.inner;
5471
5239
  }
5472
- async executeBatch(query, params) {
5473
- return this.writeLock((ctx) => this._executeBatch(query, params));
5474
- }
5475
- /**
5476
- * Attempts to close the connection.
5477
- * Shared workers might not actually close the connection if other
5478
- * tabs are still using it.
5479
- */
5480
5240
  async close() {
5481
- this.closing = true;
5482
- /**
5483
- * Note that we obtain a reference to the callback to avoid calling the callback with `this` as the context.
5484
- * This is to avoid Comlink attempting to clone `this` when calling the method.
5485
- */
5486
- const dispose = this._disposeTableChangeListener;
5487
- if (dispose) {
5488
- dispose();
5489
- }
5490
- this.pendingAbortControllers.forEach((controller) => controller.abort('Closed'));
5491
- await this.baseDB?.close?.();
5492
- this.closed = true;
5493
- }
5494
- async getAll(sql, parameters) {
5495
- await this.waitForInitialized();
5496
- return this.dbGetHelpers.getAll(sql, parameters);
5497
- }
5498
- async getOptional(sql, parameters) {
5499
- await this.waitForInitialized();
5500
- return this.dbGetHelpers.getOptional(sql, parameters);
5501
- }
5502
- async get(sql, parameters) {
5503
- await this.waitForInitialized();
5504
- return this.dbGetHelpers.get(sql, parameters);
5241
+ const inner = await this.inner;
5242
+ return await inner.close();
5505
5243
  }
5506
5244
  async readLock(fn, options) {
5507
- await this.waitForInitialized();
5508
- return this.acquireLock(async () => fn(this.generateDBHelpers({ execute: this._execute, executeRaw: this._executeRaw })), {
5509
- timeoutMs: options?.timeoutMs ?? this.options.defaultLockTimeoutMs
5510
- });
5245
+ const inner = await this.inner;
5246
+ return await inner.readLock(fn, options);
5511
5247
  }
5512
5248
  async writeLock(fn, options) {
5513
- await this.waitForInitialized();
5514
- return this.acquireLock(async () => fn(this.generateDBHelpers({ execute: this._execute, executeRaw: this._executeRaw })), {
5515
- timeoutMs: options?.timeoutMs ?? this.options.defaultLockTimeoutMs
5516
- });
5249
+ const inner = await this.inner;
5250
+ return await inner.writeLock(fn, options);
5517
5251
  }
5518
- async _acquireLock(callback, options) {
5519
- if (this.closing) {
5520
- throw new Error(`Cannot acquire lock, the database is closing`);
5521
- }
5522
- const abortController = new AbortController();
5523
- this.pendingAbortControllers.add(abortController);
5524
- const { timeoutMs } = options ?? {};
5525
- const timeoutId = timeoutMs
5526
- ? setTimeout(() => {
5527
- abortController.abort(`Timeout after ${timeoutMs}ms`);
5528
- this.pendingAbortControllers.delete(abortController);
5529
- }, timeoutMs)
5530
- : null;
5531
- return (0,_shared_navigator_js__WEBPACK_IMPORTED_MODULE_1__.getNavigatorLocks)().request(`db-lock-${this._dbIdentifier}`, { signal: abortController.signal }, async () => {
5532
- this.pendingAbortControllers.delete(abortController);
5533
- if (timeoutId) {
5534
- clearTimeout(timeoutId);
5535
- }
5536
- return await callback();
5537
- });
5252
+ async refreshSchema() {
5253
+ await (await this.inner).refreshSchema();
5538
5254
  }
5539
- async acquireLock(callback, options) {
5540
- await this.waitForInitialized();
5541
- // The database is being (re)opened in the background. Wait for it here.
5542
- if (this.databaseOpenPromise) {
5543
- await this.databaseOpenPromise;
5544
- }
5545
- else if (!this._db) {
5546
- /**
5547
- * The database is not open anymore, we might need to re-open it.
5548
- * Typically, _db, can be `null` if we tried to reOpen the database, but failed to succeed in re-opening.
5549
- * This can happen when disconnecting the client.
5550
- * Note: It is safe to re-enter this method multiple times.
5551
- */
5552
- await this.reOpenInternalDB();
5255
+ registerListener(listener) {
5256
+ if (this.resolvedClient) {
5257
+ return this.resolvedClient.registerListener(listener);
5553
5258
  }
5554
- return this._acquireLock(async () => {
5555
- let holdId = null;
5556
- try {
5557
- /**
5558
- * We can't await this since it uses the same lock as we're in now.
5559
- * If there is a pending open, this call will throw.
5560
- * If there is no pending open, but there is also no database - the open
5561
- * might have failed. We need to re-open the database.
5562
- */
5563
- if (this.databaseOpenPromise || !this._db) {
5564
- throw new _powersync_common__WEBPACK_IMPORTED_MODULE_0__.ConnectionClosedError('Connection is busy re-opening');
5565
- }
5566
- holdId = this.requiresHolds ? await this.baseDB.markHold() : null;
5567
- return await callback();
5568
- }
5569
- catch (ex) {
5570
- if (_powersync_common__WEBPACK_IMPORTED_MODULE_0__.ConnectionClosedError.MATCHES(ex)) {
5571
- // Immediately re-open the database. We need to miss as little table updates as possible.
5572
- // Note, don't await this since it uses the same lock as we're in now.
5573
- this.reOpenInternalDB();
5259
+ else {
5260
+ const pending = { listener };
5261
+ this.pendingListeners.add(pending);
5262
+ return () => {
5263
+ if (pending.closeAfterRegisteredOnResolvedPool) {
5264
+ return pending.closeAfterRegisteredOnResolvedPool();
5574
5265
  }
5575
- throw ex;
5576
- }
5577
- finally {
5578
- if (holdId) {
5579
- 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);
5580
5269
  }
5581
- }
5582
- }, options);
5583
- }
5584
- async readTransaction(fn, options) {
5585
- return this.readLock(this.wrapTransaction(fn));
5586
- }
5587
- writeTransaction(fn, options) {
5588
- return this.writeLock(this.wrapTransaction(fn, true));
5270
+ };
5271
+ }
5589
5272
  }
5590
- generateDBHelpers(tx) {
5591
- return {
5592
- ...tx,
5593
- /**
5594
- * Execute a read-only query and return results
5595
- */
5596
- async getAll(sql, parameters) {
5597
- const res = await tx.execute(sql, parameters);
5598
- return res.rows?._array ?? [];
5599
- },
5600
- /**
5601
- * Execute a read-only query and return the first result, or null if the ResultSet is empty.
5602
- */
5603
- async getOptional(sql, parameters) {
5604
- const res = await tx.execute(sql, parameters);
5605
- return res.rows?.item(0) ?? null;
5606
- },
5607
- /**
5608
- * Execute a read-only query and return the first result, error if the ResultSet is empty.
5609
- */
5610
- async get(sql, parameters) {
5611
- const res = await tx.execute(sql, parameters);
5612
- const first = res.rows?.item(0);
5613
- if (!first) {
5614
- throw new Error('Result set is empty');
5615
- }
5616
- return first;
5617
- }
5618
- };
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();
5619
5278
  }
5620
- /**
5621
- * Wraps a lock context into a transaction context
5622
- */
5623
- wrapTransaction(cb, write = false) {
5624
- return async (tx) => {
5625
- await this._execute(write ? 'BEGIN EXCLUSIVE' : 'BEGIN');
5626
- let finalized = false;
5627
- const commit = async () => {
5628
- if (finalized) {
5629
- return { rowsAffected: 0 };
5630
- }
5631
- finalized = true;
5632
- return this._execute('COMMIT');
5633
- };
5634
- const rollback = () => {
5635
- finalized = true;
5636
- return this._execute('ROLLBACK');
5637
- };
5638
- try {
5639
- const result = await cb({
5640
- ...tx,
5641
- commit,
5642
- rollback
5643
- });
5644
- if (!finalized) {
5645
- await commit();
5646
- }
5647
- return result;
5648
- }
5649
- catch (ex) {
5650
- this.logger.debug('Caught ex in transaction', ex);
5651
- try {
5652
- await rollback();
5653
- }
5654
- catch (ex2) {
5655
- // In rare cases, a rollback may fail.
5656
- // Safe to ignore.
5657
- }
5658
- throw ex;
5659
- }
5660
- };
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.');
5661
5284
  }
5662
- /**
5663
- * Wraps the worker execute function, awaiting for it to be available
5664
- */
5665
- _execute = async (sql, bindings) => {
5666
- await this.waitForInitialized();
5667
- const result = await this.baseDB.execute(sql, bindings);
5668
- return {
5669
- ...result,
5670
- rows: {
5671
- ...result.rows,
5672
- item: (idx) => result.rows._array[idx]
5673
- }
5674
- };
5675
- };
5676
- /**
5677
- * Wraps the worker executeRaw function, awaiting for it to be available
5678
- */
5679
- _executeRaw = async (sql, bindings) => {
5680
- await this.waitForInitialized();
5681
- return await this.baseDB.executeRaw(sql, bindings);
5682
- };
5683
- /**
5684
- * Wraps the worker executeBatch function, awaiting for it to be available
5685
- */
5686
- _executeBatch = async (query, params) => {
5687
- await this.waitForInitialized();
5688
- const result = await this.baseDB.executeBatch(query, params);
5689
- return {
5690
- ...result,
5691
- rows: undefined
5692
- };
5693
- };
5694
5285
  }
5695
5286
 
5696
5287
 
@@ -5707,8 +5298,6 @@ __webpack_require__.r(__webpack_exports__);
5707
5298
  /* harmony export */ SSRDBAdapter: () => (/* binding */ SSRDBAdapter)
5708
5299
  /* harmony export */ });
5709
5300
  /* harmony import */ var _powersync_common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @powersync/common */ "@powersync/common");
5710
- /* harmony import */ var async_mutex__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! async-mutex */ "async-mutex");
5711
-
5712
5301
 
5713
5302
  const MOCK_QUERY_RESPONSE = {
5714
5303
  rowsAffected: 0
@@ -5725,21 +5314,21 @@ class SSRDBAdapter extends _powersync_common__WEBPACK_IMPORTED_MODULE_0__.BaseOb
5725
5314
  constructor() {
5726
5315
  super();
5727
5316
  this.name = 'SSR DB';
5728
- this.readMutex = new async_mutex__WEBPACK_IMPORTED_MODULE_1__.Mutex();
5729
- 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();
5730
5319
  }
5731
5320
  close() { }
5732
5321
  async readLock(fn, options) {
5733
- return this.readMutex.runExclusive(() => fn(this));
5322
+ return this.readMutex.runExclusive(() => fn(this), (0,_powersync_common__WEBPACK_IMPORTED_MODULE_0__.timeoutSignal)(options?.timeoutMs));
5734
5323
  }
5735
5324
  async readTransaction(fn, options) {
5736
- return this.readLock(() => fn(this.generateMockTransactionContext()));
5325
+ return this.readLock(() => fn(this.generateMockTransactionContext()), options);
5737
5326
  }
5738
5327
  async writeLock(fn, options) {
5739
- return this.writeMutex.runExclusive(() => fn(this));
5328
+ return this.writeMutex.runExclusive(() => fn(this), (0,_powersync_common__WEBPACK_IMPORTED_MODULE_0__.timeoutSignal)(options?.timeoutMs));
5740
5329
  }
5741
5330
  async writeTransaction(fn, options) {
5742
- return this.writeLock(() => fn(this.generateMockTransactionContext()));
5331
+ return this.writeLock(() => fn(this.generateMockTransactionContext()), options);
5743
5332
  }
5744
5333
  async execute(query, params) {
5745
5334
  return this.writeMutex.runExclusive(async () => MOCK_QUERY_RESPONSE);
@@ -5793,116 +5382,256 @@ __webpack_require__.r(__webpack_exports__);
5793
5382
 
5794
5383
  /***/ },
5795
5384
 
5796
- /***/ "./lib/src/db/adapters/WorkerWrappedAsyncDatabaseConnection.js"
5797
- /*!*********************************************************************!*\
5798
- !*** ./lib/src/db/adapters/WorkerWrappedAsyncDatabaseConnection.js ***!
5799
- \*********************************************************************/
5385
+ /***/ "./lib/src/db/adapters/wa-sqlite/ConcurrentConnection.js"
5386
+ /*!***************************************************************!*\
5387
+ !*** ./lib/src/db/adapters/wa-sqlite/ConcurrentConnection.js ***!
5388
+ \***************************************************************/
5800
5389
  (__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) {
5801
5390
 
5802
5391
  __webpack_require__.r(__webpack_exports__);
5803
5392
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
5804
- /* harmony export */ WorkerWrappedAsyncDatabaseConnection: () => (/* binding */ WorkerWrappedAsyncDatabaseConnection)
5393
+ /* harmony export */ ConcurrentSqliteConnection: () => (/* binding */ ConcurrentSqliteConnection),
5394
+ /* harmony export */ ConnectionLeaseToken: () => (/* binding */ ConnectionLeaseToken)
5805
5395
  /* harmony export */ });
5806
5396
  /* harmony import */ var _powersync_common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @powersync/common */ "@powersync/common");
5807
- /* harmony import */ var comlink__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! comlink */ "comlink");
5808
-
5809
5397
 
5810
5398
  /**
5811
- * Wraps a provided instance of {@link AsyncDatabaseConnection}, providing necessary proxy
5812
- * 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.
5813
5406
  */
5814
- class WorkerWrappedAsyncDatabaseConnection extends _powersync_common__WEBPACK_IMPORTED_MODULE_0__.BaseObserver {
5815
- options;
5816
- lockAbortController = new AbortController();
5817
- notifyRemoteClosed;
5818
- constructor(options) {
5819
- super();
5820
- this.options = options;
5821
- if (options.remoteCanCloseUnexpectedly) {
5822
- this.notifyRemoteClosed = new AbortController();
5823
- }
5824
- }
5825
- get baseConnection() {
5826
- return this.options.baseConnection;
5827
- }
5828
- init() {
5829
- return this.baseConnection.init();
5830
- }
5407
+ class ConcurrentSqliteConnection {
5408
+ inner;
5831
5409
  /**
5832
- * Marks the remote as closed.
5410
+ * An outer mutex ensuring at most one {@link ConnectionLeaseToken} can exist for this connection at a time.
5833
5411
  *
5834
- * This can sometimes happen outside of our control, e.g. when a shared worker requests a connection from a tab. When
5835
- * it happens, all methods on the {@link baseConnection} would never resolve. To avoid livelocks in this scenario, we
5836
- * throw on all outstanding promises and forbid new calls.
5412
+ * If null, we'll use navigator locks instead.
5837
5413
  */
5838
- markRemoteClosed() {
5839
- // Can non-null assert here because this function is only supposed to be called when remoteCanCloseUnexpectedly was
5840
- // set.
5841
- 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();
5842
5425
  }
5843
- markHold() {
5844
- return this.withRemote(() => this.baseConnection.markHold());
5426
+ get options() {
5427
+ return this.inner.options;
5845
5428
  }
5846
- releaseHold(holdId) {
5847
- return this.withRemote(() => this.baseConnection.releaseHold(holdId));
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
- isAutoCommit() {
5850
- return this.withRemote(() => this.baseConnection.isAutoCommit());
5851
- }
5852
- withRemote(workerPromise, fireActionOnAbort = false) {
5853
- const controller = this.notifyRemoteClosed;
5854
- if (controller) {
5855
- return new Promise((resolve, reject) => {
5856
- if (controller.signal.aborted) {
5857
- reject(new _powersync_common__WEBPACK_IMPORTED_MODULE_0__.ConnectionClosedError('Called operation on closed remote'));
5858
- if (!fireActionOnAbort) {
5859
- // Don't run the operation if we're going to reject
5860
- // We might want to fire-and-forget the operation in some cases (like a close operation)
5861
- return;
5862
- }
5863
- }
5864
- function handleAbort() {
5865
- reject(new _powersync_common__WEBPACK_IMPORTED_MODULE_0__.ConnectionClosedError('Remote peer closed with request in flight'));
5866
- }
5867
- function completePromise(action) {
5868
- controller.signal.removeEventListener('abort', handleAbort);
5869
- action();
5870
- }
5871
- controller.signal.addEventListener('abort', handleAbort);
5872
- workerPromise()
5873
- .then((data) => completePromise(() => resolve(data)))
5874
- .catch((e) => completePromise(() => reject(e)));
5875
- });
5446
+ // Unsafe, unguarded access to the SQLite connection.
5447
+ unsafeUseInner() {
5448
+ return this.inner;
5449
+ }
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
+ }
5876
5465
  }
5877
- else {
5878
- // Can't close, so just return the inner worker promise unguarded.
5879
- 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();
5880
5479
  }
5881
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
+ }
5882
5495
  /**
5883
- * 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.
5884
5497
  */
5885
- async shareConnection() {
5886
- const { identifier, remote } = this.options;
5887
- /**
5888
- * Hold a navigator lock in order to avoid features such as Chrome's frozen tabs,
5889
- * or Edge's sleeping tabs from pausing the thread for this connection.
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.
5890
5614
  * This promise resolves once a lock is obtained.
5891
5615
  * This lock will be held as long as this connection is open.
5892
5616
  * The `shareConnection` method should not be called on multiple tabs concurrently.
5893
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
+ }
5894
5623
  await new Promise((resolve, reject) => navigator.locks
5895
- .request(`shared-connection-${this.options.identifier}-${Date.now()}-${Math.round(Math.random() * 10000)}`, {
5896
- signal: this.lockAbortController.signal
5624
+ .request(`shared-connection-${this.name}-${Date.now()}-${Math.round(Math.random() * 10000)}`, {
5625
+ signal: abort.signal
5897
5626
  }, async () => {
5898
5627
  resolve();
5899
5628
  // Free the lock when the connection is already closed.
5900
- if (this.lockAbortController.signal.aborted) {
5629
+ if (abort.signal.aborted) {
5901
5630
  return;
5902
5631
  }
5903
5632
  // Hold the lock while the shared connection is in use.
5904
5633
  await new Promise((releaseLock) => {
5905
- this.lockAbortController.signal.addEventListener('abort', () => {
5634
+ abort.signal.addEventListener('abort', () => {
5906
5635
  releaseLock();
5907
5636
  });
5908
5637
  });
@@ -5916,280 +5645,335 @@ class WorkerWrappedAsyncDatabaseConnection extends _powersync_common__WEBPACK_IM
5916
5645
  reject(ex);
5917
5646
  }
5918
5647
  }));
5919
- const newPort = await remote[comlink__WEBPACK_IMPORTED_MODULE_1__.createEndpoint]();
5920
- 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;
5921
5667
  }
5922
5668
  /**
5923
- * Registers a table change notification callback with the base database.
5924
- * 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.
5925
5670
  */
5926
- async registerOnTableChange(callback) {
5927
- return this.baseConnection.registerOnTableChange(comlink__WEBPACK_IMPORTED_MODULE_1__.proxy(callback));
5928
- }
5929
- async close() {
5930
- // Abort any pending lock requests.
5931
- this.lockAbortController.abort();
5932
- try {
5933
- // fire and forget the close operation
5934
- 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
+ }
5935
5684
  }
5936
- finally {
5937
- this.options.remote[comlink__WEBPACK_IMPORTED_MODULE_1__.releaseProxy]();
5938
- this.options.onClose?.();
5939
- 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
+ };
5940
5705
  }
5706
+ return {
5707
+ rowsAffected: rs.changes,
5708
+ insertId: rs.lastInsertRowId,
5709
+ rows
5710
+ };
5941
5711
  }
5942
- execute(sql, params) {
5943
- 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 ?? [];
5944
5715
  }
5945
- executeRaw(sql, params) {
5946
- 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);
5947
5718
  }
5948
- executeBatch(sql, params) {
5949
- 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
+ });
5950
5755
  }
5951
- getConfig() {
5952
- 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);
5953
5759
  }
5954
5760
  }
5955
5761
 
5956
5762
 
5957
5763
  /***/ },
5958
5764
 
5959
- /***/ "./lib/src/db/adapters/wa-sqlite/InternalWASQLiteDBAdapter.js"
5960
- /*!********************************************************************!*\
5961
- !*** ./lib/src/db/adapters/wa-sqlite/InternalWASQLiteDBAdapter.js ***!
5962
- \********************************************************************/
5765
+ /***/ "./lib/src/db/adapters/wa-sqlite/DatabaseServer.js"
5766
+ /*!*********************************************************!*\
5767
+ !*** ./lib/src/db/adapters/wa-sqlite/DatabaseServer.js ***!
5768
+ \*********************************************************/
5963
5769
  (__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) {
5964
5770
 
5965
5771
  __webpack_require__.r(__webpack_exports__);
5966
5772
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
5967
- /* harmony export */ InternalWASQLiteDBAdapter: () => (/* binding */ InternalWASQLiteDBAdapter)
5773
+ /* harmony export */ DatabaseServer: () => (/* binding */ DatabaseServer)
5968
5774
  /* harmony export */ });
5969
- /* harmony import */ var _LockedAsyncDatabaseAdapter_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../LockedAsyncDatabaseAdapter.js */ "./lib/src/db/adapters/LockedAsyncDatabaseAdapter.js");
5970
- /* harmony import */ var _WASQLiteConnection_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./WASQLiteConnection.js */ "./lib/src/db/adapters/wa-sqlite/WASQLiteConnection.js");
5971
-
5972
-
5973
5775
  /**
5974
- * @internal
5975
- * An intermediary implementation of WASQLiteDBAdapter, which takes the same
5976
- * constructor arguments as {@link LockedAsyncDatabaseAdapter}, but provides some
5977
- * basic WA-SQLite specific functionality.
5978
- * 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.
5979
5778
  */
5980
- class InternalWASQLiteDBAdapter extends _LockedAsyncDatabaseAdapter_js__WEBPACK_IMPORTED_MODULE_0__.LockedAsyncDatabaseAdapter {
5981
- getConfiguration() {
5982
- // This is valid since we only handle WASQLite connections
5983
- 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
+ }
5984
5855
  return {
5985
- ...super.getConfiguration(),
5986
- 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
+ }
5987
5911
  };
5988
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
+ }
5989
5920
  }
5990
5921
 
5991
5922
 
5992
5923
  /***/ },
5993
5924
 
5994
- /***/ "./lib/src/db/adapters/wa-sqlite/WASQLiteConnection.js"
5995
- /*!*************************************************************!*\
5996
- !*** ./lib/src/db/adapters/wa-sqlite/WASQLiteConnection.js ***!
5997
- \*************************************************************/
5925
+ /***/ "./lib/src/db/adapters/wa-sqlite/RawSqliteConnection.js"
5926
+ /*!**************************************************************!*\
5927
+ !*** ./lib/src/db/adapters/wa-sqlite/RawSqliteConnection.js ***!
5928
+ \**************************************************************/
5998
5929
  (__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) {
5999
5930
 
6000
5931
  __webpack_require__.r(__webpack_exports__);
6001
5932
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
6002
- /* harmony export */ AsyncWASQLiteModuleFactory: () => (/* binding */ AsyncWASQLiteModuleFactory),
6003
- /* harmony export */ DEFAULT_MODULE_FACTORIES: () => (/* binding */ DEFAULT_MODULE_FACTORIES),
6004
- /* harmony export */ MultiCipherAsyncWASQLiteModuleFactory: () => (/* binding */ MultiCipherAsyncWASQLiteModuleFactory),
6005
- /* harmony export */ MultiCipherSyncWASQLiteModuleFactory: () => (/* binding */ MultiCipherSyncWASQLiteModuleFactory),
6006
- /* harmony export */ SyncWASQLiteModuleFactory: () => (/* binding */ SyncWASQLiteModuleFactory),
6007
- /* harmony export */ WASQLiteVFS: () => (/* binding */ WASQLiteVFS),
6008
- /* harmony export */ WASqliteConnection: () => (/* binding */ WASqliteConnection)
5933
+ /* harmony export */ RawSqliteConnection: () => (/* binding */ RawSqliteConnection)
6009
5934
  /* harmony export */ });
6010
5935
  /* harmony import */ var _journeyapps_wa_sqlite__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @journeyapps/wa-sqlite */ "@journeyapps/wa-sqlite");
6011
- /* harmony import */ var _powersync_common__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @powersync/common */ "@powersync/common");
6012
- /* harmony import */ var async_mutex__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! async-mutex */ "async-mutex");
6013
-
5936
+ /* harmony import */ var _vfs_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./vfs.js */ "./lib/src/db/adapters/wa-sqlite/vfs.js");
6014
5937
 
6015
5938
 
6016
5939
  /**
6017
- * List of currently tested virtual filesystems
6018
- */
6019
- var WASQLiteVFS;
6020
- (function (WASQLiteVFS) {
6021
- WASQLiteVFS["IDBBatchAtomicVFS"] = "IDBBatchAtomicVFS";
6022
- WASQLiteVFS["OPFSCoopSyncVFS"] = "OPFSCoopSyncVFS";
6023
- WASQLiteVFS["AccessHandlePoolVFS"] = "AccessHandlePoolVFS";
6024
- })(WASQLiteVFS || (WASQLiteVFS = {}));
6025
- /**
6026
- * @internal
6027
- */
6028
- const AsyncWASQLiteModuleFactory = async () => {
6029
- 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"));
6030
- return factory();
6031
- };
6032
- /**
6033
- * @internal
6034
- */
6035
- const MultiCipherAsyncWASQLiteModuleFactory = async () => {
6036
- 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"));
6037
- return factory();
6038
- };
6039
- /**
6040
- * @internal
6041
- */
6042
- const SyncWASQLiteModuleFactory = async () => {
6043
- 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"));
6044
- return factory();
6045
- };
6046
- /**
6047
- * @internal
6048
- */
6049
- const MultiCipherSyncWASQLiteModuleFactory = async () => {
6050
- 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"));
6051
- return factory();
6052
- };
6053
- /**
6054
- * @internal
6055
- */
6056
- const DEFAULT_MODULE_FACTORIES = {
6057
- [WASQLiteVFS.IDBBatchAtomicVFS]: async (options) => {
6058
- let module;
6059
- if (options.encryptionKey) {
6060
- module = await MultiCipherAsyncWASQLiteModuleFactory();
6061
- }
6062
- else {
6063
- module = await AsyncWASQLiteModuleFactory();
6064
- }
6065
- 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));
6066
- return {
6067
- module,
6068
- // @ts-expect-error The types for this static method are missing upstream
6069
- vfs: await IDBBatchAtomicVFS.create(options.dbFileName, module, { lockPolicy: 'exclusive' })
6070
- };
6071
- },
6072
- [WASQLiteVFS.AccessHandlePoolVFS]: async (options) => {
6073
- let module;
6074
- if (options.encryptionKey) {
6075
- module = await MultiCipherSyncWASQLiteModuleFactory();
6076
- }
6077
- else {
6078
- module = await SyncWASQLiteModuleFactory();
6079
- }
6080
- // @ts-expect-error The types for this static method are missing upstream
6081
- 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));
6082
- return {
6083
- module,
6084
- vfs: await AccessHandlePoolVFS.create(options.dbFileName, module)
6085
- };
6086
- },
6087
- [WASQLiteVFS.OPFSCoopSyncVFS]: async (options) => {
6088
- let module;
6089
- if (options.encryptionKey) {
6090
- module = await MultiCipherSyncWASQLiteModuleFactory();
6091
- }
6092
- else {
6093
- module = await SyncWASQLiteModuleFactory();
6094
- }
6095
- // @ts-expect-error The types for this static method are missing upstream
6096
- 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));
6097
- const vfs = await OPFSCoopSyncVFS.create(options.dbFileName, module);
6098
- return {
6099
- module,
6100
- vfs
6101
- };
6102
- }
6103
- };
6104
- /**
6105
- * @internal
6106
- * WA-SQLite connection which directly interfaces with WA-SQLite.
6107
- * 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.
6108
5944
  */
6109
- class WASqliteConnection extends _powersync_common__WEBPACK_IMPORTED_MODULE_1__.BaseObserver {
5945
+ class RawSqliteConnection {
6110
5946
  options;
6111
5947
  _sqliteAPI = null;
6112
- _dbP = null;
6113
- _moduleFactory;
6114
- updatedTables;
6115
- updateTimer;
6116
- statementMutex;
6117
- broadcastChannel;
6118
5948
  /**
6119
- * Unique id for this specific connection. This is used to prevent broadcast table change
6120
- * notification loops.
5949
+ * The `sqlite3*` connection pointer.
6121
5950
  */
6122
- connectionId;
6123
- _holdCounter;
6124
- _holdId;
5951
+ db = 0;
5952
+ _moduleFactory;
6125
5953
  constructor(options) {
6126
- super();
6127
5954
  this.options = options;
6128
- this.updatedTables = new Set();
6129
- this.updateTimer = null;
6130
- this.broadcastChannel = null;
6131
- this.connectionId = new Date().valueOf() + Math.random();
6132
- this.statementMutex = new async_mutex__WEBPACK_IMPORTED_MODULE_2__.Mutex();
6133
- this._moduleFactory = DEFAULT_MODULE_FACTORIES[this.options.vfs];
6134
- this._holdCounter = 0;
6135
- this._holdId = null;
5955
+ this._moduleFactory = _vfs_js__WEBPACK_IMPORTED_MODULE_1__.DEFAULT_MODULE_FACTORIES[this.options.vfs];
6136
5956
  }
6137
- /**
6138
- * Gets the id for the current hold.
6139
- * This can be used to check for invalid states.
6140
- */
6141
- get currentHoldId() {
6142
- return this._holdId;
6143
- }
6144
- get sqliteAPI() {
6145
- if (!this._sqliteAPI) {
6146
- throw new Error(`Initialization has not completed`);
6147
- }
6148
- return this._sqliteAPI;
5957
+ get isOpen() {
5958
+ return this.db != 0;
6149
5959
  }
6150
- get dbP() {
6151
- if (!this._dbP) {
6152
- throw new Error(`Initialization has not completed`);
6153
- }
6154
- return this._dbP;
6155
- }
6156
- /**
6157
- * Checks if the database connection is in autocommit mode.
6158
- * @returns true if in autocommit mode, false if in a transaction
6159
- */
6160
- async isAutoCommit() {
6161
- return this.sqliteAPI.get_autocommit(this.dbP) != 0;
6162
- }
6163
- async markHold() {
6164
- const previousHoldId = this._holdId;
6165
- this._holdId = `${++this._holdCounter}`;
6166
- if (previousHoldId) {
6167
- await this.iterateAsyncListeners(async (cb) => cb.holdOverwritten?.(previousHoldId));
6168
- }
6169
- return this._holdId;
6170
- }
6171
- async releaseHold(holdId) {
6172
- if (holdId != this._holdId) {
6173
- throw new Error(`Invalid hold state, expected ${this._holdId} but got ${holdId}`);
6174
- }
6175
- this._holdId = null;
6176
- }
6177
- async openDB() {
6178
- this._dbP = await this.sqliteAPI.open_v2(this.options.dbFilename);
6179
- return this._dbP;
6180
- }
6181
- 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};`);
6182
5964
  if (this.options.encryptionKey) {
6183
- await this.executeSingleStatement(`PRAGMA key = "${this.options.encryptionKey}"`);
5965
+ const escapedKey = this.options.encryptionKey.replace("'", "''");
5966
+ await this.executeRaw(`PRAGMA key = '${escapedKey}'`);
6184
5967
  }
6185
- return;
5968
+ await this.executeRaw(`PRAGMA cache_size = -${this.options.cacheSizeKb};`);
5969
+ await this.executeRaw(`SELECT powersync_update_hooks('install');`);
6186
5970
  }
6187
5971
  async openSQLiteAPI() {
6188
5972
  const { module, vfs } = await this._moduleFactory({
6189
5973
  dbFileName: this.options.dbFilename,
6190
5974
  encryptionKey: this.options.encryptionKey
6191
5975
  });
6192
- const sqlite3 = _journeyapps_wa_sqlite__WEBPACK_IMPORTED_MODULE_0__.Factory(module);
5976
+ const sqlite3 = (0,_journeyapps_wa_sqlite__WEBPACK_IMPORTED_MODULE_0__.Factory)(module);
6193
5977
  sqlite3.vfs_register(vfs, true);
6194
5978
  /**
6195
5979
  * Register the PowerSync core SQLite extension
@@ -6206,278 +5990,95 @@ class WASqliteConnection extends _powersync_common__WEBPACK_IMPORTED_MODULE_1__.
6206
5990
  }
6207
5991
  return sqlite3;
6208
5992
  }
6209
- registerBroadcastListeners() {
6210
- this.broadcastChannel = new BroadcastChannel(`${this.options.dbFilename}-table-updates`);
6211
- this.broadcastChannel.addEventListener('message', (event) => {
6212
- const data = event.data;
6213
- if (this.connectionId == data.connectionId) {
6214
- // Ignore messages from the same connection
6215
- return;
6216
- }
6217
- // Ensuring that we don't rebroadcast the same message
6218
- this.queueTableUpdate(data.changedTables, false);
6219
- });
6220
- }
6221
- queueTableUpdate(tableNames, shouldBroadcast = true) {
6222
- tableNames.forEach((tableName) => this.updatedTables.add(tableName));
6223
- if (this.updateTimer == null) {
6224
- this.updateTimer = setTimeout(() => this.fireUpdates(shouldBroadcast), 0);
6225
- }
6226
- }
6227
- async init() {
6228
- this._sqliteAPI = await this.openSQLiteAPI();
6229
- await this.openDB();
6230
- this.registerBroadcastListeners();
6231
- await this.executeSingleStatement(`PRAGMA temp_store = ${this.options.temporaryStorage};`);
6232
- await this.executeEncryptionPragma();
6233
- await this.executeSingleStatement(`PRAGMA cache_size = -${this.options.cacheSizeKb};`);
6234
- this.sqliteAPI.update_hook(this.dbP, (updateType, dbName, tableName) => {
6235
- if (!tableName) {
6236
- return;
6237
- }
6238
- const changedTables = new Set([tableName]);
6239
- this.queueTableUpdate(changedTables);
6240
- });
6241
- }
6242
- async getConfig() {
6243
- return this.options;
6244
- }
6245
- fireUpdates(shouldBroadcast = true) {
6246
- this.updateTimer = null;
6247
- const event = { tables: [...this.updatedTables], groupedUpdates: {}, rawUpdates: [] };
6248
- // Share to other connections
6249
- if (shouldBroadcast) {
6250
- this.broadcastChannel.postMessage({
6251
- changedTables: this.updatedTables,
6252
- connectionId: this.connectionId
6253
- });
5993
+ requireSqlite() {
5994
+ if (!this._sqliteAPI) {
5995
+ throw new Error(`Initialization has not completed`);
6254
5996
  }
6255
- this.updatedTables.clear();
6256
- this.iterateListeners((cb) => cb.tablesUpdated?.(event));
5997
+ return this._sqliteAPI;
6257
5998
  }
6258
5999
  /**
6259
- * 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
6260
6002
  */
6261
- async executeBatch(sql, bindings) {
6262
- return this.acquireExecuteLock(async () => {
6263
- let affectedRows = 0;
6264
- try {
6265
- await this.executeSingleStatement('BEGIN TRANSACTION');
6266
- const wrappedBindings = bindings ? bindings : [];
6267
- for await (const stmt of this.sqliteAPI.statements(this.dbP, sql)) {
6268
- if (stmt === null) {
6269
- return {
6270
- rowsAffected: 0,
6271
- rows: { _array: [], length: 0 }
6272
- };
6273
- }
6274
- //Prepare statement once
6275
- for (const binding of wrappedBindings) {
6276
- // TODO not sure why this is needed currently, but booleans break
6277
- for (let i = 0; i < binding.length; i++) {
6278
- const b = binding[i];
6279
- if (typeof b == 'boolean') {
6280
- binding[i] = b ? 1 : 0;
6281
- }
6282
- }
6283
- if (bindings) {
6284
- this.sqliteAPI.bind_collection(stmt, binding);
6285
- }
6286
- const result = await this.sqliteAPI.step(stmt);
6287
- if (result === _journeyapps_wa_sqlite__WEBPACK_IMPORTED_MODULE_0__.SQLITE_DONE) {
6288
- //The value returned by sqlite3_changes() immediately after an INSERT, UPDATE or DELETE statement run on a view is always zero.
6289
- affectedRows += this.sqliteAPI.changes(this.dbP);
6290
- }
6291
- this.sqliteAPI.reset(stmt);
6292
- }
6293
- }
6294
- await this.executeSingleStatement('COMMIT');
6295
- }
6296
- catch (err) {
6297
- await this.executeSingleStatement('ROLLBACK');
6298
- return {
6299
- rowsAffected: 0,
6300
- rows: { _array: [], length: 0 }
6301
- };
6302
- }
6303
- const result = {
6304
- rowsAffected: affectedRows,
6305
- rows: { _array: [], length: 0 }
6306
- };
6307
- return result;
6308
- });
6003
+ isAutoCommit() {
6004
+ return this.requireSqlite().get_autocommit(this.db) != 0;
6309
6005
  }
6310
- /**
6311
- * This executes single SQL statements inside a requested lock.
6312
- */
6313
6006
  async execute(sql, bindings) {
6314
- // Running multiple statements on the same connection concurrently should not be allowed
6315
- return this.acquireExecuteLock(async () => {
6316
- return this.executeSingleStatement(sql, bindings);
6317
- });
6318
- }
6319
- async executeRaw(sql, bindings) {
6320
- return this.acquireExecuteLock(async () => {
6321
- return this.executeSingleStatementRaw(sql, bindings);
6322
- });
6323
- }
6324
- async close() {
6325
- this.broadcastChannel?.close();
6326
- await this.acquireExecuteLock(async () => {
6327
- /**
6328
- * Running the close operation inside the same execute mutex prevents errors like:
6329
- * ```
6330
- * unable to close due to unfinalized statements or unfinished backups
6331
- * ```
6332
- */
6333
- await this.sqliteAPI.close(this.dbP);
6334
- });
6335
- }
6336
- async registerOnTableChange(callback) {
6337
- return this.registerListener({
6338
- tablesUpdated: (event) => callback(event)
6339
- });
6007
+ const resultSet = await this.executeSingleStatementRaw(sql, bindings);
6008
+ return this.wrapQueryResults(this.requireSqlite(), resultSet);
6340
6009
  }
6341
- /**
6342
- * This requests a lock for executing statements.
6343
- * Should only be used internally.
6344
- */
6345
- acquireExecuteLock = (callback) => {
6346
- return this.statementMutex.runExclusive(callback);
6347
- };
6348
- /**
6349
- * This executes a single statement using SQLite3.
6350
- */
6351
- async executeSingleStatement(sql, bindings) {
6352
- const results = await this._execute(sql, bindings);
6353
- const rows = [];
6354
- for (const resultSet of results) {
6355
- for (const row of resultSet.rows) {
6356
- const outRow = {};
6357
- resultSet.columns.forEach((key, index) => {
6358
- outRow[key] = row[index];
6359
- });
6360
- 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));
6361
6018
  }
6019
+ // executeBatch can only use a single statement
6020
+ break;
6362
6021
  }
6363
- const result = {
6364
- insertId: this.sqliteAPI.last_insert_id(this.dbP),
6365
- rowsAffected: this.sqliteAPI.changes(this.dbP),
6366
- rows: {
6367
- _array: rows,
6368
- length: rows.length
6369
- }
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
6370
6030
  };
6371
- return result;
6372
6031
  }
6373
6032
  /**
6374
- * 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}.
6375
6034
  */
6376
6035
  async executeSingleStatementRaw(sql, bindings) {
6377
- const results = await this._execute(sql, bindings);
6378
- 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;
6379
6038
  }
6380
- async _execute(sql, bindings) {
6039
+ async executeRaw(sql, bindings) {
6381
6040
  const results = [];
6382
- 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)) {
6383
6043
  let columns;
6384
- const wrappedBindings = bindings ? [bindings] : [[]];
6385
- for (const binding of wrappedBindings) {
6386
- // TODO not sure why this is needed currently, but booleans break
6387
- binding.forEach((b, index, arr) => {
6388
- if (typeof b == 'boolean') {
6389
- arr[index] = b ? 1 : 0;
6390
- }
6391
- });
6392
- this.sqliteAPI.reset(stmt);
6393
- if (bindings) {
6394
- this.sqliteAPI.bind_collection(stmt, binding);
6395
- }
6396
- const rows = [];
6397
- while ((await this.sqliteAPI.step(stmt)) === _journeyapps_wa_sqlite__WEBPACK_IMPORTED_MODULE_0__.SQLITE_ROW) {
6398
- const row = this.sqliteAPI.row(stmt);
6399
- rows.push(row);
6400
- }
6401
- columns = columns ?? this.sqliteAPI.column_names(stmt);
6402
- if (columns.length) {
6403
- results.push({ columns, rows });
6404
- }
6044
+ const rs = await this.stepThroughStatement(api, stmt, bindings ?? [], columns);
6045
+ columns = rs.columns;
6046
+ if (columns.length) {
6047
+ results.push(rs);
6405
6048
  }
6406
6049
  // When binding parameters, only a single statement is executed.
6407
6050
  if (bindings) {
6408
6051
  break;
6409
- }
6410
- }
6411
- return results;
6412
- }
6413
- }
6414
-
6415
-
6416
- /***/ },
6417
-
6418
- /***/ "./lib/src/db/adapters/wa-sqlite/WASQLiteDBAdapter.js"
6419
- /*!************************************************************!*\
6420
- !*** ./lib/src/db/adapters/wa-sqlite/WASQLiteDBAdapter.js ***!
6421
- \************************************************************/
6422
- (__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) {
6423
-
6424
- __webpack_require__.r(__webpack_exports__);
6425
- /* harmony export */ __webpack_require__.d(__webpack_exports__, {
6426
- /* harmony export */ WASQLiteDBAdapter: () => (/* binding */ WASQLiteDBAdapter)
6427
- /* harmony export */ });
6428
- /* harmony import */ var comlink__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! comlink */ "comlink");
6429
- /* harmony import */ var _PowerSyncDatabase_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../PowerSyncDatabase.js */ "./lib/src/db/PowerSyncDatabase.js");
6430
- /* 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");
6431
- /* harmony import */ var _WorkerWrappedAsyncDatabaseConnection_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../WorkerWrappedAsyncDatabaseConnection.js */ "./lib/src/db/adapters/WorkerWrappedAsyncDatabaseConnection.js");
6432
- /* harmony import */ var _InternalWASQLiteDBAdapter_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./InternalWASQLiteDBAdapter.js */ "./lib/src/db/adapters/wa-sqlite/InternalWASQLiteDBAdapter.js");
6433
- /* harmony import */ var _WASQLiteOpenFactory_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./WASQLiteOpenFactory.js */ "./lib/src/db/adapters/wa-sqlite/WASQLiteOpenFactory.js");
6434
-
6435
-
6436
-
6437
-
6438
-
6439
-
6440
- /**
6441
- * Adapter for WA-SQLite SQLite connections.
6442
- */
6443
- class WASQLiteDBAdapter extends _InternalWASQLiteDBAdapter_js__WEBPACK_IMPORTED_MODULE_4__.InternalWASQLiteDBAdapter {
6444
- constructor(options) {
6445
- super({
6446
- name: options.dbFilename,
6447
- openConnection: async () => {
6448
- const { workerPort, temporaryStorage, cacheSizeKb } = options;
6449
- if (workerPort) {
6450
- const remote = comlink__WEBPACK_IMPORTED_MODULE_0__.wrap(workerPort);
6451
- return new _WorkerWrappedAsyncDatabaseConnection_js__WEBPACK_IMPORTED_MODULE_3__.WorkerWrappedAsyncDatabaseConnection({
6452
- remote,
6453
- remoteCanCloseUnexpectedly: false,
6454
- identifier: options.dbFilename,
6455
- baseConnection: await remote({
6456
- ...options,
6457
- temporaryStorage: temporaryStorage ?? _web_sql_flags_js__WEBPACK_IMPORTED_MODULE_2__.TemporaryStorageOption.MEMORY,
6458
- cacheSizeKb: cacheSizeKb ?? _web_sql_flags_js__WEBPACK_IMPORTED_MODULE_2__.DEFAULT_CACHE_SIZE_KB,
6459
- flags: (0,_PowerSyncDatabase_js__WEBPACK_IMPORTED_MODULE_1__.resolveWebPowerSyncFlags)(options.flags),
6460
- encryptionKey: options.encryptionKey
6461
- })
6462
- });
6463
- }
6464
- const openFactory = new _WASQLiteOpenFactory_js__WEBPACK_IMPORTED_MODULE_5__.WASQLiteOpenFactory({
6465
- dbFilename: options.dbFilename,
6466
- dbLocation: options.dbLocation,
6467
- debugMode: options.debugMode,
6468
- flags: options.flags,
6469
- temporaryStorage,
6470
- cacheSizeKb,
6471
- logger: options.logger,
6472
- vfs: options.vfs,
6473
- encryptionKey: options.encryptionKey,
6474
- worker: options.worker
6475
- });
6476
- return openFactory.openConnection();
6477
- },
6478
- debugMode: options.debugMode,
6479
- 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
+ }
6480
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
+ }
6481
6082
  }
6482
6083
  }
6483
6084
 
@@ -6494,13 +6095,19 @@ __webpack_require__.r(__webpack_exports__);
6494
6095
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
6495
6096
  /* harmony export */ WASQLiteOpenFactory: () => (/* binding */ WASQLiteOpenFactory)
6496
6097
  /* harmony export */ });
6497
- /* harmony import */ var comlink__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! comlink */ "comlink");
6498
- /* 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");
6499
- /* harmony import */ var _AbstractWebSQLOpenFactory_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../AbstractWebSQLOpenFactory.js */ "./lib/src/db/adapters/AbstractWebSQLOpenFactory.js");
6500
- /* harmony import */ var _WorkerWrappedAsyncDatabaseConnection_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../WorkerWrappedAsyncDatabaseConnection.js */ "./lib/src/db/adapters/WorkerWrappedAsyncDatabaseConnection.js");
6501
- /* 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");
6502
- /* harmony import */ var _InternalWASQLiteDBAdapter_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./InternalWASQLiteDBAdapter.js */ "./lib/src/db/adapters/wa-sqlite/InternalWASQLiteDBAdapter.js");
6503
- /* 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
+
6504
6111
 
6505
6112
 
6506
6113
 
@@ -6511,56 +6118,82 @@ __webpack_require__.r(__webpack_exports__);
6511
6118
  /**
6512
6119
  * Opens a SQLite connection using WA-SQLite.
6513
6120
  */
6514
- class WASQLiteOpenFactory extends _AbstractWebSQLOpenFactory_js__WEBPACK_IMPORTED_MODULE_2__.AbstractWebSQLOpenFactory {
6121
+ class WASQLiteOpenFactory {
6122
+ options;
6123
+ resolvedFlags;
6124
+ logger;
6515
6125
  constructor(options) {
6516
- super(options);
6126
+ this.options = options;
6517
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}`);
6518
6130
  }
6519
6131
  get waOptions() {
6520
6132
  // Cast to extended type
6521
6133
  return this.options;
6522
6134
  }
6523
6135
  openAdapter() {
6524
- return new _InternalWASQLiteDBAdapter_js__WEBPACK_IMPORTED_MODULE_5__.InternalWASQLiteDBAdapter({
6525
- name: this.options.dbFilename,
6526
- openConnection: () => this.openConnection(),
6527
- debugMode: this.options.debugMode,
6528
- logger: this.logger
6529
- });
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();
6530
6153
  }
6531
6154
  async openConnection() {
6532
6155
  const { enableMultiTabs, useWebWorker } = this.resolvedFlags;
6533
- 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;
6534
6157
  if (!enableMultiTabs) {
6535
6158
  this.logger.warn('Multiple tabs are not enabled in this browser');
6536
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);
6537
6172
  if (useWebWorker) {
6538
6173
  const optionsDbWorker = this.options.worker;
6539
6174
  const workerPort = typeof optionsDbWorker == 'function'
6540
- ? (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({
6541
6176
  ...this.options,
6542
6177
  temporaryStorage,
6543
6178
  cacheSizeKb,
6544
6179
  flags: this.resolvedFlags,
6545
6180
  encryptionKey
6546
6181
  }))
6547
- : (0,_worker_db_open_worker_database_js__WEBPACK_IMPORTED_MODULE_1__.openWorkerDatabasePort)(this.options.dbFilename, enableMultiTabs, optionsDbWorker, this.waOptions.vfs);
6548
- const workerDBOpener = comlink__WEBPACK_IMPORTED_MODULE_0__.wrap(workerPort);
6549
- return new _WorkerWrappedAsyncDatabaseConnection_js__WEBPACK_IMPORTED_MODULE_3__.WorkerWrappedAsyncDatabaseConnection({
6550
- 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,
6551
6193
  // This tab owns the worker, so we're guaranteed to outlive it.
6552
6194
  remoteCanCloseUnexpectedly: false,
6553
- baseConnection: await workerDBOpener({
6554
- dbFilename: this.options.dbFilename,
6555
- vfs,
6556
- temporaryStorage,
6557
- cacheSizeKb,
6558
- flags: this.resolvedFlags,
6559
- encryptionKey: encryptionKey,
6560
- logLevel: this.logger.getLevel()
6561
- }),
6562
- identifier: this.options.dbFilename,
6563
6195
  onClose: () => {
6196
+ closeSignal.abort();
6564
6197
  if (workerPort instanceof Worker) {
6565
6198
  workerPort.terminate();
6566
6199
  }
@@ -6568,21 +6201,19 @@ class WASQLiteOpenFactory extends _AbstractWebSQLOpenFactory_js__WEBPACK_IMPORTE
6568
6201
  workerPort.close();
6569
6202
  }
6570
6203
  }
6571
- });
6204
+ };
6572
6205
  }
6573
6206
  else {
6574
- // Don't use a web worker
6575
- return new _WASQLiteConnection_js__WEBPACK_IMPORTED_MODULE_6__.WASqliteConnection({
6576
- dbFilename: this.options.dbFilename,
6577
- dbLocation: this.options.dbLocation,
6578
- debugMode: this.options.debugMode,
6579
- vfs,
6580
- temporaryStorage,
6581
- cacheSizeKb,
6582
- flags: this.resolvedFlags,
6583
- encryptionKey: encryptionKey
6584
- });
6585
- }
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
+ });
6586
6217
  }
6587
6218
  }
6588
6219
  /**
@@ -6592,7 +6223,7 @@ function assertValidWASQLiteOpenFactoryOptions(options) {
6592
6223
  // The OPFS VFS only works in dedicated web workers.
6593
6224
  if ('vfs' in options && 'flags' in options) {
6594
6225
  const { vfs, flags = {} } = options;
6595
- 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) {
6596
6227
  throw new Error(`Invalid configuration: The 'useWebWorker' flag must be true when using an OPFS-based VFS (${vfs}).`);
6597
6228
  }
6598
6229
  }
@@ -6637,6 +6268,117 @@ class WASQLitePowerSyncDatabaseOpenFactory extends _AbstractWebPowerSyncDatabase
6637
6268
  }
6638
6269
 
6639
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
+
6640
6382
  /***/ },
6641
6383
 
6642
6384
  /***/ "./lib/src/db/adapters/web-sql-flags.js"
@@ -6704,8 +6446,6 @@ __webpack_require__.r(__webpack_exports__);
6704
6446
  /* harmony export */ SSRStreamingSyncImplementation: () => (/* binding */ SSRStreamingSyncImplementation)
6705
6447
  /* harmony export */ });
6706
6448
  /* harmony import */ var _powersync_common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @powersync/common */ "@powersync/common");
6707
- /* harmony import */ var async_mutex__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! async-mutex */ "async-mutex");
6708
-
6709
6449
 
6710
6450
  class SSRStreamingSyncImplementation extends _powersync_common__WEBPACK_IMPORTED_MODULE_0__.BaseObserver {
6711
6451
  syncMutex;
@@ -6715,14 +6455,14 @@ class SSRStreamingSyncImplementation extends _powersync_common__WEBPACK_IMPORTED
6715
6455
  syncStatus;
6716
6456
  constructor(options) {
6717
6457
  super();
6718
- this.syncMutex = new async_mutex__WEBPACK_IMPORTED_MODULE_1__.Mutex();
6719
- 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();
6720
6460
  this.syncStatus = new _powersync_common__WEBPACK_IMPORTED_MODULE_0__.SyncStatus({});
6721
6461
  this.isConnected = false;
6722
6462
  }
6723
6463
  obtainLock(lockOptions) {
6724
6464
  const mutex = lockOptions.type == _powersync_common__WEBPACK_IMPORTED_MODULE_0__.LockType.CRUD ? this.crudMutex : this.syncMutex;
6725
- return mutex.runExclusive(lockOptions.callback);
6465
+ return mutex.runExclusive(lockOptions.callback, lockOptions.signal);
6726
6466
  }
6727
6467
  /**
6728
6468
  * This is a no-op in SSR mode
@@ -6785,11 +6525,11 @@ __webpack_require__.r(__webpack_exports__);
6785
6525
  /* harmony export */ SharedWebStreamingSyncImplementation: () => (/* binding */ SharedWebStreamingSyncImplementation)
6786
6526
  /* harmony export */ });
6787
6527
  /* harmony import */ var comlink__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! comlink */ "comlink");
6788
- /* harmony import */ var _shared_navigator_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../shared/navigator.js */ "./lib/src/shared/navigator.js");
6789
- /* harmony import */ var _worker_sync_AbstractSharedSyncClientProvider_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../worker/sync/AbstractSharedSyncClientProvider.js */ "./lib/src/worker/sync/AbstractSharedSyncClientProvider.js");
6790
- /* harmony import */ var _worker_sync_SharedSyncImplementation_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../worker/sync/SharedSyncImplementation.js */ "./lib/src/worker/sync/SharedSyncImplementation.js");
6791
- /* 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");
6792
- /* 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");
6793
6533
 
6794
6534
 
6795
6535
 
@@ -6800,7 +6540,7 @@ __webpack_require__.r(__webpack_exports__);
6800
6540
  * The shared worker will trigger methods on this side of the message port
6801
6541
  * via this client provider.
6802
6542
  */
6803
- 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 {
6804
6544
  options;
6805
6545
  statusChanged;
6806
6546
  webDB;
@@ -6871,7 +6611,7 @@ class SharedSyncClientProvider extends _worker_sync_AbstractSharedSyncClientProv
6871
6611
  /**
6872
6612
  * The local part of the sync implementation on the web, which talks to a sync implementation hosted in a shared worker.
6873
6613
  */
6874
- class SharedWebStreamingSyncImplementation extends _WebStreamingSyncImplementation_js__WEBPACK_IMPORTED_MODULE_5__.WebStreamingSyncImplementation {
6614
+ class SharedWebStreamingSyncImplementation extends _WebStreamingSyncImplementation_js__WEBPACK_IMPORTED_MODULE_4__.WebStreamingSyncImplementation {
6875
6615
  syncManager;
6876
6616
  clientProvider;
6877
6617
  messagePort;
@@ -6887,10 +6627,10 @@ class SharedWebStreamingSyncImplementation extends _WebStreamingSyncImplementati
6887
6627
  */
6888
6628
  const resolvedWorkerOptions = {
6889
6629
  dbFilename: this.options.identifier,
6890
- temporaryStorage: _adapters_web_sql_flags_js__WEBPACK_IMPORTED_MODULE_4__.TemporaryStorageOption.MEMORY,
6891
- 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,
6892
6632
  ...options,
6893
- 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)
6894
6634
  };
6895
6635
  const syncWorker = options.sync?.worker;
6896
6636
  if (syncWorker) {
@@ -6905,7 +6645,7 @@ class SharedWebStreamingSyncImplementation extends _WebStreamingSyncImplementati
6905
6645
  }
6906
6646
  }
6907
6647
  else {
6908
- 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), {
6909
6649
  /* @vite-ignore */
6910
6650
  name: `shared-sync-${this.webOptions.identifier}`,
6911
6651
  type: undefined
@@ -6948,24 +6688,9 @@ class SharedWebStreamingSyncImplementation extends _WebStreamingSyncImplementati
6948
6688
  * - We resolve the top-level promise after the lock has been registered with the shared worker.
6949
6689
  * - The client sends the params to the shared worker after locks have been registered.
6950
6690
  */
6951
- await new Promise((resolve) => {
6952
- // Request a random lock until this client is disposed. The name of the lock is sent to the shared worker, which
6953
- // will also attempt to acquire it. Since the lock is returned when the tab is closed, this allows the share worker
6954
- // to free resources associated with this tab.
6955
- // We take hold of this lock as soon-as-possible in order to cater for potentially closed tabs.
6956
- (0,_shared_navigator_js__WEBPACK_IMPORTED_MODULE_1__.getNavigatorLocks)().request(`tab-close-signal-${crypto.randomUUID()}`, async (lock) => {
6957
- if (this.abortOnClose.signal.aborted) {
6958
- return;
6959
- }
6960
- // Awaiting here ensures the worker is waiting for the lock
6961
- await this.syncManager.addLockBasedCloseSignal(lock.name);
6962
- // The lock has been registered, we can continue with the initialization
6963
- resolve();
6964
- await new Promise((r) => {
6965
- this.abortOnClose.signal.onabort = () => r();
6966
- });
6967
- });
6968
- });
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);
6969
6694
  const { crudUploadThrottleMs, identifier, retryDelayMs } = this.options;
6970
6695
  const flags = { ...this.webOptions.flags, workers: undefined };
6971
6696
  await this.syncManager.setParams({
@@ -7003,13 +6728,13 @@ class SharedWebStreamingSyncImplementation extends _WebStreamingSyncImplementati
7003
6728
  // Listen for the close acknowledgment from the worker
7004
6729
  this.messagePort.addEventListener('message', (event) => {
7005
6730
  const payload = event.data;
7006
- 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) {
7007
6732
  resolve();
7008
6733
  }
7009
6734
  });
7010
6735
  // Signal the shared worker that this client is closing its connection to the worker
7011
6736
  const closeMessagePayload = {
7012
- 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,
7013
6738
  data: {}
7014
6739
  };
7015
6740
  this.messagePort.postMessage(closeMessagePayload);
@@ -7221,6 +6946,153 @@ const getNavigatorLocks = () => {
7221
6946
  };
7222
6947
 
7223
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
+
7224
7096
  /***/ },
7225
7097
 
7226
7098
  /***/ "./lib/src/worker/db/open-worker-database.js"
@@ -7237,14 +7109,14 @@ __webpack_require__.r(__webpack_exports__);
7237
7109
  /* harmony export */ resolveWorkerDatabasePortFactory: () => (/* binding */ resolveWorkerDatabasePortFactory)
7238
7110
  /* harmony export */ });
7239
7111
  /* harmony import */ var comlink__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! comlink */ "comlink");
7240
- /* 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");
7241
7113
 
7242
7114
 
7243
7115
  /**
7244
7116
  * Opens a shared or dedicated worker which exposes opening of database connections
7245
7117
  */
7246
7118
  function openWorkerDatabasePort(workerIdentifier, multipleTabs = true, worker = '', vfs) {
7247
- 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;
7248
7120
  if (worker) {
7249
7121
  return !needsDedicated && multipleTabs
7250
7122
  ? new SharedWorker(`${worker}`, {
@@ -7264,12 +7136,12 @@ function openWorkerDatabasePort(workerIdentifier, multipleTabs = true, worker =
7264
7136
  * (in the case of Android)
7265
7137
  */
7266
7138
  return !needsDedicated && multipleTabs
7267
- ? 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), {
7268
7140
  /* @vite-ignore */
7269
7141
  name: `shared-DB-worker-${workerIdentifier}`,
7270
7142
  type: undefined
7271
7143
  }).port
7272
- : 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), {
7273
7145
  /* @vite-ignore */
7274
7146
  name: `DB-worker-${workerIdentifier}`,
7275
7147
  type: undefined
@@ -7468,14 +7340,12 @@ __webpack_require__.r(__webpack_exports__);
7468
7340
  /* harmony export */ SharedSyncImplementation: () => (/* binding */ SharedSyncImplementation)
7469
7341
  /* harmony export */ });
7470
7342
  /* harmony import */ var _powersync_common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @powersync/common */ "@powersync/common");
7471
- /* harmony import */ var async_mutex__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! async-mutex */ "async-mutex");
7472
- /* harmony import */ var comlink__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! comlink */ "comlink");
7473
- /* harmony import */ var _db_sync_WebRemote_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../db/sync/WebRemote.js */ "./lib/src/db/sync/WebRemote.js");
7474
- /* harmony import */ var _db_sync_WebStreamingSyncImplementation_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../db/sync/WebStreamingSyncImplementation.js */ "./lib/src/db/sync/WebStreamingSyncImplementation.js");
7475
- /* harmony import */ var _db_adapters_LockedAsyncDatabaseAdapter_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../../db/adapters/LockedAsyncDatabaseAdapter.js */ "./lib/src/db/adapters/LockedAsyncDatabaseAdapter.js");
7476
- /* harmony import */ var _db_adapters_WorkerWrappedAsyncDatabaseConnection_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../../db/adapters/WorkerWrappedAsyncDatabaseConnection.js */ "./lib/src/db/adapters/WorkerWrappedAsyncDatabaseConnection.js");
7477
- /* harmony import */ var _BroadcastLogger_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./BroadcastLogger.js */ "./lib/src/worker/sync/BroadcastLogger.js");
7478
-
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");
7479
7349
 
7480
7350
 
7481
7351
 
@@ -7520,14 +7390,14 @@ class SharedSyncImplementation extends _powersync_common__WEBPACK_IMPORTED_MODUL
7520
7390
  connectionManager;
7521
7391
  syncStatus;
7522
7392
  broadCastLogger;
7523
- distributedDB;
7393
+ database = this.generateReconnectableDatabase();
7524
7394
  constructor() {
7525
7395
  super();
7526
7396
  this.ports = [];
7527
7397
  this.syncParams = null;
7528
7398
  this.logger = (0,_powersync_common__WEBPACK_IMPORTED_MODULE_0__.createLogger)('shared-sync');
7529
7399
  this.lastConnectOptions = undefined;
7530
- this.portMutex = new async_mutex__WEBPACK_IMPORTED_MODULE_1__.Mutex();
7400
+ this.portMutex = new _powersync_common__WEBPACK_IMPORTED_MODULE_0__.Mutex();
7531
7401
  this.isInitialized = new Promise((resolve) => {
7532
7402
  const callback = this.registerListener({
7533
7403
  initialized: () => {
@@ -7536,10 +7406,8 @@ class SharedSyncImplementation extends _powersync_common__WEBPACK_IMPORTED_MODUL
7536
7406
  }
7537
7407
  });
7538
7408
  });
7539
- // Should be configured once we get params
7540
- this.distributedDB = null;
7541
7409
  this.syncStatus = new _powersync_common__WEBPACK_IMPORTED_MODULE_0__.SyncStatus({});
7542
- 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);
7543
7411
  this.connectionManager = new _powersync_common__WEBPACK_IMPORTED_MODULE_0__.ConnectionManager({
7544
7412
  createSyncImplementation: async () => {
7545
7413
  await this.waitForReady();
@@ -7637,38 +7505,8 @@ class SharedSyncImplementation extends _powersync_common__WEBPACK_IMPORTED_MODUL
7637
7505
  if (params.streamOptions?.flags?.broadcastLogs) {
7638
7506
  this.logger = this.broadCastLogger;
7639
7507
  }
7640
- const lockedAdapter = new _db_adapters_LockedAsyncDatabaseAdapter_js__WEBPACK_IMPORTED_MODULE_5__.LockedAsyncDatabaseAdapter({
7641
- name: params.dbParams.dbFilename,
7642
- openConnection: async () => {
7643
- // Gets a connection from the clients when a new connection is requested.
7644
- const db = await this.openInternalDB();
7645
- db.registerListener({
7646
- closing: () => {
7647
- lockedAdapter.reOpenInternalDB();
7648
- }
7649
- });
7650
- return db;
7651
- },
7652
- logger: this.logger,
7653
- reOpenOnConnectionClosed: true
7654
- });
7655
- this.distributedDB = lockedAdapter;
7656
- await lockedAdapter.init();
7657
- lockedAdapter.registerListener({
7658
- databaseReOpened: () => {
7659
- // We may have missed some table updates while the database was closed.
7660
- // We can poke the crud in case we missed any updates.
7661
- this.connectionManager.syncStreamImplementation?.triggerCrudUpload();
7662
- /**
7663
- * FIXME or IMPROVE ME
7664
- * The Rust client implementation stores sync state on the connection level.
7665
- * Reopening the database causes a state machine error which should cause the
7666
- * StreamingSyncImplementation to reconnect. It would be nicer if we could trigger
7667
- * this reconnect earlier.
7668
- * This reconnect is not required for IndexedDB.
7669
- */
7670
- }
7671
- });
7508
+ // Ensure we have a usable database connection, the reconnectable database will connect lazily on first use.
7509
+ await this.database.readLock(async () => { });
7672
7510
  self.onerror = (event) => {
7673
7511
  // Share any uncaught events on the broadcast logger
7674
7512
  this.logger.error('Uncaught exception in PowerSync shared sync worker', event);
@@ -7700,7 +7538,7 @@ class SharedSyncImplementation extends _powersync_common__WEBPACK_IMPORTED_MODUL
7700
7538
  return await this.portMutex.runExclusive(() => {
7701
7539
  const portProvider = {
7702
7540
  port,
7703
- clientProvider: comlink__WEBPACK_IMPORTED_MODULE_2__.wrap(port),
7541
+ clientProvider: comlink__WEBPACK_IMPORTED_MODULE_1__.wrap(port),
7704
7542
  currentSubscriptions: [],
7705
7543
  closeListeners: [],
7706
7544
  isClosing: false
@@ -7747,7 +7585,7 @@ class SharedSyncImplementation extends _powersync_common__WEBPACK_IMPORTED_MODUL
7747
7585
  await closeListener();
7748
7586
  }
7749
7587
  this.collectActiveSubscriptions();
7750
- return () => trackedPort.clientProvider[comlink__WEBPACK_IMPORTED_MODULE_2__.releaseProxy]();
7588
+ return () => trackedPort.clientProvider[comlink__WEBPACK_IMPORTED_MODULE_1__.releaseProxy]();
7751
7589
  });
7752
7590
  }
7753
7591
  triggerCrudUpload() {
@@ -7784,9 +7622,9 @@ class SharedSyncImplementation extends _powersync_common__WEBPACK_IMPORTED_MODUL
7784
7622
  // This should only be called after initialization has completed
7785
7623
  const syncParams = this.syncParams;
7786
7624
  // Create a new StreamingSyncImplementation for each connect call. This is usually done is all SDKs.
7787
- return new _db_sync_WebStreamingSyncImplementation_js__WEBPACK_IMPORTED_MODULE_4__.WebStreamingSyncImplementation({
7788
- adapter: new _powersync_common__WEBPACK_IMPORTED_MODULE_0__.SqliteBucketStorage(this.distributedDB, this.logger),
7789
- 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({
7790
7628
  invalidateCredentials: async () => {
7791
7629
  const lastPort = await this.getLastWrappedPort();
7792
7630
  if (!lastPort) {
@@ -7856,9 +7694,9 @@ class SharedSyncImplementation extends _powersync_common__WEBPACK_IMPORTED_MODUL
7856
7694
  });
7857
7695
  }
7858
7696
  /**
7859
- * Opens a worker wrapped database connection. Using the last connected client port.
7697
+ * Requests a random client to share its database connection with us.
7860
7698
  */
7861
- async openInternalDB() {
7699
+ async openInternalDB(handleClosed) {
7862
7700
  const client = await this.getRandomWrappedPort();
7863
7701
  if (!client) {
7864
7702
  // Should not really happen in practice
@@ -7892,8 +7730,9 @@ class SharedSyncImplementation extends _powersync_common__WEBPACK_IMPORTED_MODUL
7892
7730
  removeCloseListener();
7893
7731
  throw ex;
7894
7732
  });
7895
- const remote = comlink__WEBPACK_IMPORTED_MODULE_2__.wrap(workerPort);
7733
+ const remote = comlink__WEBPACK_IMPORTED_MODULE_1__.wrap(workerPort);
7896
7734
  const identifier = this.syncParams.dbParams.dbFilename;
7735
+ const clientLockName = await (0,_shared_tab_close_signal_js__WEBPACK_IMPORTED_MODULE_6__.generateTabCloseSignal)();
7897
7736
  /**
7898
7737
  * The open could fail if the tab is closed while we're busy opening the database.
7899
7738
  * This operation is typically executed inside an exclusive portMutex lock.
@@ -7901,7 +7740,16 @@ class SharedSyncImplementation extends _powersync_common__WEBPACK_IMPORTED_MODUL
7901
7740
  * We can't rely on the closeListeners to abort the operation if the tab is closed.
7902
7741
  */
7903
7742
  const db = await withAbort({
7904
- 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
+ },
7905
7753
  signal: abortController.signal,
7906
7754
  cleanupOnAbort: (db) => {
7907
7755
  db.close();
@@ -7911,25 +7759,86 @@ class SharedSyncImplementation extends _powersync_common__WEBPACK_IMPORTED_MODUL
7911
7759
  removeCloseListener();
7912
7760
  });
7913
7761
  clearTimeout(timeout);
7914
- const wrapped = new _db_adapters_WorkerWrappedAsyncDatabaseConnection_js__WEBPACK_IMPORTED_MODULE_6__.WorkerWrappedAsyncDatabaseConnection({
7915
- remote,
7916
- baseConnection: db,
7917
- identifier,
7918
- // It's possible for this worker to outlive the client hosting the database for us. We need to be prepared for
7919
- // that and ensure pending requests are aborted when the tab is closed.
7920
- remoteCanCloseUnexpectedly: true
7921
- });
7922
7762
  client.closeListeners.push(async () => {
7923
7763
  this.logger.info('Aborting open connection because associated tab closed.');
7764
+ handleClosed(db);
7924
7765
  /**
7925
7766
  * Don't await this close operation. It might never resolve if the tab is closed.
7926
7767
  * We mark the remote as closed first, this will reject any pending requests.
7927
7768
  * We then call close. The close operation is configured to fire-and-forget, the main promise will reject immediately.
7928
7769
  */
7929
- wrapped.markRemoteClosed();
7930
- 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));
7931
7772
  });
7932
- 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();
7933
7842
  }
7934
7843
  /**
7935
7844
  * A method to update the all shared statuses for each
@@ -8148,51 +8057,37 @@ var __webpack_exports__ = {};
8148
8057
  __webpack_require__.r(__webpack_exports__);
8149
8058
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
8150
8059
  /* harmony export */ AbstractWebPowerSyncDatabaseOpenFactory: () => (/* reexport safe */ _db_adapters_AbstractWebPowerSyncDatabaseOpenFactory_js__WEBPACK_IMPORTED_MODULE_2__.AbstractWebPowerSyncDatabaseOpenFactory),
8151
- /* harmony export */ AbstractWebSQLOpenFactory: () => (/* reexport safe */ _db_adapters_AbstractWebSQLOpenFactory_js__WEBPACK_IMPORTED_MODULE_3__.AbstractWebSQLOpenFactory),
8152
- /* harmony export */ AsyncWASQLiteModuleFactory: () => (/* reexport safe */ _db_adapters_wa_sqlite_WASQLiteConnection_js__WEBPACK_IMPORTED_MODULE_5__.AsyncWASQLiteModuleFactory),
8153
- /* harmony export */ DEFAULT_CACHE_SIZE_KB: () => (/* reexport safe */ _db_adapters_web_sql_flags_js__WEBPACK_IMPORTED_MODULE_9__.DEFAULT_CACHE_SIZE_KB),
8154
- /* harmony export */ DEFAULT_MODULE_FACTORIES: () => (/* reexport safe */ _db_adapters_wa_sqlite_WASQLiteConnection_js__WEBPACK_IMPORTED_MODULE_5__.DEFAULT_MODULE_FACTORIES),
8155
- /* harmony export */ DEFAULT_POWERSYNC_FLAGS: () => (/* reexport safe */ _db_PowerSyncDatabase_js__WEBPACK_IMPORTED_MODULE_10__.DEFAULT_POWERSYNC_FLAGS),
8156
- /* 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),
8157
8063
  /* harmony export */ IndexDBFileSystemStorageAdapter: () => (/* reexport safe */ _attachments_IndexDBFileSystemAdapter_js__WEBPACK_IMPORTED_MODULE_1__.IndexDBFileSystemStorageAdapter),
8158
- /* harmony export */ MultiCipherAsyncWASQLiteModuleFactory: () => (/* reexport safe */ _db_adapters_wa_sqlite_WASQLiteConnection_js__WEBPACK_IMPORTED_MODULE_5__.MultiCipherAsyncWASQLiteModuleFactory),
8159
- /* harmony export */ MultiCipherSyncWASQLiteModuleFactory: () => (/* reexport safe */ _db_adapters_wa_sqlite_WASQLiteConnection_js__WEBPACK_IMPORTED_MODULE_5__.MultiCipherSyncWASQLiteModuleFactory),
8160
- /* harmony export */ PowerSyncDatabase: () => (/* reexport safe */ _db_PowerSyncDatabase_js__WEBPACK_IMPORTED_MODULE_10__.PowerSyncDatabase),
8161
- /* harmony export */ SharedWebStreamingSyncImplementation: () => (/* reexport safe */ _db_sync_SharedWebStreamingSyncImplementation_js__WEBPACK_IMPORTED_MODULE_11__.SharedWebStreamingSyncImplementation),
8162
- /* harmony export */ SyncWASQLiteModuleFactory: () => (/* reexport safe */ _db_adapters_wa_sqlite_WASQLiteConnection_js__WEBPACK_IMPORTED_MODULE_5__.SyncWASQLiteModuleFactory),
8163
- /* harmony export */ TemporaryStorageOption: () => (/* reexport safe */ _db_adapters_web_sql_flags_js__WEBPACK_IMPORTED_MODULE_9__.TemporaryStorageOption),
8164
- /* harmony export */ WASQLiteDBAdapter: () => (/* reexport safe */ _db_adapters_wa_sqlite_WASQLiteDBAdapter_js__WEBPACK_IMPORTED_MODULE_6__.WASQLiteDBAdapter),
8165
- /* harmony export */ WASQLiteOpenFactory: () => (/* reexport safe */ _db_adapters_wa_sqlite_WASQLiteOpenFactory_js__WEBPACK_IMPORTED_MODULE_7__.WASQLiteOpenFactory),
8166
- /* harmony export */ WASQLitePowerSyncDatabaseOpenFactory: () => (/* reexport safe */ _db_adapters_wa_sqlite_WASQLitePowerSyncDatabaseOpenFactory_js__WEBPACK_IMPORTED_MODULE_8__.WASQLitePowerSyncDatabaseOpenFactory),
8167
- /* harmony export */ WASQLiteVFS: () => (/* reexport safe */ _db_adapters_wa_sqlite_WASQLiteConnection_js__WEBPACK_IMPORTED_MODULE_5__.WASQLiteVFS),
8168
- /* harmony export */ WASqliteConnection: () => (/* reexport safe */ _db_adapters_wa_sqlite_WASQLiteConnection_js__WEBPACK_IMPORTED_MODULE_5__.WASqliteConnection),
8169
- /* harmony export */ WebRemote: () => (/* reexport safe */ _db_sync_WebRemote_js__WEBPACK_IMPORTED_MODULE_12__.WebRemote),
8170
- /* harmony export */ WebStreamingSyncImplementation: () => (/* reexport safe */ _db_sync_WebStreamingSyncImplementation_js__WEBPACK_IMPORTED_MODULE_13__.WebStreamingSyncImplementation),
8171
- /* harmony export */ isServerSide: () => (/* reexport safe */ _db_adapters_web_sql_flags_js__WEBPACK_IMPORTED_MODULE_9__.isServerSide),
8172
- /* harmony export */ resolveWebPowerSyncFlags: () => (/* reexport safe */ _db_PowerSyncDatabase_js__WEBPACK_IMPORTED_MODULE_10__.resolveWebPowerSyncFlags),
8173
- /* 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)
8174
8075
  /* harmony export */ });
8175
8076
  /* harmony import */ var _powersync_common__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @powersync/common */ "@powersync/common");
8176
8077
  /* harmony reexport (unknown) */ var __WEBPACK_REEXPORT_OBJECT__ = {};
8177
- /* 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__]
8178
8079
  /* harmony reexport (unknown) */ __webpack_require__.d(__webpack_exports__, __WEBPACK_REEXPORT_OBJECT__);
8179
8080
  /* harmony import */ var _attachments_IndexDBFileSystemAdapter_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./attachments/IndexDBFileSystemAdapter.js */ "./lib/src/attachments/IndexDBFileSystemAdapter.js");
8180
8081
  /* harmony import */ var _db_adapters_AbstractWebPowerSyncDatabaseOpenFactory_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./db/adapters/AbstractWebPowerSyncDatabaseOpenFactory.js */ "./lib/src/db/adapters/AbstractWebPowerSyncDatabaseOpenFactory.js");
8181
- /* harmony import */ var _db_adapters_AbstractWebSQLOpenFactory_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./db/adapters/AbstractWebSQLOpenFactory.js */ "./lib/src/db/adapters/AbstractWebSQLOpenFactory.js");
8182
- /* harmony import */ var _db_adapters_AsyncDatabaseConnection_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./db/adapters/AsyncDatabaseConnection.js */ "./lib/src/db/adapters/AsyncDatabaseConnection.js");
8183
- /* 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");
8184
- /* 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");
8185
- /* 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");
8186
- /* 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");
8187
- /* 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");
8188
- /* harmony import */ var _db_PowerSyncDatabase_js__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./db/PowerSyncDatabase.js */ "./lib/src/db/PowerSyncDatabase.js");
8189
- /* harmony import */ var _db_sync_SharedWebStreamingSyncImplementation_js__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./db/sync/SharedWebStreamingSyncImplementation.js */ "./lib/src/db/sync/SharedWebStreamingSyncImplementation.js");
8190
- /* harmony import */ var _db_sync_WebRemote_js__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./db/sync/WebRemote.js */ "./lib/src/db/sync/WebRemote.js");
8191
- /* harmony import */ var _db_sync_WebStreamingSyncImplementation_js__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./db/sync/WebStreamingSyncImplementation.js */ "./lib/src/db/sync/WebStreamingSyncImplementation.js");
8192
- /* harmony import */ var _db_adapters_WebDBAdapter_js__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./db/adapters/WebDBAdapter.js */ "./lib/src/db/adapters/WebDBAdapter.js");
8193
-
8194
-
8195
-
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");
8196
8091
 
8197
8092
 
8198
8093