@powersync/web 1.2.4 → 1.4.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.
- package/README.md +1 -25
- package/lib/src/db/PowerSyncDatabase.d.ts +2 -2
- package/lib/src/db/PowerSyncDatabase.js +35 -28
- package/lib/src/db/adapters/AbstractWebPowerSyncDatabaseOpenFactory.js +7 -1
- package/lib/src/db/adapters/AbstractWebSQLOpenFactory.js +2 -0
- package/lib/src/db/adapters/SSRDBAdapter.js +28 -48
- package/lib/src/db/adapters/wa-sqlite/WASQLiteDBAdapter.d.ts +1 -0
- package/lib/src/db/adapters/wa-sqlite/WASQLiteDBAdapter.js +117 -119
- package/lib/src/db/adapters/wa-sqlite/WASQLiteOpenFactory.js +4 -1
- package/lib/src/db/adapters/web-sql-flags.js +6 -3
- package/lib/src/db/sync/SSRWebStreamingSyncImplementation.js +2 -0
- package/lib/src/db/sync/SharedWebStreamingSyncImplementation.js +66 -95
- package/lib/src/db/sync/WebRemote.d.ts +5 -1
- package/lib/src/db/sync/WebRemote.js +29 -21
- package/lib/src/shared/open-db.js +153 -184
- package/lib/src/worker/db/SharedWASQLiteDB.worker.js +15 -23
- package/lib/src/worker/db/WASQLiteDB.worker.js +1 -10
- package/lib/src/worker/sync/BroadcastLogger.js +16 -19
- package/lib/src/worker/sync/SharedSyncImplementation.js +96 -115
- package/lib/src/worker/sync/SharedSyncImplementation.worker.js +1 -5
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +13 -7
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
# PowerSync SDK for Web
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
_[PowerSync](https://www.powersync.com) is a Postgres-SQLite sync layer, which helps developers to create local-first real-time reactive apps that work seamlessly both online and offline._
|
|
8
8
|
|
|
9
9
|
This package (`packages/web`) is the PowerSync SDK for JavaScript Web clients. It is an extension of `packages/common`.
|
|
10
10
|
|
|
@@ -28,30 +28,6 @@ Install it in your app with:
|
|
|
28
28
|
npm install @journeyapps/wa-sqlite
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
## Polyfills
|
|
32
|
-
|
|
33
|
-
### WebSocket Connections: Buffer
|
|
34
|
-
|
|
35
|
-
Note: Beta Release - WebSockets are currently in a beta release. It should be safe to use in production if sufficient testing is done on the client side.
|
|
36
|
-
|
|
37
|
-
This SDK connects to a PowerSync instance via HTTP streams (enabled by default) or WebSockets. The WebSocket connection method requires `Buffer` to be available in the global scope. When multiple tabs are used the shared web worker will apply a polyfill in its own scope, but the `Buffer` class should be polyfills in the application for cases where multiple tabs are not supported.
|
|
38
|
-
|
|
39
|
-
Install a suitable Buffer implementation
|
|
40
|
-
|
|
41
|
-
```bash
|
|
42
|
-
npm install buffer
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
Apply it in your application if not yet provided
|
|
46
|
-
|
|
47
|
-
```Javascript
|
|
48
|
-
import { Buffer } from 'buffer';
|
|
49
|
-
|
|
50
|
-
if (typeof self.Buffer == 'undefined') {
|
|
51
|
-
self.Buffer = Buffer;
|
|
52
|
-
}
|
|
53
|
-
```
|
|
54
|
-
|
|
55
31
|
## Webpack
|
|
56
32
|
|
|
57
33
|
See the [example Webpack config](https://github.com/powersync-ja/powersync-js/blob/main/demos/example-webpack/webpack.config.js) for details on polyfills and requirements.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type AbstractStreamingSyncImplementation, type
|
|
1
|
+
import { type AbstractStreamingSyncImplementation, type BucketStorageAdapter, type PowerSyncBackendConnector, type PowerSyncCloseOptions, type PowerSyncConnectionOptions, AbstractPowerSyncDatabase, DBAdapter, PowerSyncDatabaseOptions, PowerSyncDatabaseOptionsWithDBAdapter, PowerSyncDatabaseOptionsWithOpenFactory, PowerSyncDatabaseOptionsWithSettings } from '@powersync/common';
|
|
2
2
|
import { Mutex } from 'async-mutex';
|
|
3
3
|
import { WebSQLFlags } from './adapters/web-sql-flags';
|
|
4
4
|
export interface WebPowerSyncFlags extends WebSQLFlags {
|
|
@@ -42,7 +42,7 @@ export declare class PowerSyncDatabase extends AbstractPowerSyncDatabase {
|
|
|
42
42
|
constructor(options: WebPowerSyncDatabaseOptionsWithSettings);
|
|
43
43
|
constructor(options: WebPowerSyncDatabaseOptions);
|
|
44
44
|
_initialize(): Promise<void>;
|
|
45
|
-
protected openDBAdapter(options:
|
|
45
|
+
protected openDBAdapter(options: WebPowerSyncDatabaseOptionsWithSettings): DBAdapter;
|
|
46
46
|
/**
|
|
47
47
|
* Closes the database connection.
|
|
48
48
|
* By default the sync stream client is only disconnected if
|
|
@@ -1,23 +1,21 @@
|
|
|
1
|
-
|
|
2
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
-
});
|
|
9
|
-
};
|
|
10
|
-
import { AbstractPowerSyncDatabase, SqliteBucketStorage, DEFAULT_POWERSYNC_CLOSE_OPTIONS } from '@powersync/common';
|
|
1
|
+
import { AbstractPowerSyncDatabase, DEFAULT_POWERSYNC_CLOSE_OPTIONS, SqliteBucketStorage } from '@powersync/common';
|
|
11
2
|
import { Mutex } from 'async-mutex';
|
|
12
|
-
import {
|
|
3
|
+
import { WASQLiteOpenFactory } from './adapters/wa-sqlite/WASQLiteOpenFactory';
|
|
4
|
+
import { DEFAULT_WEB_SQL_FLAGS, resolveWebSQLFlags } from './adapters/web-sql-flags';
|
|
13
5
|
import { SharedWebStreamingSyncImplementation } from './sync/SharedWebStreamingSyncImplementation';
|
|
14
6
|
import { SSRStreamingSyncImplementation } from './sync/SSRWebStreamingSyncImplementation';
|
|
7
|
+
import { WebRemote } from './sync/WebRemote';
|
|
15
8
|
import { WebStreamingSyncImplementation } from './sync/WebStreamingSyncImplementation';
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
9
|
+
export const DEFAULT_POWERSYNC_FLAGS = {
|
|
10
|
+
...DEFAULT_WEB_SQL_FLAGS,
|
|
11
|
+
externallyUnload: false
|
|
12
|
+
};
|
|
19
13
|
export const resolveWebPowerSyncFlags = (flags) => {
|
|
20
|
-
return
|
|
14
|
+
return {
|
|
15
|
+
...DEFAULT_POWERSYNC_FLAGS,
|
|
16
|
+
...flags,
|
|
17
|
+
...resolveWebSQLFlags(flags)
|
|
18
|
+
};
|
|
21
19
|
};
|
|
22
20
|
/**
|
|
23
21
|
* A PowerSync database which provides SQLite functionality
|
|
@@ -34,6 +32,10 @@ export const resolveWebPowerSyncFlags = (flags) => {
|
|
|
34
32
|
* ```
|
|
35
33
|
*/
|
|
36
34
|
export class PowerSyncDatabase extends AbstractPowerSyncDatabase {
|
|
35
|
+
options;
|
|
36
|
+
static SHARED_MUTEX = new Mutex();
|
|
37
|
+
unloadListener;
|
|
38
|
+
resolvedFlags;
|
|
37
39
|
constructor(options) {
|
|
38
40
|
super(options);
|
|
39
41
|
this.options = options;
|
|
@@ -43,11 +45,12 @@ export class PowerSyncDatabase extends AbstractPowerSyncDatabase {
|
|
|
43
45
|
window.addEventListener('unload', this.unloadListener);
|
|
44
46
|
}
|
|
45
47
|
}
|
|
46
|
-
_initialize() {
|
|
47
|
-
return __awaiter(this, void 0, void 0, function* () { });
|
|
48
|
-
}
|
|
48
|
+
async _initialize() { }
|
|
49
49
|
openDBAdapter(options) {
|
|
50
|
-
const defaultFactory = new WASQLiteOpenFactory(
|
|
50
|
+
const defaultFactory = new WASQLiteOpenFactory({
|
|
51
|
+
...options.database,
|
|
52
|
+
flags: resolveWebPowerSyncFlags(options.flags)
|
|
53
|
+
});
|
|
51
54
|
return defaultFactory.openDB();
|
|
52
55
|
}
|
|
53
56
|
/**
|
|
@@ -56,13 +59,12 @@ export class PowerSyncDatabase extends AbstractPowerSyncDatabase {
|
|
|
56
59
|
* multiple tabs are not enabled.
|
|
57
60
|
*/
|
|
58
61
|
close(options = DEFAULT_POWERSYNC_CLOSE_OPTIONS) {
|
|
59
|
-
var _a;
|
|
60
62
|
if (this.unloadListener) {
|
|
61
63
|
window.removeEventListener('unload', this.unloadListener);
|
|
62
64
|
}
|
|
63
65
|
return super.close({
|
|
64
66
|
// Don't disconnect by default if multiple tabs are enabled
|
|
65
|
-
disconnect:
|
|
67
|
+
disconnect: options.disconnect ?? !this.resolvedFlags.enableMultiTabs
|
|
66
68
|
});
|
|
67
69
|
}
|
|
68
70
|
connect(connector, options) {
|
|
@@ -72,8 +74,7 @@ export class PowerSyncDatabase extends AbstractPowerSyncDatabase {
|
|
|
72
74
|
* connection attempts.
|
|
73
75
|
*/
|
|
74
76
|
return this.runExclusive(() => {
|
|
75
|
-
|
|
76
|
-
(_a = this.options.logger) === null || _a === void 0 ? void 0 : _a.debug('Attempting to connect to PowerSync instance');
|
|
77
|
+
this.options.logger?.debug('Attempting to connect to PowerSync instance');
|
|
77
78
|
return super.connect(connector, options);
|
|
78
79
|
});
|
|
79
80
|
}
|
|
@@ -88,10 +89,17 @@ export class PowerSyncDatabase extends AbstractPowerSyncDatabase {
|
|
|
88
89
|
}
|
|
89
90
|
generateSyncStreamImplementation(connector) {
|
|
90
91
|
const remote = new WebRemote(connector);
|
|
91
|
-
const syncOptions =
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
92
|
+
const syncOptions = {
|
|
93
|
+
...this.options,
|
|
94
|
+
flags: this.resolvedFlags,
|
|
95
|
+
adapter: this.bucketStorageAdapter,
|
|
96
|
+
remote,
|
|
97
|
+
uploadCrud: async () => {
|
|
98
|
+
await this.waitForReady();
|
|
99
|
+
await connector.uploadData(this);
|
|
100
|
+
},
|
|
101
|
+
identifier: this.database.name
|
|
102
|
+
};
|
|
95
103
|
switch (true) {
|
|
96
104
|
case this.resolvedFlags.ssrMode:
|
|
97
105
|
return new SSRStreamingSyncImplementation(syncOptions);
|
|
@@ -110,4 +118,3 @@ export class PowerSyncDatabase extends AbstractPowerSyncDatabase {
|
|
|
110
118
|
}
|
|
111
119
|
}
|
|
112
120
|
}
|
|
113
|
-
PowerSyncDatabase.SHARED_MUTEX = new Mutex();
|
|
@@ -7,12 +7,18 @@ import { PowerSyncDatabase, resolveWebPowerSyncFlags } from '../../db/PowerSyncD
|
|
|
7
7
|
* empty query results in SSR which will allow for generating server partial views.
|
|
8
8
|
*/
|
|
9
9
|
export class AbstractWebPowerSyncDatabaseOpenFactory extends AbstractPowerSyncDatabaseOpenFactory {
|
|
10
|
+
options;
|
|
10
11
|
constructor(options) {
|
|
11
12
|
super(options);
|
|
12
13
|
this.options = options;
|
|
13
14
|
}
|
|
14
15
|
generateOptions() {
|
|
15
|
-
return
|
|
16
|
+
return {
|
|
17
|
+
...this.options,
|
|
18
|
+
database: this.openDB(),
|
|
19
|
+
schema: this.schema,
|
|
20
|
+
flags: resolveWebPowerSyncFlags(this.options.flags)
|
|
21
|
+
};
|
|
16
22
|
}
|
|
17
23
|
generateInstance(options) {
|
|
18
24
|
return new PowerSyncDatabase(options);
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { SSRDBAdapter } from './SSRDBAdapter';
|
|
2
2
|
import { isServerSide, resolveWebSQLFlags } from './web-sql-flags';
|
|
3
3
|
export class AbstractWebSQLOpenFactory {
|
|
4
|
+
options;
|
|
5
|
+
resolvedFlags;
|
|
4
6
|
constructor(options) {
|
|
5
7
|
this.options = options;
|
|
6
8
|
this.resolvedFlags = resolveWebSQLFlags(options.flags);
|
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
-
});
|
|
9
|
-
};
|
|
10
1
|
import { BaseObserver } from '@powersync/common';
|
|
11
2
|
import { Mutex } from 'async-mutex';
|
|
12
3
|
const MOCK_QUERY_RESPONSE = {
|
|
@@ -18,6 +9,9 @@ const MOCK_QUERY_RESPONSE = {
|
|
|
18
9
|
* server rendered views to initially generate scaffolding components
|
|
19
10
|
*/
|
|
20
11
|
export class SSRDBAdapter extends BaseObserver {
|
|
12
|
+
name;
|
|
13
|
+
readMutex;
|
|
14
|
+
writeMutex;
|
|
21
15
|
constructor() {
|
|
22
16
|
super();
|
|
23
17
|
this.name = 'SSR DB';
|
|
@@ -25,50 +19,32 @@ export class SSRDBAdapter extends BaseObserver {
|
|
|
25
19
|
this.writeMutex = new Mutex();
|
|
26
20
|
}
|
|
27
21
|
close() { }
|
|
28
|
-
readLock(fn, options) {
|
|
29
|
-
return
|
|
30
|
-
return this.readMutex.runExclusive(() => fn(this));
|
|
31
|
-
});
|
|
22
|
+
async readLock(fn, options) {
|
|
23
|
+
return this.readMutex.runExclusive(() => fn(this));
|
|
32
24
|
}
|
|
33
|
-
readTransaction(fn, options) {
|
|
34
|
-
return
|
|
35
|
-
return this.readLock(() => fn(this.generateMockTransactionContext()));
|
|
36
|
-
});
|
|
25
|
+
async readTransaction(fn, options) {
|
|
26
|
+
return this.readLock(() => fn(this.generateMockTransactionContext()));
|
|
37
27
|
}
|
|
38
|
-
writeLock(fn, options) {
|
|
39
|
-
return
|
|
40
|
-
return this.writeMutex.runExclusive(() => fn(this));
|
|
41
|
-
});
|
|
28
|
+
async writeLock(fn, options) {
|
|
29
|
+
return this.writeMutex.runExclusive(() => fn(this));
|
|
42
30
|
}
|
|
43
|
-
writeTransaction(fn, options) {
|
|
44
|
-
return
|
|
45
|
-
return this.writeLock(() => fn(this.generateMockTransactionContext()));
|
|
46
|
-
});
|
|
31
|
+
async writeTransaction(fn, options) {
|
|
32
|
+
return this.writeLock(() => fn(this.generateMockTransactionContext()));
|
|
47
33
|
}
|
|
48
|
-
execute(query, params) {
|
|
49
|
-
return
|
|
50
|
-
return this.writeMutex.runExclusive(() => __awaiter(this, void 0, void 0, function* () { return MOCK_QUERY_RESPONSE; }));
|
|
51
|
-
});
|
|
34
|
+
async execute(query, params) {
|
|
35
|
+
return this.writeMutex.runExclusive(async () => MOCK_QUERY_RESPONSE);
|
|
52
36
|
}
|
|
53
|
-
executeBatch(query, params) {
|
|
54
|
-
return
|
|
55
|
-
return this.writeMutex.runExclusive(() => __awaiter(this, void 0, void 0, function* () { return MOCK_QUERY_RESPONSE; }));
|
|
56
|
-
});
|
|
37
|
+
async executeBatch(query, params) {
|
|
38
|
+
return this.writeMutex.runExclusive(async () => MOCK_QUERY_RESPONSE);
|
|
57
39
|
}
|
|
58
|
-
getAll(sql, parameters) {
|
|
59
|
-
return
|
|
60
|
-
return [];
|
|
61
|
-
});
|
|
40
|
+
async getAll(sql, parameters) {
|
|
41
|
+
return [];
|
|
62
42
|
}
|
|
63
|
-
getOptional(sql, parameters) {
|
|
64
|
-
return
|
|
65
|
-
return null;
|
|
66
|
-
});
|
|
43
|
+
async getOptional(sql, parameters) {
|
|
44
|
+
return null;
|
|
67
45
|
}
|
|
68
|
-
get(sql, parameters) {
|
|
69
|
-
|
|
70
|
-
throw new Error(`No values are returned in SSR mode`);
|
|
71
|
-
});
|
|
46
|
+
async get(sql, parameters) {
|
|
47
|
+
throw new Error(`No values are returned in SSR mode`);
|
|
72
48
|
}
|
|
73
49
|
/**
|
|
74
50
|
* Generates a mock context for use in read/write transactions.
|
|
@@ -76,10 +52,14 @@ export class SSRDBAdapter extends BaseObserver {
|
|
|
76
52
|
* are added here
|
|
77
53
|
*/
|
|
78
54
|
generateMockTransactionContext() {
|
|
79
|
-
return
|
|
55
|
+
return {
|
|
56
|
+
...this,
|
|
57
|
+
commit: async () => {
|
|
80
58
|
return MOCK_QUERY_RESPONSE;
|
|
81
|
-
}
|
|
59
|
+
},
|
|
60
|
+
rollback: async () => {
|
|
82
61
|
return MOCK_QUERY_RESPONSE;
|
|
83
|
-
}
|
|
62
|
+
}
|
|
63
|
+
};
|
|
84
64
|
}
|
|
85
65
|
}
|
|
@@ -22,6 +22,7 @@ export declare class WASQLiteDBAdapter extends BaseObserver<DBAdapterListener> i
|
|
|
22
22
|
private logger;
|
|
23
23
|
private dbGetHelpers;
|
|
24
24
|
private methods;
|
|
25
|
+
private debugMode;
|
|
25
26
|
constructor(options: WASQLiteDBAdapterOptions);
|
|
26
27
|
get name(): string;
|
|
27
28
|
protected get flags(): WASQLiteFlags;
|
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
-
});
|
|
9
|
-
};
|
|
10
1
|
import { BaseObserver } from '@powersync/common';
|
|
11
2
|
import * as Comlink from 'comlink';
|
|
12
3
|
import Logger from 'js-logger';
|
|
@@ -16,28 +7,34 @@ import { getWorkerDatabaseOpener } from '../../../worker/db/open-worker-database
|
|
|
16
7
|
* Adapter for WA-SQLite SQLite connections.
|
|
17
8
|
*/
|
|
18
9
|
export class WASQLiteDBAdapter extends BaseObserver {
|
|
10
|
+
options;
|
|
11
|
+
initialized;
|
|
12
|
+
logger;
|
|
13
|
+
dbGetHelpers;
|
|
14
|
+
methods;
|
|
15
|
+
debugMode;
|
|
19
16
|
constructor(options) {
|
|
20
17
|
super();
|
|
21
18
|
this.options = options;
|
|
22
|
-
/**
|
|
23
|
-
* Wraps the worker execute function, awaiting for it to be available
|
|
24
|
-
*/
|
|
25
|
-
this._execute = (sql, bindings) => __awaiter(this, void 0, void 0, function* () {
|
|
26
|
-
yield this.initialized;
|
|
27
|
-
const result = yield this.methods.execute(sql, bindings);
|
|
28
|
-
return Object.assign(Object.assign({}, result), { rows: Object.assign(Object.assign({}, result.rows), { item: (idx) => result.rows._array[idx] }) });
|
|
29
|
-
});
|
|
30
|
-
/**
|
|
31
|
-
* Wraps the worker executeBatch function, awaiting for it to be available
|
|
32
|
-
*/
|
|
33
|
-
this._executeBatch = (query, params) => __awaiter(this, void 0, void 0, function* () {
|
|
34
|
-
yield this.initialized;
|
|
35
|
-
const result = yield this.methods.executeBatch(query, params);
|
|
36
|
-
return Object.assign(Object.assign({}, result), { rows: undefined });
|
|
37
|
-
});
|
|
38
19
|
this.logger = Logger.get('WASQLite');
|
|
39
20
|
this.dbGetHelpers = null;
|
|
40
21
|
this.methods = null;
|
|
22
|
+
this.debugMode = options.debugMode ?? false;
|
|
23
|
+
if (this.debugMode) {
|
|
24
|
+
const originalExecute = this._execute.bind(this);
|
|
25
|
+
this._execute = async (sql, bindings) => {
|
|
26
|
+
const start = performance.now();
|
|
27
|
+
try {
|
|
28
|
+
const r = await originalExecute(sql, bindings);
|
|
29
|
+
performance.measure(`[SQL] ${sql}`, { start });
|
|
30
|
+
return r;
|
|
31
|
+
}
|
|
32
|
+
catch (e) {
|
|
33
|
+
performance.measure(`[SQL] [ERROR: ${e.message}] ${sql}`, { start });
|
|
34
|
+
throw e;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
}
|
|
41
38
|
this.initialized = this.init();
|
|
42
39
|
this.dbGetHelpers = this.generateDBHelpers({
|
|
43
40
|
execute: (query, params) => this.acquireLock(() => this._execute(query, params))
|
|
@@ -47,88 +44,93 @@ export class WASQLiteDBAdapter extends BaseObserver {
|
|
|
47
44
|
return this.options.dbFilename;
|
|
48
45
|
}
|
|
49
46
|
get flags() {
|
|
50
|
-
|
|
51
|
-
return (_a = this.options.flags) !== null && _a !== void 0 ? _a : {};
|
|
47
|
+
return this.options.flags ?? {};
|
|
52
48
|
}
|
|
53
49
|
getWorker() { }
|
|
54
|
-
init() {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
this.
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
this.
|
|
72
|
-
this.iterateListeners((cb) => { var _a; return (_a = cb.tablesUpdated) === null || _a === void 0 ? void 0 : _a.call(cb, { opType, table: tableName, rowId }); });
|
|
73
|
-
});
|
|
50
|
+
async init() {
|
|
51
|
+
const { enableMultiTabs, useWebWorker } = this.flags;
|
|
52
|
+
if (!enableMultiTabs) {
|
|
53
|
+
this.logger.warn('Multiple tabs are not enabled in this browser');
|
|
54
|
+
}
|
|
55
|
+
if (useWebWorker) {
|
|
56
|
+
const dbOpener = this.options.workerPort
|
|
57
|
+
? Comlink.wrap(this.options.workerPort)
|
|
58
|
+
: getWorkerDatabaseOpener(this.options.dbFilename, enableMultiTabs);
|
|
59
|
+
this.methods = await dbOpener(this.options.dbFilename);
|
|
60
|
+
this.methods.registerOnTableChange(Comlink.proxy((opType, tableName, rowId) => {
|
|
61
|
+
this.iterateListeners((cb) => cb.tablesUpdated?.({ opType, table: tableName, rowId }));
|
|
62
|
+
}));
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
this.methods = await _openDB(this.options.dbFilename, { useWebWorker: false });
|
|
66
|
+
this.methods.registerOnTableChange((opType, tableName, rowId) => {
|
|
67
|
+
this.iterateListeners((cb) => cb.tablesUpdated?.({ opType, table: tableName, rowId }));
|
|
74
68
|
});
|
|
75
69
|
}
|
|
76
|
-
execute(query, params) {
|
|
77
|
-
return
|
|
78
|
-
return this.writeLock((ctx) => ctx.execute(query, params));
|
|
79
|
-
});
|
|
70
|
+
async execute(query, params) {
|
|
71
|
+
return this.writeLock((ctx) => ctx.execute(query, params));
|
|
80
72
|
}
|
|
81
|
-
executeBatch(query, params) {
|
|
82
|
-
return
|
|
83
|
-
return this.writeLock((ctx) => this._executeBatch(query, params));
|
|
84
|
-
});
|
|
73
|
+
async executeBatch(query, params) {
|
|
74
|
+
return this.writeLock((ctx) => this._executeBatch(query, params));
|
|
85
75
|
}
|
|
76
|
+
/**
|
|
77
|
+
* Wraps the worker execute function, awaiting for it to be available
|
|
78
|
+
*/
|
|
79
|
+
_execute = async (sql, bindings) => {
|
|
80
|
+
await this.initialized;
|
|
81
|
+
const result = await this.methods.execute(sql, bindings);
|
|
82
|
+
return {
|
|
83
|
+
...result,
|
|
84
|
+
rows: {
|
|
85
|
+
...result.rows,
|
|
86
|
+
item: (idx) => result.rows._array[idx]
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
91
|
+
* Wraps the worker executeBatch function, awaiting for it to be available
|
|
92
|
+
*/
|
|
93
|
+
_executeBatch = async (query, params) => {
|
|
94
|
+
await this.initialized;
|
|
95
|
+
const result = await this.methods.executeBatch(query, params);
|
|
96
|
+
return {
|
|
97
|
+
...result,
|
|
98
|
+
rows: undefined
|
|
99
|
+
};
|
|
100
|
+
};
|
|
86
101
|
/**
|
|
87
102
|
* Attempts to close the connection.
|
|
88
103
|
* Shared workers might not actually close the connection if other
|
|
89
104
|
* tabs are still using it.
|
|
90
105
|
*/
|
|
91
106
|
close() {
|
|
92
|
-
|
|
93
|
-
(_b = (_a = this.methods) === null || _a === void 0 ? void 0 : _a.close) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
107
|
+
this.methods?.close?.();
|
|
94
108
|
}
|
|
95
|
-
getAll(sql, parameters) {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
return this.dbGetHelpers.getAll(sql, parameters);
|
|
99
|
-
});
|
|
109
|
+
async getAll(sql, parameters) {
|
|
110
|
+
await this.initialized;
|
|
111
|
+
return this.dbGetHelpers.getAll(sql, parameters);
|
|
100
112
|
}
|
|
101
|
-
getOptional(sql, parameters) {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
return this.dbGetHelpers.getOptional(sql, parameters);
|
|
105
|
-
});
|
|
113
|
+
async getOptional(sql, parameters) {
|
|
114
|
+
await this.initialized;
|
|
115
|
+
return this.dbGetHelpers.getOptional(sql, parameters);
|
|
106
116
|
}
|
|
107
|
-
get(sql, parameters) {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
return this.dbGetHelpers.get(sql, parameters);
|
|
111
|
-
});
|
|
117
|
+
async get(sql, parameters) {
|
|
118
|
+
await this.initialized;
|
|
119
|
+
return this.dbGetHelpers.get(sql, parameters);
|
|
112
120
|
}
|
|
113
|
-
readLock(fn, options) {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
return this.acquireLock(() => __awaiter(this, void 0, void 0, function* () { return fn(this.generateDBHelpers({ execute: this._execute })); }));
|
|
117
|
-
});
|
|
121
|
+
async readLock(fn, options) {
|
|
122
|
+
await this.initialized;
|
|
123
|
+
return this.acquireLock(async () => fn(this.generateDBHelpers({ execute: this._execute })));
|
|
118
124
|
}
|
|
119
|
-
writeLock(fn, options) {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
return this.acquireLock(() => __awaiter(this, void 0, void 0, function* () { return fn(this.generateDBHelpers({ execute: this._execute })); }));
|
|
123
|
-
});
|
|
125
|
+
async writeLock(fn, options) {
|
|
126
|
+
await this.initialized;
|
|
127
|
+
return this.acquireLock(async () => fn(this.generateDBHelpers({ execute: this._execute })));
|
|
124
128
|
}
|
|
125
129
|
acquireLock(callback) {
|
|
126
130
|
return navigator.locks.request(`db-lock-${this.options.dbFilename}`, callback);
|
|
127
131
|
}
|
|
128
|
-
readTransaction(fn, options) {
|
|
129
|
-
return
|
|
130
|
-
return this.readLock(this.wrapTransaction(fn));
|
|
131
|
-
});
|
|
132
|
+
async readTransaction(fn, options) {
|
|
133
|
+
return this.readLock(this.wrapTransaction(fn));
|
|
132
134
|
}
|
|
133
135
|
writeTransaction(fn, options) {
|
|
134
136
|
return this.writeLock(this.wrapTransaction(fn));
|
|
@@ -137,32 +139,35 @@ export class WASQLiteDBAdapter extends BaseObserver {
|
|
|
137
139
|
* Wraps a lock context into a transaction context
|
|
138
140
|
*/
|
|
139
141
|
wrapTransaction(cb) {
|
|
140
|
-
return (tx) =>
|
|
141
|
-
|
|
142
|
+
return async (tx) => {
|
|
143
|
+
await this._execute('BEGIN TRANSACTION');
|
|
142
144
|
let finalized = false;
|
|
143
|
-
const commit = () =>
|
|
145
|
+
const commit = async () => {
|
|
144
146
|
if (finalized) {
|
|
145
147
|
return { rowsAffected: 0 };
|
|
146
148
|
}
|
|
147
149
|
finalized = true;
|
|
148
150
|
return this._execute('COMMIT');
|
|
149
|
-
}
|
|
151
|
+
};
|
|
150
152
|
const rollback = () => {
|
|
151
153
|
finalized = true;
|
|
152
154
|
return this._execute('ROLLBACK');
|
|
153
155
|
};
|
|
154
156
|
try {
|
|
155
|
-
const result =
|
|
156
|
-
|
|
157
|
+
const result = await cb({
|
|
158
|
+
...tx,
|
|
159
|
+
commit,
|
|
160
|
+
rollback
|
|
161
|
+
});
|
|
157
162
|
if (!finalized) {
|
|
158
|
-
|
|
163
|
+
await commit();
|
|
159
164
|
}
|
|
160
165
|
return result;
|
|
161
166
|
}
|
|
162
167
|
catch (ex) {
|
|
163
168
|
this.logger.debug('Caught ex in transaction', ex);
|
|
164
169
|
try {
|
|
165
|
-
|
|
170
|
+
await rollback();
|
|
166
171
|
}
|
|
167
172
|
catch (ex2) {
|
|
168
173
|
// In rare cases, a rollback may fail.
|
|
@@ -170,43 +175,36 @@ export class WASQLiteDBAdapter extends BaseObserver {
|
|
|
170
175
|
}
|
|
171
176
|
throw ex;
|
|
172
177
|
}
|
|
173
|
-
}
|
|
178
|
+
};
|
|
174
179
|
}
|
|
175
180
|
generateDBHelpers(tx) {
|
|
176
|
-
return
|
|
181
|
+
return {
|
|
182
|
+
...tx,
|
|
177
183
|
/**
|
|
178
184
|
* Execute a read-only query and return results
|
|
179
185
|
*/
|
|
180
|
-
getAll(sql, parameters) {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
const res = yield tx.execute(sql, parameters);
|
|
184
|
-
return (_b = (_a = res.rows) === null || _a === void 0 ? void 0 : _a._array) !== null && _b !== void 0 ? _b : [];
|
|
185
|
-
});
|
|
186
|
+
async getAll(sql, parameters) {
|
|
187
|
+
const res = await tx.execute(sql, parameters);
|
|
188
|
+
return res.rows?._array ?? [];
|
|
186
189
|
},
|
|
187
190
|
/**
|
|
188
191
|
* Execute a read-only query and return the first result, or null if the ResultSet is empty.
|
|
189
192
|
*/
|
|
190
|
-
getOptional(sql, parameters) {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
const res = yield tx.execute(sql, parameters);
|
|
194
|
-
return (_b = (_a = res.rows) === null || _a === void 0 ? void 0 : _a.item(0)) !== null && _b !== void 0 ? _b : null;
|
|
195
|
-
});
|
|
193
|
+
async getOptional(sql, parameters) {
|
|
194
|
+
const res = await tx.execute(sql, parameters);
|
|
195
|
+
return res.rows?.item(0) ?? null;
|
|
196
196
|
},
|
|
197
197
|
/**
|
|
198
198
|
* Execute a read-only query and return the first result, error if the ResultSet is empty.
|
|
199
199
|
*/
|
|
200
|
-
get(sql, parameters) {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
});
|
|
210
|
-
} });
|
|
200
|
+
async get(sql, parameters) {
|
|
201
|
+
const res = await tx.execute(sql, parameters);
|
|
202
|
+
const first = res.rows?.item(0);
|
|
203
|
+
if (!first) {
|
|
204
|
+
throw new Error('Result set is empty');
|
|
205
|
+
}
|
|
206
|
+
return first;
|
|
207
|
+
}
|
|
208
|
+
};
|
|
211
209
|
}
|
|
212
210
|
}
|
|
@@ -5,6 +5,9 @@ import { AbstractWebSQLOpenFactory } from '../AbstractWebSQLOpenFactory';
|
|
|
5
5
|
*/
|
|
6
6
|
export class WASQLiteOpenFactory extends AbstractWebSQLOpenFactory {
|
|
7
7
|
openAdapter() {
|
|
8
|
-
return new WASQLiteDBAdapter(
|
|
8
|
+
return new WASQLiteDBAdapter({
|
|
9
|
+
...this.options,
|
|
10
|
+
flags: this.resolvedFlags
|
|
11
|
+
});
|
|
9
12
|
}
|
|
10
13
|
}
|