@powersync/web 0.0.0-dev-20240712065121 → 0.0.0-dev-20240715085239
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/lib/src/db/PowerSyncDatabase.js +28 -24
- 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.js +100 -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 +73 -100
- package/lib/src/db/sync/WebRemote.js +10 -20
- package/lib/src/shared/open-db.js +149 -180
- package/lib/src/worker/db/SharedWASQLiteDB.worker.js +15 -23
- package/lib/src/worker/db/WASQLiteDB.worker.js +1 -10
- package/lib/src/worker/db/open-worker-database.d.ts +1 -1
- package/lib/src/worker/db/open-worker-database.js +13 -11
- 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 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +15 -7
|
@@ -1,103 +1,90 @@
|
|
|
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 * as Comlink from 'comlink';
|
|
11
2
|
import { WebStreamingSyncImplementation } from './WebStreamingSyncImplementation';
|
|
12
3
|
import { SharedSyncClientEvent } from '../../worker/sync/SharedSyncImplementation';
|
|
13
4
|
import { AbstractSharedSyncClientProvider } from '../../worker/sync/AbstractSharedSyncClientProvider';
|
|
14
5
|
import { openWorkerDatabasePort } from '../../worker/db/open-worker-database';
|
|
6
|
+
import SharedSyncImplementationWorker from '../../worker/sync/SharedSyncImplementation.worker?sharedworker&inline';
|
|
15
7
|
/**
|
|
16
8
|
* The shared worker will trigger methods on this side of the message port
|
|
17
9
|
* via this client provider.
|
|
18
10
|
*/
|
|
19
11
|
class SharedSyncClientProvider extends AbstractSharedSyncClientProvider {
|
|
12
|
+
options;
|
|
13
|
+
statusChanged;
|
|
20
14
|
constructor(options, statusChanged) {
|
|
21
15
|
super();
|
|
22
16
|
this.options = options;
|
|
23
17
|
this.statusChanged = statusChanged;
|
|
24
18
|
}
|
|
25
|
-
fetchCredentials() {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
};
|
|
42
|
-
});
|
|
19
|
+
async fetchCredentials() {
|
|
20
|
+
const credentials = await this.options.remote.getCredentials();
|
|
21
|
+
if (credentials == null) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* The credentials need to be serializable.
|
|
26
|
+
* Users might extend [PowerSyncCredentials] to contain
|
|
27
|
+
* items which are not serializable.
|
|
28
|
+
* This returns only the essential fields.
|
|
29
|
+
*/
|
|
30
|
+
return {
|
|
31
|
+
endpoint: credentials.endpoint,
|
|
32
|
+
token: credentials.token,
|
|
33
|
+
expiresAt: credentials.expiresAt
|
|
34
|
+
};
|
|
43
35
|
}
|
|
44
|
-
uploadCrud() {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
yield this.options.uploadCrud();
|
|
51
|
-
});
|
|
36
|
+
async uploadCrud() {
|
|
37
|
+
/**
|
|
38
|
+
* Don't return anything here, just incase something which is not
|
|
39
|
+
* serializable is returned from the `uploadCrud` function.
|
|
40
|
+
*/
|
|
41
|
+
await this.options.uploadCrud();
|
|
52
42
|
}
|
|
53
43
|
get logger() {
|
|
54
44
|
return this.options.logger;
|
|
55
45
|
}
|
|
56
46
|
trace(...x) {
|
|
57
|
-
|
|
58
|
-
(_a = this.logger) === null || _a === void 0 ? void 0 : _a.trace(...x);
|
|
47
|
+
this.logger?.trace(...x);
|
|
59
48
|
}
|
|
60
49
|
debug(...x) {
|
|
61
|
-
|
|
62
|
-
(_a = this.logger) === null || _a === void 0 ? void 0 : _a.debug(...x);
|
|
50
|
+
this.logger?.debug(...x);
|
|
63
51
|
}
|
|
64
52
|
info(...x) {
|
|
65
|
-
|
|
66
|
-
(_a = this.logger) === null || _a === void 0 ? void 0 : _a.info(...x);
|
|
53
|
+
this.logger?.info(...x);
|
|
67
54
|
}
|
|
68
55
|
log(...x) {
|
|
69
|
-
|
|
70
|
-
(_a = this.logger) === null || _a === void 0 ? void 0 : _a.log(...x);
|
|
56
|
+
this.logger?.log(...x);
|
|
71
57
|
}
|
|
72
58
|
warn(...x) {
|
|
73
|
-
|
|
74
|
-
(_a = this.logger) === null || _a === void 0 ? void 0 : _a.warn(...x);
|
|
59
|
+
this.logger?.warn(...x);
|
|
75
60
|
}
|
|
76
61
|
error(...x) {
|
|
77
|
-
|
|
78
|
-
(_a = this.logger) === null || _a === void 0 ? void 0 : _a.error(...x);
|
|
62
|
+
this.logger?.error(...x);
|
|
79
63
|
}
|
|
80
64
|
time(label) {
|
|
81
|
-
|
|
82
|
-
(_a = this.logger) === null || _a === void 0 ? void 0 : _a.time(label);
|
|
65
|
+
this.logger?.time(label);
|
|
83
66
|
}
|
|
84
67
|
timeEnd(label) {
|
|
85
|
-
|
|
86
|
-
(_a = this.logger) === null || _a === void 0 ? void 0 : _a.timeEnd(label);
|
|
68
|
+
this.logger?.timeEnd(label);
|
|
87
69
|
}
|
|
88
70
|
}
|
|
89
71
|
export class SharedWebStreamingSyncImplementation extends WebStreamingSyncImplementation {
|
|
72
|
+
syncManager;
|
|
73
|
+
clientProvider;
|
|
74
|
+
messagePort;
|
|
75
|
+
isInitialized;
|
|
90
76
|
constructor(options) {
|
|
91
77
|
super(options);
|
|
92
78
|
/**
|
|
93
79
|
* Configure or connect to the shared sync worker.
|
|
94
80
|
* This worker will manage all syncing operations remotely.
|
|
95
81
|
*/
|
|
96
|
-
const syncWorker = new
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
82
|
+
const syncWorker = new SharedSyncImplementationWorker({ name: `shared-sync-${this.webOptions.identifier}` });
|
|
83
|
+
// new SharedWorker(new URL('../../worker/sync/SharedSyncImplementation.worker.js', import.meta.url), {
|
|
84
|
+
// /* @vite-ignore */
|
|
85
|
+
// name: `shared-sync-${this.webOptions.identifier}`,
|
|
86
|
+
// type: 'module'
|
|
87
|
+
// });
|
|
101
88
|
this.messagePort = syncWorker.port;
|
|
102
89
|
this.syncManager = Comlink.wrap(this.messagePort);
|
|
103
90
|
this.triggerCrudUpload = this.syncManager.triggerCrudUpload;
|
|
@@ -135,53 +122,39 @@ export class SharedWebStreamingSyncImplementation extends WebStreamingSyncImplem
|
|
|
135
122
|
* Starts the sync process, this effectively acts as a call to
|
|
136
123
|
* `connect` if not yet connected.
|
|
137
124
|
*/
|
|
138
|
-
connect(options) {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
return
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
event: SharedSyncClientEvent.CLOSE_CLIENT,
|
|
167
|
-
data: {}
|
|
168
|
-
};
|
|
169
|
-
this.messagePort.postMessage(closeMessagePayload);
|
|
170
|
-
// Release the proxy
|
|
171
|
-
this.syncManager[Comlink.releaseProxy]();
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
|
-
waitForReady() {
|
|
175
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
176
|
-
return this.isInitialized;
|
|
177
|
-
});
|
|
125
|
+
async connect(options) {
|
|
126
|
+
await this.waitForReady();
|
|
127
|
+
return this.syncManager.connect(options);
|
|
128
|
+
}
|
|
129
|
+
async disconnect() {
|
|
130
|
+
await this.waitForReady();
|
|
131
|
+
return this.syncManager.disconnect();
|
|
132
|
+
}
|
|
133
|
+
async getWriteCheckpoint() {
|
|
134
|
+
await this.waitForReady();
|
|
135
|
+
return this.syncManager.getWriteCheckpoint();
|
|
136
|
+
}
|
|
137
|
+
async hasCompletedSync() {
|
|
138
|
+
return this.syncManager.hasCompletedSync();
|
|
139
|
+
}
|
|
140
|
+
async dispose() {
|
|
141
|
+
await this.waitForReady();
|
|
142
|
+
// Signal the shared worker that this client is closing its connection to the worker
|
|
143
|
+
const closeMessagePayload = {
|
|
144
|
+
event: SharedSyncClientEvent.CLOSE_CLIENT,
|
|
145
|
+
data: {}
|
|
146
|
+
};
|
|
147
|
+
this.messagePort.postMessage(closeMessagePayload);
|
|
148
|
+
// Release the proxy
|
|
149
|
+
this.syncManager[Comlink.releaseProxy]();
|
|
150
|
+
}
|
|
151
|
+
async waitForReady() {
|
|
152
|
+
return this.isInitialized;
|
|
178
153
|
}
|
|
179
154
|
/**
|
|
180
155
|
* Used in tests to force a connection states
|
|
181
156
|
*/
|
|
182
|
-
_testUpdateStatus(status) {
|
|
183
|
-
return
|
|
184
|
-
return this.syncManager['_testUpdateAllStatuses'](status.toJSON());
|
|
185
|
-
});
|
|
157
|
+
async _testUpdateStatus(status) {
|
|
158
|
+
return this.syncManager['_testUpdateAllStatuses'](status.toJSON());
|
|
186
159
|
}
|
|
187
160
|
}
|
|
@@ -1,25 +1,15 @@
|
|
|
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 { AbstractRemote } from '@powersync/common';
|
|
11
2
|
export class WebRemote extends AbstractRemote {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
return this._bson;
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* Dynamic import to be used only when needed.
|
|
19
|
-
*/
|
|
20
|
-
const { BSON } = yield import('bson');
|
|
21
|
-
this._bson = BSON;
|
|
3
|
+
_bson;
|
|
4
|
+
async getBSON() {
|
|
5
|
+
if (this._bson) {
|
|
22
6
|
return this._bson;
|
|
23
|
-
}
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Dynamic import to be used only when needed.
|
|
10
|
+
*/
|
|
11
|
+
const { BSON } = await import('bson');
|
|
12
|
+
this._bson = BSON;
|
|
13
|
+
return this._bson;
|
|
24
14
|
}
|
|
25
15
|
}
|
|
@@ -1,207 +1,176 @@
|
|
|
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
|
-
var __asyncValues = (this && this.__asyncValues) || function (o) {
|
|
11
|
-
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
|
12
|
-
var m = o[Symbol.asyncIterator], i;
|
|
13
|
-
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
|
|
14
|
-
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
|
|
15
|
-
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
|
|
16
|
-
};
|
|
17
1
|
import * as SQLite from '@journeyapps/wa-sqlite';
|
|
18
2
|
import '@journeyapps/wa-sqlite';
|
|
19
3
|
import * as Comlink from 'comlink';
|
|
20
4
|
let nextId = 1;
|
|
21
|
-
export function _openDB(
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
return
|
|
43
|
-
return executeSingleStatement(sql, bindings);
|
|
44
|
-
}));
|
|
5
|
+
export async function _openDB(dbFileName, options = { useWebWorker: true }) {
|
|
6
|
+
const { default: moduleFactory } = await import('@journeyapps/wa-sqlite/dist/wa-sqlite-async.mjs');
|
|
7
|
+
const module = await moduleFactory();
|
|
8
|
+
const sqlite3 = SQLite.Factory(module);
|
|
9
|
+
const { IDBBatchAtomicVFS } = await import('@journeyapps/wa-sqlite/src/examples/IDBBatchAtomicVFS.js');
|
|
10
|
+
const vfs = new IDBBatchAtomicVFS(dbFileName);
|
|
11
|
+
sqlite3.vfs_register(vfs, true);
|
|
12
|
+
const db = await sqlite3.open_v2(dbFileName);
|
|
13
|
+
/**
|
|
14
|
+
* Listeners are exclusive to the DB connection.
|
|
15
|
+
*/
|
|
16
|
+
const listeners = new Map();
|
|
17
|
+
sqlite3.register_table_onchange_hook(db, (opType, tableName, rowId) => {
|
|
18
|
+
Array.from(listeners.values()).forEach((l) => l(opType, tableName, rowId));
|
|
19
|
+
});
|
|
20
|
+
/**
|
|
21
|
+
* This executes single SQL statements inside a requested lock.
|
|
22
|
+
*/
|
|
23
|
+
const execute = async (sql, bindings) => {
|
|
24
|
+
// Running multiple statements on the same connection concurrently should not be allowed
|
|
25
|
+
return _acquireExecuteLock(async () => {
|
|
26
|
+
return executeSingleStatement(sql, bindings);
|
|
45
27
|
});
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* This requests a lock for executing statements.
|
|
31
|
+
* Should only be used interanlly.
|
|
32
|
+
*/
|
|
33
|
+
const _acquireExecuteLock = (callback) => {
|
|
34
|
+
return navigator.locks.request(`db-execute-${dbFileName}`, callback);
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* This executes a single statement using SQLite3.
|
|
38
|
+
*/
|
|
39
|
+
const executeSingleStatement = async (sql, bindings) => {
|
|
40
|
+
const results = [];
|
|
41
|
+
for await (const stmt of sqlite3.statements(db, sql)) {
|
|
42
|
+
let columns;
|
|
43
|
+
const wrappedBindings = bindings ? [bindings] : [[]];
|
|
44
|
+
for (const binding of wrappedBindings) {
|
|
45
|
+
// TODO not sure why this is needed currently, but booleans break
|
|
46
|
+
binding.forEach((b, index, arr) => {
|
|
47
|
+
if (typeof b == 'boolean') {
|
|
48
|
+
arr[index] = b ? 1 : 0;
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
sqlite3.reset(stmt);
|
|
52
|
+
if (bindings) {
|
|
53
|
+
sqlite3.bind_collection(stmt, binding);
|
|
54
|
+
}
|
|
55
|
+
const rows = [];
|
|
56
|
+
while ((await sqlite3.step(stmt)) === SQLite.SQLITE_ROW) {
|
|
57
|
+
const row = sqlite3.row(stmt);
|
|
58
|
+
rows.push(row);
|
|
59
|
+
}
|
|
60
|
+
columns = columns ?? sqlite3.column_names(stmt);
|
|
61
|
+
if (columns.length) {
|
|
62
|
+
results.push({ columns, rows });
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// When binding parameters, only a single statement is executed.
|
|
66
|
+
if (bindings) {
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
const rows = [];
|
|
71
|
+
for (const resultset of results) {
|
|
72
|
+
for (const row of resultset.rows) {
|
|
73
|
+
const outRow = {};
|
|
74
|
+
resultset.columns.forEach((key, index) => {
|
|
75
|
+
outRow[key] = row[index];
|
|
76
|
+
});
|
|
77
|
+
rows.push(outRow);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const result = {
|
|
81
|
+
insertId: sqlite3.last_insert_id(db),
|
|
82
|
+
rowsAffected: sqlite3.changes(db),
|
|
83
|
+
rows: {
|
|
84
|
+
_array: rows,
|
|
85
|
+
length: rows.length
|
|
86
|
+
}
|
|
52
87
|
};
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
88
|
+
return result;
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
91
|
+
* This executes SQL statements in a batch.
|
|
92
|
+
*/
|
|
93
|
+
const executeBatch = async (sql, bindings) => {
|
|
94
|
+
return _acquireExecuteLock(async () => {
|
|
95
|
+
let affectedRows = 0;
|
|
96
|
+
const str = sqlite3.str_new(db, sql);
|
|
97
|
+
const query = sqlite3.str_value(str);
|
|
59
98
|
try {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
sqlite3.bind_collection(stmt, binding);
|
|
76
|
-
}
|
|
77
|
-
const rows = [];
|
|
78
|
-
while ((yield sqlite3.step(stmt)) === SQLite.SQLITE_ROW) {
|
|
79
|
-
const row = sqlite3.row(stmt);
|
|
80
|
-
rows.push(row);
|
|
81
|
-
}
|
|
82
|
-
columns = columns !== null && columns !== void 0 ? columns : sqlite3.column_names(stmt);
|
|
83
|
-
if (columns.length) {
|
|
84
|
-
results.push({ columns, rows });
|
|
99
|
+
await executeSingleStatement('BEGIN TRANSACTION');
|
|
100
|
+
//Prepare statement once
|
|
101
|
+
const prepared = await sqlite3.prepare_v2(db, query);
|
|
102
|
+
if (prepared === null) {
|
|
103
|
+
return {
|
|
104
|
+
rowsAffected: 0
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
const wrappedBindings = bindings ? bindings : [];
|
|
108
|
+
for (const binding of wrappedBindings) {
|
|
109
|
+
// TODO not sure why this is needed currently, but booleans break
|
|
110
|
+
for (let i = 0; i < binding.length; i++) {
|
|
111
|
+
const b = binding[i];
|
|
112
|
+
if (typeof b == 'boolean') {
|
|
113
|
+
binding[i] = b ? 1 : 0;
|
|
85
114
|
}
|
|
86
115
|
}
|
|
87
|
-
//
|
|
116
|
+
//Reset bindings
|
|
117
|
+
sqlite3.reset(prepared.stmt);
|
|
88
118
|
if (bindings) {
|
|
89
|
-
|
|
119
|
+
sqlite3.bind_collection(prepared.stmt, binding);
|
|
120
|
+
}
|
|
121
|
+
const result = await sqlite3.step(prepared.stmt);
|
|
122
|
+
if (result === SQLite.SQLITE_DONE) {
|
|
123
|
+
//The value returned by sqlite3_changes() immediately after an INSERT, UPDATE or DELETE statement run on a view is always zero.
|
|
124
|
+
affectedRows += sqlite3.changes(db);
|
|
90
125
|
}
|
|
91
126
|
}
|
|
127
|
+
//Finalize prepared statement
|
|
128
|
+
await sqlite3.finalize(prepared.stmt);
|
|
129
|
+
await executeSingleStatement('COMMIT');
|
|
92
130
|
}
|
|
93
|
-
catch (
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}
|
|
98
|
-
finally { if (e_1) throw e_1.error; }
|
|
131
|
+
catch (err) {
|
|
132
|
+
await executeSingleStatement('ROLLBACK');
|
|
133
|
+
return {
|
|
134
|
+
rowsAffected: 0
|
|
135
|
+
};
|
|
99
136
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
for (const row of resultset.rows) {
|
|
103
|
-
const outRow = {};
|
|
104
|
-
resultset.columns.forEach((key, index) => {
|
|
105
|
-
outRow[key] = row[index];
|
|
106
|
-
});
|
|
107
|
-
rows.push(outRow);
|
|
108
|
-
}
|
|
137
|
+
finally {
|
|
138
|
+
sqlite3.str_finish(str);
|
|
109
139
|
}
|
|
110
140
|
const result = {
|
|
111
|
-
|
|
112
|
-
rowsAffected: sqlite3.changes(db),
|
|
113
|
-
rows: {
|
|
114
|
-
_array: rows,
|
|
115
|
-
length: rows.length
|
|
116
|
-
}
|
|
141
|
+
rowsAffected: affectedRows
|
|
117
142
|
};
|
|
118
143
|
return result;
|
|
119
144
|
});
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
*/
|
|
123
|
-
const executeBatch = (sql, bindings) => __awaiter(this, void 0, void 0, function* () {
|
|
124
|
-
return _acquireExecuteLock(() => __awaiter(this, void 0, void 0, function* () {
|
|
125
|
-
let affectedRows = 0;
|
|
126
|
-
const str = sqlite3.str_new(db, sql);
|
|
127
|
-
const query = sqlite3.str_value(str);
|
|
128
|
-
try {
|
|
129
|
-
yield executeSingleStatement('BEGIN TRANSACTION');
|
|
130
|
-
//Prepare statement once
|
|
131
|
-
const prepared = yield sqlite3.prepare_v2(db, query);
|
|
132
|
-
if (prepared === null) {
|
|
133
|
-
return {
|
|
134
|
-
rowsAffected: 0
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
const wrappedBindings = bindings ? bindings : [];
|
|
138
|
-
for (const binding of wrappedBindings) {
|
|
139
|
-
// TODO not sure why this is needed currently, but booleans break
|
|
140
|
-
for (let i = 0; i < binding.length; i++) {
|
|
141
|
-
const b = binding[i];
|
|
142
|
-
if (typeof b == 'boolean') {
|
|
143
|
-
binding[i] = b ? 1 : 0;
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
//Reset bindings
|
|
147
|
-
sqlite3.reset(prepared.stmt);
|
|
148
|
-
if (bindings) {
|
|
149
|
-
sqlite3.bind_collection(prepared.stmt, binding);
|
|
150
|
-
}
|
|
151
|
-
const result = yield sqlite3.step(prepared.stmt);
|
|
152
|
-
if (result === SQLite.SQLITE_DONE) {
|
|
153
|
-
//The value returned by sqlite3_changes() immediately after an INSERT, UPDATE or DELETE statement run on a view is always zero.
|
|
154
|
-
affectedRows += sqlite3.changes(db);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
//Finalize prepared statement
|
|
158
|
-
yield sqlite3.finalize(prepared.stmt);
|
|
159
|
-
yield executeSingleStatement('COMMIT');
|
|
160
|
-
}
|
|
161
|
-
catch (err) {
|
|
162
|
-
yield executeSingleStatement('ROLLBACK');
|
|
163
|
-
return {
|
|
164
|
-
rowsAffected: 0
|
|
165
|
-
};
|
|
166
|
-
}
|
|
167
|
-
finally {
|
|
168
|
-
sqlite3.str_finish(str);
|
|
169
|
-
}
|
|
170
|
-
const result = {
|
|
171
|
-
rowsAffected: affectedRows
|
|
172
|
-
};
|
|
173
|
-
return result;
|
|
174
|
-
}));
|
|
175
|
-
});
|
|
176
|
-
if (options.useWebWorker) {
|
|
177
|
-
const registerOnTableChange = (callback) => {
|
|
178
|
-
const id = nextId++;
|
|
179
|
-
listeners.set(id, callback);
|
|
180
|
-
return Comlink.proxy(() => {
|
|
181
|
-
listeners.delete(id);
|
|
182
|
-
});
|
|
183
|
-
};
|
|
184
|
-
return {
|
|
185
|
-
execute: Comlink.proxy(execute),
|
|
186
|
-
executeBatch: Comlink.proxy(executeBatch),
|
|
187
|
-
registerOnTableChange: Comlink.proxy(registerOnTableChange),
|
|
188
|
-
close: Comlink.proxy(() => {
|
|
189
|
-
sqlite3.close(db);
|
|
190
|
-
})
|
|
191
|
-
};
|
|
192
|
-
}
|
|
145
|
+
};
|
|
146
|
+
if (options.useWebWorker) {
|
|
193
147
|
const registerOnTableChange = (callback) => {
|
|
194
148
|
const id = nextId++;
|
|
195
149
|
listeners.set(id, callback);
|
|
196
|
-
return () => {
|
|
150
|
+
return Comlink.proxy(() => {
|
|
197
151
|
listeners.delete(id);
|
|
198
|
-
};
|
|
152
|
+
});
|
|
199
153
|
};
|
|
200
154
|
return {
|
|
201
|
-
execute: execute,
|
|
202
|
-
executeBatch: executeBatch,
|
|
203
|
-
registerOnTableChange: registerOnTableChange,
|
|
204
|
-
close: () =>
|
|
155
|
+
execute: Comlink.proxy(execute),
|
|
156
|
+
executeBatch: Comlink.proxy(executeBatch),
|
|
157
|
+
registerOnTableChange: Comlink.proxy(registerOnTableChange),
|
|
158
|
+
close: Comlink.proxy(() => {
|
|
159
|
+
sqlite3.close(db);
|
|
160
|
+
})
|
|
205
161
|
};
|
|
206
|
-
}
|
|
162
|
+
}
|
|
163
|
+
const registerOnTableChange = (callback) => {
|
|
164
|
+
const id = nextId++;
|
|
165
|
+
listeners.set(id, callback);
|
|
166
|
+
return () => {
|
|
167
|
+
listeners.delete(id);
|
|
168
|
+
};
|
|
169
|
+
};
|
|
170
|
+
return {
|
|
171
|
+
execute: execute,
|
|
172
|
+
executeBatch: executeBatch,
|
|
173
|
+
registerOnTableChange: registerOnTableChange,
|
|
174
|
+
close: () => sqlite3.close(db)
|
|
175
|
+
};
|
|
207
176
|
}
|