@matter/general 0.16.1 → 0.16.2-alpha.0-20260114-d3127faee
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/cjs/net/ServerAddress.d.ts +52 -14
- package/dist/cjs/net/ServerAddress.d.ts.map +1 -1
- package/dist/cjs/net/ServerAddress.js +41 -6
- package/dist/cjs/net/ServerAddress.js.map +2 -2
- package/dist/cjs/net/ServerAddressSet.d.ts +65 -0
- package/dist/cjs/net/ServerAddressSet.d.ts.map +1 -0
- package/dist/cjs/net/ServerAddressSet.js +144 -0
- package/dist/cjs/net/ServerAddressSet.js.map +6 -0
- package/dist/cjs/net/dns-sd/MdnsSocket.d.ts +40 -0
- package/dist/cjs/net/dns-sd/MdnsSocket.d.ts.map +1 -0
- package/dist/cjs/net/dns-sd/MdnsSocket.js +164 -0
- package/dist/cjs/net/dns-sd/MdnsSocket.js.map +6 -0
- package/dist/cjs/net/dns-sd/index.d.ts +7 -0
- package/dist/cjs/net/dns-sd/index.d.ts.map +1 -0
- package/dist/cjs/net/dns-sd/index.js +24 -0
- package/dist/cjs/net/dns-sd/index.js.map +6 -0
- package/dist/cjs/net/index.d.ts +2 -0
- package/dist/cjs/net/index.d.ts.map +1 -1
- package/dist/cjs/net/index.js +2 -0
- package/dist/cjs/net/index.js.map +1 -1
- package/dist/cjs/util/Abort.d.ts +9 -0
- package/dist/cjs/util/Abort.d.ts.map +1 -1
- package/dist/cjs/util/Abort.js +20 -0
- package/dist/cjs/util/Abort.js.map +1 -1
- package/dist/cjs/util/Heap.d.ts +84 -0
- package/dist/cjs/util/Heap.d.ts.map +1 -0
- package/dist/cjs/util/Heap.js +286 -0
- package/dist/cjs/util/Heap.js.map +6 -0
- package/dist/cjs/util/Observable.d.ts +29 -6
- package/dist/cjs/util/Observable.d.ts.map +1 -1
- package/dist/cjs/util/Observable.js +40 -6
- package/dist/cjs/util/Observable.js.map +1 -1
- package/dist/cjs/util/Promises.d.ts +3 -0
- package/dist/cjs/util/Promises.d.ts.map +1 -1
- package/dist/cjs/util/Promises.js +33 -3
- package/dist/cjs/util/Promises.js.map +2 -2
- package/dist/cjs/util/Set.d.ts.map +1 -1
- package/dist/cjs/util/Set.js +14 -8
- package/dist/cjs/util/Set.js.map +1 -1
- package/dist/cjs/util/index.d.ts +1 -0
- package/dist/cjs/util/index.d.ts.map +1 -1
- package/dist/cjs/util/index.js +1 -0
- package/dist/cjs/util/index.js.map +1 -1
- package/dist/esm/net/ServerAddress.d.ts +52 -14
- package/dist/esm/net/ServerAddress.d.ts.map +1 -1
- package/dist/esm/net/ServerAddress.js +41 -6
- package/dist/esm/net/ServerAddress.js.map +2 -2
- package/dist/esm/net/ServerAddressSet.d.ts +65 -0
- package/dist/esm/net/ServerAddressSet.d.ts.map +1 -0
- package/dist/esm/net/ServerAddressSet.js +124 -0
- package/dist/esm/net/ServerAddressSet.js.map +6 -0
- package/dist/esm/net/dns-sd/MdnsSocket.d.ts +40 -0
- package/dist/esm/net/dns-sd/MdnsSocket.d.ts.map +1 -0
- package/dist/esm/net/dns-sd/MdnsSocket.js +149 -0
- package/dist/esm/net/dns-sd/MdnsSocket.js.map +6 -0
- package/dist/esm/net/dns-sd/index.d.ts +7 -0
- package/dist/esm/net/dns-sd/index.d.ts.map +1 -0
- package/dist/esm/net/dns-sd/index.js +7 -0
- package/dist/esm/net/dns-sd/index.js.map +6 -0
- package/dist/esm/net/index.d.ts +2 -0
- package/dist/esm/net/index.d.ts.map +1 -1
- package/dist/esm/net/index.js +2 -0
- package/dist/esm/net/index.js.map +1 -1
- package/dist/esm/util/Abort.d.ts +9 -0
- package/dist/esm/util/Abort.d.ts.map +1 -1
- package/dist/esm/util/Abort.js +20 -0
- package/dist/esm/util/Abort.js.map +1 -1
- package/dist/esm/util/Heap.d.ts +84 -0
- package/dist/esm/util/Heap.d.ts.map +1 -0
- package/dist/esm/util/Heap.js +266 -0
- package/dist/esm/util/Heap.js.map +6 -0
- package/dist/esm/util/Observable.d.ts +29 -6
- package/dist/esm/util/Observable.d.ts.map +1 -1
- package/dist/esm/util/Observable.js +40 -6
- package/dist/esm/util/Observable.js.map +1 -1
- package/dist/esm/util/Promises.d.ts +3 -0
- package/dist/esm/util/Promises.d.ts.map +1 -1
- package/dist/esm/util/Promises.js +33 -3
- package/dist/esm/util/Promises.js.map +2 -2
- package/dist/esm/util/Set.d.ts.map +1 -1
- package/dist/esm/util/Set.js +14 -8
- package/dist/esm/util/Set.js.map +1 -1
- package/dist/esm/util/index.d.ts +1 -0
- package/dist/esm/util/index.d.ts.map +1 -1
- package/dist/esm/util/index.js +1 -0
- package/dist/esm/util/index.js.map +1 -1
- package/package.json +2 -2
- package/src/net/ServerAddress.ts +93 -19
- package/src/net/ServerAddressSet.ts +225 -0
- package/src/net/dns-sd/MdnsSocket.ts +203 -0
- package/src/net/dns-sd/index.ts +7 -0
- package/src/net/index.ts +2 -0
- package/src/util/Abort.ts +25 -0
- package/src/util/Heap.ts +332 -0
- package/src/util/Observable.ts +74 -10
- package/src/util/Promises.ts +61 -4
- package/src/util/Set.ts +15 -8
- package/src/util/index.ts +1 -0
package/src/util/Heap.ts
ADDED
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2022-2026 Matter.js Authors
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { InternalError } from "#MatterError.js";
|
|
8
|
+
import { Abort } from "./Abort.js";
|
|
9
|
+
import { Observable } from "./Observable.js";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* A heap, useful as a priority queue.
|
|
13
|
+
*
|
|
14
|
+
* Features:
|
|
15
|
+
*
|
|
16
|
+
* * Configurable as min-heap or max-heap using comparator
|
|
17
|
+
*
|
|
18
|
+
* * O(1) lookup of first item
|
|
19
|
+
*
|
|
20
|
+
* * O(log(n)) enqueue and dequeue
|
|
21
|
+
*
|
|
22
|
+
* * O(1) deletion of arbitrary position, but will add expense of maintaining index of positions
|
|
23
|
+
*
|
|
24
|
+
* * {@link added}, {@link deleted} and {@link firstChanged} events
|
|
25
|
+
*/
|
|
26
|
+
export class Heap<T> {
|
|
27
|
+
// We encode the heap as an array for efficiency. See #leftChildOf and #rightChildOf for how to navigate the tree
|
|
28
|
+
// when encoded this way
|
|
29
|
+
readonly #buffer = Array<T>();
|
|
30
|
+
|
|
31
|
+
readonly #compare: (a: T, b: T) => number;
|
|
32
|
+
readonly #normalize?: (a: T) => T;
|
|
33
|
+
#firstChanged?: Observable<[T | undefined]>;
|
|
34
|
+
#deleted?: Observable<[T]>;
|
|
35
|
+
#added?: Observable<[T]>;
|
|
36
|
+
#positions?: Map<T, number>;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Create new heap.
|
|
40
|
+
*
|
|
41
|
+
* @param comparator performs ordering of items in the heap
|
|
42
|
+
* @param normalizer optionally converts items to normal form on insert
|
|
43
|
+
*/
|
|
44
|
+
constructor(comparator: (a: T, b: T) => number, normalizer?: (entry: T) => T) {
|
|
45
|
+
this.#compare = comparator;
|
|
46
|
+
this.#normalize = normalizer;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Return lowest-ranked item.
|
|
51
|
+
*/
|
|
52
|
+
shift() {
|
|
53
|
+
if (!this.#buffer?.length) {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
const result = this.first;
|
|
57
|
+
|
|
58
|
+
this.#deleteAt(0);
|
|
59
|
+
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* The lowest-ranked item.
|
|
65
|
+
*/
|
|
66
|
+
get first(): T | undefined {
|
|
67
|
+
return this.#buffer?.[0];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* The queue length.
|
|
72
|
+
*/
|
|
73
|
+
get size() {
|
|
74
|
+
return this.#buffer.length;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Is the heap empty?
|
|
79
|
+
*/
|
|
80
|
+
get isEmpty() {
|
|
81
|
+
return !!this.#buffer.length;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Emits when the head of the queue changes.
|
|
86
|
+
*/
|
|
87
|
+
get firstChanged() {
|
|
88
|
+
if (!this.#firstChanged) {
|
|
89
|
+
this.#firstChanged = new Observable();
|
|
90
|
+
}
|
|
91
|
+
return this.#firstChanged;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Emits when an item is added to the heap.
|
|
96
|
+
*/
|
|
97
|
+
get added() {
|
|
98
|
+
if (!this.#added) {
|
|
99
|
+
this.#added = new Observable();
|
|
100
|
+
}
|
|
101
|
+
return this.#added;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Emits when an item is deleted.
|
|
106
|
+
*/
|
|
107
|
+
get deleted() {
|
|
108
|
+
if (!this.#deleted) {
|
|
109
|
+
this.#deleted = new Observable();
|
|
110
|
+
}
|
|
111
|
+
return this.#deleted;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Add an item.
|
|
116
|
+
*/
|
|
117
|
+
add(...items: T[]) {
|
|
118
|
+
for (let item of items) {
|
|
119
|
+
if (this.#normalize) {
|
|
120
|
+
item = this.#normalize(item);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
this.#buffer.push(item);
|
|
124
|
+
this.#bubbleUp(this.#buffer.length - 1);
|
|
125
|
+
|
|
126
|
+
this.#added?.emit(item);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Delete an item.
|
|
132
|
+
*
|
|
133
|
+
* The first delete is O(n); subsequent deletes are O(1) but insertions and deletions will have additional cost
|
|
134
|
+
* associated.
|
|
135
|
+
*/
|
|
136
|
+
delete(item: T) {
|
|
137
|
+
if (this.#buffer === undefined) {
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (!this.#positions) {
|
|
142
|
+
this.#positions = new Map();
|
|
143
|
+
for (let i = 0; i < this.#buffer.length; i++) {
|
|
144
|
+
this.#positions.set(this.#buffer[i], i);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (this.#normalize) {
|
|
149
|
+
item = this.#normalize(item);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const pos = this.#positions.get(item);
|
|
153
|
+
if (pos === undefined) {
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
this.#deleteAt(pos);
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Remove all entries.
|
|
163
|
+
*/
|
|
164
|
+
clear() {
|
|
165
|
+
if (this.#buffer.length) this.#buffer.length = 0;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Stream the first value from the heap until aborted.
|
|
170
|
+
*/
|
|
171
|
+
async *stream(abort?: Abort.Signal) {
|
|
172
|
+
if (Abort.is(abort)) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
while (true) {
|
|
177
|
+
// Yield values currently available
|
|
178
|
+
while (this.size) {
|
|
179
|
+
yield this.shift()!;
|
|
180
|
+
|
|
181
|
+
if (Abort.is(abort)) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Create promise to resolve when a new value is ready
|
|
187
|
+
let resolve!: () => void;
|
|
188
|
+
const ready = new Promise<void>(r => (resolve = r));
|
|
189
|
+
|
|
190
|
+
// Wait for new value
|
|
191
|
+
try {
|
|
192
|
+
this.added.once(resolve);
|
|
193
|
+
|
|
194
|
+
await Abort.race(abort, ready);
|
|
195
|
+
|
|
196
|
+
if (Abort.is(abort)) {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
} finally {
|
|
200
|
+
this.added.off(resolve);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Perform internal validation of queue order.
|
|
207
|
+
*/
|
|
208
|
+
validate() {
|
|
209
|
+
for (let i = 0; i < this.#buffer.length; i++) {
|
|
210
|
+
const leftChild = this.#leftChildOf(i);
|
|
211
|
+
if (leftChild < this.#buffer.length) {
|
|
212
|
+
if (this.#compare(this.#buffer[i], this.#buffer[leftChild]) > 0) {
|
|
213
|
+
throw new InternalError(
|
|
214
|
+
`Heap error: buffer #${i} (${this.#buffer[i]}) is greater than left child #${leftChild} (${this.#buffer[leftChild]})`,
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const rightChild = this.#rightChildOf(i);
|
|
219
|
+
if (rightChild < this.#buffer.length) {
|
|
220
|
+
if (this.#compare(this.#buffer[i], this.#buffer[rightChild]) > 0) {
|
|
221
|
+
throw new InternalError(
|
|
222
|
+
`Heap error: buffer #${i} (${this.#buffer[i]}) is greater than right child #${rightChild} (${this.#buffer[rightChild]})`,
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (this.#compare(this.#buffer[leftChild], this.#buffer[rightChild]) > 0) {
|
|
227
|
+
throw new InternalError(
|
|
228
|
+
`Heap error: buffer #${leftChild} (${this.#buffer[leftChild]}) is greater than right sibling #${rightChild} (${this.#buffer[rightChild]})`,
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
#deleteAt(index: number) {
|
|
237
|
+
if (index >= this.#buffer.length) {
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const item = this.#buffer[index];
|
|
242
|
+
if (this.#buffer.length === 1) {
|
|
243
|
+
this.#buffer.length = 0;
|
|
244
|
+
} else {
|
|
245
|
+
const lastIndex = this.#buffer.length - 1;
|
|
246
|
+
this.#buffer[index] = this.#buffer[lastIndex];
|
|
247
|
+
this.#positions?.set(this.#buffer[index], index);
|
|
248
|
+
this.#buffer.length = lastIndex;
|
|
249
|
+
if (index < this.#buffer.length) {
|
|
250
|
+
if (index) {
|
|
251
|
+
index = this.#bubbleUp(index);
|
|
252
|
+
}
|
|
253
|
+
this.#sinkDown(index);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
this.#positions?.delete(item);
|
|
258
|
+
|
|
259
|
+
this.#deleted?.emit(item);
|
|
260
|
+
if (!index) {
|
|
261
|
+
this.#firstChanged?.emit(this.first);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
#sinkDown(index: number) {
|
|
266
|
+
while (true) {
|
|
267
|
+
let moveTo: number | undefined;
|
|
268
|
+
|
|
269
|
+
const leftChild = this.#leftChildOf(index);
|
|
270
|
+
|
|
271
|
+
if (leftChild < this.#buffer.length) {
|
|
272
|
+
if (this.#compare(this.#buffer[index], this.#buffer[leftChild]) > 0) {
|
|
273
|
+
moveTo = leftChild;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const rightChild = this.#rightChildOf(index);
|
|
277
|
+
|
|
278
|
+
if (rightChild < this.#buffer.length) {
|
|
279
|
+
if (this.#compare(this.#buffer[moveTo ?? index], this.#buffer[rightChild]) > 0) {
|
|
280
|
+
moveTo = rightChild;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if (moveTo === undefined) {
|
|
286
|
+
break;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
this.#swap(index, moveTo);
|
|
290
|
+
index = moveTo;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
#bubbleUp(index: number) {
|
|
295
|
+
while (index) {
|
|
296
|
+
const parent = this.#parentOf(index);
|
|
297
|
+
|
|
298
|
+
if (this.#compare(this.#buffer[parent], this.#buffer[index]) <= 0) {
|
|
299
|
+
break;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
this.#swap(index, parent);
|
|
303
|
+
index = parent;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
if (!index) {
|
|
307
|
+
this.#firstChanged?.emit(this.first);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return index;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
#swap(index1: number, index2: number) {
|
|
314
|
+
[this.#buffer[index1], this.#buffer[index2]] = [this.#buffer[index2], this.#buffer[index1]];
|
|
315
|
+
if (this.#positions) {
|
|
316
|
+
this.#positions.set(this.#buffer[index1], index1);
|
|
317
|
+
this.#positions.set(this.#buffer[index2], index2);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
#leftChildOf(index: number) {
|
|
322
|
+
return 2 * index + 1;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
#rightChildOf(index: number) {
|
|
326
|
+
return 2 * index + 2;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
#parentOf(index: number) {
|
|
330
|
+
return Math.floor((index - 1) / 2);
|
|
331
|
+
}
|
|
332
|
+
}
|
package/src/util/Observable.ts
CHANGED
|
@@ -54,6 +54,16 @@ export interface Observable<T extends any[] = any[], R = void> extends AsyncIter
|
|
|
54
54
|
*/
|
|
55
55
|
on(observer: Observer<T, R>): void;
|
|
56
56
|
|
|
57
|
+
/**
|
|
58
|
+
* Add an observer that may be released via disposal.
|
|
59
|
+
*/
|
|
60
|
+
use(observer: Observer<T, R>): Disposable;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Add a "once" observer that may be released via disposal.
|
|
64
|
+
*/
|
|
65
|
+
useOnce(observer: Observer<T, R>): Disposable;
|
|
66
|
+
|
|
57
67
|
/**
|
|
58
68
|
* Remove an observer.
|
|
59
69
|
*/
|
|
@@ -148,7 +158,13 @@ export interface Observable<T extends any[] = any[], R = void> extends AsyncIter
|
|
|
148
158
|
*/
|
|
149
159
|
export interface ObservableValue<T extends [any, ...any[]] = [boolean], R extends MaybePromise<void> = void>
|
|
150
160
|
extends Observable<T, R>, Promise<T[0]> {
|
|
161
|
+
/**
|
|
162
|
+
* The current value.
|
|
163
|
+
*
|
|
164
|
+
* Setting the value will resolve the promise interface but you must use {@link emit} to also emit an event.
|
|
165
|
+
*/
|
|
151
166
|
value: T[0] | undefined;
|
|
167
|
+
|
|
152
168
|
error?: Error;
|
|
153
169
|
|
|
154
170
|
/**
|
|
@@ -166,6 +182,12 @@ export interface ObservableValue<T extends [any, ...any[]] = [boolean], R extend
|
|
|
166
182
|
catch<TResult = never>(
|
|
167
183
|
onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null,
|
|
168
184
|
): Promise<T[0] | TResult>;
|
|
185
|
+
|
|
186
|
+
onError(handler: (cause: Error) => void): void;
|
|
187
|
+
|
|
188
|
+
offError(handler: (cause: Error) => void): void;
|
|
189
|
+
|
|
190
|
+
useError(handler: (cause: Error) => void): Disposable;
|
|
169
191
|
}
|
|
170
192
|
|
|
171
193
|
/**
|
|
@@ -344,7 +366,7 @@ export class BasicObservable<T extends any[] = any[], R = void> implements Obser
|
|
|
344
366
|
};
|
|
345
367
|
}
|
|
346
368
|
|
|
347
|
-
// Initially emit using a synchronous loop. When we hit the first
|
|
369
|
+
// Initially emit using a synchronous loop. When we hit the first promise we convert to an async function
|
|
348
370
|
for (; nextObserver < observers.length; nextObserver++) {
|
|
349
371
|
let result: ReturnType<Observer<T, R>>;
|
|
350
372
|
|
|
@@ -410,8 +432,27 @@ export class BasicObservable<T extends any[] = any[], R = void> implements Obser
|
|
|
410
432
|
this.#observers.add(observer);
|
|
411
433
|
}
|
|
412
434
|
|
|
435
|
+
use(observer: Observer<T, R>) {
|
|
436
|
+
this.on(observer);
|
|
437
|
+
return {
|
|
438
|
+
[Symbol.dispose]: () => {
|
|
439
|
+
this.off(observer);
|
|
440
|
+
},
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
useOnce(observer: Observer<T, R>) {
|
|
445
|
+
this.once(observer);
|
|
446
|
+
return {
|
|
447
|
+
[Symbol.dispose]: () => {
|
|
448
|
+
this.off(observer);
|
|
449
|
+
},
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
|
|
413
453
|
off(observer: Observer<T, R>) {
|
|
414
454
|
this.#observers?.delete(observer);
|
|
455
|
+
this.#once?.delete(observer);
|
|
415
456
|
}
|
|
416
457
|
|
|
417
458
|
once(observer: Observer<T, R>) {
|
|
@@ -552,9 +593,9 @@ function event<E, N extends string>(emitter: E, name: N) {
|
|
|
552
593
|
/**
|
|
553
594
|
* A concrete {@link ObservableValue} implementation.
|
|
554
595
|
*/
|
|
555
|
-
export class BasicObservableValue<T extends [any, ...any[]] = [boolean]>
|
|
556
|
-
extends BasicObservable<T,
|
|
557
|
-
implements ObservableValue<T>
|
|
596
|
+
export class BasicObservableValue<T extends [any, ...any[]] = [boolean], R extends MaybePromise<void> = void>
|
|
597
|
+
extends BasicObservable<T, R>
|
|
598
|
+
implements ObservableValue<T, R>
|
|
558
599
|
{
|
|
559
600
|
#value: T | undefined;
|
|
560
601
|
#error?: Error;
|
|
@@ -566,14 +607,12 @@ export class BasicObservableValue<T extends [any, ...any[]] = [boolean]>
|
|
|
566
607
|
constructor(value?: T[0], handleError?: ObserverErrorHandler, asyncConfig?: ObserverPromiseHandler | boolean) {
|
|
567
608
|
super(handleError, asyncConfig);
|
|
568
609
|
this.#value = value;
|
|
569
|
-
|
|
610
|
+
|
|
611
|
+
const maybeResolve = this.#maybeResolve.bind(this) as unknown as Observer<T, R>;
|
|
612
|
+
Object.defineProperty(maybeResolve, observant, { value: false });
|
|
613
|
+
this.on(maybeResolve);
|
|
570
614
|
}
|
|
571
615
|
|
|
572
|
-
/**
|
|
573
|
-
* The current value.
|
|
574
|
-
*
|
|
575
|
-
* This will resolve the promise interface but you must use {@link emit} to also emit an event..
|
|
576
|
-
*/
|
|
577
616
|
get value(): T[0] | undefined {
|
|
578
617
|
return this.#value;
|
|
579
618
|
}
|
|
@@ -639,6 +678,27 @@ export class BasicObservableValue<T extends [any, ...any[]] = [boolean]>
|
|
|
639
678
|
return this.then(undefined, onrejected);
|
|
640
679
|
}
|
|
641
680
|
|
|
681
|
+
onError(handler: (cause: Error) => void) {
|
|
682
|
+
if (!this.#awaiters) {
|
|
683
|
+
this.#awaiters = [];
|
|
684
|
+
}
|
|
685
|
+
this.#awaiters?.push({ resolve: undefined, reject: handler });
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
offError(handler: (cause: Error) => void) {
|
|
689
|
+
this.#awaiters = this.#awaiters?.filter(awaiter => awaiter.resolve === undefined && awaiter.reject === handler);
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
useError(handler: (cause: Error) => void) {
|
|
693
|
+
this.onError(handler);
|
|
694
|
+
|
|
695
|
+
return {
|
|
696
|
+
[Symbol.dispose]: () => {
|
|
697
|
+
this.offError(handler);
|
|
698
|
+
},
|
|
699
|
+
};
|
|
700
|
+
}
|
|
701
|
+
|
|
642
702
|
finally(onfinally?: (() => void) | null): Promise<T> {
|
|
643
703
|
return Promise.resolve(this).finally(onfinally);
|
|
644
704
|
}
|
|
@@ -924,6 +984,10 @@ export class ObserverGroup {
|
|
|
924
984
|
this.#observers.clear();
|
|
925
985
|
this.#boundObservers.clear();
|
|
926
986
|
}
|
|
987
|
+
|
|
988
|
+
[Symbol.dispose]() {
|
|
989
|
+
this.close();
|
|
990
|
+
}
|
|
927
991
|
}
|
|
928
992
|
|
|
929
993
|
/**
|
package/src/util/Promises.ts
CHANGED
|
@@ -10,6 +10,13 @@ import { Duration } from "#time/Duration.js";
|
|
|
10
10
|
import { asError } from "#util/Error.js";
|
|
11
11
|
import { InternalError, TimeoutError } from "../MatterError.js";
|
|
12
12
|
import { Time } from "../time/Time.js";
|
|
13
|
+
import type {
|
|
14
|
+
AsyncObservable,
|
|
15
|
+
AsyncObservableValue,
|
|
16
|
+
AsyncObserver,
|
|
17
|
+
Observable,
|
|
18
|
+
ObservableValue,
|
|
19
|
+
} from "./Observable.js";
|
|
13
20
|
|
|
14
21
|
/**
|
|
15
22
|
* Obtain a promise with functions to resolve and reject.
|
|
@@ -413,10 +420,14 @@ export namespace SafePromise {
|
|
|
413
420
|
* https://github.com/nodejs/node/issues/17469#issuecomment-685216777
|
|
414
421
|
*
|
|
415
422
|
* ...although this isn't an issue specific to Node.
|
|
423
|
+
*
|
|
424
|
+
* We specialize support for {@link Observable} and {@link ObservableValue}. Those contracts are awaitable like a
|
|
425
|
+
* {@link Promise} but we instead register listeners directly so we can unregister using {@link Observable#off}.
|
|
416
426
|
*/
|
|
417
427
|
export function race<T>(values: Iterable<T>): Promise<Awaited<T>> {
|
|
418
428
|
let listener!: SettlementListener;
|
|
419
429
|
let registered: undefined | Set<SettlementListener>[];
|
|
430
|
+
let disposables: undefined | Disposable[];
|
|
420
431
|
|
|
421
432
|
let race = new Promise<Awaited<T>>((resolve, reject) => {
|
|
422
433
|
listener = { resolve, reject };
|
|
@@ -428,6 +439,44 @@ export namespace SafePromise {
|
|
|
428
439
|
continue;
|
|
429
440
|
}
|
|
430
441
|
|
|
442
|
+
// If this is an Observable, use on/off so we can reliably unregister listeners
|
|
443
|
+
if (
|
|
444
|
+
"use" in value &&
|
|
445
|
+
"off" in value &&
|
|
446
|
+
typeof value.use === "function" &&
|
|
447
|
+
typeof value.off === "function"
|
|
448
|
+
) {
|
|
449
|
+
// Further specialize for ObservableValue contract, which behaves as resolved when a truthy value is
|
|
450
|
+
// present
|
|
451
|
+
if ("value" in value && value.value) {
|
|
452
|
+
Promise.resolve(value.value as Awaited<T>).then(resolve, reject);
|
|
453
|
+
continue;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
if (!disposables) {
|
|
457
|
+
disposables = [];
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
let observer: AsyncObserver<[Awaited<T>]>;
|
|
461
|
+
if ("value" in value) {
|
|
462
|
+
// For observable value, only resolve if value is truthy
|
|
463
|
+
observer = value => {
|
|
464
|
+
if (value) {
|
|
465
|
+
resolve(value);
|
|
466
|
+
}
|
|
467
|
+
};
|
|
468
|
+
|
|
469
|
+
// And handle errors
|
|
470
|
+
disposables.push((value as unknown as AsyncObservableValue).useError(reject));
|
|
471
|
+
} else {
|
|
472
|
+
// Normal observables "resolve" on any emit and do not have an error channel
|
|
473
|
+
observer = resolve;
|
|
474
|
+
}
|
|
475
|
+
disposables.push((value as unknown as AsyncObservable<[Awaited<T>]>).use(observer));
|
|
476
|
+
|
|
477
|
+
continue;
|
|
478
|
+
}
|
|
479
|
+
|
|
431
480
|
// We only use Promise#then once per promise and dispatch to a set of listeners from there
|
|
432
481
|
const settlement = settlementOf(value);
|
|
433
482
|
if (settlement.isSettled) {
|
|
@@ -447,11 +496,19 @@ export namespace SafePromise {
|
|
|
447
496
|
}
|
|
448
497
|
});
|
|
449
498
|
|
|
450
|
-
//
|
|
451
|
-
if (registered) {
|
|
499
|
+
// Ensure we unregister listeners when settled
|
|
500
|
+
if (registered || disposables) {
|
|
452
501
|
race = race.finally(() => {
|
|
453
|
-
|
|
454
|
-
listeners
|
|
502
|
+
if (registered) {
|
|
503
|
+
for (const listeners of registered) {
|
|
504
|
+
listeners.delete(listener);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
if (disposables) {
|
|
509
|
+
for (const disposable of disposables) {
|
|
510
|
+
disposable[Symbol.dispose]();
|
|
511
|
+
}
|
|
455
512
|
}
|
|
456
513
|
});
|
|
457
514
|
}
|
package/src/util/Set.ts
CHANGED
|
@@ -61,7 +61,7 @@ export interface IndexedSet<T> {
|
|
|
61
61
|
* Unused features have minimal performance impact.
|
|
62
62
|
*/
|
|
63
63
|
export class BasicSet<T, AddT = T> implements ImmutableSet<T>, MutableSet<T, AddT>, ObservableSet<T>, IndexedSet<T> {
|
|
64
|
-
#entries
|
|
64
|
+
#entries?: Set<T>;
|
|
65
65
|
#added?: Observable<[T]>;
|
|
66
66
|
#deleted?: Observable<[T]>;
|
|
67
67
|
#empty?: ObservableValue;
|
|
@@ -79,11 +79,11 @@ export class BasicSet<T, AddT = T> implements ImmutableSet<T>, MutableSet<T, Add
|
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
[Symbol.iterator]() {
|
|
82
|
-
return this.#
|
|
82
|
+
return this.#definedEntries[Symbol.iterator]();
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
get size() {
|
|
86
|
-
return this.#entries
|
|
86
|
+
return this.#entries?.size ?? 0;
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
map<R>(mapper: (item: T) => R) {
|
|
@@ -109,17 +109,17 @@ export class BasicSet<T, AddT = T> implements ImmutableSet<T>, MutableSet<T, Add
|
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
has(item: T) {
|
|
112
|
-
return this.#entries
|
|
112
|
+
return this.#entries?.has(item) ?? false;
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
add(item: AddT) {
|
|
116
116
|
const created = this.create(item);
|
|
117
117
|
|
|
118
|
-
if (this.#
|
|
118
|
+
if (this.#definedEntries.has(item as any)) {
|
|
119
119
|
return;
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
-
this.#
|
|
122
|
+
this.#definedEntries.add(item as any);
|
|
123
123
|
|
|
124
124
|
if (this.#indices) {
|
|
125
125
|
for (const field in this.#indices) {
|
|
@@ -148,6 +148,13 @@ export class BasicSet<T, AddT = T> implements ImmutableSet<T>, MutableSet<T, Add
|
|
|
148
148
|
return this.#indexOf(field).get(value);
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
+
get #definedEntries() {
|
|
152
|
+
if (this.#entries === undefined) {
|
|
153
|
+
this.#entries = new Set();
|
|
154
|
+
}
|
|
155
|
+
return this.#entries;
|
|
156
|
+
}
|
|
157
|
+
|
|
151
158
|
#indexOf<F extends keyof T>(field: F) {
|
|
152
159
|
if (!this.#indices) {
|
|
153
160
|
this.#indices = {};
|
|
@@ -197,7 +204,7 @@ export class BasicSet<T, AddT = T> implements ImmutableSet<T>, MutableSet<T, Add
|
|
|
197
204
|
}
|
|
198
205
|
}
|
|
199
206
|
|
|
200
|
-
if (!this.#entries
|
|
207
|
+
if (!this.#entries?.delete(item)) {
|
|
201
208
|
return false;
|
|
202
209
|
}
|
|
203
210
|
|
|
@@ -246,7 +253,7 @@ export class BasicSet<T, AddT = T> implements ImmutableSet<T>, MutableSet<T, Add
|
|
|
246
253
|
|
|
247
254
|
get empty() {
|
|
248
255
|
if (this.#empty === undefined) {
|
|
249
|
-
this.#empty = ObservableValue(!this.#entries
|
|
256
|
+
this.#empty = ObservableValue(!this.#entries?.size);
|
|
250
257
|
}
|
|
251
258
|
return this.#empty;
|
|
252
259
|
}
|
package/src/util/index.ts
CHANGED
|
@@ -23,6 +23,7 @@ export * from "./FormattedText.js";
|
|
|
23
23
|
export * from "./Function.js";
|
|
24
24
|
export * from "./GeneratedClass.js";
|
|
25
25
|
export * from "./Github.js";
|
|
26
|
+
export * from "./Heap.js";
|
|
26
27
|
export * from "./identifier-case.js";
|
|
27
28
|
export * from "./Ip.js";
|
|
28
29
|
export * from "./Lifecycle.js";
|