@powersync/web 1.38.3 → 1.38.4

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.
@@ -4342,165 +4342,10 @@ var EncodingType;
4342
4342
  EncodingType["Base64"] = "base64";
4343
4343
  })(EncodingType || (EncodingType = {}));
4344
4344
 
4345
- const symbolAsyncIterator = Symbol.asyncIterator ?? Symbol.for('Symbol.asyncIterator');
4346
-
4347
4345
  function getDefaultExportFromCjs (x) {
4348
4346
  return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
4349
4347
  }
4350
4348
 
4351
- var dom = {};
4352
-
4353
- var eventIterator = {};
4354
-
4355
- var hasRequiredEventIterator;
4356
-
4357
- function requireEventIterator () {
4358
- if (hasRequiredEventIterator) return eventIterator;
4359
- hasRequiredEventIterator = 1;
4360
- Object.defineProperty(eventIterator, "__esModule", { value: true });
4361
- class EventQueue {
4362
- constructor() {
4363
- this.pullQueue = [];
4364
- this.pushQueue = [];
4365
- this.eventHandlers = {};
4366
- this.isPaused = false;
4367
- this.isStopped = false;
4368
- }
4369
- push(value) {
4370
- if (this.isStopped)
4371
- return;
4372
- const resolution = { value, done: false };
4373
- if (this.pullQueue.length) {
4374
- const placeholder = this.pullQueue.shift();
4375
- if (placeholder)
4376
- placeholder.resolve(resolution);
4377
- }
4378
- else {
4379
- this.pushQueue.push(Promise.resolve(resolution));
4380
- if (this.highWaterMark !== undefined &&
4381
- this.pushQueue.length >= this.highWaterMark &&
4382
- !this.isPaused) {
4383
- this.isPaused = true;
4384
- if (this.eventHandlers.highWater) {
4385
- this.eventHandlers.highWater();
4386
- }
4387
- else if (console) {
4388
- console.warn(`EventIterator queue reached ${this.pushQueue.length} items`);
4389
- }
4390
- }
4391
- }
4392
- }
4393
- stop() {
4394
- if (this.isStopped)
4395
- return;
4396
- this.isStopped = true;
4397
- this.remove();
4398
- for (const placeholder of this.pullQueue) {
4399
- placeholder.resolve({ value: undefined, done: true });
4400
- }
4401
- this.pullQueue.length = 0;
4402
- }
4403
- fail(error) {
4404
- if (this.isStopped)
4405
- return;
4406
- this.isStopped = true;
4407
- this.remove();
4408
- if (this.pullQueue.length) {
4409
- for (const placeholder of this.pullQueue) {
4410
- placeholder.reject(error);
4411
- }
4412
- this.pullQueue.length = 0;
4413
- }
4414
- else {
4415
- const rejection = Promise.reject(error);
4416
- /* Attach error handler to avoid leaking an unhandled promise rejection. */
4417
- rejection.catch(() => { });
4418
- this.pushQueue.push(rejection);
4419
- }
4420
- }
4421
- remove() {
4422
- Promise.resolve().then(() => {
4423
- if (this.removeCallback)
4424
- this.removeCallback();
4425
- });
4426
- }
4427
- [symbolAsyncIterator]() {
4428
- return {
4429
- next: (value) => {
4430
- const result = this.pushQueue.shift();
4431
- if (result) {
4432
- if (this.lowWaterMark !== undefined &&
4433
- this.pushQueue.length <= this.lowWaterMark &&
4434
- this.isPaused) {
4435
- this.isPaused = false;
4436
- if (this.eventHandlers.lowWater) {
4437
- this.eventHandlers.lowWater();
4438
- }
4439
- }
4440
- return result;
4441
- }
4442
- else if (this.isStopped) {
4443
- return Promise.resolve({ value: undefined, done: true });
4444
- }
4445
- else {
4446
- return new Promise((resolve, reject) => {
4447
- this.pullQueue.push({ resolve, reject });
4448
- });
4449
- }
4450
- },
4451
- return: () => {
4452
- this.isStopped = true;
4453
- this.pushQueue.length = 0;
4454
- this.remove();
4455
- return Promise.resolve({ value: undefined, done: true });
4456
- },
4457
- };
4458
- }
4459
- }
4460
- class EventIterator {
4461
- constructor(listen, { highWaterMark = 100, lowWaterMark = 1 } = {}) {
4462
- const queue = new EventQueue();
4463
- queue.highWaterMark = highWaterMark;
4464
- queue.lowWaterMark = lowWaterMark;
4465
- queue.removeCallback =
4466
- listen({
4467
- push: value => queue.push(value),
4468
- stop: () => queue.stop(),
4469
- fail: error => queue.fail(error),
4470
- on: (event, fn) => {
4471
- queue.eventHandlers[event] = fn;
4472
- },
4473
- }) || (() => { });
4474
- this[symbolAsyncIterator] = () => queue[symbolAsyncIterator]();
4475
- Object.freeze(this);
4476
- }
4477
- }
4478
- eventIterator.EventIterator = EventIterator;
4479
- eventIterator.default = EventIterator;
4480
- return eventIterator;
4481
- }
4482
-
4483
- var hasRequiredDom;
4484
-
4485
- function requireDom () {
4486
- if (hasRequiredDom) return dom;
4487
- hasRequiredDom = 1;
4488
- Object.defineProperty(dom, "__esModule", { value: true });
4489
- const event_iterator_1 = requireEventIterator();
4490
- dom.EventIterator = event_iterator_1.EventIterator;
4491
- function subscribe(event, options, evOptions) {
4492
- return new event_iterator_1.EventIterator(({ push }) => {
4493
- this.addEventListener(event, push, options);
4494
- return () => this.removeEventListener(event, push, options);
4495
- }, evOptions);
4496
- }
4497
- dom.subscribe = subscribe;
4498
- dom.default = event_iterator_1.EventIterator;
4499
- return dom;
4500
- }
4501
-
4502
- var domExports = requireDom();
4503
-
4504
4349
  var logger$1 = {exports: {}};
4505
4350
 
4506
4351
  /*!
@@ -5384,6 +5229,210 @@ class ControlledExecutor {
5384
5229
  }
5385
5230
  }
5386
5231
 
5232
+ /**
5233
+ * Some JavaScript engines, in particular older versions of React Native, don't support Symbol.asyncIterator.
5234
+ *
5235
+ * 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).
5236
+ * This definition is compatible with that polyfill, so transpiled apps can use async iterables created by the PowerSync
5237
+ * SDK.
5238
+ */
5239
+ const symbolAsyncIterator = Symbol.asyncIterator ?? Symbol.for('Symbol.asyncIterator');
5240
+
5241
+ const doneResult = { done: true, value: undefined };
5242
+ function valueResult(value) {
5243
+ return { done: false, value };
5244
+ }
5245
+ /**
5246
+ * Expands a source async iterator by allowing to inject events asynchronously.
5247
+ *
5248
+ * The resulting iterator will emit all events from its source. Additionally though, events can be injected. These
5249
+ * events are dropped once the main iterator completes, but are otherwise forwarded.
5250
+ *
5251
+ * The iterator completes when its source completes, and it supports backpressure by only calling `next()` on the source
5252
+ * in response to a `next()` call from downstream if no pending injected events can be dispatched.
5253
+ */
5254
+ function injectable(source) {
5255
+ let sourceIsDone = false;
5256
+ let waiter = undefined; // An active, waiting next() call.
5257
+ // A pending upstream event that couldn't be dispatched because inject() has been called before it was resolved.
5258
+ let pendingSourceEvent = null;
5259
+ let sourceFetchInFlight = false;
5260
+ let pendingInjectedEvents = [];
5261
+ const consumeWaiter = () => {
5262
+ const pending = waiter;
5263
+ waiter = undefined;
5264
+ return pending;
5265
+ };
5266
+ const fetchFromSource = () => {
5267
+ const resolveWaiter = (propagate) => {
5268
+ sourceFetchInFlight = false;
5269
+ const active = consumeWaiter();
5270
+ if (active) {
5271
+ propagate(active);
5272
+ }
5273
+ else {
5274
+ pendingSourceEvent = propagate;
5275
+ }
5276
+ };
5277
+ sourceFetchInFlight = true;
5278
+ const nextFromSource = source.next();
5279
+ nextFromSource.then((value) => {
5280
+ sourceIsDone = value.done == true;
5281
+ resolveWaiter((w) => w.resolve(value));
5282
+ }, (error) => {
5283
+ resolveWaiter((w) => w.reject(error));
5284
+ });
5285
+ };
5286
+ return {
5287
+ next: () => {
5288
+ return new Promise((resolve, reject) => {
5289
+ // First priority: Dispatch ready upstream events.
5290
+ if (sourceIsDone) {
5291
+ return resolve(doneResult);
5292
+ }
5293
+ if (pendingSourceEvent) {
5294
+ pendingSourceEvent({ resolve, reject });
5295
+ pendingSourceEvent = null;
5296
+ return;
5297
+ }
5298
+ // Second priority: Dispatch injected events
5299
+ if (pendingInjectedEvents.length) {
5300
+ return resolve(valueResult(pendingInjectedEvents.shift()));
5301
+ }
5302
+ // Nothing pending? Fetch from source
5303
+ waiter = { resolve, reject };
5304
+ if (!sourceFetchInFlight) {
5305
+ fetchFromSource();
5306
+ }
5307
+ });
5308
+ },
5309
+ inject: (event) => {
5310
+ const pending = consumeWaiter();
5311
+ if (pending != null) {
5312
+ pending.resolve(valueResult(event));
5313
+ }
5314
+ else {
5315
+ pendingInjectedEvents.push(event);
5316
+ }
5317
+ }
5318
+ };
5319
+ }
5320
+ /**
5321
+ * Splits a byte stream at line endings, emitting each line as a string.
5322
+ */
5323
+ function extractJsonLines(source, decoder) {
5324
+ let buffer = '';
5325
+ const pendingLines = [];
5326
+ let isFinalEvent = false;
5327
+ return {
5328
+ next: async () => {
5329
+ while (true) {
5330
+ if (isFinalEvent) {
5331
+ return doneResult;
5332
+ }
5333
+ {
5334
+ const first = pendingLines.shift();
5335
+ if (first) {
5336
+ return { done: false, value: first };
5337
+ }
5338
+ }
5339
+ const { done, value } = await source.next();
5340
+ if (done) {
5341
+ const remaining = buffer.trim();
5342
+ if (remaining.length != 0) {
5343
+ isFinalEvent = true;
5344
+ return { done: false, value: remaining };
5345
+ }
5346
+ return doneResult;
5347
+ }
5348
+ const data = decoder.decode(value, { stream: true });
5349
+ buffer += data;
5350
+ const lines = buffer.split('\n');
5351
+ for (let i = 0; i < lines.length - 1; i++) {
5352
+ const l = lines[i].trim();
5353
+ if (l.length > 0) {
5354
+ pendingLines.push(l);
5355
+ }
5356
+ }
5357
+ buffer = lines[lines.length - 1];
5358
+ }
5359
+ }
5360
+ };
5361
+ }
5362
+ /**
5363
+ * Splits a concatenated stream of BSON objects by emitting individual objects.
5364
+ */
5365
+ function extractBsonObjects(source) {
5366
+ // Fully read but not emitted yet.
5367
+ const completedObjects = [];
5368
+ // Whether source has returned { done: true }. We do the same once completed objects have been emitted.
5369
+ let isDone = false;
5370
+ const lengthBuffer = new DataView(new ArrayBuffer(4));
5371
+ let objectBody = null;
5372
+ // If we're parsing the length field, a number between 1 and 4 (inclusive) describing remaining bytes in the header.
5373
+ // If we're consuming a document, the bytes remaining.
5374
+ let remainingLength = 4;
5375
+ return {
5376
+ async next() {
5377
+ while (true) {
5378
+ // Before fetching new data from upstream, return completed objects.
5379
+ if (completedObjects.length) {
5380
+ return valueResult(completedObjects.shift());
5381
+ }
5382
+ if (isDone) {
5383
+ return doneResult;
5384
+ }
5385
+ const upstreamEvent = await source.next();
5386
+ if (upstreamEvent.done) {
5387
+ isDone = true;
5388
+ if (objectBody || remainingLength != 4) {
5389
+ throw new Error('illegal end of stream in BSON object');
5390
+ }
5391
+ return doneResult;
5392
+ }
5393
+ const chunk = upstreamEvent.value;
5394
+ for (let i = 0; i < chunk.length;) {
5395
+ const availableInData = chunk.length - i;
5396
+ if (objectBody) {
5397
+ // We're in the middle of reading a BSON document.
5398
+ const bytesToRead = Math.min(availableInData, remainingLength);
5399
+ const copySource = new Uint8Array(chunk.buffer, chunk.byteOffset + i, bytesToRead);
5400
+ objectBody.set(copySource, objectBody.length - remainingLength);
5401
+ i += bytesToRead;
5402
+ remainingLength -= bytesToRead;
5403
+ if (remainingLength == 0) {
5404
+ completedObjects.push(objectBody);
5405
+ // Prepare to read another document, starting with its length
5406
+ objectBody = null;
5407
+ remainingLength = 4;
5408
+ }
5409
+ }
5410
+ else {
5411
+ // Copy up to 4 bytes into lengthBuffer, depending on how many we still need.
5412
+ const bytesToRead = Math.min(availableInData, remainingLength);
5413
+ for (let j = 0; j < bytesToRead; j++) {
5414
+ lengthBuffer.setUint8(4 - remainingLength + j, chunk[i + j]);
5415
+ }
5416
+ i += bytesToRead;
5417
+ remainingLength -= bytesToRead;
5418
+ if (remainingLength == 0) {
5419
+ // Transition from reading length header to reading document. Subtracting 4 because the length of the
5420
+ // header is included in length.
5421
+ const length = lengthBuffer.getInt32(0, true /* little endian */);
5422
+ remainingLength = length - 4;
5423
+ if (remainingLength < 1) {
5424
+ throw new Error(`invalid length for bson: ${length}`);
5425
+ }
5426
+ objectBody = new Uint8Array(length);
5427
+ new DataView(objectBody.buffer).setInt32(0, length, true);
5428
+ }
5429
+ }
5430
+ }
5431
+ }
5432
+ }
5433
+ };
5434
+ }
5435
+
5387
5436
  /**
5388
5437
  * Throttle a function to be called at most once every "wait" milliseconds,
5389
5438
  * on the trailing edge.
@@ -5403,45 +5452,128 @@ function throttleTrailing(func, wait) {
5403
5452
  };
5404
5453
  }
5405
5454
  function asyncNotifier() {
5406
- let waitingConsumer = null;
5407
- let hasPendingNotification = false;
5455
+ const queue = new EventQueue();
5408
5456
  return {
5409
5457
  notify() {
5410
- if (waitingConsumer != null) {
5411
- waitingConsumer();
5412
- waitingConsumer = null;
5458
+ if (queue.countOutstandingEvents > 0) ;
5459
+ else {
5460
+ queue.notify();
5461
+ }
5462
+ },
5463
+ waitForNotification(signal) {
5464
+ return queue.waitForEvent(signal);
5465
+ }
5466
+ };
5467
+ }
5468
+ class EventQueue {
5469
+ options;
5470
+ waitingConsumer;
5471
+ outstandingEvents;
5472
+ constructor(options = {}) {
5473
+ this.options = options;
5474
+ this.outstandingEvents = [];
5475
+ }
5476
+ /**
5477
+ * The amount of buffered events not yet dispatched to listeners.
5478
+ */
5479
+ get countOutstandingEvents() {
5480
+ return this.outstandingEvents.length;
5481
+ }
5482
+ notifyInner(dispatch) {
5483
+ const existing = this.waitingConsumer;
5484
+ this.waitingConsumer = undefined;
5485
+ const dispatchAndNotifyListeners = (waiter) => {
5486
+ dispatch(waiter);
5487
+ this.options.eventDelivered?.();
5488
+ };
5489
+ if (existing) {
5490
+ dispatchAndNotifyListeners(existing);
5491
+ }
5492
+ else {
5493
+ this.outstandingEvents.push(dispatchAndNotifyListeners);
5494
+ }
5495
+ }
5496
+ notify(value) {
5497
+ this.notifyInner((l) => l.resolve(value));
5498
+ }
5499
+ notifyError(error) {
5500
+ this.notifyInner((l) => l.reject(error));
5501
+ }
5502
+ waitForEvent(signal) {
5503
+ return new Promise((resolve, reject) => {
5504
+ if (this.waitingConsumer != null) {
5505
+ throw new Error('Illegal call to waitForEvent, already has a waiter.');
5506
+ }
5507
+ const complete = () => {
5508
+ signal?.removeEventListener('abort', onAbort);
5509
+ };
5510
+ const onAbort = () => {
5511
+ complete();
5512
+ this.waitingConsumer = undefined;
5513
+ resolve(undefined);
5514
+ };
5515
+ const waiter = {
5516
+ resolve: (value) => {
5517
+ complete();
5518
+ resolve(value);
5519
+ },
5520
+ reject: (error) => {
5521
+ complete();
5522
+ reject(error);
5523
+ }
5524
+ };
5525
+ if (signal.aborted) {
5526
+ resolve(undefined);
5527
+ }
5528
+ else if (this.countOutstandingEvents > 0) {
5529
+ const [event] = this.outstandingEvents.splice(0, 1);
5530
+ event(waiter);
5413
5531
  }
5414
5532
  else {
5415
- hasPendingNotification = true;
5533
+ this.waitingConsumer = waiter;
5534
+ signal.addEventListener('abort', onAbort);
5416
5535
  }
5417
- },
5418
- waitForNotification(signal) {
5419
- return new Promise((resolve) => {
5420
- if (waitingConsumer != null) {
5421
- throw new Error('Illegal call to waitForNotification, already has a waiter.');
5422
- }
5423
- if (signal.aborted) {
5424
- resolve();
5425
- }
5426
- else if (hasPendingNotification) {
5427
- resolve();
5428
- hasPendingNotification = false;
5536
+ });
5537
+ }
5538
+ /**
5539
+ * Creates an async iterable backed by event queues.
5540
+ *
5541
+ * @param run A function invoked for every new listener. It receives a queue backing the async iterator.
5542
+ * @param abort An additional abort signal. The `run` callback will also be aborted when `AsyncIterator.return` is
5543
+ * called.
5544
+ * @returns An object conforming to the async iterable protocol.
5545
+ */
5546
+ static queueBasedAsyncIterable(run, abort) {
5547
+ return {
5548
+ [symbolAsyncIterator]: () => {
5549
+ const queue = new EventQueue();
5550
+ const controller = new AbortController();
5551
+ function dispose() {
5552
+ controller.abort();
5553
+ abort?.removeEventListener('abort', dispose);
5429
5554
  }
5430
- else {
5431
- function complete() {
5432
- signal.removeEventListener('abort', onAbort);
5433
- resolve();
5555
+ if (abort) {
5556
+ if (abort.aborted) {
5557
+ controller.abort();
5434
5558
  }
5435
- function onAbort() {
5436
- waitingConsumer = null;
5437
- resolve();
5559
+ else {
5560
+ abort.addEventListener('abort', dispose);
5438
5561
  }
5439
- waitingConsumer = complete;
5440
- signal.addEventListener('abort', onAbort);
5441
5562
  }
5442
- });
5443
- }
5444
- };
5563
+ run(queue, controller.signal);
5564
+ return {
5565
+ async next() {
5566
+ const event = await queue.waitForEvent(controller.signal);
5567
+ return event == null ? doneResult : valueResult(event);
5568
+ },
5569
+ async return() {
5570
+ dispose();
5571
+ return doneResult;
5572
+ }
5573
+ };
5574
+ }
5575
+ };
5576
+ }
5445
5577
  }
5446
5578
 
5447
5579
  /**
@@ -13672,7 +13804,7 @@ function requireDist () {
13672
13804
 
13673
13805
  var distExports = requireDist();
13674
13806
 
13675
- var version = "1.54.0";
13807
+ var version = "1.55.0";
13676
13808
  var PACKAGE = {
13677
13809
  version: version};
13678
13810
 
@@ -13839,201 +13971,6 @@ class WebsocketClientTransport {
13839
13971
  }
13840
13972
  }
13841
13973
 
13842
- const doneResult = { done: true, value: undefined };
13843
- function valueResult(value) {
13844
- return { done: false, value };
13845
- }
13846
- /**
13847
- * Expands a source async iterator by allowing to inject events asynchronously.
13848
- *
13849
- * The resulting iterator will emit all events from its source. Additionally though, events can be injected. These
13850
- * events are dropped once the main iterator completes, but are otherwise forwarded.
13851
- *
13852
- * The iterator completes when its source completes, and it supports backpressure by only calling `next()` on the source
13853
- * in response to a `next()` call from downstream if no pending injected events can be dispatched.
13854
- */
13855
- function injectable(source) {
13856
- let sourceIsDone = false;
13857
- let waiter = undefined; // An active, waiting next() call.
13858
- // A pending upstream event that couldn't be dispatched because inject() has been called before it was resolved.
13859
- let pendingSourceEvent = null;
13860
- let sourceFetchInFlight = false;
13861
- let pendingInjectedEvents = [];
13862
- const consumeWaiter = () => {
13863
- const pending = waiter;
13864
- waiter = undefined;
13865
- return pending;
13866
- };
13867
- const fetchFromSource = () => {
13868
- const resolveWaiter = (propagate) => {
13869
- sourceFetchInFlight = false;
13870
- const active = consumeWaiter();
13871
- if (active) {
13872
- propagate(active);
13873
- }
13874
- else {
13875
- pendingSourceEvent = propagate;
13876
- }
13877
- };
13878
- sourceFetchInFlight = true;
13879
- const nextFromSource = source.next();
13880
- nextFromSource.then((value) => {
13881
- sourceIsDone = value.done == true;
13882
- resolveWaiter((w) => w.resolve(value));
13883
- }, (error) => {
13884
- resolveWaiter((w) => w.reject(error));
13885
- });
13886
- };
13887
- return {
13888
- next: () => {
13889
- return new Promise((resolve, reject) => {
13890
- // First priority: Dispatch ready upstream events.
13891
- if (sourceIsDone) {
13892
- return resolve(doneResult);
13893
- }
13894
- if (pendingSourceEvent) {
13895
- pendingSourceEvent({ resolve, reject });
13896
- pendingSourceEvent = null;
13897
- return;
13898
- }
13899
- // Second priority: Dispatch injected events
13900
- if (pendingInjectedEvents.length) {
13901
- return resolve(valueResult(pendingInjectedEvents.shift()));
13902
- }
13903
- // Nothing pending? Fetch from source
13904
- waiter = { resolve, reject };
13905
- if (!sourceFetchInFlight) {
13906
- fetchFromSource();
13907
- }
13908
- });
13909
- },
13910
- inject: (event) => {
13911
- const pending = consumeWaiter();
13912
- if (pending != null) {
13913
- pending.resolve(valueResult(event));
13914
- }
13915
- else {
13916
- pendingInjectedEvents.push(event);
13917
- }
13918
- }
13919
- };
13920
- }
13921
- /**
13922
- * Splits a byte stream at line endings, emitting each line as a string.
13923
- */
13924
- function extractJsonLines(source, decoder) {
13925
- let buffer = '';
13926
- const pendingLines = [];
13927
- let isFinalEvent = false;
13928
- return {
13929
- next: async () => {
13930
- while (true) {
13931
- if (isFinalEvent) {
13932
- return doneResult;
13933
- }
13934
- {
13935
- const first = pendingLines.shift();
13936
- if (first) {
13937
- return { done: false, value: first };
13938
- }
13939
- }
13940
- const { done, value } = await source.next();
13941
- if (done) {
13942
- const remaining = buffer.trim();
13943
- if (remaining.length != 0) {
13944
- isFinalEvent = true;
13945
- return { done: false, value: remaining };
13946
- }
13947
- return doneResult;
13948
- }
13949
- const data = decoder.decode(value, { stream: true });
13950
- buffer += data;
13951
- const lines = buffer.split('\n');
13952
- for (let i = 0; i < lines.length - 1; i++) {
13953
- const l = lines[i].trim();
13954
- if (l.length > 0) {
13955
- pendingLines.push(l);
13956
- }
13957
- }
13958
- buffer = lines[lines.length - 1];
13959
- }
13960
- }
13961
- };
13962
- }
13963
- /**
13964
- * Splits a concatenated stream of BSON objects by emitting individual objects.
13965
- */
13966
- function extractBsonObjects(source) {
13967
- // Fully read but not emitted yet.
13968
- const completedObjects = [];
13969
- // Whether source has returned { done: true }. We do the same once completed objects have been emitted.
13970
- let isDone = false;
13971
- const lengthBuffer = new DataView(new ArrayBuffer(4));
13972
- let objectBody = null;
13973
- // If we're parsing the length field, a number between 1 and 4 (inclusive) describing remaining bytes in the header.
13974
- // If we're consuming a document, the bytes remaining.
13975
- let remainingLength = 4;
13976
- return {
13977
- async next() {
13978
- while (true) {
13979
- // Before fetching new data from upstream, return completed objects.
13980
- if (completedObjects.length) {
13981
- return valueResult(completedObjects.shift());
13982
- }
13983
- if (isDone) {
13984
- return doneResult;
13985
- }
13986
- const upstreamEvent = await source.next();
13987
- if (upstreamEvent.done) {
13988
- isDone = true;
13989
- if (objectBody || remainingLength != 4) {
13990
- throw new Error('illegal end of stream in BSON object');
13991
- }
13992
- return doneResult;
13993
- }
13994
- const chunk = upstreamEvent.value;
13995
- for (let i = 0; i < chunk.length;) {
13996
- const availableInData = chunk.length - i;
13997
- if (objectBody) {
13998
- // We're in the middle of reading a BSON document.
13999
- const bytesToRead = Math.min(availableInData, remainingLength);
14000
- const copySource = new Uint8Array(chunk.buffer, chunk.byteOffset + i, bytesToRead);
14001
- objectBody.set(copySource, objectBody.length - remainingLength);
14002
- i += bytesToRead;
14003
- remainingLength -= bytesToRead;
14004
- if (remainingLength == 0) {
14005
- completedObjects.push(objectBody);
14006
- // Prepare to read another document, starting with its length
14007
- objectBody = null;
14008
- remainingLength = 4;
14009
- }
14010
- }
14011
- else {
14012
- // Copy up to 4 bytes into lengthBuffer, depending on how many we still need.
14013
- const bytesToRead = Math.min(availableInData, remainingLength);
14014
- for (let j = 0; j < bytesToRead; j++) {
14015
- lengthBuffer.setUint8(4 - remainingLength + j, chunk[i + j]);
14016
- }
14017
- i += bytesToRead;
14018
- remainingLength -= bytesToRead;
14019
- if (remainingLength == 0) {
14020
- // Transition from reading length header to reading document. Subtracting 4 because the length of the
14021
- // header is included in length.
14022
- const length = lengthBuffer.getInt32(0, true /* little endian */);
14023
- remainingLength = length - 4;
14024
- if (remainingLength < 1) {
14025
- throw new Error(`invalid length for bson: ${length}`);
14026
- }
14027
- objectBody = new Uint8Array(length);
14028
- new DataView(objectBody.buffer).setInt32(0, length, true);
14029
- }
14030
- }
14031
- }
14032
- }
14033
- }
14034
- };
14035
- }
14036
-
14037
13974
  const POWERSYNC_TRAILING_SLASH_MATCH = /\/+$/;
14038
13975
  const POWERSYNC_JS_VERSION = PACKAGE.version;
14039
13976
  const SYNC_QUEUE_REQUEST_HIGH_WATER = 10;
@@ -14252,8 +14189,19 @@ class AbstractRemote {
14252
14189
  let pendingSocket = null;
14253
14190
  let keepAliveTimeout;
14254
14191
  let rsocket = null;
14255
- let queue = null;
14192
+ let paused = false;
14193
+ const queue = new EventQueue({
14194
+ eventDelivered: () => {
14195
+ if (queue.countOutstandingEvents <= SYNC_QUEUE_REQUEST_LOW_WATER) {
14196
+ paused = false;
14197
+ requestMore();
14198
+ }
14199
+ }
14200
+ });
14256
14201
  let didClose = false;
14202
+ let connectionEstablished = false;
14203
+ let pendingEventsCount = syncQueueRequestSize;
14204
+ let res = null;
14257
14205
  const abortRequest = () => {
14258
14206
  if (didClose) {
14259
14207
  return;
@@ -14266,10 +14214,23 @@ class AbstractRemote {
14266
14214
  if (rsocket) {
14267
14215
  rsocket.close();
14268
14216
  }
14269
- if (queue) {
14270
- queue.stop();
14271
- }
14217
+ // Send a bogus event to the queue to ensure a pending listener gets woken up. We check for didClose and would
14218
+ // return a doneEvent.
14219
+ queue.notify(null);
14272
14220
  };
14221
+ function push(event) {
14222
+ queue.notify(event);
14223
+ if (queue.countOutstandingEvents >= SYNC_QUEUE_REQUEST_HIGH_WATER) {
14224
+ paused = true;
14225
+ }
14226
+ }
14227
+ function requestMore() {
14228
+ const delta = syncQueueRequestSize - pendingEventsCount;
14229
+ if (!paused && delta > 0) {
14230
+ res?.request(delta);
14231
+ pendingEventsCount = syncQueueRequestSize;
14232
+ }
14233
+ }
14273
14234
  // Handle upstream abort
14274
14235
  if (options.abortSignal.aborted) {
14275
14236
  throw new AbortOperation('Connection request aborted');
@@ -14324,25 +14285,19 @@ class AbstractRemote {
14324
14285
  // Helps to prevent double close scenarios
14325
14286
  rsocket.onClose(() => (rsocket = null));
14326
14287
  return await new Promise((resolve, reject) => {
14327
- let connectionEstablished = false;
14328
- let pendingEventsCount = syncQueueRequestSize;
14329
- let paused = false;
14330
- let res = null;
14331
- function requestMore() {
14332
- const delta = syncQueueRequestSize - pendingEventsCount;
14333
- if (!paused && delta > 0) {
14334
- res?.request(delta);
14335
- pendingEventsCount = syncQueueRequestSize;
14288
+ const queueAsIterator = {
14289
+ next: async () => {
14290
+ if (didClose)
14291
+ return doneResult;
14292
+ const notification = await queue.waitForEvent(options.abortSignal);
14293
+ if (didClose) {
14294
+ return doneResult;
14295
+ }
14296
+ else {
14297
+ return valueResult(notification);
14298
+ }
14336
14299
  }
14337
- }
14338
- const events = new domExports.EventIterator((q) => {
14339
- queue = q;
14340
- q.on('highWater', () => (paused = true));
14341
- q.on('lowWater', () => {
14342
- paused = false;
14343
- requestMore();
14344
- });
14345
- }, { highWaterMark: SYNC_QUEUE_REQUEST_HIGH_WATER, lowWaterMark: SYNC_QUEUE_REQUEST_LOW_WATER })[symbolAsyncIterator]();
14300
+ };
14346
14301
  res = rsocket.requestStream({
14347
14302
  data: toBuffer(options.data),
14348
14303
  metadata: toBuffer({
@@ -14378,11 +14333,11 @@ class AbstractRemote {
14378
14333
  // The connection is active
14379
14334
  if (!connectionEstablished) {
14380
14335
  connectionEstablished = true;
14381
- resolve(events);
14336
+ resolve(queueAsIterator);
14382
14337
  }
14383
14338
  const { data } = payload;
14384
14339
  if (data) {
14385
- queue.push(data);
14340
+ push(data);
14386
14341
  }
14387
14342
  // Less events are now pending
14388
14343
  pendingEventsCount--;
@@ -16500,20 +16455,17 @@ SELECT * FROM crud_entries;
16500
16455
  * @returns An AsyncIterable that yields QueryResults whenever the data changes
16501
16456
  */
16502
16457
  watchWithAsyncGenerator(sql, parameters, options) {
16503
- return new domExports.EventIterator((eventOptions) => {
16458
+ return EventQueue.queueBasedAsyncIterable((queue, abort) => {
16504
16459
  const handler = {
16505
16460
  onResult: (result) => {
16506
- eventOptions.push(result);
16461
+ queue.notify(result);
16507
16462
  },
16508
16463
  onError: (error) => {
16509
- eventOptions.fail(error);
16464
+ queue.notifyError(error);
16510
16465
  }
16511
16466
  };
16512
- this.watchWithCallback(sql, parameters, handler, options);
16513
- options?.signal?.addEventListener('abort', () => {
16514
- eventOptions.stop();
16515
- });
16516
- });
16467
+ this.watchWithCallback(sql, parameters, handler, { ...options, signal: abort });
16468
+ }, options?.signal);
16517
16469
  }
16518
16470
  /**
16519
16471
  * Resolves the list of tables that are used in a SQL query.
@@ -16603,28 +16555,23 @@ SELECT * FROM crud_entries;
16603
16555
  * This is preferred over {@link AbstractPowerSyncDatabase.watchWithAsyncGenerator} when multiple queries need to be
16604
16556
  * performed together when data is changed.
16605
16557
  *
16606
- * Note: do not declare this as `async *onChange` as it will not work in React Native.
16607
- *
16608
16558
  * @param options - Options for configuring watch behavior
16609
16559
  * @returns An AsyncIterable that yields change events whenever the specified tables change
16610
16560
  */
16561
+ // Note: do not declare this as `async *onChange` as it will not work in React Native.
16611
16562
  onChangeWithAsyncGenerator(options) {
16612
- const resolvedOptions = options ?? {};
16613
- return new domExports.EventIterator((eventOptions) => {
16614
- const dispose = this.onChangeWithCallback({
16563
+ return EventQueue.queueBasedAsyncIterable((queue, abort) => {
16564
+ this.onChangeWithCallback({
16615
16565
  onChange: (event) => {
16616
- eventOptions.push(event);
16566
+ queue.notify(event);
16617
16567
  },
16618
16568
  onError: (error) => {
16619
- eventOptions.fail(error);
16569
+ queue.notifyError(error);
16620
16570
  }
16621
- }, options);
16622
- resolvedOptions.signal?.addEventListener('abort', () => {
16623
- eventOptions.stop();
16624
- // Maybe fail?
16625
- });
16626
- return () => dispose();
16627
- });
16571
+ }, { ...options, signal: abort });
16572
+ // Note: We don't have to track the dispose function returned by onChangeWithCallback, it cleans up
16573
+ // after the abort signal completes.
16574
+ }, options?.signal);
16628
16575
  }
16629
16576
  handleTableChanges(changedTables, watchedTables, onDetectedChanges) {
16630
16577
  if (changedTables.size > 0) {