@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
|
@@ -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
|
/*!
|
|
@@ -4025,11 +3870,7 @@ class SyncStatus {
|
|
|
4025
3870
|
*/
|
|
4026
3871
|
const replacer = (_, value) => {
|
|
4027
3872
|
if (value instanceof Error) {
|
|
4028
|
-
return
|
|
4029
|
-
name: value.name,
|
|
4030
|
-
message: value.message,
|
|
4031
|
-
stack: value.stack
|
|
4032
|
-
};
|
|
3873
|
+
return this.serializeError(value);
|
|
4033
3874
|
}
|
|
4034
3875
|
return value;
|
|
4035
3876
|
};
|
|
@@ -4072,11 +3913,18 @@ class SyncStatus {
|
|
|
4072
3913
|
if (typeof error == 'undefined') {
|
|
4073
3914
|
return undefined;
|
|
4074
3915
|
}
|
|
4075
|
-
|
|
3916
|
+
const serialized = {
|
|
4076
3917
|
name: error.name,
|
|
4077
3918
|
message: error.message,
|
|
4078
3919
|
stack: error.stack
|
|
4079
3920
|
};
|
|
3921
|
+
// `Error.cause` can be any value (the spec types it as unknown). Preserve it
|
|
3922
|
+
// so consumers reading uploadError/downloadError keep the failure context.
|
|
3923
|
+
// Recurse for Error causes so the whole chain is flattened the same way.
|
|
3924
|
+
if (typeof error.cause != 'undefined') {
|
|
3925
|
+
serialized.cause = error.cause instanceof Error ? this.serializeError(error.cause) : error.cause;
|
|
3926
|
+
}
|
|
3927
|
+
return serialized;
|
|
4080
3928
|
}
|
|
4081
3929
|
static comparePriorities(a, b) {
|
|
4082
3930
|
return b.priority - a.priority; // Reverse because higher priorities have lower numbers
|
|
@@ -4226,6 +4074,210 @@ class ControlledExecutor {
|
|
|
4226
4074
|
}
|
|
4227
4075
|
}
|
|
4228
4076
|
|
|
4077
|
+
/**
|
|
4078
|
+
* Some JavaScript engines, in particular older versions of React Native, don't support Symbol.asyncIterator.
|
|
4079
|
+
*
|
|
4080
|
+
* 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).
|
|
4081
|
+
* This definition is compatible with that polyfill, so transpiled apps can use async iterables created by the PowerSync
|
|
4082
|
+
* SDK.
|
|
4083
|
+
*/
|
|
4084
|
+
const symbolAsyncIterator = Symbol.asyncIterator ?? Symbol.for('Symbol.asyncIterator');
|
|
4085
|
+
|
|
4086
|
+
const doneResult = { done: true, value: undefined };
|
|
4087
|
+
function valueResult(value) {
|
|
4088
|
+
return { done: false, value };
|
|
4089
|
+
}
|
|
4090
|
+
/**
|
|
4091
|
+
* Expands a source async iterator by allowing to inject events asynchronously.
|
|
4092
|
+
*
|
|
4093
|
+
* The resulting iterator will emit all events from its source. Additionally though, events can be injected. These
|
|
4094
|
+
* events are dropped once the main iterator completes, but are otherwise forwarded.
|
|
4095
|
+
*
|
|
4096
|
+
* The iterator completes when its source completes, and it supports backpressure by only calling `next()` on the source
|
|
4097
|
+
* in response to a `next()` call from downstream if no pending injected events can be dispatched.
|
|
4098
|
+
*/
|
|
4099
|
+
function injectable(source) {
|
|
4100
|
+
let sourceIsDone = false;
|
|
4101
|
+
let waiter = undefined; // An active, waiting next() call.
|
|
4102
|
+
// A pending upstream event that couldn't be dispatched because inject() has been called before it was resolved.
|
|
4103
|
+
let pendingSourceEvent = null;
|
|
4104
|
+
let sourceFetchInFlight = false;
|
|
4105
|
+
let pendingInjectedEvents = [];
|
|
4106
|
+
const consumeWaiter = () => {
|
|
4107
|
+
const pending = waiter;
|
|
4108
|
+
waiter = undefined;
|
|
4109
|
+
return pending;
|
|
4110
|
+
};
|
|
4111
|
+
const fetchFromSource = () => {
|
|
4112
|
+
const resolveWaiter = (propagate) => {
|
|
4113
|
+
sourceFetchInFlight = false;
|
|
4114
|
+
const active = consumeWaiter();
|
|
4115
|
+
if (active) {
|
|
4116
|
+
propagate(active);
|
|
4117
|
+
}
|
|
4118
|
+
else {
|
|
4119
|
+
pendingSourceEvent = propagate;
|
|
4120
|
+
}
|
|
4121
|
+
};
|
|
4122
|
+
sourceFetchInFlight = true;
|
|
4123
|
+
const nextFromSource = source.next();
|
|
4124
|
+
nextFromSource.then((value) => {
|
|
4125
|
+
sourceIsDone = value.done == true;
|
|
4126
|
+
resolveWaiter((w) => w.resolve(value));
|
|
4127
|
+
}, (error) => {
|
|
4128
|
+
resolveWaiter((w) => w.reject(error));
|
|
4129
|
+
});
|
|
4130
|
+
};
|
|
4131
|
+
return {
|
|
4132
|
+
next: () => {
|
|
4133
|
+
return new Promise((resolve, reject) => {
|
|
4134
|
+
// First priority: Dispatch ready upstream events.
|
|
4135
|
+
if (sourceIsDone) {
|
|
4136
|
+
return resolve(doneResult);
|
|
4137
|
+
}
|
|
4138
|
+
if (pendingSourceEvent) {
|
|
4139
|
+
pendingSourceEvent({ resolve, reject });
|
|
4140
|
+
pendingSourceEvent = null;
|
|
4141
|
+
return;
|
|
4142
|
+
}
|
|
4143
|
+
// Second priority: Dispatch injected events
|
|
4144
|
+
if (pendingInjectedEvents.length) {
|
|
4145
|
+
return resolve(valueResult(pendingInjectedEvents.shift()));
|
|
4146
|
+
}
|
|
4147
|
+
// Nothing pending? Fetch from source
|
|
4148
|
+
waiter = { resolve, reject };
|
|
4149
|
+
if (!sourceFetchInFlight) {
|
|
4150
|
+
fetchFromSource();
|
|
4151
|
+
}
|
|
4152
|
+
});
|
|
4153
|
+
},
|
|
4154
|
+
inject: (event) => {
|
|
4155
|
+
const pending = consumeWaiter();
|
|
4156
|
+
if (pending != null) {
|
|
4157
|
+
pending.resolve(valueResult(event));
|
|
4158
|
+
}
|
|
4159
|
+
else {
|
|
4160
|
+
pendingInjectedEvents.push(event);
|
|
4161
|
+
}
|
|
4162
|
+
}
|
|
4163
|
+
};
|
|
4164
|
+
}
|
|
4165
|
+
/**
|
|
4166
|
+
* Splits a byte stream at line endings, emitting each line as a string.
|
|
4167
|
+
*/
|
|
4168
|
+
function extractJsonLines(source, decoder) {
|
|
4169
|
+
let buffer = '';
|
|
4170
|
+
const pendingLines = [];
|
|
4171
|
+
let isFinalEvent = false;
|
|
4172
|
+
return {
|
|
4173
|
+
next: async () => {
|
|
4174
|
+
while (true) {
|
|
4175
|
+
if (isFinalEvent) {
|
|
4176
|
+
return doneResult;
|
|
4177
|
+
}
|
|
4178
|
+
{
|
|
4179
|
+
const first = pendingLines.shift();
|
|
4180
|
+
if (first) {
|
|
4181
|
+
return { done: false, value: first };
|
|
4182
|
+
}
|
|
4183
|
+
}
|
|
4184
|
+
const { done, value } = await source.next();
|
|
4185
|
+
if (done) {
|
|
4186
|
+
const remaining = buffer.trim();
|
|
4187
|
+
if (remaining.length != 0) {
|
|
4188
|
+
isFinalEvent = true;
|
|
4189
|
+
return { done: false, value: remaining };
|
|
4190
|
+
}
|
|
4191
|
+
return doneResult;
|
|
4192
|
+
}
|
|
4193
|
+
const data = decoder.decode(value, { stream: true });
|
|
4194
|
+
buffer += data;
|
|
4195
|
+
const lines = buffer.split('\n');
|
|
4196
|
+
for (let i = 0; i < lines.length - 1; i++) {
|
|
4197
|
+
const l = lines[i].trim();
|
|
4198
|
+
if (l.length > 0) {
|
|
4199
|
+
pendingLines.push(l);
|
|
4200
|
+
}
|
|
4201
|
+
}
|
|
4202
|
+
buffer = lines[lines.length - 1];
|
|
4203
|
+
}
|
|
4204
|
+
}
|
|
4205
|
+
};
|
|
4206
|
+
}
|
|
4207
|
+
/**
|
|
4208
|
+
* Splits a concatenated stream of BSON objects by emitting individual objects.
|
|
4209
|
+
*/
|
|
4210
|
+
function extractBsonObjects(source) {
|
|
4211
|
+
// Fully read but not emitted yet.
|
|
4212
|
+
const completedObjects = [];
|
|
4213
|
+
// Whether source has returned { done: true }. We do the same once completed objects have been emitted.
|
|
4214
|
+
let isDone = false;
|
|
4215
|
+
const lengthBuffer = new DataView(new ArrayBuffer(4));
|
|
4216
|
+
let objectBody = null;
|
|
4217
|
+
// If we're parsing the length field, a number between 1 and 4 (inclusive) describing remaining bytes in the header.
|
|
4218
|
+
// If we're consuming a document, the bytes remaining.
|
|
4219
|
+
let remainingLength = 4;
|
|
4220
|
+
return {
|
|
4221
|
+
async next() {
|
|
4222
|
+
while (true) {
|
|
4223
|
+
// Before fetching new data from upstream, return completed objects.
|
|
4224
|
+
if (completedObjects.length) {
|
|
4225
|
+
return valueResult(completedObjects.shift());
|
|
4226
|
+
}
|
|
4227
|
+
if (isDone) {
|
|
4228
|
+
return doneResult;
|
|
4229
|
+
}
|
|
4230
|
+
const upstreamEvent = await source.next();
|
|
4231
|
+
if (upstreamEvent.done) {
|
|
4232
|
+
isDone = true;
|
|
4233
|
+
if (objectBody || remainingLength != 4) {
|
|
4234
|
+
throw new Error('illegal end of stream in BSON object');
|
|
4235
|
+
}
|
|
4236
|
+
return doneResult;
|
|
4237
|
+
}
|
|
4238
|
+
const chunk = upstreamEvent.value;
|
|
4239
|
+
for (let i = 0; i < chunk.length;) {
|
|
4240
|
+
const availableInData = chunk.length - i;
|
|
4241
|
+
if (objectBody) {
|
|
4242
|
+
// We're in the middle of reading a BSON document.
|
|
4243
|
+
const bytesToRead = Math.min(availableInData, remainingLength);
|
|
4244
|
+
const copySource = new Uint8Array(chunk.buffer, chunk.byteOffset + i, bytesToRead);
|
|
4245
|
+
objectBody.set(copySource, objectBody.length - remainingLength);
|
|
4246
|
+
i += bytesToRead;
|
|
4247
|
+
remainingLength -= bytesToRead;
|
|
4248
|
+
if (remainingLength == 0) {
|
|
4249
|
+
completedObjects.push(objectBody);
|
|
4250
|
+
// Prepare to read another document, starting with its length
|
|
4251
|
+
objectBody = null;
|
|
4252
|
+
remainingLength = 4;
|
|
4253
|
+
}
|
|
4254
|
+
}
|
|
4255
|
+
else {
|
|
4256
|
+
// Copy up to 4 bytes into lengthBuffer, depending on how many we still need.
|
|
4257
|
+
const bytesToRead = Math.min(availableInData, remainingLength);
|
|
4258
|
+
for (let j = 0; j < bytesToRead; j++) {
|
|
4259
|
+
lengthBuffer.setUint8(4 - remainingLength + j, chunk[i + j]);
|
|
4260
|
+
}
|
|
4261
|
+
i += bytesToRead;
|
|
4262
|
+
remainingLength -= bytesToRead;
|
|
4263
|
+
if (remainingLength == 0) {
|
|
4264
|
+
// Transition from reading length header to reading document. Subtracting 4 because the length of the
|
|
4265
|
+
// header is included in length.
|
|
4266
|
+
const length = lengthBuffer.getInt32(0, true /* little endian */);
|
|
4267
|
+
remainingLength = length - 4;
|
|
4268
|
+
if (remainingLength < 1) {
|
|
4269
|
+
throw new Error(`invalid length for bson: ${length}`);
|
|
4270
|
+
}
|
|
4271
|
+
objectBody = new Uint8Array(length);
|
|
4272
|
+
new DataView(objectBody.buffer).setInt32(0, length, true);
|
|
4273
|
+
}
|
|
4274
|
+
}
|
|
4275
|
+
}
|
|
4276
|
+
}
|
|
4277
|
+
}
|
|
4278
|
+
};
|
|
4279
|
+
}
|
|
4280
|
+
|
|
4229
4281
|
/**
|
|
4230
4282
|
* Throttle a function to be called at most once every "wait" milliseconds,
|
|
4231
4283
|
* on the trailing edge.
|
|
@@ -4245,45 +4297,128 @@ function throttleTrailing(func, wait) {
|
|
|
4245
4297
|
};
|
|
4246
4298
|
}
|
|
4247
4299
|
function asyncNotifier() {
|
|
4248
|
-
|
|
4249
|
-
let hasPendingNotification = false;
|
|
4300
|
+
const queue = new EventQueue();
|
|
4250
4301
|
return {
|
|
4251
4302
|
notify() {
|
|
4252
|
-
if (
|
|
4253
|
-
|
|
4254
|
-
|
|
4303
|
+
if (queue.countOutstandingEvents > 0) ;
|
|
4304
|
+
else {
|
|
4305
|
+
queue.notify();
|
|
4306
|
+
}
|
|
4307
|
+
},
|
|
4308
|
+
waitForNotification(signal) {
|
|
4309
|
+
return queue.waitForEvent(signal);
|
|
4310
|
+
}
|
|
4311
|
+
};
|
|
4312
|
+
}
|
|
4313
|
+
class EventQueue {
|
|
4314
|
+
options;
|
|
4315
|
+
waitingConsumer;
|
|
4316
|
+
outstandingEvents;
|
|
4317
|
+
constructor(options = {}) {
|
|
4318
|
+
this.options = options;
|
|
4319
|
+
this.outstandingEvents = [];
|
|
4320
|
+
}
|
|
4321
|
+
/**
|
|
4322
|
+
* The amount of buffered events not yet dispatched to listeners.
|
|
4323
|
+
*/
|
|
4324
|
+
get countOutstandingEvents() {
|
|
4325
|
+
return this.outstandingEvents.length;
|
|
4326
|
+
}
|
|
4327
|
+
notifyInner(dispatch) {
|
|
4328
|
+
const existing = this.waitingConsumer;
|
|
4329
|
+
this.waitingConsumer = undefined;
|
|
4330
|
+
const dispatchAndNotifyListeners = (waiter) => {
|
|
4331
|
+
dispatch(waiter);
|
|
4332
|
+
this.options.eventDelivered?.();
|
|
4333
|
+
};
|
|
4334
|
+
if (existing) {
|
|
4335
|
+
dispatchAndNotifyListeners(existing);
|
|
4336
|
+
}
|
|
4337
|
+
else {
|
|
4338
|
+
this.outstandingEvents.push(dispatchAndNotifyListeners);
|
|
4339
|
+
}
|
|
4340
|
+
}
|
|
4341
|
+
notify(value) {
|
|
4342
|
+
this.notifyInner((l) => l.resolve(value));
|
|
4343
|
+
}
|
|
4344
|
+
notifyError(error) {
|
|
4345
|
+
this.notifyInner((l) => l.reject(error));
|
|
4346
|
+
}
|
|
4347
|
+
waitForEvent(signal) {
|
|
4348
|
+
return new Promise((resolve, reject) => {
|
|
4349
|
+
if (this.waitingConsumer != null) {
|
|
4350
|
+
throw new Error('Illegal call to waitForEvent, already has a waiter.');
|
|
4351
|
+
}
|
|
4352
|
+
const complete = () => {
|
|
4353
|
+
signal?.removeEventListener('abort', onAbort);
|
|
4354
|
+
};
|
|
4355
|
+
const onAbort = () => {
|
|
4356
|
+
complete();
|
|
4357
|
+
this.waitingConsumer = undefined;
|
|
4358
|
+
resolve(undefined);
|
|
4359
|
+
};
|
|
4360
|
+
const waiter = {
|
|
4361
|
+
resolve: (value) => {
|
|
4362
|
+
complete();
|
|
4363
|
+
resolve(value);
|
|
4364
|
+
},
|
|
4365
|
+
reject: (error) => {
|
|
4366
|
+
complete();
|
|
4367
|
+
reject(error);
|
|
4368
|
+
}
|
|
4369
|
+
};
|
|
4370
|
+
if (signal.aborted) {
|
|
4371
|
+
resolve(undefined);
|
|
4372
|
+
}
|
|
4373
|
+
else if (this.countOutstandingEvents > 0) {
|
|
4374
|
+
const [event] = this.outstandingEvents.splice(0, 1);
|
|
4375
|
+
event(waiter);
|
|
4255
4376
|
}
|
|
4256
4377
|
else {
|
|
4257
|
-
|
|
4378
|
+
this.waitingConsumer = waiter;
|
|
4379
|
+
signal.addEventListener('abort', onAbort);
|
|
4258
4380
|
}
|
|
4259
|
-
}
|
|
4260
|
-
|
|
4261
|
-
|
|
4262
|
-
|
|
4263
|
-
|
|
4264
|
-
|
|
4265
|
-
|
|
4266
|
-
|
|
4267
|
-
|
|
4268
|
-
|
|
4269
|
-
|
|
4270
|
-
|
|
4381
|
+
});
|
|
4382
|
+
}
|
|
4383
|
+
/**
|
|
4384
|
+
* Creates an async iterable backed by event queues.
|
|
4385
|
+
*
|
|
4386
|
+
* @param run A function invoked for every new listener. It receives a queue backing the async iterator.
|
|
4387
|
+
* @param abort An additional abort signal. The `run` callback will also be aborted when `AsyncIterator.return` is
|
|
4388
|
+
* called.
|
|
4389
|
+
* @returns An object conforming to the async iterable protocol.
|
|
4390
|
+
*/
|
|
4391
|
+
static queueBasedAsyncIterable(run, abort) {
|
|
4392
|
+
return {
|
|
4393
|
+
[symbolAsyncIterator]: () => {
|
|
4394
|
+
const queue = new EventQueue();
|
|
4395
|
+
const controller = new AbortController();
|
|
4396
|
+
function dispose() {
|
|
4397
|
+
controller.abort();
|
|
4398
|
+
abort?.removeEventListener('abort', dispose);
|
|
4271
4399
|
}
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
resolve();
|
|
4400
|
+
if (abort) {
|
|
4401
|
+
if (abort.aborted) {
|
|
4402
|
+
controller.abort();
|
|
4276
4403
|
}
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
resolve();
|
|
4404
|
+
else {
|
|
4405
|
+
abort.addEventListener('abort', dispose);
|
|
4280
4406
|
}
|
|
4281
|
-
waitingConsumer = complete;
|
|
4282
|
-
signal.addEventListener('abort', onAbort);
|
|
4283
4407
|
}
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
|
|
4408
|
+
run(queue, controller.signal);
|
|
4409
|
+
return {
|
|
4410
|
+
async next() {
|
|
4411
|
+
const event = await queue.waitForEvent(controller.signal);
|
|
4412
|
+
return event == null ? doneResult : valueResult(event);
|
|
4413
|
+
},
|
|
4414
|
+
async return() {
|
|
4415
|
+
dispose();
|
|
4416
|
+
return doneResult;
|
|
4417
|
+
}
|
|
4418
|
+
};
|
|
4419
|
+
}
|
|
4420
|
+
};
|
|
4421
|
+
}
|
|
4287
4422
|
}
|
|
4288
4423
|
|
|
4289
4424
|
/**
|
|
@@ -12514,7 +12649,7 @@ function requireDist () {
|
|
|
12514
12649
|
|
|
12515
12650
|
var distExports = requireDist();
|
|
12516
12651
|
|
|
12517
|
-
var version = "1.
|
|
12652
|
+
var version = "1.56.0";
|
|
12518
12653
|
var PACKAGE = {
|
|
12519
12654
|
version: version};
|
|
12520
12655
|
|
|
@@ -12681,201 +12816,6 @@ class WebsocketClientTransport {
|
|
|
12681
12816
|
}
|
|
12682
12817
|
}
|
|
12683
12818
|
|
|
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
12819
|
const POWERSYNC_TRAILING_SLASH_MATCH = /\/+$/;
|
|
12880
12820
|
const POWERSYNC_JS_VERSION = PACKAGE.version;
|
|
12881
12821
|
const SYNC_QUEUE_REQUEST_HIGH_WATER = 10;
|
|
@@ -13094,8 +13034,19 @@ class AbstractRemote {
|
|
|
13094
13034
|
let pendingSocket = null;
|
|
13095
13035
|
let keepAliveTimeout;
|
|
13096
13036
|
let rsocket = null;
|
|
13097
|
-
let
|
|
13037
|
+
let paused = false;
|
|
13038
|
+
const queue = new EventQueue({
|
|
13039
|
+
eventDelivered: () => {
|
|
13040
|
+
if (queue.countOutstandingEvents <= SYNC_QUEUE_REQUEST_LOW_WATER) {
|
|
13041
|
+
paused = false;
|
|
13042
|
+
requestMore();
|
|
13043
|
+
}
|
|
13044
|
+
}
|
|
13045
|
+
});
|
|
13098
13046
|
let didClose = false;
|
|
13047
|
+
let connectionEstablished = false;
|
|
13048
|
+
let pendingEventsCount = syncQueueRequestSize;
|
|
13049
|
+
let res = null;
|
|
13099
13050
|
const abortRequest = () => {
|
|
13100
13051
|
if (didClose) {
|
|
13101
13052
|
return;
|
|
@@ -13108,10 +13059,23 @@ class AbstractRemote {
|
|
|
13108
13059
|
if (rsocket) {
|
|
13109
13060
|
rsocket.close();
|
|
13110
13061
|
}
|
|
13111
|
-
|
|
13112
|
-
|
|
13113
|
-
|
|
13062
|
+
// Send a bogus event to the queue to ensure a pending listener gets woken up. We check for didClose and would
|
|
13063
|
+
// return a doneEvent.
|
|
13064
|
+
queue.notify(null);
|
|
13114
13065
|
};
|
|
13066
|
+
function push(event) {
|
|
13067
|
+
queue.notify(event);
|
|
13068
|
+
if (queue.countOutstandingEvents >= SYNC_QUEUE_REQUEST_HIGH_WATER) {
|
|
13069
|
+
paused = true;
|
|
13070
|
+
}
|
|
13071
|
+
}
|
|
13072
|
+
function requestMore() {
|
|
13073
|
+
const delta = syncQueueRequestSize - pendingEventsCount;
|
|
13074
|
+
if (!paused && delta > 0) {
|
|
13075
|
+
res?.request(delta);
|
|
13076
|
+
pendingEventsCount = syncQueueRequestSize;
|
|
13077
|
+
}
|
|
13078
|
+
}
|
|
13115
13079
|
// Handle upstream abort
|
|
13116
13080
|
if (options.abortSignal.aborted) {
|
|
13117
13081
|
throw new AbortOperation('Connection request aborted');
|
|
@@ -13166,25 +13130,19 @@ class AbstractRemote {
|
|
|
13166
13130
|
// Helps to prevent double close scenarios
|
|
13167
13131
|
rsocket.onClose(() => (rsocket = null));
|
|
13168
13132
|
return await new Promise((resolve, reject) => {
|
|
13169
|
-
|
|
13170
|
-
|
|
13171
|
-
|
|
13172
|
-
|
|
13173
|
-
|
|
13174
|
-
|
|
13175
|
-
|
|
13176
|
-
|
|
13177
|
-
|
|
13133
|
+
const queueAsIterator = {
|
|
13134
|
+
next: async () => {
|
|
13135
|
+
if (didClose)
|
|
13136
|
+
return doneResult;
|
|
13137
|
+
const notification = await queue.waitForEvent(options.abortSignal);
|
|
13138
|
+
if (didClose) {
|
|
13139
|
+
return doneResult;
|
|
13140
|
+
}
|
|
13141
|
+
else {
|
|
13142
|
+
return valueResult(notification);
|
|
13143
|
+
}
|
|
13178
13144
|
}
|
|
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]();
|
|
13145
|
+
};
|
|
13188
13146
|
res = rsocket.requestStream({
|
|
13189
13147
|
data: toBuffer(options.data),
|
|
13190
13148
|
metadata: toBuffer({
|
|
@@ -13220,11 +13178,11 @@ class AbstractRemote {
|
|
|
13220
13178
|
// The connection is active
|
|
13221
13179
|
if (!connectionEstablished) {
|
|
13222
13180
|
connectionEstablished = true;
|
|
13223
|
-
resolve(
|
|
13181
|
+
resolve(queueAsIterator);
|
|
13224
13182
|
}
|
|
13225
13183
|
const { data } = payload;
|
|
13226
13184
|
if (data) {
|
|
13227
|
-
|
|
13185
|
+
push(data);
|
|
13228
13186
|
}
|
|
13229
13187
|
// Less events are now pending
|
|
13230
13188
|
pendingEventsCount--;
|
|
@@ -14822,7 +14780,7 @@ class AbstractPowerSyncDatabase extends BaseObserver {
|
|
|
14822
14780
|
this.logger.warn('Schema validation failed. Unexpected behaviour could occur', ex);
|
|
14823
14781
|
}
|
|
14824
14782
|
this._schema = schema;
|
|
14825
|
-
await this.database.execute('SELECT powersync_replace_schema(?)', [JSON.stringify(this.schema.toJSON())]);
|
|
14783
|
+
await this.database.writeTransaction((tx) => tx.execute('SELECT powersync_replace_schema(?)', [JSON.stringify(this.schema.toJSON())]));
|
|
14826
14784
|
await this.database.refreshSchema();
|
|
14827
14785
|
this.iterateListeners(async (cb) => cb.schemaChanged?.(schema));
|
|
14828
14786
|
}
|
|
@@ -15342,20 +15300,17 @@ SELECT * FROM crud_entries;
|
|
|
15342
15300
|
* @returns An AsyncIterable that yields QueryResults whenever the data changes
|
|
15343
15301
|
*/
|
|
15344
15302
|
watchWithAsyncGenerator(sql, parameters, options) {
|
|
15345
|
-
return
|
|
15303
|
+
return EventQueue.queueBasedAsyncIterable((queue, abort) => {
|
|
15346
15304
|
const handler = {
|
|
15347
15305
|
onResult: (result) => {
|
|
15348
|
-
|
|
15306
|
+
queue.notify(result);
|
|
15349
15307
|
},
|
|
15350
15308
|
onError: (error) => {
|
|
15351
|
-
|
|
15309
|
+
queue.notifyError(error);
|
|
15352
15310
|
}
|
|
15353
15311
|
};
|
|
15354
|
-
this.watchWithCallback(sql, parameters, handler, options);
|
|
15355
|
-
|
|
15356
|
-
eventOptions.stop();
|
|
15357
|
-
});
|
|
15358
|
-
});
|
|
15312
|
+
this.watchWithCallback(sql, parameters, handler, { ...options, signal: abort });
|
|
15313
|
+
}, options?.signal);
|
|
15359
15314
|
}
|
|
15360
15315
|
/**
|
|
15361
15316
|
* Resolves the list of tables that are used in a SQL query.
|
|
@@ -15445,28 +15400,23 @@ SELECT * FROM crud_entries;
|
|
|
15445
15400
|
* This is preferred over {@link AbstractPowerSyncDatabase.watchWithAsyncGenerator} when multiple queries need to be
|
|
15446
15401
|
* performed together when data is changed.
|
|
15447
15402
|
*
|
|
15448
|
-
* Note: do not declare this as `async *onChange` as it will not work in React Native.
|
|
15449
|
-
*
|
|
15450
15403
|
* @param options - Options for configuring watch behavior
|
|
15451
15404
|
* @returns An AsyncIterable that yields change events whenever the specified tables change
|
|
15452
15405
|
*/
|
|
15406
|
+
// Note: do not declare this as `async *onChange` as it will not work in React Native.
|
|
15453
15407
|
onChangeWithAsyncGenerator(options) {
|
|
15454
|
-
|
|
15455
|
-
|
|
15456
|
-
const dispose = this.onChangeWithCallback({
|
|
15408
|
+
return EventQueue.queueBasedAsyncIterable((queue, abort) => {
|
|
15409
|
+
this.onChangeWithCallback({
|
|
15457
15410
|
onChange: (event) => {
|
|
15458
|
-
|
|
15411
|
+
queue.notify(event);
|
|
15459
15412
|
},
|
|
15460
15413
|
onError: (error) => {
|
|
15461
|
-
|
|
15414
|
+
queue.notifyError(error);
|
|
15462
15415
|
}
|
|
15463
|
-
}, options);
|
|
15464
|
-
|
|
15465
|
-
|
|
15466
|
-
|
|
15467
|
-
});
|
|
15468
|
-
return () => dispose();
|
|
15469
|
-
});
|
|
15416
|
+
}, { ...options, signal: abort });
|
|
15417
|
+
// Note: We don't have to track the dispose function returned by onChangeWithCallback, it cleans up
|
|
15418
|
+
// after the abort signal completes.
|
|
15419
|
+
}, options?.signal);
|
|
15470
15420
|
}
|
|
15471
15421
|
handleTableChanges(changedTables, watchedTables, onDetectedChanges) {
|
|
15472
15422
|
if (changedTables.size > 0) {
|