@mark1russell7/collections 0.9.3 → 0.9.5
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/README.md +4 -0
- package/build/dag/index.d.ts +10 -10
- package/build/dag/index.js +12 -12
- package/build/dag/index.js.map +1 -1
- package/build/index.d.ts +109 -77
- package/build/index.d.ts.map +1 -1
- package/build/index.js +104 -42
- package/build/index.js.map +1 -1
- package/package.json +4 -1
- package/src/async/async-queue.ts +24 -10
- package/src/async/channels.ts +3 -3
- package/src/async/semaphore.ts +88 -0
- package/src/behaviors/bounded.ts +1 -1
- package/src/behaviors/evented.ts +1 -1
- package/src/behaviors/readonly.ts +1 -1
- package/src/behaviors/safe.ts +16 -16
- package/src/behaviors/synchronized.ts +1 -1
- package/src/behaviors/ttl.ts +6 -6
- package/src/core/events.ts +14 -14
- package/src/core/middleware.ts +6 -6
- package/src/core/policies.ts +3 -3
- package/src/dag/builder.ts +5 -5
- package/src/dag/executor.ts +5 -5
- package/src/dag/traversal.ts +2 -2
- package/src/fx/collectors.ts +1 -1
- package/src/impl/hash-map.ts +2 -2
- package/src/impl/hash-set.ts +1 -1
- package/src/impl/linked-hash-map.ts +1 -1
- package/src/index.ts +2 -0
- package/src/interfaces/collection.ts +2 -2
- package/src/interfaces/list.ts +4 -4
- package/src/interfaces/map.ts +3 -3
- package/src/interfaces/queue.ts +10 -10
- package/src/interfaces/set.ts +3 -3
- package/src/utils/defaults.ts +2 -2
- package/src/utils/helpers.ts +4 -4
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package",
|
|
3
3
|
"name": "@mark1russell7/collections",
|
|
4
|
-
"version": "0.9.
|
|
4
|
+
"version": "0.9.5",
|
|
5
5
|
"description": "Collections with no dependencies: lists, maps, queues, a summary tree, channels, lazy operations and dependency graphs.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"collections",
|
|
@@ -76,7 +76,10 @@
|
|
|
76
76
|
"@mark1russell7/rigor-generators": "^0.1.5",
|
|
77
77
|
"@mark1russell7/rigor-statistics": "^0.1.8",
|
|
78
78
|
"@mark1russell7/rigor-testkit": "^0.1.0",
|
|
79
|
+
"@stryker-mutator/core": "^10.1.1",
|
|
80
|
+
"@stryker-mutator/vitest-runner": "^10.1.1",
|
|
79
81
|
"@types/node": "^26.6.5",
|
|
82
|
+
"@vitest/coverage-v8": "^5.1.0",
|
|
80
83
|
"esbuild": "^0.28.2",
|
|
81
84
|
"fast-check": "^4.10.2",
|
|
82
85
|
"tsdown": "^0.23.1",
|
package/src/async/async-queue.ts
CHANGED
|
@@ -22,12 +22,19 @@
|
|
|
22
22
|
|
|
23
23
|
import type { AsyncQueue as IAsyncQueue } from "../interfaces/queue.js";
|
|
24
24
|
|
|
25
|
+
/** The text of the error of a put, a new one or a waiting one, on a closed queue. */
|
|
26
|
+
const CLOSED = "Queue is closed";
|
|
27
|
+
|
|
28
|
+
/** The text of the error of a take, a peek or a wait on a queue that is closed and empty. */
|
|
29
|
+
const CLOSED_AND_EMPTY = "Queue is closed and empty";
|
|
30
|
+
|
|
25
31
|
/**
|
|
26
32
|
* Options for making an AsyncQueue.
|
|
27
33
|
*/
|
|
28
34
|
export interface AsyncQueueOptions {
|
|
29
35
|
/**
|
|
30
|
-
*
|
|
36
|
+
* The maximum number of elements: a whole number of 0 or more, or `Infinity` for no bound.
|
|
37
|
+
* Any other value gives a `RangeError`.
|
|
31
38
|
* @default Infinity
|
|
32
39
|
*/
|
|
33
40
|
capacity?: number;
|
|
@@ -80,7 +87,12 @@ export class AsyncQueue<T> implements IAsyncQueue<T> {
|
|
|
80
87
|
private readonly defaultTimeout?: number;
|
|
81
88
|
|
|
82
89
|
constructor(options: AsyncQueueOptions = {}) {
|
|
83
|
-
|
|
90
|
+
const capacity = options.capacity ?? Infinity;
|
|
91
|
+
// A capacity of -1, 1.5 or NaN gave a wrong remainingCapacity, or each put waited with no end.
|
|
92
|
+
if (capacity !== Infinity && !(Number.isInteger(capacity) && capacity >= 0)) {
|
|
93
|
+
throw new RangeError(`The capacity must be a whole number of 0 or more, or Infinity: ${String(capacity)}`);
|
|
94
|
+
}
|
|
95
|
+
this.capacity = capacity;
|
|
84
96
|
if (options.timeout !== undefined) {
|
|
85
97
|
this.defaultTimeout = options.timeout;
|
|
86
98
|
}
|
|
@@ -124,7 +136,7 @@ export class AsyncQueue<T> implements IAsyncQueue<T> {
|
|
|
124
136
|
*/
|
|
125
137
|
async put(element: T, timeout?: number): Promise<void> {
|
|
126
138
|
if (this._isClosed) {
|
|
127
|
-
throw new Error(
|
|
139
|
+
throw new Error(CLOSED);
|
|
128
140
|
}
|
|
129
141
|
if (this.tryPutNow(element)) {
|
|
130
142
|
return;
|
|
@@ -152,7 +164,7 @@ export class AsyncQueue<T> implements IAsyncQueue<T> {
|
|
|
152
164
|
}
|
|
153
165
|
|
|
154
166
|
/**
|
|
155
|
-
*
|
|
167
|
+
* This method removes an element from the queue and gives it.
|
|
156
168
|
* If the queue is empty, waits until an element is available.
|
|
157
169
|
*
|
|
158
170
|
* @throws Error if queue is closed and empty
|
|
@@ -166,7 +178,7 @@ export class AsyncQueue<T> implements IAsyncQueue<T> {
|
|
|
166
178
|
|
|
167
179
|
// If closed and empty, throw
|
|
168
180
|
if (this._isClosed) {
|
|
169
|
-
throw new Error(
|
|
181
|
+
throw new Error(CLOSED_AND_EMPTY);
|
|
170
182
|
}
|
|
171
183
|
|
|
172
184
|
// Wait for an element
|
|
@@ -203,7 +215,7 @@ export class AsyncQueue<T> implements IAsyncQueue<T> {
|
|
|
203
215
|
return () => false;
|
|
204
216
|
}
|
|
205
217
|
if (this._isClosed) {
|
|
206
|
-
onClosed(new Error(
|
|
218
|
+
onClosed(new Error(CLOSED_AND_EMPTY));
|
|
207
219
|
return () => false;
|
|
208
220
|
}
|
|
209
221
|
const taker: Resolver<T> = { resolve: onElement, reject: onClosed };
|
|
@@ -306,7 +318,7 @@ export class AsyncQueue<T> implements IAsyncQueue<T> {
|
|
|
306
318
|
}
|
|
307
319
|
|
|
308
320
|
if (this._isClosed) {
|
|
309
|
-
throw new Error(
|
|
321
|
+
throw new Error(CLOSED_AND_EMPTY);
|
|
310
322
|
}
|
|
311
323
|
|
|
312
324
|
// Wait for the next element without a take. (Before, peek took the element and put it back
|
|
@@ -349,7 +361,7 @@ export class AsyncQueue<T> implements IAsyncQueue<T> {
|
|
|
349
361
|
// Reject all waiting putters
|
|
350
362
|
for (const putter of this.putters) {
|
|
351
363
|
this.clearTimeout(putter);
|
|
352
|
-
putter.reject(new Error(
|
|
364
|
+
putter.reject(new Error(CLOSED));
|
|
353
365
|
}
|
|
354
366
|
this.putters = [];
|
|
355
367
|
|
|
@@ -357,12 +369,12 @@ export class AsyncQueue<T> implements IAsyncQueue<T> {
|
|
|
357
369
|
if (this.buffer.length === 0) {
|
|
358
370
|
for (const taker of this.takers) {
|
|
359
371
|
this.clearTimeout(taker);
|
|
360
|
-
taker.reject(new Error(
|
|
372
|
+
taker.reject(new Error(CLOSED_AND_EMPTY));
|
|
361
373
|
}
|
|
362
374
|
this.takers = [];
|
|
363
375
|
for (const peeker of this.peekers) {
|
|
364
376
|
this.clearTimeout(peeker);
|
|
365
|
-
peeker.reject(new Error(
|
|
377
|
+
peeker.reject(new Error(CLOSED_AND_EMPTY));
|
|
366
378
|
}
|
|
367
379
|
this.peekers = [];
|
|
368
380
|
}
|
|
@@ -471,6 +483,8 @@ export class AsyncQueue<T> implements IAsyncQueue<T> {
|
|
|
471
483
|
/**
|
|
472
484
|
* Factory function to make an AsyncQueue.
|
|
473
485
|
*
|
|
486
|
+
* @throws RangeError if the capacity is not a whole number of 0 or more, or `Infinity`
|
|
487
|
+
*
|
|
474
488
|
* @example
|
|
475
489
|
* const queue = asyncQueue<number>({ capacity: 100 })
|
|
476
490
|
* await queue.put(42)
|
package/src/async/channels.ts
CHANGED
|
@@ -42,7 +42,7 @@ export class Channel<T> {
|
|
|
42
42
|
|
|
43
43
|
/**
|
|
44
44
|
* This method sends a value through the channel.
|
|
45
|
-
*
|
|
45
|
+
* The call blocks while the buffer of the channel is full.
|
|
46
46
|
*
|
|
47
47
|
* @throws Error if channel is closed
|
|
48
48
|
*/
|
|
@@ -55,7 +55,7 @@ export class Channel<T> {
|
|
|
55
55
|
|
|
56
56
|
/**
|
|
57
57
|
* This method receives a value from the channel.
|
|
58
|
-
*
|
|
58
|
+
* The call blocks while no value is available.
|
|
59
59
|
*
|
|
60
60
|
* @throws Error if channel is closed and empty
|
|
61
61
|
*/
|
|
@@ -337,7 +337,7 @@ export function fanOut<T>(input: Channel<T>, count: number, bufferSize: number =
|
|
|
337
337
|
}
|
|
338
338
|
|
|
339
339
|
/**
|
|
340
|
-
* Fan-in: merges multiple input channels into one output channel.
|
|
340
|
+
* Fan-in: this function merges multiple input channels into one output channel.
|
|
341
341
|
*
|
|
342
342
|
* @example
|
|
343
343
|
* const ch1 = channel<number>()
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* semaphore - A bound on the tasks that run at the same time.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* const writes = semaphore(4);
|
|
6
|
+
* // At most 4 writes run at once. The others wait in the order that they came.
|
|
7
|
+
* await Promise.all(items.map((item) => writes.run(() => store.set(item.id, item))));
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/** A semaphore: a count of permits, and a queue of the callers that wait for one. */
|
|
11
|
+
export interface Semaphore {
|
|
12
|
+
/** The permits of the semaphore. */
|
|
13
|
+
readonly permits: number;
|
|
14
|
+
/** The permits that no caller holds. */
|
|
15
|
+
readonly available: number;
|
|
16
|
+
/** The callers that wait for a permit. */
|
|
17
|
+
readonly waiting: number;
|
|
18
|
+
/**
|
|
19
|
+
* This method gives a permit. It waits while all permits are held. The waiting callers get the permits in the order
|
|
20
|
+
* that they came. The result is the release of the permit. A second call of the release does nothing.
|
|
21
|
+
*/
|
|
22
|
+
acquire(): Promise<() => void>;
|
|
23
|
+
/** This method starts a task with a permit. The permit goes back when the task ends, also when it throws. */
|
|
24
|
+
run<R>(task: () => R | PromiseLike<R>): Promise<R>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* This function makes a semaphore with `permits` permits. The value is a whole number of 1 or more, or `Infinity`
|
|
29
|
+
* for no bound. Another value throws a `RangeError`.
|
|
30
|
+
*/
|
|
31
|
+
export function semaphore(permits: number): Semaphore {
|
|
32
|
+
if (!(permits === Infinity || (Number.isInteger(permits) && permits >= 1))) {
|
|
33
|
+
throw new RangeError(`A semaphore needs a whole number of permits of 1 or more, or Infinity: ${permits}`);
|
|
34
|
+
}
|
|
35
|
+
let held = 0;
|
|
36
|
+
// The waiting callers, in the order that they came. A release gives its permit to the first.
|
|
37
|
+
const queue: Array<() => void> = [];
|
|
38
|
+
let head = 0;
|
|
39
|
+
|
|
40
|
+
const releaseOnce = (): (() => void) => {
|
|
41
|
+
let done = false;
|
|
42
|
+
return () => {
|
|
43
|
+
if (done) return;
|
|
44
|
+
done = true;
|
|
45
|
+
if (head < queue.length) {
|
|
46
|
+
const next = queue[head]!;
|
|
47
|
+
head += 1;
|
|
48
|
+
// The permit goes from the releaser to the next caller, so `held` does not change
|
|
49
|
+
if (head > 64 && head * 2 > queue.length) {
|
|
50
|
+
queue.splice(0, head);
|
|
51
|
+
head = 0;
|
|
52
|
+
}
|
|
53
|
+
next();
|
|
54
|
+
} else {
|
|
55
|
+
held -= 1;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const acquire = (): Promise<() => void> => {
|
|
61
|
+
if (held < permits) {
|
|
62
|
+
held += 1;
|
|
63
|
+
return Promise.resolve(releaseOnce());
|
|
64
|
+
}
|
|
65
|
+
return new Promise((resolve) => {
|
|
66
|
+
queue.push(() => resolve(releaseOnce()));
|
|
67
|
+
});
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
permits,
|
|
72
|
+
get available() {
|
|
73
|
+
return permits - held;
|
|
74
|
+
},
|
|
75
|
+
get waiting() {
|
|
76
|
+
return queue.length - head;
|
|
77
|
+
},
|
|
78
|
+
acquire,
|
|
79
|
+
async run<R>(task: () => R | PromiseLike<R>): Promise<R> {
|
|
80
|
+
const release = await acquire();
|
|
81
|
+
try {
|
|
82
|
+
return await task();
|
|
83
|
+
} finally {
|
|
84
|
+
release();
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
}
|
package/src/behaviors/bounded.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Bounded behavior
|
|
2
|
+
* Bounded behavior: this module enforces capacity limits on collections.
|
|
3
3
|
*
|
|
4
4
|
* This module wraps collections to prevent them from exceeding a maximum size.
|
|
5
5
|
* This module gives various overflow policies to handle full collections.
|
package/src/behaviors/evented.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Evented behavior
|
|
2
|
+
* Evented behavior: this module emits typed events on collection mutations.
|
|
3
3
|
*
|
|
4
4
|
* This module wraps collections to emit events whenever elements are added, removed,
|
|
5
5
|
* or the collection is cleared. This module enables reactive programming patterns.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Readonly behavior
|
|
2
|
+
* Readonly behavior: this module makes immutable views of collections.
|
|
3
3
|
*
|
|
4
4
|
* This module wraps collections to prevent modifications by throwing errors
|
|
5
5
|
* on any mutation try. Mirrors Java's Collections.unmodifiable*() methods.
|
package/src/behaviors/safe.ts
CHANGED
|
@@ -21,27 +21,27 @@ import { none, some, toOptional, tryCatch, type Optional, type Result } from "@m
|
|
|
21
21
|
*/
|
|
22
22
|
export interface SafeListOperations<T> {
|
|
23
23
|
/**
|
|
24
|
-
*
|
|
24
|
+
* This method gives the element at an index, as an optional.
|
|
25
25
|
*/
|
|
26
26
|
get(index: number): Optional<T>;
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
|
-
*
|
|
29
|
+
* This method sets the element at an index. It gives a Result with the old value or an error.
|
|
30
30
|
*/
|
|
31
31
|
set(index: number, element: T): Result<T, string>;
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
|
-
*
|
|
34
|
+
* This method removes the element at an index. It gives a Result with the removed value or an error.
|
|
35
35
|
*/
|
|
36
36
|
removeAt(index: number): Result<T, string>;
|
|
37
37
|
|
|
38
38
|
/**
|
|
39
|
-
*
|
|
39
|
+
* This method gives the first element, as an optional.
|
|
40
40
|
*/
|
|
41
41
|
first(): Optional<T>;
|
|
42
42
|
|
|
43
43
|
/**
|
|
44
|
-
*
|
|
44
|
+
* This method gives the last element, as an optional.
|
|
45
45
|
*/
|
|
46
46
|
last(): Optional<T>;
|
|
47
47
|
|
|
@@ -61,17 +61,17 @@ export interface SafeListOperations<T> {
|
|
|
61
61
|
*/
|
|
62
62
|
export interface SafeQueueOperations<T> {
|
|
63
63
|
/**
|
|
64
|
-
*
|
|
64
|
+
* This method gives the head element as an optional, and does not remove it.
|
|
65
65
|
*/
|
|
66
66
|
peek(): Optional<T>;
|
|
67
67
|
|
|
68
68
|
/**
|
|
69
|
-
*
|
|
69
|
+
* This method removes the head element and gives it as an optional.
|
|
70
70
|
*/
|
|
71
71
|
poll(): Optional<T>;
|
|
72
72
|
|
|
73
73
|
/**
|
|
74
|
-
*
|
|
74
|
+
* This method dequeues the head element and gives it as an optional.
|
|
75
75
|
*/
|
|
76
76
|
dequeue(): Optional<T>;
|
|
77
77
|
}
|
|
@@ -81,27 +81,27 @@ export interface SafeQueueOperations<T> {
|
|
|
81
81
|
*/
|
|
82
82
|
export interface SafeDequeOperations<T> extends SafeQueueOperations<T> {
|
|
83
83
|
/**
|
|
84
|
-
*
|
|
84
|
+
* This method gives the first element as an optional, and does not remove it.
|
|
85
85
|
*/
|
|
86
86
|
peekFirst(): Optional<T>;
|
|
87
87
|
|
|
88
88
|
/**
|
|
89
|
-
*
|
|
89
|
+
* This method gives the last element as an optional, and does not remove it.
|
|
90
90
|
*/
|
|
91
91
|
peekLast(): Optional<T>;
|
|
92
92
|
|
|
93
93
|
/**
|
|
94
|
-
*
|
|
94
|
+
* This method removes the first element and gives it as an optional.
|
|
95
95
|
*/
|
|
96
96
|
pollFirst(): Optional<T>;
|
|
97
97
|
|
|
98
98
|
/**
|
|
99
|
-
*
|
|
99
|
+
* This method removes the last element and gives it as an optional.
|
|
100
100
|
*/
|
|
101
101
|
pollLast(): Optional<T>;
|
|
102
102
|
|
|
103
103
|
/**
|
|
104
|
-
*
|
|
104
|
+
* This method pops the top element and gives it as an optional.
|
|
105
105
|
*/
|
|
106
106
|
pop(): Optional<T>;
|
|
107
107
|
}
|
|
@@ -111,17 +111,17 @@ export interface SafeDequeOperations<T> extends SafeQueueOperations<T> {
|
|
|
111
111
|
*/
|
|
112
112
|
export interface SafeMapOperations<K, V> {
|
|
113
113
|
/**
|
|
114
|
-
*
|
|
114
|
+
* This method gives the value of a key, as an optional.
|
|
115
115
|
*/
|
|
116
116
|
get(key: K): Optional<V>;
|
|
117
117
|
|
|
118
118
|
/**
|
|
119
|
-
*
|
|
119
|
+
* This method sets the value of a key. It gives a Result with the old value or a success.
|
|
120
120
|
*/
|
|
121
121
|
set(key: K, value: V): Result<V | undefined, string>;
|
|
122
122
|
|
|
123
123
|
/**
|
|
124
|
-
*
|
|
124
|
+
* This method deletes a key. It gives a Result with the deleted value or an error.
|
|
125
125
|
*/
|
|
126
126
|
delete(key: K): Result<V | undefined, string>;
|
|
127
127
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Synchronized behavior
|
|
2
|
+
* Synchronized behavior: this module gives thread-safe access with mutex locking.
|
|
3
3
|
*
|
|
4
4
|
* This module wraps collections to serialize all operations using a mutex, making sure that
|
|
5
5
|
* thread-safe access in concurrent environments. Mirrors Java's
|
package/src/behaviors/ttl.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* TTL (Time-To-Live) expiration behavior.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* The behavior removes each element when its time to live ends.
|
|
5
5
|
* Perfect for temporary caches and time-limited data.
|
|
6
6
|
*
|
|
7
7
|
* @example
|
|
@@ -20,7 +20,7 @@ import { removals, removalSource, type ElementRemoval, type MapRemoval } from ".
|
|
|
20
20
|
import type { Collection } from "../interfaces/collection.js";
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
|
-
*
|
|
23
|
+
* This function starts the background expiration checker, and it does not keep a Node.js process alive.
|
|
24
24
|
* (In a browser, setInterval gives a number and there is nothing to unref.)
|
|
25
25
|
*/
|
|
26
26
|
function startExpirationTimer(check: () => void, interval: number): ReturnType<typeof setInterval> {
|
|
@@ -368,7 +368,7 @@ export class TTLCache<K, V> {
|
|
|
368
368
|
}
|
|
369
369
|
|
|
370
370
|
/**
|
|
371
|
-
*
|
|
371
|
+
* This method removes the expired entries at once.
|
|
372
372
|
*/
|
|
373
373
|
cleanup(): void {
|
|
374
374
|
const now = Date.now();
|
|
@@ -392,7 +392,7 @@ export class TTLCache<K, V> {
|
|
|
392
392
|
}
|
|
393
393
|
|
|
394
394
|
/**
|
|
395
|
-
*
|
|
395
|
+
* This method gives the remaining TTL of a key, in milliseconds.
|
|
396
396
|
* This method gives undefined if key doesn't exist or is expired.
|
|
397
397
|
*/
|
|
398
398
|
getTTL(key: K): number | undefined {
|
|
@@ -404,7 +404,7 @@ export class TTLCache<K, V> {
|
|
|
404
404
|
}
|
|
405
405
|
|
|
406
406
|
/**
|
|
407
|
-
*
|
|
407
|
+
* This method updates the TTL of a key that is in the map.
|
|
408
408
|
*/
|
|
409
409
|
touch(key: K, customTTL?: number): boolean {
|
|
410
410
|
const entry = this.map.get(key);
|
|
@@ -425,7 +425,7 @@ export class TTLCache<K, V> {
|
|
|
425
425
|
}
|
|
426
426
|
|
|
427
427
|
/**
|
|
428
|
-
*
|
|
428
|
+
* This method stops the background expiration checker and cleans up.
|
|
429
429
|
*/
|
|
430
430
|
dispose(): void {
|
|
431
431
|
if (this.intervalId) {
|
package/src/core/events.ts
CHANGED
|
@@ -54,7 +54,7 @@ export class Emitter<E extends EventMap> {
|
|
|
54
54
|
private listeners = new Map<keyof E, Set<Listener<any>>>();
|
|
55
55
|
|
|
56
56
|
/**
|
|
57
|
-
*
|
|
57
|
+
* This method subscribes a listener to an event.
|
|
58
58
|
*
|
|
59
59
|
* @param event The event name
|
|
60
60
|
* @param listener The callback to invoke when event is emitted
|
|
@@ -79,7 +79,7 @@ export class Emitter<E extends EventMap> {
|
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
/**
|
|
82
|
-
*
|
|
82
|
+
* This method subscribes a listener to an event, and unsubscribes it after the first emission.
|
|
83
83
|
*
|
|
84
84
|
* @param event The event name
|
|
85
85
|
* @param listener The callback to invoke once
|
|
@@ -96,7 +96,7 @@ export class Emitter<E extends EventMap> {
|
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
/**
|
|
99
|
-
*
|
|
99
|
+
* This method emits an event to all registered listeners.
|
|
100
100
|
*
|
|
101
101
|
* @param event The event name
|
|
102
102
|
* @param payload The event payload
|
|
@@ -121,7 +121,7 @@ export class Emitter<E extends EventMap> {
|
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
/**
|
|
124
|
-
*
|
|
124
|
+
* This method removes all listeners of an event, or of all events when the call gives no event.
|
|
125
125
|
*
|
|
126
126
|
* @param event Optional event name to clear listeners for
|
|
127
127
|
*/
|
|
@@ -134,7 +134,7 @@ export class Emitter<E extends EventMap> {
|
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
/**
|
|
137
|
-
*
|
|
137
|
+
* This method gives the number of listeners of an event.
|
|
138
138
|
*
|
|
139
139
|
* @param event The event name
|
|
140
140
|
* @returns Number of registered listeners
|
|
@@ -144,21 +144,21 @@ export class Emitter<E extends EventMap> {
|
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
/**
|
|
147
|
-
*
|
|
147
|
+
* This method gives the names of the events that have one or more listeners.
|
|
148
148
|
*/
|
|
149
149
|
eventNames(): (keyof E)[] {
|
|
150
150
|
return Array.from(this.listeners.keys());
|
|
151
151
|
}
|
|
152
152
|
|
|
153
153
|
/**
|
|
154
|
-
*
|
|
154
|
+
* This method gives true when an event has one or more listeners.
|
|
155
155
|
*/
|
|
156
156
|
hasListeners<K extends keyof E>(event: K): boolean {
|
|
157
157
|
return this.listenerCount(event) > 0;
|
|
158
158
|
}
|
|
159
159
|
|
|
160
160
|
/**
|
|
161
|
-
*
|
|
161
|
+
* This method removes all listeners and releases the resources.
|
|
162
162
|
*/
|
|
163
163
|
dispose(): void {
|
|
164
164
|
this.listeners.clear();
|
|
@@ -166,7 +166,7 @@ export class Emitter<E extends EventMap> {
|
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
/**
|
|
169
|
-
*
|
|
169
|
+
* This function makes an event emitter for an event map.
|
|
170
170
|
*
|
|
171
171
|
* This is a convenience factory function.
|
|
172
172
|
*
|
|
@@ -205,7 +205,7 @@ export class EventBatcher<E extends EventMap, K extends keyof E> {
|
|
|
205
205
|
}
|
|
206
206
|
|
|
207
207
|
/**
|
|
208
|
-
*
|
|
208
|
+
* This method adds an event to the current batch.
|
|
209
209
|
*/
|
|
210
210
|
queue(payload: E[K]): void {
|
|
211
211
|
this.batch.push(payload);
|
|
@@ -216,7 +216,7 @@ export class EventBatcher<E extends EventMap, K extends keyof E> {
|
|
|
216
216
|
}
|
|
217
217
|
|
|
218
218
|
/**
|
|
219
|
-
*
|
|
219
|
+
* This method emits all batched events at once.
|
|
220
220
|
*/
|
|
221
221
|
flush(): void {
|
|
222
222
|
if (this.timer !== null) {
|
|
@@ -232,7 +232,7 @@ export class EventBatcher<E extends EventMap, K extends keyof E> {
|
|
|
232
232
|
}
|
|
233
233
|
|
|
234
234
|
/**
|
|
235
|
-
*
|
|
235
|
+
* This method sends the events of the batch and releases the resources.
|
|
236
236
|
*/
|
|
237
237
|
dispose(): void {
|
|
238
238
|
this.flush();
|
|
@@ -272,7 +272,7 @@ export class AsyncEmitter<E extends EventMap> {
|
|
|
272
272
|
}
|
|
273
273
|
|
|
274
274
|
/**
|
|
275
|
-
*
|
|
275
|
+
* This method emits an event and waits until all async listeners complete.
|
|
276
276
|
*
|
|
277
277
|
* @param event The event name
|
|
278
278
|
* @param payload The event payload
|
|
@@ -340,7 +340,7 @@ export const forwardEvent = <
|
|
|
340
340
|
};
|
|
341
341
|
|
|
342
342
|
/**
|
|
343
|
-
*
|
|
343
|
+
* This function makes a filtered event subscription that fires only when the predicate is true.
|
|
344
344
|
*
|
|
345
345
|
* @example
|
|
346
346
|
* const emitter = new Emitter<{ change: { value: number } }>()
|
package/src/core/middleware.ts
CHANGED
|
@@ -90,13 +90,13 @@ export const hasCapability = <C extends Collection<any>, K extends keyof any>(
|
|
|
90
90
|
*/
|
|
91
91
|
export interface Evented<E extends Record<string, any>> {
|
|
92
92
|
/**
|
|
93
|
-
*
|
|
93
|
+
* This method subscribes a listener to an event.
|
|
94
94
|
* @returns Unsubscribe function
|
|
95
95
|
*/
|
|
96
96
|
on<K extends keyof E>(event: K, handler: (payload: E[K]) => void): () => void;
|
|
97
97
|
|
|
98
98
|
/**
|
|
99
|
-
*
|
|
99
|
+
* This method emits an event. Only the middleware calls it.
|
|
100
100
|
* @internal
|
|
101
101
|
*/
|
|
102
102
|
emit?<K extends keyof E>(event: K, payload: E[K]): void;
|
|
@@ -135,7 +135,7 @@ export interface SafeCollection {
|
|
|
135
135
|
}
|
|
136
136
|
|
|
137
137
|
/**
|
|
138
|
-
*
|
|
138
|
+
* This type gives the element type of a collection.
|
|
139
139
|
*/
|
|
140
140
|
export type ElementType<C> = C extends {
|
|
141
141
|
[Symbol.iterator](): Iterator<infer T>;
|
|
@@ -148,7 +148,7 @@ export type ElementType<C> = C extends {
|
|
|
148
148
|
: never;
|
|
149
149
|
|
|
150
150
|
/**
|
|
151
|
-
*
|
|
151
|
+
* This type changes each method of a type so that it gives its async equivalent.
|
|
152
152
|
*/
|
|
153
153
|
export type Asyncify<C> = {
|
|
154
154
|
[K in keyof C]: C[K] extends (...args: infer Args) => infer R
|
|
@@ -265,7 +265,7 @@ export abstract class CollectionBuilder<C> {
|
|
|
265
265
|
protected middleware: Middleware<C>[] = [];
|
|
266
266
|
|
|
267
267
|
/**
|
|
268
|
-
*
|
|
268
|
+
* This method applies a custom middleware.
|
|
269
269
|
*/
|
|
270
270
|
use(middleware: Middleware<C>): this {
|
|
271
271
|
this.middleware.push(middleware);
|
|
@@ -273,7 +273,7 @@ export abstract class CollectionBuilder<C> {
|
|
|
273
273
|
}
|
|
274
274
|
|
|
275
275
|
/**
|
|
276
|
-
*
|
|
276
|
+
* This method builds the final collection: it composes all middleware with the base.
|
|
277
277
|
*/
|
|
278
278
|
abstract build(): C;
|
|
279
279
|
}
|
package/src/core/policies.ts
CHANGED
|
@@ -209,11 +209,11 @@ export type MergeFunction<T> = (current: T, incoming: T) => T;
|
|
|
209
209
|
* Policy for when to compact internal storage.
|
|
210
210
|
*/
|
|
211
211
|
export interface CompactionPolicy {
|
|
212
|
-
/**
|
|
212
|
+
/** The collection compacts when the used part of its capacity is less than this ratio, from 0 to 1. */
|
|
213
213
|
readonly minLoadFactor?: number;
|
|
214
|
-
/**
|
|
214
|
+
/** The collection compacts when its wasted space is more than this number of elements. */
|
|
215
215
|
readonly maxWastedSpace?: number;
|
|
216
|
-
/**
|
|
216
|
+
/** When it is true, the collection compacts at each removal. */
|
|
217
217
|
readonly autoCompact?: boolean;
|
|
218
218
|
}
|
|
219
219
|
|
package/src/dag/builder.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import type { DAGNode } from "./types.js";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
8
|
+
* This function filters a DAG to the nodes that a root can reach.
|
|
9
9
|
*/
|
|
10
10
|
export function filterDAGFromRoot<TNode extends DAGNode>(
|
|
11
11
|
nodes: Map<string, TNode>,
|
|
@@ -33,7 +33,7 @@ export function filterDAGFromRoot<TNode extends DAGNode>(
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
|
-
*
|
|
36
|
+
* This function gives all ancestors of a node: the nodes that this node depends on, transitively.
|
|
37
37
|
*/
|
|
38
38
|
export function getAncestors<TNode extends DAGNode>(
|
|
39
39
|
nodes: Map<string, TNode>,
|
|
@@ -62,7 +62,7 @@ export function getAncestors<TNode extends DAGNode>(
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
/**
|
|
65
|
-
*
|
|
65
|
+
* This function gives all descendants of a node: the nodes that depend on this node, transitively.
|
|
66
66
|
*/
|
|
67
67
|
export function getDescendants<TNode extends DAGNode>(
|
|
68
68
|
nodes: Map<string, TNode>,
|
|
@@ -100,7 +100,7 @@ export function getDescendants<TNode extends DAGNode>(
|
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
/**
|
|
103
|
-
*
|
|
103
|
+
* This function makes a DAG node from a minimal input.
|
|
104
104
|
*/
|
|
105
105
|
export function createNode<TData = unknown>(
|
|
106
106
|
id: string,
|
|
@@ -115,7 +115,7 @@ export function createNode<TData = unknown>(
|
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
/**
|
|
118
|
-
*
|
|
118
|
+
* This function builds a Map of nodes from an array.
|
|
119
119
|
*/
|
|
120
120
|
export function buildNodeMap<TNode extends DAGNode>(
|
|
121
121
|
nodes: TNode[]
|