actly 1.1.5 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +232 -434
- package/dist/core/act.cjs +353 -35
- package/dist/core/act.d.ts.map +1 -1
- package/dist/core/act.js +355 -37
- package/dist/core/act.js.map +1 -1
- package/dist/core/executor.cjs +2 -0
- package/dist/core/executor.d.ts +6 -1
- package/dist/core/executor.d.ts.map +1 -1
- package/dist/core/executor.js +2 -0
- package/dist/core/executor.js.map +1 -1
- package/dist/core/health.cjs +34 -0
- package/dist/core/health.d.ts +18 -0
- package/dist/core/health.d.ts.map +1 -0
- package/dist/core/health.js +28 -0
- package/dist/core/health.js.map +1 -0
- package/dist/core/shutdown.cjs +60 -0
- package/dist/core/shutdown.d.ts +8 -0
- package/dist/core/shutdown.d.ts.map +1 -0
- package/dist/core/shutdown.js +56 -0
- package/dist/core/shutdown.js.map +1 -0
- package/dist/core/tenant.cjs +71 -0
- package/dist/core/tenant.d.ts +29 -0
- package/dist/core/tenant.d.ts.map +1 -0
- package/dist/core/tenant.js +68 -0
- package/dist/core/tenant.js.map +1 -0
- package/dist/errors.cjs +182 -0
- package/dist/errors.d.ts +151 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +171 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.cjs +44 -4
- package/dist/index.d.ts +17 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -1
- package/dist/index.js.map +1 -1
- package/dist/observability.cjs +45 -0
- package/dist/observability.d.ts +145 -0
- package/dist/observability.d.ts.map +1 -0
- package/dist/observability.js +43 -0
- package/dist/observability.js.map +1 -0
- package/dist/policies/bulkhead.cjs +97 -0
- package/dist/policies/bulkhead.d.ts +3 -0
- package/dist/policies/bulkhead.d.ts.map +1 -0
- package/dist/policies/bulkhead.js +95 -0
- package/dist/policies/bulkhead.js.map +1 -0
- package/dist/policies/cache.cjs +168 -31
- package/dist/policies/cache.d.ts +17 -4
- package/dist/policies/cache.d.ts.map +1 -1
- package/dist/policies/cache.js +168 -31
- package/dist/policies/cache.js.map +1 -1
- package/dist/policies/circuitBreaker.cjs +85 -0
- package/dist/policies/circuitBreaker.d.ts +3 -0
- package/dist/policies/circuitBreaker.d.ts.map +1 -0
- package/dist/policies/circuitBreaker.js +83 -0
- package/dist/policies/circuitBreaker.js.map +1 -0
- package/dist/policies/dedupe.cjs +74 -45
- package/dist/policies/dedupe.d.ts +10 -28
- package/dist/policies/dedupe.d.ts.map +1 -1
- package/dist/policies/dedupe.js +74 -45
- package/dist/policies/dedupe.js.map +1 -1
- package/dist/policies/rateLimit.cjs +35 -0
- package/dist/policies/rateLimit.d.ts +3 -0
- package/dist/policies/rateLimit.d.ts.map +1 -0
- package/dist/policies/rateLimit.js +33 -0
- package/dist/policies/rateLimit.js.map +1 -0
- package/dist/policies/retry.cjs +45 -8
- package/dist/policies/retry.d.ts.map +1 -1
- package/dist/policies/retry.js +45 -8
- package/dist/policies/retry.js.map +1 -1
- package/dist/policies/timeout.cjs +25 -53
- package/dist/policies/timeout.d.ts +2 -33
- package/dist/policies/timeout.d.ts.map +1 -1
- package/dist/policies/timeout.js +31 -48
- package/dist/policies/timeout.js.map +1 -1
- package/dist/state/store.cjs +3 -3
- package/dist/state/store.d.ts +3 -3
- package/dist/state/store.js +3 -3
- package/dist/stores/memory.cjs +126 -53
- package/dist/stores/memory.d.ts +35 -16
- package/dist/stores/memory.d.ts.map +1 -1
- package/dist/stores/memory.js +125 -53
- package/dist/stores/memory.js.map +1 -1
- package/dist/types/index.d.ts +176 -3
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/abort.cjs +108 -38
- package/dist/utils/abort.d.ts +43 -19
- package/dist/utils/abort.d.ts.map +1 -1
- package/dist/utils/abort.js +108 -38
- package/dist/utils/abort.js.map +1 -1
- package/dist/utils/abortPool.cjs +26 -0
- package/dist/utils/abortPool.d.ts +4 -0
- package/dist/utils/abortPool.d.ts.map +1 -0
- package/dist/utils/abortPool.js +22 -0
- package/dist/utils/abortPool.js.map +1 -0
- package/dist/utils/backoff.cjs +12 -1
- package/dist/utils/backoff.d.ts.map +1 -1
- package/dist/utils/backoff.js +12 -1
- package/dist/utils/backoff.js.map +1 -1
- package/dist/utils/key.cjs +77 -0
- package/dist/utils/key.d.ts +29 -0
- package/dist/utils/key.d.ts.map +1 -0
- package/dist/utils/key.js +75 -0
- package/dist/utils/key.js.map +1 -0
- package/dist/utils/limits.cjs +40 -0
- package/dist/utils/limits.d.ts +39 -0
- package/dist/utils/limits.d.ts.map +1 -0
- package/dist/utils/limits.js +38 -0
- package/dist/utils/limits.js.map +1 -0
- package/dist/utils/sanitize.cjs +29 -0
- package/dist/utils/sanitize.d.ts +3 -0
- package/dist/utils/sanitize.d.ts.map +1 -0
- package/dist/utils/sanitize.js +26 -0
- package/dist/utils/sanitize.js.map +1 -0
- package/dist/utils/validate.cjs +90 -16
- package/dist/utils/validate.d.ts +11 -0
- package/dist/utils/validate.d.ts.map +1 -1
- package/dist/utils/validate.js +85 -16
- package/dist/utils/validate.js.map +1 -1
- package/package.json +6 -5
package/dist/utils/abort.cjs
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
// ─── AbortSignal helpers ──────────────────────────────────────────────────────
|
|
3
3
|
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
4
|
+
// Contract: zero listener accumulation on long-lived AbortSignals.
|
|
5
|
+
//
|
|
6
|
+
// Every `addEventListener('abort', ...)` MUST be paired with a
|
|
7
|
+
// `removeEventListener` on the success path, OR use `AbortSignal.any()`
|
|
8
|
+
// (Node 20+ native) which handles cleanup automatically.
|
|
9
|
+
//
|
|
10
|
+
// Long-lived signals (server shutdown, request pool) must not accumulate
|
|
11
|
+
// one listener per `act()` call — that would cause
|
|
12
|
+
// `MaxListenersExceededWarning` and unbounded closure retention.
|
|
8
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
14
|
exports.anySignal = anySignal;
|
|
10
15
|
exports.raceAbort = raceAbort;
|
|
@@ -12,29 +17,51 @@ exports.sleep = sleep;
|
|
|
12
17
|
exports.isAbortError = isAbortError;
|
|
13
18
|
exports.linkSignal = linkSignal;
|
|
14
19
|
/**
|
|
15
|
-
*
|
|
20
|
+
* Compose multiple AbortSignals into one. Aborts when ANY input aborts,
|
|
21
|
+
* with the same reason.
|
|
22
|
+
*
|
|
23
|
+
* Uses native `AbortSignal.any()` on Node 20+ (zero allocations, no
|
|
24
|
+
* listener leaks — the runtime owns the lifecycle). Falls back to a
|
|
25
|
+
* polyfill that explicitly removes listeners on first abort.
|
|
16
26
|
*
|
|
17
|
-
*
|
|
18
|
-
* with the same reason. If any input is already aborted, the returned signal
|
|
19
|
-
* is aborted synchronously.
|
|
27
|
+
* # Listener safety
|
|
20
28
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
29
|
+
* The polyfill registers `{ once: true }` listeners on each input and
|
|
30
|
+
* manually removes the others when one fires. After settlement, the
|
|
31
|
+
* composite signal holds zero references to the inputs — they may be GC'd.
|
|
23
32
|
*/
|
|
24
33
|
function anySignal(signals) {
|
|
25
|
-
//
|
|
26
|
-
|
|
34
|
+
// Filter out undefined / null defensively.
|
|
35
|
+
const filtered = signals.filter((s) => s != null);
|
|
36
|
+
if (filtered.length === 0) {
|
|
37
|
+
// No inputs → never-aborting signal. Use a fresh controller so callers
|
|
38
|
+
// get a real AbortSignal, not a hand-rolled mock.
|
|
39
|
+
return new AbortController().signal;
|
|
40
|
+
}
|
|
41
|
+
if (filtered.length === 1)
|
|
42
|
+
return filtered[0];
|
|
43
|
+
// Native path (Node 20+, modern browsers, Bun).
|
|
27
44
|
const native = AbortSignal.any;
|
|
28
|
-
if (typeof native === 'function')
|
|
29
|
-
return native.call(AbortSignal,
|
|
30
|
-
|
|
45
|
+
if (typeof native === 'function') {
|
|
46
|
+
return native.call(AbortSignal, filtered);
|
|
47
|
+
}
|
|
48
|
+
// Polyfill: only used when native is missing. Listener-safe.
|
|
31
49
|
const controller = new AbortController();
|
|
32
|
-
|
|
50
|
+
const listeners = [];
|
|
51
|
+
for (const signal of filtered) {
|
|
33
52
|
if (signal.aborted) {
|
|
34
53
|
controller.abort(signal.reason);
|
|
35
54
|
break;
|
|
36
55
|
}
|
|
37
|
-
|
|
56
|
+
const onAbort = () => {
|
|
57
|
+
controller.abort(signal.reason);
|
|
58
|
+
// Remove all other listeners — they hold closures over `signal`
|
|
59
|
+
// and would otherwise keep the inputs alive past settlement.
|
|
60
|
+
for (const off of listeners)
|
|
61
|
+
off();
|
|
62
|
+
};
|
|
63
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
64
|
+
listeners.push(() => signal.removeEventListener('abort', onAbort));
|
|
38
65
|
}
|
|
39
66
|
return controller.signal;
|
|
40
67
|
}
|
|
@@ -45,34 +72,50 @@ function anySignal(signals) {
|
|
|
45
72
|
* - If the signal aborts while the promise is pending, rejects with `signal.reason`.
|
|
46
73
|
* - If the promise settles first, returns its value (or rejects with its error).
|
|
47
74
|
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
75
|
+
* # Listener safety
|
|
76
|
+
*
|
|
77
|
+
* On success path, the abort listener is explicitly removed. A
|
|
78
|
+
* `{ once: true }` listener would stay attached on long-lived signals,
|
|
79
|
+
* leaking one closure per call.
|
|
51
80
|
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
81
|
+
* # Mark-as-handled
|
|
82
|
+
*
|
|
83
|
+
* When the signal aborts first, the original `promise` may still settle
|
|
84
|
+
* later (success or failure). We attach a no-op `.catch` to it so V8
|
|
85
|
+
* doesn't emit an `unhandledRejection` warning. The error is NOT
|
|
86
|
+
* swallowed from the caller — the caller already received `signal.reason`.
|
|
54
87
|
*/
|
|
55
88
|
function raceAbort(promise, signal) {
|
|
56
|
-
if (signal.aborted)
|
|
89
|
+
if (signal.aborted) {
|
|
90
|
+
// Mark the promise as handled — its eventual rejection won't surface
|
|
91
|
+
// as an unhandled rejection. Its eventual success is dropped silently,
|
|
92
|
+
// which is correct: the caller has already moved on.
|
|
93
|
+
promise.catch(() => { });
|
|
57
94
|
return Promise.reject(signal.reason);
|
|
95
|
+
}
|
|
58
96
|
return new Promise((resolve, reject) => {
|
|
59
97
|
let settled = false;
|
|
60
98
|
const onAbort = () => {
|
|
61
99
|
if (settled)
|
|
62
100
|
return;
|
|
63
101
|
settled = true;
|
|
102
|
+
signal.removeEventListener('abort', onAbort);
|
|
64
103
|
reject(signal.reason);
|
|
65
104
|
};
|
|
66
|
-
|
|
105
|
+
// Not using `{ once: true }` — we want explicit removal on success
|
|
106
|
+
// path so the listener doesn't linger on a long-lived signal.
|
|
107
|
+
signal.addEventListener('abort', onAbort);
|
|
67
108
|
promise.then((value) => {
|
|
68
109
|
if (settled)
|
|
69
110
|
return;
|
|
70
111
|
settled = true;
|
|
112
|
+
signal.removeEventListener('abort', onAbort);
|
|
71
113
|
resolve(value);
|
|
72
114
|
}, (error) => {
|
|
73
115
|
if (settled)
|
|
74
116
|
return;
|
|
75
117
|
settled = true;
|
|
118
|
+
signal.removeEventListener('abort', onAbort);
|
|
76
119
|
reject(error);
|
|
77
120
|
});
|
|
78
121
|
});
|
|
@@ -84,9 +127,11 @@ function raceAbort(promise, signal) {
|
|
|
84
127
|
* signal aborts before the timer fires. If the signal is already aborted
|
|
85
128
|
* when called, rejects synchronously (in microtask).
|
|
86
129
|
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
130
|
+
* # Timer hygiene
|
|
131
|
+
*
|
|
132
|
+
* The internal `setTimeout` is `unref`'d on Node so it doesn't keep the
|
|
133
|
+
* event loop alive solely for this sleep. On browsers there is no
|
|
134
|
+
* equivalent — the timer is short-lived enough not to matter.
|
|
90
135
|
*/
|
|
91
136
|
function sleep(ms, signal) {
|
|
92
137
|
if (ms <= 0) {
|
|
@@ -101,11 +146,17 @@ function sleep(ms, signal) {
|
|
|
101
146
|
signal?.removeEventListener('abort', onAbort);
|
|
102
147
|
resolve();
|
|
103
148
|
}, ms);
|
|
149
|
+
// NOTE: do NOT `unref()` this timer. Unlike InMemoryStore's autoCleanup
|
|
150
|
+
// interval (which is a background housekeeping task), `sleep()` IS the
|
|
151
|
+
// operation the caller is awaiting. If we unref'd it, Node could exit
|
|
152
|
+
// the process while a retry delay was pending — silently dropping the
|
|
153
|
+
// operation. The timer is short-lived (cleared in onAbort or after ms),
|
|
154
|
+
// so the cost of keeping the loop alive is bounded.
|
|
104
155
|
const onAbort = () => {
|
|
105
156
|
clearTimeout(timer);
|
|
106
157
|
reject(signal.reason);
|
|
107
158
|
};
|
|
108
|
-
signal?.addEventListener('abort', onAbort
|
|
159
|
+
signal?.addEventListener('abort', onAbort);
|
|
109
160
|
});
|
|
110
161
|
}
|
|
111
162
|
/**
|
|
@@ -118,25 +169,44 @@ function isAbortError(err) {
|
|
|
118
169
|
if (err == null || typeof err !== 'object')
|
|
119
170
|
return false;
|
|
120
171
|
const name = err.name;
|
|
121
|
-
|
|
172
|
+
if (name === 'AbortError')
|
|
173
|
+
return true;
|
|
174
|
+
// DOMException with name 'TimeoutError' is what AbortSignal.timeout throws.
|
|
175
|
+
// Distinguish from our own TimeoutError class by checking for DOMException.
|
|
176
|
+
if (name === 'TimeoutError' &&
|
|
122
177
|
err instanceof Error &&
|
|
123
|
-
// DOMException with name 'TimeoutError' is what AbortSignal.timeout throws.
|
|
124
|
-
// Distinguish from our own TimeoutError class by checking for DOMException.
|
|
125
178
|
typeof DOMException !== 'undefined' &&
|
|
126
|
-
err instanceof DOMException
|
|
179
|
+
err instanceof DOMException) {
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
return false;
|
|
127
183
|
}
|
|
128
184
|
/**
|
|
129
185
|
* Link a parent signal to a child controller: when the parent aborts, the
|
|
130
|
-
* child is aborted with the same reason.
|
|
131
|
-
*
|
|
132
|
-
*
|
|
186
|
+
* child is aborted with the same reason.
|
|
187
|
+
*
|
|
188
|
+
* # Contract
|
|
189
|
+
*
|
|
190
|
+
* Returns an `unlink()` function that removes the listener. Callers MUST
|
|
191
|
+
* call `unlink()` on success path — otherwise the listener stays attached
|
|
192
|
+
* to the parent forever, leaking one closure per call.
|
|
193
|
+
*
|
|
194
|
+
* If the parent is already aborted, the child is aborted synchronously
|
|
195
|
+
* and `unlink` is a no-op.
|
|
196
|
+
*
|
|
197
|
+
* # Prefer `anySignal()` instead
|
|
133
198
|
*
|
|
134
|
-
*
|
|
199
|
+
* For new code, prefer `anySignal([parent, timeoutSignal])` which uses
|
|
200
|
+
* the native Node 20+ implementation and handles cleanup automatically.
|
|
201
|
+
* This function exists for call sites that need an `AbortController`
|
|
202
|
+
* (not just a signal) — e.g. to layer additional abort sources.
|
|
135
203
|
*/
|
|
136
204
|
function linkSignal(parent, child) {
|
|
137
205
|
if (parent.aborted) {
|
|
138
206
|
child.abort(parent.reason);
|
|
139
|
-
return;
|
|
207
|
+
return () => { };
|
|
140
208
|
}
|
|
141
|
-
|
|
209
|
+
const onAbort = () => child.abort(parent.reason);
|
|
210
|
+
parent.addEventListener('abort', onAbort);
|
|
211
|
+
return () => parent.removeEventListener('abort', onAbort);
|
|
142
212
|
}
|
package/dist/utils/abort.d.ts
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Compose multiple AbortSignals into one. Aborts when ANY input aborts,
|
|
3
|
+
* with the same reason.
|
|
3
4
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* Uses native `AbortSignal.any()` on Node 20+ (zero allocations, no
|
|
6
|
+
* listener leaks — the runtime owns the lifecycle). Falls back to a
|
|
7
|
+
* polyfill that explicitly removes listeners on first abort.
|
|
7
8
|
*
|
|
8
|
-
* Listener
|
|
9
|
-
*
|
|
9
|
+
* # Listener safety
|
|
10
|
+
*
|
|
11
|
+
* The polyfill registers `{ once: true }` listeners on each input and
|
|
12
|
+
* manually removes the others when one fires. After settlement, the
|
|
13
|
+
* composite signal holds zero references to the inputs — they may be GC'd.
|
|
10
14
|
*/
|
|
11
15
|
export declare function anySignal(signals: ReadonlyArray<AbortSignal>): AbortSignal;
|
|
12
16
|
/**
|
|
@@ -16,12 +20,18 @@ export declare function anySignal(signals: ReadonlyArray<AbortSignal>): AbortSig
|
|
|
16
20
|
* - If the signal aborts while the promise is pending, rejects with `signal.reason`.
|
|
17
21
|
* - If the promise settles first, returns its value (or rejects with its error).
|
|
18
22
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
23
|
+
* # Listener safety
|
|
24
|
+
*
|
|
25
|
+
* On success path, the abort listener is explicitly removed. A
|
|
26
|
+
* `{ once: true }` listener would stay attached on long-lived signals,
|
|
27
|
+
* leaking one closure per call.
|
|
22
28
|
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
29
|
+
* # Mark-as-handled
|
|
30
|
+
*
|
|
31
|
+
* When the signal aborts first, the original `promise` may still settle
|
|
32
|
+
* later (success or failure). We attach a no-op `.catch` to it so V8
|
|
33
|
+
* doesn't emit an `unhandledRejection` warning. The error is NOT
|
|
34
|
+
* swallowed from the caller — the caller already received `signal.reason`.
|
|
25
35
|
*/
|
|
26
36
|
export declare function raceAbort<T>(promise: Promise<T>, signal: AbortSignal): Promise<T>;
|
|
27
37
|
/**
|
|
@@ -31,9 +41,11 @@ export declare function raceAbort<T>(promise: Promise<T>, signal: AbortSignal):
|
|
|
31
41
|
* signal aborts before the timer fires. If the signal is already aborted
|
|
32
42
|
* when called, rejects synchronously (in microtask).
|
|
33
43
|
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
44
|
+
* # Timer hygiene
|
|
45
|
+
*
|
|
46
|
+
* The internal `setTimeout` is `unref`'d on Node so it doesn't keep the
|
|
47
|
+
* event loop alive solely for this sleep. On browsers there is no
|
|
48
|
+
* equivalent — the timer is short-lived enough not to matter.
|
|
37
49
|
*/
|
|
38
50
|
export declare function sleep(ms: number, signal?: AbortSignal): Promise<void>;
|
|
39
51
|
/**
|
|
@@ -45,11 +57,23 @@ export declare function sleep(ms: number, signal?: AbortSignal): Promise<void>;
|
|
|
45
57
|
export declare function isAbortError(err: unknown): boolean;
|
|
46
58
|
/**
|
|
47
59
|
* Link a parent signal to a child controller: when the parent aborts, the
|
|
48
|
-
* child is aborted with the same reason.
|
|
49
|
-
*
|
|
50
|
-
*
|
|
60
|
+
* child is aborted with the same reason.
|
|
61
|
+
*
|
|
62
|
+
* # Contract
|
|
63
|
+
*
|
|
64
|
+
* Returns an `unlink()` function that removes the listener. Callers MUST
|
|
65
|
+
* call `unlink()` on success path — otherwise the listener stays attached
|
|
66
|
+
* to the parent forever, leaking one closure per call.
|
|
67
|
+
*
|
|
68
|
+
* If the parent is already aborted, the child is aborted synchronously
|
|
69
|
+
* and `unlink` is a no-op.
|
|
70
|
+
*
|
|
71
|
+
* # Prefer `anySignal()` instead
|
|
51
72
|
*
|
|
52
|
-
*
|
|
73
|
+
* For new code, prefer `anySignal([parent, timeoutSignal])` which uses
|
|
74
|
+
* the native Node 20+ implementation and handles cleanup automatically.
|
|
75
|
+
* This function exists for call sites that need an `AbortController`
|
|
76
|
+
* (not just a signal) — e.g. to layer additional abort sources.
|
|
53
77
|
*/
|
|
54
|
-
export declare function linkSignal(parent: AbortSignal, child: AbortController): void;
|
|
78
|
+
export declare function linkSignal(parent: AbortSignal, child: AbortController): () => void;
|
|
55
79
|
//# sourceMappingURL=abort.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"abort.d.ts","sourceRoot":"","sources":["../../src/utils/abort.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"abort.d.ts","sourceRoot":"","sources":["../../src/utils/abort.ts"],"names":[],"mappings":"AAYA;;;;;;;;;;;;;GAaG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,aAAa,CAAC,WAAW,CAAC,GAAG,WAAW,CAsC1E;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,SAAS,CAAC,CAAC,EACzB,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EACnB,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,CAAC,CAAC,CAsCZ;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CA2BrE;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAelD;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,UAAU,CACxB,MAAM,EAAE,WAAW,EACnB,KAAK,EAAE,eAAe,GACrB,MAAM,IAAI,CAQZ"}
|
package/dist/utils/abort.js
CHANGED
|
@@ -1,33 +1,60 @@
|
|
|
1
1
|
// ─── AbortSignal helpers ──────────────────────────────────────────────────────
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
3
|
+
// Contract: zero listener accumulation on long-lived AbortSignals.
|
|
4
|
+
//
|
|
5
|
+
// Every `addEventListener('abort', ...)` MUST be paired with a
|
|
6
|
+
// `removeEventListener` on the success path, OR use `AbortSignal.any()`
|
|
7
|
+
// (Node 20+ native) which handles cleanup automatically.
|
|
8
|
+
//
|
|
9
|
+
// Long-lived signals (server shutdown, request pool) must not accumulate
|
|
10
|
+
// one listener per `act()` call — that would cause
|
|
11
|
+
// `MaxListenersExceededWarning` and unbounded closure retention.
|
|
7
12
|
/**
|
|
8
|
-
*
|
|
13
|
+
* Compose multiple AbortSignals into one. Aborts when ANY input aborts,
|
|
14
|
+
* with the same reason.
|
|
15
|
+
*
|
|
16
|
+
* Uses native `AbortSignal.any()` on Node 20+ (zero allocations, no
|
|
17
|
+
* listener leaks — the runtime owns the lifecycle). Falls back to a
|
|
18
|
+
* polyfill that explicitly removes listeners on first abort.
|
|
9
19
|
*
|
|
10
|
-
*
|
|
11
|
-
* with the same reason. If any input is already aborted, the returned signal
|
|
12
|
-
* is aborted synchronously.
|
|
20
|
+
* # Listener safety
|
|
13
21
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
22
|
+
* The polyfill registers `{ once: true }` listeners on each input and
|
|
23
|
+
* manually removes the others when one fires. After settlement, the
|
|
24
|
+
* composite signal holds zero references to the inputs — they may be GC'd.
|
|
16
25
|
*/
|
|
17
26
|
export function anySignal(signals) {
|
|
18
|
-
//
|
|
19
|
-
|
|
27
|
+
// Filter out undefined / null defensively.
|
|
28
|
+
const filtered = signals.filter((s) => s != null);
|
|
29
|
+
if (filtered.length === 0) {
|
|
30
|
+
// No inputs → never-aborting signal. Use a fresh controller so callers
|
|
31
|
+
// get a real AbortSignal, not a hand-rolled mock.
|
|
32
|
+
return new AbortController().signal;
|
|
33
|
+
}
|
|
34
|
+
if (filtered.length === 1)
|
|
35
|
+
return filtered[0];
|
|
36
|
+
// Native path (Node 20+, modern browsers, Bun).
|
|
20
37
|
const native = AbortSignal.any;
|
|
21
|
-
if (typeof native === 'function')
|
|
22
|
-
return native.call(AbortSignal,
|
|
23
|
-
|
|
38
|
+
if (typeof native === 'function') {
|
|
39
|
+
return native.call(AbortSignal, filtered);
|
|
40
|
+
}
|
|
41
|
+
// Polyfill: only used when native is missing. Listener-safe.
|
|
24
42
|
const controller = new AbortController();
|
|
25
|
-
|
|
43
|
+
const listeners = [];
|
|
44
|
+
for (const signal of filtered) {
|
|
26
45
|
if (signal.aborted) {
|
|
27
46
|
controller.abort(signal.reason);
|
|
28
47
|
break;
|
|
29
48
|
}
|
|
30
|
-
|
|
49
|
+
const onAbort = () => {
|
|
50
|
+
controller.abort(signal.reason);
|
|
51
|
+
// Remove all other listeners — they hold closures over `signal`
|
|
52
|
+
// and would otherwise keep the inputs alive past settlement.
|
|
53
|
+
for (const off of listeners)
|
|
54
|
+
off();
|
|
55
|
+
};
|
|
56
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
57
|
+
listeners.push(() => signal.removeEventListener('abort', onAbort));
|
|
31
58
|
}
|
|
32
59
|
return controller.signal;
|
|
33
60
|
}
|
|
@@ -38,34 +65,50 @@ export function anySignal(signals) {
|
|
|
38
65
|
* - If the signal aborts while the promise is pending, rejects with `signal.reason`.
|
|
39
66
|
* - If the promise settles first, returns its value (or rejects with its error).
|
|
40
67
|
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
68
|
+
* # Listener safety
|
|
69
|
+
*
|
|
70
|
+
* On success path, the abort listener is explicitly removed. A
|
|
71
|
+
* `{ once: true }` listener would stay attached on long-lived signals,
|
|
72
|
+
* leaking one closure per call.
|
|
44
73
|
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
74
|
+
* # Mark-as-handled
|
|
75
|
+
*
|
|
76
|
+
* When the signal aborts first, the original `promise` may still settle
|
|
77
|
+
* later (success or failure). We attach a no-op `.catch` to it so V8
|
|
78
|
+
* doesn't emit an `unhandledRejection` warning. The error is NOT
|
|
79
|
+
* swallowed from the caller — the caller already received `signal.reason`.
|
|
47
80
|
*/
|
|
48
81
|
export function raceAbort(promise, signal) {
|
|
49
|
-
if (signal.aborted)
|
|
82
|
+
if (signal.aborted) {
|
|
83
|
+
// Mark the promise as handled — its eventual rejection won't surface
|
|
84
|
+
// as an unhandled rejection. Its eventual success is dropped silently,
|
|
85
|
+
// which is correct: the caller has already moved on.
|
|
86
|
+
promise.catch(() => { });
|
|
50
87
|
return Promise.reject(signal.reason);
|
|
88
|
+
}
|
|
51
89
|
return new Promise((resolve, reject) => {
|
|
52
90
|
let settled = false;
|
|
53
91
|
const onAbort = () => {
|
|
54
92
|
if (settled)
|
|
55
93
|
return;
|
|
56
94
|
settled = true;
|
|
95
|
+
signal.removeEventListener('abort', onAbort);
|
|
57
96
|
reject(signal.reason);
|
|
58
97
|
};
|
|
59
|
-
|
|
98
|
+
// Not using `{ once: true }` — we want explicit removal on success
|
|
99
|
+
// path so the listener doesn't linger on a long-lived signal.
|
|
100
|
+
signal.addEventListener('abort', onAbort);
|
|
60
101
|
promise.then((value) => {
|
|
61
102
|
if (settled)
|
|
62
103
|
return;
|
|
63
104
|
settled = true;
|
|
105
|
+
signal.removeEventListener('abort', onAbort);
|
|
64
106
|
resolve(value);
|
|
65
107
|
}, (error) => {
|
|
66
108
|
if (settled)
|
|
67
109
|
return;
|
|
68
110
|
settled = true;
|
|
111
|
+
signal.removeEventListener('abort', onAbort);
|
|
69
112
|
reject(error);
|
|
70
113
|
});
|
|
71
114
|
});
|
|
@@ -77,9 +120,11 @@ export function raceAbort(promise, signal) {
|
|
|
77
120
|
* signal aborts before the timer fires. If the signal is already aborted
|
|
78
121
|
* when called, rejects synchronously (in microtask).
|
|
79
122
|
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
123
|
+
* # Timer hygiene
|
|
124
|
+
*
|
|
125
|
+
* The internal `setTimeout` is `unref`'d on Node so it doesn't keep the
|
|
126
|
+
* event loop alive solely for this sleep. On browsers there is no
|
|
127
|
+
* equivalent — the timer is short-lived enough not to matter.
|
|
83
128
|
*/
|
|
84
129
|
export function sleep(ms, signal) {
|
|
85
130
|
if (ms <= 0) {
|
|
@@ -94,11 +139,17 @@ export function sleep(ms, signal) {
|
|
|
94
139
|
signal?.removeEventListener('abort', onAbort);
|
|
95
140
|
resolve();
|
|
96
141
|
}, ms);
|
|
142
|
+
// NOTE: do NOT `unref()` this timer. Unlike InMemoryStore's autoCleanup
|
|
143
|
+
// interval (which is a background housekeeping task), `sleep()` IS the
|
|
144
|
+
// operation the caller is awaiting. If we unref'd it, Node could exit
|
|
145
|
+
// the process while a retry delay was pending — silently dropping the
|
|
146
|
+
// operation. The timer is short-lived (cleared in onAbort or after ms),
|
|
147
|
+
// so the cost of keeping the loop alive is bounded.
|
|
97
148
|
const onAbort = () => {
|
|
98
149
|
clearTimeout(timer);
|
|
99
150
|
reject(signal.reason);
|
|
100
151
|
};
|
|
101
|
-
signal?.addEventListener('abort', onAbort
|
|
152
|
+
signal?.addEventListener('abort', onAbort);
|
|
102
153
|
});
|
|
103
154
|
}
|
|
104
155
|
/**
|
|
@@ -111,26 +162,45 @@ export function isAbortError(err) {
|
|
|
111
162
|
if (err == null || typeof err !== 'object')
|
|
112
163
|
return false;
|
|
113
164
|
const name = err.name;
|
|
114
|
-
|
|
165
|
+
if (name === 'AbortError')
|
|
166
|
+
return true;
|
|
167
|
+
// DOMException with name 'TimeoutError' is what AbortSignal.timeout throws.
|
|
168
|
+
// Distinguish from our own TimeoutError class by checking for DOMException.
|
|
169
|
+
if (name === 'TimeoutError' &&
|
|
115
170
|
err instanceof Error &&
|
|
116
|
-
// DOMException with name 'TimeoutError' is what AbortSignal.timeout throws.
|
|
117
|
-
// Distinguish from our own TimeoutError class by checking for DOMException.
|
|
118
171
|
typeof DOMException !== 'undefined' &&
|
|
119
|
-
err instanceof DOMException
|
|
172
|
+
err instanceof DOMException) {
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
return false;
|
|
120
176
|
}
|
|
121
177
|
/**
|
|
122
178
|
* Link a parent signal to a child controller: when the parent aborts, the
|
|
123
|
-
* child is aborted with the same reason.
|
|
124
|
-
*
|
|
125
|
-
*
|
|
179
|
+
* child is aborted with the same reason.
|
|
180
|
+
*
|
|
181
|
+
* # Contract
|
|
182
|
+
*
|
|
183
|
+
* Returns an `unlink()` function that removes the listener. Callers MUST
|
|
184
|
+
* call `unlink()` on success path — otherwise the listener stays attached
|
|
185
|
+
* to the parent forever, leaking one closure per call.
|
|
186
|
+
*
|
|
187
|
+
* If the parent is already aborted, the child is aborted synchronously
|
|
188
|
+
* and `unlink` is a no-op.
|
|
189
|
+
*
|
|
190
|
+
* # Prefer `anySignal()` instead
|
|
126
191
|
*
|
|
127
|
-
*
|
|
192
|
+
* For new code, prefer `anySignal([parent, timeoutSignal])` which uses
|
|
193
|
+
* the native Node 20+ implementation and handles cleanup automatically.
|
|
194
|
+
* This function exists for call sites that need an `AbortController`
|
|
195
|
+
* (not just a signal) — e.g. to layer additional abort sources.
|
|
128
196
|
*/
|
|
129
197
|
export function linkSignal(parent, child) {
|
|
130
198
|
if (parent.aborted) {
|
|
131
199
|
child.abort(parent.reason);
|
|
132
|
-
return;
|
|
200
|
+
return () => { };
|
|
133
201
|
}
|
|
134
|
-
|
|
202
|
+
const onAbort = () => child.abort(parent.reason);
|
|
203
|
+
parent.addEventListener('abort', onAbort);
|
|
204
|
+
return () => parent.removeEventListener('abort', onAbort);
|
|
135
205
|
}
|
|
136
206
|
//# sourceMappingURL=abort.js.map
|
package/dist/utils/abort.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"abort.js","sourceRoot":"","sources":["../../src/utils/abort.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,EAAE;AACF,wEAAwE;AACxE,yEAAyE;AACzE,
|
|
1
|
+
{"version":3,"file":"abort.js","sourceRoot":"","sources":["../../src/utils/abort.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,EAAE;AACF,mEAAmE;AACnE,EAAE;AACF,+DAA+D;AAC/D,wEAAwE;AACxE,yDAAyD;AACzD,EAAE;AACF,yEAAyE;AACzE,mDAAmD;AACnD,iEAAiE;AAEjE;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,SAAS,CAAC,OAAmC;IAC3D,2CAA2C;IAC3C,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAoB,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC,CAAA;IACnE,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,uEAAuE;QACvE,kDAAkD;QAClD,OAAO,IAAI,eAAe,EAAE,CAAC,MAAM,CAAA;IACrC,CAAC;IACD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,QAAQ,CAAC,CAAC,CAAE,CAAA;IAE9C,gDAAgD;IAChD,MAAM,MAAM,GAAI,WAEd,CAAC,GAAG,CAAA;IACN,IAAI,OAAO,MAAM,KAAK,UAAU,EAAE,CAAC;QACjC,OAAO,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAA;IAC3C,CAAC;IAED,6DAA6D;IAC7D,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAA;IACxC,MAAM,SAAS,GAAsB,EAAE,CAAA;IAEvC,KAAK,MAAM,MAAM,IAAI,QAAQ,EAAE,CAAC;QAC9B,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YACnB,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;YAC/B,MAAK;QACP,CAAC;QACD,MAAM,OAAO,GAAG,GAAG,EAAE;YACnB,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;YAC/B,gEAAgE;YAChE,6DAA6D;YAC7D,KAAK,MAAM,GAAG,IAAI,SAAS;gBAAE,GAAG,EAAE,CAAA;QACpC,CAAC,CAAA;QACD,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAA;QACzD,SAAS,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAA;IACpE,CAAC;IAED,OAAO,UAAU,CAAC,MAAM,CAAA;AAC1B,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,SAAS,CACvB,OAAmB,EACnB,MAAmB;IAEnB,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACnB,qEAAqE;QACrE,uEAAuE;QACvE,qDAAqD;QACrD,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;QACvB,OAAO,OAAO,CAAC,MAAM,CAAI,MAAM,CAAC,MAAM,CAAC,CAAA;IACzC,CAAC;IAED,OAAO,IAAI,OAAO,CAAI,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACxC,IAAI,OAAO,GAAG,KAAK,CAAA;QAEnB,MAAM,OAAO,GAAG,GAAG,EAAE;YACnB,IAAI,OAAO;gBAAE,OAAM;YACnB,OAAO,GAAG,IAAI,CAAA;YACd,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;YAC5C,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;QACvB,CAAC,CAAA;QAED,mEAAmE;QACnE,8DAA8D;QAC9D,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;QAEzC,OAAO,CAAC,IAAI,CACV,CAAC,KAAK,EAAE,EAAE;YACR,IAAI,OAAO;gBAAE,OAAM;YACnB,OAAO,GAAG,IAAI,CAAA;YACd,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;YAC5C,OAAO,CAAC,KAAK,CAAC,CAAA;QAChB,CAAC,EACD,CAAC,KAAK,EAAE,EAAE;YACR,IAAI,OAAO;gBAAE,OAAM;YACnB,OAAO,GAAG,IAAI,CAAA;YACd,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;YAC5C,MAAM,CAAC,KAAK,CAAC,CAAA;QACf,CAAC,CACF,CAAA;IACH,CAAC,CAAC,CAAA;AACJ,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,KAAK,CAAC,EAAU,EAAE,MAAoB;IACpD,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC;QACZ,IAAI,MAAM,EAAE,OAAO;YAAE,OAAO,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;QACzD,OAAO,OAAO,CAAC,OAAO,EAAE,CAAA;IAC1B,CAAC;IAED,IAAI,MAAM,EAAE,OAAO;QAAE,OAAO,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IAEzD,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC3C,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,MAAM,EAAE,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;YAC7C,OAAO,EAAE,CAAA;QACX,CAAC,EAAE,EAAE,CAAC,CAAA;QACN,wEAAwE;QACxE,uEAAuE;QACvE,sEAAsE;QACtE,sEAAsE;QACtE,wEAAwE;QACxE,oDAAoD;QAEpD,MAAM,OAAO,GAAG,GAAG,EAAE;YACnB,YAAY,CAAC,KAAK,CAAC,CAAA;YACnB,MAAM,CAAC,MAAO,CAAC,MAAM,CAAC,CAAA;QACxB,CAAC,CAAA;QAED,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;IAC5C,CAAC,CAAC,CAAA;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,GAAY;IACvC,IAAI,GAAG,IAAI,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAA;IACxD,MAAM,IAAI,GAAI,GAA0B,CAAC,IAAI,CAAA;IAC7C,IAAI,IAAI,KAAK,YAAY;QAAE,OAAO,IAAI,CAAA;IACtC,4EAA4E;IAC5E,4EAA4E;IAC5E,IACE,IAAI,KAAK,cAAc;QACvB,GAAG,YAAY,KAAK;QACpB,OAAO,YAAY,KAAK,WAAW;QACnC,GAAG,YAAY,YAAY,EAC3B,CAAC;QACD,OAAO,IAAI,CAAA;IACb,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,UAAU,CACxB,MAAmB,EACnB,KAAsB;IAEtB,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACnB,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;QAC1B,OAAO,GAAG,EAAE,GAAE,CAAC,CAAA;IACjB,CAAC;IACD,MAAM,OAAO,GAAG,GAAG,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IAChD,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;IACzC,OAAO,GAAG,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;AAC3D,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.acquireController = acquireController;
|
|
4
|
+
exports.releaseController = releaseController;
|
|
5
|
+
exports.poolSize = poolSize;
|
|
6
|
+
const pool = [];
|
|
7
|
+
const MAX_POOL_SIZE = 64;
|
|
8
|
+
function acquireController() {
|
|
9
|
+
while (pool.length > 0) {
|
|
10
|
+
const c = pool.pop();
|
|
11
|
+
if (!c.signal.aborted) {
|
|
12
|
+
return c;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return new AbortController();
|
|
16
|
+
}
|
|
17
|
+
function releaseController(c) {
|
|
18
|
+
if (c.signal.aborted)
|
|
19
|
+
return;
|
|
20
|
+
if (pool.length >= MAX_POOL_SIZE)
|
|
21
|
+
return;
|
|
22
|
+
pool.push(c);
|
|
23
|
+
}
|
|
24
|
+
function poolSize() {
|
|
25
|
+
return pool.length;
|
|
26
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"abortPool.d.ts","sourceRoot":"","sources":["../../src/utils/abortPool.ts"],"names":[],"mappings":"AAGA,wBAAgB,iBAAiB,IAAI,eAAe,CAQnD;AAED,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,eAAe,GAAG,IAAI,CAI1D;AAED,wBAAgB,QAAQ,IAAI,MAAM,CAEjC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const pool = [];
|
|
2
|
+
const MAX_POOL_SIZE = 64;
|
|
3
|
+
export function acquireController() {
|
|
4
|
+
while (pool.length > 0) {
|
|
5
|
+
const c = pool.pop();
|
|
6
|
+
if (!c.signal.aborted) {
|
|
7
|
+
return c;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
return new AbortController();
|
|
11
|
+
}
|
|
12
|
+
export function releaseController(c) {
|
|
13
|
+
if (c.signal.aborted)
|
|
14
|
+
return;
|
|
15
|
+
if (pool.length >= MAX_POOL_SIZE)
|
|
16
|
+
return;
|
|
17
|
+
pool.push(c);
|
|
18
|
+
}
|
|
19
|
+
export function poolSize() {
|
|
20
|
+
return pool.length;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=abortPool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"abortPool.js","sourceRoot":"","sources":["../../src/utils/abortPool.ts"],"names":[],"mappings":"AAAA,MAAM,IAAI,GAAsB,EAAE,CAAA;AAClC,MAAM,aAAa,GAAG,EAAE,CAAA;AAExB,MAAM,UAAU,iBAAiB;IAC/B,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,EAAG,CAAA;QACrB,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACtB,OAAO,CAAC,CAAA;QACV,CAAC;IACH,CAAC;IACD,OAAO,IAAI,eAAe,EAAE,CAAA;AAC9B,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,CAAkB;IAClD,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO;QAAE,OAAM;IAC5B,IAAI,IAAI,CAAC,MAAM,IAAI,aAAa;QAAE,OAAM;IACxC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AACd,CAAC;AAED,MAAM,UAAU,QAAQ;IACtB,OAAO,IAAI,CAAC,MAAM,CAAA;AACpB,CAAC"}
|