@powersync/common 1.54.0 → 1.56.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/dist/bundle.cjs +384 -434
- package/dist/bundle.cjs.map +1 -1
- package/dist/bundle.mjs +384 -434
- package/dist/bundle.mjs.map +1 -1
- package/dist/bundle.node.cjs +384 -280
- package/dist/bundle.node.cjs.map +1 -1
- package/dist/bundle.node.mjs +384 -280
- package/dist/bundle.node.mjs.map +1 -1
- package/dist/index.d.cts +54 -52
- package/lib/attachments/AttachmentQueue.d.ts +52 -44
- package/lib/attachments/AttachmentQueue.js.map +1 -1
- package/lib/client/AbstractPowerSyncDatabase.d.ts +0 -2
- package/lib/client/AbstractPowerSyncDatabase.js +19 -27
- package/lib/client/AbstractPowerSyncDatabase.js.map +1 -1
- package/lib/client/sync/stream/AbstractRemote.js +44 -26
- package/lib/client/sync/stream/AbstractRemote.js.map +1 -1
- package/lib/db/crud/SyncStatus.d.ts +1 -5
- package/lib/db/crud/SyncStatus.js +9 -6
- package/lib/db/crud/SyncStatus.js.map +1 -1
- package/lib/utils/async.d.ts +26 -0
- package/lib/utils/async.js +114 -27
- package/lib/utils/async.js.map +1 -1
- package/lib/utils/compatibility.d.ts +8 -0
- package/lib/utils/compatibility.js +9 -0
- package/lib/utils/compatibility.js.map +1 -0
- package/package.json +1 -2
- package/src/attachments/AttachmentQueue.ts +54 -44
- package/src/client/AbstractPowerSyncDatabase.ts +21 -30
- package/src/client/sync/stream/AbstractRemote.ts +46 -33
- package/src/db/crud/SyncStatus.ts +10 -7
- package/src/utils/async.ts +136 -28
- package/src/utils/compatibility.ts +9 -0
package/dist/bundle.cjs
CHANGED
|
@@ -1451,165 +1451,10 @@ exports.EncodingType = void 0;
|
|
|
1451
1451
|
EncodingType["Base64"] = "base64";
|
|
1452
1452
|
})(exports.EncodingType || (exports.EncodingType = {}));
|
|
1453
1453
|
|
|
1454
|
-
const symbolAsyncIterator = Symbol.asyncIterator ?? Symbol.for('Symbol.asyncIterator');
|
|
1455
|
-
|
|
1456
1454
|
function getDefaultExportFromCjs (x) {
|
|
1457
1455
|
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
|
1458
1456
|
}
|
|
1459
1457
|
|
|
1460
|
-
var dom = {};
|
|
1461
|
-
|
|
1462
|
-
var eventIterator = {};
|
|
1463
|
-
|
|
1464
|
-
var hasRequiredEventIterator;
|
|
1465
|
-
|
|
1466
|
-
function requireEventIterator () {
|
|
1467
|
-
if (hasRequiredEventIterator) return eventIterator;
|
|
1468
|
-
hasRequiredEventIterator = 1;
|
|
1469
|
-
Object.defineProperty(eventIterator, "__esModule", { value: true });
|
|
1470
|
-
class EventQueue {
|
|
1471
|
-
constructor() {
|
|
1472
|
-
this.pullQueue = [];
|
|
1473
|
-
this.pushQueue = [];
|
|
1474
|
-
this.eventHandlers = {};
|
|
1475
|
-
this.isPaused = false;
|
|
1476
|
-
this.isStopped = false;
|
|
1477
|
-
}
|
|
1478
|
-
push(value) {
|
|
1479
|
-
if (this.isStopped)
|
|
1480
|
-
return;
|
|
1481
|
-
const resolution = { value, done: false };
|
|
1482
|
-
if (this.pullQueue.length) {
|
|
1483
|
-
const placeholder = this.pullQueue.shift();
|
|
1484
|
-
if (placeholder)
|
|
1485
|
-
placeholder.resolve(resolution);
|
|
1486
|
-
}
|
|
1487
|
-
else {
|
|
1488
|
-
this.pushQueue.push(Promise.resolve(resolution));
|
|
1489
|
-
if (this.highWaterMark !== undefined &&
|
|
1490
|
-
this.pushQueue.length >= this.highWaterMark &&
|
|
1491
|
-
!this.isPaused) {
|
|
1492
|
-
this.isPaused = true;
|
|
1493
|
-
if (this.eventHandlers.highWater) {
|
|
1494
|
-
this.eventHandlers.highWater();
|
|
1495
|
-
}
|
|
1496
|
-
else if (console) {
|
|
1497
|
-
console.warn(`EventIterator queue reached ${this.pushQueue.length} items`);
|
|
1498
|
-
}
|
|
1499
|
-
}
|
|
1500
|
-
}
|
|
1501
|
-
}
|
|
1502
|
-
stop() {
|
|
1503
|
-
if (this.isStopped)
|
|
1504
|
-
return;
|
|
1505
|
-
this.isStopped = true;
|
|
1506
|
-
this.remove();
|
|
1507
|
-
for (const placeholder of this.pullQueue) {
|
|
1508
|
-
placeholder.resolve({ value: undefined, done: true });
|
|
1509
|
-
}
|
|
1510
|
-
this.pullQueue.length = 0;
|
|
1511
|
-
}
|
|
1512
|
-
fail(error) {
|
|
1513
|
-
if (this.isStopped)
|
|
1514
|
-
return;
|
|
1515
|
-
this.isStopped = true;
|
|
1516
|
-
this.remove();
|
|
1517
|
-
if (this.pullQueue.length) {
|
|
1518
|
-
for (const placeholder of this.pullQueue) {
|
|
1519
|
-
placeholder.reject(error);
|
|
1520
|
-
}
|
|
1521
|
-
this.pullQueue.length = 0;
|
|
1522
|
-
}
|
|
1523
|
-
else {
|
|
1524
|
-
const rejection = Promise.reject(error);
|
|
1525
|
-
/* Attach error handler to avoid leaking an unhandled promise rejection. */
|
|
1526
|
-
rejection.catch(() => { });
|
|
1527
|
-
this.pushQueue.push(rejection);
|
|
1528
|
-
}
|
|
1529
|
-
}
|
|
1530
|
-
remove() {
|
|
1531
|
-
Promise.resolve().then(() => {
|
|
1532
|
-
if (this.removeCallback)
|
|
1533
|
-
this.removeCallback();
|
|
1534
|
-
});
|
|
1535
|
-
}
|
|
1536
|
-
[symbolAsyncIterator]() {
|
|
1537
|
-
return {
|
|
1538
|
-
next: (value) => {
|
|
1539
|
-
const result = this.pushQueue.shift();
|
|
1540
|
-
if (result) {
|
|
1541
|
-
if (this.lowWaterMark !== undefined &&
|
|
1542
|
-
this.pushQueue.length <= this.lowWaterMark &&
|
|
1543
|
-
this.isPaused) {
|
|
1544
|
-
this.isPaused = false;
|
|
1545
|
-
if (this.eventHandlers.lowWater) {
|
|
1546
|
-
this.eventHandlers.lowWater();
|
|
1547
|
-
}
|
|
1548
|
-
}
|
|
1549
|
-
return result;
|
|
1550
|
-
}
|
|
1551
|
-
else if (this.isStopped) {
|
|
1552
|
-
return Promise.resolve({ value: undefined, done: true });
|
|
1553
|
-
}
|
|
1554
|
-
else {
|
|
1555
|
-
return new Promise((resolve, reject) => {
|
|
1556
|
-
this.pullQueue.push({ resolve, reject });
|
|
1557
|
-
});
|
|
1558
|
-
}
|
|
1559
|
-
},
|
|
1560
|
-
return: () => {
|
|
1561
|
-
this.isStopped = true;
|
|
1562
|
-
this.pushQueue.length = 0;
|
|
1563
|
-
this.remove();
|
|
1564
|
-
return Promise.resolve({ value: undefined, done: true });
|
|
1565
|
-
},
|
|
1566
|
-
};
|
|
1567
|
-
}
|
|
1568
|
-
}
|
|
1569
|
-
class EventIterator {
|
|
1570
|
-
constructor(listen, { highWaterMark = 100, lowWaterMark = 1 } = {}) {
|
|
1571
|
-
const queue = new EventQueue();
|
|
1572
|
-
queue.highWaterMark = highWaterMark;
|
|
1573
|
-
queue.lowWaterMark = lowWaterMark;
|
|
1574
|
-
queue.removeCallback =
|
|
1575
|
-
listen({
|
|
1576
|
-
push: value => queue.push(value),
|
|
1577
|
-
stop: () => queue.stop(),
|
|
1578
|
-
fail: error => queue.fail(error),
|
|
1579
|
-
on: (event, fn) => {
|
|
1580
|
-
queue.eventHandlers[event] = fn;
|
|
1581
|
-
},
|
|
1582
|
-
}) || (() => { });
|
|
1583
|
-
this[symbolAsyncIterator] = () => queue[symbolAsyncIterator]();
|
|
1584
|
-
Object.freeze(this);
|
|
1585
|
-
}
|
|
1586
|
-
}
|
|
1587
|
-
eventIterator.EventIterator = EventIterator;
|
|
1588
|
-
eventIterator.default = EventIterator;
|
|
1589
|
-
return eventIterator;
|
|
1590
|
-
}
|
|
1591
|
-
|
|
1592
|
-
var hasRequiredDom;
|
|
1593
|
-
|
|
1594
|
-
function requireDom () {
|
|
1595
|
-
if (hasRequiredDom) return dom;
|
|
1596
|
-
hasRequiredDom = 1;
|
|
1597
|
-
Object.defineProperty(dom, "__esModule", { value: true });
|
|
1598
|
-
const event_iterator_1 = requireEventIterator();
|
|
1599
|
-
dom.EventIterator = event_iterator_1.EventIterator;
|
|
1600
|
-
function subscribe(event, options, evOptions) {
|
|
1601
|
-
return new event_iterator_1.EventIterator(({ push }) => {
|
|
1602
|
-
this.addEventListener(event, push, options);
|
|
1603
|
-
return () => this.removeEventListener(event, push, options);
|
|
1604
|
-
}, evOptions);
|
|
1605
|
-
}
|
|
1606
|
-
dom.subscribe = subscribe;
|
|
1607
|
-
dom.default = event_iterator_1.EventIterator;
|
|
1608
|
-
return dom;
|
|
1609
|
-
}
|
|
1610
|
-
|
|
1611
|
-
var domExports = requireDom();
|
|
1612
|
-
|
|
1613
1458
|
var logger$1 = {exports: {}};
|
|
1614
1459
|
|
|
1615
1460
|
/*!
|
|
@@ -2292,11 +2137,7 @@ class SyncStatus {
|
|
|
2292
2137
|
*/
|
|
2293
2138
|
const replacer = (_, value) => {
|
|
2294
2139
|
if (value instanceof Error) {
|
|
2295
|
-
return
|
|
2296
|
-
name: value.name,
|
|
2297
|
-
message: value.message,
|
|
2298
|
-
stack: value.stack
|
|
2299
|
-
};
|
|
2140
|
+
return this.serializeError(value);
|
|
2300
2141
|
}
|
|
2301
2142
|
return value;
|
|
2302
2143
|
};
|
|
@@ -2339,11 +2180,18 @@ class SyncStatus {
|
|
|
2339
2180
|
if (typeof error == 'undefined') {
|
|
2340
2181
|
return undefined;
|
|
2341
2182
|
}
|
|
2342
|
-
|
|
2183
|
+
const serialized = {
|
|
2343
2184
|
name: error.name,
|
|
2344
2185
|
message: error.message,
|
|
2345
2186
|
stack: error.stack
|
|
2346
2187
|
};
|
|
2188
|
+
// `Error.cause` can be any value (the spec types it as unknown). Preserve it
|
|
2189
|
+
// so consumers reading uploadError/downloadError keep the failure context.
|
|
2190
|
+
// Recurse for Error causes so the whole chain is flattened the same way.
|
|
2191
|
+
if (typeof error.cause != 'undefined') {
|
|
2192
|
+
serialized.cause = error.cause instanceof Error ? this.serializeError(error.cause) : error.cause;
|
|
2193
|
+
}
|
|
2194
|
+
return serialized;
|
|
2347
2195
|
}
|
|
2348
2196
|
static comparePriorities(a, b) {
|
|
2349
2197
|
return b.priority - a.priority; // Reverse because higher priorities have lower numbers
|
|
@@ -2493,6 +2341,210 @@ class ControlledExecutor {
|
|
|
2493
2341
|
}
|
|
2494
2342
|
}
|
|
2495
2343
|
|
|
2344
|
+
/**
|
|
2345
|
+
* Some JavaScript engines, in particular older versions of React Native, don't support Symbol.asyncIterator.
|
|
2346
|
+
*
|
|
2347
|
+
* For those, users relying on async generators typically lower them with a transpiler and [this polyfill](https://github.com/Azure/azure-sdk-for-js/blob/%40azure/core-asynciterator-polyfill_1.0.2/sdk/core/core-asynciterator-polyfill/src/index.ts#L4-L6).
|
|
2348
|
+
* This definition is compatible with that polyfill, so transpiled apps can use async iterables created by the PowerSync
|
|
2349
|
+
* SDK.
|
|
2350
|
+
*/
|
|
2351
|
+
const symbolAsyncIterator = Symbol.asyncIterator ?? Symbol.for('Symbol.asyncIterator');
|
|
2352
|
+
|
|
2353
|
+
const doneResult = { done: true, value: undefined };
|
|
2354
|
+
function valueResult(value) {
|
|
2355
|
+
return { done: false, value };
|
|
2356
|
+
}
|
|
2357
|
+
/**
|
|
2358
|
+
* Expands a source async iterator by allowing to inject events asynchronously.
|
|
2359
|
+
*
|
|
2360
|
+
* The resulting iterator will emit all events from its source. Additionally though, events can be injected. These
|
|
2361
|
+
* events are dropped once the main iterator completes, but are otherwise forwarded.
|
|
2362
|
+
*
|
|
2363
|
+
* The iterator completes when its source completes, and it supports backpressure by only calling `next()` on the source
|
|
2364
|
+
* in response to a `next()` call from downstream if no pending injected events can be dispatched.
|
|
2365
|
+
*/
|
|
2366
|
+
function injectable(source) {
|
|
2367
|
+
let sourceIsDone = false;
|
|
2368
|
+
let waiter = undefined; // An active, waiting next() call.
|
|
2369
|
+
// A pending upstream event that couldn't be dispatched because inject() has been called before it was resolved.
|
|
2370
|
+
let pendingSourceEvent = null;
|
|
2371
|
+
let sourceFetchInFlight = false;
|
|
2372
|
+
let pendingInjectedEvents = [];
|
|
2373
|
+
const consumeWaiter = () => {
|
|
2374
|
+
const pending = waiter;
|
|
2375
|
+
waiter = undefined;
|
|
2376
|
+
return pending;
|
|
2377
|
+
};
|
|
2378
|
+
const fetchFromSource = () => {
|
|
2379
|
+
const resolveWaiter = (propagate) => {
|
|
2380
|
+
sourceFetchInFlight = false;
|
|
2381
|
+
const active = consumeWaiter();
|
|
2382
|
+
if (active) {
|
|
2383
|
+
propagate(active);
|
|
2384
|
+
}
|
|
2385
|
+
else {
|
|
2386
|
+
pendingSourceEvent = propagate;
|
|
2387
|
+
}
|
|
2388
|
+
};
|
|
2389
|
+
sourceFetchInFlight = true;
|
|
2390
|
+
const nextFromSource = source.next();
|
|
2391
|
+
nextFromSource.then((value) => {
|
|
2392
|
+
sourceIsDone = value.done == true;
|
|
2393
|
+
resolveWaiter((w) => w.resolve(value));
|
|
2394
|
+
}, (error) => {
|
|
2395
|
+
resolveWaiter((w) => w.reject(error));
|
|
2396
|
+
});
|
|
2397
|
+
};
|
|
2398
|
+
return {
|
|
2399
|
+
next: () => {
|
|
2400
|
+
return new Promise((resolve, reject) => {
|
|
2401
|
+
// First priority: Dispatch ready upstream events.
|
|
2402
|
+
if (sourceIsDone) {
|
|
2403
|
+
return resolve(doneResult);
|
|
2404
|
+
}
|
|
2405
|
+
if (pendingSourceEvent) {
|
|
2406
|
+
pendingSourceEvent({ resolve, reject });
|
|
2407
|
+
pendingSourceEvent = null;
|
|
2408
|
+
return;
|
|
2409
|
+
}
|
|
2410
|
+
// Second priority: Dispatch injected events
|
|
2411
|
+
if (pendingInjectedEvents.length) {
|
|
2412
|
+
return resolve(valueResult(pendingInjectedEvents.shift()));
|
|
2413
|
+
}
|
|
2414
|
+
// Nothing pending? Fetch from source
|
|
2415
|
+
waiter = { resolve, reject };
|
|
2416
|
+
if (!sourceFetchInFlight) {
|
|
2417
|
+
fetchFromSource();
|
|
2418
|
+
}
|
|
2419
|
+
});
|
|
2420
|
+
},
|
|
2421
|
+
inject: (event) => {
|
|
2422
|
+
const pending = consumeWaiter();
|
|
2423
|
+
if (pending != null) {
|
|
2424
|
+
pending.resolve(valueResult(event));
|
|
2425
|
+
}
|
|
2426
|
+
else {
|
|
2427
|
+
pendingInjectedEvents.push(event);
|
|
2428
|
+
}
|
|
2429
|
+
}
|
|
2430
|
+
};
|
|
2431
|
+
}
|
|
2432
|
+
/**
|
|
2433
|
+
* Splits a byte stream at line endings, emitting each line as a string.
|
|
2434
|
+
*/
|
|
2435
|
+
function extractJsonLines(source, decoder) {
|
|
2436
|
+
let buffer = '';
|
|
2437
|
+
const pendingLines = [];
|
|
2438
|
+
let isFinalEvent = false;
|
|
2439
|
+
return {
|
|
2440
|
+
next: async () => {
|
|
2441
|
+
while (true) {
|
|
2442
|
+
if (isFinalEvent) {
|
|
2443
|
+
return doneResult;
|
|
2444
|
+
}
|
|
2445
|
+
{
|
|
2446
|
+
const first = pendingLines.shift();
|
|
2447
|
+
if (first) {
|
|
2448
|
+
return { done: false, value: first };
|
|
2449
|
+
}
|
|
2450
|
+
}
|
|
2451
|
+
const { done, value } = await source.next();
|
|
2452
|
+
if (done) {
|
|
2453
|
+
const remaining = buffer.trim();
|
|
2454
|
+
if (remaining.length != 0) {
|
|
2455
|
+
isFinalEvent = true;
|
|
2456
|
+
return { done: false, value: remaining };
|
|
2457
|
+
}
|
|
2458
|
+
return doneResult;
|
|
2459
|
+
}
|
|
2460
|
+
const data = decoder.decode(value, { stream: true });
|
|
2461
|
+
buffer += data;
|
|
2462
|
+
const lines = buffer.split('\n');
|
|
2463
|
+
for (let i = 0; i < lines.length - 1; i++) {
|
|
2464
|
+
const l = lines[i].trim();
|
|
2465
|
+
if (l.length > 0) {
|
|
2466
|
+
pendingLines.push(l);
|
|
2467
|
+
}
|
|
2468
|
+
}
|
|
2469
|
+
buffer = lines[lines.length - 1];
|
|
2470
|
+
}
|
|
2471
|
+
}
|
|
2472
|
+
};
|
|
2473
|
+
}
|
|
2474
|
+
/**
|
|
2475
|
+
* Splits a concatenated stream of BSON objects by emitting individual objects.
|
|
2476
|
+
*/
|
|
2477
|
+
function extractBsonObjects(source) {
|
|
2478
|
+
// Fully read but not emitted yet.
|
|
2479
|
+
const completedObjects = [];
|
|
2480
|
+
// Whether source has returned { done: true }. We do the same once completed objects have been emitted.
|
|
2481
|
+
let isDone = false;
|
|
2482
|
+
const lengthBuffer = new DataView(new ArrayBuffer(4));
|
|
2483
|
+
let objectBody = null;
|
|
2484
|
+
// If we're parsing the length field, a number between 1 and 4 (inclusive) describing remaining bytes in the header.
|
|
2485
|
+
// If we're consuming a document, the bytes remaining.
|
|
2486
|
+
let remainingLength = 4;
|
|
2487
|
+
return {
|
|
2488
|
+
async next() {
|
|
2489
|
+
while (true) {
|
|
2490
|
+
// Before fetching new data from upstream, return completed objects.
|
|
2491
|
+
if (completedObjects.length) {
|
|
2492
|
+
return valueResult(completedObjects.shift());
|
|
2493
|
+
}
|
|
2494
|
+
if (isDone) {
|
|
2495
|
+
return doneResult;
|
|
2496
|
+
}
|
|
2497
|
+
const upstreamEvent = await source.next();
|
|
2498
|
+
if (upstreamEvent.done) {
|
|
2499
|
+
isDone = true;
|
|
2500
|
+
if (objectBody || remainingLength != 4) {
|
|
2501
|
+
throw new Error('illegal end of stream in BSON object');
|
|
2502
|
+
}
|
|
2503
|
+
return doneResult;
|
|
2504
|
+
}
|
|
2505
|
+
const chunk = upstreamEvent.value;
|
|
2506
|
+
for (let i = 0; i < chunk.length;) {
|
|
2507
|
+
const availableInData = chunk.length - i;
|
|
2508
|
+
if (objectBody) {
|
|
2509
|
+
// We're in the middle of reading a BSON document.
|
|
2510
|
+
const bytesToRead = Math.min(availableInData, remainingLength);
|
|
2511
|
+
const copySource = new Uint8Array(chunk.buffer, chunk.byteOffset + i, bytesToRead);
|
|
2512
|
+
objectBody.set(copySource, objectBody.length - remainingLength);
|
|
2513
|
+
i += bytesToRead;
|
|
2514
|
+
remainingLength -= bytesToRead;
|
|
2515
|
+
if (remainingLength == 0) {
|
|
2516
|
+
completedObjects.push(objectBody);
|
|
2517
|
+
// Prepare to read another document, starting with its length
|
|
2518
|
+
objectBody = null;
|
|
2519
|
+
remainingLength = 4;
|
|
2520
|
+
}
|
|
2521
|
+
}
|
|
2522
|
+
else {
|
|
2523
|
+
// Copy up to 4 bytes into lengthBuffer, depending on how many we still need.
|
|
2524
|
+
const bytesToRead = Math.min(availableInData, remainingLength);
|
|
2525
|
+
for (let j = 0; j < bytesToRead; j++) {
|
|
2526
|
+
lengthBuffer.setUint8(4 - remainingLength + j, chunk[i + j]);
|
|
2527
|
+
}
|
|
2528
|
+
i += bytesToRead;
|
|
2529
|
+
remainingLength -= bytesToRead;
|
|
2530
|
+
if (remainingLength == 0) {
|
|
2531
|
+
// Transition from reading length header to reading document. Subtracting 4 because the length of the
|
|
2532
|
+
// header is included in length.
|
|
2533
|
+
const length = lengthBuffer.getInt32(0, true /* little endian */);
|
|
2534
|
+
remainingLength = length - 4;
|
|
2535
|
+
if (remainingLength < 1) {
|
|
2536
|
+
throw new Error(`invalid length for bson: ${length}`);
|
|
2537
|
+
}
|
|
2538
|
+
objectBody = new Uint8Array(length);
|
|
2539
|
+
new DataView(objectBody.buffer).setInt32(0, length, true);
|
|
2540
|
+
}
|
|
2541
|
+
}
|
|
2542
|
+
}
|
|
2543
|
+
}
|
|
2544
|
+
}
|
|
2545
|
+
};
|
|
2546
|
+
}
|
|
2547
|
+
|
|
2496
2548
|
/**
|
|
2497
2549
|
* Throttle a function to be called at most once every "wait" milliseconds,
|
|
2498
2550
|
* on the trailing edge.
|
|
@@ -2512,45 +2564,128 @@ function throttleTrailing(func, wait) {
|
|
|
2512
2564
|
};
|
|
2513
2565
|
}
|
|
2514
2566
|
function asyncNotifier() {
|
|
2515
|
-
|
|
2516
|
-
let hasPendingNotification = false;
|
|
2567
|
+
const queue = new EventQueue();
|
|
2517
2568
|
return {
|
|
2518
2569
|
notify() {
|
|
2519
|
-
if (
|
|
2520
|
-
|
|
2521
|
-
|
|
2570
|
+
if (queue.countOutstandingEvents > 0) ;
|
|
2571
|
+
else {
|
|
2572
|
+
queue.notify();
|
|
2573
|
+
}
|
|
2574
|
+
},
|
|
2575
|
+
waitForNotification(signal) {
|
|
2576
|
+
return queue.waitForEvent(signal);
|
|
2577
|
+
}
|
|
2578
|
+
};
|
|
2579
|
+
}
|
|
2580
|
+
class EventQueue {
|
|
2581
|
+
options;
|
|
2582
|
+
waitingConsumer;
|
|
2583
|
+
outstandingEvents;
|
|
2584
|
+
constructor(options = {}) {
|
|
2585
|
+
this.options = options;
|
|
2586
|
+
this.outstandingEvents = [];
|
|
2587
|
+
}
|
|
2588
|
+
/**
|
|
2589
|
+
* The amount of buffered events not yet dispatched to listeners.
|
|
2590
|
+
*/
|
|
2591
|
+
get countOutstandingEvents() {
|
|
2592
|
+
return this.outstandingEvents.length;
|
|
2593
|
+
}
|
|
2594
|
+
notifyInner(dispatch) {
|
|
2595
|
+
const existing = this.waitingConsumer;
|
|
2596
|
+
this.waitingConsumer = undefined;
|
|
2597
|
+
const dispatchAndNotifyListeners = (waiter) => {
|
|
2598
|
+
dispatch(waiter);
|
|
2599
|
+
this.options.eventDelivered?.();
|
|
2600
|
+
};
|
|
2601
|
+
if (existing) {
|
|
2602
|
+
dispatchAndNotifyListeners(existing);
|
|
2603
|
+
}
|
|
2604
|
+
else {
|
|
2605
|
+
this.outstandingEvents.push(dispatchAndNotifyListeners);
|
|
2606
|
+
}
|
|
2607
|
+
}
|
|
2608
|
+
notify(value) {
|
|
2609
|
+
this.notifyInner((l) => l.resolve(value));
|
|
2610
|
+
}
|
|
2611
|
+
notifyError(error) {
|
|
2612
|
+
this.notifyInner((l) => l.reject(error));
|
|
2613
|
+
}
|
|
2614
|
+
waitForEvent(signal) {
|
|
2615
|
+
return new Promise((resolve, reject) => {
|
|
2616
|
+
if (this.waitingConsumer != null) {
|
|
2617
|
+
throw new Error('Illegal call to waitForEvent, already has a waiter.');
|
|
2618
|
+
}
|
|
2619
|
+
const complete = () => {
|
|
2620
|
+
signal?.removeEventListener('abort', onAbort);
|
|
2621
|
+
};
|
|
2622
|
+
const onAbort = () => {
|
|
2623
|
+
complete();
|
|
2624
|
+
this.waitingConsumer = undefined;
|
|
2625
|
+
resolve(undefined);
|
|
2626
|
+
};
|
|
2627
|
+
const waiter = {
|
|
2628
|
+
resolve: (value) => {
|
|
2629
|
+
complete();
|
|
2630
|
+
resolve(value);
|
|
2631
|
+
},
|
|
2632
|
+
reject: (error) => {
|
|
2633
|
+
complete();
|
|
2634
|
+
reject(error);
|
|
2635
|
+
}
|
|
2636
|
+
};
|
|
2637
|
+
if (signal.aborted) {
|
|
2638
|
+
resolve(undefined);
|
|
2639
|
+
}
|
|
2640
|
+
else if (this.countOutstandingEvents > 0) {
|
|
2641
|
+
const [event] = this.outstandingEvents.splice(0, 1);
|
|
2642
|
+
event(waiter);
|
|
2522
2643
|
}
|
|
2523
2644
|
else {
|
|
2524
|
-
|
|
2645
|
+
this.waitingConsumer = waiter;
|
|
2646
|
+
signal.addEventListener('abort', onAbort);
|
|
2525
2647
|
}
|
|
2526
|
-
}
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2648
|
+
});
|
|
2649
|
+
}
|
|
2650
|
+
/**
|
|
2651
|
+
* Creates an async iterable backed by event queues.
|
|
2652
|
+
*
|
|
2653
|
+
* @param run A function invoked for every new listener. It receives a queue backing the async iterator.
|
|
2654
|
+
* @param abort An additional abort signal. The `run` callback will also be aborted when `AsyncIterator.return` is
|
|
2655
|
+
* called.
|
|
2656
|
+
* @returns An object conforming to the async iterable protocol.
|
|
2657
|
+
*/
|
|
2658
|
+
static queueBasedAsyncIterable(run, abort) {
|
|
2659
|
+
return {
|
|
2660
|
+
[symbolAsyncIterator]: () => {
|
|
2661
|
+
const queue = new EventQueue();
|
|
2662
|
+
const controller = new AbortController();
|
|
2663
|
+
function dispose() {
|
|
2664
|
+
controller.abort();
|
|
2665
|
+
abort?.removeEventListener('abort', dispose);
|
|
2538
2666
|
}
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
resolve();
|
|
2667
|
+
if (abort) {
|
|
2668
|
+
if (abort.aborted) {
|
|
2669
|
+
controller.abort();
|
|
2543
2670
|
}
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
resolve();
|
|
2671
|
+
else {
|
|
2672
|
+
abort.addEventListener('abort', dispose);
|
|
2547
2673
|
}
|
|
2548
|
-
waitingConsumer = complete;
|
|
2549
|
-
signal.addEventListener('abort', onAbort);
|
|
2550
2674
|
}
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2675
|
+
run(queue, controller.signal);
|
|
2676
|
+
return {
|
|
2677
|
+
async next() {
|
|
2678
|
+
const event = await queue.waitForEvent(controller.signal);
|
|
2679
|
+
return event == null ? doneResult : valueResult(event);
|
|
2680
|
+
},
|
|
2681
|
+
async return() {
|
|
2682
|
+
dispose();
|
|
2683
|
+
return doneResult;
|
|
2684
|
+
}
|
|
2685
|
+
};
|
|
2686
|
+
}
|
|
2687
|
+
};
|
|
2688
|
+
}
|
|
2554
2689
|
}
|
|
2555
2690
|
|
|
2556
2691
|
/**
|
|
@@ -10781,7 +10916,7 @@ function requireDist () {
|
|
|
10781
10916
|
|
|
10782
10917
|
var distExports = requireDist();
|
|
10783
10918
|
|
|
10784
|
-
var version = "1.
|
|
10919
|
+
var version = "1.56.0";
|
|
10785
10920
|
var PACKAGE = {
|
|
10786
10921
|
version: version};
|
|
10787
10922
|
|
|
@@ -10948,201 +11083,6 @@ class WebsocketClientTransport {
|
|
|
10948
11083
|
}
|
|
10949
11084
|
}
|
|
10950
11085
|
|
|
10951
|
-
const doneResult = { done: true, value: undefined };
|
|
10952
|
-
function valueResult(value) {
|
|
10953
|
-
return { done: false, value };
|
|
10954
|
-
}
|
|
10955
|
-
/**
|
|
10956
|
-
* Expands a source async iterator by allowing to inject events asynchronously.
|
|
10957
|
-
*
|
|
10958
|
-
* The resulting iterator will emit all events from its source. Additionally though, events can be injected. These
|
|
10959
|
-
* events are dropped once the main iterator completes, but are otherwise forwarded.
|
|
10960
|
-
*
|
|
10961
|
-
* The iterator completes when its source completes, and it supports backpressure by only calling `next()` on the source
|
|
10962
|
-
* in response to a `next()` call from downstream if no pending injected events can be dispatched.
|
|
10963
|
-
*/
|
|
10964
|
-
function injectable(source) {
|
|
10965
|
-
let sourceIsDone = false;
|
|
10966
|
-
let waiter = undefined; // An active, waiting next() call.
|
|
10967
|
-
// A pending upstream event that couldn't be dispatched because inject() has been called before it was resolved.
|
|
10968
|
-
let pendingSourceEvent = null;
|
|
10969
|
-
let sourceFetchInFlight = false;
|
|
10970
|
-
let pendingInjectedEvents = [];
|
|
10971
|
-
const consumeWaiter = () => {
|
|
10972
|
-
const pending = waiter;
|
|
10973
|
-
waiter = undefined;
|
|
10974
|
-
return pending;
|
|
10975
|
-
};
|
|
10976
|
-
const fetchFromSource = () => {
|
|
10977
|
-
const resolveWaiter = (propagate) => {
|
|
10978
|
-
sourceFetchInFlight = false;
|
|
10979
|
-
const active = consumeWaiter();
|
|
10980
|
-
if (active) {
|
|
10981
|
-
propagate(active);
|
|
10982
|
-
}
|
|
10983
|
-
else {
|
|
10984
|
-
pendingSourceEvent = propagate;
|
|
10985
|
-
}
|
|
10986
|
-
};
|
|
10987
|
-
sourceFetchInFlight = true;
|
|
10988
|
-
const nextFromSource = source.next();
|
|
10989
|
-
nextFromSource.then((value) => {
|
|
10990
|
-
sourceIsDone = value.done == true;
|
|
10991
|
-
resolveWaiter((w) => w.resolve(value));
|
|
10992
|
-
}, (error) => {
|
|
10993
|
-
resolveWaiter((w) => w.reject(error));
|
|
10994
|
-
});
|
|
10995
|
-
};
|
|
10996
|
-
return {
|
|
10997
|
-
next: () => {
|
|
10998
|
-
return new Promise((resolve, reject) => {
|
|
10999
|
-
// First priority: Dispatch ready upstream events.
|
|
11000
|
-
if (sourceIsDone) {
|
|
11001
|
-
return resolve(doneResult);
|
|
11002
|
-
}
|
|
11003
|
-
if (pendingSourceEvent) {
|
|
11004
|
-
pendingSourceEvent({ resolve, reject });
|
|
11005
|
-
pendingSourceEvent = null;
|
|
11006
|
-
return;
|
|
11007
|
-
}
|
|
11008
|
-
// Second priority: Dispatch injected events
|
|
11009
|
-
if (pendingInjectedEvents.length) {
|
|
11010
|
-
return resolve(valueResult(pendingInjectedEvents.shift()));
|
|
11011
|
-
}
|
|
11012
|
-
// Nothing pending? Fetch from source
|
|
11013
|
-
waiter = { resolve, reject };
|
|
11014
|
-
if (!sourceFetchInFlight) {
|
|
11015
|
-
fetchFromSource();
|
|
11016
|
-
}
|
|
11017
|
-
});
|
|
11018
|
-
},
|
|
11019
|
-
inject: (event) => {
|
|
11020
|
-
const pending = consumeWaiter();
|
|
11021
|
-
if (pending != null) {
|
|
11022
|
-
pending.resolve(valueResult(event));
|
|
11023
|
-
}
|
|
11024
|
-
else {
|
|
11025
|
-
pendingInjectedEvents.push(event);
|
|
11026
|
-
}
|
|
11027
|
-
}
|
|
11028
|
-
};
|
|
11029
|
-
}
|
|
11030
|
-
/**
|
|
11031
|
-
* Splits a byte stream at line endings, emitting each line as a string.
|
|
11032
|
-
*/
|
|
11033
|
-
function extractJsonLines(source, decoder) {
|
|
11034
|
-
let buffer = '';
|
|
11035
|
-
const pendingLines = [];
|
|
11036
|
-
let isFinalEvent = false;
|
|
11037
|
-
return {
|
|
11038
|
-
next: async () => {
|
|
11039
|
-
while (true) {
|
|
11040
|
-
if (isFinalEvent) {
|
|
11041
|
-
return doneResult;
|
|
11042
|
-
}
|
|
11043
|
-
{
|
|
11044
|
-
const first = pendingLines.shift();
|
|
11045
|
-
if (first) {
|
|
11046
|
-
return { done: false, value: first };
|
|
11047
|
-
}
|
|
11048
|
-
}
|
|
11049
|
-
const { done, value } = await source.next();
|
|
11050
|
-
if (done) {
|
|
11051
|
-
const remaining = buffer.trim();
|
|
11052
|
-
if (remaining.length != 0) {
|
|
11053
|
-
isFinalEvent = true;
|
|
11054
|
-
return { done: false, value: remaining };
|
|
11055
|
-
}
|
|
11056
|
-
return doneResult;
|
|
11057
|
-
}
|
|
11058
|
-
const data = decoder.decode(value, { stream: true });
|
|
11059
|
-
buffer += data;
|
|
11060
|
-
const lines = buffer.split('\n');
|
|
11061
|
-
for (let i = 0; i < lines.length - 1; i++) {
|
|
11062
|
-
const l = lines[i].trim();
|
|
11063
|
-
if (l.length > 0) {
|
|
11064
|
-
pendingLines.push(l);
|
|
11065
|
-
}
|
|
11066
|
-
}
|
|
11067
|
-
buffer = lines[lines.length - 1];
|
|
11068
|
-
}
|
|
11069
|
-
}
|
|
11070
|
-
};
|
|
11071
|
-
}
|
|
11072
|
-
/**
|
|
11073
|
-
* Splits a concatenated stream of BSON objects by emitting individual objects.
|
|
11074
|
-
*/
|
|
11075
|
-
function extractBsonObjects(source) {
|
|
11076
|
-
// Fully read but not emitted yet.
|
|
11077
|
-
const completedObjects = [];
|
|
11078
|
-
// Whether source has returned { done: true }. We do the same once completed objects have been emitted.
|
|
11079
|
-
let isDone = false;
|
|
11080
|
-
const lengthBuffer = new DataView(new ArrayBuffer(4));
|
|
11081
|
-
let objectBody = null;
|
|
11082
|
-
// If we're parsing the length field, a number between 1 and 4 (inclusive) describing remaining bytes in the header.
|
|
11083
|
-
// If we're consuming a document, the bytes remaining.
|
|
11084
|
-
let remainingLength = 4;
|
|
11085
|
-
return {
|
|
11086
|
-
async next() {
|
|
11087
|
-
while (true) {
|
|
11088
|
-
// Before fetching new data from upstream, return completed objects.
|
|
11089
|
-
if (completedObjects.length) {
|
|
11090
|
-
return valueResult(completedObjects.shift());
|
|
11091
|
-
}
|
|
11092
|
-
if (isDone) {
|
|
11093
|
-
return doneResult;
|
|
11094
|
-
}
|
|
11095
|
-
const upstreamEvent = await source.next();
|
|
11096
|
-
if (upstreamEvent.done) {
|
|
11097
|
-
isDone = true;
|
|
11098
|
-
if (objectBody || remainingLength != 4) {
|
|
11099
|
-
throw new Error('illegal end of stream in BSON object');
|
|
11100
|
-
}
|
|
11101
|
-
return doneResult;
|
|
11102
|
-
}
|
|
11103
|
-
const chunk = upstreamEvent.value;
|
|
11104
|
-
for (let i = 0; i < chunk.length;) {
|
|
11105
|
-
const availableInData = chunk.length - i;
|
|
11106
|
-
if (objectBody) {
|
|
11107
|
-
// We're in the middle of reading a BSON document.
|
|
11108
|
-
const bytesToRead = Math.min(availableInData, remainingLength);
|
|
11109
|
-
const copySource = new Uint8Array(chunk.buffer, chunk.byteOffset + i, bytesToRead);
|
|
11110
|
-
objectBody.set(copySource, objectBody.length - remainingLength);
|
|
11111
|
-
i += bytesToRead;
|
|
11112
|
-
remainingLength -= bytesToRead;
|
|
11113
|
-
if (remainingLength == 0) {
|
|
11114
|
-
completedObjects.push(objectBody);
|
|
11115
|
-
// Prepare to read another document, starting with its length
|
|
11116
|
-
objectBody = null;
|
|
11117
|
-
remainingLength = 4;
|
|
11118
|
-
}
|
|
11119
|
-
}
|
|
11120
|
-
else {
|
|
11121
|
-
// Copy up to 4 bytes into lengthBuffer, depending on how many we still need.
|
|
11122
|
-
const bytesToRead = Math.min(availableInData, remainingLength);
|
|
11123
|
-
for (let j = 0; j < bytesToRead; j++) {
|
|
11124
|
-
lengthBuffer.setUint8(4 - remainingLength + j, chunk[i + j]);
|
|
11125
|
-
}
|
|
11126
|
-
i += bytesToRead;
|
|
11127
|
-
remainingLength -= bytesToRead;
|
|
11128
|
-
if (remainingLength == 0) {
|
|
11129
|
-
// Transition from reading length header to reading document. Subtracting 4 because the length of the
|
|
11130
|
-
// header is included in length.
|
|
11131
|
-
const length = lengthBuffer.getInt32(0, true /* little endian */);
|
|
11132
|
-
remainingLength = length - 4;
|
|
11133
|
-
if (remainingLength < 1) {
|
|
11134
|
-
throw new Error(`invalid length for bson: ${length}`);
|
|
11135
|
-
}
|
|
11136
|
-
objectBody = new Uint8Array(length);
|
|
11137
|
-
new DataView(objectBody.buffer).setInt32(0, length, true);
|
|
11138
|
-
}
|
|
11139
|
-
}
|
|
11140
|
-
}
|
|
11141
|
-
}
|
|
11142
|
-
}
|
|
11143
|
-
};
|
|
11144
|
-
}
|
|
11145
|
-
|
|
11146
11086
|
const POWERSYNC_TRAILING_SLASH_MATCH = /\/+$/;
|
|
11147
11087
|
const POWERSYNC_JS_VERSION = PACKAGE.version;
|
|
11148
11088
|
const SYNC_QUEUE_REQUEST_HIGH_WATER = 10;
|
|
@@ -11361,8 +11301,19 @@ class AbstractRemote {
|
|
|
11361
11301
|
let pendingSocket = null;
|
|
11362
11302
|
let keepAliveTimeout;
|
|
11363
11303
|
let rsocket = null;
|
|
11364
|
-
let
|
|
11304
|
+
let paused = false;
|
|
11305
|
+
const queue = new EventQueue({
|
|
11306
|
+
eventDelivered: () => {
|
|
11307
|
+
if (queue.countOutstandingEvents <= SYNC_QUEUE_REQUEST_LOW_WATER) {
|
|
11308
|
+
paused = false;
|
|
11309
|
+
requestMore();
|
|
11310
|
+
}
|
|
11311
|
+
}
|
|
11312
|
+
});
|
|
11365
11313
|
let didClose = false;
|
|
11314
|
+
let connectionEstablished = false;
|
|
11315
|
+
let pendingEventsCount = syncQueueRequestSize;
|
|
11316
|
+
let res = null;
|
|
11366
11317
|
const abortRequest = () => {
|
|
11367
11318
|
if (didClose) {
|
|
11368
11319
|
return;
|
|
@@ -11375,10 +11326,23 @@ class AbstractRemote {
|
|
|
11375
11326
|
if (rsocket) {
|
|
11376
11327
|
rsocket.close();
|
|
11377
11328
|
}
|
|
11378
|
-
|
|
11379
|
-
|
|
11380
|
-
|
|
11329
|
+
// Send a bogus event to the queue to ensure a pending listener gets woken up. We check for didClose and would
|
|
11330
|
+
// return a doneEvent.
|
|
11331
|
+
queue.notify(null);
|
|
11381
11332
|
};
|
|
11333
|
+
function push(event) {
|
|
11334
|
+
queue.notify(event);
|
|
11335
|
+
if (queue.countOutstandingEvents >= SYNC_QUEUE_REQUEST_HIGH_WATER) {
|
|
11336
|
+
paused = true;
|
|
11337
|
+
}
|
|
11338
|
+
}
|
|
11339
|
+
function requestMore() {
|
|
11340
|
+
const delta = syncQueueRequestSize - pendingEventsCount;
|
|
11341
|
+
if (!paused && delta > 0) {
|
|
11342
|
+
res?.request(delta);
|
|
11343
|
+
pendingEventsCount = syncQueueRequestSize;
|
|
11344
|
+
}
|
|
11345
|
+
}
|
|
11382
11346
|
// Handle upstream abort
|
|
11383
11347
|
if (options.abortSignal.aborted) {
|
|
11384
11348
|
throw new AbortOperation('Connection request aborted');
|
|
@@ -11433,25 +11397,19 @@ class AbstractRemote {
|
|
|
11433
11397
|
// Helps to prevent double close scenarios
|
|
11434
11398
|
rsocket.onClose(() => (rsocket = null));
|
|
11435
11399
|
return await new Promise((resolve, reject) => {
|
|
11436
|
-
|
|
11437
|
-
|
|
11438
|
-
|
|
11439
|
-
|
|
11440
|
-
|
|
11441
|
-
|
|
11442
|
-
|
|
11443
|
-
|
|
11444
|
-
|
|
11400
|
+
const queueAsIterator = {
|
|
11401
|
+
next: async () => {
|
|
11402
|
+
if (didClose)
|
|
11403
|
+
return doneResult;
|
|
11404
|
+
const notification = await queue.waitForEvent(options.abortSignal);
|
|
11405
|
+
if (didClose) {
|
|
11406
|
+
return doneResult;
|
|
11407
|
+
}
|
|
11408
|
+
else {
|
|
11409
|
+
return valueResult(notification);
|
|
11410
|
+
}
|
|
11445
11411
|
}
|
|
11446
|
-
}
|
|
11447
|
-
const events = new domExports.EventIterator((q) => {
|
|
11448
|
-
queue = q;
|
|
11449
|
-
q.on('highWater', () => (paused = true));
|
|
11450
|
-
q.on('lowWater', () => {
|
|
11451
|
-
paused = false;
|
|
11452
|
-
requestMore();
|
|
11453
|
-
});
|
|
11454
|
-
}, { highWaterMark: SYNC_QUEUE_REQUEST_HIGH_WATER, lowWaterMark: SYNC_QUEUE_REQUEST_LOW_WATER })[symbolAsyncIterator]();
|
|
11412
|
+
};
|
|
11455
11413
|
res = rsocket.requestStream({
|
|
11456
11414
|
data: toBuffer(options.data),
|
|
11457
11415
|
metadata: toBuffer({
|
|
@@ -11487,11 +11445,11 @@ class AbstractRemote {
|
|
|
11487
11445
|
// The connection is active
|
|
11488
11446
|
if (!connectionEstablished) {
|
|
11489
11447
|
connectionEstablished = true;
|
|
11490
|
-
resolve(
|
|
11448
|
+
resolve(queueAsIterator);
|
|
11491
11449
|
}
|
|
11492
11450
|
const { data } = payload;
|
|
11493
11451
|
if (data) {
|
|
11494
|
-
|
|
11452
|
+
push(data);
|
|
11495
11453
|
}
|
|
11496
11454
|
// Less events are now pending
|
|
11497
11455
|
pendingEventsCount--;
|
|
@@ -13089,7 +13047,7 @@ class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
13089
13047
|
this.logger.warn('Schema validation failed. Unexpected behaviour could occur', ex);
|
|
13090
13048
|
}
|
|
13091
13049
|
this._schema = schema;
|
|
13092
|
-
await this.database.execute('SELECT powersync_replace_schema(?)', [JSON.stringify(this.schema.toJSON())]);
|
|
13050
|
+
await this.database.writeTransaction((tx) => tx.execute('SELECT powersync_replace_schema(?)', [JSON.stringify(this.schema.toJSON())]));
|
|
13093
13051
|
await this.database.refreshSchema();
|
|
13094
13052
|
this.iterateListeners(async (cb) => cb.schemaChanged?.(schema));
|
|
13095
13053
|
}
|
|
@@ -13609,20 +13567,17 @@ SELECT * FROM crud_entries;
|
|
|
13609
13567
|
* @returns An AsyncIterable that yields QueryResults whenever the data changes
|
|
13610
13568
|
*/
|
|
13611
13569
|
watchWithAsyncGenerator(sql, parameters, options) {
|
|
13612
|
-
return
|
|
13570
|
+
return EventQueue.queueBasedAsyncIterable((queue, abort) => {
|
|
13613
13571
|
const handler = {
|
|
13614
13572
|
onResult: (result) => {
|
|
13615
|
-
|
|
13573
|
+
queue.notify(result);
|
|
13616
13574
|
},
|
|
13617
13575
|
onError: (error) => {
|
|
13618
|
-
|
|
13576
|
+
queue.notifyError(error);
|
|
13619
13577
|
}
|
|
13620
13578
|
};
|
|
13621
|
-
this.watchWithCallback(sql, parameters, handler, options);
|
|
13622
|
-
|
|
13623
|
-
eventOptions.stop();
|
|
13624
|
-
});
|
|
13625
|
-
});
|
|
13579
|
+
this.watchWithCallback(sql, parameters, handler, { ...options, signal: abort });
|
|
13580
|
+
}, options?.signal);
|
|
13626
13581
|
}
|
|
13627
13582
|
/**
|
|
13628
13583
|
* Resolves the list of tables that are used in a SQL query.
|
|
@@ -13712,28 +13667,23 @@ SELECT * FROM crud_entries;
|
|
|
13712
13667
|
* This is preferred over {@link AbstractPowerSyncDatabase.watchWithAsyncGenerator} when multiple queries need to be
|
|
13713
13668
|
* performed together when data is changed.
|
|
13714
13669
|
*
|
|
13715
|
-
* Note: do not declare this as `async *onChange` as it will not work in React Native.
|
|
13716
|
-
*
|
|
13717
13670
|
* @param options - Options for configuring watch behavior
|
|
13718
13671
|
* @returns An AsyncIterable that yields change events whenever the specified tables change
|
|
13719
13672
|
*/
|
|
13673
|
+
// Note: do not declare this as `async *onChange` as it will not work in React Native.
|
|
13720
13674
|
onChangeWithAsyncGenerator(options) {
|
|
13721
|
-
|
|
13722
|
-
|
|
13723
|
-
const dispose = this.onChangeWithCallback({
|
|
13675
|
+
return EventQueue.queueBasedAsyncIterable((queue, abort) => {
|
|
13676
|
+
this.onChangeWithCallback({
|
|
13724
13677
|
onChange: (event) => {
|
|
13725
|
-
|
|
13678
|
+
queue.notify(event);
|
|
13726
13679
|
},
|
|
13727
13680
|
onError: (error) => {
|
|
13728
|
-
|
|
13681
|
+
queue.notifyError(error);
|
|
13729
13682
|
}
|
|
13730
|
-
}, options);
|
|
13731
|
-
|
|
13732
|
-
|
|
13733
|
-
|
|
13734
|
-
});
|
|
13735
|
-
return () => dispose();
|
|
13736
|
-
});
|
|
13683
|
+
}, { ...options, signal: abort });
|
|
13684
|
+
// Note: We don't have to track the dispose function returned by onChangeWithCallback, it cleans up
|
|
13685
|
+
// after the abort signal completes.
|
|
13686
|
+
}, options?.signal);
|
|
13737
13687
|
}
|
|
13738
13688
|
handleTableChanges(changedTables, watchedTables, onDetectedChanges) {
|
|
13739
13689
|
if (changedTables.size > 0) {
|