@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.
@@ -3184,165 +3184,10 @@ var EncodingType;
3184
3184
  EncodingType["Base64"] = "base64";
3185
3185
  })(EncodingType || (EncodingType = {}));
3186
3186
 
3187
- const symbolAsyncIterator = Symbol.asyncIterator ?? Symbol.for('Symbol.asyncIterator');
3188
-
3189
3187
  function getDefaultExportFromCjs (x) {
3190
3188
  return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
3191
3189
  }
3192
3190
 
3193
- var dom = {};
3194
-
3195
- var eventIterator = {};
3196
-
3197
- var hasRequiredEventIterator;
3198
-
3199
- function requireEventIterator () {
3200
- if (hasRequiredEventIterator) return eventIterator;
3201
- hasRequiredEventIterator = 1;
3202
- Object.defineProperty(eventIterator, "__esModule", { value: true });
3203
- class EventQueue {
3204
- constructor() {
3205
- this.pullQueue = [];
3206
- this.pushQueue = [];
3207
- this.eventHandlers = {};
3208
- this.isPaused = false;
3209
- this.isStopped = false;
3210
- }
3211
- push(value) {
3212
- if (this.isStopped)
3213
- return;
3214
- const resolution = { value, done: false };
3215
- if (this.pullQueue.length) {
3216
- const placeholder = this.pullQueue.shift();
3217
- if (placeholder)
3218
- placeholder.resolve(resolution);
3219
- }
3220
- else {
3221
- this.pushQueue.push(Promise.resolve(resolution));
3222
- if (this.highWaterMark !== undefined &&
3223
- this.pushQueue.length >= this.highWaterMark &&
3224
- !this.isPaused) {
3225
- this.isPaused = true;
3226
- if (this.eventHandlers.highWater) {
3227
- this.eventHandlers.highWater();
3228
- }
3229
- else if (console) {
3230
- console.warn(`EventIterator queue reached ${this.pushQueue.length} items`);
3231
- }
3232
- }
3233
- }
3234
- }
3235
- stop() {
3236
- if (this.isStopped)
3237
- return;
3238
- this.isStopped = true;
3239
- this.remove();
3240
- for (const placeholder of this.pullQueue) {
3241
- placeholder.resolve({ value: undefined, done: true });
3242
- }
3243
- this.pullQueue.length = 0;
3244
- }
3245
- fail(error) {
3246
- if (this.isStopped)
3247
- return;
3248
- this.isStopped = true;
3249
- this.remove();
3250
- if (this.pullQueue.length) {
3251
- for (const placeholder of this.pullQueue) {
3252
- placeholder.reject(error);
3253
- }
3254
- this.pullQueue.length = 0;
3255
- }
3256
- else {
3257
- const rejection = Promise.reject(error);
3258
- /* Attach error handler to avoid leaking an unhandled promise rejection. */
3259
- rejection.catch(() => { });
3260
- this.pushQueue.push(rejection);
3261
- }
3262
- }
3263
- remove() {
3264
- Promise.resolve().then(() => {
3265
- if (this.removeCallback)
3266
- this.removeCallback();
3267
- });
3268
- }
3269
- [symbolAsyncIterator]() {
3270
- return {
3271
- next: (value) => {
3272
- const result = this.pushQueue.shift();
3273
- if (result) {
3274
- if (this.lowWaterMark !== undefined &&
3275
- this.pushQueue.length <= this.lowWaterMark &&
3276
- this.isPaused) {
3277
- this.isPaused = false;
3278
- if (this.eventHandlers.lowWater) {
3279
- this.eventHandlers.lowWater();
3280
- }
3281
- }
3282
- return result;
3283
- }
3284
- else if (this.isStopped) {
3285
- return Promise.resolve({ value: undefined, done: true });
3286
- }
3287
- else {
3288
- return new Promise((resolve, reject) => {
3289
- this.pullQueue.push({ resolve, reject });
3290
- });
3291
- }
3292
- },
3293
- return: () => {
3294
- this.isStopped = true;
3295
- this.pushQueue.length = 0;
3296
- this.remove();
3297
- return Promise.resolve({ value: undefined, done: true });
3298
- },
3299
- };
3300
- }
3301
- }
3302
- class EventIterator {
3303
- constructor(listen, { highWaterMark = 100, lowWaterMark = 1 } = {}) {
3304
- const queue = new EventQueue();
3305
- queue.highWaterMark = highWaterMark;
3306
- queue.lowWaterMark = lowWaterMark;
3307
- queue.removeCallback =
3308
- listen({
3309
- push: value => queue.push(value),
3310
- stop: () => queue.stop(),
3311
- fail: error => queue.fail(error),
3312
- on: (event, fn) => {
3313
- queue.eventHandlers[event] = fn;
3314
- },
3315
- }) || (() => { });
3316
- this[symbolAsyncIterator] = () => queue[symbolAsyncIterator]();
3317
- Object.freeze(this);
3318
- }
3319
- }
3320
- eventIterator.EventIterator = EventIterator;
3321
- eventIterator.default = EventIterator;
3322
- return eventIterator;
3323
- }
3324
-
3325
- var hasRequiredDom;
3326
-
3327
- function requireDom () {
3328
- if (hasRequiredDom) return dom;
3329
- hasRequiredDom = 1;
3330
- Object.defineProperty(dom, "__esModule", { value: true });
3331
- const event_iterator_1 = requireEventIterator();
3332
- dom.EventIterator = event_iterator_1.EventIterator;
3333
- function subscribe(event, options, evOptions) {
3334
- return new event_iterator_1.EventIterator(({ push }) => {
3335
- this.addEventListener(event, push, options);
3336
- return () => this.removeEventListener(event, push, options);
3337
- }, evOptions);
3338
- }
3339
- dom.subscribe = subscribe;
3340
- dom.default = event_iterator_1.EventIterator;
3341
- return dom;
3342
- }
3343
-
3344
- var domExports = requireDom();
3345
-
3346
3191
  var logger$1 = {exports: {}};
3347
3192
 
3348
3193
  /*!
@@ -4226,6 +4071,210 @@ class ControlledExecutor {
4226
4071
  }
4227
4072
  }
4228
4073
 
4074
+ /**
4075
+ * Some JavaScript engines, in particular older versions of React Native, don't support Symbol.asyncIterator.
4076
+ *
4077
+ * 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).
4078
+ * This definition is compatible with that polyfill, so transpiled apps can use async iterables created by the PowerSync
4079
+ * SDK.
4080
+ */
4081
+ const symbolAsyncIterator = Symbol.asyncIterator ?? Symbol.for('Symbol.asyncIterator');
4082
+
4083
+ const doneResult = { done: true, value: undefined };
4084
+ function valueResult(value) {
4085
+ return { done: false, value };
4086
+ }
4087
+ /**
4088
+ * Expands a source async iterator by allowing to inject events asynchronously.
4089
+ *
4090
+ * The resulting iterator will emit all events from its source. Additionally though, events can be injected. These
4091
+ * events are dropped once the main iterator completes, but are otherwise forwarded.
4092
+ *
4093
+ * The iterator completes when its source completes, and it supports backpressure by only calling `next()` on the source
4094
+ * in response to a `next()` call from downstream if no pending injected events can be dispatched.
4095
+ */
4096
+ function injectable(source) {
4097
+ let sourceIsDone = false;
4098
+ let waiter = undefined; // An active, waiting next() call.
4099
+ // A pending upstream event that couldn't be dispatched because inject() has been called before it was resolved.
4100
+ let pendingSourceEvent = null;
4101
+ let sourceFetchInFlight = false;
4102
+ let pendingInjectedEvents = [];
4103
+ const consumeWaiter = () => {
4104
+ const pending = waiter;
4105
+ waiter = undefined;
4106
+ return pending;
4107
+ };
4108
+ const fetchFromSource = () => {
4109
+ const resolveWaiter = (propagate) => {
4110
+ sourceFetchInFlight = false;
4111
+ const active = consumeWaiter();
4112
+ if (active) {
4113
+ propagate(active);
4114
+ }
4115
+ else {
4116
+ pendingSourceEvent = propagate;
4117
+ }
4118
+ };
4119
+ sourceFetchInFlight = true;
4120
+ const nextFromSource = source.next();
4121
+ nextFromSource.then((value) => {
4122
+ sourceIsDone = value.done == true;
4123
+ resolveWaiter((w) => w.resolve(value));
4124
+ }, (error) => {
4125
+ resolveWaiter((w) => w.reject(error));
4126
+ });
4127
+ };
4128
+ return {
4129
+ next: () => {
4130
+ return new Promise((resolve, reject) => {
4131
+ // First priority: Dispatch ready upstream events.
4132
+ if (sourceIsDone) {
4133
+ return resolve(doneResult);
4134
+ }
4135
+ if (pendingSourceEvent) {
4136
+ pendingSourceEvent({ resolve, reject });
4137
+ pendingSourceEvent = null;
4138
+ return;
4139
+ }
4140
+ // Second priority: Dispatch injected events
4141
+ if (pendingInjectedEvents.length) {
4142
+ return resolve(valueResult(pendingInjectedEvents.shift()));
4143
+ }
4144
+ // Nothing pending? Fetch from source
4145
+ waiter = { resolve, reject };
4146
+ if (!sourceFetchInFlight) {
4147
+ fetchFromSource();
4148
+ }
4149
+ });
4150
+ },
4151
+ inject: (event) => {
4152
+ const pending = consumeWaiter();
4153
+ if (pending != null) {
4154
+ pending.resolve(valueResult(event));
4155
+ }
4156
+ else {
4157
+ pendingInjectedEvents.push(event);
4158
+ }
4159
+ }
4160
+ };
4161
+ }
4162
+ /**
4163
+ * Splits a byte stream at line endings, emitting each line as a string.
4164
+ */
4165
+ function extractJsonLines(source, decoder) {
4166
+ let buffer = '';
4167
+ const pendingLines = [];
4168
+ let isFinalEvent = false;
4169
+ return {
4170
+ next: async () => {
4171
+ while (true) {
4172
+ if (isFinalEvent) {
4173
+ return doneResult;
4174
+ }
4175
+ {
4176
+ const first = pendingLines.shift();
4177
+ if (first) {
4178
+ return { done: false, value: first };
4179
+ }
4180
+ }
4181
+ const { done, value } = await source.next();
4182
+ if (done) {
4183
+ const remaining = buffer.trim();
4184
+ if (remaining.length != 0) {
4185
+ isFinalEvent = true;
4186
+ return { done: false, value: remaining };
4187
+ }
4188
+ return doneResult;
4189
+ }
4190
+ const data = decoder.decode(value, { stream: true });
4191
+ buffer += data;
4192
+ const lines = buffer.split('\n');
4193
+ for (let i = 0; i < lines.length - 1; i++) {
4194
+ const l = lines[i].trim();
4195
+ if (l.length > 0) {
4196
+ pendingLines.push(l);
4197
+ }
4198
+ }
4199
+ buffer = lines[lines.length - 1];
4200
+ }
4201
+ }
4202
+ };
4203
+ }
4204
+ /**
4205
+ * Splits a concatenated stream of BSON objects by emitting individual objects.
4206
+ */
4207
+ function extractBsonObjects(source) {
4208
+ // Fully read but not emitted yet.
4209
+ const completedObjects = [];
4210
+ // Whether source has returned { done: true }. We do the same once completed objects have been emitted.
4211
+ let isDone = false;
4212
+ const lengthBuffer = new DataView(new ArrayBuffer(4));
4213
+ let objectBody = null;
4214
+ // If we're parsing the length field, a number between 1 and 4 (inclusive) describing remaining bytes in the header.
4215
+ // If we're consuming a document, the bytes remaining.
4216
+ let remainingLength = 4;
4217
+ return {
4218
+ async next() {
4219
+ while (true) {
4220
+ // Before fetching new data from upstream, return completed objects.
4221
+ if (completedObjects.length) {
4222
+ return valueResult(completedObjects.shift());
4223
+ }
4224
+ if (isDone) {
4225
+ return doneResult;
4226
+ }
4227
+ const upstreamEvent = await source.next();
4228
+ if (upstreamEvent.done) {
4229
+ isDone = true;
4230
+ if (objectBody || remainingLength != 4) {
4231
+ throw new Error('illegal end of stream in BSON object');
4232
+ }
4233
+ return doneResult;
4234
+ }
4235
+ const chunk = upstreamEvent.value;
4236
+ for (let i = 0; i < chunk.length;) {
4237
+ const availableInData = chunk.length - i;
4238
+ if (objectBody) {
4239
+ // We're in the middle of reading a BSON document.
4240
+ const bytesToRead = Math.min(availableInData, remainingLength);
4241
+ const copySource = new Uint8Array(chunk.buffer, chunk.byteOffset + i, bytesToRead);
4242
+ objectBody.set(copySource, objectBody.length - remainingLength);
4243
+ i += bytesToRead;
4244
+ remainingLength -= bytesToRead;
4245
+ if (remainingLength == 0) {
4246
+ completedObjects.push(objectBody);
4247
+ // Prepare to read another document, starting with its length
4248
+ objectBody = null;
4249
+ remainingLength = 4;
4250
+ }
4251
+ }
4252
+ else {
4253
+ // Copy up to 4 bytes into lengthBuffer, depending on how many we still need.
4254
+ const bytesToRead = Math.min(availableInData, remainingLength);
4255
+ for (let j = 0; j < bytesToRead; j++) {
4256
+ lengthBuffer.setUint8(4 - remainingLength + j, chunk[i + j]);
4257
+ }
4258
+ i += bytesToRead;
4259
+ remainingLength -= bytesToRead;
4260
+ if (remainingLength == 0) {
4261
+ // Transition from reading length header to reading document. Subtracting 4 because the length of the
4262
+ // header is included in length.
4263
+ const length = lengthBuffer.getInt32(0, true /* little endian */);
4264
+ remainingLength = length - 4;
4265
+ if (remainingLength < 1) {
4266
+ throw new Error(`invalid length for bson: ${length}`);
4267
+ }
4268
+ objectBody = new Uint8Array(length);
4269
+ new DataView(objectBody.buffer).setInt32(0, length, true);
4270
+ }
4271
+ }
4272
+ }
4273
+ }
4274
+ }
4275
+ };
4276
+ }
4277
+
4229
4278
  /**
4230
4279
  * Throttle a function to be called at most once every "wait" milliseconds,
4231
4280
  * on the trailing edge.
@@ -4245,45 +4294,128 @@ function throttleTrailing(func, wait) {
4245
4294
  };
4246
4295
  }
4247
4296
  function asyncNotifier() {
4248
- let waitingConsumer = null;
4249
- let hasPendingNotification = false;
4297
+ const queue = new EventQueue();
4250
4298
  return {
4251
4299
  notify() {
4252
- if (waitingConsumer != null) {
4253
- waitingConsumer();
4254
- waitingConsumer = null;
4300
+ if (queue.countOutstandingEvents > 0) ;
4301
+ else {
4302
+ queue.notify();
4303
+ }
4304
+ },
4305
+ waitForNotification(signal) {
4306
+ return queue.waitForEvent(signal);
4307
+ }
4308
+ };
4309
+ }
4310
+ class EventQueue {
4311
+ options;
4312
+ waitingConsumer;
4313
+ outstandingEvents;
4314
+ constructor(options = {}) {
4315
+ this.options = options;
4316
+ this.outstandingEvents = [];
4317
+ }
4318
+ /**
4319
+ * The amount of buffered events not yet dispatched to listeners.
4320
+ */
4321
+ get countOutstandingEvents() {
4322
+ return this.outstandingEvents.length;
4323
+ }
4324
+ notifyInner(dispatch) {
4325
+ const existing = this.waitingConsumer;
4326
+ this.waitingConsumer = undefined;
4327
+ const dispatchAndNotifyListeners = (waiter) => {
4328
+ dispatch(waiter);
4329
+ this.options.eventDelivered?.();
4330
+ };
4331
+ if (existing) {
4332
+ dispatchAndNotifyListeners(existing);
4333
+ }
4334
+ else {
4335
+ this.outstandingEvents.push(dispatchAndNotifyListeners);
4336
+ }
4337
+ }
4338
+ notify(value) {
4339
+ this.notifyInner((l) => l.resolve(value));
4340
+ }
4341
+ notifyError(error) {
4342
+ this.notifyInner((l) => l.reject(error));
4343
+ }
4344
+ waitForEvent(signal) {
4345
+ return new Promise((resolve, reject) => {
4346
+ if (this.waitingConsumer != null) {
4347
+ throw new Error('Illegal call to waitForEvent, already has a waiter.');
4348
+ }
4349
+ const complete = () => {
4350
+ signal?.removeEventListener('abort', onAbort);
4351
+ };
4352
+ const onAbort = () => {
4353
+ complete();
4354
+ this.waitingConsumer = undefined;
4355
+ resolve(undefined);
4356
+ };
4357
+ const waiter = {
4358
+ resolve: (value) => {
4359
+ complete();
4360
+ resolve(value);
4361
+ },
4362
+ reject: (error) => {
4363
+ complete();
4364
+ reject(error);
4365
+ }
4366
+ };
4367
+ if (signal.aborted) {
4368
+ resolve(undefined);
4369
+ }
4370
+ else if (this.countOutstandingEvents > 0) {
4371
+ const [event] = this.outstandingEvents.splice(0, 1);
4372
+ event(waiter);
4255
4373
  }
4256
4374
  else {
4257
- hasPendingNotification = true;
4375
+ this.waitingConsumer = waiter;
4376
+ signal.addEventListener('abort', onAbort);
4258
4377
  }
4259
- },
4260
- waitForNotification(signal) {
4261
- return new Promise((resolve) => {
4262
- if (waitingConsumer != null) {
4263
- throw new Error('Illegal call to waitForNotification, already has a waiter.');
4264
- }
4265
- if (signal.aborted) {
4266
- resolve();
4267
- }
4268
- else if (hasPendingNotification) {
4269
- resolve();
4270
- hasPendingNotification = false;
4378
+ });
4379
+ }
4380
+ /**
4381
+ * Creates an async iterable backed by event queues.
4382
+ *
4383
+ * @param run A function invoked for every new listener. It receives a queue backing the async iterator.
4384
+ * @param abort An additional abort signal. The `run` callback will also be aborted when `AsyncIterator.return` is
4385
+ * called.
4386
+ * @returns An object conforming to the async iterable protocol.
4387
+ */
4388
+ static queueBasedAsyncIterable(run, abort) {
4389
+ return {
4390
+ [symbolAsyncIterator]: () => {
4391
+ const queue = new EventQueue();
4392
+ const controller = new AbortController();
4393
+ function dispose() {
4394
+ controller.abort();
4395
+ abort?.removeEventListener('abort', dispose);
4271
4396
  }
4272
- else {
4273
- function complete() {
4274
- signal.removeEventListener('abort', onAbort);
4275
- resolve();
4397
+ if (abort) {
4398
+ if (abort.aborted) {
4399
+ controller.abort();
4276
4400
  }
4277
- function onAbort() {
4278
- waitingConsumer = null;
4279
- resolve();
4401
+ else {
4402
+ abort.addEventListener('abort', dispose);
4280
4403
  }
4281
- waitingConsumer = complete;
4282
- signal.addEventListener('abort', onAbort);
4283
4404
  }
4284
- });
4285
- }
4286
- };
4405
+ run(queue, controller.signal);
4406
+ return {
4407
+ async next() {
4408
+ const event = await queue.waitForEvent(controller.signal);
4409
+ return event == null ? doneResult : valueResult(event);
4410
+ },
4411
+ async return() {
4412
+ dispose();
4413
+ return doneResult;
4414
+ }
4415
+ };
4416
+ }
4417
+ };
4418
+ }
4287
4419
  }
4288
4420
 
4289
4421
  /**
@@ -12514,7 +12646,7 @@ function requireDist () {
12514
12646
 
12515
12647
  var distExports = requireDist();
12516
12648
 
12517
- var version = "1.54.0";
12649
+ var version = "1.55.0";
12518
12650
  var PACKAGE = {
12519
12651
  version: version};
12520
12652
 
@@ -12681,201 +12813,6 @@ class WebsocketClientTransport {
12681
12813
  }
12682
12814
  }
12683
12815
 
12684
- const doneResult = { done: true, value: undefined };
12685
- function valueResult(value) {
12686
- return { done: false, value };
12687
- }
12688
- /**
12689
- * Expands a source async iterator by allowing to inject events asynchronously.
12690
- *
12691
- * The resulting iterator will emit all events from its source. Additionally though, events can be injected. These
12692
- * events are dropped once the main iterator completes, but are otherwise forwarded.
12693
- *
12694
- * The iterator completes when its source completes, and it supports backpressure by only calling `next()` on the source
12695
- * in response to a `next()` call from downstream if no pending injected events can be dispatched.
12696
- */
12697
- function injectable(source) {
12698
- let sourceIsDone = false;
12699
- let waiter = undefined; // An active, waiting next() call.
12700
- // A pending upstream event that couldn't be dispatched because inject() has been called before it was resolved.
12701
- let pendingSourceEvent = null;
12702
- let sourceFetchInFlight = false;
12703
- let pendingInjectedEvents = [];
12704
- const consumeWaiter = () => {
12705
- const pending = waiter;
12706
- waiter = undefined;
12707
- return pending;
12708
- };
12709
- const fetchFromSource = () => {
12710
- const resolveWaiter = (propagate) => {
12711
- sourceFetchInFlight = false;
12712
- const active = consumeWaiter();
12713
- if (active) {
12714
- propagate(active);
12715
- }
12716
- else {
12717
- pendingSourceEvent = propagate;
12718
- }
12719
- };
12720
- sourceFetchInFlight = true;
12721
- const nextFromSource = source.next();
12722
- nextFromSource.then((value) => {
12723
- sourceIsDone = value.done == true;
12724
- resolveWaiter((w) => w.resolve(value));
12725
- }, (error) => {
12726
- resolveWaiter((w) => w.reject(error));
12727
- });
12728
- };
12729
- return {
12730
- next: () => {
12731
- return new Promise((resolve, reject) => {
12732
- // First priority: Dispatch ready upstream events.
12733
- if (sourceIsDone) {
12734
- return resolve(doneResult);
12735
- }
12736
- if (pendingSourceEvent) {
12737
- pendingSourceEvent({ resolve, reject });
12738
- pendingSourceEvent = null;
12739
- return;
12740
- }
12741
- // Second priority: Dispatch injected events
12742
- if (pendingInjectedEvents.length) {
12743
- return resolve(valueResult(pendingInjectedEvents.shift()));
12744
- }
12745
- // Nothing pending? Fetch from source
12746
- waiter = { resolve, reject };
12747
- if (!sourceFetchInFlight) {
12748
- fetchFromSource();
12749
- }
12750
- });
12751
- },
12752
- inject: (event) => {
12753
- const pending = consumeWaiter();
12754
- if (pending != null) {
12755
- pending.resolve(valueResult(event));
12756
- }
12757
- else {
12758
- pendingInjectedEvents.push(event);
12759
- }
12760
- }
12761
- };
12762
- }
12763
- /**
12764
- * Splits a byte stream at line endings, emitting each line as a string.
12765
- */
12766
- function extractJsonLines(source, decoder) {
12767
- let buffer = '';
12768
- const pendingLines = [];
12769
- let isFinalEvent = false;
12770
- return {
12771
- next: async () => {
12772
- while (true) {
12773
- if (isFinalEvent) {
12774
- return doneResult;
12775
- }
12776
- {
12777
- const first = pendingLines.shift();
12778
- if (first) {
12779
- return { done: false, value: first };
12780
- }
12781
- }
12782
- const { done, value } = await source.next();
12783
- if (done) {
12784
- const remaining = buffer.trim();
12785
- if (remaining.length != 0) {
12786
- isFinalEvent = true;
12787
- return { done: false, value: remaining };
12788
- }
12789
- return doneResult;
12790
- }
12791
- const data = decoder.decode(value, { stream: true });
12792
- buffer += data;
12793
- const lines = buffer.split('\n');
12794
- for (let i = 0; i < lines.length - 1; i++) {
12795
- const l = lines[i].trim();
12796
- if (l.length > 0) {
12797
- pendingLines.push(l);
12798
- }
12799
- }
12800
- buffer = lines[lines.length - 1];
12801
- }
12802
- }
12803
- };
12804
- }
12805
- /**
12806
- * Splits a concatenated stream of BSON objects by emitting individual objects.
12807
- */
12808
- function extractBsonObjects(source) {
12809
- // Fully read but not emitted yet.
12810
- const completedObjects = [];
12811
- // Whether source has returned { done: true }. We do the same once completed objects have been emitted.
12812
- let isDone = false;
12813
- const lengthBuffer = new DataView(new ArrayBuffer(4));
12814
- let objectBody = null;
12815
- // If we're parsing the length field, a number between 1 and 4 (inclusive) describing remaining bytes in the header.
12816
- // If we're consuming a document, the bytes remaining.
12817
- let remainingLength = 4;
12818
- return {
12819
- async next() {
12820
- while (true) {
12821
- // Before fetching new data from upstream, return completed objects.
12822
- if (completedObjects.length) {
12823
- return valueResult(completedObjects.shift());
12824
- }
12825
- if (isDone) {
12826
- return doneResult;
12827
- }
12828
- const upstreamEvent = await source.next();
12829
- if (upstreamEvent.done) {
12830
- isDone = true;
12831
- if (objectBody || remainingLength != 4) {
12832
- throw new Error('illegal end of stream in BSON object');
12833
- }
12834
- return doneResult;
12835
- }
12836
- const chunk = upstreamEvent.value;
12837
- for (let i = 0; i < chunk.length;) {
12838
- const availableInData = chunk.length - i;
12839
- if (objectBody) {
12840
- // We're in the middle of reading a BSON document.
12841
- const bytesToRead = Math.min(availableInData, remainingLength);
12842
- const copySource = new Uint8Array(chunk.buffer, chunk.byteOffset + i, bytesToRead);
12843
- objectBody.set(copySource, objectBody.length - remainingLength);
12844
- i += bytesToRead;
12845
- remainingLength -= bytesToRead;
12846
- if (remainingLength == 0) {
12847
- completedObjects.push(objectBody);
12848
- // Prepare to read another document, starting with its length
12849
- objectBody = null;
12850
- remainingLength = 4;
12851
- }
12852
- }
12853
- else {
12854
- // Copy up to 4 bytes into lengthBuffer, depending on how many we still need.
12855
- const bytesToRead = Math.min(availableInData, remainingLength);
12856
- for (let j = 0; j < bytesToRead; j++) {
12857
- lengthBuffer.setUint8(4 - remainingLength + j, chunk[i + j]);
12858
- }
12859
- i += bytesToRead;
12860
- remainingLength -= bytesToRead;
12861
- if (remainingLength == 0) {
12862
- // Transition from reading length header to reading document. Subtracting 4 because the length of the
12863
- // header is included in length.
12864
- const length = lengthBuffer.getInt32(0, true /* little endian */);
12865
- remainingLength = length - 4;
12866
- if (remainingLength < 1) {
12867
- throw new Error(`invalid length for bson: ${length}`);
12868
- }
12869
- objectBody = new Uint8Array(length);
12870
- new DataView(objectBody.buffer).setInt32(0, length, true);
12871
- }
12872
- }
12873
- }
12874
- }
12875
- }
12876
- };
12877
- }
12878
-
12879
12816
  const POWERSYNC_TRAILING_SLASH_MATCH = /\/+$/;
12880
12817
  const POWERSYNC_JS_VERSION = PACKAGE.version;
12881
12818
  const SYNC_QUEUE_REQUEST_HIGH_WATER = 10;
@@ -13094,8 +13031,19 @@ class AbstractRemote {
13094
13031
  let pendingSocket = null;
13095
13032
  let keepAliveTimeout;
13096
13033
  let rsocket = null;
13097
- let queue = null;
13034
+ let paused = false;
13035
+ const queue = new EventQueue({
13036
+ eventDelivered: () => {
13037
+ if (queue.countOutstandingEvents <= SYNC_QUEUE_REQUEST_LOW_WATER) {
13038
+ paused = false;
13039
+ requestMore();
13040
+ }
13041
+ }
13042
+ });
13098
13043
  let didClose = false;
13044
+ let connectionEstablished = false;
13045
+ let pendingEventsCount = syncQueueRequestSize;
13046
+ let res = null;
13099
13047
  const abortRequest = () => {
13100
13048
  if (didClose) {
13101
13049
  return;
@@ -13108,10 +13056,23 @@ class AbstractRemote {
13108
13056
  if (rsocket) {
13109
13057
  rsocket.close();
13110
13058
  }
13111
- if (queue) {
13112
- queue.stop();
13113
- }
13059
+ // Send a bogus event to the queue to ensure a pending listener gets woken up. We check for didClose and would
13060
+ // return a doneEvent.
13061
+ queue.notify(null);
13114
13062
  };
13063
+ function push(event) {
13064
+ queue.notify(event);
13065
+ if (queue.countOutstandingEvents >= SYNC_QUEUE_REQUEST_HIGH_WATER) {
13066
+ paused = true;
13067
+ }
13068
+ }
13069
+ function requestMore() {
13070
+ const delta = syncQueueRequestSize - pendingEventsCount;
13071
+ if (!paused && delta > 0) {
13072
+ res?.request(delta);
13073
+ pendingEventsCount = syncQueueRequestSize;
13074
+ }
13075
+ }
13115
13076
  // Handle upstream abort
13116
13077
  if (options.abortSignal.aborted) {
13117
13078
  throw new AbortOperation('Connection request aborted');
@@ -13166,25 +13127,19 @@ class AbstractRemote {
13166
13127
  // Helps to prevent double close scenarios
13167
13128
  rsocket.onClose(() => (rsocket = null));
13168
13129
  return await new Promise((resolve, reject) => {
13169
- let connectionEstablished = false;
13170
- let pendingEventsCount = syncQueueRequestSize;
13171
- let paused = false;
13172
- let res = null;
13173
- function requestMore() {
13174
- const delta = syncQueueRequestSize - pendingEventsCount;
13175
- if (!paused && delta > 0) {
13176
- res?.request(delta);
13177
- pendingEventsCount = syncQueueRequestSize;
13130
+ const queueAsIterator = {
13131
+ next: async () => {
13132
+ if (didClose)
13133
+ return doneResult;
13134
+ const notification = await queue.waitForEvent(options.abortSignal);
13135
+ if (didClose) {
13136
+ return doneResult;
13137
+ }
13138
+ else {
13139
+ return valueResult(notification);
13140
+ }
13178
13141
  }
13179
- }
13180
- const events = new domExports.EventIterator((q) => {
13181
- queue = q;
13182
- q.on('highWater', () => (paused = true));
13183
- q.on('lowWater', () => {
13184
- paused = false;
13185
- requestMore();
13186
- });
13187
- }, { highWaterMark: SYNC_QUEUE_REQUEST_HIGH_WATER, lowWaterMark: SYNC_QUEUE_REQUEST_LOW_WATER })[symbolAsyncIterator]();
13142
+ };
13188
13143
  res = rsocket.requestStream({
13189
13144
  data: toBuffer(options.data),
13190
13145
  metadata: toBuffer({
@@ -13220,11 +13175,11 @@ class AbstractRemote {
13220
13175
  // The connection is active
13221
13176
  if (!connectionEstablished) {
13222
13177
  connectionEstablished = true;
13223
- resolve(events);
13178
+ resolve(queueAsIterator);
13224
13179
  }
13225
13180
  const { data } = payload;
13226
13181
  if (data) {
13227
- queue.push(data);
13182
+ push(data);
13228
13183
  }
13229
13184
  // Less events are now pending
13230
13185
  pendingEventsCount--;
@@ -15342,20 +15297,17 @@ SELECT * FROM crud_entries;
15342
15297
  * @returns An AsyncIterable that yields QueryResults whenever the data changes
15343
15298
  */
15344
15299
  watchWithAsyncGenerator(sql, parameters, options) {
15345
- return new domExports.EventIterator((eventOptions) => {
15300
+ return EventQueue.queueBasedAsyncIterable((queue, abort) => {
15346
15301
  const handler = {
15347
15302
  onResult: (result) => {
15348
- eventOptions.push(result);
15303
+ queue.notify(result);
15349
15304
  },
15350
15305
  onError: (error) => {
15351
- eventOptions.fail(error);
15306
+ queue.notifyError(error);
15352
15307
  }
15353
15308
  };
15354
- this.watchWithCallback(sql, parameters, handler, options);
15355
- options?.signal?.addEventListener('abort', () => {
15356
- eventOptions.stop();
15357
- });
15358
- });
15309
+ this.watchWithCallback(sql, parameters, handler, { ...options, signal: abort });
15310
+ }, options?.signal);
15359
15311
  }
15360
15312
  /**
15361
15313
  * Resolves the list of tables that are used in a SQL query.
@@ -15445,28 +15397,23 @@ SELECT * FROM crud_entries;
15445
15397
  * This is preferred over {@link AbstractPowerSyncDatabase.watchWithAsyncGenerator} when multiple queries need to be
15446
15398
  * performed together when data is changed.
15447
15399
  *
15448
- * Note: do not declare this as `async *onChange` as it will not work in React Native.
15449
- *
15450
15400
  * @param options - Options for configuring watch behavior
15451
15401
  * @returns An AsyncIterable that yields change events whenever the specified tables change
15452
15402
  */
15403
+ // Note: do not declare this as `async *onChange` as it will not work in React Native.
15453
15404
  onChangeWithAsyncGenerator(options) {
15454
- const resolvedOptions = options ?? {};
15455
- return new domExports.EventIterator((eventOptions) => {
15456
- const dispose = this.onChangeWithCallback({
15405
+ return EventQueue.queueBasedAsyncIterable((queue, abort) => {
15406
+ this.onChangeWithCallback({
15457
15407
  onChange: (event) => {
15458
- eventOptions.push(event);
15408
+ queue.notify(event);
15459
15409
  },
15460
15410
  onError: (error) => {
15461
- eventOptions.fail(error);
15411
+ queue.notifyError(error);
15462
15412
  }
15463
- }, options);
15464
- resolvedOptions.signal?.addEventListener('abort', () => {
15465
- eventOptions.stop();
15466
- // Maybe fail?
15467
- });
15468
- return () => dispose();
15469
- });
15413
+ }, { ...options, signal: abort });
15414
+ // Note: We don't have to track the dispose function returned by onChangeWithCallback, it cleans up
15415
+ // after the abort signal completes.
15416
+ }, options?.signal);
15470
15417
  }
15471
15418
  handleTableChanges(changedTables, watchedTables, onDetectedChanges) {
15472
15419
  if (changedTables.size > 0) {