@matter/general 0.16.0-alpha.0-20251213-e83db3732 → 0.16.0-alpha.0-20251217-038f88085
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 +1 -1
- package/dist/cjs/util/Observable.d.ts +9 -0
- package/dist/cjs/util/Observable.d.ts.map +1 -1
- package/dist/cjs/util/Observable.js +21 -0
- package/dist/cjs/util/Observable.js.map +1 -1
- package/dist/cjs/util/Semaphore.d.ts +60 -0
- package/dist/cjs/util/Semaphore.d.ts.map +1 -0
- package/dist/cjs/util/Semaphore.js +232 -0
- package/dist/cjs/util/Semaphore.js.map +6 -0
- package/dist/cjs/util/identifier-case.d.ts +1 -0
- package/dist/cjs/util/identifier-case.d.ts.map +1 -1
- package/dist/cjs/util/identifier-case.js +6 -1
- package/dist/cjs/util/identifier-case.js.map +1 -1
- package/dist/cjs/util/index.d.ts +1 -1
- package/dist/cjs/util/index.d.ts.map +1 -1
- package/dist/cjs/util/index.js +1 -1
- package/dist/cjs/util/index.js.map +1 -1
- package/dist/esm/util/Observable.d.ts +9 -0
- package/dist/esm/util/Observable.d.ts.map +1 -1
- package/dist/esm/util/Observable.js +21 -0
- package/dist/esm/util/Observable.js.map +1 -1
- package/dist/esm/util/Semaphore.d.ts +60 -0
- package/dist/esm/util/Semaphore.d.ts.map +1 -0
- package/dist/esm/util/Semaphore.js +212 -0
- package/dist/esm/util/Semaphore.js.map +6 -0
- package/dist/esm/util/identifier-case.d.ts +1 -0
- package/dist/esm/util/identifier-case.d.ts.map +1 -1
- package/dist/esm/util/identifier-case.js +6 -1
- package/dist/esm/util/identifier-case.js.map +1 -1
- package/dist/esm/util/index.d.ts +1 -1
- package/dist/esm/util/index.d.ts.map +1 -1
- package/dist/esm/util/index.js +1 -1
- package/package.json +2 -2
- package/src/util/Observable.ts +32 -0
- package/src/util/Semaphore.ts +231 -0
- package/src/util/identifier-case.ts +8 -1
- package/src/util/index.ts +1 -1
- package/dist/cjs/util/PromiseQueue.d.ts +0 -28
- package/dist/cjs/util/PromiseQueue.d.ts.map +0 -1
- package/dist/cjs/util/PromiseQueue.js +0 -112
- package/dist/cjs/util/PromiseQueue.js.map +0 -6
- package/dist/esm/util/PromiseQueue.d.ts +0 -28
- package/dist/esm/util/PromiseQueue.d.ts.map +0 -1
- package/dist/esm/util/PromiseQueue.js +0 -92
- package/dist/esm/util/PromiseQueue.js.map +0 -6
- package/src/util/PromiseQueue.ts +0 -109
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2022-2025 Matter.js Authors
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { AbortedError } from "#MatterError.js";
|
|
8
|
+
import { Duration } from "#time/Duration.js";
|
|
9
|
+
import { Time, Timer } from "#time/Time.js";
|
|
10
|
+
import { Instant } from "#time/TimeUnit.js";
|
|
11
|
+
import { Logger } from "../log/Logger.js";
|
|
12
|
+
import { Abort } from "./Abort.js";
|
|
13
|
+
import { createPromise } from "./Promises.js";
|
|
14
|
+
|
|
15
|
+
const logger = Logger.get("Semaphore");
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* A work slot that must be released when work is complete.
|
|
19
|
+
*/
|
|
20
|
+
export interface WorkSlot extends Disposable {
|
|
21
|
+
/**
|
|
22
|
+
* Release the slot manually.
|
|
23
|
+
* This is called automatically when using `using` syntax.
|
|
24
|
+
*/
|
|
25
|
+
close(): void;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Release the slot automatically when the object is garbage collected.
|
|
29
|
+
*/
|
|
30
|
+
[Symbol.dispose](): void;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* A queue that limits concurrent work using a slot-based approach.
|
|
35
|
+
*
|
|
36
|
+
* Instead of queueing promises or iterators directly, callers get a "work slot"
|
|
37
|
+
* which they hold while doing work. The slot must be released when work is complete.
|
|
38
|
+
*/
|
|
39
|
+
export class Semaphore {
|
|
40
|
+
readonly #delay: Duration;
|
|
41
|
+
readonly #queue = new Array<{
|
|
42
|
+
resolve: (slot: WorkSlot) => void;
|
|
43
|
+
}>();
|
|
44
|
+
#delayTimer: Timer;
|
|
45
|
+
readonly #concurrency: number;
|
|
46
|
+
#runningCount = 0;
|
|
47
|
+
#abort = new Abort();
|
|
48
|
+
#closed = false;
|
|
49
|
+
|
|
50
|
+
constructor(concurrency = 1, delay = Instant) {
|
|
51
|
+
this.#concurrency = concurrency;
|
|
52
|
+
this.#delay = delay;
|
|
53
|
+
this.#delayTimer = Time.getTimer("Queue delay", this.#delay, () => this.#processNextInQueue());
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Get a work slot from the queue.
|
|
58
|
+
*
|
|
59
|
+
* This method returns a promise that resolves when a slot is available.
|
|
60
|
+
* The returned slot must be released when work is complete, either by
|
|
61
|
+
* calling `close()` or by using the `using` syntax.
|
|
62
|
+
*
|
|
63
|
+
* @param abort - Optional abort signal to cancel waiting for a slot
|
|
64
|
+
* @returns A disposable work slot
|
|
65
|
+
* @throws AbortedError if the abort signal is triggered before a slot is obtained
|
|
66
|
+
*/
|
|
67
|
+
async obtainSlot(abort?: Abort.Signal): Promise<WorkSlot> {
|
|
68
|
+
// Check if closed or already aborted before proceeding
|
|
69
|
+
if (this.#closed) {
|
|
70
|
+
throw new AbortedError("Queue is closed");
|
|
71
|
+
}
|
|
72
|
+
if (abort) {
|
|
73
|
+
const signal = "signal" in abort ? abort.signal : abort;
|
|
74
|
+
signal.throwIfAborted();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Combine caller's abort with our internal abort for unified cancellation
|
|
78
|
+
using combinedAbort = new Abort({ abort: abort ? [abort, this.#abort] : [this.#abort] });
|
|
79
|
+
|
|
80
|
+
// Check if we can grant it immediately:
|
|
81
|
+
// - Must have capacity
|
|
82
|
+
// - No one else waiting in the queue
|
|
83
|
+
// - Either no delay configured, or delay timer not running (a cooldown period passed)
|
|
84
|
+
const canGrantImmediately =
|
|
85
|
+
this.#runningCount < this.#concurrency &&
|
|
86
|
+
this.#queue.length === 0 &&
|
|
87
|
+
(this.#delay === 0 || !this.#delayTimer.isRunning);
|
|
88
|
+
|
|
89
|
+
if (canGrantImmediately) {
|
|
90
|
+
return this.#grantSlot();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Need to queue - either no capacity or delay hasn't passed yet
|
|
94
|
+
const { promise, resolver } = createPromise<WorkSlot>();
|
|
95
|
+
|
|
96
|
+
const entry = { resolve: resolver };
|
|
97
|
+
|
|
98
|
+
logger.debug("Queueing slot request at position", this.#queue.length + 1);
|
|
99
|
+
this.#queue.push(entry);
|
|
100
|
+
|
|
101
|
+
// Ensure the timer is running to process queue (handles both capacity-wait and delay-wait)
|
|
102
|
+
this.#scheduleProcessing();
|
|
103
|
+
|
|
104
|
+
// Race the promise against abort - if aborted, remove from the queue and throw
|
|
105
|
+
const result = await combinedAbort.race(promise);
|
|
106
|
+
if (result === undefined) {
|
|
107
|
+
// Aborted - remove from queue if still present
|
|
108
|
+
const index = this.#queue.indexOf(entry);
|
|
109
|
+
if (index !== -1) {
|
|
110
|
+
this.#queue.splice(index, 1);
|
|
111
|
+
logger.debug("Slot request aborted, removed from queue. Remaining:", this.#queue.length);
|
|
112
|
+
}
|
|
113
|
+
// Throw AbortedError (use reason if it's already an AbortedError)
|
|
114
|
+
const reason = combinedAbort.reason;
|
|
115
|
+
throw reason instanceof AbortedError ? reason : new AbortedError();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return result;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Schedule processing of the queue if there's capacity and items waiting.
|
|
123
|
+
*/
|
|
124
|
+
#scheduleProcessing(): void {
|
|
125
|
+
if (this.#delayTimer.isRunning) return;
|
|
126
|
+
if (this.#queue.length === 0) return;
|
|
127
|
+
if (this.#runningCount >= this.#concurrency) return;
|
|
128
|
+
|
|
129
|
+
const delayMs = this.#delay;
|
|
130
|
+
if (delayMs === 0) {
|
|
131
|
+
// No delay configured, process immediately
|
|
132
|
+
this.#processNextInQueue();
|
|
133
|
+
} else {
|
|
134
|
+
// Start the delay timer
|
|
135
|
+
this.#delayTimer.start();
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Grant a slot immediately (when capacity is available).
|
|
141
|
+
*/
|
|
142
|
+
#grantSlot(): WorkSlot {
|
|
143
|
+
this.#runningCount++;
|
|
144
|
+
|
|
145
|
+
// Start a delay timer to enforce cooldown before the next grant
|
|
146
|
+
if (this.#delay > 0) {
|
|
147
|
+
this.#delayTimer.start();
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
let released = false;
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
close: () => {
|
|
154
|
+
if (released) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
released = true;
|
|
158
|
+
this.#releaseSlot();
|
|
159
|
+
},
|
|
160
|
+
|
|
161
|
+
[Symbol.dispose]() {
|
|
162
|
+
this.close();
|
|
163
|
+
},
|
|
164
|
+
} satisfies WorkSlot;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Release a slot and potentially grant to next in queue.
|
|
169
|
+
*/
|
|
170
|
+
#releaseSlot(): void {
|
|
171
|
+
this.#runningCount--;
|
|
172
|
+
this.#scheduleProcessing();
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Process the next waiting request in the queue.
|
|
177
|
+
*/
|
|
178
|
+
#processNextInQueue(): void {
|
|
179
|
+
if (this.#queue.length === 0) {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
if (this.#runningCount >= this.#concurrency) {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const next = this.#queue.shift()!;
|
|
187
|
+
|
|
188
|
+
// Grant the slot to the next waiter
|
|
189
|
+
const slot = this.#grantSlot();
|
|
190
|
+
next.resolve(slot);
|
|
191
|
+
|
|
192
|
+
// Schedule next processing if more items in queue
|
|
193
|
+
this.#scheduleProcessing();
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Clear the queue (entries will be rejected via abort).
|
|
198
|
+
*/
|
|
199
|
+
clear(): void {
|
|
200
|
+
if (this.#queue.length > 0) {
|
|
201
|
+
// Abort current waiters and create fresh abort for future requests
|
|
202
|
+
this.#abort.abort(new AbortedError("Queue cleared"));
|
|
203
|
+
this.#abort = new Abort();
|
|
204
|
+
}
|
|
205
|
+
this.#queue.length = 0;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Get the number of pending slot requests in the queue.
|
|
210
|
+
*/
|
|
211
|
+
get count() {
|
|
212
|
+
return this.#queue.length;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Get the number of currently active slots.
|
|
217
|
+
*/
|
|
218
|
+
get running() {
|
|
219
|
+
return this.#runningCount;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Close the queue and reject all pending slot requests.
|
|
224
|
+
*/
|
|
225
|
+
close(): void {
|
|
226
|
+
this.#closed = true;
|
|
227
|
+
this.#abort.abort(new AbortedError("Queue is closed"));
|
|
228
|
+
this.clear();
|
|
229
|
+
this.#delayTimer.stop();
|
|
230
|
+
}
|
|
231
|
+
}
|
|
@@ -14,6 +14,7 @@ export function capitalize<T extends string>(text: T) {
|
|
|
14
14
|
/**
|
|
15
15
|
* Converts identifiers of the form "foo-bar", "foo_bar", "foo bar", "foo*bar",
|
|
16
16
|
* "fooBar" or "FOOBar" into "fooBar" or "FooBar".
|
|
17
|
+
* Stops processing when a "$" is reached
|
|
17
18
|
*/
|
|
18
19
|
export function camelize(name: string, upperFirst = false) {
|
|
19
20
|
const pieces = new Array<string>();
|
|
@@ -28,6 +29,9 @@ export function camelize(name: string, upperFirst = false) {
|
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
for (; i < name.length; i++) {
|
|
32
|
+
if (name[i] === "$") {
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
31
35
|
if (name[i] >= "A" && name[i] <= "Z") {
|
|
32
36
|
if (sawLower) {
|
|
33
37
|
addPiece(i);
|
|
@@ -55,7 +59,6 @@ export function camelize(name: string, upperFirst = false) {
|
|
|
55
59
|
}
|
|
56
60
|
|
|
57
61
|
pieceStart = i + 1;
|
|
58
|
-
continue;
|
|
59
62
|
}
|
|
60
63
|
addPiece(i);
|
|
61
64
|
|
|
@@ -76,6 +79,10 @@ export function camelize(name: string, upperFirst = false) {
|
|
|
76
79
|
// Special case so "100ths" doesn't become "100Ths" which is formally correct but goofy
|
|
77
80
|
result = result.replace(/(\d)Ths/i, "$1ths");
|
|
78
81
|
|
|
82
|
+
if (i < name.length) {
|
|
83
|
+
result += name.slice(i);
|
|
84
|
+
}
|
|
85
|
+
|
|
79
86
|
return result;
|
|
80
87
|
}
|
|
81
88
|
|
package/src/util/index.ts
CHANGED
|
@@ -34,8 +34,8 @@ export * from "./Mutex.js";
|
|
|
34
34
|
export * from "./NamedHandler.js";
|
|
35
35
|
export * from "./Number.js";
|
|
36
36
|
export * from "./Observable.js";
|
|
37
|
-
export * from "./PromiseQueue.js";
|
|
38
37
|
export * from "./Promises.js";
|
|
38
|
+
export * from "./Semaphore.js";
|
|
39
39
|
export * from "./serialize.js";
|
|
40
40
|
export * from "./Set.js";
|
|
41
41
|
export * from "./Singleton.js";
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license
|
|
3
|
-
* Copyright 2022-2025 Matter.js Authors
|
|
4
|
-
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
-
*/
|
|
6
|
-
import { Duration } from "#time/Duration.js";
|
|
7
|
-
/**
|
|
8
|
-
* A queue that processes promises with a given concurrency and delays after each promise if desired.
|
|
9
|
-
*/
|
|
10
|
-
export declare class PromiseQueue {
|
|
11
|
-
#private;
|
|
12
|
-
constructor(concurrency?: number, delay?: Duration);
|
|
13
|
-
/**
|
|
14
|
-
* Add a promise to the queue. It returns a promise that can be awaited.
|
|
15
|
-
*/
|
|
16
|
-
add<T>(executor: () => Promise<T>): Promise<T>;
|
|
17
|
-
/**
|
|
18
|
-
* Clear the queue.
|
|
19
|
-
*/
|
|
20
|
-
clear(reject: boolean): void;
|
|
21
|
-
/** Get the number of promises in the queue. */
|
|
22
|
-
get count(): number;
|
|
23
|
-
/**
|
|
24
|
-
* Close the queue and remove all outstanding promises (but do not reject them).
|
|
25
|
-
*/
|
|
26
|
-
close(): void;
|
|
27
|
-
}
|
|
28
|
-
//# sourceMappingURL=PromiseQueue.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"PromiseQueue.d.ts","sourceRoot":"","sources":["../../../src/util/PromiseQueue.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAQ7C;;GAEG;AACH,qBAAa,YAAY;;gBAMT,WAAW,SAAI,EAAE,KAAK,WAAU;IAK5C;;OAEG;IACH,GAAG,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAsD9C;;OAEG;IACH,KAAK,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI;IAS5B,+CAA+C;IAC/C,IAAI,KAAK,WAER;IAED;;OAEG;IACH,KAAK,IAAI,IAAI;CAGhB"}
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
-
var __export = (target, all) => {
|
|
7
|
-
for (var name in all)
|
|
8
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
-
};
|
|
10
|
-
var __copyProps = (to, from, except, desc) => {
|
|
11
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
-
for (let key of __getOwnPropNames(from))
|
|
13
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
-
}
|
|
16
|
-
return to;
|
|
17
|
-
};
|
|
18
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
-
var PromiseQueue_exports = {};
|
|
20
|
-
__export(PromiseQueue_exports, {
|
|
21
|
-
PromiseQueue: () => PromiseQueue
|
|
22
|
-
});
|
|
23
|
-
module.exports = __toCommonJS(PromiseQueue_exports);
|
|
24
|
-
var import_Time = require("#time/Time.js");
|
|
25
|
-
var import_TimeUnit = require("#time/TimeUnit.js");
|
|
26
|
-
var import_Logger = require("../log/Logger.js");
|
|
27
|
-
var import_Promises = require("./Promises.js");
|
|
28
|
-
/**
|
|
29
|
-
* @license
|
|
30
|
-
* Copyright 2022-2025 Matter.js Authors
|
|
31
|
-
* SPDX-License-Identifier: Apache-2.0
|
|
32
|
-
*/
|
|
33
|
-
const logger = import_Logger.Logger.get("PromiseQueue");
|
|
34
|
-
class PromiseQueue {
|
|
35
|
-
#delay;
|
|
36
|
-
#queue = new Array();
|
|
37
|
-
#concurrency;
|
|
38
|
-
#runningCount = 0;
|
|
39
|
-
constructor(concurrency = 1, delay = import_TimeUnit.Instant) {
|
|
40
|
-
this.#concurrency = concurrency;
|
|
41
|
-
this.#delay = delay;
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* Add a promise to the queue. It returns a promise that can be awaited.
|
|
45
|
-
*/
|
|
46
|
-
add(executor) {
|
|
47
|
-
const { promise, resolver, rejecter } = (0, import_Promises.createPromise)();
|
|
48
|
-
logger.debug("Add promise to queue on place", this.#queue.length + 1);
|
|
49
|
-
this.#queue.push({
|
|
50
|
-
func: () => executor().then(resolver).catch(rejecter),
|
|
51
|
-
rejecter
|
|
52
|
-
});
|
|
53
|
-
this.#run();
|
|
54
|
-
return promise;
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* Run the next promise in the queue.
|
|
58
|
-
*/
|
|
59
|
-
#run() {
|
|
60
|
-
if (this.#runningCount >= this.#concurrency || this.#queue.length === 0) {
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
logger.debug(
|
|
64
|
-
"Processing promise from queue ... Current queue length:",
|
|
65
|
-
this.#queue.length,
|
|
66
|
-
"Already running:",
|
|
67
|
-
this.#runningCount
|
|
68
|
-
);
|
|
69
|
-
const { func } = this.#queue.shift() ?? {};
|
|
70
|
-
if (func !== void 0) {
|
|
71
|
-
this.#runningCount++;
|
|
72
|
-
func().catch((error) => {
|
|
73
|
-
throw error;
|
|
74
|
-
}).finally(() => {
|
|
75
|
-
logger.debug("Promise processed ... Still running:", this.#runningCount - 1);
|
|
76
|
-
if (this.#delay > 0) {
|
|
77
|
-
import_Time.Time.sleep("Queue delay", this.#delay).then(() => this.#runNext()).catch((error) => {
|
|
78
|
-
throw error;
|
|
79
|
-
});
|
|
80
|
-
} else {
|
|
81
|
-
this.#runNext();
|
|
82
|
-
}
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
#runNext() {
|
|
87
|
-
this.#runningCount--;
|
|
88
|
-
this.#run();
|
|
89
|
-
}
|
|
90
|
-
/**
|
|
91
|
-
* Clear the queue.
|
|
92
|
-
*/
|
|
93
|
-
clear(reject) {
|
|
94
|
-
if (reject) {
|
|
95
|
-
for (const { rejecter } of this.#queue) {
|
|
96
|
-
rejecter();
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
this.#queue.length = 0;
|
|
100
|
-
}
|
|
101
|
-
/** Get the number of promises in the queue. */
|
|
102
|
-
get count() {
|
|
103
|
-
return this.#queue.length;
|
|
104
|
-
}
|
|
105
|
-
/**
|
|
106
|
-
* Close the queue and remove all outstanding promises (but do not reject them).
|
|
107
|
-
*/
|
|
108
|
-
close() {
|
|
109
|
-
this.clear(false);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
//# sourceMappingURL=PromiseQueue.js.map
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/util/PromiseQueue.ts"],
|
|
4
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAOA,kBAAqB;AACrB,sBAAwB;AACxB,oBAAuB;AACvB,sBAA8B;AAV9B;AAAA;AAAA;AAAA;AAAA;AAYA,MAAM,SAAS,qBAAO,IAAI,cAAc;AAKjC,MAAM,aAAa;AAAA,EACb;AAAA,EACA,SAAS,IAAI,MAAsE;AAAA,EACnF;AAAA,EACT,gBAAgB;AAAA,EAEhB,YAAY,cAAc,GAAG,QAAQ,yBAAS;AAC1C,SAAK,eAAe;AACpB,SAAK,SAAS;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA,EAKA,IAAO,UAAwC;AAC3C,UAAM,EAAE,SAAS,UAAU,SAAS,QAAI,+BAAiB;AAEzD,WAAO,MAAM,iCAAiC,KAAK,OAAO,SAAS,CAAC;AACpE,SAAK,OAAO,KAAK;AAAA,MACb,MAAM,MAAM,SAAS,EAAE,KAAK,QAAQ,EAAE,MAAM,QAAQ;AAAA,MACpD;AAAA,IACJ,CAAC;AACD,SAAK,KAAK;AACV,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA,EAKA,OAAa;AACT,QAAI,KAAK,iBAAiB,KAAK,gBAAgB,KAAK,OAAO,WAAW,GAAG;AACrE;AAAA,IACJ;AAEA,WAAO;AAAA,MACH;AAAA,MACA,KAAK,OAAO;AAAA,MACZ;AAAA,MACA,KAAK;AAAA,IACT;AACA,UAAM,EAAE,KAAK,IAAI,KAAK,OAAO,MAAM,KAAK,CAAC;AACzC,QAAI,SAAS,QAAW;AACpB,WAAK;AACL,WAAK,EACA,MAAM,WAAS;AACZ,cAAM;AAAA,MACV,CAAC,EACA,QAAQ,MAAM;AACX,eAAO,MAAM,wCAAwC,KAAK,gBAAgB,CAAC;AAC3E,YAAI,KAAK,SAAS,GAAG;AAEjB,2BAAK,MAAM,eAAe,KAAK,MAAM,EAChC,KAAK,MAAM,KAAK,SAAS,CAAC,EAC1B,MAAM,WAAS;AACZ,kBAAM;AAAA,UACV,CAAC;AAAA,QACT,OAAO;AACH,eAAK,SAAS;AAAA,QAClB;AAAA,MACJ,CAAC;AAAA,IACT;AAAA,EACJ;AAAA,EAEA,WAAiB;AACb,SAAK;AACL,SAAK,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAuB;AACzB,QAAI,QAAQ;AACR,iBAAW,EAAE,SAAS,KAAK,KAAK,QAAQ;AACpC,iBAAS;AAAA,MACb;AAAA,IACJ;AACA,SAAK,OAAO,SAAS;AAAA,EACzB;AAAA;AAAA,EAGA,IAAI,QAAQ;AACR,WAAO,KAAK,OAAO;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA,EAKA,QAAc;AACV,SAAK,MAAM,KAAK;AAAA,EACpB;AACJ;",
|
|
5
|
-
"names": []
|
|
6
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license
|
|
3
|
-
* Copyright 2022-2025 Matter.js Authors
|
|
4
|
-
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
-
*/
|
|
6
|
-
import { Duration } from "#time/Duration.js";
|
|
7
|
-
/**
|
|
8
|
-
* A queue that processes promises with a given concurrency and delays after each promise if desired.
|
|
9
|
-
*/
|
|
10
|
-
export declare class PromiseQueue {
|
|
11
|
-
#private;
|
|
12
|
-
constructor(concurrency?: number, delay?: Duration);
|
|
13
|
-
/**
|
|
14
|
-
* Add a promise to the queue. It returns a promise that can be awaited.
|
|
15
|
-
*/
|
|
16
|
-
add<T>(executor: () => Promise<T>): Promise<T>;
|
|
17
|
-
/**
|
|
18
|
-
* Clear the queue.
|
|
19
|
-
*/
|
|
20
|
-
clear(reject: boolean): void;
|
|
21
|
-
/** Get the number of promises in the queue. */
|
|
22
|
-
get count(): number;
|
|
23
|
-
/**
|
|
24
|
-
* Close the queue and remove all outstanding promises (but do not reject them).
|
|
25
|
-
*/
|
|
26
|
-
close(): void;
|
|
27
|
-
}
|
|
28
|
-
//# sourceMappingURL=PromiseQueue.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"PromiseQueue.d.ts","sourceRoot":"","sources":["../../../src/util/PromiseQueue.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAQ7C;;GAEG;AACH,qBAAa,YAAY;;gBAMT,WAAW,SAAI,EAAE,KAAK,WAAU;IAK5C;;OAEG;IACH,GAAG,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAsD9C;;OAEG;IACH,KAAK,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI;IAS5B,+CAA+C;IAC/C,IAAI,KAAK,WAER;IAED;;OAEG;IACH,KAAK,IAAI,IAAI;CAGhB"}
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license
|
|
3
|
-
* Copyright 2022-2025 Matter.js Authors
|
|
4
|
-
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
-
*/
|
|
6
|
-
import { Time } from "#time/Time.js";
|
|
7
|
-
import { Instant } from "#time/TimeUnit.js";
|
|
8
|
-
import { Logger } from "../log/Logger.js";
|
|
9
|
-
import { createPromise } from "./Promises.js";
|
|
10
|
-
const logger = Logger.get("PromiseQueue");
|
|
11
|
-
class PromiseQueue {
|
|
12
|
-
#delay;
|
|
13
|
-
#queue = new Array();
|
|
14
|
-
#concurrency;
|
|
15
|
-
#runningCount = 0;
|
|
16
|
-
constructor(concurrency = 1, delay = Instant) {
|
|
17
|
-
this.#concurrency = concurrency;
|
|
18
|
-
this.#delay = delay;
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Add a promise to the queue. It returns a promise that can be awaited.
|
|
22
|
-
*/
|
|
23
|
-
add(executor) {
|
|
24
|
-
const { promise, resolver, rejecter } = createPromise();
|
|
25
|
-
logger.debug("Add promise to queue on place", this.#queue.length + 1);
|
|
26
|
-
this.#queue.push({
|
|
27
|
-
func: () => executor().then(resolver).catch(rejecter),
|
|
28
|
-
rejecter
|
|
29
|
-
});
|
|
30
|
-
this.#run();
|
|
31
|
-
return promise;
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Run the next promise in the queue.
|
|
35
|
-
*/
|
|
36
|
-
#run() {
|
|
37
|
-
if (this.#runningCount >= this.#concurrency || this.#queue.length === 0) {
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
logger.debug(
|
|
41
|
-
"Processing promise from queue ... Current queue length:",
|
|
42
|
-
this.#queue.length,
|
|
43
|
-
"Already running:",
|
|
44
|
-
this.#runningCount
|
|
45
|
-
);
|
|
46
|
-
const { func } = this.#queue.shift() ?? {};
|
|
47
|
-
if (func !== void 0) {
|
|
48
|
-
this.#runningCount++;
|
|
49
|
-
func().catch((error) => {
|
|
50
|
-
throw error;
|
|
51
|
-
}).finally(() => {
|
|
52
|
-
logger.debug("Promise processed ... Still running:", this.#runningCount - 1);
|
|
53
|
-
if (this.#delay > 0) {
|
|
54
|
-
Time.sleep("Queue delay", this.#delay).then(() => this.#runNext()).catch((error) => {
|
|
55
|
-
throw error;
|
|
56
|
-
});
|
|
57
|
-
} else {
|
|
58
|
-
this.#runNext();
|
|
59
|
-
}
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
#runNext() {
|
|
64
|
-
this.#runningCount--;
|
|
65
|
-
this.#run();
|
|
66
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* Clear the queue.
|
|
69
|
-
*/
|
|
70
|
-
clear(reject) {
|
|
71
|
-
if (reject) {
|
|
72
|
-
for (const { rejecter } of this.#queue) {
|
|
73
|
-
rejecter();
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
this.#queue.length = 0;
|
|
77
|
-
}
|
|
78
|
-
/** Get the number of promises in the queue. */
|
|
79
|
-
get count() {
|
|
80
|
-
return this.#queue.length;
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* Close the queue and remove all outstanding promises (but do not reject them).
|
|
84
|
-
*/
|
|
85
|
-
close() {
|
|
86
|
-
this.clear(false);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
export {
|
|
90
|
-
PromiseQueue
|
|
91
|
-
};
|
|
92
|
-
//# sourceMappingURL=PromiseQueue.js.map
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/util/PromiseQueue.ts"],
|
|
4
|
-
"mappings": "AAAA;AAAA;AAAA;AAAA;AAAA;AAOA,SAAS,YAAY;AACrB,SAAS,eAAe;AACxB,SAAS,cAAc;AACvB,SAAS,qBAAqB;AAE9B,MAAM,SAAS,OAAO,IAAI,cAAc;AAKjC,MAAM,aAAa;AAAA,EACb;AAAA,EACA,SAAS,IAAI,MAAsE;AAAA,EACnF;AAAA,EACT,gBAAgB;AAAA,EAEhB,YAAY,cAAc,GAAG,QAAQ,SAAS;AAC1C,SAAK,eAAe;AACpB,SAAK,SAAS;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA,EAKA,IAAO,UAAwC;AAC3C,UAAM,EAAE,SAAS,UAAU,SAAS,IAAI,cAAiB;AAEzD,WAAO,MAAM,iCAAiC,KAAK,OAAO,SAAS,CAAC;AACpE,SAAK,OAAO,KAAK;AAAA,MACb,MAAM,MAAM,SAAS,EAAE,KAAK,QAAQ,EAAE,MAAM,QAAQ;AAAA,MACpD;AAAA,IACJ,CAAC;AACD,SAAK,KAAK;AACV,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA,EAKA,OAAa;AACT,QAAI,KAAK,iBAAiB,KAAK,gBAAgB,KAAK,OAAO,WAAW,GAAG;AACrE;AAAA,IACJ;AAEA,WAAO;AAAA,MACH;AAAA,MACA,KAAK,OAAO;AAAA,MACZ;AAAA,MACA,KAAK;AAAA,IACT;AACA,UAAM,EAAE,KAAK,IAAI,KAAK,OAAO,MAAM,KAAK,CAAC;AACzC,QAAI,SAAS,QAAW;AACpB,WAAK;AACL,WAAK,EACA,MAAM,WAAS;AACZ,cAAM;AAAA,MACV,CAAC,EACA,QAAQ,MAAM;AACX,eAAO,MAAM,wCAAwC,KAAK,gBAAgB,CAAC;AAC3E,YAAI,KAAK,SAAS,GAAG;AAEjB,eAAK,MAAM,eAAe,KAAK,MAAM,EAChC,KAAK,MAAM,KAAK,SAAS,CAAC,EAC1B,MAAM,WAAS;AACZ,kBAAM;AAAA,UACV,CAAC;AAAA,QACT,OAAO;AACH,eAAK,SAAS;AAAA,QAClB;AAAA,MACJ,CAAC;AAAA,IACT;AAAA,EACJ;AAAA,EAEA,WAAiB;AACb,SAAK;AACL,SAAK,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAuB;AACzB,QAAI,QAAQ;AACR,iBAAW,EAAE,SAAS,KAAK,KAAK,QAAQ;AACpC,iBAAS;AAAA,MACb;AAAA,IACJ;AACA,SAAK,OAAO,SAAS;AAAA,EACzB;AAAA;AAAA,EAGA,IAAI,QAAQ;AACR,WAAO,KAAK,OAAO;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA,EAKA,QAAc;AACV,SAAK,MAAM,KAAK;AAAA,EACpB;AACJ;",
|
|
5
|
-
"names": []
|
|
6
|
-
}
|
package/src/util/PromiseQueue.ts
DELETED
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license
|
|
3
|
-
* Copyright 2022-2025 Matter.js Authors
|
|
4
|
-
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { Duration } from "#time/Duration.js";
|
|
8
|
-
import { Time } from "#time/Time.js";
|
|
9
|
-
import { Instant } from "#time/TimeUnit.js";
|
|
10
|
-
import { Logger } from "../log/Logger.js";
|
|
11
|
-
import { createPromise } from "./Promises.js";
|
|
12
|
-
|
|
13
|
-
const logger = Logger.get("PromiseQueue");
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* A queue that processes promises with a given concurrency and delays after each promise if desired.
|
|
17
|
-
*/
|
|
18
|
-
export class PromiseQueue {
|
|
19
|
-
readonly #delay: Duration;
|
|
20
|
-
readonly #queue = new Array<{ func: () => Promise<any>; rejecter: (reason?: any) => void }>();
|
|
21
|
-
readonly #concurrency: number;
|
|
22
|
-
#runningCount = 0;
|
|
23
|
-
|
|
24
|
-
constructor(concurrency = 1, delay = Instant) {
|
|
25
|
-
this.#concurrency = concurrency;
|
|
26
|
-
this.#delay = delay;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Add a promise to the queue. It returns a promise that can be awaited.
|
|
31
|
-
*/
|
|
32
|
-
add<T>(executor: () => Promise<T>): Promise<T> {
|
|
33
|
-
const { promise, resolver, rejecter } = createPromise<T>();
|
|
34
|
-
|
|
35
|
-
logger.debug("Add promise to queue on place", this.#queue.length + 1);
|
|
36
|
-
this.#queue.push({
|
|
37
|
-
func: () => executor().then(resolver).catch(rejecter),
|
|
38
|
-
rejecter,
|
|
39
|
-
});
|
|
40
|
-
this.#run();
|
|
41
|
-
return promise;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Run the next promise in the queue.
|
|
46
|
-
*/
|
|
47
|
-
#run(): void {
|
|
48
|
-
if (this.#runningCount >= this.#concurrency || this.#queue.length === 0) {
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
logger.debug(
|
|
53
|
-
"Processing promise from queue ... Current queue length:",
|
|
54
|
-
this.#queue.length,
|
|
55
|
-
"Already running:",
|
|
56
|
-
this.#runningCount,
|
|
57
|
-
);
|
|
58
|
-
const { func } = this.#queue.shift() ?? {};
|
|
59
|
-
if (func !== undefined) {
|
|
60
|
-
this.#runningCount++;
|
|
61
|
-
func()
|
|
62
|
-
.catch(error => {
|
|
63
|
-
throw error;
|
|
64
|
-
}) // already catched internally, but rethrow if it happens to not hide it
|
|
65
|
-
.finally(() => {
|
|
66
|
-
logger.debug("Promise processed ... Still running:", this.#runningCount - 1);
|
|
67
|
-
if (this.#delay > 0) {
|
|
68
|
-
// Keep the queue blocked for the delay time
|
|
69
|
-
Time.sleep("Queue delay", this.#delay)
|
|
70
|
-
.then(() => this.#runNext())
|
|
71
|
-
.catch(error => {
|
|
72
|
-
throw error;
|
|
73
|
-
}); // rethrow to not hide errors - but none should happen
|
|
74
|
-
} else {
|
|
75
|
-
this.#runNext();
|
|
76
|
-
}
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
#runNext(): void {
|
|
82
|
-
this.#runningCount--;
|
|
83
|
-
this.#run();
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Clear the queue.
|
|
88
|
-
*/
|
|
89
|
-
clear(reject: boolean): void {
|
|
90
|
-
if (reject) {
|
|
91
|
-
for (const { rejecter } of this.#queue) {
|
|
92
|
-
rejecter();
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
this.#queue.length = 0;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/** Get the number of promises in the queue. */
|
|
99
|
-
get count() {
|
|
100
|
-
return this.#queue.length;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Close the queue and remove all outstanding promises (but do not reject them).
|
|
105
|
-
*/
|
|
106
|
-
close(): void {
|
|
107
|
-
this.clear(false);
|
|
108
|
-
}
|
|
109
|
-
}
|