@powersync/web 0.0.0-dev-20250207081035 → 0.0.0-dev-20250210155038
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 +44 -4
- package/dist/index.umd.js.map +1 -1
- package/dist/worker/SharedSyncImplementation.umd.js +54 -23
- package/dist/worker/SharedSyncImplementation.umd.js.map +1 -1
- package/dist/worker/WASQLiteDB.umd.js +24 -23
- package/dist/worker/WASQLiteDB.umd.js.map +1 -1
- package/lib/package.json +2 -6
- package/lib/src/db/adapters/WorkerWrappedAsyncDatabaseConnection.d.ts +2 -0
- package/lib/src/db/adapters/WorkerWrappedAsyncDatabaseConnection.js +31 -0
- package/lib/src/db/adapters/wa-sqlite/WASQLiteConnection.js +1 -0
- package/lib/src/db/adapters/wa-sqlite/WASQLiteDBAdapter.d.ts +1 -0
- package/lib/src/db/adapters/wa-sqlite/WASQLiteDBAdapter.js +4 -2
- package/lib/src/db/adapters/wa-sqlite/WASQLiteOpenFactory.js +5 -2
- package/lib/src/db/adapters/web-sql-flags.d.ts +10 -0
- package/lib/src/db/adapters/web-sql-flags.js +1 -0
- package/lib/src/db/sync/SharedWebStreamingSyncImplementation.js +3 -3
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -9
package/lib/package.json
CHANGED
|
@@ -74,20 +74,16 @@
|
|
|
74
74
|
"devDependencies": {
|
|
75
75
|
"@journeyapps/wa-sqlite": "^1.2.0",
|
|
76
76
|
"@types/uuid": "^9.0.6",
|
|
77
|
-
"@vitest/browser": "^2.1.4",
|
|
78
77
|
"crypto-browserify": "^3.12.0",
|
|
79
78
|
"p-defer": "^4.0.1",
|
|
80
79
|
"source-map-loader": "^5.0.0",
|
|
81
80
|
"stream-browserify": "^3.0.0",
|
|
82
81
|
"terser-webpack-plugin": "^5.3.9",
|
|
83
|
-
"typescript": "^5.5.3",
|
|
84
82
|
"uuid": "^9.0.1",
|
|
85
|
-
"vite": "^
|
|
86
|
-
"vite-plugin-top-level-await": "^1.4.
|
|
83
|
+
"vite": "^6.1.0",
|
|
84
|
+
"vite-plugin-top-level-await": "^1.4.4",
|
|
87
85
|
"vite-plugin-wasm": "^3.3.0",
|
|
88
|
-
"vitest": "^2.1.4",
|
|
89
86
|
"vm-browserify": "^1.1.2",
|
|
90
|
-
"webdriverio": "^8.40.6",
|
|
91
87
|
"webpack": "^5.90.1",
|
|
92
88
|
"webpack-cli": "^5.1.4",
|
|
93
89
|
"webpack-node-externals": "^3.0.0"
|
|
@@ -20,6 +20,8 @@ export type WrappedWorkerConnectionOptions<Config extends ResolvedWebSQLOpenOpti
|
|
|
20
20
|
*/
|
|
21
21
|
export declare class WorkerWrappedAsyncDatabaseConnection<Config extends ResolvedWebSQLOpenOptions = ResolvedWebSQLOpenOptions> implements AsyncDatabaseConnection {
|
|
22
22
|
protected options: WrappedWorkerConnectionOptions<Config>;
|
|
23
|
+
protected releaseSharedConnectionLock: (() => void) | null;
|
|
24
|
+
protected lockAbortController: AbortController;
|
|
23
25
|
constructor(options: WrappedWorkerConnectionOptions<Config>);
|
|
24
26
|
protected get baseConnection(): AsyncDatabaseConnection<ResolvedWebSQLOpenOptions>;
|
|
25
27
|
init(): Promise<void>;
|
|
@@ -5,8 +5,12 @@ import * as Comlink from 'comlink';
|
|
|
5
5
|
*/
|
|
6
6
|
export class WorkerWrappedAsyncDatabaseConnection {
|
|
7
7
|
options;
|
|
8
|
+
releaseSharedConnectionLock;
|
|
9
|
+
lockAbortController;
|
|
8
10
|
constructor(options) {
|
|
9
11
|
this.options = options;
|
|
12
|
+
this.releaseSharedConnectionLock = null;
|
|
13
|
+
this.lockAbortController = new AbortController();
|
|
10
14
|
}
|
|
11
15
|
get baseConnection() {
|
|
12
16
|
return this.options.baseConnection;
|
|
@@ -19,6 +23,30 @@ export class WorkerWrappedAsyncDatabaseConnection {
|
|
|
19
23
|
*/
|
|
20
24
|
async shareConnection() {
|
|
21
25
|
const { identifier, remote } = this.options;
|
|
26
|
+
/**
|
|
27
|
+
* Hold a navigator lock in order to avoid features such as Chrome's frozen tabs,
|
|
28
|
+
* or Edge's sleeping tabs from pausing the thread for this connection.
|
|
29
|
+
* This promise resolves once a lock is obtained.
|
|
30
|
+
* This lock will be held as long as this connection is open.
|
|
31
|
+
* The `shareConnection` method should not be called on multiple tabs concurrently.
|
|
32
|
+
*/
|
|
33
|
+
await new Promise((lockObtained) => navigator.locks
|
|
34
|
+
.request(`shared-connection-${this.options.identifier}`, {
|
|
35
|
+
signal: this.lockAbortController.signal
|
|
36
|
+
}, async () => {
|
|
37
|
+
lockObtained();
|
|
38
|
+
// Free the lock when the connection is already closed.
|
|
39
|
+
if (this.lockAbortController.signal.aborted) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
// Hold the lock while the shared connection is in use.
|
|
43
|
+
await new Promise((releaseLock) => {
|
|
44
|
+
// We can use the resolver to free the lock
|
|
45
|
+
this.releaseSharedConnectionLock = releaseLock;
|
|
46
|
+
});
|
|
47
|
+
})
|
|
48
|
+
// We aren't concerned with errors here
|
|
49
|
+
.catch(() => { }));
|
|
22
50
|
const newPort = await remote[Comlink.createEndpoint]();
|
|
23
51
|
return { port: newPort, identifier };
|
|
24
52
|
}
|
|
@@ -30,6 +58,9 @@ export class WorkerWrappedAsyncDatabaseConnection {
|
|
|
30
58
|
return this.baseConnection.registerOnTableChange(Comlink.proxy(callback));
|
|
31
59
|
}
|
|
32
60
|
async close() {
|
|
61
|
+
// Abort any pending lock requests.
|
|
62
|
+
this.lockAbortController.abort();
|
|
63
|
+
this.releaseSharedConnectionLock?.();
|
|
33
64
|
await this.baseConnection.close();
|
|
34
65
|
this.options.remote[Comlink.releaseProxy]();
|
|
35
66
|
this.options.onClose?.();
|
|
@@ -184,6 +184,7 @@ export class WASqliteConnection extends BaseObserver {
|
|
|
184
184
|
await this.openDB();
|
|
185
185
|
this.registerBroadcastListeners();
|
|
186
186
|
await this.executeSingleStatement(`PRAGMA temp_store = ${this.options.temporaryStorage};`);
|
|
187
|
+
await this.executeSingleStatement(`PRAGMA cache_size = -${this.options.cacheSizeKb};`);
|
|
187
188
|
await this.executeEncryptionPragma();
|
|
188
189
|
this.sqliteAPI.update_hook(this.dbP, (updateType, dbName, tableName) => {
|
|
189
190
|
if (!tableName) {
|
|
@@ -17,6 +17,7 @@ export interface WASQLiteDBAdapterOptions extends Omit<PowerSyncOpenFactoryOptio
|
|
|
17
17
|
worker?: string | URL | ((options: ResolvedWebSQLOpenOptions) => Worker | SharedWorker);
|
|
18
18
|
vfs?: WASQLiteVFS;
|
|
19
19
|
temporaryStorage?: TemporaryStorageOption;
|
|
20
|
+
cacheSizeKb?: number;
|
|
20
21
|
/**
|
|
21
22
|
* Encryption key for the database.
|
|
22
23
|
* If set, the database will be encrypted using multiple-ciphers.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as Comlink from 'comlink';
|
|
2
2
|
import { resolveWebPowerSyncFlags } from '../../PowerSyncDatabase';
|
|
3
3
|
import { LockedAsyncDatabaseAdapter } from '../LockedAsyncDatabaseAdapter';
|
|
4
|
-
import { TemporaryStorageOption } from '../web-sql-flags';
|
|
4
|
+
import { DEFAULT_CACHE_SIZE_KB, TemporaryStorageOption } from '../web-sql-flags';
|
|
5
5
|
import { WorkerWrappedAsyncDatabaseConnection } from '../WorkerWrappedAsyncDatabaseConnection';
|
|
6
6
|
import { WASQLiteOpenFactory } from './WASQLiteOpenFactory';
|
|
7
7
|
/**
|
|
@@ -12,7 +12,7 @@ export class WASQLiteDBAdapter extends LockedAsyncDatabaseAdapter {
|
|
|
12
12
|
super({
|
|
13
13
|
name: options.dbFilename,
|
|
14
14
|
openConnection: async () => {
|
|
15
|
-
const { workerPort, temporaryStorage } = options;
|
|
15
|
+
const { workerPort, temporaryStorage, cacheSizeKb } = options;
|
|
16
16
|
if (workerPort) {
|
|
17
17
|
const remote = Comlink.wrap(workerPort);
|
|
18
18
|
return new WorkerWrappedAsyncDatabaseConnection({
|
|
@@ -21,6 +21,7 @@ export class WASQLiteDBAdapter extends LockedAsyncDatabaseAdapter {
|
|
|
21
21
|
baseConnection: await remote({
|
|
22
22
|
...options,
|
|
23
23
|
temporaryStorage: temporaryStorage ?? TemporaryStorageOption.MEMORY,
|
|
24
|
+
cacheSizeKb: cacheSizeKb ?? DEFAULT_CACHE_SIZE_KB,
|
|
24
25
|
flags: resolveWebPowerSyncFlags(options.flags),
|
|
25
26
|
encryptionKey: options.encryptionKey
|
|
26
27
|
})
|
|
@@ -32,6 +33,7 @@ export class WASQLiteDBAdapter extends LockedAsyncDatabaseAdapter {
|
|
|
32
33
|
debugMode: options.debugMode,
|
|
33
34
|
flags: options.flags,
|
|
34
35
|
temporaryStorage,
|
|
36
|
+
cacheSizeKb,
|
|
35
37
|
logger: options.logger,
|
|
36
38
|
vfs: options.vfs,
|
|
37
39
|
encryptionKey: options.encryptionKey,
|
|
@@ -2,7 +2,7 @@ import * as Comlink from 'comlink';
|
|
|
2
2
|
import { openWorkerDatabasePort, resolveWorkerDatabasePortFactory } from '../../../worker/db/open-worker-database';
|
|
3
3
|
import { AbstractWebSQLOpenFactory } from '../AbstractWebSQLOpenFactory';
|
|
4
4
|
import { LockedAsyncDatabaseAdapter } from '../LockedAsyncDatabaseAdapter';
|
|
5
|
-
import { TemporaryStorageOption } from '../web-sql-flags';
|
|
5
|
+
import { DEFAULT_CACHE_SIZE_KB, TemporaryStorageOption } from '../web-sql-flags';
|
|
6
6
|
import { WorkerWrappedAsyncDatabaseConnection } from '../WorkerWrappedAsyncDatabaseConnection';
|
|
7
7
|
import { WASqliteConnection, WASQLiteVFS } from './WASQLiteConnection';
|
|
8
8
|
/**
|
|
@@ -27,7 +27,7 @@ export class WASQLiteOpenFactory extends AbstractWebSQLOpenFactory {
|
|
|
27
27
|
}
|
|
28
28
|
async openConnection() {
|
|
29
29
|
const { enableMultiTabs, useWebWorker } = this.resolvedFlags;
|
|
30
|
-
const { vfs = WASQLiteVFS.IDBBatchAtomicVFS, temporaryStorage = TemporaryStorageOption.MEMORY, encryptionKey } = this.waOptions;
|
|
30
|
+
const { vfs = WASQLiteVFS.IDBBatchAtomicVFS, temporaryStorage = TemporaryStorageOption.MEMORY, cacheSizeKb = DEFAULT_CACHE_SIZE_KB, encryptionKey } = this.waOptions;
|
|
31
31
|
if (!enableMultiTabs) {
|
|
32
32
|
this.logger.warn('Multiple tabs are not enabled in this browser');
|
|
33
33
|
}
|
|
@@ -37,6 +37,7 @@ export class WASQLiteOpenFactory extends AbstractWebSQLOpenFactory {
|
|
|
37
37
|
? resolveWorkerDatabasePortFactory(() => optionsDbWorker({
|
|
38
38
|
...this.options,
|
|
39
39
|
temporaryStorage,
|
|
40
|
+
cacheSizeKb,
|
|
40
41
|
flags: this.resolvedFlags,
|
|
41
42
|
encryptionKey
|
|
42
43
|
}))
|
|
@@ -48,6 +49,7 @@ export class WASQLiteOpenFactory extends AbstractWebSQLOpenFactory {
|
|
|
48
49
|
dbFilename: this.options.dbFilename,
|
|
49
50
|
vfs,
|
|
50
51
|
temporaryStorage,
|
|
52
|
+
cacheSizeKb,
|
|
51
53
|
flags: this.resolvedFlags,
|
|
52
54
|
encryptionKey: encryptionKey
|
|
53
55
|
}),
|
|
@@ -70,6 +72,7 @@ export class WASQLiteOpenFactory extends AbstractWebSQLOpenFactory {
|
|
|
70
72
|
debugMode: this.options.debugMode,
|
|
71
73
|
vfs,
|
|
72
74
|
temporaryStorage,
|
|
75
|
+
cacheSizeKb,
|
|
73
76
|
flags: this.resolvedFlags,
|
|
74
77
|
encryptionKey: encryptionKey
|
|
75
78
|
});
|
|
@@ -39,6 +39,7 @@ export interface ResolvedWebSQLOpenOptions extends SQLOpenOptions {
|
|
|
39
39
|
* Setting this to `FILESYSTEM` can cause issues with larger queries or datasets.
|
|
40
40
|
*/
|
|
41
41
|
temporaryStorage: TemporaryStorageOption;
|
|
42
|
+
cacheSizeKb: number;
|
|
42
43
|
/**
|
|
43
44
|
* Encryption key for the database.
|
|
44
45
|
* If set, the database will be encrypted using ChaCha20.
|
|
@@ -49,6 +50,7 @@ export declare enum TemporaryStorageOption {
|
|
|
49
50
|
MEMORY = "memory",
|
|
50
51
|
FILESYSTEM = "file"
|
|
51
52
|
}
|
|
53
|
+
export declare const DEFAULT_CACHE_SIZE_KB: number;
|
|
52
54
|
/**
|
|
53
55
|
* Options for opening a Web SQL connection
|
|
54
56
|
*/
|
|
@@ -65,8 +67,16 @@ export interface WebSQLOpenFactoryOptions extends SQLOpenOptions {
|
|
|
65
67
|
/**
|
|
66
68
|
* Where to store SQLite temporary files. Defaults to 'MEMORY'.
|
|
67
69
|
* Setting this to `FILESYSTEM` can cause issues with larger queries or datasets.
|
|
70
|
+
*
|
|
71
|
+
* For details, see: https://www.sqlite.org/pragma.html#pragma_temp_store
|
|
68
72
|
*/
|
|
69
73
|
temporaryStorage?: TemporaryStorageOption;
|
|
74
|
+
/**
|
|
75
|
+
* Maximum SQLite cache size. Defaults to 50MB.
|
|
76
|
+
*
|
|
77
|
+
* For details, see: https://www.sqlite.org/pragma.html#pragma_cache_size
|
|
78
|
+
*/
|
|
79
|
+
cacheSizeKb?: number;
|
|
70
80
|
/**
|
|
71
81
|
* Encryption key for the database.
|
|
72
82
|
* If set, the database will be encrypted using ChaCha20.
|
|
@@ -3,6 +3,7 @@ export var TemporaryStorageOption;
|
|
|
3
3
|
TemporaryStorageOption["MEMORY"] = "memory";
|
|
4
4
|
TemporaryStorageOption["FILESYSTEM"] = "file";
|
|
5
5
|
})(TemporaryStorageOption || (TemporaryStorageOption = {}));
|
|
6
|
+
export const DEFAULT_CACHE_SIZE_KB = 50 * 1024;
|
|
6
7
|
export function isServerSide() {
|
|
7
8
|
return typeof window == 'undefined';
|
|
8
9
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as Comlink from 'comlink';
|
|
2
2
|
import { AbstractSharedSyncClientProvider } from '../../worker/sync/AbstractSharedSyncClientProvider';
|
|
3
3
|
import { SharedSyncClientEvent } from '../../worker/sync/SharedSyncImplementation';
|
|
4
|
-
import { resolveWebSQLFlags, TemporaryStorageOption } from '../adapters/web-sql-flags';
|
|
4
|
+
import { DEFAULT_CACHE_SIZE_KB, resolveWebSQLFlags, TemporaryStorageOption } from '../adapters/web-sql-flags';
|
|
5
5
|
import { WebStreamingSyncImplementation } from './WebStreamingSyncImplementation';
|
|
6
6
|
/**
|
|
7
7
|
* The shared worker will trigger methods on this side of the message port
|
|
@@ -86,10 +86,10 @@ export class SharedWebStreamingSyncImplementation extends WebStreamingSyncImplem
|
|
|
86
86
|
* This worker will manage all syncing operations remotely.
|
|
87
87
|
*/
|
|
88
88
|
const resolvedWorkerOptions = {
|
|
89
|
-
...options,
|
|
90
89
|
dbFilename: this.options.identifier,
|
|
91
|
-
// TODO
|
|
92
90
|
temporaryStorage: TemporaryStorageOption.MEMORY,
|
|
91
|
+
cacheSizeKb: DEFAULT_CACHE_SIZE_KB,
|
|
92
|
+
...options,
|
|
93
93
|
flags: resolveWebSQLFlags(options.flags)
|
|
94
94
|
};
|
|
95
95
|
const syncWorker = options.sync?.worker;
|