@powersync/common 0.0.0-dev-20260311103504 → 0.0.0-dev-20260503073249

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.
Files changed (72) hide show
  1. package/dist/bundle.cjs +791 -489
  2. package/dist/bundle.cjs.map +1 -1
  3. package/dist/bundle.mjs +785 -485
  4. package/dist/bundle.mjs.map +1 -1
  5. package/dist/bundle.node.cjs +789 -488
  6. package/dist/bundle.node.cjs.map +1 -1
  7. package/dist/bundle.node.mjs +783 -484
  8. package/dist/bundle.node.mjs.map +1 -1
  9. package/dist/index.d.cts +165 -103
  10. package/lib/attachments/AttachmentQueue.d.ts +10 -4
  11. package/lib/attachments/AttachmentQueue.js +10 -4
  12. package/lib/attachments/AttachmentQueue.js.map +1 -1
  13. package/lib/attachments/AttachmentService.js +2 -3
  14. package/lib/attachments/AttachmentService.js.map +1 -1
  15. package/lib/attachments/SyncingService.d.ts +2 -1
  16. package/lib/attachments/SyncingService.js +4 -5
  17. package/lib/attachments/SyncingService.js.map +1 -1
  18. package/lib/client/AbstractPowerSyncDatabase.d.ts +5 -1
  19. package/lib/client/AbstractPowerSyncDatabase.js +9 -5
  20. package/lib/client/AbstractPowerSyncDatabase.js.map +1 -1
  21. package/lib/client/sync/stream/AbstractRemote.d.ts +29 -8
  22. package/lib/client/sync/stream/AbstractRemote.js +154 -177
  23. package/lib/client/sync/stream/AbstractRemote.js.map +1 -1
  24. package/lib/client/sync/stream/AbstractStreamingSyncImplementation.d.ts +4 -0
  25. package/lib/client/sync/stream/AbstractStreamingSyncImplementation.js +88 -88
  26. package/lib/client/sync/stream/AbstractStreamingSyncImplementation.js.map +1 -1
  27. package/lib/db/DBAdapter.d.ts +55 -9
  28. package/lib/db/DBAdapter.js +126 -0
  29. package/lib/db/DBAdapter.js.map +1 -1
  30. package/lib/db/crud/SyncStatus.d.ts +0 -4
  31. package/lib/db/crud/SyncStatus.js +0 -4
  32. package/lib/db/crud/SyncStatus.js.map +1 -1
  33. package/lib/db/schema/RawTable.d.ts +0 -5
  34. package/lib/db/schema/Schema.d.ts +0 -2
  35. package/lib/db/schema/Schema.js +0 -2
  36. package/lib/db/schema/Schema.js.map +1 -1
  37. package/lib/index.d.ts +1 -1
  38. package/lib/index.js +0 -1
  39. package/lib/index.js.map +1 -1
  40. package/lib/utils/async.d.ts +0 -9
  41. package/lib/utils/async.js +0 -9
  42. package/lib/utils/async.js.map +1 -1
  43. package/lib/utils/mutex.d.ts +47 -5
  44. package/lib/utils/mutex.js +146 -21
  45. package/lib/utils/mutex.js.map +1 -1
  46. package/lib/utils/queue.d.ts +16 -0
  47. package/lib/utils/queue.js +42 -0
  48. package/lib/utils/queue.js.map +1 -0
  49. package/lib/utils/stream_transform.d.ts +39 -0
  50. package/lib/utils/stream_transform.js +206 -0
  51. package/lib/utils/stream_transform.js.map +1 -0
  52. package/package.json +9 -8
  53. package/src/attachments/AttachmentQueue.ts +10 -4
  54. package/src/attachments/AttachmentService.ts +2 -3
  55. package/src/attachments/README.md +6 -4
  56. package/src/attachments/SyncingService.ts +4 -5
  57. package/src/client/AbstractPowerSyncDatabase.ts +9 -5
  58. package/src/client/sync/stream/AbstractRemote.ts +182 -206
  59. package/src/client/sync/stream/AbstractStreamingSyncImplementation.ts +96 -83
  60. package/src/db/DBAdapter.ts +167 -9
  61. package/src/db/crud/SyncStatus.ts +0 -4
  62. package/src/db/schema/RawTable.ts +0 -5
  63. package/src/db/schema/Schema.ts +0 -2
  64. package/src/index.ts +1 -1
  65. package/src/utils/async.ts +0 -11
  66. package/src/utils/mutex.ts +184 -26
  67. package/src/utils/queue.ts +48 -0
  68. package/src/utils/stream_transform.ts +252 -0
  69. package/lib/utils/DataStream.d.ts +0 -62
  70. package/lib/utils/DataStream.js +0 -169
  71. package/lib/utils/DataStream.js.map +0 -1
  72. package/src/utils/DataStream.ts +0 -222
@@ -1,34 +1,192 @@
1
- import { Mutex } from 'async-mutex';
1
+ import { Queue } from './queue.js';
2
+
3
+ export type UnlockFn = () => void;
2
4
 
3
5
  /**
4
- * Wrapper for async-mutex runExclusive, which allows for a timeout on each exclusive lock.
6
+ * An asynchronous semaphore implementation with associated items per lease.
7
+ *
8
+ * @internal This class is meant to be used in PowerSync SDKs only, and is not part of the public API.
5
9
  */
6
- export async function mutexRunExclusive<T>(
7
- mutex: Mutex,
8
- callback: () => Promise<T>,
9
- options?: { timeoutMs?: number }
10
- ): Promise<T> {
11
- return new Promise((resolve, reject) => {
12
- const timeout = options?.timeoutMs;
13
- let timedOut = false;
14
- const timeoutId = timeout
15
- ? setTimeout(() => {
16
- timedOut = true;
17
- reject(new Error('Timeout waiting for lock'));
18
- }, timeout)
19
- : undefined;
20
-
21
- mutex.runExclusive(async () => {
22
- if (timeoutId) {
23
- clearTimeout(timeoutId);
10
+ export class Semaphore<T> {
11
+ // Available items that are not currently assigned to a waiter.
12
+ private readonly available: Queue<T>;
13
+
14
+ readonly size: number;
15
+ // Linked list of waiters. We don't expect the wait list to become particularly large, and this allows removing
16
+ // aborted waiters from the middle of the list efficiently.
17
+ private firstWaiter?: SemaphoreWaitNode<T>;
18
+ private lastWaiter?: SemaphoreWaitNode<T>;
19
+
20
+ constructor(elements: Iterable<T>) {
21
+ this.available = new Queue(elements);
22
+ this.size = this.available.length;
23
+ }
24
+
25
+ private addWaiter(requestedItems: number, onAcquire: () => void): SemaphoreWaitNode<T> {
26
+ const node: SemaphoreWaitNode<T> = {
27
+ isActive: true,
28
+ acquiredItems: [],
29
+ remainingItems: requestedItems,
30
+ onAcquire,
31
+ prev: this.lastWaiter
32
+ };
33
+ if (this.lastWaiter) {
34
+ this.lastWaiter.next = node;
35
+ this.lastWaiter = node;
36
+ } else {
37
+ // First waiter
38
+ this.lastWaiter = this.firstWaiter = node;
39
+ }
40
+
41
+ return node;
42
+ }
43
+
44
+ private deactivateWaiter(waiter: SemaphoreWaitNode<T>) {
45
+ const { prev, next } = waiter;
46
+ waiter.isActive = false;
47
+
48
+ if (prev) prev.next = next;
49
+ if (next) next.prev = prev;
50
+ if (waiter == this.firstWaiter) this.firstWaiter = next;
51
+ if (waiter == this.lastWaiter) this.lastWaiter = prev;
52
+ }
53
+
54
+ private requestPermits(amount: number, abort?: AbortSignal): Promise<{ items: T[]; release: UnlockFn }> {
55
+ if (amount <= 0 || amount > this.size) {
56
+ throw new Error(`Invalid amount of items requested (${amount}), must be between 1 and ${this.size}`);
57
+ }
58
+
59
+ return new Promise((resolve, reject) => {
60
+ function rejectAborted() {
61
+ reject(abort?.reason ?? new Error('Semaphore acquire aborted'));
62
+ }
63
+ if (abort?.aborted) {
64
+ return rejectAborted();
65
+ }
66
+
67
+ let waiter: SemaphoreWaitNode<T>;
68
+
69
+ const markCompleted = () => {
70
+ const items = waiter.acquiredItems;
71
+ waiter.acquiredItems = []; // Avoid releasing items twice.
72
+
73
+ for (const element of items) {
74
+ // Give to next waiter, if possible.
75
+ const nextWaiter = this.firstWaiter;
76
+ if (nextWaiter) {
77
+ nextWaiter.acquiredItems.push(element);
78
+ nextWaiter.remainingItems--;
79
+ if (nextWaiter.remainingItems == 0) {
80
+ nextWaiter.onAcquire();
81
+ }
82
+ } else {
83
+ // No pending waiter, return lease into pool.
84
+ this.available.addLast(element);
85
+ }
86
+ }
87
+ };
88
+
89
+ const onAbort = () => {
90
+ abort?.removeEventListener('abort', onAbort);
91
+
92
+ if (waiter.isActive) {
93
+ this.deactivateWaiter(waiter);
94
+ rejectAborted();
95
+ }
96
+ };
97
+
98
+ const resolvePromise = () => {
99
+ this.deactivateWaiter(waiter);
100
+ abort?.removeEventListener('abort', onAbort);
101
+
102
+ const items = waiter.acquiredItems;
103
+ resolve({ items, release: markCompleted });
104
+ };
105
+
106
+ waiter = this.addWaiter(amount, resolvePromise);
107
+
108
+ // If there are items in the pool that haven't been assigned, we can pull them into this waiter. Note that this is
109
+ // only the case if we're the first waiter (otherwise, items would have been assigned to an earlier waiter).
110
+ while (!this.available.isEmpty && waiter.remainingItems > 0) {
111
+ waiter.acquiredItems.push(this.available.removeFirst());
112
+ waiter.remainingItems--;
24
113
  }
25
- if (timedOut) return;
26
114
 
27
- try {
28
- resolve(await callback());
29
- } catch (ex) {
30
- reject(ex);
115
+ if (waiter.remainingItems == 0) {
116
+ return resolvePromise();
31
117
  }
118
+
119
+ abort?.addEventListener('abort', onAbort);
32
120
  });
33
- });
121
+ }
122
+
123
+ /**
124
+ * Requests a single item from the pool.
125
+ *
126
+ * The returned `release` callback must be invoked to return the item into the pool.
127
+ */
128
+ async requestOne(abort?: AbortSignal): Promise<{ item: T; release: UnlockFn }> {
129
+ const { items, release } = await this.requestPermits(1, abort);
130
+ return { release, item: items[0] };
131
+ }
132
+
133
+ /**
134
+ * Requests access to all items from the pool.
135
+ *
136
+ * The returned `release` callback must be invoked to return items into the pool.
137
+ */
138
+ requestAll(abort?: AbortSignal): Promise<{ items: T[]; release: UnlockFn }> {
139
+ return this.requestPermits(this.size, abort);
140
+ }
141
+ }
142
+
143
+ interface SemaphoreWaitNode<T> {
144
+ /**
145
+ * Whether the waiter is currently active (not aborted and not fullfilled).
146
+ */
147
+ isActive: boolean;
148
+ acquiredItems: T[];
149
+ remainingItems: number;
150
+ onAcquire: () => void;
151
+ prev?: SemaphoreWaitNode<T>;
152
+ next?: SemaphoreWaitNode<T>;
153
+ }
154
+
155
+ /**
156
+ * An asynchronous mutex implementation.
157
+ *
158
+ * @internal This class is meant to be used in PowerSync SDKs only, and is not part of the public API.
159
+ */
160
+ export class Mutex {
161
+ private inner = new Semaphore([null]);
162
+
163
+ async acquire(abort?: AbortSignal): Promise<UnlockFn> {
164
+ const { release } = await this.inner.requestOne(abort);
165
+ return release;
166
+ }
167
+
168
+ async runExclusive<T>(fn: () => PromiseLike<T> | T, abort?: AbortSignal): Promise<T> {
169
+ const returnMutex = await this.acquire(abort);
170
+
171
+ try {
172
+ return await fn();
173
+ } finally {
174
+ returnMutex();
175
+ }
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Creates a signal aborting after the set timeout.
181
+ */
182
+ export function timeoutSignal(timeout: number): AbortSignal;
183
+ export function timeoutSignal(timeout?: number): AbortSignal | undefined;
184
+
185
+ export function timeoutSignal(timeout?: number): AbortSignal | undefined {
186
+ if (timeout == null) return;
187
+ if ('timeout' in AbortSignal) return AbortSignal.timeout(timeout);
188
+
189
+ const controller = new AbortController();
190
+ setTimeout(() => controller.abort(new Error('Timeout waiting for lock')), timeout);
191
+ return controller.signal;
34
192
  }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * A simple fixed-capacity queue implementation.
3
+ *
4
+ * Unlike a naive queue implemented by `array.push()` and `array.shift()`, this avoids moving array elements around
5
+ * and is `O(1)` for {@link addLast} and {@link removeFirst}.
6
+ */
7
+ export class Queue<T> {
8
+ private table: (T | undefined)[];
9
+ // Index of the first element in the table.
10
+ private head: number;
11
+ // Amount of items currently in the queue.
12
+ private _length: number;
13
+
14
+ constructor(initialItems: Iterable<T>) {
15
+ this.table = [...initialItems];
16
+ this.head = 0;
17
+ this._length = this.table.length;
18
+ }
19
+
20
+ get isEmpty(): boolean {
21
+ return this.length == 0;
22
+ }
23
+
24
+ get length(): number {
25
+ return this._length;
26
+ }
27
+
28
+ removeFirst(): T {
29
+ if (this.isEmpty) {
30
+ throw new Error('Queue is empty');
31
+ }
32
+
33
+ const result = this.table[this.head] as T;
34
+ this._length--;
35
+ this.table[this.head] = undefined;
36
+ this.head = (this.head + 1) % this.table.length;
37
+ return result;
38
+ }
39
+
40
+ addLast(element: T) {
41
+ if (this.length == this.table.length) {
42
+ throw new Error('Queue is full');
43
+ }
44
+
45
+ this.table[(this.head + this._length) % this.table.length] = element;
46
+ this._length++;
47
+ }
48
+ }
@@ -0,0 +1,252 @@
1
+ /**
2
+ * An async iterator that can't be cancelled.
3
+ *
4
+ * To keep data flow simple, we always pass an explicit cancellation token when subscribing to async streams. Once the
5
+ * {@link AbortSignal} aborts, iterators are supposed to clean up and then emit a final `{done: true}` event. This means
6
+ * that there's no way to distinguish between streams that have completed normally and streams that have been cancelled,
7
+ * but that is acceptable for our uses of this.
8
+ */
9
+ export type SimpleAsyncIterator<T> = Pick<AsyncIterator<T>, 'next'>;
10
+
11
+ export const doneResult: IteratorReturnResult<any> = { done: true, value: undefined };
12
+
13
+ export function valueResult<T>(value: T) {
14
+ return { done: false, value };
15
+ }
16
+
17
+ /**
18
+ * A variant of {@link Array.map} for async iterators.
19
+ */
20
+ export function map<T1, T2>(source: SimpleAsyncIterator<T1>, map: (source: T1) => T2): SimpleAsyncIterator<T2> {
21
+ return {
22
+ next: async () => {
23
+ const value = await source.next();
24
+ if (value.done) {
25
+ return value;
26
+ } else {
27
+ return { value: map(value.value) };
28
+ }
29
+ }
30
+ };
31
+ }
32
+
33
+ export interface InjectableIterator<T> extends SimpleAsyncIterator<T> {
34
+ inject(event: T): void;
35
+ }
36
+
37
+ /**
38
+ * Expands a source async iterator by allowing to inject events asynchronously.
39
+ *
40
+ * The resulting iterator will emit all events from its source. Additionally though, events can be injected. These
41
+ * events are dropped once the main iterator completes, but are otherwise forwarded.
42
+ *
43
+ * The iterator completes when its source completes, and it supports backpressure by only calling `next()` on the source
44
+ * in response to a `next()` call from downstream if no pending injected events can be dispatched.
45
+ */
46
+ export function injectable<T>(source: SimpleAsyncIterator<T>): InjectableIterator<T> {
47
+ type Waiter = { resolve: (t: IteratorResult<T>) => void; reject: (e: unknown) => void };
48
+
49
+ let sourceIsDone = false;
50
+ let waiter: Waiter | undefined = undefined; // An active, waiting next() call.
51
+ // A pending upstream event that couldn't be dispatched because inject() has been called before it was resolved.
52
+ let pendingSourceEvent: ((w: Waiter) => void) | null = null;
53
+
54
+ let pendingInjectedEvents: T[] = [];
55
+
56
+ const consumeWaiter = () => {
57
+ const pending = waiter;
58
+ waiter = undefined;
59
+ return pending;
60
+ };
61
+
62
+ const fetchFromSource = () => {
63
+ const resolveWaiter = (propagate: (w: Waiter) => void) => {
64
+ const active = consumeWaiter();
65
+ if (active) {
66
+ propagate(active);
67
+ } else {
68
+ pendingSourceEvent = propagate;
69
+ }
70
+ };
71
+
72
+ const nextFromSource = source.next();
73
+ nextFromSource.then(
74
+ (value) => {
75
+ sourceIsDone = value.done == true;
76
+ resolveWaiter((w) => w.resolve(value));
77
+ },
78
+ (error) => {
79
+ resolveWaiter((w) => w.reject(error));
80
+ }
81
+ );
82
+ };
83
+
84
+ return {
85
+ next: () => {
86
+ return new Promise((resolve, reject) => {
87
+ // First priority: Dispatch ready upstream events.
88
+ if (sourceIsDone) {
89
+ return resolve(doneResult);
90
+ }
91
+ if (pendingSourceEvent) {
92
+ pendingSourceEvent({ resolve, reject });
93
+ pendingSourceEvent = null;
94
+ return;
95
+ }
96
+
97
+ // Second priority: Dispatch injected events
98
+ if (pendingInjectedEvents.length) {
99
+ return resolve(valueResult(pendingInjectedEvents.shift()!));
100
+ }
101
+
102
+ // Nothing pending? Fetch from source
103
+ waiter = { resolve, reject };
104
+ return fetchFromSource();
105
+ });
106
+ },
107
+ inject: (event) => {
108
+ const pending = consumeWaiter();
109
+ if (pending != null) {
110
+ pending.resolve(valueResult(event));
111
+ } else {
112
+ pendingInjectedEvents.push(event);
113
+ }
114
+ }
115
+ };
116
+ }
117
+
118
+ /**
119
+ * Splits a byte stream at line endings, emitting each line as a string.
120
+ */
121
+ export function extractJsonLines(
122
+ source: SimpleAsyncIterator<Uint8Array>,
123
+ decoder: TextDecoder
124
+ ): SimpleAsyncIterator<string> {
125
+ let buffer = '';
126
+ const pendingLines: string[] = [];
127
+ let isFinalEvent = false;
128
+
129
+ return {
130
+ next: async () => {
131
+ while (true) {
132
+ if (isFinalEvent) {
133
+ return doneResult;
134
+ }
135
+
136
+ {
137
+ const first = pendingLines.shift();
138
+ if (first) {
139
+ return { done: false, value: first };
140
+ }
141
+ }
142
+
143
+ const { done, value } = await source.next();
144
+ if (done) {
145
+ const remaining = buffer.trim();
146
+ if (remaining.length != 0) {
147
+ isFinalEvent = true;
148
+ return { done: false, value: remaining };
149
+ }
150
+
151
+ return doneResult;
152
+ }
153
+
154
+ const data = decoder.decode(value, { stream: true });
155
+ buffer += data;
156
+
157
+ const lines = buffer.split('\n');
158
+ for (let i = 0; i < lines.length - 1; i++) {
159
+ const l = lines[i].trim();
160
+ if (l.length > 0) {
161
+ pendingLines.push(l);
162
+ }
163
+ }
164
+
165
+ buffer = lines[lines.length - 1];
166
+ }
167
+ }
168
+ };
169
+ }
170
+
171
+ /**
172
+ * Splits a concatenated stream of BSON objects by emitting individual objects.
173
+ */
174
+ export function extractBsonObjects(source: SimpleAsyncIterator<Uint8Array>): SimpleAsyncIterator<Uint8Array> {
175
+ // Fully read but not emitted yet.
176
+ const completedObjects: Uint8Array[] = [];
177
+
178
+ // Whether source has returned { done: true }. We do the same once completed objects have been emitted.
179
+ let isDone = false;
180
+
181
+ const lengthBuffer = new DataView(new ArrayBuffer(4));
182
+ let objectBody: Uint8Array | null = null;
183
+ // If we're parsing the length field, a number between 1 and 4 (inclusive) describing remaining bytes in the header.
184
+ // If we're consuming a document, the bytes remaining.
185
+ let remainingLength = 4;
186
+
187
+ return {
188
+ async next(): Promise<IteratorResult<Uint8Array>> {
189
+ while (true) {
190
+ // Before fetching new data from upstream, return completed objects.
191
+ if (completedObjects.length) {
192
+ return valueResult(completedObjects.shift()!);
193
+ }
194
+ if (isDone) {
195
+ return doneResult;
196
+ }
197
+
198
+ const upstreamEvent = await source.next();
199
+ if (upstreamEvent.done) {
200
+ isDone = true;
201
+ if (objectBody || remainingLength != 4) {
202
+ throw new Error('illegal end of stream in BSON object');
203
+ }
204
+ return doneResult;
205
+ }
206
+
207
+ const chunk = upstreamEvent.value;
208
+ for (let i = 0; i < chunk.length; ) {
209
+ const availableInData = chunk.length - i;
210
+
211
+ if (objectBody) {
212
+ // We're in the middle of reading a BSON document.
213
+ const bytesToRead = Math.min(availableInData, remainingLength);
214
+ const copySource = new Uint8Array(chunk.buffer, chunk.byteOffset + i, bytesToRead);
215
+ objectBody.set(copySource, objectBody.length - remainingLength);
216
+ i += bytesToRead;
217
+ remainingLength -= bytesToRead;
218
+
219
+ if (remainingLength == 0) {
220
+ completedObjects.push(objectBody);
221
+
222
+ // Prepare to read another document, starting with its length
223
+ objectBody = null;
224
+ remainingLength = 4;
225
+ }
226
+ } else {
227
+ // Copy up to 4 bytes into lengthBuffer, depending on how many we still need.
228
+ const bytesToRead = Math.min(availableInData, remainingLength);
229
+ for (let j = 0; j < bytesToRead; j++) {
230
+ lengthBuffer.setUint8(4 - remainingLength + j, chunk[i + j]);
231
+ }
232
+ i += bytesToRead;
233
+ remainingLength -= bytesToRead;
234
+
235
+ if (remainingLength == 0) {
236
+ // Transition from reading length header to reading document. Subtracting 4 because the length of the
237
+ // header is included in length.
238
+ const length = lengthBuffer.getInt32(0, true /* little endian */);
239
+ remainingLength = length - 4;
240
+ if (remainingLength < 1) {
241
+ throw new Error(`invalid length for bson: ${length}`);
242
+ }
243
+
244
+ objectBody = new Uint8Array(length);
245
+ new DataView(objectBody.buffer).setInt32(0, length, true);
246
+ }
247
+ }
248
+ }
249
+ }
250
+ }
251
+ };
252
+ }
@@ -1,62 +0,0 @@
1
- import { ILogger } from 'js-logger';
2
- import { BaseListener, BaseObserver } from './BaseObserver.js';
3
- export type DataStreamOptions<ParsedData, SourceData> = {
4
- mapLine?: (line: SourceData) => ParsedData;
5
- /**
6
- * Close the stream if any consumer throws an error
7
- */
8
- closeOnError?: boolean;
9
- pressure?: {
10
- highWaterMark?: number;
11
- lowWaterMark?: number;
12
- };
13
- logger?: ILogger;
14
- };
15
- export type DataStreamCallback<Data extends any = any> = (data: Data) => Promise<void>;
16
- export interface DataStreamListener<Data extends any = any> extends BaseListener {
17
- data: (data: Data) => Promise<void>;
18
- closed: () => void;
19
- error: (error: Error) => void;
20
- highWater: () => Promise<void>;
21
- lowWater: () => Promise<void>;
22
- }
23
- export declare const DEFAULT_PRESSURE_LIMITS: {
24
- highWater: number;
25
- lowWater: number;
26
- };
27
- /**
28
- * A very basic implementation of a data stream with backpressure support which does not use
29
- * native JS streams or async iterators.
30
- * This is handy for environments such as React Native which need polyfills for the above.
31
- */
32
- export declare class DataStream<ParsedData, SourceData = any> extends BaseObserver<DataStreamListener<ParsedData>> {
33
- protected options?: DataStreamOptions<ParsedData, SourceData> | undefined;
34
- dataQueue: SourceData[];
35
- protected isClosed: boolean;
36
- protected processingPromise: Promise<void> | null;
37
- protected notifyDataAdded: (() => void) | null;
38
- protected logger: ILogger;
39
- protected mapLine: (line: SourceData) => ParsedData;
40
- constructor(options?: DataStreamOptions<ParsedData, SourceData> | undefined);
41
- get highWatermark(): number;
42
- get lowWatermark(): number;
43
- get closed(): boolean;
44
- close(): Promise<void>;
45
- /**
46
- * Enqueues data for the consumers to read
47
- */
48
- enqueueData(data: SourceData): void;
49
- /**
50
- * Reads data once from the data stream
51
- * @returns a Data payload or Null if the stream closed.
52
- */
53
- read(): Promise<ParsedData | null>;
54
- /**
55
- * Executes a callback for each data item in the stream
56
- */
57
- forEach(callback: DataStreamCallback<ParsedData>): () => void;
58
- protected processQueue(): Promise<void> | undefined;
59
- protected hasDataReader(): boolean;
60
- protected _processQueue(): Promise<void>;
61
- protected iterateAsyncErrored(cb: (l: Partial<DataStreamListener<ParsedData>>) => Promise<void>): Promise<void>;
62
- }