@powersync/web 1.38.3 → 1.38.5
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/worker/SharedSyncImplementation.umd.js +384 -434
- package/dist/worker/SharedSyncImplementation.umd.js.map +1 -1
- package/dist/worker/WASQLiteDB.umd.js +384 -434
- package/dist/worker/WASQLiteDB.umd.js.map +1 -1
- package/lib/package.json +2 -2
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
|
@@ -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
|
/*!
|
|
@@ -5183,11 +5028,7 @@ class SyncStatus {
|
|
|
5183
5028
|
*/
|
|
5184
5029
|
const replacer = (_, value) => {
|
|
5185
5030
|
if (value instanceof Error) {
|
|
5186
|
-
return
|
|
5187
|
-
name: value.name,
|
|
5188
|
-
message: value.message,
|
|
5189
|
-
stack: value.stack
|
|
5190
|
-
};
|
|
5031
|
+
return this.serializeError(value);
|
|
5191
5032
|
}
|
|
5192
5033
|
return value;
|
|
5193
5034
|
};
|
|
@@ -5230,11 +5071,18 @@ class SyncStatus {
|
|
|
5230
5071
|
if (typeof error == 'undefined') {
|
|
5231
5072
|
return undefined;
|
|
5232
5073
|
}
|
|
5233
|
-
|
|
5074
|
+
const serialized = {
|
|
5234
5075
|
name: error.name,
|
|
5235
5076
|
message: error.message,
|
|
5236
5077
|
stack: error.stack
|
|
5237
5078
|
};
|
|
5079
|
+
// `Error.cause` can be any value (the spec types it as unknown). Preserve it
|
|
5080
|
+
// so consumers reading uploadError/downloadError keep the failure context.
|
|
5081
|
+
// Recurse for Error causes so the whole chain is flattened the same way.
|
|
5082
|
+
if (typeof error.cause != 'undefined') {
|
|
5083
|
+
serialized.cause = error.cause instanceof Error ? this.serializeError(error.cause) : error.cause;
|
|
5084
|
+
}
|
|
5085
|
+
return serialized;
|
|
5238
5086
|
}
|
|
5239
5087
|
static comparePriorities(a, b) {
|
|
5240
5088
|
return b.priority - a.priority; // Reverse because higher priorities have lower numbers
|
|
@@ -5384,6 +5232,210 @@ class ControlledExecutor {
|
|
|
5384
5232
|
}
|
|
5385
5233
|
}
|
|
5386
5234
|
|
|
5235
|
+
/**
|
|
5236
|
+
* Some JavaScript engines, in particular older versions of React Native, don't support Symbol.asyncIterator.
|
|
5237
|
+
*
|
|
5238
|
+
* 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).
|
|
5239
|
+
* This definition is compatible with that polyfill, so transpiled apps can use async iterables created by the PowerSync
|
|
5240
|
+
* SDK.
|
|
5241
|
+
*/
|
|
5242
|
+
const symbolAsyncIterator = Symbol.asyncIterator ?? Symbol.for('Symbol.asyncIterator');
|
|
5243
|
+
|
|
5244
|
+
const doneResult = { done: true, value: undefined };
|
|
5245
|
+
function valueResult(value) {
|
|
5246
|
+
return { done: false, value };
|
|
5247
|
+
}
|
|
5248
|
+
/**
|
|
5249
|
+
* Expands a source async iterator by allowing to inject events asynchronously.
|
|
5250
|
+
*
|
|
5251
|
+
* The resulting iterator will emit all events from its source. Additionally though, events can be injected. These
|
|
5252
|
+
* events are dropped once the main iterator completes, but are otherwise forwarded.
|
|
5253
|
+
*
|
|
5254
|
+
* The iterator completes when its source completes, and it supports backpressure by only calling `next()` on the source
|
|
5255
|
+
* in response to a `next()` call from downstream if no pending injected events can be dispatched.
|
|
5256
|
+
*/
|
|
5257
|
+
function injectable(source) {
|
|
5258
|
+
let sourceIsDone = false;
|
|
5259
|
+
let waiter = undefined; // An active, waiting next() call.
|
|
5260
|
+
// A pending upstream event that couldn't be dispatched because inject() has been called before it was resolved.
|
|
5261
|
+
let pendingSourceEvent = null;
|
|
5262
|
+
let sourceFetchInFlight = false;
|
|
5263
|
+
let pendingInjectedEvents = [];
|
|
5264
|
+
const consumeWaiter = () => {
|
|
5265
|
+
const pending = waiter;
|
|
5266
|
+
waiter = undefined;
|
|
5267
|
+
return pending;
|
|
5268
|
+
};
|
|
5269
|
+
const fetchFromSource = () => {
|
|
5270
|
+
const resolveWaiter = (propagate) => {
|
|
5271
|
+
sourceFetchInFlight = false;
|
|
5272
|
+
const active = consumeWaiter();
|
|
5273
|
+
if (active) {
|
|
5274
|
+
propagate(active);
|
|
5275
|
+
}
|
|
5276
|
+
else {
|
|
5277
|
+
pendingSourceEvent = propagate;
|
|
5278
|
+
}
|
|
5279
|
+
};
|
|
5280
|
+
sourceFetchInFlight = true;
|
|
5281
|
+
const nextFromSource = source.next();
|
|
5282
|
+
nextFromSource.then((value) => {
|
|
5283
|
+
sourceIsDone = value.done == true;
|
|
5284
|
+
resolveWaiter((w) => w.resolve(value));
|
|
5285
|
+
}, (error) => {
|
|
5286
|
+
resolveWaiter((w) => w.reject(error));
|
|
5287
|
+
});
|
|
5288
|
+
};
|
|
5289
|
+
return {
|
|
5290
|
+
next: () => {
|
|
5291
|
+
return new Promise((resolve, reject) => {
|
|
5292
|
+
// First priority: Dispatch ready upstream events.
|
|
5293
|
+
if (sourceIsDone) {
|
|
5294
|
+
return resolve(doneResult);
|
|
5295
|
+
}
|
|
5296
|
+
if (pendingSourceEvent) {
|
|
5297
|
+
pendingSourceEvent({ resolve, reject });
|
|
5298
|
+
pendingSourceEvent = null;
|
|
5299
|
+
return;
|
|
5300
|
+
}
|
|
5301
|
+
// Second priority: Dispatch injected events
|
|
5302
|
+
if (pendingInjectedEvents.length) {
|
|
5303
|
+
return resolve(valueResult(pendingInjectedEvents.shift()));
|
|
5304
|
+
}
|
|
5305
|
+
// Nothing pending? Fetch from source
|
|
5306
|
+
waiter = { resolve, reject };
|
|
5307
|
+
if (!sourceFetchInFlight) {
|
|
5308
|
+
fetchFromSource();
|
|
5309
|
+
}
|
|
5310
|
+
});
|
|
5311
|
+
},
|
|
5312
|
+
inject: (event) => {
|
|
5313
|
+
const pending = consumeWaiter();
|
|
5314
|
+
if (pending != null) {
|
|
5315
|
+
pending.resolve(valueResult(event));
|
|
5316
|
+
}
|
|
5317
|
+
else {
|
|
5318
|
+
pendingInjectedEvents.push(event);
|
|
5319
|
+
}
|
|
5320
|
+
}
|
|
5321
|
+
};
|
|
5322
|
+
}
|
|
5323
|
+
/**
|
|
5324
|
+
* Splits a byte stream at line endings, emitting each line as a string.
|
|
5325
|
+
*/
|
|
5326
|
+
function extractJsonLines(source, decoder) {
|
|
5327
|
+
let buffer = '';
|
|
5328
|
+
const pendingLines = [];
|
|
5329
|
+
let isFinalEvent = false;
|
|
5330
|
+
return {
|
|
5331
|
+
next: async () => {
|
|
5332
|
+
while (true) {
|
|
5333
|
+
if (isFinalEvent) {
|
|
5334
|
+
return doneResult;
|
|
5335
|
+
}
|
|
5336
|
+
{
|
|
5337
|
+
const first = pendingLines.shift();
|
|
5338
|
+
if (first) {
|
|
5339
|
+
return { done: false, value: first };
|
|
5340
|
+
}
|
|
5341
|
+
}
|
|
5342
|
+
const { done, value } = await source.next();
|
|
5343
|
+
if (done) {
|
|
5344
|
+
const remaining = buffer.trim();
|
|
5345
|
+
if (remaining.length != 0) {
|
|
5346
|
+
isFinalEvent = true;
|
|
5347
|
+
return { done: false, value: remaining };
|
|
5348
|
+
}
|
|
5349
|
+
return doneResult;
|
|
5350
|
+
}
|
|
5351
|
+
const data = decoder.decode(value, { stream: true });
|
|
5352
|
+
buffer += data;
|
|
5353
|
+
const lines = buffer.split('\n');
|
|
5354
|
+
for (let i = 0; i < lines.length - 1; i++) {
|
|
5355
|
+
const l = lines[i].trim();
|
|
5356
|
+
if (l.length > 0) {
|
|
5357
|
+
pendingLines.push(l);
|
|
5358
|
+
}
|
|
5359
|
+
}
|
|
5360
|
+
buffer = lines[lines.length - 1];
|
|
5361
|
+
}
|
|
5362
|
+
}
|
|
5363
|
+
};
|
|
5364
|
+
}
|
|
5365
|
+
/**
|
|
5366
|
+
* Splits a concatenated stream of BSON objects by emitting individual objects.
|
|
5367
|
+
*/
|
|
5368
|
+
function extractBsonObjects(source) {
|
|
5369
|
+
// Fully read but not emitted yet.
|
|
5370
|
+
const completedObjects = [];
|
|
5371
|
+
// Whether source has returned { done: true }. We do the same once completed objects have been emitted.
|
|
5372
|
+
let isDone = false;
|
|
5373
|
+
const lengthBuffer = new DataView(new ArrayBuffer(4));
|
|
5374
|
+
let objectBody = null;
|
|
5375
|
+
// If we're parsing the length field, a number between 1 and 4 (inclusive) describing remaining bytes in the header.
|
|
5376
|
+
// If we're consuming a document, the bytes remaining.
|
|
5377
|
+
let remainingLength = 4;
|
|
5378
|
+
return {
|
|
5379
|
+
async next() {
|
|
5380
|
+
while (true) {
|
|
5381
|
+
// Before fetching new data from upstream, return completed objects.
|
|
5382
|
+
if (completedObjects.length) {
|
|
5383
|
+
return valueResult(completedObjects.shift());
|
|
5384
|
+
}
|
|
5385
|
+
if (isDone) {
|
|
5386
|
+
return doneResult;
|
|
5387
|
+
}
|
|
5388
|
+
const upstreamEvent = await source.next();
|
|
5389
|
+
if (upstreamEvent.done) {
|
|
5390
|
+
isDone = true;
|
|
5391
|
+
if (objectBody || remainingLength != 4) {
|
|
5392
|
+
throw new Error('illegal end of stream in BSON object');
|
|
5393
|
+
}
|
|
5394
|
+
return doneResult;
|
|
5395
|
+
}
|
|
5396
|
+
const chunk = upstreamEvent.value;
|
|
5397
|
+
for (let i = 0; i < chunk.length;) {
|
|
5398
|
+
const availableInData = chunk.length - i;
|
|
5399
|
+
if (objectBody) {
|
|
5400
|
+
// We're in the middle of reading a BSON document.
|
|
5401
|
+
const bytesToRead = Math.min(availableInData, remainingLength);
|
|
5402
|
+
const copySource = new Uint8Array(chunk.buffer, chunk.byteOffset + i, bytesToRead);
|
|
5403
|
+
objectBody.set(copySource, objectBody.length - remainingLength);
|
|
5404
|
+
i += bytesToRead;
|
|
5405
|
+
remainingLength -= bytesToRead;
|
|
5406
|
+
if (remainingLength == 0) {
|
|
5407
|
+
completedObjects.push(objectBody);
|
|
5408
|
+
// Prepare to read another document, starting with its length
|
|
5409
|
+
objectBody = null;
|
|
5410
|
+
remainingLength = 4;
|
|
5411
|
+
}
|
|
5412
|
+
}
|
|
5413
|
+
else {
|
|
5414
|
+
// Copy up to 4 bytes into lengthBuffer, depending on how many we still need.
|
|
5415
|
+
const bytesToRead = Math.min(availableInData, remainingLength);
|
|
5416
|
+
for (let j = 0; j < bytesToRead; j++) {
|
|
5417
|
+
lengthBuffer.setUint8(4 - remainingLength + j, chunk[i + j]);
|
|
5418
|
+
}
|
|
5419
|
+
i += bytesToRead;
|
|
5420
|
+
remainingLength -= bytesToRead;
|
|
5421
|
+
if (remainingLength == 0) {
|
|
5422
|
+
// Transition from reading length header to reading document. Subtracting 4 because the length of the
|
|
5423
|
+
// header is included in length.
|
|
5424
|
+
const length = lengthBuffer.getInt32(0, true /* little endian */);
|
|
5425
|
+
remainingLength = length - 4;
|
|
5426
|
+
if (remainingLength < 1) {
|
|
5427
|
+
throw new Error(`invalid length for bson: ${length}`);
|
|
5428
|
+
}
|
|
5429
|
+
objectBody = new Uint8Array(length);
|
|
5430
|
+
new DataView(objectBody.buffer).setInt32(0, length, true);
|
|
5431
|
+
}
|
|
5432
|
+
}
|
|
5433
|
+
}
|
|
5434
|
+
}
|
|
5435
|
+
}
|
|
5436
|
+
};
|
|
5437
|
+
}
|
|
5438
|
+
|
|
5387
5439
|
/**
|
|
5388
5440
|
* Throttle a function to be called at most once every "wait" milliseconds,
|
|
5389
5441
|
* on the trailing edge.
|
|
@@ -5403,45 +5455,128 @@ function throttleTrailing(func, wait) {
|
|
|
5403
5455
|
};
|
|
5404
5456
|
}
|
|
5405
5457
|
function asyncNotifier() {
|
|
5406
|
-
|
|
5407
|
-
let hasPendingNotification = false;
|
|
5458
|
+
const queue = new EventQueue();
|
|
5408
5459
|
return {
|
|
5409
5460
|
notify() {
|
|
5410
|
-
if (
|
|
5411
|
-
|
|
5412
|
-
|
|
5461
|
+
if (queue.countOutstandingEvents > 0) ;
|
|
5462
|
+
else {
|
|
5463
|
+
queue.notify();
|
|
5464
|
+
}
|
|
5465
|
+
},
|
|
5466
|
+
waitForNotification(signal) {
|
|
5467
|
+
return queue.waitForEvent(signal);
|
|
5468
|
+
}
|
|
5469
|
+
};
|
|
5470
|
+
}
|
|
5471
|
+
class EventQueue {
|
|
5472
|
+
options;
|
|
5473
|
+
waitingConsumer;
|
|
5474
|
+
outstandingEvents;
|
|
5475
|
+
constructor(options = {}) {
|
|
5476
|
+
this.options = options;
|
|
5477
|
+
this.outstandingEvents = [];
|
|
5478
|
+
}
|
|
5479
|
+
/**
|
|
5480
|
+
* The amount of buffered events not yet dispatched to listeners.
|
|
5481
|
+
*/
|
|
5482
|
+
get countOutstandingEvents() {
|
|
5483
|
+
return this.outstandingEvents.length;
|
|
5484
|
+
}
|
|
5485
|
+
notifyInner(dispatch) {
|
|
5486
|
+
const existing = this.waitingConsumer;
|
|
5487
|
+
this.waitingConsumer = undefined;
|
|
5488
|
+
const dispatchAndNotifyListeners = (waiter) => {
|
|
5489
|
+
dispatch(waiter);
|
|
5490
|
+
this.options.eventDelivered?.();
|
|
5491
|
+
};
|
|
5492
|
+
if (existing) {
|
|
5493
|
+
dispatchAndNotifyListeners(existing);
|
|
5494
|
+
}
|
|
5495
|
+
else {
|
|
5496
|
+
this.outstandingEvents.push(dispatchAndNotifyListeners);
|
|
5497
|
+
}
|
|
5498
|
+
}
|
|
5499
|
+
notify(value) {
|
|
5500
|
+
this.notifyInner((l) => l.resolve(value));
|
|
5501
|
+
}
|
|
5502
|
+
notifyError(error) {
|
|
5503
|
+
this.notifyInner((l) => l.reject(error));
|
|
5504
|
+
}
|
|
5505
|
+
waitForEvent(signal) {
|
|
5506
|
+
return new Promise((resolve, reject) => {
|
|
5507
|
+
if (this.waitingConsumer != null) {
|
|
5508
|
+
throw new Error('Illegal call to waitForEvent, already has a waiter.');
|
|
5509
|
+
}
|
|
5510
|
+
const complete = () => {
|
|
5511
|
+
signal?.removeEventListener('abort', onAbort);
|
|
5512
|
+
};
|
|
5513
|
+
const onAbort = () => {
|
|
5514
|
+
complete();
|
|
5515
|
+
this.waitingConsumer = undefined;
|
|
5516
|
+
resolve(undefined);
|
|
5517
|
+
};
|
|
5518
|
+
const waiter = {
|
|
5519
|
+
resolve: (value) => {
|
|
5520
|
+
complete();
|
|
5521
|
+
resolve(value);
|
|
5522
|
+
},
|
|
5523
|
+
reject: (error) => {
|
|
5524
|
+
complete();
|
|
5525
|
+
reject(error);
|
|
5526
|
+
}
|
|
5527
|
+
};
|
|
5528
|
+
if (signal.aborted) {
|
|
5529
|
+
resolve(undefined);
|
|
5530
|
+
}
|
|
5531
|
+
else if (this.countOutstandingEvents > 0) {
|
|
5532
|
+
const [event] = this.outstandingEvents.splice(0, 1);
|
|
5533
|
+
event(waiter);
|
|
5413
5534
|
}
|
|
5414
5535
|
else {
|
|
5415
|
-
|
|
5536
|
+
this.waitingConsumer = waiter;
|
|
5537
|
+
signal.addEventListener('abort', onAbort);
|
|
5416
5538
|
}
|
|
5417
|
-
}
|
|
5418
|
-
|
|
5419
|
-
|
|
5420
|
-
|
|
5421
|
-
|
|
5422
|
-
|
|
5423
|
-
|
|
5424
|
-
|
|
5425
|
-
|
|
5426
|
-
|
|
5427
|
-
|
|
5428
|
-
|
|
5539
|
+
});
|
|
5540
|
+
}
|
|
5541
|
+
/**
|
|
5542
|
+
* Creates an async iterable backed by event queues.
|
|
5543
|
+
*
|
|
5544
|
+
* @param run A function invoked for every new listener. It receives a queue backing the async iterator.
|
|
5545
|
+
* @param abort An additional abort signal. The `run` callback will also be aborted when `AsyncIterator.return` is
|
|
5546
|
+
* called.
|
|
5547
|
+
* @returns An object conforming to the async iterable protocol.
|
|
5548
|
+
*/
|
|
5549
|
+
static queueBasedAsyncIterable(run, abort) {
|
|
5550
|
+
return {
|
|
5551
|
+
[symbolAsyncIterator]: () => {
|
|
5552
|
+
const queue = new EventQueue();
|
|
5553
|
+
const controller = new AbortController();
|
|
5554
|
+
function dispose() {
|
|
5555
|
+
controller.abort();
|
|
5556
|
+
abort?.removeEventListener('abort', dispose);
|
|
5429
5557
|
}
|
|
5430
|
-
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
resolve();
|
|
5558
|
+
if (abort) {
|
|
5559
|
+
if (abort.aborted) {
|
|
5560
|
+
controller.abort();
|
|
5434
5561
|
}
|
|
5435
|
-
|
|
5436
|
-
|
|
5437
|
-
resolve();
|
|
5562
|
+
else {
|
|
5563
|
+
abort.addEventListener('abort', dispose);
|
|
5438
5564
|
}
|
|
5439
|
-
waitingConsumer = complete;
|
|
5440
|
-
signal.addEventListener('abort', onAbort);
|
|
5441
5565
|
}
|
|
5442
|
-
|
|
5443
|
-
|
|
5444
|
-
|
|
5566
|
+
run(queue, controller.signal);
|
|
5567
|
+
return {
|
|
5568
|
+
async next() {
|
|
5569
|
+
const event = await queue.waitForEvent(controller.signal);
|
|
5570
|
+
return event == null ? doneResult : valueResult(event);
|
|
5571
|
+
},
|
|
5572
|
+
async return() {
|
|
5573
|
+
dispose();
|
|
5574
|
+
return doneResult;
|
|
5575
|
+
}
|
|
5576
|
+
};
|
|
5577
|
+
}
|
|
5578
|
+
};
|
|
5579
|
+
}
|
|
5445
5580
|
}
|
|
5446
5581
|
|
|
5447
5582
|
/**
|
|
@@ -13672,7 +13807,7 @@ function requireDist () {
|
|
|
13672
13807
|
|
|
13673
13808
|
var distExports = requireDist();
|
|
13674
13809
|
|
|
13675
|
-
var version = "1.
|
|
13810
|
+
var version = "1.56.0";
|
|
13676
13811
|
var PACKAGE = {
|
|
13677
13812
|
version: version};
|
|
13678
13813
|
|
|
@@ -13839,201 +13974,6 @@ class WebsocketClientTransport {
|
|
|
13839
13974
|
}
|
|
13840
13975
|
}
|
|
13841
13976
|
|
|
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
13977
|
const POWERSYNC_TRAILING_SLASH_MATCH = /\/+$/;
|
|
14038
13978
|
const POWERSYNC_JS_VERSION = PACKAGE.version;
|
|
14039
13979
|
const SYNC_QUEUE_REQUEST_HIGH_WATER = 10;
|
|
@@ -14252,8 +14192,19 @@ class AbstractRemote {
|
|
|
14252
14192
|
let pendingSocket = null;
|
|
14253
14193
|
let keepAliveTimeout;
|
|
14254
14194
|
let rsocket = null;
|
|
14255
|
-
let
|
|
14195
|
+
let paused = false;
|
|
14196
|
+
const queue = new EventQueue({
|
|
14197
|
+
eventDelivered: () => {
|
|
14198
|
+
if (queue.countOutstandingEvents <= SYNC_QUEUE_REQUEST_LOW_WATER) {
|
|
14199
|
+
paused = false;
|
|
14200
|
+
requestMore();
|
|
14201
|
+
}
|
|
14202
|
+
}
|
|
14203
|
+
});
|
|
14256
14204
|
let didClose = false;
|
|
14205
|
+
let connectionEstablished = false;
|
|
14206
|
+
let pendingEventsCount = syncQueueRequestSize;
|
|
14207
|
+
let res = null;
|
|
14257
14208
|
const abortRequest = () => {
|
|
14258
14209
|
if (didClose) {
|
|
14259
14210
|
return;
|
|
@@ -14266,10 +14217,23 @@ class AbstractRemote {
|
|
|
14266
14217
|
if (rsocket) {
|
|
14267
14218
|
rsocket.close();
|
|
14268
14219
|
}
|
|
14269
|
-
|
|
14270
|
-
|
|
14271
|
-
|
|
14220
|
+
// Send a bogus event to the queue to ensure a pending listener gets woken up. We check for didClose and would
|
|
14221
|
+
// return a doneEvent.
|
|
14222
|
+
queue.notify(null);
|
|
14272
14223
|
};
|
|
14224
|
+
function push(event) {
|
|
14225
|
+
queue.notify(event);
|
|
14226
|
+
if (queue.countOutstandingEvents >= SYNC_QUEUE_REQUEST_HIGH_WATER) {
|
|
14227
|
+
paused = true;
|
|
14228
|
+
}
|
|
14229
|
+
}
|
|
14230
|
+
function requestMore() {
|
|
14231
|
+
const delta = syncQueueRequestSize - pendingEventsCount;
|
|
14232
|
+
if (!paused && delta > 0) {
|
|
14233
|
+
res?.request(delta);
|
|
14234
|
+
pendingEventsCount = syncQueueRequestSize;
|
|
14235
|
+
}
|
|
14236
|
+
}
|
|
14273
14237
|
// Handle upstream abort
|
|
14274
14238
|
if (options.abortSignal.aborted) {
|
|
14275
14239
|
throw new AbortOperation('Connection request aborted');
|
|
@@ -14324,25 +14288,19 @@ class AbstractRemote {
|
|
|
14324
14288
|
// Helps to prevent double close scenarios
|
|
14325
14289
|
rsocket.onClose(() => (rsocket = null));
|
|
14326
14290
|
return await new Promise((resolve, reject) => {
|
|
14327
|
-
|
|
14328
|
-
|
|
14329
|
-
|
|
14330
|
-
|
|
14331
|
-
|
|
14332
|
-
|
|
14333
|
-
|
|
14334
|
-
|
|
14335
|
-
|
|
14291
|
+
const queueAsIterator = {
|
|
14292
|
+
next: async () => {
|
|
14293
|
+
if (didClose)
|
|
14294
|
+
return doneResult;
|
|
14295
|
+
const notification = await queue.waitForEvent(options.abortSignal);
|
|
14296
|
+
if (didClose) {
|
|
14297
|
+
return doneResult;
|
|
14298
|
+
}
|
|
14299
|
+
else {
|
|
14300
|
+
return valueResult(notification);
|
|
14301
|
+
}
|
|
14336
14302
|
}
|
|
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]();
|
|
14303
|
+
};
|
|
14346
14304
|
res = rsocket.requestStream({
|
|
14347
14305
|
data: toBuffer(options.data),
|
|
14348
14306
|
metadata: toBuffer({
|
|
@@ -14378,11 +14336,11 @@ class AbstractRemote {
|
|
|
14378
14336
|
// The connection is active
|
|
14379
14337
|
if (!connectionEstablished) {
|
|
14380
14338
|
connectionEstablished = true;
|
|
14381
|
-
resolve(
|
|
14339
|
+
resolve(queueAsIterator);
|
|
14382
14340
|
}
|
|
14383
14341
|
const { data } = payload;
|
|
14384
14342
|
if (data) {
|
|
14385
|
-
|
|
14343
|
+
push(data);
|
|
14386
14344
|
}
|
|
14387
14345
|
// Less events are now pending
|
|
14388
14346
|
pendingEventsCount--;
|
|
@@ -15980,7 +15938,7 @@ class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
15980
15938
|
this.logger.warn('Schema validation failed. Unexpected behaviour could occur', ex);
|
|
15981
15939
|
}
|
|
15982
15940
|
this._schema = schema;
|
|
15983
|
-
await this.database.execute('SELECT powersync_replace_schema(?)', [JSON.stringify(this.schema.toJSON())]);
|
|
15941
|
+
await this.database.writeTransaction((tx) => tx.execute('SELECT powersync_replace_schema(?)', [JSON.stringify(this.schema.toJSON())]));
|
|
15984
15942
|
await this.database.refreshSchema();
|
|
15985
15943
|
this.iterateListeners(async (cb) => cb.schemaChanged?.(schema));
|
|
15986
15944
|
}
|
|
@@ -16500,20 +16458,17 @@ SELECT * FROM crud_entries;
|
|
|
16500
16458
|
* @returns An AsyncIterable that yields QueryResults whenever the data changes
|
|
16501
16459
|
*/
|
|
16502
16460
|
watchWithAsyncGenerator(sql, parameters, options) {
|
|
16503
|
-
return
|
|
16461
|
+
return EventQueue.queueBasedAsyncIterable((queue, abort) => {
|
|
16504
16462
|
const handler = {
|
|
16505
16463
|
onResult: (result) => {
|
|
16506
|
-
|
|
16464
|
+
queue.notify(result);
|
|
16507
16465
|
},
|
|
16508
16466
|
onError: (error) => {
|
|
16509
|
-
|
|
16467
|
+
queue.notifyError(error);
|
|
16510
16468
|
}
|
|
16511
16469
|
};
|
|
16512
|
-
this.watchWithCallback(sql, parameters, handler, options);
|
|
16513
|
-
|
|
16514
|
-
eventOptions.stop();
|
|
16515
|
-
});
|
|
16516
|
-
});
|
|
16470
|
+
this.watchWithCallback(sql, parameters, handler, { ...options, signal: abort });
|
|
16471
|
+
}, options?.signal);
|
|
16517
16472
|
}
|
|
16518
16473
|
/**
|
|
16519
16474
|
* Resolves the list of tables that are used in a SQL query.
|
|
@@ -16603,28 +16558,23 @@ SELECT * FROM crud_entries;
|
|
|
16603
16558
|
* This is preferred over {@link AbstractPowerSyncDatabase.watchWithAsyncGenerator} when multiple queries need to be
|
|
16604
16559
|
* performed together when data is changed.
|
|
16605
16560
|
*
|
|
16606
|
-
* Note: do not declare this as `async *onChange` as it will not work in React Native.
|
|
16607
|
-
*
|
|
16608
16561
|
* @param options - Options for configuring watch behavior
|
|
16609
16562
|
* @returns An AsyncIterable that yields change events whenever the specified tables change
|
|
16610
16563
|
*/
|
|
16564
|
+
// Note: do not declare this as `async *onChange` as it will not work in React Native.
|
|
16611
16565
|
onChangeWithAsyncGenerator(options) {
|
|
16612
|
-
|
|
16613
|
-
|
|
16614
|
-
const dispose = this.onChangeWithCallback({
|
|
16566
|
+
return EventQueue.queueBasedAsyncIterable((queue, abort) => {
|
|
16567
|
+
this.onChangeWithCallback({
|
|
16615
16568
|
onChange: (event) => {
|
|
16616
|
-
|
|
16569
|
+
queue.notify(event);
|
|
16617
16570
|
},
|
|
16618
16571
|
onError: (error) => {
|
|
16619
|
-
|
|
16572
|
+
queue.notifyError(error);
|
|
16620
16573
|
}
|
|
16621
|
-
}, options);
|
|
16622
|
-
|
|
16623
|
-
|
|
16624
|
-
|
|
16625
|
-
});
|
|
16626
|
-
return () => dispose();
|
|
16627
|
-
});
|
|
16574
|
+
}, { ...options, signal: abort });
|
|
16575
|
+
// Note: We don't have to track the dispose function returned by onChangeWithCallback, it cleans up
|
|
16576
|
+
// after the abort signal completes.
|
|
16577
|
+
}, options?.signal);
|
|
16628
16578
|
}
|
|
16629
16579
|
handleTableChanges(changedTables, watchedTables, onDetectedChanges) {
|
|
16630
16580
|
if (changedTables.size > 0) {
|