@powersync/common 1.40.0 → 1.41.1
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 +10809 -22
- package/dist/bundle.cjs.map +1 -0
- package/dist/bundle.mjs +10730 -22
- package/dist/bundle.mjs.map +1 -0
- package/dist/bundle.node.cjs +10809 -0
- package/dist/bundle.node.cjs.map +1 -0
- package/dist/bundle.node.mjs +10730 -0
- package/dist/bundle.node.mjs.map +1 -0
- package/dist/index.d.cts +5 -1
- package/lib/client/AbstractPowerSyncDatabase.js +1 -0
- package/lib/client/AbstractPowerSyncDatabase.js.map +1 -0
- package/lib/client/AbstractPowerSyncOpenFactory.js +1 -0
- package/lib/client/AbstractPowerSyncOpenFactory.js.map +1 -0
- package/lib/client/ConnectionManager.js +1 -0
- package/lib/client/ConnectionManager.js.map +1 -0
- package/lib/client/CustomQuery.js +1 -0
- package/lib/client/CustomQuery.js.map +1 -0
- package/lib/client/Query.js +1 -0
- package/lib/client/Query.js.map +1 -0
- package/lib/client/SQLOpenFactory.js +1 -0
- package/lib/client/SQLOpenFactory.js.map +1 -0
- package/lib/client/compilableQueryWatch.js +1 -0
- package/lib/client/compilableQueryWatch.js.map +1 -0
- package/lib/client/connection/PowerSyncBackendConnector.js +1 -0
- package/lib/client/connection/PowerSyncBackendConnector.js.map +1 -0
- package/lib/client/connection/PowerSyncCredentials.js +1 -0
- package/lib/client/connection/PowerSyncCredentials.js.map +1 -0
- package/lib/client/constants.js +1 -0
- package/lib/client/constants.js.map +1 -0
- package/lib/client/runOnSchemaChange.js +1 -0
- package/lib/client/runOnSchemaChange.js.map +1 -0
- package/lib/client/sync/bucket/BucketStorageAdapter.js +1 -0
- package/lib/client/sync/bucket/BucketStorageAdapter.js.map +1 -0
- package/lib/client/sync/bucket/CrudBatch.js +1 -0
- package/lib/client/sync/bucket/CrudBatch.js.map +1 -0
- package/lib/client/sync/bucket/CrudEntry.js +1 -0
- package/lib/client/sync/bucket/CrudEntry.js.map +1 -0
- package/lib/client/sync/bucket/CrudTransaction.js +1 -0
- package/lib/client/sync/bucket/CrudTransaction.js.map +1 -0
- package/lib/client/sync/bucket/OpType.js +1 -0
- package/lib/client/sync/bucket/OpType.js.map +1 -0
- package/lib/client/sync/bucket/OplogEntry.js +1 -0
- package/lib/client/sync/bucket/OplogEntry.js.map +1 -0
- package/lib/client/sync/bucket/SqliteBucketStorage.js +1 -0
- package/lib/client/sync/bucket/SqliteBucketStorage.js.map +1 -0
- package/lib/client/sync/bucket/SyncDataBatch.js +1 -0
- package/lib/client/sync/bucket/SyncDataBatch.js.map +1 -0
- package/lib/client/sync/bucket/SyncDataBucket.js +1 -0
- package/lib/client/sync/bucket/SyncDataBucket.js.map +1 -0
- package/lib/client/sync/stream/AbstractRemote.d.ts +5 -0
- package/lib/client/sync/stream/AbstractRemote.js +9 -2
- package/lib/client/sync/stream/AbstractRemote.js.map +1 -0
- package/lib/client/sync/stream/AbstractStreamingSyncImplementation.js +1 -0
- package/lib/client/sync/stream/AbstractStreamingSyncImplementation.js.map +1 -0
- package/lib/client/sync/stream/WebsocketClientTransport.js +1 -0
- package/lib/client/sync/stream/WebsocketClientTransport.js.map +1 -0
- package/lib/client/sync/stream/core-instruction.js +1 -0
- package/lib/client/sync/stream/core-instruction.js.map +1 -0
- package/lib/client/sync/stream/streaming-sync-types.js +1 -0
- package/lib/client/sync/stream/streaming-sync-types.js.map +1 -0
- package/lib/client/sync/sync-streams.js +1 -0
- package/lib/client/sync/sync-streams.js.map +1 -0
- package/lib/client/triggers/TriggerManager.js +1 -0
- package/lib/client/triggers/TriggerManager.js.map +1 -0
- package/lib/client/triggers/TriggerManagerImpl.js +1 -0
- package/lib/client/triggers/TriggerManagerImpl.js.map +1 -0
- package/lib/client/triggers/sanitizeSQL.js +1 -0
- package/lib/client/triggers/sanitizeSQL.js.map +1 -0
- package/lib/client/watched/GetAllQuery.js +1 -0
- package/lib/client/watched/GetAllQuery.js.map +1 -0
- package/lib/client/watched/WatchedQuery.js +1 -0
- package/lib/client/watched/WatchedQuery.js.map +1 -0
- package/lib/client/watched/processors/AbstractQueryProcessor.js +1 -0
- package/lib/client/watched/processors/AbstractQueryProcessor.js.map +1 -0
- package/lib/client/watched/processors/DifferentialQueryProcessor.js +1 -0
- package/lib/client/watched/processors/DifferentialQueryProcessor.js.map +1 -0
- package/lib/client/watched/processors/OnChangeQueryProcessor.js +1 -0
- package/lib/client/watched/processors/OnChangeQueryProcessor.js.map +1 -0
- package/lib/client/watched/processors/comparators.js +1 -0
- package/lib/client/watched/processors/comparators.js.map +1 -0
- package/lib/db/DBAdapter.js +1 -0
- package/lib/db/DBAdapter.js.map +1 -0
- package/lib/db/crud/SyncProgress.js +1 -0
- package/lib/db/crud/SyncProgress.js.map +1 -0
- package/lib/db/crud/SyncStatus.js +1 -0
- package/lib/db/crud/SyncStatus.js.map +1 -0
- package/lib/db/crud/UploadQueueStatus.js +1 -0
- package/lib/db/crud/UploadQueueStatus.js.map +1 -0
- package/lib/db/schema/Column.js +1 -0
- package/lib/db/schema/Column.js.map +1 -0
- package/lib/db/schema/Index.js +1 -0
- package/lib/db/schema/Index.js.map +1 -0
- package/lib/db/schema/IndexedColumn.js +1 -0
- package/lib/db/schema/IndexedColumn.js.map +1 -0
- package/lib/db/schema/RawTable.js +1 -0
- package/lib/db/schema/RawTable.js.map +1 -0
- package/lib/db/schema/Schema.d.ts +0 -1
- package/lib/db/schema/Schema.js +4 -8
- package/lib/db/schema/Schema.js.map +1 -0
- package/lib/db/schema/Table.js +1 -0
- package/lib/db/schema/Table.js.map +1 -0
- package/lib/db/schema/TableV2.js +1 -0
- package/lib/db/schema/TableV2.js.map +1 -0
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -0
- package/lib/types/types.js +1 -0
- package/lib/types/types.js.map +1 -0
- package/lib/utils/AbortOperation.js +1 -0
- package/lib/utils/AbortOperation.js.map +1 -0
- package/lib/utils/BaseObserver.js +1 -0
- package/lib/utils/BaseObserver.js.map +1 -0
- package/lib/utils/ControlledExecutor.js +1 -0
- package/lib/utils/ControlledExecutor.js.map +1 -0
- package/lib/utils/DataStream.js +1 -0
- package/lib/utils/DataStream.js.map +1 -0
- package/lib/utils/Logger.js +1 -0
- package/lib/utils/Logger.js.map +1 -0
- package/lib/utils/MetaBaseObserver.js +1 -0
- package/lib/utils/MetaBaseObserver.js.map +1 -0
- package/lib/utils/async.js +1 -0
- package/lib/utils/async.js.map +1 -0
- package/lib/utils/mutex.js +1 -0
- package/lib/utils/mutex.js.map +1 -0
- package/lib/utils/parseQuery.js +1 -0
- package/lib/utils/parseQuery.js.map +1 -0
- package/package.json +23 -15
- package/src/client/AbstractPowerSyncDatabase.ts +1343 -0
- package/src/client/AbstractPowerSyncOpenFactory.ts +39 -0
- package/src/client/ConnectionManager.ts +402 -0
- package/src/client/CustomQuery.ts +56 -0
- package/src/client/Query.ts +106 -0
- package/src/client/SQLOpenFactory.ts +55 -0
- package/src/client/compilableQueryWatch.ts +55 -0
- package/src/client/connection/PowerSyncBackendConnector.ts +25 -0
- package/src/client/connection/PowerSyncCredentials.ts +5 -0
- package/src/client/constants.ts +1 -0
- package/src/client/runOnSchemaChange.ts +31 -0
- package/src/client/sync/bucket/BucketStorageAdapter.ts +118 -0
- package/src/client/sync/bucket/CrudBatch.ts +21 -0
- package/src/client/sync/bucket/CrudEntry.ts +172 -0
- package/src/client/sync/bucket/CrudTransaction.ts +21 -0
- package/src/client/sync/bucket/OpType.ts +23 -0
- package/src/client/sync/bucket/OplogEntry.ts +50 -0
- package/src/client/sync/bucket/SqliteBucketStorage.ts +395 -0
- package/src/client/sync/bucket/SyncDataBatch.ts +11 -0
- package/src/client/sync/bucket/SyncDataBucket.ts +49 -0
- package/src/client/sync/stream/AbstractRemote.ts +626 -0
- package/src/client/sync/stream/AbstractStreamingSyncImplementation.ts +1258 -0
- package/src/client/sync/stream/WebsocketClientTransport.ts +80 -0
- package/src/client/sync/stream/core-instruction.ts +99 -0
- package/src/client/sync/stream/streaming-sync-types.ts +205 -0
- package/src/client/sync/sync-streams.ts +107 -0
- package/src/client/triggers/TriggerManager.ts +384 -0
- package/src/client/triggers/TriggerManagerImpl.ts +314 -0
- package/src/client/triggers/sanitizeSQL.ts +66 -0
- package/src/client/watched/GetAllQuery.ts +46 -0
- package/src/client/watched/WatchedQuery.ts +121 -0
- package/src/client/watched/processors/AbstractQueryProcessor.ts +226 -0
- package/src/client/watched/processors/DifferentialQueryProcessor.ts +305 -0
- package/src/client/watched/processors/OnChangeQueryProcessor.ts +122 -0
- package/src/client/watched/processors/comparators.ts +57 -0
- package/src/db/DBAdapter.ts +134 -0
- package/src/db/crud/SyncProgress.ts +100 -0
- package/src/db/crud/SyncStatus.ts +308 -0
- package/src/db/crud/UploadQueueStatus.ts +20 -0
- package/src/db/schema/Column.ts +60 -0
- package/src/db/schema/Index.ts +39 -0
- package/src/db/schema/IndexedColumn.ts +42 -0
- package/src/db/schema/RawTable.ts +67 -0
- package/src/db/schema/Schema.ts +76 -0
- package/src/db/schema/Table.ts +359 -0
- package/src/db/schema/TableV2.ts +9 -0
- package/src/index.ts +52 -0
- package/src/types/types.ts +9 -0
- package/src/utils/AbortOperation.ts +17 -0
- package/src/utils/BaseObserver.ts +41 -0
- package/src/utils/ControlledExecutor.ts +72 -0
- package/src/utils/DataStream.ts +211 -0
- package/src/utils/Logger.ts +47 -0
- package/src/utils/MetaBaseObserver.ts +81 -0
- package/src/utils/async.ts +61 -0
- package/src/utils/mutex.ts +34 -0
- package/src/utils/parseQuery.ts +25 -0
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import Logger, { ILogger } from 'js-logger';
|
|
2
|
+
import { BaseListener, BaseObserver } from './BaseObserver.js';
|
|
3
|
+
|
|
4
|
+
export type DataStreamOptions<ParsedData, SourceData> = {
|
|
5
|
+
mapLine?: (line: SourceData) => ParsedData;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Close the stream if any consumer throws an error
|
|
9
|
+
*/
|
|
10
|
+
closeOnError?: boolean;
|
|
11
|
+
pressure?: {
|
|
12
|
+
highWaterMark?: number;
|
|
13
|
+
lowWaterMark?: number;
|
|
14
|
+
};
|
|
15
|
+
logger?: ILogger;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type DataStreamCallback<Data extends any = any> = (data: Data) => Promise<void>;
|
|
19
|
+
|
|
20
|
+
export interface DataStreamListener<Data extends any = any> extends BaseListener {
|
|
21
|
+
data: (data: Data) => Promise<void>;
|
|
22
|
+
closed: () => void;
|
|
23
|
+
error: (error: Error) => void;
|
|
24
|
+
highWater: () => Promise<void>;
|
|
25
|
+
lowWater: () => Promise<void>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const DEFAULT_PRESSURE_LIMITS = {
|
|
29
|
+
highWater: 10,
|
|
30
|
+
lowWater: 0
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* A very basic implementation of a data stream with backpressure support which does not use
|
|
35
|
+
* native JS streams or async iterators.
|
|
36
|
+
* This is handy for environments such as React Native which need polyfills for the above.
|
|
37
|
+
*/
|
|
38
|
+
export class DataStream<ParsedData, SourceData = any> extends BaseObserver<DataStreamListener<ParsedData>> {
|
|
39
|
+
dataQueue: SourceData[];
|
|
40
|
+
|
|
41
|
+
protected isClosed: boolean;
|
|
42
|
+
|
|
43
|
+
protected processingPromise: Promise<void> | null;
|
|
44
|
+
protected notifyDataAdded: (() => void) | null;
|
|
45
|
+
|
|
46
|
+
protected logger: ILogger;
|
|
47
|
+
|
|
48
|
+
protected mapLine: (line: SourceData) => ParsedData;
|
|
49
|
+
|
|
50
|
+
constructor(protected options?: DataStreamOptions<ParsedData, SourceData>) {
|
|
51
|
+
super();
|
|
52
|
+
this.processingPromise = null;
|
|
53
|
+
this.isClosed = false;
|
|
54
|
+
this.dataQueue = [];
|
|
55
|
+
this.mapLine = options?.mapLine ?? ((line) => line as any);
|
|
56
|
+
|
|
57
|
+
this.logger = options?.logger ?? Logger.get('DataStream');
|
|
58
|
+
|
|
59
|
+
if (options?.closeOnError) {
|
|
60
|
+
const l = this.registerListener({
|
|
61
|
+
error: (ex) => {
|
|
62
|
+
l?.();
|
|
63
|
+
this.close();
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
get highWatermark() {
|
|
70
|
+
return this.options?.pressure?.highWaterMark ?? DEFAULT_PRESSURE_LIMITS.highWater;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
get lowWatermark() {
|
|
74
|
+
return this.options?.pressure?.lowWaterMark ?? DEFAULT_PRESSURE_LIMITS.lowWater;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
get closed() {
|
|
78
|
+
return this.isClosed;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async close() {
|
|
82
|
+
this.isClosed = true;
|
|
83
|
+
await this.processingPromise;
|
|
84
|
+
this.iterateListeners((l) => l.closed?.());
|
|
85
|
+
// Discard any data in the queue
|
|
86
|
+
this.dataQueue = [];
|
|
87
|
+
this.listeners.clear();
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Enqueues data for the consumers to read
|
|
92
|
+
*/
|
|
93
|
+
enqueueData(data: SourceData) {
|
|
94
|
+
if (this.isClosed) {
|
|
95
|
+
throw new Error('Cannot enqueue data into closed stream.');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
this.dataQueue.push(data);
|
|
99
|
+
this.notifyDataAdded?.();
|
|
100
|
+
|
|
101
|
+
this.processQueue();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Reads data once from the data stream
|
|
106
|
+
* @returns a Data payload or Null if the stream closed.
|
|
107
|
+
*/
|
|
108
|
+
async read(): Promise<ParsedData | null> {
|
|
109
|
+
if (this.closed) {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return new Promise((resolve, reject) => {
|
|
114
|
+
const l = this.registerListener({
|
|
115
|
+
data: async (data) => {
|
|
116
|
+
resolve(data);
|
|
117
|
+
// Remove the listener
|
|
118
|
+
l?.();
|
|
119
|
+
},
|
|
120
|
+
closed: () => {
|
|
121
|
+
resolve(null);
|
|
122
|
+
l?.();
|
|
123
|
+
},
|
|
124
|
+
error: (ex) => {
|
|
125
|
+
reject(ex);
|
|
126
|
+
l?.();
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
this.processQueue();
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Executes a callback for each data item in the stream
|
|
136
|
+
*/
|
|
137
|
+
forEach(callback: DataStreamCallback<ParsedData>) {
|
|
138
|
+
if (this.dataQueue.length <= this.lowWatermark) {
|
|
139
|
+
this.iterateAsyncErrored(async (l) => l.lowWater?.());
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return this.registerListener({
|
|
143
|
+
data: callback
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
protected processQueue() {
|
|
148
|
+
if (this.processingPromise) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const promise = (this.processingPromise = this._processQueue());
|
|
153
|
+
promise.finally(() => {
|
|
154
|
+
return (this.processingPromise = null);
|
|
155
|
+
});
|
|
156
|
+
return promise;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
protected hasDataReader() {
|
|
160
|
+
return Array.from(this.listeners.values()).some((l) => !!l.data);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
protected async _processQueue() {
|
|
164
|
+
/**
|
|
165
|
+
* Allow listeners to mutate the queue before processing.
|
|
166
|
+
* This allows for operations such as dropping or compressing data
|
|
167
|
+
* on high water or requesting more data on low water.
|
|
168
|
+
*/
|
|
169
|
+
if (this.dataQueue.length >= this.highWatermark) {
|
|
170
|
+
await this.iterateAsyncErrored(async (l) => l.highWater?.());
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (this.isClosed || !this.hasDataReader()) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (this.dataQueue.length) {
|
|
178
|
+
const data = this.dataQueue.shift()!;
|
|
179
|
+
const mapped = this.mapLine(data);
|
|
180
|
+
await this.iterateAsyncErrored(async (l) => l.data?.(mapped));
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (this.dataQueue.length <= this.lowWatermark) {
|
|
184
|
+
const dataAdded = new Promise<void>((resolve) => {
|
|
185
|
+
this.notifyDataAdded = resolve;
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
await Promise.race([this.iterateAsyncErrored(async (l) => l.lowWater?.()), dataAdded]);
|
|
189
|
+
this.notifyDataAdded = null;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (this.dataQueue.length > 0) {
|
|
193
|
+
// Next tick
|
|
194
|
+
setTimeout(() => this.processQueue());
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
protected async iterateAsyncErrored(cb: (l: Partial<DataStreamListener<ParsedData>>) => Promise<void>) {
|
|
199
|
+
// Important: We need to copy the listeners, as calling a listener could result in adding another
|
|
200
|
+
// listener, resulting in infinite loops.
|
|
201
|
+
const listeners = Array.from(this.listeners.values());
|
|
202
|
+
for (let i of listeners) {
|
|
203
|
+
try {
|
|
204
|
+
await cb(i);
|
|
205
|
+
} catch (ex) {
|
|
206
|
+
this.logger.error(ex);
|
|
207
|
+
this.iterateListeners((l) => l.error?.(ex));
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import Logger, { type ILogger, type ILogLevel } from 'js-logger';
|
|
2
|
+
|
|
3
|
+
export { GlobalLogger, ILogger, ILoggerOpts, ILogHandler, ILogLevel } from 'js-logger';
|
|
4
|
+
|
|
5
|
+
const TypedLogger: ILogger = Logger as any;
|
|
6
|
+
|
|
7
|
+
export const LogLevel = {
|
|
8
|
+
TRACE: TypedLogger.TRACE,
|
|
9
|
+
DEBUG: TypedLogger.DEBUG,
|
|
10
|
+
INFO: TypedLogger.INFO,
|
|
11
|
+
TIME: TypedLogger.TIME,
|
|
12
|
+
WARN: TypedLogger.WARN,
|
|
13
|
+
ERROR: TypedLogger.ERROR,
|
|
14
|
+
OFF: TypedLogger.OFF
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export interface CreateLoggerOptions {
|
|
18
|
+
logLevel?: ILogLevel;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Retrieves the base (default) logger instance.
|
|
23
|
+
*
|
|
24
|
+
* This base logger controls the default logging configuration and is shared
|
|
25
|
+
* across all loggers created with `createLogger`. Adjusting settings on this
|
|
26
|
+
* base logger affects all loggers derived from it unless explicitly overridden.
|
|
27
|
+
*
|
|
28
|
+
*/
|
|
29
|
+
export function createBaseLogger() {
|
|
30
|
+
return Logger;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Creates and configures a new named logger based on the base logger.
|
|
35
|
+
*
|
|
36
|
+
* Named loggers allow specific modules or areas of your application to have
|
|
37
|
+
* their own logging levels and behaviors. These loggers inherit configuration
|
|
38
|
+
* from the base logger by default but can override settings independently.
|
|
39
|
+
*/
|
|
40
|
+
export function createLogger(name: string, options: CreateLoggerOptions = {}): ILogger {
|
|
41
|
+
const logger = Logger.get(name);
|
|
42
|
+
if (options.logLevel) {
|
|
43
|
+
logger.setLevel(options.logLevel);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return logger;
|
|
47
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { BaseListener, BaseObserver, BaseObserverInterface } from './BaseObserver.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Represents the counts of listeners for each event type in a BaseListener.
|
|
5
|
+
*/
|
|
6
|
+
export type ListenerCounts<Listener extends BaseListener> = Partial<Record<keyof Listener, number>> & {
|
|
7
|
+
total: number;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Meta listener which reports the counts of listeners for each event type.
|
|
12
|
+
*/
|
|
13
|
+
export interface MetaListener<ParentListener extends BaseListener> extends BaseListener {
|
|
14
|
+
listenersChanged?: (counts: ListenerCounts<ParentListener>) => void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface ListenerMetaManager<Listener extends BaseListener>
|
|
18
|
+
extends BaseObserverInterface<MetaListener<Listener>> {
|
|
19
|
+
counts: ListenerCounts<Listener>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface MetaBaseObserverInterface<Listener extends BaseListener> extends BaseObserverInterface<Listener> {
|
|
23
|
+
listenerMeta: ListenerMetaManager<Listener>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* A BaseObserver that tracks the counts of listeners for each event type.
|
|
28
|
+
*/
|
|
29
|
+
export class MetaBaseObserver<Listener extends BaseListener>
|
|
30
|
+
extends BaseObserver<Listener>
|
|
31
|
+
implements MetaBaseObserverInterface<Listener>
|
|
32
|
+
{
|
|
33
|
+
protected get listenerCounts(): ListenerCounts<Listener> {
|
|
34
|
+
const counts = {} as Partial<Record<keyof Listener, number>>;
|
|
35
|
+
let total = 0;
|
|
36
|
+
for (const listener of this.listeners) {
|
|
37
|
+
for (const key in listener) {
|
|
38
|
+
if (listener[key]) {
|
|
39
|
+
counts[key] = (counts[key] ?? 0) + 1;
|
|
40
|
+
total++;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
...counts,
|
|
46
|
+
total
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
get listenerMeta(): ListenerMetaManager<Listener> {
|
|
51
|
+
return {
|
|
52
|
+
counts: this.listenerCounts,
|
|
53
|
+
// Allows registering a meta listener that will be notified of changes in listener counts
|
|
54
|
+
registerListener: (listener: Partial<MetaListener<Listener>>) => {
|
|
55
|
+
return this.metaListener.registerListener(listener);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
protected metaListener: BaseObserver<MetaListener<Listener>>;
|
|
61
|
+
|
|
62
|
+
constructor() {
|
|
63
|
+
super();
|
|
64
|
+
this.metaListener = new BaseObserver<MetaListener<Listener>>();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
registerListener(listener: Partial<Listener>): () => void {
|
|
68
|
+
const dispose = super.registerListener(listener);
|
|
69
|
+
const updatedCount = this.listenerCounts;
|
|
70
|
+
this.metaListener.iterateListeners((l) => {
|
|
71
|
+
l.listenersChanged?.(updatedCount);
|
|
72
|
+
});
|
|
73
|
+
return () => {
|
|
74
|
+
dispose();
|
|
75
|
+
const updatedCount = this.listenerCounts;
|
|
76
|
+
this.metaListener.iterateListeners((l) => {
|
|
77
|
+
l.listenersChanged?.(updatedCount);
|
|
78
|
+
});
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A ponyfill for `Symbol.asyncIterator` that is compatible with the
|
|
3
|
+
* [recommended 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)
|
|
4
|
+
* we recommend for React Native.
|
|
5
|
+
*
|
|
6
|
+
* As long as we use this symbol (instead of `for await` and `async *`) in this package, we can be compatible with async
|
|
7
|
+
* iterators without requiring them.
|
|
8
|
+
*/
|
|
9
|
+
export const symbolAsyncIterator: typeof Symbol.asyncIterator =
|
|
10
|
+
Symbol.asyncIterator ?? Symbol.for('Symbol.asyncIterator');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Throttle a function to be called at most once every "wait" milliseconds,
|
|
14
|
+
* on the trailing edge.
|
|
15
|
+
*
|
|
16
|
+
* Roughly equivalent to lodash/throttle with {leading: false, trailing: true}
|
|
17
|
+
*/
|
|
18
|
+
export function throttleTrailing(func: () => void, wait: number) {
|
|
19
|
+
let timeoutId: ReturnType<typeof setTimeout> | null = null;
|
|
20
|
+
|
|
21
|
+
const later = () => {
|
|
22
|
+
func();
|
|
23
|
+
timeoutId = null;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
return function () {
|
|
27
|
+
if (timeoutId == null) {
|
|
28
|
+
timeoutId = setTimeout(later, wait);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Throttle a function to be called at most once every "wait" milliseconds,
|
|
35
|
+
* on the leading and trailing edge.
|
|
36
|
+
*
|
|
37
|
+
* Roughly equivalent to lodash/throttle with {leading: true, trailing: true}
|
|
38
|
+
*/
|
|
39
|
+
export function throttleLeadingTrailing(func: () => void, wait: number) {
|
|
40
|
+
let timeoutId: ReturnType<typeof setTimeout> | null = null;
|
|
41
|
+
let lastCallTime: number = 0;
|
|
42
|
+
|
|
43
|
+
const invokeFunction = () => {
|
|
44
|
+
func();
|
|
45
|
+
lastCallTime = Date.now();
|
|
46
|
+
timeoutId = null;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
return function () {
|
|
50
|
+
const now = Date.now();
|
|
51
|
+
const timeToWait = wait - (now - lastCallTime);
|
|
52
|
+
|
|
53
|
+
if (timeToWait <= 0) {
|
|
54
|
+
// Leading edge: Call the function immediately if enough time has passed
|
|
55
|
+
invokeFunction();
|
|
56
|
+
} else if (!timeoutId) {
|
|
57
|
+
// Set a timeout for the trailing edge if not already set
|
|
58
|
+
timeoutId = setTimeout(invokeFunction, timeToWait);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Mutex } from 'async-mutex';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Wrapper for async-mutex runExclusive, which allows for a timeout on each exclusive lock.
|
|
5
|
+
*/
|
|
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);
|
|
24
|
+
}
|
|
25
|
+
if (timedOut) return;
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
resolve(await callback());
|
|
29
|
+
} catch (ex) {
|
|
30
|
+
reject(ex);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { CompilableQuery } from '../types/types.js';
|
|
2
|
+
|
|
3
|
+
export interface ParsedQuery {
|
|
4
|
+
sqlStatement: string;
|
|
5
|
+
parameters: any[];
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const parseQuery = <T>(query: string | CompilableQuery<T>, parameters: any[]): ParsedQuery => {
|
|
9
|
+
let sqlStatement: string;
|
|
10
|
+
|
|
11
|
+
if (typeof query == 'string') {
|
|
12
|
+
sqlStatement = query;
|
|
13
|
+
} else {
|
|
14
|
+
const hasAdditionalParameters = parameters.length > 0;
|
|
15
|
+
if (hasAdditionalParameters) {
|
|
16
|
+
throw new Error('You cannot pass parameters to a compiled query.');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const compiled = query.compile();
|
|
20
|
+
sqlStatement = compiled.sql;
|
|
21
|
+
parameters = compiled.parameters as any[];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return { sqlStatement, parameters: parameters };
|
|
25
|
+
};
|