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