@powersync/web 0.0.0-dev-20260311103504 → 0.0.0-dev-20260503073249

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 (121) hide show
  1. package/dist/2075a31bb151adbb9767.wasm +0 -0
  2. package/dist/3322bc84de986b63c2cd.wasm +0 -0
  3. package/dist/8e97452e297be23b5e50.wasm +0 -0
  4. package/dist/fbc178b70d530e8ce02b.wasm +0 -0
  5. package/dist/index.umd.js +5341 -1279
  6. package/dist/index.umd.js.map +1 -1
  7. package/dist/worker/SharedSyncImplementation.umd.js +1113 -3526
  8. package/dist/worker/SharedSyncImplementation.umd.js.map +1 -1
  9. package/dist/worker/WASQLiteDB.umd.js +1397 -1332
  10. package/dist/worker/WASQLiteDB.umd.js.map +1 -1
  11. package/dist/worker/node_modules_pnpm_journeyapps_wa-sqlite_1_7_0_node_modules_journeyapps_wa-sqlite_dist_mc-wa-s-9af0a7.umd.js +31 -0
  12. package/dist/worker/node_modules_pnpm_journeyapps_wa-sqlite_1_7_0_node_modules_journeyapps_wa-sqlite_dist_mc-wa-s-9af0a7.umd.js.map +1 -0
  13. package/dist/worker/node_modules_pnpm_journeyapps_wa-sqlite_1_7_0_node_modules_journeyapps_wa-sqlite_dist_mc-wa-s-bbf5a9.umd.js +31 -0
  14. package/dist/worker/node_modules_pnpm_journeyapps_wa-sqlite_1_7_0_node_modules_journeyapps_wa-sqlite_dist_mc-wa-s-bbf5a9.umd.js.map +1 -0
  15. package/dist/worker/node_modules_pnpm_journeyapps_wa-sqlite_1_7_0_node_modules_journeyapps_wa-sqlite_dist_wa-sqli-c26e0f.umd.js +31 -0
  16. package/dist/worker/{node_modules_pnpm_journeyapps_wa-sqlite_1_5_0_node_modules_journeyapps_wa-sqlite_dist_wa-sqli-cc5fcc.umd.js.map → node_modules_pnpm_journeyapps_wa-sqlite_1_7_0_node_modules_journeyapps_wa-sqlite_dist_wa-sqli-c26e0f.umd.js.map} +1 -1
  17. package/dist/worker/node_modules_pnpm_journeyapps_wa-sqlite_1_7_0_node_modules_journeyapps_wa-sqlite_dist_wa-sqlite_mjs.umd.js +31 -0
  18. package/dist/worker/{node_modules_pnpm_journeyapps_wa-sqlite_1_5_0_node_modules_journeyapps_wa-sqlite_dist_wa-sqlite_mjs.umd.js.map → node_modules_pnpm_journeyapps_wa-sqlite_1_7_0_node_modules_journeyapps_wa-sqlite_dist_wa-sqlite_mjs.umd.js.map} +1 -1
  19. package/dist/worker/node_modules_pnpm_journeyapps_wa-sqlite_1_7_0_node_modules_journeyapps_wa-sqlite_src_examples-2fb422.umd.js +3562 -0
  20. package/dist/worker/node_modules_pnpm_journeyapps_wa-sqlite_1_7_0_node_modules_journeyapps_wa-sqlite_src_examples-2fb422.umd.js.map +1 -0
  21. package/dist/worker/{node_modules_pnpm_journeyapps_wa-sqlite_1_5_0_node_modules_journeyapps_wa-sqlite_src_examples-0df390.umd.js → node_modules_pnpm_journeyapps_wa-sqlite_1_7_0_node_modules_journeyapps_wa-sqlite_src_examples-96fb23.umd.js} +16 -16
  22. package/dist/worker/{node_modules_pnpm_journeyapps_wa-sqlite_1_5_0_node_modules_journeyapps_wa-sqlite_src_examples-0df390.umd.js.map → node_modules_pnpm_journeyapps_wa-sqlite_1_7_0_node_modules_journeyapps_wa-sqlite_src_examples-96fb23.umd.js.map} +1 -1
  23. package/dist/worker/{node_modules_pnpm_journeyapps_wa-sqlite_1_5_0_node_modules_journeyapps_wa-sqlite_src_examples-151024.umd.js → node_modules_pnpm_journeyapps_wa-sqlite_1_7_0_node_modules_journeyapps_wa-sqlite_src_examples-c89911.umd.js} +12 -12
  24. package/dist/worker/{node_modules_pnpm_journeyapps_wa-sqlite_1_5_0_node_modules_journeyapps_wa-sqlite_src_examples-151024.umd.js.map → node_modules_pnpm_journeyapps_wa-sqlite_1_7_0_node_modules_journeyapps_wa-sqlite_src_examples-c89911.umd.js.map} +1 -1
  25. package/dist/worker/{node_modules_pnpm_journeyapps_wa-sqlite_1_5_0_node_modules_journeyapps_wa-sqlite_src_examples-c01ef0.umd.js → node_modules_pnpm_journeyapps_wa-sqlite_1_7_0_node_modules_journeyapps_wa-sqlite_src_examples-ec4eb1.umd.js} +14 -14
  26. package/dist/worker/{node_modules_pnpm_journeyapps_wa-sqlite_1_5_0_node_modules_journeyapps_wa-sqlite_src_examples-c01ef0.umd.js.map → node_modules_pnpm_journeyapps_wa-sqlite_1_7_0_node_modules_journeyapps_wa-sqlite_src_examples-ec4eb1.umd.js.map} +1 -1
  27. package/lib/package.json +4 -5
  28. package/lib/src/db/PowerSyncDatabase.d.ts +2 -3
  29. package/lib/src/db/PowerSyncDatabase.js +3 -12
  30. package/lib/src/db/adapters/AsyncWebAdapter.d.ts +50 -0
  31. package/lib/src/db/adapters/AsyncWebAdapter.js +163 -0
  32. package/lib/src/db/adapters/SSRDBAdapter.d.ts +1 -2
  33. package/lib/src/db/adapters/SSRDBAdapter.js +5 -6
  34. package/lib/src/db/adapters/wa-sqlite/ConcurrentConnection.d.ts +56 -0
  35. package/lib/src/db/adapters/wa-sqlite/ConcurrentConnection.js +121 -0
  36. package/lib/src/db/adapters/wa-sqlite/DatabaseClient.d.ts +54 -0
  37. package/lib/src/db/adapters/wa-sqlite/DatabaseClient.js +227 -0
  38. package/lib/src/db/adapters/wa-sqlite/DatabaseServer.d.ts +47 -0
  39. package/lib/src/db/adapters/wa-sqlite/DatabaseServer.js +145 -0
  40. package/lib/src/db/adapters/wa-sqlite/RawSqliteConnection.d.ts +46 -0
  41. package/lib/src/db/adapters/wa-sqlite/RawSqliteConnection.js +147 -0
  42. package/lib/src/db/adapters/wa-sqlite/WASQLiteOpenFactory.d.ts +28 -6
  43. package/lib/src/db/adapters/wa-sqlite/WASQLiteOpenFactory.js +104 -55
  44. package/lib/src/db/adapters/wa-sqlite/vfs.d.ts +50 -0
  45. package/lib/src/db/adapters/wa-sqlite/vfs.js +76 -0
  46. package/lib/src/db/adapters/web-sql-flags.d.ts +5 -0
  47. package/lib/src/db/sync/SSRWebStreamingSyncImplementation.d.ts +5 -2
  48. package/lib/src/db/sync/SSRWebStreamingSyncImplementation.js +6 -3
  49. package/lib/src/db/sync/SharedWebStreamingSyncImplementation.js +4 -19
  50. package/lib/src/index.d.ts +1 -4
  51. package/lib/src/index.js +1 -4
  52. package/lib/src/shared/tab_close_signal.d.ts +11 -0
  53. package/lib/src/shared/tab_close_signal.js +26 -0
  54. package/lib/src/worker/db/MultiDatabaseServer.d.ts +17 -0
  55. package/lib/src/worker/db/MultiDatabaseServer.js +89 -0
  56. package/lib/src/worker/db/WASQLiteDB.worker.js +9 -48
  57. package/lib/src/worker/db/open-worker-database.d.ts +3 -3
  58. package/lib/src/worker/db/open-worker-database.js +2 -2
  59. package/lib/src/worker/sync/SharedSyncImplementation.d.ts +5 -6
  60. package/lib/src/worker/sync/SharedSyncImplementation.js +88 -54
  61. package/lib/tsconfig.tsbuildinfo +1 -1
  62. package/package.json +5 -6
  63. package/src/db/PowerSyncDatabase.ts +4 -12
  64. package/src/db/adapters/AsyncWebAdapter.ts +207 -0
  65. package/src/db/adapters/SSRDBAdapter.ts +7 -7
  66. package/src/db/adapters/wa-sqlite/ConcurrentConnection.ts +137 -0
  67. package/src/db/adapters/wa-sqlite/DatabaseClient.ts +325 -0
  68. package/src/db/adapters/wa-sqlite/DatabaseServer.ts +203 -0
  69. package/src/db/adapters/wa-sqlite/RawSqliteConnection.ts +194 -0
  70. package/src/db/adapters/wa-sqlite/WASQLiteOpenFactory.ts +152 -63
  71. package/src/db/adapters/wa-sqlite/vfs.ts +96 -0
  72. package/src/db/adapters/web-sql-flags.ts +6 -0
  73. package/src/db/sync/SSRWebStreamingSyncImplementation.ts +7 -3
  74. package/src/db/sync/SharedWebStreamingSyncImplementation.ts +4 -20
  75. package/src/index.ts +1 -4
  76. package/src/shared/tab_close_signal.ts +28 -0
  77. package/src/worker/db/MultiDatabaseServer.ts +107 -0
  78. package/src/worker/db/WASQLiteDB.worker.ts +10 -57
  79. package/src/worker/db/open-worker-database.ts +4 -4
  80. package/src/worker/sync/SharedSyncImplementation.ts +114 -58
  81. package/dist/26d61ca9f5694d064635.wasm +0 -0
  82. package/dist/_journeyapps_wa-sqlite-_journeyapps_wa-sqlite_src_examples_AccessHandlePoolVFS_js-_journeyapp-89f0ba.index.umd.js +0 -1878
  83. package/dist/_journeyapps_wa-sqlite-_journeyapps_wa-sqlite_src_examples_AccessHandlePoolVFS_js-_journeyapp-89f0ba.index.umd.js.map +0 -1
  84. package/dist/_journeyapps_wa-sqlite_src_examples_AccessHandlePoolVFS_js-_journeyapps_wa-sqlite_src_example-97ebe9.index.umd.js +0 -555
  85. package/dist/_journeyapps_wa-sqlite_src_examples_AccessHandlePoolVFS_js-_journeyapps_wa-sqlite_src_example-97ebe9.index.umd.js.map +0 -1
  86. package/dist/b4c6283dc473b6b3fd24.wasm +0 -0
  87. package/dist/c78985091a0b22aaef03.wasm +0 -0
  88. package/dist/ca59e199e1138b553fad.wasm +0 -0
  89. package/dist/worker/node_modules_pnpm_journeyapps_wa-sqlite_1_5_0_node_modules_journeyapps_wa-sqlite_dist_mc-wa-s-b9c070.umd.js +0 -31
  90. package/dist/worker/node_modules_pnpm_journeyapps_wa-sqlite_1_5_0_node_modules_journeyapps_wa-sqlite_dist_mc-wa-s-b9c070.umd.js.map +0 -1
  91. package/dist/worker/node_modules_pnpm_journeyapps_wa-sqlite_1_5_0_node_modules_journeyapps_wa-sqlite_dist_mc-wa-s-c99c07.umd.js +0 -31
  92. package/dist/worker/node_modules_pnpm_journeyapps_wa-sqlite_1_5_0_node_modules_journeyapps_wa-sqlite_dist_mc-wa-s-c99c07.umd.js.map +0 -1
  93. package/dist/worker/node_modules_pnpm_journeyapps_wa-sqlite_1_5_0_node_modules_journeyapps_wa-sqlite_dist_wa-sqli-cc5fcc.umd.js +0 -31
  94. package/dist/worker/node_modules_pnpm_journeyapps_wa-sqlite_1_5_0_node_modules_journeyapps_wa-sqlite_dist_wa-sqlite_mjs.umd.js +0 -31
  95. package/lib/src/db/adapters/AbstractWebSQLOpenFactory.d.ts +0 -17
  96. package/lib/src/db/adapters/AbstractWebSQLOpenFactory.js +0 -33
  97. package/lib/src/db/adapters/AsyncDatabaseConnection.d.ts +0 -49
  98. package/lib/src/db/adapters/AsyncDatabaseConnection.js +0 -1
  99. package/lib/src/db/adapters/LockedAsyncDatabaseAdapter.d.ts +0 -109
  100. package/lib/src/db/adapters/LockedAsyncDatabaseAdapter.js +0 -401
  101. package/lib/src/db/adapters/WorkerWrappedAsyncDatabaseConnection.d.ts +0 -59
  102. package/lib/src/db/adapters/WorkerWrappedAsyncDatabaseConnection.js +0 -147
  103. package/lib/src/db/adapters/wa-sqlite/InternalWASQLiteDBAdapter.d.ts +0 -12
  104. package/lib/src/db/adapters/wa-sqlite/InternalWASQLiteDBAdapter.js +0 -19
  105. package/lib/src/db/adapters/wa-sqlite/WASQLiteConnection.d.ts +0 -155
  106. package/lib/src/db/adapters/wa-sqlite/WASQLiteConnection.js +0 -401
  107. package/lib/src/db/adapters/wa-sqlite/WASQLiteDBAdapter.d.ts +0 -32
  108. package/lib/src/db/adapters/wa-sqlite/WASQLiteDBAdapter.js +0 -49
  109. package/lib/src/worker/db/SharedWASQLiteConnection.d.ts +0 -42
  110. package/lib/src/worker/db/SharedWASQLiteConnection.js +0 -90
  111. package/lib/src/worker/db/WorkerWASQLiteConnection.d.ts +0 -9
  112. package/lib/src/worker/db/WorkerWASQLiteConnection.js +0 -12
  113. package/src/db/adapters/AbstractWebSQLOpenFactory.ts +0 -48
  114. package/src/db/adapters/AsyncDatabaseConnection.ts +0 -55
  115. package/src/db/adapters/LockedAsyncDatabaseAdapter.ts +0 -490
  116. package/src/db/adapters/WorkerWrappedAsyncDatabaseConnection.ts +0 -201
  117. package/src/db/adapters/wa-sqlite/InternalWASQLiteDBAdapter.ts +0 -23
  118. package/src/db/adapters/wa-sqlite/WASQLiteConnection.ts +0 -497
  119. package/src/db/adapters/wa-sqlite/WASQLiteDBAdapter.ts +0 -86
  120. package/src/worker/db/SharedWASQLiteConnection.ts +0 -131
  121. package/src/worker/db/WorkerWASQLiteConnection.ts +0 -14
package/lib/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@powersync/web",
3
- "version": "1.35.0",
3
+ "version": "1.37.2",
4
4
  "description": "PowerSync Web SDK",
5
5
  "main": "lib/src/index.js",
6
6
  "module": "lib/src/index.js",
@@ -63,21 +63,20 @@
63
63
  "real-time data stream",
64
64
  "live data"
65
65
  ],
66
- "author": "JOURNEYAPPS",
66
+ "author": "PowerSync",
67
67
  "license": "Apache-2.0",
68
68
  "peerDependencies": {
69
69
  "@journeyapps/wa-sqlite": "catalog:",
70
- "@powersync/common": "workspace:^1.48.0"
70
+ "@powersync/common": "workspace:^1.52.0"
71
71
  },
72
72
  "dependencies": {
73
73
  "@powersync/common": "workspace:*",
74
- "async-mutex": "catalog:",
75
74
  "bson": "catalog:",
76
75
  "comlink": "catalog:",
77
76
  "commander": "^12.1.0"
78
77
  },
79
78
  "devDependencies": {
80
- "@journeyapps/wa-sqlite": "^1.5.0",
79
+ "@journeyapps/wa-sqlite": "^1.7.0",
81
80
  "@types/uuid": "catalog:",
82
81
  "crypto-browserify": "^3.12.0",
83
82
  "glob": "catalog:",
@@ -1,8 +1,8 @@
1
- import { AbstractPowerSyncDatabase, DBAdapter, PowerSyncDatabaseOptions, PowerSyncDatabaseOptionsWithDBAdapter, PowerSyncDatabaseOptionsWithOpenFactory, PowerSyncDatabaseOptionsWithSettings, StreamingSyncImplementation, TriggerManagerConfig, type BucketStorageAdapter, type PowerSyncBackendConnector, type PowerSyncCloseOptions, type RequiredAdditionalConnectionOptions } from '@powersync/common';
2
- import { Mutex } from 'async-mutex';
1
+ import { AbstractPowerSyncDatabase, DBAdapter, PowerSyncDatabaseOptions, PowerSyncDatabaseOptionsWithDBAdapter, PowerSyncDatabaseOptionsWithOpenFactory, PowerSyncDatabaseOptionsWithSettings, StreamingSyncImplementation, TriggerManagerConfig, Mutex, type BucketStorageAdapter, type PowerSyncBackendConnector, type PowerSyncCloseOptions, type RequiredAdditionalConnectionOptions } from '@powersync/common';
3
2
  import { ResolvedWebSQLOpenOptions, WebSQLFlags } from './adapters/web-sql-flags.js';
4
3
  export interface WebPowerSyncFlags extends WebSQLFlags {
5
4
  /**
5
+ * @deprecated This flag is no longer used. Navigator locks now handle tab detection automatically.
6
6
  * Externally unload open PowerSync database instances when the window closes.
7
7
  * Setting this to `true` requires calling `close` on all open PowerSyncDatabase
8
8
  * instances before the window unloads
@@ -55,7 +55,6 @@ export declare const resolveWebPowerSyncFlags: (flags?: WebPowerSyncFlags) => Re
55
55
  export declare class PowerSyncDatabase extends AbstractPowerSyncDatabase {
56
56
  protected options: WebPowerSyncDatabaseOptions;
57
57
  static SHARED_MUTEX: Mutex;
58
- protected unloadListener?: () => Promise<void>;
59
58
  protected resolvedFlags: WebPowerSyncFlags;
60
59
  constructor(options: WebPowerSyncDatabaseOptionsWithAdapter);
61
60
  constructor(options: WebPowerSyncDatabaseOptionsWithOpenFactory);
@@ -1,14 +1,13 @@
1
- import { AbstractPowerSyncDatabase, SqliteBucketStorage, isDBAdapter, isSQLOpenFactory } from '@powersync/common';
2
- import { Mutex } from 'async-mutex';
1
+ import { AbstractPowerSyncDatabase, SqliteBucketStorage, isDBAdapter, isSQLOpenFactory, Mutex } from '@powersync/common';
3
2
  import { getNavigatorLocks } from '../shared/navigator.js';
4
3
  import { NAVIGATOR_TRIGGER_CLAIM_MANAGER } from './NavigatorTriggerClaimManager.js';
5
- import { LockedAsyncDatabaseAdapter } from './adapters/LockedAsyncDatabaseAdapter.js';
6
4
  import { WASQLiteOpenFactory } from './adapters/wa-sqlite/WASQLiteOpenFactory.js';
7
5
  import { DEFAULT_WEB_SQL_FLAGS, isServerSide, resolveWebSQLFlags } from './adapters/web-sql-flags.js';
8
6
  import { SSRStreamingSyncImplementation } from './sync/SSRWebStreamingSyncImplementation.js';
9
7
  import { SharedWebStreamingSyncImplementation } from './sync/SharedWebStreamingSyncImplementation.js';
10
8
  import { WebRemote } from './sync/WebRemote.js';
11
9
  import { WebStreamingSyncImplementation } from './sync/WebStreamingSyncImplementation.js';
10
+ import { AsyncDbAdapter } from './adapters/AsyncWebAdapter.js';
12
11
  export const DEFAULT_POWERSYNC_FLAGS = {
13
12
  ...DEFAULT_WEB_SQL_FLAGS,
14
13
  externallyUnload: false
@@ -48,20 +47,15 @@ function assertValidDatabaseOptions(options) {
48
47
  export class PowerSyncDatabase extends AbstractPowerSyncDatabase {
49
48
  options;
50
49
  static SHARED_MUTEX = new Mutex();
51
- unloadListener;
52
50
  resolvedFlags;
53
51
  constructor(options) {
54
52
  super(options);
55
53
  this.options = options;
56
54
  assertValidDatabaseOptions(options);
57
55
  this.resolvedFlags = resolveWebPowerSyncFlags(options.flags);
58
- if (this.resolvedFlags.enableMultiTabs && !this.resolvedFlags.externallyUnload) {
59
- this.unloadListener = () => this.close({ disconnect: false });
60
- window.addEventListener('unload', this.unloadListener);
61
- }
62
56
  }
63
57
  async _initialize() {
64
- if (this.database instanceof LockedAsyncDatabaseAdapter) {
58
+ if (this.database instanceof AsyncDbAdapter) {
65
59
  /**
66
60
  * While init is done automatically,
67
61
  * LockedAsyncDatabaseAdapter only exposes config after init.
@@ -99,9 +93,6 @@ export class PowerSyncDatabase extends AbstractPowerSyncDatabase {
99
93
  * multiple tabs are not enabled.
100
94
  */
101
95
  close(options) {
102
- if (this.unloadListener) {
103
- window.removeEventListener('unload', this.unloadListener);
104
- }
105
96
  return super.close({
106
97
  // Don't disconnect by default if multiple tabs are enabled
107
98
  disconnect: options?.disconnect ?? !this.resolvedFlags.enableMultiTabs
@@ -0,0 +1,50 @@
1
+ import { ConnectionPool, DBAdapterListener, DBLockOptions, LockContext } from '@powersync/common';
2
+ import { SharedConnectionWorker, WebDBAdapter, WebDBAdapterConfiguration } from './WebDBAdapter.js';
3
+ import { DatabaseClient } from './wa-sqlite/DatabaseClient.js';
4
+ /**
5
+ * A connection pool implementation delegating to another pool opened asynchronnously.
6
+ */
7
+ declare class AsyncConnectionPool implements ConnectionPool {
8
+ readonly name: string;
9
+ protected readonly state: Promise<PoolState>;
10
+ protected resolvedWriter?: DatabaseClient;
11
+ private readonly pendingListeners;
12
+ constructor(inner: Promise<PoolConnection>, name: string);
13
+ init(): Promise<void>;
14
+ close(): Promise<void>;
15
+ readLock<T>(fn: (tx: LockContext) => Promise<T>, options?: DBLockOptions): Promise<T>;
16
+ writeLock<T>(fn: (tx: LockContext) => Promise<T>, options?: DBLockOptions): Promise<T>;
17
+ refreshSchema(): Promise<void>;
18
+ registerListener(listener: Partial<DBAdapterListener>): () => void;
19
+ }
20
+ export interface PoolConnection {
21
+ writer: DatabaseClient;
22
+ additionalReaders: DatabaseClient[];
23
+ }
24
+ interface PoolState {
25
+ writer: DatabaseClient;
26
+ withConnection<T>(allowReadOnly: boolean, fn: (tx: LockContext) => Promise<T>, options?: DBLockOptions): Promise<T>;
27
+ close(): Promise<void>;
28
+ refreshSchema(): Promise<void>;
29
+ }
30
+ declare const AsyncDbAdapter_base: (new (...args: any[]) => {
31
+ readTransaction<T>(fn: (tx: import("@powersync/common").Transaction) => Promise<T>, options?: DBLockOptions): Promise<T>;
32
+ writeTransaction<T>(fn: (tx: import("@powersync/common").Transaction) => Promise<T>, options?: DBLockOptions): Promise<T>;
33
+ getAll<T>(sql: string, parameters?: any[]): Promise<T[]>;
34
+ getOptional<T>(sql: string, parameters?: any[]): Promise<T | null>;
35
+ get<T>(sql: string, parameters?: any[]): Promise<T>;
36
+ execute(query: string, params?: any[]): Promise<import("@powersync/common").QueryResult>;
37
+ executeRaw(query: string, params?: any[]): Promise<any[][]>;
38
+ executeBatch(query: string, params?: any[][]): Promise<import("@powersync/common").QueryResult>;
39
+ name: string;
40
+ close: () => void | Promise<void>;
41
+ readLock: <T>(fn: (tx: LockContext) => Promise<T>, options?: DBLockOptions) => Promise<T>;
42
+ writeLock: <T>(fn: (tx: LockContext) => Promise<T>, options?: DBLockOptions) => Promise<T>;
43
+ refreshSchema: () => Promise<void>;
44
+ registerListener(listener: Partial<DBAdapterListener>): () => void;
45
+ }) & typeof AsyncConnectionPool;
46
+ export declare class AsyncDbAdapter extends AsyncDbAdapter_base implements WebDBAdapter {
47
+ shareConnection(): Promise<SharedConnectionWorker>;
48
+ getConfiguration(): WebDBAdapterConfiguration;
49
+ }
50
+ export {};
@@ -0,0 +1,163 @@
1
+ import { DBAdapterDefaultMixin, Mutex, Semaphore } from '@powersync/common';
2
+ /**
3
+ * A connection pool implementation delegating to another pool opened asynchronnously.
4
+ */
5
+ class AsyncConnectionPool {
6
+ name;
7
+ state;
8
+ resolvedWriter;
9
+ pendingListeners = new Set();
10
+ constructor(inner, name) {
11
+ this.name = name;
12
+ this.state = inner.then((client) => {
13
+ for (const pending of this.pendingListeners) {
14
+ pending.closeAfterRegisteredOnResolvedPool = client.writer.registerListener(pending.listener);
15
+ }
16
+ this.pendingListeners.clear();
17
+ this.resolvedWriter = client.writer;
18
+ if (client.additionalReaders.length) {
19
+ return readWritePoolState(client.writer, client.additionalReaders);
20
+ }
21
+ return singleConnectionPoolState(client.writer);
22
+ });
23
+ }
24
+ async init() {
25
+ await this.state;
26
+ }
27
+ async close() {
28
+ const state = await this.state;
29
+ await state.close();
30
+ }
31
+ async readLock(fn, options) {
32
+ const state = await this.state;
33
+ return state.withConnection(true, fn, options);
34
+ }
35
+ async writeLock(fn, options) {
36
+ const state = await this.state;
37
+ return state.withConnection(false, fn, options);
38
+ }
39
+ async refreshSchema() {
40
+ const state = await this.state;
41
+ await state.refreshSchema();
42
+ }
43
+ registerListener(listener) {
44
+ if (this.resolvedWriter) {
45
+ return this.resolvedWriter.registerListener(listener);
46
+ }
47
+ else {
48
+ const pending = { listener };
49
+ this.pendingListeners.add(pending);
50
+ return () => {
51
+ if (pending.closeAfterRegisteredOnResolvedPool) {
52
+ return pending.closeAfterRegisteredOnResolvedPool();
53
+ }
54
+ else {
55
+ // Has not been registered yet, we can just remove the pending listener.
56
+ this.pendingListeners.delete(pending);
57
+ }
58
+ };
59
+ }
60
+ }
61
+ }
62
+ function singleConnectionPoolState(connection) {
63
+ return {
64
+ writer: connection,
65
+ withConnection: (allowReadOnly, fn, options) => {
66
+ if (allowReadOnly) {
67
+ return connection.readLock(fn, options);
68
+ }
69
+ else {
70
+ return connection.writeLock(fn, options);
71
+ }
72
+ },
73
+ close: () => connection.close(),
74
+ refreshSchema: () => connection.refreshSchema()
75
+ };
76
+ }
77
+ function readWritePoolState(writer, readers) {
78
+ // DatabaseClients have locks internally, so these aren't necessary for correctness. However, our mutex and semaphore
79
+ // implementations are very cheap to cancel, which we use to dispatch reads to the first available connection (by
80
+ // simply requesting all of them and sticking with the first connection we get).
81
+ const writerMutex = new Mutex();
82
+ const readerSemaphore = new Semaphore(readers);
83
+ return {
84
+ writer,
85
+ async withConnection(allowReadOnly, fn, options) {
86
+ const abortController = new AbortController();
87
+ const abortSignal = abortController.signal;
88
+ let timeout = null;
89
+ let release;
90
+ if (options?.timeoutMs) {
91
+ timeout = setTimeout(() => abortController.abort, options.timeoutMs);
92
+ }
93
+ try {
94
+ if (allowReadOnly) {
95
+ let connection;
96
+ // Even if we have a pool of read connections, it's typically very small and we assume that most queries are
97
+ // reads. So, we want to request any connection from the read pool and the dedicated write connection (which
98
+ // can also serve reads). We race for the first connection we can obtain this way, and then abort the other
99
+ // request.
100
+ [connection, release] = await new Promise((resolve, reject) => {
101
+ let didComplete = false;
102
+ function complete() {
103
+ didComplete = true;
104
+ abortController.abort();
105
+ }
106
+ function completeSuccess(connection, returnFn) {
107
+ if (didComplete) {
108
+ // We're not going to use this connection, so return it immediately.
109
+ returnFn();
110
+ }
111
+ else {
112
+ complete();
113
+ resolve([connection, returnFn]);
114
+ }
115
+ }
116
+ function completeError(error) {
117
+ // We either have a working connection already, or we've rejected the promise. Either way, we don't need
118
+ // to do either thing again.
119
+ if (didComplete)
120
+ return;
121
+ complete();
122
+ reject(error);
123
+ }
124
+ writerMutex.acquire(abortSignal).then((unlock) => completeSuccess(writer, unlock), completeError);
125
+ readerSemaphore
126
+ .requestOne(abortSignal)
127
+ .then(({ item, release }) => completeSuccess(item, release), completeError);
128
+ });
129
+ return await connection.readLock(fn);
130
+ }
131
+ else {
132
+ return await writerMutex.runExclusive(() => writer.writeLock(fn), abortSignal);
133
+ }
134
+ }
135
+ finally {
136
+ if (timeout != null) {
137
+ clearTimeout(timeout);
138
+ }
139
+ release?.();
140
+ }
141
+ },
142
+ async close() {
143
+ await writer.close();
144
+ await Promise.all(readers.map((r) => r.close()));
145
+ },
146
+ async refreshSchema() {
147
+ await writer.refreshSchema();
148
+ await Promise.all(readers.map((r) => r.refreshSchema()));
149
+ }
150
+ };
151
+ }
152
+ export class AsyncDbAdapter extends DBAdapterDefaultMixin(AsyncConnectionPool) {
153
+ async shareConnection() {
154
+ const state = await this.state;
155
+ return state.writer.shareConnection();
156
+ }
157
+ getConfiguration() {
158
+ if (this.resolvedWriter) {
159
+ return this.resolvedWriter.getConfiguration();
160
+ }
161
+ throw new Error('AsyncDbAdapter.getConfiguration() can only be called after initializing it.');
162
+ }
163
+ }
@@ -1,5 +1,4 @@
1
- import { BaseObserver, DBAdapterListener, DBAdapter, DBLockOptions, LockContext, QueryResult, Transaction } from '@powersync/common';
2
- import { Mutex } from 'async-mutex';
1
+ import { BaseObserver, DBAdapterListener, DBAdapter, DBLockOptions, LockContext, QueryResult, Transaction, Mutex } from '@powersync/common';
3
2
  /**
4
3
  * Implements a Mock DB adapter for use in Server Side Rendering (SSR).
5
4
  * This adapter will return empty results for queries, which will allow
@@ -1,5 +1,4 @@
1
- import { BaseObserver } from '@powersync/common';
2
- import { Mutex } from 'async-mutex';
1
+ import { BaseObserver, Mutex, timeoutSignal } from '@powersync/common';
3
2
  const MOCK_QUERY_RESPONSE = {
4
3
  rowsAffected: 0
5
4
  };
@@ -20,16 +19,16 @@ export class SSRDBAdapter extends BaseObserver {
20
19
  }
21
20
  close() { }
22
21
  async readLock(fn, options) {
23
- return this.readMutex.runExclusive(() => fn(this));
22
+ return this.readMutex.runExclusive(() => fn(this), timeoutSignal(options?.timeoutMs));
24
23
  }
25
24
  async readTransaction(fn, options) {
26
- return this.readLock(() => fn(this.generateMockTransactionContext()));
25
+ return this.readLock(() => fn(this.generateMockTransactionContext()), options);
27
26
  }
28
27
  async writeLock(fn, options) {
29
- return this.writeMutex.runExclusive(() => fn(this));
28
+ return this.writeMutex.runExclusive(() => fn(this), timeoutSignal(options?.timeoutMs));
30
29
  }
31
30
  async writeTransaction(fn, options) {
32
- return this.writeLock(() => fn(this.generateMockTransactionContext()));
31
+ return this.writeLock(() => fn(this.generateMockTransactionContext()), options);
33
32
  }
34
33
  async execute(query, params) {
35
34
  return this.writeMutex.runExclusive(async () => MOCK_QUERY_RESPONSE);
@@ -0,0 +1,56 @@
1
+ import { UnlockFn } from '@powersync/common';
2
+ import { RawSqliteConnection } from './RawSqliteConnection.js';
3
+ import { ResolvedWASQLiteOpenFactoryOptions } from './WASQLiteOpenFactory.js';
4
+ /**
5
+ * A wrapper around a {@link RawSqliteConnection} allowing multiple tabs to access it.
6
+ *
7
+ * To allow potentially concurrent accesses from different clients, this requires a local mutex implementation here.
8
+ *
9
+ * Note that instances of this class are not safe to proxy across context boundaries with comlink! We need to be able to
10
+ * rely on mutexes being returned reliably, so additional checks to detect say a client tab closing are required to
11
+ * avoid deadlocks.
12
+ */
13
+ export declare class ConcurrentSqliteConnection {
14
+ private readonly inner;
15
+ /**
16
+ * An outer mutex ensuring at most one {@link ConnectionLeaseToken} can exist for this connection at a time.
17
+ *
18
+ * If null, we'll use navigator locks instead.
19
+ */
20
+ private leaseMutex;
21
+ /**
22
+ * @param needsNavigatorLocks Whether access to the database needs an additional navigator lock guard.
23
+ *
24
+ * While {@link ConcurrentSqliteConnection} prevents concurrent access to a database _connection_, it's possible we
25
+ * might have multiple connections to the same physical database (e.g. if multiple tabs use dedicated workers).
26
+ * In those setups, we use navigator locks instead of an internal mutex to guard access..
27
+ */
28
+ constructor(inner: RawSqliteConnection, needsNavigatorLocks: boolean);
29
+ get options(): ResolvedWASQLiteOpenFactoryOptions;
30
+ acquireMutex(abort?: AbortSignal): Promise<UnlockFn>;
31
+ unsafeUseInner(): RawSqliteConnection;
32
+ /**
33
+ * @returns A {@link ConnectionLeaseToken}. Until that token is returned, no other client can use the database.
34
+ */
35
+ acquireConnection(abort?: AbortSignal): Promise<ConnectionLeaseToken>;
36
+ close(): Promise<void>;
37
+ }
38
+ /**
39
+ * An instance representing temporary exclusive access to a {@link ConcurrentSqliteConnection}.
40
+ */
41
+ export declare class ConnectionLeaseToken {
42
+ private returnMutex;
43
+ private connection;
44
+ /** Ensures that the client with access to this token can't run statements concurrently. */
45
+ private useMutex;
46
+ private closed;
47
+ constructor(returnMutex: UnlockFn, connection: RawSqliteConnection);
48
+ /**
49
+ * Returns this lease, allowing another client to use the database connection.
50
+ */
51
+ returnLease(): Promise<void>;
52
+ /**
53
+ * This should only be used internally, since the callback must not use the raw connection after resolving.
54
+ */
55
+ use<T>(callback: (conn: RawSqliteConnection) => Promise<T>): Promise<T>;
56
+ }
@@ -0,0 +1,121 @@
1
+ import { Mutex } from '@powersync/common';
2
+ /**
3
+ * A wrapper around a {@link RawSqliteConnection} allowing multiple tabs to access it.
4
+ *
5
+ * To allow potentially concurrent accesses from different clients, this requires a local mutex implementation here.
6
+ *
7
+ * Note that instances of this class are not safe to proxy across context boundaries with comlink! We need to be able to
8
+ * rely on mutexes being returned reliably, so additional checks to detect say a client tab closing are required to
9
+ * avoid deadlocks.
10
+ */
11
+ export class ConcurrentSqliteConnection {
12
+ inner;
13
+ /**
14
+ * An outer mutex ensuring at most one {@link ConnectionLeaseToken} can exist for this connection at a time.
15
+ *
16
+ * If null, we'll use navigator locks instead.
17
+ */
18
+ leaseMutex;
19
+ /**
20
+ * @param needsNavigatorLocks Whether access to the database needs an additional navigator lock guard.
21
+ *
22
+ * While {@link ConcurrentSqliteConnection} prevents concurrent access to a database _connection_, it's possible we
23
+ * might have multiple connections to the same physical database (e.g. if multiple tabs use dedicated workers).
24
+ * In those setups, we use navigator locks instead of an internal mutex to guard access..
25
+ */
26
+ constructor(inner, needsNavigatorLocks) {
27
+ this.inner = inner;
28
+ this.leaseMutex = needsNavigatorLocks ? null : new Mutex();
29
+ }
30
+ get options() {
31
+ return this.inner.options;
32
+ }
33
+ acquireMutex(abort) {
34
+ if (this.leaseMutex) {
35
+ return this.leaseMutex.acquire(abort);
36
+ }
37
+ return new Promise((resolve, reject) => {
38
+ const options = { signal: abort };
39
+ navigator.locks
40
+ .request(`db-lock-${this.options.dbFilename}`, options, (_) => {
41
+ return new Promise((returnLock) => {
42
+ return resolve(() => {
43
+ returnLock();
44
+ });
45
+ });
46
+ })
47
+ .catch(reject);
48
+ });
49
+ }
50
+ // Unsafe, unguarded access to the SQLite connection.
51
+ unsafeUseInner() {
52
+ return this.inner;
53
+ }
54
+ /**
55
+ * @returns A {@link ConnectionLeaseToken}. Until that token is returned, no other client can use the database.
56
+ */
57
+ async acquireConnection(abort) {
58
+ const returnMutex = await this.acquireMutex(abort);
59
+ const token = new ConnectionLeaseToken(returnMutex, this.inner);
60
+ try {
61
+ // Assert that the inner connection is initialized at this point, fail early if it's not.
62
+ this.inner.requireSqlite();
63
+ // If a previous client was interrupted in the middle of a transaction AND this is a shared worker, it's possible
64
+ // for the connection to still be in a transaction. To avoid inconsistent state, we roll back connection leases
65
+ // that haven't been comitted.
66
+ if (!this.inner.isAutoCommit()) {
67
+ await this.inner.executeRaw('ROLLBACK');
68
+ }
69
+ }
70
+ catch (e) {
71
+ returnMutex();
72
+ throw e;
73
+ }
74
+ return token;
75
+ }
76
+ async close() {
77
+ const returnMutex = await this.acquireMutex();
78
+ try {
79
+ await this.inner.close();
80
+ }
81
+ finally {
82
+ returnMutex();
83
+ }
84
+ }
85
+ }
86
+ /**
87
+ * An instance representing temporary exclusive access to a {@link ConcurrentSqliteConnection}.
88
+ */
89
+ export class ConnectionLeaseToken {
90
+ returnMutex;
91
+ connection;
92
+ /** Ensures that the client with access to this token can't run statements concurrently. */
93
+ useMutex = new Mutex();
94
+ closed = false;
95
+ constructor(returnMutex, connection) {
96
+ this.returnMutex = returnMutex;
97
+ this.connection = connection;
98
+ }
99
+ /**
100
+ * Returns this lease, allowing another client to use the database connection.
101
+ */
102
+ async returnLease() {
103
+ await this.useMutex.runExclusive(async () => {
104
+ if (!this.closed) {
105
+ this.closed = true;
106
+ this.returnMutex();
107
+ }
108
+ });
109
+ }
110
+ /**
111
+ * This should only be used internally, since the callback must not use the raw connection after resolving.
112
+ */
113
+ async use(callback) {
114
+ return await this.useMutex.runExclusive(async () => {
115
+ if (this.closed) {
116
+ throw new Error('lease token has already been closed');
117
+ }
118
+ return await callback(this.connection);
119
+ });
120
+ }
121
+ }
@@ -0,0 +1,54 @@
1
+ import { LockContext, DBLockOptions, DBAdapterListener, ConnectionPool, BaseObserver, SQLOpenOptions } from '@powersync/common';
2
+ import { SharedConnectionWorker, WebDBAdapterConfiguration } from '../WebDBAdapter.js';
3
+ import { ClientConnectionView } from './DatabaseServer.js';
4
+ import * as Comlink from 'comlink';
5
+ import { WorkerDBOpenerOptions } from './WASQLiteOpenFactory.js';
6
+ export interface OpenWorkerConnection {
7
+ connect(config: WorkerDBOpenerOptions): Promise<ClientConnectionView>;
8
+ connectToExisting(options: {
9
+ identifier: string;
10
+ lockName: string;
11
+ }): Promise<ClientConnectionView>;
12
+ }
13
+ export interface ClientOptions {
14
+ connection: ClientConnectionView;
15
+ /**
16
+ * The remote from which the {@link connection} has been obtained.
17
+ *
18
+ * We use this to be able to expose this port to other clients wanting to connect to the database (e.g. the shared
19
+ * sync worker).
20
+ *
21
+ * For sources not based on workers, returns null.
22
+ */
23
+ source: Comlink.Remote<OpenWorkerConnection> | null;
24
+ /**
25
+ * Whether the remote we're connecting to can close unexpectedly (e.g. because we're a shared worker connecting to a
26
+ * dedicated worker handle we've received from a tab).
27
+ */
28
+ remoteCanCloseUnexpectedly: boolean;
29
+ onClose?: () => void;
30
+ }
31
+ /**
32
+ * A single-connection {@link ConnectionPool} implementation based on a worker connection.
33
+ */
34
+ export declare class DatabaseClient<Config extends SQLOpenOptions = WebDBAdapterConfiguration> extends BaseObserver<DBAdapterListener> implements ConnectionPool {
35
+ #private;
36
+ private readonly options;
37
+ private readonly config;
38
+ constructor(options: ClientOptions, config: Config);
39
+ get name(): string;
40
+ /**
41
+ * Marks the remote as closed.
42
+ *
43
+ * This can sometimes happen outside of our control, e.g. when a shared worker requests a connection from a tab. When
44
+ * it happens, all outstanding requests on this pool would never resolve. To avoid livelocks in this scenario, we
45
+ * throw on all outstanding promises and forbid new calls.
46
+ */
47
+ markRemoteClosed(): void;
48
+ close(): Promise<void>;
49
+ readLock<T>(fn: (tx: LockContext) => Promise<T>, options?: DBLockOptions): Promise<T>;
50
+ writeLock<T>(fn: (tx: LockContext) => Promise<T>, options?: DBLockOptions): Promise<T>;
51
+ refreshSchema(): Promise<void>;
52
+ shareConnection(): Promise<SharedConnectionWorker>;
53
+ getConfiguration(): Config;
54
+ }