@powersync/common 1.54.0 → 1.56.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bundle.cjs +384 -434
- package/dist/bundle.cjs.map +1 -1
- package/dist/bundle.mjs +384 -434
- package/dist/bundle.mjs.map +1 -1
- package/dist/bundle.node.cjs +384 -280
- package/dist/bundle.node.cjs.map +1 -1
- package/dist/bundle.node.mjs +384 -280
- package/dist/bundle.node.mjs.map +1 -1
- package/dist/index.d.cts +54 -52
- package/lib/attachments/AttachmentQueue.d.ts +52 -44
- package/lib/attachments/AttachmentQueue.js.map +1 -1
- package/lib/client/AbstractPowerSyncDatabase.d.ts +0 -2
- package/lib/client/AbstractPowerSyncDatabase.js +19 -27
- package/lib/client/AbstractPowerSyncDatabase.js.map +1 -1
- package/lib/client/sync/stream/AbstractRemote.js +44 -26
- package/lib/client/sync/stream/AbstractRemote.js.map +1 -1
- package/lib/db/crud/SyncStatus.d.ts +1 -5
- package/lib/db/crud/SyncStatus.js +9 -6
- package/lib/db/crud/SyncStatus.js.map +1 -1
- package/lib/utils/async.d.ts +26 -0
- package/lib/utils/async.js +114 -27
- package/lib/utils/async.js.map +1 -1
- package/lib/utils/compatibility.d.ts +8 -0
- package/lib/utils/compatibility.js +9 -0
- package/lib/utils/compatibility.js.map +1 -0
- package/package.json +1 -2
- package/src/attachments/AttachmentQueue.ts +54 -44
- package/src/client/AbstractPowerSyncDatabase.ts +21 -30
- package/src/client/sync/stream/AbstractRemote.ts +46 -33
- package/src/db/crud/SyncStatus.ts +10 -7
- package/src/utils/async.ts +136 -28
- package/src/utils/compatibility.ts +9 -0
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { EventIterator } from 'event-iterator';
|
|
2
1
|
import Logger, { ILogger } from 'js-logger';
|
|
3
2
|
import {
|
|
4
3
|
BatchedUpdateNotification,
|
|
@@ -14,7 +13,7 @@ import { UploadQueueStats } from '../db/crud/UploadQueueStatus.js';
|
|
|
14
13
|
import { Schema } from '../db/schema/Schema.js';
|
|
15
14
|
import { BaseObserver } from '../utils/BaseObserver.js';
|
|
16
15
|
import { ControlledExecutor } from '../utils/ControlledExecutor.js';
|
|
17
|
-
import { throttleTrailing } from '../utils/async.js';
|
|
16
|
+
import { EventQueue, throttleTrailing } from '../utils/async.js';
|
|
18
17
|
import {
|
|
19
18
|
ConnectionManager,
|
|
20
19
|
CreateSyncImplementationOptions,
|
|
@@ -47,6 +46,7 @@ import { DEFAULT_WATCH_THROTTLE_MS, WatchCompatibleQuery } from './watched/Watch
|
|
|
47
46
|
import { OnChangeQueryProcessor } from './watched/processors/OnChangeQueryProcessor.js';
|
|
48
47
|
import { WatchedQueryComparator } from './watched/processors/comparators.js';
|
|
49
48
|
import { Mutex } from '../utils/mutex.js';
|
|
49
|
+
import { symbolAsyncIterator } from '../utils/compatibility.js';
|
|
50
50
|
|
|
51
51
|
/**
|
|
52
52
|
* @public
|
|
@@ -557,7 +557,9 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
|
|
|
557
557
|
}
|
|
558
558
|
this._schema = schema;
|
|
559
559
|
|
|
560
|
-
await this.database.
|
|
560
|
+
await this.database.writeTransaction((tx) =>
|
|
561
|
+
tx.execute('SELECT powersync_replace_schema(?)', [JSON.stringify(this.schema.toJSON())])
|
|
562
|
+
);
|
|
561
563
|
await this.database.refreshSchema();
|
|
562
564
|
this.iterateListeners(async (cb) => cb.schemaChanged?.(schema));
|
|
563
565
|
}
|
|
@@ -762,7 +764,7 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
|
|
|
762
764
|
* @returns A transaction of CRUD operations to upload, or null if there are none
|
|
763
765
|
*/
|
|
764
766
|
async getNextCrudTransaction(): Promise<CrudTransaction | null> {
|
|
765
|
-
const iterator = this.getCrudTransactions()[
|
|
767
|
+
const iterator = this.getCrudTransactions()[symbolAsyncIterator]();
|
|
766
768
|
return (await iterator.next()).value;
|
|
767
769
|
}
|
|
768
770
|
|
|
@@ -799,7 +801,7 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
|
|
|
799
801
|
*/
|
|
800
802
|
getCrudTransactions(): AsyncIterable<CrudTransaction, null> {
|
|
801
803
|
return {
|
|
802
|
-
[
|
|
804
|
+
[symbolAsyncIterator]: () => {
|
|
803
805
|
let lastCrudItemId = -1;
|
|
804
806
|
const sql = `
|
|
805
807
|
WITH RECURSIVE crud_entries AS (
|
|
@@ -1193,22 +1195,18 @@ SELECT * FROM crud_entries;
|
|
|
1193
1195
|
* @returns An AsyncIterable that yields QueryResults whenever the data changes
|
|
1194
1196
|
*/
|
|
1195
1197
|
watchWithAsyncGenerator(sql: string, parameters?: any[], options?: SQLWatchOptions): AsyncIterable<QueryResult> {
|
|
1196
|
-
return
|
|
1198
|
+
return EventQueue.queueBasedAsyncIterable((queue, abort) => {
|
|
1197
1199
|
const handler: WatchHandler = {
|
|
1198
1200
|
onResult: (result) => {
|
|
1199
|
-
|
|
1201
|
+
queue.notify(result);
|
|
1200
1202
|
},
|
|
1201
1203
|
onError: (error) => {
|
|
1202
|
-
|
|
1204
|
+
queue.notifyError(error);
|
|
1203
1205
|
}
|
|
1204
1206
|
};
|
|
1205
1207
|
|
|
1206
|
-
this.watchWithCallback(sql, parameters, handler, options);
|
|
1207
|
-
|
|
1208
|
-
options?.signal?.addEventListener('abort', () => {
|
|
1209
|
-
eventOptions.stop();
|
|
1210
|
-
});
|
|
1211
|
-
});
|
|
1208
|
+
this.watchWithCallback(sql, parameters, handler, { ...options, signal: abort });
|
|
1209
|
+
}, options?.signal);
|
|
1212
1210
|
}
|
|
1213
1211
|
|
|
1214
1212
|
/**
|
|
@@ -1353,34 +1351,27 @@ SELECT * FROM crud_entries;
|
|
|
1353
1351
|
* This is preferred over {@link AbstractPowerSyncDatabase.watchWithAsyncGenerator} when multiple queries need to be
|
|
1354
1352
|
* performed together when data is changed.
|
|
1355
1353
|
*
|
|
1356
|
-
* Note: do not declare this as `async *onChange` as it will not work in React Native.
|
|
1357
|
-
*
|
|
1358
1354
|
* @param options - Options for configuring watch behavior
|
|
1359
1355
|
* @returns An AsyncIterable that yields change events whenever the specified tables change
|
|
1360
1356
|
*/
|
|
1357
|
+
// Note: do not declare this as `async *onChange` as it will not work in React Native.
|
|
1361
1358
|
onChangeWithAsyncGenerator(options?: SQLWatchOptions): AsyncIterable<WatchOnChangeEvent> {
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
return new EventIterator<WatchOnChangeEvent>((eventOptions) => {
|
|
1365
|
-
const dispose = this.onChangeWithCallback(
|
|
1359
|
+
return EventQueue.queueBasedAsyncIterable((queue, abort) => {
|
|
1360
|
+
this.onChangeWithCallback(
|
|
1366
1361
|
{
|
|
1367
1362
|
onChange: (event): void => {
|
|
1368
|
-
|
|
1363
|
+
queue.notify(event);
|
|
1369
1364
|
},
|
|
1370
1365
|
onError: (error) => {
|
|
1371
|
-
|
|
1366
|
+
queue.notifyError(error);
|
|
1372
1367
|
}
|
|
1373
1368
|
},
|
|
1374
|
-
options
|
|
1369
|
+
{ ...options, signal: abort }
|
|
1375
1370
|
);
|
|
1376
1371
|
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
});
|
|
1381
|
-
|
|
1382
|
-
return () => dispose();
|
|
1383
|
-
});
|
|
1372
|
+
// Note: We don't have to track the dispose function returned by onChangeWithCallback, it cleans up
|
|
1373
|
+
// after the abort signal completes.
|
|
1374
|
+
}, options?.signal);
|
|
1384
1375
|
}
|
|
1385
1376
|
|
|
1386
1377
|
private handleTableChanges(
|
|
@@ -9,10 +9,10 @@ import {
|
|
|
9
9
|
doneResult,
|
|
10
10
|
extractBsonObjects,
|
|
11
11
|
extractJsonLines,
|
|
12
|
-
SimpleAsyncIterator
|
|
12
|
+
SimpleAsyncIterator,
|
|
13
|
+
valueResult
|
|
13
14
|
} from '../../../utils/stream_transform.js';
|
|
14
|
-
import {
|
|
15
|
-
import type { Queue } from 'event-iterator/lib/event-iterator.js';
|
|
15
|
+
import { EventQueue } from '../../../utils/async.js';
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* @internal
|
|
@@ -329,8 +329,19 @@ export abstract class AbstractRemote {
|
|
|
329
329
|
let pendingSocket: WebSocket | null = null;
|
|
330
330
|
let keepAliveTimeout: any;
|
|
331
331
|
let rsocket: RSocket | null = null;
|
|
332
|
-
let
|
|
332
|
+
let paused = false;
|
|
333
|
+
const queue = new EventQueue<Uint8Array | null>({
|
|
334
|
+
eventDelivered: () => {
|
|
335
|
+
if (queue.countOutstandingEvents <= SYNC_QUEUE_REQUEST_LOW_WATER) {
|
|
336
|
+
paused = false;
|
|
337
|
+
requestMore();
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
});
|
|
333
341
|
let didClose = false;
|
|
342
|
+
let connectionEstablished = false;
|
|
343
|
+
let pendingEventsCount = syncQueueRequestSize;
|
|
344
|
+
let res: Requestable | null = null;
|
|
334
345
|
|
|
335
346
|
const abortRequest = () => {
|
|
336
347
|
if (didClose) {
|
|
@@ -348,11 +359,26 @@ export abstract class AbstractRemote {
|
|
|
348
359
|
rsocket.close();
|
|
349
360
|
}
|
|
350
361
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
362
|
+
// Send a bogus event to the queue to ensure a pending listener gets woken up. We check for didClose and would
|
|
363
|
+
// return a doneEvent.
|
|
364
|
+
queue.notify(null);
|
|
354
365
|
};
|
|
355
366
|
|
|
367
|
+
function push(event: Uint8Array) {
|
|
368
|
+
queue.notify(event);
|
|
369
|
+
if (queue.countOutstandingEvents >= SYNC_QUEUE_REQUEST_HIGH_WATER) {
|
|
370
|
+
paused = true;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function requestMore() {
|
|
375
|
+
const delta = syncQueueRequestSize - pendingEventsCount;
|
|
376
|
+
if (!paused && delta > 0) {
|
|
377
|
+
res?.request(delta);
|
|
378
|
+
pendingEventsCount = syncQueueRequestSize;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
356
382
|
// Handle upstream abort
|
|
357
383
|
if (options.abortSignal.aborted) {
|
|
358
384
|
throw new AbortOperation('Connection request aborted');
|
|
@@ -413,31 +439,18 @@ export abstract class AbstractRemote {
|
|
|
413
439
|
rsocket.onClose(() => (rsocket = null));
|
|
414
440
|
|
|
415
441
|
return await new Promise((resolve, reject) => {
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
let res: Requestable | null = null;
|
|
420
|
-
|
|
421
|
-
function requestMore() {
|
|
422
|
-
const delta = syncQueueRequestSize - pendingEventsCount;
|
|
423
|
-
if (!paused && delta > 0) {
|
|
424
|
-
res?.request(delta);
|
|
425
|
-
pendingEventsCount = syncQueueRequestSize;
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
const events = new EventIterator<Uint8Array>(
|
|
430
|
-
(q) => {
|
|
431
|
-
queue = q;
|
|
442
|
+
const queueAsIterator: SimpleAsyncIterator<Uint8Array> = {
|
|
443
|
+
next: async () => {
|
|
444
|
+
if (didClose) return doneResult;
|
|
432
445
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
446
|
+
const notification = await queue.waitForEvent(options.abortSignal);
|
|
447
|
+
if (didClose) {
|
|
448
|
+
return doneResult;
|
|
449
|
+
} else {
|
|
450
|
+
return valueResult(notification!);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
};
|
|
441
454
|
|
|
442
455
|
res = rsocket!.requestStream(
|
|
443
456
|
{
|
|
@@ -476,12 +489,12 @@ export abstract class AbstractRemote {
|
|
|
476
489
|
// The connection is active
|
|
477
490
|
if (!connectionEstablished) {
|
|
478
491
|
connectionEstablished = true;
|
|
479
|
-
resolve(
|
|
492
|
+
resolve(queueAsIterator);
|
|
480
493
|
}
|
|
481
494
|
const { data } = payload;
|
|
482
495
|
|
|
483
496
|
if (data) {
|
|
484
|
-
|
|
497
|
+
push(data);
|
|
485
498
|
}
|
|
486
499
|
|
|
487
500
|
// Less events are now pending
|
|
@@ -234,11 +234,7 @@ export class SyncStatus {
|
|
|
234
234
|
*/
|
|
235
235
|
const replacer = (_: string, value: any) => {
|
|
236
236
|
if (value instanceof Error) {
|
|
237
|
-
return
|
|
238
|
-
name: value.name,
|
|
239
|
-
message: value.message,
|
|
240
|
-
stack: value.stack
|
|
241
|
-
};
|
|
237
|
+
return this.serializeError(value);
|
|
242
238
|
}
|
|
243
239
|
return value;
|
|
244
240
|
};
|
|
@@ -281,15 +277,22 @@ export class SyncStatus {
|
|
|
281
277
|
* Not all errors are serializable over a MessagePort. E.g. some `DomExceptions` fail to be passed across workers.
|
|
282
278
|
* This explicitly serializes errors in the SyncStatus.
|
|
283
279
|
*/
|
|
284
|
-
protected serializeError(error?: Error) {
|
|
280
|
+
protected serializeError(error?: Error): Error | undefined {
|
|
285
281
|
if (typeof error == 'undefined') {
|
|
286
282
|
return undefined;
|
|
287
283
|
}
|
|
288
|
-
|
|
284
|
+
const serialized: Error = {
|
|
289
285
|
name: error.name,
|
|
290
286
|
message: error.message,
|
|
291
287
|
stack: error.stack
|
|
292
288
|
};
|
|
289
|
+
// `Error.cause` can be any value (the spec types it as unknown). Preserve it
|
|
290
|
+
// so consumers reading uploadError/downloadError keep the failure context.
|
|
291
|
+
// Recurse for Error causes so the whole chain is flattened the same way.
|
|
292
|
+
if (typeof error.cause != 'undefined') {
|
|
293
|
+
serialized.cause = error.cause instanceof Error ? this.serializeError(error.cause) : error.cause;
|
|
294
|
+
}
|
|
295
|
+
return serialized;
|
|
293
296
|
}
|
|
294
297
|
|
|
295
298
|
private static comparePriorities(a: SyncPriorityStatus, b: SyncPriorityStatus) {
|
package/src/utils/async.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { symbolAsyncIterator } from './compatibility.js';
|
|
2
|
+
import { doneResult, valueResult } from './stream_transform.js';
|
|
3
|
+
|
|
1
4
|
/**
|
|
2
5
|
* Throttle a function to be called at most once every "wait" milliseconds,
|
|
3
6
|
* on the trailing edge.
|
|
@@ -34,44 +37,149 @@ export interface AsyncNotifier {
|
|
|
34
37
|
}
|
|
35
38
|
|
|
36
39
|
export function asyncNotifier(): AsyncNotifier {
|
|
37
|
-
|
|
38
|
-
let hasPendingNotification = false;
|
|
40
|
+
const queue = new EventQueue<void>();
|
|
39
41
|
|
|
40
42
|
return {
|
|
41
43
|
notify() {
|
|
42
|
-
if (
|
|
43
|
-
|
|
44
|
-
waitingConsumer = null;
|
|
44
|
+
if (queue.countOutstandingEvents > 0) {
|
|
45
|
+
// Already has an outstanding event, no need to buffer another one.
|
|
45
46
|
} else {
|
|
46
|
-
|
|
47
|
+
queue.notify();
|
|
47
48
|
}
|
|
48
49
|
},
|
|
49
50
|
waitForNotification(signal: AbortSignal) {
|
|
50
|
-
return
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
return queue.waitForEvent(signal);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
type QueueWaiter<T> = { resolve: (event: T) => void; reject: (error: unknown) => void };
|
|
57
|
+
|
|
58
|
+
export interface QueueOptions {
|
|
59
|
+
eventDelivered?: () => void;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export class EventQueue<T> {
|
|
63
|
+
private waitingConsumer: QueueWaiter<T> | undefined;
|
|
64
|
+
private readonly outstandingEvents: Array<(waiter: QueueWaiter<T>) => void>;
|
|
65
|
+
|
|
66
|
+
constructor(private readonly options: QueueOptions = {}) {
|
|
67
|
+
this.outstandingEvents = [];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* The amount of buffered events not yet dispatched to listeners.
|
|
72
|
+
*/
|
|
73
|
+
get countOutstandingEvents(): number {
|
|
74
|
+
return this.outstandingEvents.length;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
private notifyInner(dispatch: (waiter: QueueWaiter<T>) => void) {
|
|
78
|
+
const existing = this.waitingConsumer;
|
|
79
|
+
this.waitingConsumer = undefined;
|
|
80
|
+
|
|
81
|
+
const dispatchAndNotifyListeners = (waiter: QueueWaiter<T>) => {
|
|
82
|
+
dispatch(waiter);
|
|
83
|
+
this.options.eventDelivered?.();
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
if (existing) {
|
|
87
|
+
dispatchAndNotifyListeners(existing);
|
|
88
|
+
} else {
|
|
89
|
+
this.outstandingEvents.push(dispatchAndNotifyListeners);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
notify(value: T) {
|
|
94
|
+
this.notifyInner((l) => l.resolve(value));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
notifyError(error: unknown) {
|
|
98
|
+
this.notifyInner((l) => l.reject(error));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
waitForEvent(signal: AbortSignal): Promise<T | undefined> {
|
|
102
|
+
return new Promise((resolve, reject) => {
|
|
103
|
+
if (this.waitingConsumer != null) {
|
|
104
|
+
throw new Error('Illegal call to waitForEvent, already has a waiter.');
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const complete = () => {
|
|
108
|
+
signal?.removeEventListener('abort', onAbort);
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const onAbort = () => {
|
|
112
|
+
complete();
|
|
113
|
+
this.waitingConsumer = undefined;
|
|
114
|
+
resolve(undefined);
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const waiter: QueueWaiter<T> = {
|
|
118
|
+
resolve: (value) => {
|
|
119
|
+
complete();
|
|
120
|
+
resolve(value);
|
|
121
|
+
},
|
|
122
|
+
reject: (error) => {
|
|
123
|
+
complete();
|
|
124
|
+
reject(error);
|
|
53
125
|
}
|
|
126
|
+
};
|
|
54
127
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
128
|
+
if (signal.aborted) {
|
|
129
|
+
resolve(undefined);
|
|
130
|
+
} else if (this.countOutstandingEvents > 0) {
|
|
131
|
+
const [event] = this.outstandingEvents.splice(0, 1);
|
|
132
|
+
event(waiter);
|
|
133
|
+
} else {
|
|
134
|
+
this.waitingConsumer = waiter;
|
|
135
|
+
signal.addEventListener('abort', onAbort);
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
}
|
|
65
139
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
140
|
+
/**
|
|
141
|
+
* Creates an async iterable backed by event queues.
|
|
142
|
+
*
|
|
143
|
+
* @param run A function invoked for every new listener. It receives a queue backing the async iterator.
|
|
144
|
+
* @param abort An additional abort signal. The `run` callback will also be aborted when `AsyncIterator.return` is
|
|
145
|
+
* called.
|
|
146
|
+
* @returns An object conforming to the async iterable protocol.
|
|
147
|
+
*/
|
|
148
|
+
static queueBasedAsyncIterable<T>(
|
|
149
|
+
run: (queue: EventQueue<T>, abort: AbortSignal) => void,
|
|
150
|
+
abort?: AbortSignal
|
|
151
|
+
): AsyncIterable<T> {
|
|
152
|
+
return {
|
|
153
|
+
[symbolAsyncIterator]: () => {
|
|
154
|
+
const queue = new EventQueue<T>();
|
|
155
|
+
const controller = new AbortController();
|
|
70
156
|
|
|
71
|
-
|
|
72
|
-
|
|
157
|
+
function dispose() {
|
|
158
|
+
controller.abort();
|
|
159
|
+
abort?.removeEventListener('abort', dispose);
|
|
73
160
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
161
|
+
|
|
162
|
+
if (abort) {
|
|
163
|
+
if (abort.aborted) {
|
|
164
|
+
controller.abort();
|
|
165
|
+
} else {
|
|
166
|
+
abort.addEventListener('abort', dispose);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
run(queue, controller.signal);
|
|
171
|
+
|
|
172
|
+
return {
|
|
173
|
+
async next(): Promise<IteratorResult<T>> {
|
|
174
|
+
const event = await queue.waitForEvent(controller.signal);
|
|
175
|
+
return event == null ? doneResult : valueResult(event);
|
|
176
|
+
},
|
|
177
|
+
async return(): Promise<IteratorResult<T>> {
|
|
178
|
+
dispose();
|
|
179
|
+
return doneResult;
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
}
|
|
77
185
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Some JavaScript engines, in particular older versions of React Native, don't support Symbol.asyncIterator.
|
|
3
|
+
*
|
|
4
|
+
* 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).
|
|
5
|
+
* This definition is compatible with that polyfill, so transpiled apps can use async iterables created by the PowerSync
|
|
6
|
+
* SDK.
|
|
7
|
+
*/
|
|
8
|
+
export const symbolAsyncIterator: typeof Symbol.asyncIterator =
|
|
9
|
+
Symbol.asyncIterator ?? Symbol.for('Symbol.asyncIterator');
|