bytekit 2.2.0 → 2.2.3

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.
Files changed (44) hide show
  1. package/dist/cli/ddd-boilerplate.d.ts.map +1 -1
  2. package/dist/cli/ddd-boilerplate.js +4 -1
  3. package/dist/cli/ddd-boilerplate.js.map +1 -1
  4. package/dist/cli/index.d.ts.map +1 -1
  5. package/dist/cli/index.js +4 -1
  6. package/dist/cli/index.js.map +1 -1
  7. package/dist/utils/async/index.d.ts +8 -0
  8. package/dist/utils/async/index.d.ts.map +1 -1
  9. package/dist/utils/async/index.js +4 -0
  10. package/dist/utils/async/index.js.map +1 -1
  11. package/dist/utils/async/pipeline.d.ts +164 -0
  12. package/dist/utils/async/pipeline.d.ts.map +1 -0
  13. package/dist/utils/async/pipeline.js +154 -0
  14. package/dist/utils/async/pipeline.js.map +1 -0
  15. package/dist/utils/async/promise-pool.d.ts +59 -0
  16. package/dist/utils/async/promise-pool.d.ts.map +1 -0
  17. package/dist/utils/async/promise-pool.js +130 -0
  18. package/dist/utils/async/promise-pool.js.map +1 -0
  19. package/dist/utils/async/request-batcher.d.ts +82 -0
  20. package/dist/utils/async/request-batcher.d.ts.map +1 -0
  21. package/dist/utils/async/request-batcher.js +178 -0
  22. package/dist/utils/async/request-batcher.js.map +1 -0
  23. package/dist/utils/async/request-queue.d.ts +115 -0
  24. package/dist/utils/async/request-queue.d.ts.map +1 -0
  25. package/dist/utils/async/request-queue.js +236 -0
  26. package/dist/utils/async/request-queue.js.map +1 -0
  27. package/dist/utils/core/ApiClient.d.ts +22 -1
  28. package/dist/utils/core/ApiClient.d.ts.map +1 -1
  29. package/dist/utils/core/ApiClient.js +52 -3
  30. package/dist/utils/core/ApiClient.js.map +1 -1
  31. package/dist/utils/helpers/FileUploadHelper.d.ts +68 -1
  32. package/dist/utils/helpers/FileUploadHelper.d.ts.map +1 -1
  33. package/dist/utils/helpers/FileUploadHelper.js +72 -19
  34. package/dist/utils/helpers/FileUploadHelper.js.map +1 -1
  35. package/dist/utils/helpers/StreamingHelper.d.ts.map +1 -1
  36. package/dist/utils/helpers/StreamingHelper.js +9 -5
  37. package/dist/utils/helpers/StreamingHelper.js.map +1 -1
  38. package/dist/utils/helpers/UrlHelper.d.ts.map +1 -1
  39. package/dist/utils/helpers/UrlHelper.js.map +1 -1
  40. package/dist/utils/helpers/WebSocketHelper.d.ts +30 -0
  41. package/dist/utils/helpers/WebSocketHelper.d.ts.map +1 -1
  42. package/dist/utils/helpers/WebSocketHelper.js +111 -3
  43. package/dist/utils/helpers/WebSocketHelper.js.map +1 -1
  44. package/package.json +5 -1
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Error thrown when a task exceeds the configured per-task timeout.
3
+ */
4
+ export class PoolTimeoutError extends Error {
5
+ constructor(timeoutMs) {
6
+ super(`Task timed out after ${timeoutMs}ms`);
7
+ this.name = "PoolTimeoutError";
8
+ }
9
+ }
10
+ /**
11
+ * Executes an array of async tasks with a configurable concurrency limit.
12
+ *
13
+ * Unlike `parallel()`, PromisePool:
14
+ * - Is stateful and reusable across multiple `run()` calls.
15
+ * - Does NOT fail fast: individual task errors are isolated via `onError`.
16
+ * - Supports per-task timeouts.
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * const pool = new PromisePool({ concurrency: 3, timeout: 5000 });
21
+ * const results = await pool.run([
22
+ * () => fetch("/api/1").then(r => r.json()),
23
+ * () => fetch("/api/2").then(r => r.json()),
24
+ * ]);
25
+ * ```
26
+ */
27
+ export class PromisePool {
28
+ constructor(options) {
29
+ Object.defineProperty(this, "options", {
30
+ enumerable: true,
31
+ configurable: true,
32
+ writable: true,
33
+ value: void 0
34
+ });
35
+ Object.defineProperty(this, "running", {
36
+ enumerable: true,
37
+ configurable: true,
38
+ writable: true,
39
+ value: 0
40
+ });
41
+ Object.defineProperty(this, "queue", {
42
+ enumerable: true,
43
+ configurable: true,
44
+ writable: true,
45
+ value: []
46
+ });
47
+ if (options.concurrency < 1) {
48
+ throw new TypeError("concurrency must be at least 1");
49
+ }
50
+ if (options.timeout !== undefined && options.timeout <= 0) {
51
+ throw new TypeError("timeout must be a positive number");
52
+ }
53
+ this.options = { ...options };
54
+ }
55
+ /**
56
+ * Runs an array of task factory functions with concurrency control.
57
+ * Tasks are lazy — they are not started until the pool has a free slot.
58
+ *
59
+ * @param tasks Array of functions that return Promises.
60
+ * @returns Promise resolving to an array of results in original order.
61
+ * @throws TypeError If `tasks` is not an array or any element is not a function.
62
+ */
63
+ async run(tasks) {
64
+ if (!Array.isArray(tasks)) {
65
+ throw new TypeError("tasks must be an array");
66
+ }
67
+ if (tasks.length === 0) {
68
+ return [];
69
+ }
70
+ for (let i = 0; i < tasks.length; i++) {
71
+ if (typeof tasks[i] !== "function") {
72
+ throw new TypeError(`Task at index ${i} is not a function`);
73
+ }
74
+ }
75
+ const results = new Array(tasks.length);
76
+ await Promise.all(tasks.map((task, index) => this.addTask(task, index, results)));
77
+ return results;
78
+ }
79
+ addTask(task, index, results) {
80
+ return new Promise((resolve, reject) => {
81
+ this.queue.push({
82
+ task: task,
83
+ resolve: resolve,
84
+ reject,
85
+ index,
86
+ });
87
+ this.processQueue(results);
88
+ });
89
+ }
90
+ processQueue(results) {
91
+ while (this.running < this.options.concurrency &&
92
+ this.queue.length > 0) {
93
+ const item = this.queue.shift();
94
+ this.running++;
95
+ this.executeTask(item, results);
96
+ }
97
+ }
98
+ async executeTask(item, results) {
99
+ try {
100
+ const promise = item.task();
101
+ const result = this.options.timeout
102
+ ? await this.withTimeout(promise, this.options.timeout)
103
+ : await promise;
104
+ results[item.index] = result;
105
+ item.resolve(result);
106
+ }
107
+ catch (error) {
108
+ if (this.options.onError) {
109
+ this.options.onError(error, item.index);
110
+ }
111
+ item.reject(error);
112
+ }
113
+ finally {
114
+ this.running--;
115
+ this.processQueue(results);
116
+ }
117
+ }
118
+ withTimeout(promise, timeoutMs) {
119
+ return new Promise((resolve, reject) => {
120
+ const timer = setTimeout(() => {
121
+ reject(new PoolTimeoutError(timeoutMs));
122
+ }, timeoutMs);
123
+ promise
124
+ .then(resolve)
125
+ .catch(reject)
126
+ .finally(() => clearTimeout(timer));
127
+ });
128
+ }
129
+ }
130
+ //# sourceMappingURL=promise-pool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"promise-pool.js","sourceRoot":"","sources":["../../../src/utils/async/promise-pool.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * RequestBatcher — Time-window deduplication batcher for HTTP requests.
3
+ * Feature: 004-batching-system
4
+ * Zero external dependencies: Map, setTimeout/clearTimeout (built-in).
5
+ */
6
+ /** Constructor options for {@link RequestBatcher}. */
7
+ export interface BatchOptions {
8
+ /**
9
+ * Time window in milliseconds for collecting requests before dispatching.
10
+ * Must be greater than 0.
11
+ */
12
+ windowMs: number;
13
+ /**
14
+ * Maximum requests per batch. Flushes early when reached.
15
+ * Default: `Infinity`.
16
+ */
17
+ maxSize?: number;
18
+ /**
19
+ * If `true`, the window timer resets on each new request (sliding window).
20
+ * Default: `false` — fixed window: timer fires once after the first request.
21
+ */
22
+ sliding?: boolean;
23
+ /**
24
+ * Custom function to compute the batch deduplication key.
25
+ * Requests with the same key within a window are coalesced.
26
+ * Default: `"METHOD:url:serialized(body)"`.
27
+ */
28
+ keyFn?: (url: string, init: RequestInit) => string;
29
+ }
30
+ /**
31
+ * Coalesces same-key HTTP requests within a time window into a single fetch invocation.
32
+ * All callers sharing the same key receive the same resolved value.
33
+ *
34
+ * The deduplication key defaults to `"METHOD:url:serialized(body)"` and can be
35
+ * overridden via `BatchOptions.keyFn`.
36
+ *
37
+ * @example
38
+ * ```typescript
39
+ * const batcher = new RequestBatcher({ windowMs: 50 });
40
+ *
41
+ * // These three calls share the same key → fetcher invoked once
42
+ * const [a, b, c] = await Promise.all([
43
+ * batcher.add("/api/users", { method: "GET" }, fetch),
44
+ * batcher.add("/api/users", { method: "GET" }, fetch),
45
+ * batcher.add("/api/users", { method: "GET" }, fetch),
46
+ * ]);
47
+ * ```
48
+ */
49
+ export declare class RequestBatcher {
50
+ private readonly _windowMs;
51
+ private readonly _maxSize;
52
+ private readonly _sliding;
53
+ private readonly _keyFn;
54
+ private readonly _buckets;
55
+ private readonly _timers;
56
+ /**
57
+ * Creates a new RequestBatcher.
58
+ * @throws {TypeError} if `windowMs` is not greater than 0.
59
+ * @throws {TypeError} if `maxSize` is less than 1.
60
+ */
61
+ constructor(options: BatchOptions);
62
+ /**
63
+ * Adds a request to the current batch window.
64
+ * Requests with the same key (method + url + body) are coalesced —
65
+ * all callers receive the same resolved value when the window fires.
66
+ *
67
+ * @param url The request URL.
68
+ * @param init The RequestInit options (method, headers, body).
69
+ * @param fetcher The actual fetch function to invoke when the window fires.
70
+ * @returns `Promise<T>` resolving to the fetch result.
71
+ */
72
+ add<T>(url: string, init: RequestInit, fetcher: (url: string, init: RequestInit) => Promise<T>): Promise<T>;
73
+ private _dispatch;
74
+ /**
75
+ * Forces immediate dispatch of all pending batches.
76
+ * Resolves when all dispatched fetchers have settled.
77
+ */
78
+ flush(): Promise<void>;
79
+ /** Total pending requests across all batch buckets. */
80
+ get pendingCount(): number;
81
+ }
82
+ //# sourceMappingURL=request-batcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-batcher.d.ts","sourceRoot":"","sources":["../../../src/utils/async/request-batcher.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,sDAAsD;AACtD,MAAM,WAAW,YAAY;IACzB;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;OAIG;IACH,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,KAAK,MAAM,CAAC;CACtD;AAmCD;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,cAAc;IACvB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAU;IACnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA6C;IACpE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmC;IAC5D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoD;IAE5E;;;;OAIG;gBACS,OAAO,EAAE,YAAY;IAajC;;;;;;;;;OASG;IACH,GAAG,CAAC,CAAC,EACD,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,WAAW,EACjB,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,GACxD,OAAO,CAAC,CAAC,CAAC;YAgCC,SAAS;IA2BvB;;;OAGG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAK5B,uDAAuD;IACvD,IAAI,YAAY,IAAI,MAAM,CAMzB;CACJ"}
@@ -0,0 +1,178 @@
1
+ /**
2
+ * RequestBatcher — Time-window deduplication batcher for HTTP requests.
3
+ * Feature: 004-batching-system
4
+ * Zero external dependencies: Map, setTimeout/clearTimeout (built-in).
5
+ */
6
+ function stableSerialize(body) {
7
+ if (body === undefined || body === null)
8
+ return "";
9
+ if (typeof body === "string" ||
10
+ typeof body === "number" ||
11
+ typeof body === "boolean") {
12
+ return String(body);
13
+ }
14
+ try {
15
+ return JSON.stringify(body);
16
+ }
17
+ catch {
18
+ return String(body);
19
+ }
20
+ }
21
+ function defaultKeyFn(url, init) {
22
+ const method = (init.method ?? "GET").toUpperCase();
23
+ const body = stableSerialize(init.body);
24
+ return `${method}:${url}:${body}`;
25
+ }
26
+ /**
27
+ * Coalesces same-key HTTP requests within a time window into a single fetch invocation.
28
+ * All callers sharing the same key receive the same resolved value.
29
+ *
30
+ * The deduplication key defaults to `"METHOD:url:serialized(body)"` and can be
31
+ * overridden via `BatchOptions.keyFn`.
32
+ *
33
+ * @example
34
+ * ```typescript
35
+ * const batcher = new RequestBatcher({ windowMs: 50 });
36
+ *
37
+ * // These three calls share the same key → fetcher invoked once
38
+ * const [a, b, c] = await Promise.all([
39
+ * batcher.add("/api/users", { method: "GET" }, fetch),
40
+ * batcher.add("/api/users", { method: "GET" }, fetch),
41
+ * batcher.add("/api/users", { method: "GET" }, fetch),
42
+ * ]);
43
+ * ```
44
+ */
45
+ export class RequestBatcher {
46
+ /**
47
+ * Creates a new RequestBatcher.
48
+ * @throws {TypeError} if `windowMs` is not greater than 0.
49
+ * @throws {TypeError} if `maxSize` is less than 1.
50
+ */
51
+ constructor(options) {
52
+ Object.defineProperty(this, "_windowMs", {
53
+ enumerable: true,
54
+ configurable: true,
55
+ writable: true,
56
+ value: void 0
57
+ });
58
+ Object.defineProperty(this, "_maxSize", {
59
+ enumerable: true,
60
+ configurable: true,
61
+ writable: true,
62
+ value: void 0
63
+ });
64
+ Object.defineProperty(this, "_sliding", {
65
+ enumerable: true,
66
+ configurable: true,
67
+ writable: true,
68
+ value: void 0
69
+ });
70
+ Object.defineProperty(this, "_keyFn", {
71
+ enumerable: true,
72
+ configurable: true,
73
+ writable: true,
74
+ value: void 0
75
+ });
76
+ Object.defineProperty(this, "_buckets", {
77
+ enumerable: true,
78
+ configurable: true,
79
+ writable: true,
80
+ value: new Map()
81
+ });
82
+ Object.defineProperty(this, "_timers", {
83
+ enumerable: true,
84
+ configurable: true,
85
+ writable: true,
86
+ value: new Map()
87
+ });
88
+ if (options.windowMs <= 0) {
89
+ throw new TypeError("windowMs must be > 0");
90
+ }
91
+ if (options.maxSize !== undefined && options.maxSize < 1) {
92
+ throw new TypeError("maxSize must be >= 1");
93
+ }
94
+ this._windowMs = options.windowMs;
95
+ this._maxSize = options.maxSize ?? Infinity;
96
+ this._sliding = options.sliding ?? false;
97
+ this._keyFn = options.keyFn ?? defaultKeyFn;
98
+ }
99
+ /**
100
+ * Adds a request to the current batch window.
101
+ * Requests with the same key (method + url + body) are coalesced —
102
+ * all callers receive the same resolved value when the window fires.
103
+ *
104
+ * @param url The request URL.
105
+ * @param init The RequestInit options (method, headers, body).
106
+ * @param fetcher The actual fetch function to invoke when the window fires.
107
+ * @returns `Promise<T>` resolving to the fetch result.
108
+ */
109
+ add(url, init, fetcher) {
110
+ const key = this._keyFn(url, init);
111
+ return new Promise((resolve, reject) => {
112
+ const entry = { fetcher, url, init, resolve, reject };
113
+ if (!this._buckets.has(key)) {
114
+ this._buckets.set(key, []);
115
+ }
116
+ this._buckets.get(key).push(entry);
117
+ // Sliding window: reset timer on each new request
118
+ if (this._sliding && this._timers.has(key)) {
119
+ clearTimeout(this._timers.get(key));
120
+ this._timers.delete(key);
121
+ }
122
+ // Start timer if not already running (or just cleared for sliding)
123
+ if (!this._timers.has(key)) {
124
+ const timer = setTimeout(() => {
125
+ void this._dispatch(key);
126
+ }, this._windowMs);
127
+ this._timers.set(key, timer);
128
+ }
129
+ // Early flush if maxSize reached
130
+ if (this._buckets.get(key).length >= this._maxSize) {
131
+ void this._dispatch(key);
132
+ }
133
+ });
134
+ }
135
+ async _dispatch(key) {
136
+ // Cancel the timer for this key
137
+ const timer = this._timers.get(key);
138
+ if (timer !== undefined) {
139
+ clearTimeout(timer);
140
+ this._timers.delete(key);
141
+ }
142
+ // Splice the bucket — guard against double dispatch
143
+ const entries = this._buckets.get(key);
144
+ if (!entries || entries.length === 0)
145
+ return;
146
+ this._buckets.delete(key);
147
+ // Call the first entry's fetcher once; share result with all callers
148
+ const first = entries[0];
149
+ try {
150
+ const result = await first.fetcher(first.url, first.init);
151
+ for (const entry of entries) {
152
+ entry.resolve(result);
153
+ }
154
+ }
155
+ catch (err) {
156
+ for (const entry of entries) {
157
+ entry.reject(err);
158
+ }
159
+ }
160
+ }
161
+ /**
162
+ * Forces immediate dispatch of all pending batches.
163
+ * Resolves when all dispatched fetchers have settled.
164
+ */
165
+ async flush() {
166
+ const keys = [...this._buckets.keys()];
167
+ await Promise.allSettled(keys.map((key) => this._dispatch(key)));
168
+ }
169
+ /** Total pending requests across all batch buckets. */
170
+ get pendingCount() {
171
+ let count = 0;
172
+ for (const bucket of this._buckets.values()) {
173
+ count += bucket.length;
174
+ }
175
+ return count;
176
+ }
177
+ }
178
+ //# sourceMappingURL=request-batcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-batcher.js","sourceRoot":"","sources":["../../../src/utils/async/request-batcher.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,115 @@
1
+ /**
2
+ * RequestQueue — Priority-aware, concurrency-limited task queue with AbortSignal cancellation.
3
+ * Feature: 004-batching-system
4
+ * Zero external dependencies: AbortController, Map (built-in).
5
+ */
6
+ /** The three fixed priority lanes for task execution ordering. */
7
+ export type QueuePriority = "high" | "normal" | "low";
8
+ /** Constructor options for {@link RequestQueue}. */
9
+ export interface RequestQueueOptions {
10
+ /**
11
+ * Max tasks running simultaneously. Minimum: 1.
12
+ */
13
+ concurrency: number;
14
+ /**
15
+ * Called when a task rejects. Queue continues executing remaining tasks.
16
+ * @param error The rejection reason (cast to Error).
17
+ * @param id The task's unique identifier.
18
+ */
19
+ onError?: (error: Error, id: string) => void;
20
+ }
21
+ /** Per-task options for {@link RequestQueue.add}. */
22
+ export interface AddOptions {
23
+ /**
24
+ * Lane priority. Defaults to `"normal"`.
25
+ * Tasks in `high` always dequeue before `normal`; `normal` before `low`.
26
+ */
27
+ priority?: QueuePriority;
28
+ /**
29
+ * External AbortSignal. If it fires before the task starts,
30
+ * the task is cancelled immediately with {@link QueueAbortError}.
31
+ * This is the **public consumer cancellation path**.
32
+ */
33
+ signal?: AbortSignal;
34
+ }
35
+ /**
36
+ * Thrown when a queued task is cancelled — either via an external AbortSignal
37
+ * or via the internal `cancel(id)` mechanism.
38
+ */
39
+ export declare class QueueAbortError extends Error {
40
+ constructor(message?: string);
41
+ }
42
+ /**
43
+ * A priority-aware, concurrency-limited task queue with AbortSignal-based cancellation.
44
+ *
45
+ * Tasks are organised into three lanes (`high`, `normal`, `low`). At any moment,
46
+ * at most `concurrency` tasks execute simultaneously. When a slot opens, the
47
+ * highest-priority queued task is dequeued and started.
48
+ *
49
+ * **Consumer cancellation** is done via `AddOptions.signal`.
50
+ * The `cancel(id)` method is an internal queue-management mechanism.
51
+ *
52
+ * @example
53
+ * ```typescript
54
+ * const queue = new RequestQueue({ concurrency: 3 });
55
+ *
56
+ * const result = await queue.add(
57
+ * (signal) => fetch("/api/data", { signal }),
58
+ * { priority: "high" }
59
+ * );
60
+ * ```
61
+ */
62
+ export declare class RequestQueue {
63
+ private readonly _concurrency;
64
+ private readonly _onError?;
65
+ private readonly _high;
66
+ private readonly _normal;
67
+ private readonly _low;
68
+ private _running;
69
+ private readonly _active;
70
+ private _counter;
71
+ private _flushWaiters;
72
+ /**
73
+ * Creates a new RequestQueue.
74
+ * @throws {TypeError} if `concurrency` is less than 1.
75
+ */
76
+ constructor(options: RequestQueueOptions);
77
+ /**
78
+ * Enqueues a task. The task factory receives an internal AbortSignal
79
+ * that fires when the task is cancelled via `cancel(id)`.
80
+ *
81
+ * @returns `Promise<T>` resolving to the task's return value.
82
+ * @throws {@link QueueAbortError} if cancelled before or during execution.
83
+ * @throws Whatever the task itself throws.
84
+ */
85
+ add<T>(task: (signal: AbortSignal) => Promise<T>, options?: AddOptions): Promise<T>;
86
+ private _drain;
87
+ private _notifyFlushWaiters;
88
+ /**
89
+ * Cancels a task by ID.
90
+ * - **Queued task**: removed from its lane; promise rejects with {@link QueueAbortError}.
91
+ * Task factory is never called.
92
+ * - **In-flight task**: internal AbortSignal is fired. The task is responsible
93
+ * for reacting to signal abortion and rejecting its promise.
94
+ *
95
+ * @returns `true` if the ID was found; `false` otherwise.
96
+ * @internal Public consumers should use `AddOptions.signal` for cancellation.
97
+ */
98
+ cancel(id: string): boolean;
99
+ /**
100
+ * Resolves when all currently queued and running tasks have settled.
101
+ * Tasks added after `flush()` is called are included in the wait.
102
+ */
103
+ flush(): Promise<void>;
104
+ /** @internal Returns IDs of all queued (not yet running) items across all lanes. */
105
+ _queuedIds(): string[];
106
+ /** @internal Returns IDs of all currently running items. */
107
+ _runningIds(): string[];
108
+ /** Number of tasks waiting to start (across all priority lanes). */
109
+ get size(): number;
110
+ /** Number of tasks currently executing. */
111
+ get running(): number;
112
+ /** Total active work: `size + running`. */
113
+ get pending(): number;
114
+ }
115
+ //# sourceMappingURL=request-queue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-queue.d.ts","sourceRoot":"","sources":["../../../src/utils/async/request-queue.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,kEAAkE;AAClE,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;AAEtD,oDAAoD;AACpD,MAAM,WAAW,mBAAmB;IAChC;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;CAChD;AAED,qDAAqD;AACrD,MAAM,WAAW,UAAU;IACvB;;;OAGG;IACH,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB;;;;OAIG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;CACxB;AAED;;;GAGG;AACH,qBAAa,eAAgB,SAAQ,KAAK;gBAC1B,OAAO,CAAC,EAAE,MAAM;CAI/B;AAcD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,YAAY;IACrB,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAqC;IAC/D,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAmB;IACzC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmB;IAC3C,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAmB;IACxC,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgC;IACxD,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,aAAa,CAAyB;IAE9C;;;OAGG;gBACS,OAAO,EAAE,mBAAmB;IAQxC;;;;;;;OAOG;IACH,GAAG,CAAC,CAAC,EACD,IAAI,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,EACzC,OAAO,CAAC,EAAE,UAAU,GACrB,OAAO,CAAC,CAAC,CAAC;IA2Cb,OAAO,CAAC,MAAM;IAmCd,OAAO,CAAC,mBAAmB;IAO3B;;;;;;;;;OASG;IACH,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAoB3B;;;OAGG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAStB,oFAAoF;IACpF,UAAU,IAAI,MAAM,EAAE;IAQtB,4DAA4D;IAC5D,WAAW,IAAI,MAAM,EAAE;IAIvB,oEAAoE;IACpE,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,2CAA2C;IAC3C,IAAI,OAAO,IAAI,MAAM,CAEpB;IAED,2CAA2C;IAC3C,IAAI,OAAO,IAAI,MAAM,CAEpB;CACJ"}