@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 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
  /*!
@@ -2493,6 +2338,210 @@ class ControlledExecutor {
2493
2338
  }
2494
2339
  }
2495
2340
 
2341
+ /**
2342
+ * Some JavaScript engines, in particular older versions of React Native, don't support Symbol.asyncIterator.
2343
+ *
2344
+ * 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).
2345
+ * This definition is compatible with that polyfill, so transpiled apps can use async iterables created by the PowerSync
2346
+ * SDK.
2347
+ */
2348
+ const symbolAsyncIterator = Symbol.asyncIterator ?? Symbol.for('Symbol.asyncIterator');
2349
+
2350
+ const doneResult = { done: true, value: undefined };
2351
+ function valueResult(value) {
2352
+ return { done: false, value };
2353
+ }
2354
+ /**
2355
+ * Expands a source async iterator by allowing to inject events asynchronously.
2356
+ *
2357
+ * The resulting iterator will emit all events from its source. Additionally though, events can be injected. These
2358
+ * events are dropped once the main iterator completes, but are otherwise forwarded.
2359
+ *
2360
+ * The iterator completes when its source completes, and it supports backpressure by only calling `next()` on the source
2361
+ * in response to a `next()` call from downstream if no pending injected events can be dispatched.
2362
+ */
2363
+ function injectable(source) {
2364
+ let sourceIsDone = false;
2365
+ let waiter = undefined; // An active, waiting next() call.
2366
+ // A pending upstream event that couldn't be dispatched because inject() has been called before it was resolved.
2367
+ let pendingSourceEvent = null;
2368
+ let sourceFetchInFlight = false;
2369
+ let pendingInjectedEvents = [];
2370
+ const consumeWaiter = () => {
2371
+ const pending = waiter;
2372
+ waiter = undefined;
2373
+ return pending;
2374
+ };
2375
+ const fetchFromSource = () => {
2376
+ const resolveWaiter = (propagate) => {
2377
+ sourceFetchInFlight = false;
2378
+ const active = consumeWaiter();
2379
+ if (active) {
2380
+ propagate(active);
2381
+ }
2382
+ else {
2383
+ pendingSourceEvent = propagate;
2384
+ }
2385
+ };
2386
+ sourceFetchInFlight = true;
2387
+ const nextFromSource = source.next();
2388
+ nextFromSource.then((value) => {
2389
+ sourceIsDone = value.done == true;
2390
+ resolveWaiter((w) => w.resolve(value));
2391
+ }, (error) => {
2392
+ resolveWaiter((w) => w.reject(error));
2393
+ });
2394
+ };
2395
+ return {
2396
+ next: () => {
2397
+ return new Promise((resolve, reject) => {
2398
+ // First priority: Dispatch ready upstream events.
2399
+ if (sourceIsDone) {
2400
+ return resolve(doneResult);
2401
+ }
2402
+ if (pendingSourceEvent) {
2403
+ pendingSourceEvent({ resolve, reject });
2404
+ pendingSourceEvent = null;
2405
+ return;
2406
+ }
2407
+ // Second priority: Dispatch injected events
2408
+ if (pendingInjectedEvents.length) {
2409
+ return resolve(valueResult(pendingInjectedEvents.shift()));
2410
+ }
2411
+ // Nothing pending? Fetch from source
2412
+ waiter = { resolve, reject };
2413
+ if (!sourceFetchInFlight) {
2414
+ fetchFromSource();
2415
+ }
2416
+ });
2417
+ },
2418
+ inject: (event) => {
2419
+ const pending = consumeWaiter();
2420
+ if (pending != null) {
2421
+ pending.resolve(valueResult(event));
2422
+ }
2423
+ else {
2424
+ pendingInjectedEvents.push(event);
2425
+ }
2426
+ }
2427
+ };
2428
+ }
2429
+ /**
2430
+ * Splits a byte stream at line endings, emitting each line as a string.
2431
+ */
2432
+ function extractJsonLines(source, decoder) {
2433
+ let buffer = '';
2434
+ const pendingLines = [];
2435
+ let isFinalEvent = false;
2436
+ return {
2437
+ next: async () => {
2438
+ while (true) {
2439
+ if (isFinalEvent) {
2440
+ return doneResult;
2441
+ }
2442
+ {
2443
+ const first = pendingLines.shift();
2444
+ if (first) {
2445
+ return { done: false, value: first };
2446
+ }
2447
+ }
2448
+ const { done, value } = await source.next();
2449
+ if (done) {
2450
+ const remaining = buffer.trim();
2451
+ if (remaining.length != 0) {
2452
+ isFinalEvent = true;
2453
+ return { done: false, value: remaining };
2454
+ }
2455
+ return doneResult;
2456
+ }
2457
+ const data = decoder.decode(value, { stream: true });
2458
+ buffer += data;
2459
+ const lines = buffer.split('\n');
2460
+ for (let i = 0; i < lines.length - 1; i++) {
2461
+ const l = lines[i].trim();
2462
+ if (l.length > 0) {
2463
+ pendingLines.push(l);
2464
+ }
2465
+ }
2466
+ buffer = lines[lines.length - 1];
2467
+ }
2468
+ }
2469
+ };
2470
+ }
2471
+ /**
2472
+ * Splits a concatenated stream of BSON objects by emitting individual objects.
2473
+ */
2474
+ function extractBsonObjects(source) {
2475
+ // Fully read but not emitted yet.
2476
+ const completedObjects = [];
2477
+ // Whether source has returned { done: true }. We do the same once completed objects have been emitted.
2478
+ let isDone = false;
2479
+ const lengthBuffer = new DataView(new ArrayBuffer(4));
2480
+ let objectBody = null;
2481
+ // If we're parsing the length field, a number between 1 and 4 (inclusive) describing remaining bytes in the header.
2482
+ // If we're consuming a document, the bytes remaining.
2483
+ let remainingLength = 4;
2484
+ return {
2485
+ async next() {
2486
+ while (true) {
2487
+ // Before fetching new data from upstream, return completed objects.
2488
+ if (completedObjects.length) {
2489
+ return valueResult(completedObjects.shift());
2490
+ }
2491
+ if (isDone) {
2492
+ return doneResult;
2493
+ }
2494
+ const upstreamEvent = await source.next();
2495
+ if (upstreamEvent.done) {
2496
+ isDone = true;
2497
+ if (objectBody || remainingLength != 4) {
2498
+ throw new Error('illegal end of stream in BSON object');
2499
+ }
2500
+ return doneResult;
2501
+ }
2502
+ const chunk = upstreamEvent.value;
2503
+ for (let i = 0; i < chunk.length;) {
2504
+ const availableInData = chunk.length - i;
2505
+ if (objectBody) {
2506
+ // We're in the middle of reading a BSON document.
2507
+ const bytesToRead = Math.min(availableInData, remainingLength);
2508
+ const copySource = new Uint8Array(chunk.buffer, chunk.byteOffset + i, bytesToRead);
2509
+ objectBody.set(copySource, objectBody.length - remainingLength);
2510
+ i += bytesToRead;
2511
+ remainingLength -= bytesToRead;
2512
+ if (remainingLength == 0) {
2513
+ completedObjects.push(objectBody);
2514
+ // Prepare to read another document, starting with its length
2515
+ objectBody = null;
2516
+ remainingLength = 4;
2517
+ }
2518
+ }
2519
+ else {
2520
+ // Copy up to 4 bytes into lengthBuffer, depending on how many we still need.
2521
+ const bytesToRead = Math.min(availableInData, remainingLength);
2522
+ for (let j = 0; j < bytesToRead; j++) {
2523
+ lengthBuffer.setUint8(4 - remainingLength + j, chunk[i + j]);
2524
+ }
2525
+ i += bytesToRead;
2526
+ remainingLength -= bytesToRead;
2527
+ if (remainingLength == 0) {
2528
+ // Transition from reading length header to reading document. Subtracting 4 because the length of the
2529
+ // header is included in length.
2530
+ const length = lengthBuffer.getInt32(0, true /* little endian */);
2531
+ remainingLength = length - 4;
2532
+ if (remainingLength < 1) {
2533
+ throw new Error(`invalid length for bson: ${length}`);
2534
+ }
2535
+ objectBody = new Uint8Array(length);
2536
+ new DataView(objectBody.buffer).setInt32(0, length, true);
2537
+ }
2538
+ }
2539
+ }
2540
+ }
2541
+ }
2542
+ };
2543
+ }
2544
+
2496
2545
  /**
2497
2546
  * Throttle a function to be called at most once every "wait" milliseconds,
2498
2547
  * on the trailing edge.
@@ -2512,45 +2561,128 @@ function throttleTrailing(func, wait) {
2512
2561
  };
2513
2562
  }
2514
2563
  function asyncNotifier() {
2515
- let waitingConsumer = null;
2516
- let hasPendingNotification = false;
2564
+ const queue = new EventQueue();
2517
2565
  return {
2518
2566
  notify() {
2519
- if (waitingConsumer != null) {
2520
- waitingConsumer();
2521
- waitingConsumer = null;
2567
+ if (queue.countOutstandingEvents > 0) ;
2568
+ else {
2569
+ queue.notify();
2570
+ }
2571
+ },
2572
+ waitForNotification(signal) {
2573
+ return queue.waitForEvent(signal);
2574
+ }
2575
+ };
2576
+ }
2577
+ class EventQueue {
2578
+ options;
2579
+ waitingConsumer;
2580
+ outstandingEvents;
2581
+ constructor(options = {}) {
2582
+ this.options = options;
2583
+ this.outstandingEvents = [];
2584
+ }
2585
+ /**
2586
+ * The amount of buffered events not yet dispatched to listeners.
2587
+ */
2588
+ get countOutstandingEvents() {
2589
+ return this.outstandingEvents.length;
2590
+ }
2591
+ notifyInner(dispatch) {
2592
+ const existing = this.waitingConsumer;
2593
+ this.waitingConsumer = undefined;
2594
+ const dispatchAndNotifyListeners = (waiter) => {
2595
+ dispatch(waiter);
2596
+ this.options.eventDelivered?.();
2597
+ };
2598
+ if (existing) {
2599
+ dispatchAndNotifyListeners(existing);
2600
+ }
2601
+ else {
2602
+ this.outstandingEvents.push(dispatchAndNotifyListeners);
2603
+ }
2604
+ }
2605
+ notify(value) {
2606
+ this.notifyInner((l) => l.resolve(value));
2607
+ }
2608
+ notifyError(error) {
2609
+ this.notifyInner((l) => l.reject(error));
2610
+ }
2611
+ waitForEvent(signal) {
2612
+ return new Promise((resolve, reject) => {
2613
+ if (this.waitingConsumer != null) {
2614
+ throw new Error('Illegal call to waitForEvent, already has a waiter.');
2615
+ }
2616
+ const complete = () => {
2617
+ signal?.removeEventListener('abort', onAbort);
2618
+ };
2619
+ const onAbort = () => {
2620
+ complete();
2621
+ this.waitingConsumer = undefined;
2622
+ resolve(undefined);
2623
+ };
2624
+ const waiter = {
2625
+ resolve: (value) => {
2626
+ complete();
2627
+ resolve(value);
2628
+ },
2629
+ reject: (error) => {
2630
+ complete();
2631
+ reject(error);
2632
+ }
2633
+ };
2634
+ if (signal.aborted) {
2635
+ resolve(undefined);
2636
+ }
2637
+ else if (this.countOutstandingEvents > 0) {
2638
+ const [event] = this.outstandingEvents.splice(0, 1);
2639
+ event(waiter);
2522
2640
  }
2523
2641
  else {
2524
- hasPendingNotification = true;
2642
+ this.waitingConsumer = waiter;
2643
+ signal.addEventListener('abort', onAbort);
2525
2644
  }
2526
- },
2527
- waitForNotification(signal) {
2528
- return new Promise((resolve) => {
2529
- if (waitingConsumer != null) {
2530
- throw new Error('Illegal call to waitForNotification, already has a waiter.');
2531
- }
2532
- if (signal.aborted) {
2533
- resolve();
2534
- }
2535
- else if (hasPendingNotification) {
2536
- resolve();
2537
- hasPendingNotification = false;
2645
+ });
2646
+ }
2647
+ /**
2648
+ * Creates an async iterable backed by event queues.
2649
+ *
2650
+ * @param run A function invoked for every new listener. It receives a queue backing the async iterator.
2651
+ * @param abort An additional abort signal. The `run` callback will also be aborted when `AsyncIterator.return` is
2652
+ * called.
2653
+ * @returns An object conforming to the async iterable protocol.
2654
+ */
2655
+ static queueBasedAsyncIterable(run, abort) {
2656
+ return {
2657
+ [symbolAsyncIterator]: () => {
2658
+ const queue = new EventQueue();
2659
+ const controller = new AbortController();
2660
+ function dispose() {
2661
+ controller.abort();
2662
+ abort?.removeEventListener('abort', dispose);
2538
2663
  }
2539
- else {
2540
- function complete() {
2541
- signal.removeEventListener('abort', onAbort);
2542
- resolve();
2664
+ if (abort) {
2665
+ if (abort.aborted) {
2666
+ controller.abort();
2543
2667
  }
2544
- function onAbort() {
2545
- waitingConsumer = null;
2546
- resolve();
2668
+ else {
2669
+ abort.addEventListener('abort', dispose);
2547
2670
  }
2548
- waitingConsumer = complete;
2549
- signal.addEventListener('abort', onAbort);
2550
2671
  }
2551
- });
2552
- }
2553
- };
2672
+ run(queue, controller.signal);
2673
+ return {
2674
+ async next() {
2675
+ const event = await queue.waitForEvent(controller.signal);
2676
+ return event == null ? doneResult : valueResult(event);
2677
+ },
2678
+ async return() {
2679
+ dispose();
2680
+ return doneResult;
2681
+ }
2682
+ };
2683
+ }
2684
+ };
2685
+ }
2554
2686
  }
2555
2687
 
2556
2688
  /**
@@ -10781,7 +10913,7 @@ function requireDist () {
10781
10913
 
10782
10914
  var distExports = requireDist();
10783
10915
 
10784
- var version = "1.54.0";
10916
+ var version = "1.55.0";
10785
10917
  var PACKAGE = {
10786
10918
  version: version};
10787
10919
 
@@ -10948,201 +11080,6 @@ class WebsocketClientTransport {
10948
11080
  }
10949
11081
  }
10950
11082
 
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
11083
  const POWERSYNC_TRAILING_SLASH_MATCH = /\/+$/;
11147
11084
  const POWERSYNC_JS_VERSION = PACKAGE.version;
11148
11085
  const SYNC_QUEUE_REQUEST_HIGH_WATER = 10;
@@ -11361,8 +11298,19 @@ class AbstractRemote {
11361
11298
  let pendingSocket = null;
11362
11299
  let keepAliveTimeout;
11363
11300
  let rsocket = null;
11364
- let queue = null;
11301
+ let paused = false;
11302
+ const queue = new EventQueue({
11303
+ eventDelivered: () => {
11304
+ if (queue.countOutstandingEvents <= SYNC_QUEUE_REQUEST_LOW_WATER) {
11305
+ paused = false;
11306
+ requestMore();
11307
+ }
11308
+ }
11309
+ });
11365
11310
  let didClose = false;
11311
+ let connectionEstablished = false;
11312
+ let pendingEventsCount = syncQueueRequestSize;
11313
+ let res = null;
11366
11314
  const abortRequest = () => {
11367
11315
  if (didClose) {
11368
11316
  return;
@@ -11375,10 +11323,23 @@ class AbstractRemote {
11375
11323
  if (rsocket) {
11376
11324
  rsocket.close();
11377
11325
  }
11378
- if (queue) {
11379
- queue.stop();
11380
- }
11326
+ // Send a bogus event to the queue to ensure a pending listener gets woken up. We check for didClose and would
11327
+ // return a doneEvent.
11328
+ queue.notify(null);
11381
11329
  };
11330
+ function push(event) {
11331
+ queue.notify(event);
11332
+ if (queue.countOutstandingEvents >= SYNC_QUEUE_REQUEST_HIGH_WATER) {
11333
+ paused = true;
11334
+ }
11335
+ }
11336
+ function requestMore() {
11337
+ const delta = syncQueueRequestSize - pendingEventsCount;
11338
+ if (!paused && delta > 0) {
11339
+ res?.request(delta);
11340
+ pendingEventsCount = syncQueueRequestSize;
11341
+ }
11342
+ }
11382
11343
  // Handle upstream abort
11383
11344
  if (options.abortSignal.aborted) {
11384
11345
  throw new AbortOperation('Connection request aborted');
@@ -11433,25 +11394,19 @@ class AbstractRemote {
11433
11394
  // Helps to prevent double close scenarios
11434
11395
  rsocket.onClose(() => (rsocket = null));
11435
11396
  return await new Promise((resolve, reject) => {
11436
- let connectionEstablished = false;
11437
- let pendingEventsCount = syncQueueRequestSize;
11438
- let paused = false;
11439
- let res = null;
11440
- function requestMore() {
11441
- const delta = syncQueueRequestSize - pendingEventsCount;
11442
- if (!paused && delta > 0) {
11443
- res?.request(delta);
11444
- pendingEventsCount = syncQueueRequestSize;
11397
+ const queueAsIterator = {
11398
+ next: async () => {
11399
+ if (didClose)
11400
+ return doneResult;
11401
+ const notification = await queue.waitForEvent(options.abortSignal);
11402
+ if (didClose) {
11403
+ return doneResult;
11404
+ }
11405
+ else {
11406
+ return valueResult(notification);
11407
+ }
11445
11408
  }
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]();
11409
+ };
11455
11410
  res = rsocket.requestStream({
11456
11411
  data: toBuffer(options.data),
11457
11412
  metadata: toBuffer({
@@ -11487,11 +11442,11 @@ class AbstractRemote {
11487
11442
  // The connection is active
11488
11443
  if (!connectionEstablished) {
11489
11444
  connectionEstablished = true;
11490
- resolve(events);
11445
+ resolve(queueAsIterator);
11491
11446
  }
11492
11447
  const { data } = payload;
11493
11448
  if (data) {
11494
- queue.push(data);
11449
+ push(data);
11495
11450
  }
11496
11451
  // Less events are now pending
11497
11452
  pendingEventsCount--;
@@ -13609,20 +13564,17 @@ SELECT * FROM crud_entries;
13609
13564
  * @returns An AsyncIterable that yields QueryResults whenever the data changes
13610
13565
  */
13611
13566
  watchWithAsyncGenerator(sql, parameters, options) {
13612
- return new domExports.EventIterator((eventOptions) => {
13567
+ return EventQueue.queueBasedAsyncIterable((queue, abort) => {
13613
13568
  const handler = {
13614
13569
  onResult: (result) => {
13615
- eventOptions.push(result);
13570
+ queue.notify(result);
13616
13571
  },
13617
13572
  onError: (error) => {
13618
- eventOptions.fail(error);
13573
+ queue.notifyError(error);
13619
13574
  }
13620
13575
  };
13621
- this.watchWithCallback(sql, parameters, handler, options);
13622
- options?.signal?.addEventListener('abort', () => {
13623
- eventOptions.stop();
13624
- });
13625
- });
13576
+ this.watchWithCallback(sql, parameters, handler, { ...options, signal: abort });
13577
+ }, options?.signal);
13626
13578
  }
13627
13579
  /**
13628
13580
  * Resolves the list of tables that are used in a SQL query.
@@ -13712,28 +13664,23 @@ SELECT * FROM crud_entries;
13712
13664
  * This is preferred over {@link AbstractPowerSyncDatabase.watchWithAsyncGenerator} when multiple queries need to be
13713
13665
  * performed together when data is changed.
13714
13666
  *
13715
- * Note: do not declare this as `async *onChange` as it will not work in React Native.
13716
- *
13717
13667
  * @param options - Options for configuring watch behavior
13718
13668
  * @returns An AsyncIterable that yields change events whenever the specified tables change
13719
13669
  */
13670
+ // Note: do not declare this as `async *onChange` as it will not work in React Native.
13720
13671
  onChangeWithAsyncGenerator(options) {
13721
- const resolvedOptions = options ?? {};
13722
- return new domExports.EventIterator((eventOptions) => {
13723
- const dispose = this.onChangeWithCallback({
13672
+ return EventQueue.queueBasedAsyncIterable((queue, abort) => {
13673
+ this.onChangeWithCallback({
13724
13674
  onChange: (event) => {
13725
- eventOptions.push(event);
13675
+ queue.notify(event);
13726
13676
  },
13727
13677
  onError: (error) => {
13728
- eventOptions.fail(error);
13678
+ queue.notifyError(error);
13729
13679
  }
13730
- }, options);
13731
- resolvedOptions.signal?.addEventListener('abort', () => {
13732
- eventOptions.stop();
13733
- // Maybe fail?
13734
- });
13735
- return () => dispose();
13736
- });
13680
+ }, { ...options, signal: abort });
13681
+ // Note: We don't have to track the dispose function returned by onChangeWithCallback, it cleans up
13682
+ // after the abort signal completes.
13683
+ }, options?.signal);
13737
13684
  }
13738
13685
  handleTableChanges(changedTables, watchedTables, onDetectedChanges) {
13739
13686
  if (changedTables.size > 0) {