@uwdata/mosaic-core 0.17.0 → 0.18.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/LICENSE +47 -0
- package/README.md +0 -1
- package/dist/src/Coordinator.d.ts +147 -0
- package/dist/src/Coordinator.d.ts.map +1 -0
- package/dist/src/Coordinator.js +269 -0
- package/dist/src/Coordinator.js.map +1 -0
- package/dist/src/MosaicClient.d.ts +138 -0
- package/dist/src/MosaicClient.d.ts.map +1 -0
- package/dist/src/MosaicClient.js +213 -0
- package/dist/src/MosaicClient.js.map +1 -0
- package/dist/src/Param.d.ts +56 -0
- package/dist/src/Param.d.ts.map +1 -0
- package/dist/src/Param.js +89 -0
- package/dist/src/Param.js.map +1 -0
- package/dist/src/QueryConsolidator.d.ts +11 -0
- package/dist/src/QueryConsolidator.d.ts.map +1 -0
- package/dist/src/QueryConsolidator.js +249 -0
- package/dist/src/QueryConsolidator.js.map +1 -0
- package/dist/src/QueryManager.d.ts +77 -0
- package/dist/src/QueryManager.d.ts.map +1 -0
- package/dist/src/QueryManager.js +174 -0
- package/dist/src/QueryManager.js.map +1 -0
- package/dist/src/Selection.d.ts +222 -0
- package/dist/src/Selection.d.ts.map +1 -0
- package/dist/src/Selection.js +319 -0
- package/dist/src/Selection.js.map +1 -0
- package/dist/src/SelectionClause.d.ts +192 -0
- package/dist/src/SelectionClause.d.ts.map +1 -0
- package/dist/src/SelectionClause.js +126 -0
- package/dist/src/SelectionClause.js.map +1 -0
- package/dist/src/connectors/Connector.d.ts +26 -0
- package/dist/src/connectors/Connector.d.ts.map +1 -0
- package/dist/src/connectors/Connector.js +2 -0
- package/dist/src/connectors/Connector.js.map +1 -0
- package/dist/src/connectors/rest.d.ts +24 -0
- package/dist/src/connectors/rest.d.ts.map +1 -0
- package/dist/src/connectors/rest.js +37 -0
- package/dist/src/connectors/rest.js.map +1 -0
- package/dist/src/connectors/socket.d.ts +40 -0
- package/dist/src/connectors/socket.d.ts.map +1 -0
- package/dist/src/connectors/socket.js +115 -0
- package/dist/src/connectors/socket.js.map +1 -0
- package/dist/src/connectors/wasm.d.ts +53 -0
- package/dist/src/connectors/wasm.d.ts.map +1 -0
- package/dist/src/connectors/wasm.js +113 -0
- package/dist/src/connectors/wasm.js.map +1 -0
- package/dist/src/index.d.ts +28 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +25 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/make-client.d.ts +35 -0
- package/dist/src/make-client.d.ts.map +1 -0
- package/dist/src/make-client.js +52 -0
- package/dist/src/make-client.js.map +1 -0
- package/dist/src/preagg/PreAggregator.d.ts +150 -0
- package/dist/src/preagg/PreAggregator.d.ts.map +1 -0
- package/dist/src/preagg/PreAggregator.js +382 -0
- package/dist/src/preagg/PreAggregator.js.map +1 -0
- package/dist/src/preagg/preagg-columns.d.ts +16 -0
- package/dist/src/preagg/preagg-columns.d.ts.map +1 -0
- package/dist/src/preagg/preagg-columns.js +95 -0
- package/dist/src/preagg/preagg-columns.js.map +1 -0
- package/dist/src/preagg/sufficient-statistics.d.ts +14 -0
- package/dist/src/preagg/sufficient-statistics.d.ts.map +1 -0
- package/dist/src/preagg/sufficient-statistics.js +446 -0
- package/dist/src/preagg/sufficient-statistics.js.map +1 -0
- package/dist/src/types.d.ts +77 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +2 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/util/AsyncDispatch.d.ts +121 -0
- package/dist/src/util/AsyncDispatch.d.ts.map +1 -0
- package/dist/src/util/AsyncDispatch.js +188 -0
- package/dist/src/util/AsyncDispatch.js.map +1 -0
- package/dist/src/util/cache.d.ts +19 -0
- package/dist/src/util/cache.d.ts.map +1 -0
- package/dist/src/util/cache.js +66 -0
- package/dist/src/util/cache.js.map +1 -0
- package/dist/src/util/decode-ipc.d.ts +12 -0
- package/dist/src/util/decode-ipc.d.ts.map +1 -0
- package/{src → dist/src}/util/decode-ipc.js +5 -6
- package/dist/src/util/decode-ipc.js.map +1 -0
- package/dist/src/util/distinct.d.ts +3 -0
- package/dist/src/util/distinct.d.ts.map +1 -0
- package/dist/src/util/distinct.js +16 -0
- package/dist/src/util/distinct.js.map +1 -0
- package/dist/src/util/field-info.d.ts +26 -0
- package/dist/src/util/field-info.d.ts.map +1 -0
- package/dist/src/util/field-info.js +91 -0
- package/dist/src/util/field-info.js.map +1 -0
- package/dist/src/util/hash.d.ts +2 -0
- package/dist/src/util/hash.d.ts.map +1 -0
- package/dist/src/util/hash.js +26 -0
- package/dist/src/util/hash.js.map +1 -0
- package/dist/src/util/is-activatable.d.ts +8 -0
- package/dist/src/util/is-activatable.d.ts.map +1 -0
- package/dist/src/util/is-activatable.js +10 -0
- package/dist/src/util/is-activatable.js.map +1 -0
- package/dist/src/util/is-arrow-table.d.ts +9 -0
- package/dist/src/util/is-arrow-table.d.ts.map +1 -0
- package/dist/src/util/is-arrow-table.js +11 -0
- package/dist/src/util/is-arrow-table.js.map +1 -0
- package/dist/src/util/js-type.d.ts +9 -0
- package/dist/src/util/js-type.d.ts.map +1 -0
- package/dist/src/util/js-type.js +59 -0
- package/dist/src/util/js-type.js.map +1 -0
- package/dist/src/util/priority-queue.d.ts +35 -0
- package/dist/src/util/priority-queue.d.ts.map +1 -0
- package/dist/src/util/priority-queue.js +81 -0
- package/dist/src/util/priority-queue.js.map +1 -0
- package/dist/src/util/query-result.d.ts +47 -0
- package/dist/src/util/query-result.d.ts.map +1 -0
- package/dist/src/util/query-result.js +83 -0
- package/dist/src/util/query-result.js.map +1 -0
- package/dist/src/util/synchronizer.d.ts +36 -0
- package/dist/src/util/synchronizer.d.ts.map +1 -0
- package/dist/src/util/synchronizer.js +52 -0
- package/dist/src/util/synchronizer.js.map +1 -0
- package/dist/src/util/throttle.d.ts +12 -0
- package/dist/src/util/throttle.d.ts.map +1 -0
- package/dist/src/util/throttle.js +51 -0
- package/dist/src/util/throttle.js.map +1 -0
- package/dist/src/util/to-data-columns.d.ts +22 -0
- package/dist/src/util/to-data-columns.d.ts.map +1 -0
- package/dist/src/util/to-data-columns.js +51 -0
- package/dist/src/util/to-data-columns.js.map +1 -0
- package/dist/src/util/void-logger.d.ts +13 -0
- package/dist/src/util/void-logger.d.ts.map +1 -0
- package/dist/src/util/void-logger.js +13 -0
- package/dist/src/util/void-logger.js.map +1 -0
- package/package.json +16 -10
- package/src/Coordinator.ts +367 -0
- package/src/{MosaicClient.js → MosaicClient.ts} +49 -43
- package/src/{Param.js → Param.ts} +29 -28
- package/src/{QueryConsolidator.js → QueryConsolidator.ts} +81 -58
- package/src/{QueryManager.js → QueryManager.ts} +61 -54
- package/src/Selection.ts +388 -0
- package/src/SelectionClause.ts +275 -0
- package/src/connectors/Connector.ts +6 -6
- package/src/connectors/rest.ts +56 -0
- package/src/connectors/{socket.js → socket.ts} +53 -42
- package/src/connectors/{wasm.js → wasm.ts} +46 -62
- package/src/{index.js → index.ts} +13 -1
- package/src/make-client.ts +93 -0
- package/src/preagg/{PreAggregator.js → PreAggregator.ts} +164 -145
- package/src/preagg/{preagg-columns.js → preagg-columns.ts} +27 -24
- package/src/preagg/{sufficient-statistics.js → sufficient-statistics.ts} +160 -110
- package/src/types.ts +24 -9
- package/src/util/{AsyncDispatch.js → AsyncDispatch.ts} +62 -43
- package/src/util/{cache.js → cache.ts} +25 -15
- package/src/util/decode-ipc.ts +15 -0
- package/src/util/{distinct.js → distinct.ts} +3 -3
- package/src/util/{field-info.js → field-info.ts} +31 -32
- package/src/util/{hash.js → hash.ts} +4 -4
- package/src/util/is-activatable.ts +11 -0
- package/src/util/is-arrow-table.ts +12 -0
- package/src/util/{js-type.js → js-type.ts} +7 -5
- package/src/util/{priority-queue.js → priority-queue.ts} +32 -20
- package/src/util/{query-result.js → query-result.ts} +24 -17
- package/src/util/synchronizer.ts +56 -0
- package/src/util/throttle.ts +59 -0
- package/src/util/to-data-columns.ts +65 -0
- package/src/util/void-logger.ts +23 -0
- package/src/Coordinator.js +0 -313
- package/src/Selection.js +0 -380
- package/src/SelectionClause.js +0 -159
- package/src/connectors/rest.js +0 -38
- package/src/index-types.ts +0 -5
- package/src/make-client.js +0 -101
- package/src/util/is-activatable.js +0 -8
- package/src/util/is-arrow-table.js +0 -10
- package/src/util/selection-types.ts +0 -137
- package/src/util/synchronizer.js +0 -47
- package/src/util/throttle.js +0 -54
- package/src/util/to-data-columns.js +0 -60
- package/src/util/void-logger.js +0 -13
- package/tsconfig.json +0 -9
- package/vitest.config.ts +0 -3
|
@@ -1,43 +1,57 @@
|
|
|
1
|
+
type EventCallback<T = unknown> = (value: T) => void | Promise<unknown>;
|
|
2
|
+
|
|
3
|
+
interface DispatchEntry<T = unknown> {
|
|
4
|
+
callbacks: Set<EventCallback<T>>;
|
|
5
|
+
pending: Promise<unknown> | null;
|
|
6
|
+
queue: DispatchQueue<T>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface QueueNode<T = unknown> {
|
|
10
|
+
value: T;
|
|
11
|
+
next?: QueueNode<T> | null;
|
|
12
|
+
}
|
|
13
|
+
|
|
1
14
|
/**
|
|
2
15
|
* Event dispatcher supporting asynchronous updates. If an event handler
|
|
3
16
|
* callback returns a Promise, the dispatcher waits for all such Promises
|
|
4
17
|
* to settle before dispatching future events of the same type.
|
|
5
18
|
*/
|
|
6
|
-
export class AsyncDispatch {
|
|
19
|
+
export class AsyncDispatch<T> {
|
|
20
|
+
_callbacks: Map<string, DispatchEntry<T>>;
|
|
7
21
|
|
|
8
22
|
/**
|
|
9
23
|
* Create a new asynchronous dispatcher instance.
|
|
10
24
|
*/
|
|
11
25
|
constructor() {
|
|
12
|
-
this._callbacks = new Map;
|
|
26
|
+
this._callbacks = new Map();
|
|
13
27
|
}
|
|
14
28
|
|
|
15
29
|
/**
|
|
16
30
|
* Add an event listener callback for the provided event type.
|
|
17
|
-
* @param
|
|
18
|
-
* @param
|
|
31
|
+
* @param type The event type.
|
|
32
|
+
* @param callback The event handler
|
|
19
33
|
* callback function to add. If the callback has already been
|
|
20
34
|
* added for the event type, this method has no effect.
|
|
21
35
|
*/
|
|
22
|
-
addEventListener(type, callback) {
|
|
36
|
+
addEventListener(type: string, callback: EventCallback<T>): void {
|
|
23
37
|
if (!this._callbacks.has(type)) {
|
|
24
38
|
this._callbacks.set(type, {
|
|
25
|
-
callbacks: new Set,
|
|
39
|
+
callbacks: new Set<EventCallback<T>>(),
|
|
26
40
|
pending: null,
|
|
27
|
-
queue: new DispatchQueue()
|
|
41
|
+
queue: new DispatchQueue<T>()
|
|
28
42
|
});
|
|
29
43
|
}
|
|
30
|
-
const entry = this._callbacks.get(type)
|
|
44
|
+
const entry = this._callbacks.get(type)!;
|
|
31
45
|
entry.callbacks.add(callback);
|
|
32
46
|
}
|
|
33
47
|
|
|
34
48
|
/**
|
|
35
49
|
* Remove an event listener callback for the provided event type.
|
|
36
|
-
* @param
|
|
37
|
-
* @param
|
|
50
|
+
* @param type The event type.
|
|
51
|
+
* @param callback The event handler
|
|
38
52
|
* callback function to remove.
|
|
39
53
|
*/
|
|
40
|
-
removeEventListener(type, callback) {
|
|
54
|
+
removeEventListener(type: string, callback: EventCallback<T>): void {
|
|
41
55
|
const entry = this._callbacks.get(type);
|
|
42
56
|
if (entry) {
|
|
43
57
|
entry.callbacks.delete(callback);
|
|
@@ -49,11 +63,11 @@ export class AsyncDispatch {
|
|
|
49
63
|
* This default implementation simply returns the input value as-is.
|
|
50
64
|
* Subclasses may override this method to implement custom transformations
|
|
51
65
|
* prior to emitting an event value to all listeners.
|
|
52
|
-
* @param
|
|
53
|
-
* @param
|
|
66
|
+
* @param type The event type.
|
|
67
|
+
* @param value The event value.
|
|
54
68
|
* @returns The (possibly transformed) event value to emit.
|
|
55
69
|
*/
|
|
56
|
-
willEmit(type, value) {
|
|
70
|
+
willEmit(type: string, value: T): T {
|
|
57
71
|
return value;
|
|
58
72
|
}
|
|
59
73
|
|
|
@@ -61,35 +75,38 @@ export class AsyncDispatch {
|
|
|
61
75
|
* Lifecycle method that returns a filter function for updating the
|
|
62
76
|
* queue of unemitted event values prior to enqueueing a new value.
|
|
63
77
|
* This default implementation simply returns null, indicating that
|
|
64
|
-
*
|
|
78
|
+
* unknown other unemitted event values should be dropped (that is, all
|
|
65
79
|
* queued events are filtered).
|
|
66
|
-
* @param
|
|
67
|
-
* @param
|
|
68
|
-
* @returns
|
|
80
|
+
* @param type The event type.
|
|
81
|
+
* @param value The new event value that will be enqueued.
|
|
82
|
+
* @returns A dispatch queue filter
|
|
69
83
|
* function, or null if all unemitted event values should be filtered.
|
|
70
84
|
*/
|
|
71
|
-
emitQueueFilter(
|
|
85
|
+
emitQueueFilter(
|
|
86
|
+
_type: string, // eslint-disable-line @typescript-eslint/no-unused-vars
|
|
87
|
+
_value: unknown // eslint-disable-line @typescript-eslint/no-unused-vars
|
|
88
|
+
): ((value: unknown) => boolean | null) | null {
|
|
72
89
|
// removes all pending items
|
|
73
90
|
return null;
|
|
74
91
|
}
|
|
75
92
|
|
|
76
93
|
/**
|
|
77
94
|
* Cancel all unemitted event values for the given event type.
|
|
78
|
-
* @param
|
|
95
|
+
* @param type The event type.
|
|
79
96
|
*/
|
|
80
|
-
cancel(type) {
|
|
97
|
+
cancel(type: string): void {
|
|
81
98
|
const entry = this._callbacks.get(type);
|
|
82
99
|
entry?.queue.clear();
|
|
83
100
|
}
|
|
84
101
|
|
|
85
102
|
/**
|
|
86
|
-
* Returns a promise that resolves when
|
|
103
|
+
* Returns a promise that resolves when unknown pending updates complete for
|
|
87
104
|
* the event of the given type currently being processed. The Promise will
|
|
88
105
|
* resolve immediately if the queue for the given event type is empty.
|
|
89
|
-
* @param
|
|
90
|
-
* @returns
|
|
106
|
+
* @param type The event type to wait for.
|
|
107
|
+
* @returns A pending event promise.
|
|
91
108
|
*/
|
|
92
|
-
async pending(type) {
|
|
109
|
+
async pending(type: string): Promise<void> {
|
|
93
110
|
await this._callbacks.get(type)?.pending;
|
|
94
111
|
}
|
|
95
112
|
|
|
@@ -99,11 +116,11 @@ export class AsyncDispatch {
|
|
|
99
116
|
* will be queued to be emitted later.
|
|
100
117
|
* The actual event value given to listeners will be the result
|
|
101
118
|
* of passing the input value through the emitValue() method.
|
|
102
|
-
* @param
|
|
103
|
-
* @param
|
|
119
|
+
* @param type The event type.
|
|
120
|
+
* @param value The event value.
|
|
104
121
|
*/
|
|
105
|
-
emit(type, value) {
|
|
106
|
-
const entry = this._callbacks.get(type) || {}
|
|
122
|
+
emit(type: string, value: T): void {
|
|
123
|
+
const entry = this._callbacks.get(type) || {} as DispatchEntry<T>;
|
|
107
124
|
if (entry.pending) {
|
|
108
125
|
// an earlier emit is still processing
|
|
109
126
|
// enqueue the current update, possibly filtering other pending updates
|
|
@@ -118,7 +135,7 @@ export class AsyncDispatch {
|
|
|
118
135
|
entry.pending = Promise.allSettled(callbackValues).then(() => {
|
|
119
136
|
entry.pending = null;
|
|
120
137
|
if (!queue.isEmpty()) {
|
|
121
|
-
this.emit(type, queue.dequeue());
|
|
138
|
+
this.emit(type, queue.dequeue()!);
|
|
122
139
|
}
|
|
123
140
|
});
|
|
124
141
|
}
|
|
@@ -129,7 +146,8 @@ export class AsyncDispatch {
|
|
|
129
146
|
/**
|
|
130
147
|
* Queue for managing unemitted event values.
|
|
131
148
|
*/
|
|
132
|
-
export class DispatchQueue {
|
|
149
|
+
export class DispatchQueue<T = unknown> {
|
|
150
|
+
next: QueueNode<T> | null = null;
|
|
133
151
|
|
|
134
152
|
/**
|
|
135
153
|
* Create a new dispatch queue instance.
|
|
@@ -141,33 +159,34 @@ export class DispatchQueue {
|
|
|
141
159
|
/**
|
|
142
160
|
* Clear the queue state of all event values.
|
|
143
161
|
*/
|
|
144
|
-
clear() {
|
|
162
|
+
clear(): void {
|
|
145
163
|
this.next = null;
|
|
146
164
|
}
|
|
147
165
|
|
|
148
166
|
/**
|
|
149
167
|
* Indicate if the queue is empty.
|
|
150
|
-
* @returns
|
|
168
|
+
* @returns True if queue is empty, false otherwise.
|
|
151
169
|
*/
|
|
152
|
-
isEmpty() {
|
|
170
|
+
isEmpty(): boolean {
|
|
153
171
|
return !this.next;
|
|
154
172
|
}
|
|
155
173
|
|
|
156
174
|
/**
|
|
157
175
|
* Add a new value to the queue, and optionally filter the
|
|
158
176
|
* current queue content in response.
|
|
159
|
-
* @param
|
|
160
|
-
* @param
|
|
177
|
+
* @param value The value to add.
|
|
178
|
+
* @param filter An optional filter
|
|
161
179
|
* function to apply to existing queue content. If unspecified
|
|
162
180
|
* or falsy, all previously queued values are removed. Otherwise,
|
|
163
181
|
* the provided function is applied to all queue entries. The
|
|
164
182
|
* entry is retained if the filter function returns a truthy value,
|
|
165
183
|
* otherwise the entry is removed.
|
|
166
184
|
*/
|
|
167
|
-
enqueue(value, filter) {
|
|
168
|
-
const tail = { value };
|
|
185
|
+
enqueue(value: T, filter?: ((value: T) => boolean | null) | null): void {
|
|
186
|
+
const tail: QueueNode<T> = { value };
|
|
169
187
|
if (filter && this.next) {
|
|
170
|
-
|
|
188
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
189
|
+
let curr: QueueNode<T> | DispatchQueue<T> = this;
|
|
171
190
|
while (curr.next) {
|
|
172
191
|
if (filter(curr.next.value)) {
|
|
173
192
|
curr = curr.next;
|
|
@@ -183,11 +202,11 @@ export class DispatchQueue {
|
|
|
183
202
|
|
|
184
203
|
/**
|
|
185
204
|
* Remove and return the next queued event value.
|
|
186
|
-
* @returns
|
|
205
|
+
* @returns The next event value in the queue.
|
|
187
206
|
*/
|
|
188
|
-
dequeue() {
|
|
207
|
+
dequeue(): T | undefined {
|
|
189
208
|
const { next } = this;
|
|
190
|
-
this.next = next?.next;
|
|
209
|
+
this.next = next?.next || null;
|
|
191
210
|
return next?.value;
|
|
192
211
|
}
|
|
193
|
-
}
|
|
212
|
+
}
|
|
@@ -1,14 +1,19 @@
|
|
|
1
|
-
|
|
1
|
+
import type { Cache } from '../types.js';
|
|
2
2
|
|
|
3
3
|
const requestIdle = typeof requestIdleCallback !== 'undefined'
|
|
4
4
|
? requestIdleCallback
|
|
5
5
|
: setTimeout;
|
|
6
6
|
|
|
7
|
+
interface CacheEntry<T = unknown> {
|
|
8
|
+
last: number;
|
|
9
|
+
value: T;
|
|
10
|
+
}
|
|
11
|
+
|
|
7
12
|
/**
|
|
8
13
|
* Create a new cache that ignores all values.
|
|
9
|
-
* @returns
|
|
14
|
+
* @returns A void cache implementation.
|
|
10
15
|
*/
|
|
11
|
-
export function voidCache() {
|
|
16
|
+
export function voidCache(): Cache {
|
|
12
17
|
return {
|
|
13
18
|
get: () => undefined,
|
|
14
19
|
set: (key, value) => value,
|
|
@@ -18,20 +23,23 @@ export function voidCache() {
|
|
|
18
23
|
|
|
19
24
|
/**
|
|
20
25
|
* Create a new cache that uses an LRU eviction policy.
|
|
21
|
-
* @param
|
|
22
|
-
* @param
|
|
23
|
-
* @param
|
|
24
|
-
* @returns
|
|
26
|
+
* @param options Cache options.
|
|
27
|
+
* @param options.max Maximum number of cache entries.
|
|
28
|
+
* @param options.ttl Time-to-live for cache entries.
|
|
29
|
+
* @returns An LRU cache implementation.
|
|
25
30
|
*/
|
|
26
31
|
export function lruCache({
|
|
27
32
|
max = 1000, // max entries
|
|
28
33
|
ttl = 3 * 60 * 60 * 1000 // time-to-live, default 3 hours
|
|
29
|
-
}
|
|
30
|
-
|
|
34
|
+
}: {
|
|
35
|
+
max?: number;
|
|
36
|
+
ttl?: number;
|
|
37
|
+
} = {}): Cache {
|
|
38
|
+
let cache = new Map<string, CacheEntry>();
|
|
31
39
|
|
|
32
|
-
function evict() {
|
|
40
|
+
function evict(): void {
|
|
33
41
|
const expire = performance.now() - ttl;
|
|
34
|
-
let lruKey = null;
|
|
42
|
+
let lruKey: string | null = null;
|
|
35
43
|
let lruLast = Infinity;
|
|
36
44
|
|
|
37
45
|
for (const [key, value] of cache) {
|
|
@@ -56,18 +64,20 @@ export function lruCache({
|
|
|
56
64
|
}
|
|
57
65
|
|
|
58
66
|
return {
|
|
59
|
-
get(key) {
|
|
67
|
+
get(key: string): unknown {
|
|
60
68
|
const entry = cache.get(key);
|
|
61
69
|
if (entry) {
|
|
62
70
|
entry.last = performance.now();
|
|
63
71
|
return entry.value;
|
|
64
72
|
}
|
|
65
73
|
},
|
|
66
|
-
set(key, value) {
|
|
74
|
+
set(key: string, value: unknown): unknown {
|
|
67
75
|
cache.set(key, { last: performance.now(), value });
|
|
68
76
|
if (cache.size > max) requestIdle(evict);
|
|
69
77
|
return value;
|
|
70
78
|
},
|
|
71
|
-
clear() {
|
|
79
|
+
clear(): void {
|
|
80
|
+
cache = new Map();
|
|
81
|
+
}
|
|
72
82
|
};
|
|
73
|
-
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ExtractionOptions, Table } from '@uwdata/flechette';
|
|
2
|
+
import { tableFromIPC } from '@uwdata/flechette';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Decode Arrow IPC bytes to a table instance.
|
|
6
|
+
* The default options map date and timestamp values to JS Date objects.
|
|
7
|
+
* @param data Arrow IPC bytes.
|
|
8
|
+
* @param options Arrow IPC extraction options.
|
|
9
|
+
* If unspecified, the default options will extract date and timestamp
|
|
10
|
+
* values to JS Date objects.
|
|
11
|
+
* @returns A table instance.
|
|
12
|
+
*/
|
|
13
|
+
export function decodeIPC(data: ArrayBuffer | Uint8Array, options: ExtractionOptions = { useDate: true }): Table {
|
|
14
|
+
return tableFromIPC(data, options);
|
|
15
|
+
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
export function distinct(a, b) {
|
|
1
|
+
export function distinct(a: unknown, b: unknown): boolean {
|
|
2
2
|
return a === b ? false
|
|
3
3
|
: a instanceof Date && b instanceof Date ? +a !== +b
|
|
4
4
|
: Array.isArray(a) && Array.isArray(b) ? distinctArray(a, b)
|
|
5
5
|
: true;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
export function distinctArray(a, b) {
|
|
8
|
+
export function distinctArray(a: unknown[], b: unknown[]): boolean {
|
|
9
9
|
if (a.length !== b.length) return true;
|
|
10
10
|
for (let i = 0; i < a.length; ++i) {
|
|
11
11
|
if (a[i] !== b[i]) return true;
|
|
12
12
|
}
|
|
13
13
|
return false;
|
|
14
|
-
}
|
|
14
|
+
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
import type { AggregateNode } from '@uwdata/mosaic-sql';
|
|
2
2
|
import { Query, asTableRef, count, isAggregateExpression, isNode, isNull, max, min, sql } from '@uwdata/mosaic-sql';
|
|
3
3
|
import { jsType } from './js-type.js';
|
|
4
|
+
import type { Coordinator } from '../Coordinator.js';
|
|
5
|
+
import type { FieldInfoRequest, FieldInfo, Stat, FieldRef, ColumnDescription } from '../types.js';
|
|
6
|
+
import { Table } from '@uwdata/flechette';
|
|
4
7
|
|
|
5
8
|
export const Count = 'count';
|
|
6
9
|
export const Nulls = 'nulls';
|
|
@@ -9,13 +12,7 @@ export const Min = 'min';
|
|
|
9
12
|
export const Distinct = 'distinct';
|
|
10
13
|
export const Stats = { Count, Nulls, Max, Min, Distinct };
|
|
11
14
|
|
|
12
|
-
|
|
13
|
-
* @type {Record<
|
|
14
|
-
* import('../types.js').Stat,
|
|
15
|
-
* (column: import('../types.js').FieldRef) => AggregateNode
|
|
16
|
-
* >}
|
|
17
|
-
*/
|
|
18
|
-
const statMap = {
|
|
15
|
+
const statMap: Record<Stat, (column: FieldRef) => AggregateNode> = {
|
|
19
16
|
[Count]: count,
|
|
20
17
|
[Distinct]: column => count(column).distinct(),
|
|
21
18
|
[Max]: max,
|
|
@@ -25,13 +22,13 @@ const statMap = {
|
|
|
25
22
|
|
|
26
23
|
/**
|
|
27
24
|
* Get summary stats of the given column
|
|
28
|
-
* @param
|
|
29
|
-
* @returns
|
|
25
|
+
* @param field Field information request
|
|
26
|
+
* @returns Query for field statistics
|
|
30
27
|
*/
|
|
31
|
-
function summarize({ table, column, stats }) {
|
|
28
|
+
function summarize({ table, column, stats }: FieldInfoRequest): Query {
|
|
32
29
|
return Query
|
|
33
30
|
.from(table)
|
|
34
|
-
.select(Array.from(stats
|
|
31
|
+
.select(Array.from(stats!, s => ({ [s]: statMap[s](column) })));
|
|
35
32
|
}
|
|
36
33
|
|
|
37
34
|
/**
|
|
@@ -40,27 +37,27 @@ function summarize({ table, column, stats }) {
|
|
|
40
37
|
* the function will retrieve and return the table information using `getTableInfo`.
|
|
41
38
|
* Otherwise, it will query individual field information using `getFieldInfo`
|
|
42
39
|
* for each field in the `fields` array.
|
|
43
|
-
* @param
|
|
44
|
-
* @param
|
|
45
|
-
* @returns
|
|
40
|
+
* @param mc A Mosaic coordinator.
|
|
41
|
+
* @param fields Array of field information requests.
|
|
42
|
+
* @returns Promise resolving to array of field information.
|
|
46
43
|
*/
|
|
47
|
-
export async function queryFieldInfo(mc, fields) {
|
|
44
|
+
export async function queryFieldInfo(mc: Coordinator, fields: FieldInfoRequest[]): Promise<FieldInfo[]> {
|
|
48
45
|
if (fields.length === 1 && fields[0].column === '*') {
|
|
49
46
|
return getTableInfo(mc, fields[0].table);
|
|
50
47
|
} else {
|
|
51
48
|
return (await Promise
|
|
52
49
|
.all(fields.map(f => getFieldInfo(mc, f))))
|
|
53
|
-
.filter(x => x);
|
|
50
|
+
.filter((x): x is FieldInfo => x !== undefined);
|
|
54
51
|
}
|
|
55
52
|
}
|
|
56
53
|
|
|
57
54
|
/**
|
|
58
55
|
* Get information about a single field of a table.
|
|
59
|
-
* @param
|
|
60
|
-
* @param
|
|
61
|
-
* @returns
|
|
56
|
+
* @param mc A Mosaic coordinator.
|
|
57
|
+
* @param field Field information request.
|
|
58
|
+
* @returns Promise resolving to field information.
|
|
62
59
|
*/
|
|
63
|
-
async function getFieldInfo(mc, { table, column, stats }) {
|
|
60
|
+
async function getFieldInfo(mc: Coordinator, { table, column, stats }: FieldInfoRequest): Promise<FieldInfo> {
|
|
64
61
|
// generate and issue a query for field metadata info
|
|
65
62
|
// use GROUP BY ALL to differentiate & consolidate aggregates
|
|
66
63
|
const q = Query
|
|
@@ -68,9 +65,10 @@ async function getFieldInfo(mc, { table, column, stats }) {
|
|
|
68
65
|
.select({ column })
|
|
69
66
|
.groupby(isNode(column) && isAggregateExpression(column) ? sql`ALL` : []);
|
|
70
67
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
68
|
+
const [desc] = Array.from(
|
|
69
|
+
await mc.query(Query.describe(q)) as Table
|
|
70
|
+
) as ColumnDescription[];
|
|
71
|
+
const info: FieldInfo = {
|
|
74
72
|
table,
|
|
75
73
|
column: `${column}`,
|
|
76
74
|
sqlType: desc.column_type,
|
|
@@ -85,7 +83,7 @@ async function getFieldInfo(mc, { table, column, stats }) {
|
|
|
85
83
|
const [result] = await mc.query(
|
|
86
84
|
summarize({ table, column, stats }),
|
|
87
85
|
{ persist: true }
|
|
88
|
-
);
|
|
86
|
+
) as Table;
|
|
89
87
|
|
|
90
88
|
// extract summary stats, copy to field info, and return
|
|
91
89
|
return Object.assign(info, result);
|
|
@@ -93,13 +91,14 @@ async function getFieldInfo(mc, { table, column, stats }) {
|
|
|
93
91
|
|
|
94
92
|
/**
|
|
95
93
|
* Get information about the fields of a table.
|
|
96
|
-
* @param
|
|
97
|
-
* @param
|
|
98
|
-
* @returns
|
|
94
|
+
* @param mc A Mosaic coordinator.
|
|
95
|
+
* @param table The table name.
|
|
96
|
+
* @returns Promise resolving to array of field information.
|
|
99
97
|
*/
|
|
100
|
-
async function getTableInfo(mc, table) {
|
|
101
|
-
|
|
102
|
-
|
|
98
|
+
async function getTableInfo(mc: Coordinator, table: string): Promise<FieldInfo[]> {
|
|
99
|
+
const result = Array.from(
|
|
100
|
+
await mc.query(`DESCRIBE ${asTableRef(table)}`) as Table
|
|
101
|
+
) as ColumnDescription[];
|
|
103
102
|
return result.map(desc => ({
|
|
104
103
|
table,
|
|
105
104
|
column: desc.column_name,
|
|
@@ -107,4 +106,4 @@ async function getTableInfo(mc, table) {
|
|
|
107
106
|
type: jsType(desc.column_type),
|
|
108
107
|
nullable: desc.null === 'YES'
|
|
109
108
|
}));
|
|
110
|
-
}
|
|
109
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Fowler/Noll/Vo hashing.
|
|
2
|
-
export function fnv_hash(v) {
|
|
2
|
+
export function fnv_hash(v: string): number {
|
|
3
3
|
let a = 2166136261;
|
|
4
4
|
for (let i = 0, n = v.length; i < n; ++i) {
|
|
5
5
|
const c = v.charCodeAt(i);
|
|
@@ -11,16 +11,16 @@ export function fnv_hash(v) {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
// a * 16777619 mod 2**32
|
|
14
|
-
function fnv_multiply(a) {
|
|
14
|
+
function fnv_multiply(a: number): number {
|
|
15
15
|
return a + (a << 1) + (a << 4) + (a << 7) + (a << 8) + (a << 24);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
// See https://web.archive.org/web/20131019013225/http://home.comcast.net/~bretm/hash/6.html
|
|
19
|
-
function fnv_mix(a) {
|
|
19
|
+
function fnv_mix(a: number): number {
|
|
20
20
|
a += a << 13;
|
|
21
21
|
a ^= a >>> 7;
|
|
22
22
|
a += a << 3;
|
|
23
23
|
a ^= a >>> 17;
|
|
24
24
|
a += a << 5;
|
|
25
25
|
return a & 0xffffffff;
|
|
26
|
-
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Activatable } from '../types.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Test if a value implements the Activatable interface.
|
|
5
|
+
* @param value The value to test.
|
|
6
|
+
* @returns True if value implements Activatable interface.
|
|
7
|
+
*/
|
|
8
|
+
export function isActivatable(value: unknown): value is Activatable {
|
|
9
|
+
// @ts-expect-error check properties of unknown value
|
|
10
|
+
return typeof value?.activate === 'function' && value.activate.length === 0;
|
|
11
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Table } from '@uwdata/flechette';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Test if a value is a Flechette Arrow table.
|
|
5
|
+
* We use a "duck typing" approach and check for a getChild function.
|
|
6
|
+
* @param values The value to test
|
|
7
|
+
* @returns true if the value duck types as Arrow data
|
|
8
|
+
*/
|
|
9
|
+
export function isArrowTable(values: unknown): values is Table {
|
|
10
|
+
// @ts-expect-error check property of unknown value
|
|
11
|
+
return typeof values?.getChild === 'function';
|
|
12
|
+
}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
import type { JSType } from '../types.js';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Maps a SQL data type to its corresponding JavaScript type.
|
|
3
|
-
* @param
|
|
4
|
-
* @returns
|
|
5
|
-
* @throws
|
|
5
|
+
* @param type The name of a SQL data type
|
|
6
|
+
* @returns The corresponding JavaScript type name
|
|
7
|
+
* @throws Throws an error if the given SQL type name is unsupported or unrecognized.
|
|
6
8
|
*/
|
|
7
|
-
export function jsType(type) {
|
|
9
|
+
export function jsType(type: string): JSType {
|
|
8
10
|
switch (type) {
|
|
9
11
|
case 'BIGINT':
|
|
10
12
|
case 'HUGEINT':
|
|
@@ -52,4 +54,4 @@ export function jsType(type) {
|
|
|
52
54
|
}
|
|
53
55
|
throw new Error(`Unsupported type: ${type}`);
|
|
54
56
|
}
|
|
55
|
-
}
|
|
57
|
+
}
|
|
@@ -1,60 +1,72 @@
|
|
|
1
|
-
|
|
1
|
+
interface ListNode<T> {
|
|
2
|
+
item: T;
|
|
3
|
+
next: ListNode<T> | null;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
interface List<T> {
|
|
7
|
+
head: ListNode<T> | null;
|
|
8
|
+
tail: ListNode<T> | null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export class PriorityQueue<T = unknown> {
|
|
12
|
+
private queue: List<T>[];
|
|
13
|
+
|
|
2
14
|
/**
|
|
3
15
|
* Create a new priority queue instance.
|
|
4
|
-
* @param
|
|
16
|
+
* @param ranks An integer number of rank-order priority levels.
|
|
5
17
|
*/
|
|
6
|
-
constructor(ranks) {
|
|
18
|
+
constructor(ranks: number) {
|
|
7
19
|
// one list for each integer priority level
|
|
8
20
|
this.queue = Array.from(
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
21
|
+
{ length: ranks },
|
|
22
|
+
(): List<T> => ({ head: null, tail: null })
|
|
23
|
+
);
|
|
12
24
|
}
|
|
13
25
|
|
|
14
26
|
/**
|
|
15
27
|
* Indicate if the queue is empty.
|
|
16
|
-
* @returns
|
|
28
|
+
* @returns true if empty, false otherwise.
|
|
17
29
|
*/
|
|
18
|
-
isEmpty() {
|
|
30
|
+
isEmpty(): boolean {
|
|
19
31
|
return this.queue.every(list => !list.head);
|
|
20
32
|
}
|
|
21
33
|
|
|
22
34
|
/**
|
|
23
35
|
* Insert an item into the queue with a given priority rank.
|
|
24
|
-
* @param
|
|
25
|
-
* @param
|
|
36
|
+
* @param item The item to add.
|
|
37
|
+
* @param rank The integer priority rank.
|
|
26
38
|
* Priority ranks are integers starting at zero.
|
|
27
39
|
* Lower ranks indicate higher priority.
|
|
28
40
|
*/
|
|
29
|
-
insert(item, rank) {
|
|
41
|
+
insert(item: T, rank: number): void {
|
|
30
42
|
const list = this.queue[rank];
|
|
31
43
|
if (!list) {
|
|
32
44
|
throw new Error(`Invalid queue priority rank: ${rank}`);
|
|
33
45
|
}
|
|
34
46
|
|
|
35
|
-
const node = { item, next: null };
|
|
47
|
+
const node: ListNode<T> = { item, next: null };
|
|
36
48
|
if (list.head === null) {
|
|
37
49
|
list.head = list.tail = node;
|
|
38
50
|
} else {
|
|
39
|
-
list.tail = list.tail
|
|
51
|
+
list.tail = list.tail!.next = node;
|
|
40
52
|
}
|
|
41
53
|
}
|
|
42
54
|
|
|
43
55
|
/**
|
|
44
56
|
* Remove a set of items from the queue, regardless of priority rank.
|
|
45
57
|
* If a provided item is not in the queue it will be ignored.
|
|
46
|
-
* @param
|
|
58
|
+
* @param test A predicate function to test
|
|
47
59
|
* if an item should be removed (true to drop, false to keep).
|
|
48
60
|
*/
|
|
49
|
-
remove(test) {
|
|
61
|
+
remove(test: (item: T) => boolean): void {
|
|
50
62
|
for (const list of this.queue) {
|
|
51
63
|
let { head, tail } = list;
|
|
52
|
-
for (let prev = null, curr = head; curr; prev = curr, curr = curr.next) {
|
|
64
|
+
for (let prev: ListNode<T> | null = null, curr = head; curr; prev = curr, curr = curr.next) {
|
|
53
65
|
if (test(curr.item)) {
|
|
54
66
|
if (curr === head) {
|
|
55
67
|
head = curr.next;
|
|
56
68
|
} else {
|
|
57
|
-
prev
|
|
69
|
+
prev!.next = curr.next;
|
|
58
70
|
}
|
|
59
71
|
if (curr === tail) tail = prev || head;
|
|
60
72
|
}
|
|
@@ -66,10 +78,10 @@ export class PriorityQueue {
|
|
|
66
78
|
|
|
67
79
|
/**
|
|
68
80
|
* Remove and return the next highest priority item.
|
|
69
|
-
* @returns
|
|
81
|
+
* @returns The next item in the queue,
|
|
70
82
|
* or undefined if this queue is empty.
|
|
71
83
|
*/
|
|
72
|
-
next() {
|
|
84
|
+
next(): T | undefined {
|
|
73
85
|
for (const list of this.queue) {
|
|
74
86
|
const { head } = list;
|
|
75
87
|
if (head !== null) {
|
|
@@ -81,4 +93,4 @@ export class PriorityQueue {
|
|
|
81
93
|
}
|
|
82
94
|
}
|
|
83
95
|
}
|
|
84
|
-
}
|
|
96
|
+
}
|