@powersync/node 0.0.0-dev-20260128170935 → 0.0.0-dev-20260202162549
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/package.json +4 -4
- package/dist/DefaultWorker.cjs +0 -232
- package/dist/DefaultWorker.cjs.map +0 -1
- package/dist/bundle.cjs +0 -647
- package/dist/bundle.cjs.map +0 -1
- package/dist/bundle.d.cts +0 -127
- package/dist/worker.cjs +0 -233
- package/dist/worker.cjs.map +0 -1
- package/dist/worker.d.cts +0 -20
- package/lib/attachments/NodeFileSystemAdapter.d.ts +0 -30
- package/lib/attachments/NodeFileSystemAdapter.js +0 -77
- package/lib/attachments/NodeFileSystemAdapter.js.map +0 -1
- package/lib/db/AsyncDatabase.d.ts +0 -22
- package/lib/db/AsyncDatabase.js +0 -2
- package/lib/db/AsyncDatabase.js.map +0 -1
- package/lib/db/BetterSqliteWorker.d.ts +0 -26
- package/lib/db/BetterSqliteWorker.js +0 -59
- package/lib/db/BetterSqliteWorker.js.map +0 -1
- package/lib/db/DefaultWorker.d.ts +0 -1
- package/lib/db/DefaultWorker.js +0 -3
- package/lib/db/DefaultWorker.js.map +0 -1
- package/lib/db/NodeSqliteWorker.d.ts +0 -21
- package/lib/db/NodeSqliteWorker.js +0 -47
- package/lib/db/NodeSqliteWorker.js.map +0 -1
- package/lib/db/PowerSyncDatabase.d.ts +0 -39
- package/lib/db/PowerSyncDatabase.js +0 -57
- package/lib/db/PowerSyncDatabase.js.map +0 -1
- package/lib/db/RemoteConnection.d.ts +0 -29
- package/lib/db/RemoteConnection.js +0 -106
- package/lib/db/RemoteConnection.js.map +0 -1
- package/lib/db/SqliteWorker.d.ts +0 -17
- package/lib/db/SqliteWorker.js +0 -105
- package/lib/db/SqliteWorker.js.map +0 -1
- package/lib/db/WorkerConnectionPool.d.ts +0 -32
- package/lib/db/WorkerConnectionPool.js +0 -266
- package/lib/db/WorkerConnectionPool.js.map +0 -1
- package/lib/db/options.d.ts +0 -44
- package/lib/db/options.js +0 -2
- package/lib/db/options.js.map +0 -1
- package/lib/index.d.ts +0 -3
- package/lib/index.js +0 -5
- package/lib/index.js.map +0 -1
- package/lib/libpowersync_aarch64.linux.so +0 -0
- package/lib/libpowersync_aarch64.macos.dylib +0 -0
- package/lib/libpowersync_armv7.linux.so +0 -0
- package/lib/libpowersync_riscv64gc.linux.so +0 -0
- package/lib/libpowersync_x64.linux.so +0 -0
- package/lib/libpowersync_x64.macos.dylib +0 -0
- package/lib/libpowersync_x86.linux.so +0 -0
- package/lib/powersync_aarch64.dll +0 -0
- package/lib/powersync_x64.dll +0 -0
- package/lib/powersync_x86.dll +0 -0
- package/lib/sync/stream/NodeRemote.d.ts +0 -23
- package/lib/sync/stream/NodeRemote.js +0 -79
- package/lib/sync/stream/NodeRemote.js.map +0 -1
- package/lib/sync/stream/NodeStreamingSyncImplementation.d.ts +0 -11
- package/lib/sync/stream/NodeStreamingSyncImplementation.js +0 -44
- package/lib/sync/stream/NodeStreamingSyncImplementation.js.map +0 -1
- package/lib/utils/modules.d.ts +0 -2
- package/lib/utils/modules.js +0 -6
- package/lib/utils/modules.js.map +0 -1
- package/lib/utils/modules_commonjs.d.ts +0 -2
- package/lib/utils/modules_commonjs.js +0 -7
- package/lib/utils/modules_commonjs.js.map +0 -1
- package/lib/worker.d.ts +0 -1
- package/lib/worker.js +0 -2
- package/lib/worker.js.map +0 -1
package/dist/bundle.cjs
DELETED
|
@@ -1,647 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var common = require('@powersync/common');
|
|
4
|
-
var os = require('node:os');
|
|
5
|
-
var bson = require('bson');
|
|
6
|
-
var undici = require('undici');
|
|
7
|
-
var asyncMutex = require('async-mutex');
|
|
8
|
-
var Comlink = require('comlink');
|
|
9
|
-
var fs = require('node:fs/promises');
|
|
10
|
-
var path = require('node:path');
|
|
11
|
-
var node_worker_threads = require('node:worker_threads');
|
|
12
|
-
var node_async_hooks = require('node:async_hooks');
|
|
13
|
-
var fs$1 = require('fs');
|
|
14
|
-
var path$1 = require('path');
|
|
15
|
-
|
|
16
|
-
function _interopNamespaceDefault(e) {
|
|
17
|
-
var n = Object.create(null);
|
|
18
|
-
if (e) {
|
|
19
|
-
Object.keys(e).forEach(function (k) {
|
|
20
|
-
if (k !== 'default') {
|
|
21
|
-
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
22
|
-
Object.defineProperty(n, k, d.get ? d : {
|
|
23
|
-
enumerable: true,
|
|
24
|
-
get: function () { return e[k]; }
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
n.default = e;
|
|
30
|
-
return Object.freeze(n);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
var os__namespace = /*#__PURE__*/_interopNamespaceDefault(os);
|
|
34
|
-
var Comlink__namespace = /*#__PURE__*/_interopNamespaceDefault(Comlink);
|
|
35
|
-
var path__namespace = /*#__PURE__*/_interopNamespaceDefault(path);
|
|
36
|
-
var path__namespace$1 = /*#__PURE__*/_interopNamespaceDefault(path$1);
|
|
37
|
-
|
|
38
|
-
class NodeFetchProvider extends common.FetchImplementationProvider {
|
|
39
|
-
getFetch() {
|
|
40
|
-
return fetch.bind(globalThis);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
class NodeRemote extends common.AbstractRemote {
|
|
44
|
-
connector;
|
|
45
|
-
logger;
|
|
46
|
-
wsDispatcher;
|
|
47
|
-
constructor(connector, logger = common.DEFAULT_REMOTE_LOGGER, options) {
|
|
48
|
-
const fetchDispatcher = options?.dispatcher ?? defaultFetchDispatcher();
|
|
49
|
-
super(connector, logger, {
|
|
50
|
-
fetchImplementation: options?.fetchImplementation ?? new NodeFetchProvider(),
|
|
51
|
-
fetchOptions: {
|
|
52
|
-
dispatcher: fetchDispatcher
|
|
53
|
-
},
|
|
54
|
-
...(options ?? {})
|
|
55
|
-
});
|
|
56
|
-
this.connector = connector;
|
|
57
|
-
this.logger = logger;
|
|
58
|
-
this.wsDispatcher = options?.dispatcher;
|
|
59
|
-
}
|
|
60
|
-
createSocket(url) {
|
|
61
|
-
// Create dedicated dispatcher for this WebSocket
|
|
62
|
-
const baseDispatcher = this.getWebsocketDispatcher(url);
|
|
63
|
-
// Create WebSocket with dedicated dispatcher
|
|
64
|
-
const ws = new undici.WebSocket(url, {
|
|
65
|
-
dispatcher: baseDispatcher,
|
|
66
|
-
headers: {
|
|
67
|
-
'User-Agent': this.getUserAgent()
|
|
68
|
-
}
|
|
69
|
-
});
|
|
70
|
-
return ws;
|
|
71
|
-
}
|
|
72
|
-
getWebsocketDispatcher(url) {
|
|
73
|
-
if (this.wsDispatcher != null) {
|
|
74
|
-
return this.wsDispatcher;
|
|
75
|
-
}
|
|
76
|
-
const protocol = new URL(url).protocol.replace(':', '');
|
|
77
|
-
const proxy = getProxyForProtocol(protocol);
|
|
78
|
-
if (proxy != null) {
|
|
79
|
-
return new undici.ProxyAgent(proxy);
|
|
80
|
-
}
|
|
81
|
-
else {
|
|
82
|
-
return undici.getGlobalDispatcher();
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
getUserAgent() {
|
|
86
|
-
return [
|
|
87
|
-
super.getUserAgent(),
|
|
88
|
-
`powersync-node`,
|
|
89
|
-
`node/${process.versions.node}`,
|
|
90
|
-
`${os__namespace.platform()}/${os__namespace.release()}`
|
|
91
|
-
].join(' ');
|
|
92
|
-
}
|
|
93
|
-
async getBSON() {
|
|
94
|
-
return bson.BSON;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
function defaultFetchDispatcher() {
|
|
98
|
-
// EnvHttpProxyAgent automatically uses HTTP_PROXY, HTTPS_PROXY and NO_PROXY env vars by default.
|
|
99
|
-
// We add ALL_PROXY support.
|
|
100
|
-
return new undici.EnvHttpProxyAgent({
|
|
101
|
-
httpProxy: getProxyForProtocol('http'),
|
|
102
|
-
httpsProxy: getProxyForProtocol('https')
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
function getProxyForProtocol(protocol) {
|
|
106
|
-
return (process.env[`${protocol.toLowerCase()}_proxy`] ??
|
|
107
|
-
process.env[`${protocol.toUpperCase()}_PROXY`] ??
|
|
108
|
-
process.env[`all_proxy`] ??
|
|
109
|
-
process.env[`ALL_PROXY`]);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Global locks which prevent multiple instances from syncing
|
|
114
|
-
* concurrently.
|
|
115
|
-
*/
|
|
116
|
-
const LOCKS = new Map();
|
|
117
|
-
new Set(Object.values(common.LockType));
|
|
118
|
-
class NodeStreamingSyncImplementation extends common.AbstractStreamingSyncImplementation {
|
|
119
|
-
locks;
|
|
120
|
-
constructor(options) {
|
|
121
|
-
super(options);
|
|
122
|
-
this.initLocks();
|
|
123
|
-
}
|
|
124
|
-
/**
|
|
125
|
-
* Configures global locks on sync process
|
|
126
|
-
*/
|
|
127
|
-
initLocks() {
|
|
128
|
-
const { identifier } = this.options;
|
|
129
|
-
if (identifier && LOCKS.has(identifier)) {
|
|
130
|
-
this.locks = LOCKS.get(identifier);
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
133
|
-
this.locks = new Map();
|
|
134
|
-
this.locks.set(common.LockType.CRUD, new asyncMutex.Mutex());
|
|
135
|
-
this.locks.set(common.LockType.SYNC, new asyncMutex.Mutex());
|
|
136
|
-
if (identifier) {
|
|
137
|
-
LOCKS.set(identifier, this.locks);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
obtainLock(lockOptions) {
|
|
141
|
-
const lock = this.locks.get(lockOptions.type);
|
|
142
|
-
if (!lock) {
|
|
143
|
-
throw new Error(`Lock type ${lockOptions.type} not found`);
|
|
144
|
-
}
|
|
145
|
-
return lock.runExclusive(async () => {
|
|
146
|
-
if (lockOptions.signal?.aborted) {
|
|
147
|
-
throw new Error('Aborted');
|
|
148
|
-
}
|
|
149
|
-
return lockOptions.callback();
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* A PowerSync database connection implemented with RPC calls to a background worker.
|
|
156
|
-
*/
|
|
157
|
-
class RemoteConnection {
|
|
158
|
-
isBusy = false;
|
|
159
|
-
worker;
|
|
160
|
-
comlink;
|
|
161
|
-
database;
|
|
162
|
-
notifyWorkerClosed = new AbortController();
|
|
163
|
-
constructor(worker, comlink, database) {
|
|
164
|
-
this.worker = worker;
|
|
165
|
-
this.comlink = comlink;
|
|
166
|
-
this.database = database;
|
|
167
|
-
this.worker.once('exit', (_) => {
|
|
168
|
-
this.notifyWorkerClosed.abort();
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
/**
|
|
172
|
-
* Runs the inner function, but appends the stack trace where this function was called. This is useful for workers
|
|
173
|
-
* because stack traces from worker errors are otherwise unrelated to the application issue that has caused them.
|
|
174
|
-
*/
|
|
175
|
-
withRemote(inner) {
|
|
176
|
-
const trace = {};
|
|
177
|
-
Error.captureStackTrace(trace);
|
|
178
|
-
const controller = this.notifyWorkerClosed;
|
|
179
|
-
return new Promise((resolve, reject) => {
|
|
180
|
-
if (controller.signal.aborted) {
|
|
181
|
-
reject(new common.ConnectionClosedError('Called operation on closed remote'));
|
|
182
|
-
}
|
|
183
|
-
function handleAbort() {
|
|
184
|
-
reject(new common.ConnectionClosedError('Remote peer closed with request in flight'));
|
|
185
|
-
}
|
|
186
|
-
function completePromise(action) {
|
|
187
|
-
controller.signal.removeEventListener('abort', handleAbort);
|
|
188
|
-
action();
|
|
189
|
-
}
|
|
190
|
-
controller.signal.addEventListener('abort', handleAbort);
|
|
191
|
-
inner()
|
|
192
|
-
.then((data) => completePromise(() => resolve(data)))
|
|
193
|
-
.catch((e) => {
|
|
194
|
-
if (e instanceof Error && e.stack) {
|
|
195
|
-
e.stack += trace.stack;
|
|
196
|
-
}
|
|
197
|
-
return completePromise(() => reject(e));
|
|
198
|
-
});
|
|
199
|
-
});
|
|
200
|
-
}
|
|
201
|
-
executeBatch(query, params = []) {
|
|
202
|
-
return this.withRemote(async () => {
|
|
203
|
-
const result = await this.database.executeBatch(query, params ?? []);
|
|
204
|
-
return RemoteConnection.wrapQueryResult(result);
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
|
-
execute(query, params) {
|
|
208
|
-
return this.withRemote(async () => {
|
|
209
|
-
const result = await this.database.execute(query, params ?? []);
|
|
210
|
-
return RemoteConnection.wrapQueryResult(result);
|
|
211
|
-
});
|
|
212
|
-
}
|
|
213
|
-
executeRaw(query, params) {
|
|
214
|
-
return this.withRemote(async () => {
|
|
215
|
-
return await this.database.executeRaw(query, params ?? []);
|
|
216
|
-
});
|
|
217
|
-
}
|
|
218
|
-
async getAll(sql, parameters) {
|
|
219
|
-
const res = await this.execute(sql, parameters);
|
|
220
|
-
return res.rows?._array ?? [];
|
|
221
|
-
}
|
|
222
|
-
async getOptional(sql, parameters) {
|
|
223
|
-
const res = await this.execute(sql, parameters);
|
|
224
|
-
return res.rows?.item(0) ?? null;
|
|
225
|
-
}
|
|
226
|
-
async get(sql, parameters) {
|
|
227
|
-
const res = await this.execute(sql, parameters);
|
|
228
|
-
const first = res.rows?.item(0);
|
|
229
|
-
if (!first) {
|
|
230
|
-
throw new Error('Result set is empty');
|
|
231
|
-
}
|
|
232
|
-
return first;
|
|
233
|
-
}
|
|
234
|
-
async refreshSchema() {
|
|
235
|
-
await this.execute("pragma table_info('sqlite_master')");
|
|
236
|
-
}
|
|
237
|
-
async close() {
|
|
238
|
-
await this.database.close();
|
|
239
|
-
this.database[Comlink.releaseProxy]();
|
|
240
|
-
this.comlink[Comlink.releaseProxy]();
|
|
241
|
-
await this.worker.terminate();
|
|
242
|
-
}
|
|
243
|
-
static wrapQueryResult(result) {
|
|
244
|
-
let rows = undefined;
|
|
245
|
-
if (result.rows) {
|
|
246
|
-
rows = {
|
|
247
|
-
...result.rows,
|
|
248
|
-
item: (idx) => result.rows?._array[idx]
|
|
249
|
-
};
|
|
250
|
-
}
|
|
251
|
-
return {
|
|
252
|
-
...result,
|
|
253
|
-
rows
|
|
254
|
-
};
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
const READ_CONNECTIONS = 5;
|
|
259
|
-
const defaultDatabaseImplementation = {
|
|
260
|
-
type: 'better-sqlite3'
|
|
261
|
-
};
|
|
262
|
-
/**
|
|
263
|
-
* Adapter for better-sqlite3
|
|
264
|
-
*/
|
|
265
|
-
class WorkerConnectionPool extends common.BaseObserver {
|
|
266
|
-
options;
|
|
267
|
-
name;
|
|
268
|
-
readConnections;
|
|
269
|
-
writeConnection;
|
|
270
|
-
readQueue = [];
|
|
271
|
-
writeQueue = [];
|
|
272
|
-
constructor(options) {
|
|
273
|
-
super();
|
|
274
|
-
if (options.readWorkerCount != null && options.readWorkerCount < 1) {
|
|
275
|
-
throw `Needs at least one worker for reads, got ${options.readWorkerCount}`;
|
|
276
|
-
}
|
|
277
|
-
this.options = options;
|
|
278
|
-
this.name = options.dbFilename;
|
|
279
|
-
}
|
|
280
|
-
async initialize() {
|
|
281
|
-
let dbFilePath = this.options.dbFilename;
|
|
282
|
-
if (this.options.dbLocation !== undefined) {
|
|
283
|
-
// Make sure the dbLocation exists, we get a TypeError from better-sqlite3 otherwise.
|
|
284
|
-
let directoryExists = false;
|
|
285
|
-
try {
|
|
286
|
-
const stat = await fs.stat(this.options.dbLocation);
|
|
287
|
-
directoryExists = stat.isDirectory();
|
|
288
|
-
}
|
|
289
|
-
catch (_) {
|
|
290
|
-
// If we can't even stat, the directory won't be accessible to SQLite either.
|
|
291
|
-
}
|
|
292
|
-
if (!directoryExists) {
|
|
293
|
-
throw new Error(`The dbLocation directory at "${this.options.dbLocation}" does not exist. Please create it before opening the PowerSync database!`);
|
|
294
|
-
}
|
|
295
|
-
dbFilePath = path__namespace.join(this.options.dbLocation, dbFilePath);
|
|
296
|
-
}
|
|
297
|
-
const openWorker = async (isWriter) => {
|
|
298
|
-
let worker;
|
|
299
|
-
const workerName = isWriter ? `write ${dbFilePath}` : `read ${dbFilePath}`;
|
|
300
|
-
const workerFactory = this.options.openWorker ?? ((...args) => new node_worker_threads.Worker(...args));
|
|
301
|
-
{
|
|
302
|
-
worker = workerFactory(path__namespace.resolve(__dirname, 'DefaultWorker.cjs'), { name: workerName });
|
|
303
|
-
}
|
|
304
|
-
const listeners = new WeakMap();
|
|
305
|
-
const comlink = Comlink__namespace.wrap({
|
|
306
|
-
postMessage: worker.postMessage.bind(worker),
|
|
307
|
-
addEventListener: (type, listener) => {
|
|
308
|
-
let resolved = 'handleEvent' in listener ? listener.handleEvent.bind(listener) : listener;
|
|
309
|
-
// Comlink wants message events, but the message event on workers in Node returns the data only.
|
|
310
|
-
if (type === 'message') {
|
|
311
|
-
const original = resolved;
|
|
312
|
-
resolved = (data) => {
|
|
313
|
-
original({ data });
|
|
314
|
-
};
|
|
315
|
-
}
|
|
316
|
-
listeners.set(listener, resolved);
|
|
317
|
-
worker.addListener(type, resolved);
|
|
318
|
-
},
|
|
319
|
-
removeEventListener: (type, listener) => {
|
|
320
|
-
const resolved = listeners.get(listener);
|
|
321
|
-
if (!resolved) {
|
|
322
|
-
return;
|
|
323
|
-
}
|
|
324
|
-
worker.removeListener(type, resolved);
|
|
325
|
-
}
|
|
326
|
-
});
|
|
327
|
-
worker.once('error', (e) => {
|
|
328
|
-
console.error('Unexpected PowerSync database worker error', e);
|
|
329
|
-
});
|
|
330
|
-
const database = (await comlink.open({
|
|
331
|
-
path: dbFilePath,
|
|
332
|
-
isWriter,
|
|
333
|
-
implementation: this.options.implementation ?? defaultDatabaseImplementation
|
|
334
|
-
}));
|
|
335
|
-
if (isWriter) {
|
|
336
|
-
await database.execute("SELECT powersync_update_hooks('install');", []);
|
|
337
|
-
}
|
|
338
|
-
const connection = new RemoteConnection(worker, comlink, database);
|
|
339
|
-
if (this.options.initializeConnection) {
|
|
340
|
-
await this.options.initializeConnection(connection, isWriter);
|
|
341
|
-
}
|
|
342
|
-
if (!isWriter) {
|
|
343
|
-
await connection.execute('pragma query_only = true');
|
|
344
|
-
}
|
|
345
|
-
else {
|
|
346
|
-
// We only need to enable this on the writer connection.
|
|
347
|
-
// We can get `database is locked` errors if we enable this on concurrently opening read connections.
|
|
348
|
-
await connection.execute('pragma journal_mode = WAL');
|
|
349
|
-
}
|
|
350
|
-
return connection;
|
|
351
|
-
};
|
|
352
|
-
// Open the writer first to avoid multiple threads enabling WAL concurrently (causing "database is locked" errors).
|
|
353
|
-
this.writeConnection = await openWorker(true);
|
|
354
|
-
const createWorkers = [];
|
|
355
|
-
const amountOfReaders = this.options.readWorkerCount ?? READ_CONNECTIONS;
|
|
356
|
-
for (let i = 0; i < amountOfReaders; i++) {
|
|
357
|
-
createWorkers.push(openWorker(false));
|
|
358
|
-
}
|
|
359
|
-
this.readConnections = await Promise.all(createWorkers);
|
|
360
|
-
}
|
|
361
|
-
async close() {
|
|
362
|
-
await this.writeConnection.close();
|
|
363
|
-
for (const connection of this.readConnections) {
|
|
364
|
-
await connection.close();
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
readLock(fn, _options) {
|
|
368
|
-
let resolveConnectionPromise;
|
|
369
|
-
const connectionPromise = new Promise((resolve, _reject) => {
|
|
370
|
-
resolveConnectionPromise = node_async_hooks.AsyncResource.bind(resolve);
|
|
371
|
-
});
|
|
372
|
-
const connection = this.readConnections.find((connection) => !connection.isBusy);
|
|
373
|
-
if (connection) {
|
|
374
|
-
connection.isBusy = true;
|
|
375
|
-
resolveConnectionPromise(connection);
|
|
376
|
-
}
|
|
377
|
-
else {
|
|
378
|
-
this.readQueue.push(resolveConnectionPromise);
|
|
379
|
-
}
|
|
380
|
-
return (async () => {
|
|
381
|
-
const connection = await connectionPromise;
|
|
382
|
-
try {
|
|
383
|
-
return await fn(connection);
|
|
384
|
-
}
|
|
385
|
-
finally {
|
|
386
|
-
const next = this.readQueue.shift();
|
|
387
|
-
if (next) {
|
|
388
|
-
next(connection);
|
|
389
|
-
}
|
|
390
|
-
else {
|
|
391
|
-
connection.isBusy = false;
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
})();
|
|
395
|
-
}
|
|
396
|
-
writeLock(fn, _options) {
|
|
397
|
-
let resolveLockPromise;
|
|
398
|
-
const lockPromise = new Promise((resolve, _reject) => {
|
|
399
|
-
resolveLockPromise = node_async_hooks.AsyncResource.bind(resolve);
|
|
400
|
-
});
|
|
401
|
-
if (!this.writeConnection.isBusy) {
|
|
402
|
-
this.writeConnection.isBusy = true;
|
|
403
|
-
resolveLockPromise();
|
|
404
|
-
}
|
|
405
|
-
else {
|
|
406
|
-
this.writeQueue.push(resolveLockPromise);
|
|
407
|
-
}
|
|
408
|
-
return (async () => {
|
|
409
|
-
await lockPromise;
|
|
410
|
-
try {
|
|
411
|
-
try {
|
|
412
|
-
return await fn(this.writeConnection);
|
|
413
|
-
}
|
|
414
|
-
finally {
|
|
415
|
-
const serializedUpdates = await this.writeConnection.executeRaw("SELECT powersync_update_hooks('get');", []);
|
|
416
|
-
const updates = JSON.parse(serializedUpdates[0][0]);
|
|
417
|
-
if (updates.length > 0) {
|
|
418
|
-
const event = {
|
|
419
|
-
tables: updates,
|
|
420
|
-
groupedUpdates: {},
|
|
421
|
-
rawUpdates: []
|
|
422
|
-
};
|
|
423
|
-
this.iterateListeners((cb) => cb.tablesUpdated?.(event));
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
finally {
|
|
428
|
-
const next = this.writeQueue.shift();
|
|
429
|
-
if (next) {
|
|
430
|
-
next();
|
|
431
|
-
}
|
|
432
|
-
else {
|
|
433
|
-
this.writeConnection.isBusy = false;
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
})();
|
|
437
|
-
}
|
|
438
|
-
readTransaction(fn, _options) {
|
|
439
|
-
return this.readLock((ctx) => this.internalTransaction(ctx, fn));
|
|
440
|
-
}
|
|
441
|
-
writeTransaction(fn, _options) {
|
|
442
|
-
return this.writeLock((ctx) => this.internalTransaction(ctx, fn));
|
|
443
|
-
}
|
|
444
|
-
async internalTransaction(connection, fn) {
|
|
445
|
-
let finalized = false;
|
|
446
|
-
const commit = async () => {
|
|
447
|
-
if (!finalized) {
|
|
448
|
-
finalized = true;
|
|
449
|
-
await connection.execute('COMMIT');
|
|
450
|
-
}
|
|
451
|
-
return { rowsAffected: 0 };
|
|
452
|
-
};
|
|
453
|
-
const rollback = async () => {
|
|
454
|
-
if (!finalized) {
|
|
455
|
-
finalized = true;
|
|
456
|
-
await connection.execute('ROLLBACK');
|
|
457
|
-
}
|
|
458
|
-
return { rowsAffected: 0 };
|
|
459
|
-
};
|
|
460
|
-
try {
|
|
461
|
-
await connection.execute('BEGIN');
|
|
462
|
-
const result = await fn({
|
|
463
|
-
execute: (query, params) => connection.execute(query, params),
|
|
464
|
-
executeRaw: (query, params) => connection.executeRaw(query, params),
|
|
465
|
-
executeBatch: (query, params) => connection.executeBatch(query, params),
|
|
466
|
-
get: (query, params) => connection.get(query, params),
|
|
467
|
-
getAll: (query, params) => connection.getAll(query, params),
|
|
468
|
-
getOptional: (query, params) => connection.getOptional(query, params),
|
|
469
|
-
commit,
|
|
470
|
-
rollback
|
|
471
|
-
});
|
|
472
|
-
await commit();
|
|
473
|
-
return result;
|
|
474
|
-
}
|
|
475
|
-
catch (ex) {
|
|
476
|
-
try {
|
|
477
|
-
await rollback();
|
|
478
|
-
}
|
|
479
|
-
catch (ex2) {
|
|
480
|
-
// In rare cases, a rollback may fail.
|
|
481
|
-
// Safe to ignore.
|
|
482
|
-
}
|
|
483
|
-
throw ex;
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
getAll(sql, parameters) {
|
|
487
|
-
return this.readLock((ctx) => ctx.getAll(sql, parameters));
|
|
488
|
-
}
|
|
489
|
-
getOptional(sql, parameters) {
|
|
490
|
-
return this.readLock((ctx) => ctx.getOptional(sql, parameters));
|
|
491
|
-
}
|
|
492
|
-
get(sql, parameters) {
|
|
493
|
-
return this.readLock((ctx) => ctx.get(sql, parameters));
|
|
494
|
-
}
|
|
495
|
-
execute(query, params) {
|
|
496
|
-
return this.writeLock((ctx) => ctx.execute(query, params));
|
|
497
|
-
}
|
|
498
|
-
executeRaw(query, params) {
|
|
499
|
-
return this.writeLock((ctx) => ctx.executeRaw(query, params));
|
|
500
|
-
}
|
|
501
|
-
executeBatch(query, params) {
|
|
502
|
-
return this.writeTransaction((ctx) => ctx.executeBatch(query, params));
|
|
503
|
-
}
|
|
504
|
-
async refreshSchema() {
|
|
505
|
-
await this.writeConnection.refreshSchema();
|
|
506
|
-
for (const readConnection of this.readConnections) {
|
|
507
|
-
await readConnection.refreshSchema();
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
/**
|
|
513
|
-
* A PowerSync database which provides SQLite functionality
|
|
514
|
-
* which is automatically synced.
|
|
515
|
-
*
|
|
516
|
-
* @example
|
|
517
|
-
* ```typescript
|
|
518
|
-
* export const db = new PowerSyncDatabase({
|
|
519
|
-
* schema: AppSchema,
|
|
520
|
-
* database: {
|
|
521
|
-
* dbFilename: 'example.db'
|
|
522
|
-
* }
|
|
523
|
-
* });
|
|
524
|
-
* ```
|
|
525
|
-
*/
|
|
526
|
-
class PowerSyncDatabase extends common.AbstractPowerSyncDatabase {
|
|
527
|
-
constructor(options) {
|
|
528
|
-
super(options);
|
|
529
|
-
}
|
|
530
|
-
async _initialize() {
|
|
531
|
-
await this.database.initialize();
|
|
532
|
-
}
|
|
533
|
-
/**
|
|
534
|
-
* Opens a DBAdapter using better-sqlite3 as the default SQLite open factory.
|
|
535
|
-
*/
|
|
536
|
-
openDBAdapter(options) {
|
|
537
|
-
return new WorkerConnectionPool(options.database);
|
|
538
|
-
}
|
|
539
|
-
generateBucketStorageAdapter() {
|
|
540
|
-
return new common.SqliteBucketStorage(this.database, this.logger);
|
|
541
|
-
}
|
|
542
|
-
connect(connector, options) {
|
|
543
|
-
return super.connect(connector, options);
|
|
544
|
-
}
|
|
545
|
-
generateSyncStreamImplementation(connector, options) {
|
|
546
|
-
const logger = this.logger;
|
|
547
|
-
const remote = new NodeRemote(connector, logger, {
|
|
548
|
-
dispatcher: options.dispatcher,
|
|
549
|
-
...this.options.remoteOptions
|
|
550
|
-
});
|
|
551
|
-
return new NodeStreamingSyncImplementation({
|
|
552
|
-
adapter: this.bucketStorageAdapter,
|
|
553
|
-
remote,
|
|
554
|
-
uploadCrud: async () => {
|
|
555
|
-
await this.waitForReady();
|
|
556
|
-
await connector.uploadData(this);
|
|
557
|
-
},
|
|
558
|
-
...options,
|
|
559
|
-
identifier: this.database.name,
|
|
560
|
-
logger
|
|
561
|
-
});
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
/**
|
|
566
|
-
* NodeFileSystemAdapter implements LocalStorageAdapter using Node.js filesystem.
|
|
567
|
-
* Suitable for Node.js environments and Electron applications.
|
|
568
|
-
*/
|
|
569
|
-
class NodeFileSystemAdapter {
|
|
570
|
-
storageDirectory;
|
|
571
|
-
constructor(storageDirectory = './user_data') {
|
|
572
|
-
this.storageDirectory = storageDirectory;
|
|
573
|
-
}
|
|
574
|
-
async initialize() {
|
|
575
|
-
const dir = path__namespace$1.resolve(this.storageDirectory);
|
|
576
|
-
await fs$1.promises.mkdir(dir, { recursive: true });
|
|
577
|
-
}
|
|
578
|
-
async clear() {
|
|
579
|
-
const dir = path__namespace$1.resolve(this.storageDirectory);
|
|
580
|
-
await fs$1.promises.rmdir(dir, { recursive: true });
|
|
581
|
-
}
|
|
582
|
-
getLocalUri(filename) {
|
|
583
|
-
return path__namespace$1.join(path__namespace$1.resolve(this.storageDirectory), filename);
|
|
584
|
-
}
|
|
585
|
-
async uploadFile(filePath, data, options) {
|
|
586
|
-
const buffer = Buffer.from(data);
|
|
587
|
-
await fs$1.promises.writeFile(filePath, buffer, {
|
|
588
|
-
encoding: options?.encoding
|
|
589
|
-
});
|
|
590
|
-
}
|
|
591
|
-
async downloadFile(filePath) {
|
|
592
|
-
const data = await fs$1.promises.readFile(filePath);
|
|
593
|
-
return new Blob([new Uint8Array(data)]);
|
|
594
|
-
}
|
|
595
|
-
async saveFile(filePath, data, options) {
|
|
596
|
-
let buffer;
|
|
597
|
-
if (typeof data === 'string') {
|
|
598
|
-
buffer = Buffer.from(data, options?.encoding ?? common.EncodingType.Base64);
|
|
599
|
-
}
|
|
600
|
-
else {
|
|
601
|
-
buffer = Buffer.from(data);
|
|
602
|
-
}
|
|
603
|
-
await fs$1.promises.writeFile(filePath, buffer);
|
|
604
|
-
return buffer.length;
|
|
605
|
-
}
|
|
606
|
-
async readFile(filePath, options) {
|
|
607
|
-
const data = await fs$1.promises.readFile(filePath);
|
|
608
|
-
if (options?.encoding === common.EncodingType.Base64) {
|
|
609
|
-
return Buffer.from(data.toString(), 'base64').buffer;
|
|
610
|
-
}
|
|
611
|
-
else {
|
|
612
|
-
return data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
|
|
613
|
-
}
|
|
614
|
-
}
|
|
615
|
-
async deleteFile(path, options) {
|
|
616
|
-
await fs$1.promises.unlink(path).catch((err) => {
|
|
617
|
-
if (err.code !== 'ENOENT') {
|
|
618
|
-
throw err;
|
|
619
|
-
}
|
|
620
|
-
});
|
|
621
|
-
}
|
|
622
|
-
async fileExists(filePath) {
|
|
623
|
-
try {
|
|
624
|
-
await fs$1.promises.access(filePath);
|
|
625
|
-
return true;
|
|
626
|
-
}
|
|
627
|
-
catch {
|
|
628
|
-
return false;
|
|
629
|
-
}
|
|
630
|
-
}
|
|
631
|
-
async makeDir(path) {
|
|
632
|
-
await fs$1.promises.mkdir(path, { recursive: true });
|
|
633
|
-
}
|
|
634
|
-
async rmDir(path) {
|
|
635
|
-
await fs$1.promises.rmdir(path, { recursive: true });
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
exports.NodeFileSystemAdapter = NodeFileSystemAdapter;
|
|
640
|
-
exports.PowerSyncDatabase = PowerSyncDatabase;
|
|
641
|
-
Object.keys(common).forEach(function (k) {
|
|
642
|
-
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|
|
643
|
-
enumerable: true,
|
|
644
|
-
get: function () { return common[k]; }
|
|
645
|
-
});
|
|
646
|
-
});
|
|
647
|
-
//# sourceMappingURL=bundle.cjs.map
|