@powersync/web 0.0.0-dev-20251119142638 → 0.0.0-dev-20251120085122
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.
- package/dist/index.umd.js +21 -6
- package/dist/index.umd.js.map +1 -1
- package/dist/worker/SharedSyncImplementation.umd.js +14 -6
- package/dist/worker/SharedSyncImplementation.umd.js.map +1 -1
- package/dist/worker/WASQLiteDB.umd.js +28 -34
- package/dist/worker/WASQLiteDB.umd.js.map +1 -1
- package/lib/src/db/adapters/AsyncDatabaseConnection.d.ts +13 -0
- package/lib/src/db/adapters/WorkerWrappedAsyncDatabaseConnection.d.ts +1 -0
- package/lib/src/db/adapters/WorkerWrappedAsyncDatabaseConnection.js +7 -2
- package/lib/src/db/adapters/wa-sqlite/WASQLiteConnection.d.ts +5 -0
- package/lib/src/db/adapters/wa-sqlite/WASQLiteConnection.js +7 -0
- package/lib/src/worker/db/SharedWASQLiteConnection.d.ts +4 -3
- package/lib/src/worker/db/SharedWASQLiteConnection.js +17 -17
- package/lib/src/worker/db/WASQLiteDB.worker.js +2 -2
- package/lib/src/worker/db/WorkerWASQLiteConnection.d.ts +3 -3
- package/lib/src/worker/db/WorkerWASQLiteConnection.js +2 -14
- package/lib/src/worker/sync/SharedSyncImplementation.d.ts +2 -2
- package/lib/src/worker/sync/SharedSyncImplementation.js +7 -4
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/db/adapters/AsyncDatabaseConnection.ts +13 -0
- package/src/db/adapters/WorkerWrappedAsyncDatabaseConnection.ts +8 -2
- package/src/db/adapters/wa-sqlite/WASQLiteConnection.ts +8 -0
- package/src/worker/db/SharedWASQLiteConnection.ts +23 -20
- package/src/worker/db/WASQLiteDB.worker.ts +2 -2
- package/src/worker/db/WorkerWASQLiteConnection.ts +3 -18
- package/src/worker/sync/SharedSyncImplementation.ts +15 -12
|
@@ -22,8 +22,21 @@ export type OnTableChangeCallback = (event: BatchedUpdateNotification) => void;
|
|
|
22
22
|
export interface AsyncDatabaseConnection<Config extends ResolvedWebSQLOpenOptions = ResolvedWebSQLOpenOptions> {
|
|
23
23
|
init(): Promise<void>;
|
|
24
24
|
close(): Promise<void>;
|
|
25
|
+
/**
|
|
26
|
+
* Marks the connection as in-use by a certain actor.
|
|
27
|
+
* @returns A hold ID which can be used to release the hold.
|
|
28
|
+
*/
|
|
25
29
|
markHold(): Promise<string>;
|
|
30
|
+
/**
|
|
31
|
+
* Releases a hold on the connection.
|
|
32
|
+
* @param holdId The hold ID to release.
|
|
33
|
+
*/
|
|
26
34
|
releaseHold(holdId: string): Promise<void>;
|
|
35
|
+
/**
|
|
36
|
+
* Checks if the database connection is in autocommit mode.
|
|
37
|
+
* @returns true if in autocommit mode, false if in a transaction
|
|
38
|
+
*/
|
|
39
|
+
isAutoCommit(): Promise<boolean>;
|
|
27
40
|
execute(sql: string, params?: any[]): Promise<ProxiedQueryResult>;
|
|
28
41
|
executeRaw(sql: string, params?: any[]): Promise<any[][]>;
|
|
29
42
|
executeBatch(sql: string, params?: any[]): Promise<ProxiedQueryResult>;
|
|
@@ -36,6 +36,7 @@ export declare class WorkerWrappedAsyncDatabaseConnection<Config extends Resolve
|
|
|
36
36
|
markRemoteClosed(): void;
|
|
37
37
|
markHold(): Promise<string>;
|
|
38
38
|
releaseHold(holdId: string): Promise<void>;
|
|
39
|
+
isAutoCommit(): Promise<boolean>;
|
|
39
40
|
private withRemote;
|
|
40
41
|
/**
|
|
41
42
|
* Get a MessagePort which can be used to share the internals of this connection.
|
|
@@ -32,10 +32,13 @@ export class WorkerWrappedAsyncDatabaseConnection {
|
|
|
32
32
|
this.notifyRemoteClosed.abort();
|
|
33
33
|
}
|
|
34
34
|
markHold() {
|
|
35
|
-
return this.baseConnection.markHold();
|
|
35
|
+
return this.withRemote(() => this.baseConnection.markHold());
|
|
36
36
|
}
|
|
37
37
|
releaseHold(holdId) {
|
|
38
|
-
return this.baseConnection.releaseHold(holdId);
|
|
38
|
+
return this.withRemote(() => this.baseConnection.releaseHold(holdId));
|
|
39
|
+
}
|
|
40
|
+
isAutoCommit() {
|
|
41
|
+
return this.baseConnection.isAutoCommit();
|
|
39
42
|
}
|
|
40
43
|
withRemote(workerPromise) {
|
|
41
44
|
const controller = this.notifyRemoteClosed;
|
|
@@ -43,6 +46,8 @@ export class WorkerWrappedAsyncDatabaseConnection {
|
|
|
43
46
|
return new Promise((resolve, reject) => {
|
|
44
47
|
if (controller.signal.aborted) {
|
|
45
48
|
reject(new Error('Called operation on closed remote'));
|
|
49
|
+
// Don't run the operation if we're going to reject
|
|
50
|
+
return;
|
|
46
51
|
}
|
|
47
52
|
function handleAbort() {
|
|
48
53
|
reject(new Error('Remote peer closed with request in flight'));
|
|
@@ -112,6 +112,11 @@ export declare class WASqliteConnection extends BaseObserver<WASQLiteConnectionL
|
|
|
112
112
|
get currentHoldId(): string | null;
|
|
113
113
|
protected get sqliteAPI(): SQLiteAPI;
|
|
114
114
|
protected get dbP(): number;
|
|
115
|
+
/**
|
|
116
|
+
* Checks if the database connection is in autocommit mode.
|
|
117
|
+
* @returns true if in autocommit mode, false if in a transaction
|
|
118
|
+
*/
|
|
119
|
+
isAutoCommit(): Promise<boolean>;
|
|
115
120
|
markHold(): Promise<string>;
|
|
116
121
|
releaseHold(holdId: string): Promise<void>;
|
|
117
122
|
protected openDB(): Promise<number>;
|
|
@@ -140,6 +140,13 @@ export class WASqliteConnection extends BaseObserver {
|
|
|
140
140
|
}
|
|
141
141
|
return this._dbP;
|
|
142
142
|
}
|
|
143
|
+
/**
|
|
144
|
+
* Checks if the database connection is in autocommit mode.
|
|
145
|
+
* @returns true if in autocommit mode, false if in a transaction
|
|
146
|
+
*/
|
|
147
|
+
async isAutoCommit() {
|
|
148
|
+
return this.sqliteAPI.get_autocommit(this.dbP) != 0;
|
|
149
|
+
}
|
|
143
150
|
async markHold() {
|
|
144
151
|
const previousHoldId = this._holdId;
|
|
145
152
|
this._holdId = `${++this._holdCounter}`;
|
|
@@ -20,13 +20,14 @@ export declare class SharedWASQLiteConnection implements AsyncDatabaseConnection
|
|
|
20
20
|
protected isClosing: boolean;
|
|
21
21
|
protected activeHoldId: string | null;
|
|
22
22
|
constructor(options: SharedWASQLiteConnectionOptions);
|
|
23
|
-
init(): Promise<void>;
|
|
24
|
-
markHold(): Promise<string>;
|
|
25
|
-
releaseHold(id: string): Promise<void>;
|
|
26
23
|
protected get logger(): ILogger;
|
|
27
24
|
protected get dbEntry(): SharedDBWorkerConnection;
|
|
28
25
|
protected get connection(): AsyncDatabaseConnection<ResolvedWebSQLOpenOptions>;
|
|
29
26
|
protected get clientIds(): Set<number>;
|
|
27
|
+
init(): Promise<void>;
|
|
28
|
+
markHold(): Promise<string>;
|
|
29
|
+
releaseHold(id: string): Promise<void>;
|
|
30
|
+
isAutoCommit(): Promise<boolean>;
|
|
30
31
|
/**
|
|
31
32
|
* Handles closing of a shared connection.
|
|
32
33
|
* The connection is only closed if there are no active clients using it.
|
|
@@ -10,6 +10,18 @@ export class SharedWASQLiteConnection {
|
|
|
10
10
|
this.isClosing = false;
|
|
11
11
|
this.activeHoldId = null;
|
|
12
12
|
}
|
|
13
|
+
get logger() {
|
|
14
|
+
return this.options.logger;
|
|
15
|
+
}
|
|
16
|
+
get dbEntry() {
|
|
17
|
+
return this.options.dbMap.get(this.options.dbFilename);
|
|
18
|
+
}
|
|
19
|
+
get connection() {
|
|
20
|
+
return this.dbEntry.db;
|
|
21
|
+
}
|
|
22
|
+
get clientIds() {
|
|
23
|
+
return this.dbEntry.clientIds;
|
|
24
|
+
}
|
|
13
25
|
async init() {
|
|
14
26
|
// No-op since the connection is already initialized when it was created
|
|
15
27
|
}
|
|
@@ -25,17 +37,8 @@ export class SharedWASQLiteConnection {
|
|
|
25
37
|
this.activeHoldId = null;
|
|
26
38
|
}
|
|
27
39
|
}
|
|
28
|
-
|
|
29
|
-
return this.
|
|
30
|
-
}
|
|
31
|
-
get dbEntry() {
|
|
32
|
-
return this.options.dbMap.get(this.options.dbFilename);
|
|
33
|
-
}
|
|
34
|
-
get connection() {
|
|
35
|
-
return this.dbEntry.db;
|
|
36
|
-
}
|
|
37
|
-
get clientIds() {
|
|
38
|
-
return this.dbEntry.clientIds;
|
|
40
|
+
async isAutoCommit() {
|
|
41
|
+
return this.connection.isAutoCommit();
|
|
39
42
|
}
|
|
40
43
|
/**
|
|
41
44
|
* Handles closing of a shared connection.
|
|
@@ -49,12 +52,9 @@ export class SharedWASQLiteConnection {
|
|
|
49
52
|
logger.debug(`Close requested from client ${clientId} of ${[...clientIds]}`);
|
|
50
53
|
clientIds.delete(clientId);
|
|
51
54
|
if (this.activeHoldId) {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
*/
|
|
56
|
-
await this.connection.execute('ROLLBACK').catch(() => { });
|
|
57
|
-
await this.connection.releaseHold(this.activeHoldId).catch(() => { });
|
|
55
|
+
// We can't cleanup here since we're not in a lock context.
|
|
56
|
+
// The cleanup will occur once a new hold is acquired.
|
|
57
|
+
this.logger.info(`Hold ${this.activeHoldId} was still active when the connection was closed. Cleanup will occur once a new hold is acquired.`);
|
|
58
58
|
}
|
|
59
59
|
if (clientIds.size == 0) {
|
|
60
60
|
logger.debug(`Closing connection to ${this.options}.`);
|
|
@@ -6,7 +6,7 @@ import { createBaseLogger, createLogger } from '@powersync/common';
|
|
|
6
6
|
import * as Comlink from 'comlink';
|
|
7
7
|
import { getNavigatorLocks } from '../../shared/navigator';
|
|
8
8
|
import { SharedWASQLiteConnection } from './SharedWASQLiteConnection';
|
|
9
|
-
import { WorkerWASQLiteConnection
|
|
9
|
+
import { WorkerWASQLiteConnection } from './WorkerWASQLiteConnection';
|
|
10
10
|
const baseLogger = createBaseLogger();
|
|
11
11
|
baseLogger.useDefaults();
|
|
12
12
|
const logger = createLogger('db-worker');
|
|
@@ -46,7 +46,7 @@ const openDBShared = async (options) => {
|
|
|
46
46
|
clientId,
|
|
47
47
|
logger
|
|
48
48
|
});
|
|
49
|
-
return
|
|
49
|
+
return Comlink.proxy(sharedConnection);
|
|
50
50
|
});
|
|
51
51
|
};
|
|
52
52
|
// Check if we're in a SharedWorker context
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { OnTableChangeCallback } from '../../db/adapters/AsyncDatabaseConnection';
|
|
2
2
|
import { WASqliteConnection } from '../../db/adapters/wa-sqlite/WASQLiteConnection';
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* A Small proxy wrapper around the WASqliteConnection.
|
|
5
|
+
* This ensures that certain return types are properly proxied.
|
|
5
6
|
*/
|
|
6
|
-
export declare function proxyWASQLiteConnection(connection: AsyncDatabaseConnection): AsyncDatabaseConnection;
|
|
7
7
|
export declare class WorkerWASQLiteConnection extends WASqliteConnection {
|
|
8
8
|
registerOnTableChange(callback: OnTableChangeCallback): Promise<() => void>;
|
|
9
9
|
}
|
|
@@ -1,21 +1,9 @@
|
|
|
1
1
|
import * as Comlink from 'comlink';
|
|
2
2
|
import { WASqliteConnection } from '../../db/adapters/wa-sqlite/WASQLiteConnection';
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* A Small proxy wrapper around the WASqliteConnection.
|
|
5
|
+
* This ensures that certain return types are properly proxied.
|
|
5
6
|
*/
|
|
6
|
-
export function proxyWASQLiteConnection(connection) {
|
|
7
|
-
return Comlink.proxy({
|
|
8
|
-
init: Comlink.proxy(() => connection.init()),
|
|
9
|
-
close: Comlink.proxy(() => connection.close()),
|
|
10
|
-
markHold: Comlink.proxy(() => connection.markHold()),
|
|
11
|
-
releaseHold: Comlink.proxy((holdId) => connection.releaseHold(holdId)),
|
|
12
|
-
execute: Comlink.proxy((sql, params) => connection.execute(sql, params)),
|
|
13
|
-
executeRaw: Comlink.proxy((sql, params) => connection.executeRaw(sql, params)),
|
|
14
|
-
executeBatch: Comlink.proxy((sql, params) => connection.executeBatch(sql, params)),
|
|
15
|
-
registerOnTableChange: Comlink.proxy((callback) => connection.registerOnTableChange(callback)),
|
|
16
|
-
getConfig: Comlink.proxy(() => connection.getConfig())
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
7
|
export class WorkerWASQLiteConnection extends WASqliteConnection {
|
|
20
8
|
async registerOnTableChange(callback) {
|
|
21
9
|
// Proxy the callback remove function
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ILogger, type ILogLevel, type PowerSyncConnectionOptions, type StreamingSyncImplementation, type StreamingSyncImplementationListener, type SyncStatusOptions
|
|
1
|
+
import { BaseObserver, ConnectionManager, DBAdapter, SubscribedStream, SyncStatus, type ILogger, type ILogLevel, type PowerSyncConnectionOptions, type StreamingSyncImplementation, type StreamingSyncImplementationListener, type SyncStatusOptions } from '@powersync/common';
|
|
2
2
|
import { Mutex } from 'async-mutex';
|
|
3
3
|
import * as Comlink from 'comlink';
|
|
4
4
|
import { WebStreamingSyncImplementation, WebStreamingSyncImplementationOptions } from '../../db/sync/WebStreamingSyncImplementation';
|
|
@@ -44,7 +44,7 @@ export type WrappedSyncPort = {
|
|
|
44
44
|
clientProvider: Comlink.Remote<AbstractSharedSyncClientProvider>;
|
|
45
45
|
db?: DBAdapter;
|
|
46
46
|
currentSubscriptions: SubscribedStream[];
|
|
47
|
-
closeListeners: (() => void)[];
|
|
47
|
+
closeListeners: (() => void | Promise<void>)[];
|
|
48
48
|
};
|
|
49
49
|
/**
|
|
50
50
|
* @internal
|
|
@@ -228,11 +228,14 @@ export class SharedSyncImplementation extends BaseObserver {
|
|
|
228
228
|
return () => { };
|
|
229
229
|
}
|
|
230
230
|
for (const closeListener of trackedPort.closeListeners) {
|
|
231
|
-
closeListener();
|
|
231
|
+
await closeListener();
|
|
232
232
|
}
|
|
233
233
|
if (this.dbAdapter && this.dbAdapter == trackedPort.db) {
|
|
234
234
|
// Unconditionally close the connection because the database it's writing to has just been closed.
|
|
235
|
-
|
|
235
|
+
// The connection has been closed previously, this might throw. We should be able to ignore it.
|
|
236
|
+
await this.connectionManager
|
|
237
|
+
.disconnect()
|
|
238
|
+
.catch((ex) => this.logger.warn('Error while disconnecting. Will attempt to reconnect.', ex));
|
|
236
239
|
// Clearing the adapter will result in a new one being opened in connect
|
|
237
240
|
this.dbAdapter = null;
|
|
238
241
|
if (shouldReconnect) {
|
|
@@ -361,9 +364,9 @@ export class SharedSyncImplementation extends BaseObserver {
|
|
|
361
364
|
// that and ensure pending requests are aborted when the tab is closed.
|
|
362
365
|
remoteCanCloseUnexpectedly: true
|
|
363
366
|
});
|
|
364
|
-
lastClient.closeListeners.push(() => {
|
|
367
|
+
lastClient.closeListeners.push(async () => {
|
|
365
368
|
this.logger.info('Aborting open connection because associated tab closed.');
|
|
366
|
-
wrapped.close();
|
|
369
|
+
await wrapped.close().catch((ex) => this.logger.warn('error closing database connection', ex));
|
|
367
370
|
wrapped.markRemoteClosed();
|
|
368
371
|
});
|
|
369
372
|
return wrapped;
|