hanzio 1.2.0 → 2.0.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 +786 -239
- package/dist/api-wrapper/builders.d.ts +4 -3
- package/dist/api-wrapper/create-client.d.ts +1 -1
- package/dist/api-wrapper/describe.d.ts +16 -0
- package/dist/api-wrapper/endpoint.d.ts +18 -0
- package/dist/api-wrapper/errors.d.ts +39 -2
- package/dist/api-wrapper/index.d.ts +5 -0
- package/dist/api-wrapper/otel.d.ts +44 -0
- package/dist/api-wrapper/paginate.d.ts +12 -0
- package/dist/api-wrapper/redaction.d.ts +2 -0
- package/dist/api-wrapper/shared.d.ts +16 -2
- package/dist/api-wrapper/testing.d.ts +23 -0
- package/dist/api-wrapper/transport.d.ts +4 -5
- package/dist/api-wrapper/types.d.ts +103 -18
- package/dist/array/index.d.ts +50 -4
- package/dist/cache/createCache.d.ts +31 -0
- package/dist/cache/index.d.ts +19 -2
- package/dist/color/contrast.d.ts +40 -0
- package/dist/color/index.d.ts +2 -0
- package/dist/{math → color}/metricsSeriesColors.d.ts +14 -7
- package/dist/cool-console-log/index.d.ts +34 -5
- package/dist/crypto/encoding.d.ts +18 -0
- package/dist/crypto/hash.d.ts +33 -0
- package/dist/crypto/index.d.ts +4 -0
- package/dist/crypto/sealer.d.ts +25 -0
- package/dist/crypto/webhook.d.ts +20 -0
- package/dist/date/index.d.ts +46 -2
- package/dist/emitter/index.d.ts +21 -0
- package/dist/error/index.d.ts +12 -0
- package/dist/format/index.d.ts +102 -0
- package/dist/function/index.d.ts +60 -0
- package/dist/guard/index.d.ts +20 -0
- package/dist/i18n/index.d.ts +37 -0
- package/dist/index-0zvtcbzy.js +224 -0
- package/dist/index-181e6vfw.js +29 -0
- package/dist/index-8r2vdzjg.js +87 -0
- package/dist/index-hy7kbb99.js +157 -0
- package/dist/{src/secrets/infisical.js → index-j1ybktbr.js} +500 -538
- package/dist/index-k9yx6bne.js +89 -0
- package/dist/index-kj1phgrx.js +169 -0
- package/dist/index-mnkcfyx8.js +78 -0
- package/dist/{p-queue/index.js → index-pwt0dz63.js} +7 -11
- package/dist/index-rp5pqghz.js +157 -0
- package/dist/index-srt06xd2.js +15 -0
- package/dist/index-wjyb9bsd.js +263 -0
- package/dist/index.d.ts +7 -7
- package/dist/jwt/index.d.ts +50 -2
- package/dist/math/index.d.ts +2 -2
- package/dist/math/linearRegressionTrend.d.ts +14 -1
- package/dist/math/scale.d.ts +8 -0
- package/dist/object/index.d.ts +19 -0
- package/dist/p-queue/index.d.ts +1 -1
- package/dist/promise/index.d.ts +97 -2
- package/dist/sitemap/index.d.ts +48 -6
- package/dist/src/api-wrapper/index.js +60 -1130
- package/dist/src/api-wrapper/testing.js +20 -0
- package/dist/{cool-console-log → src/cool-console-log}/index.js +80 -33
- package/dist/src/crypto/index.js +195 -0
- package/dist/src/format/index.js +211 -0
- package/dist/src/i18n/index.js +36 -0
- package/dist/src/index.js +739 -3145
- package/dist/src/jwt/index.js +138 -92
- package/dist/src/p-queue/index.js +9 -437
- package/dist/src/secrets/index.js +44 -1428
- package/dist/src/secrets/vite.js +6 -39
- package/dist/src/secrets/worker.js +10 -273
- package/dist/src/sitemap/index.js +183 -511
- package/dist/src/storage/index.js +119 -0
- package/dist/src/zod/index.js +340 -167
- package/dist/state/index.d.ts +21 -0
- package/dist/storage/index.d.ts +58 -0
- package/dist/string/index.d.ts +75 -2
- package/dist/typedswitch/index.d.ts +31 -24
- package/dist/types/index.d.ts +11 -0
- package/dist/url/index.d.ts +50 -1
- package/dist/zod/index.d.ts +32 -3
- package/dist/zod/introspect.d.ts +19 -0
- package/dist/zod/jsonToZodSchemaMapper.d.ts +3 -1
- package/dist/zod/zodMinMaxFilter.d.ts +22 -3
- package/dist/zod/zodToTypeString.d.ts +8 -3
- package/package.json +78 -7
- package/src/api-wrapper/actions-impl.ts +11 -72
- package/src/api-wrapper/builders.ts +32 -25
- package/src/api-wrapper/create-client.ts +171 -41
- package/src/api-wrapper/describe.ts +157 -0
- package/src/api-wrapper/endpoint.ts +26 -0
- package/src/api-wrapper/error-reporting.ts +7 -1
- package/src/api-wrapper/errors.ts +107 -2
- package/src/api-wrapper/headers.ts +11 -6
- package/src/api-wrapper/index.ts +5 -0
- package/src/api-wrapper/otel.ts +139 -0
- package/src/api-wrapper/paginate.ts +28 -0
- package/src/api-wrapper/redaction.ts +42 -0
- package/src/api-wrapper/shared.ts +21 -2
- package/src/api-wrapper/testing.ts +48 -0
- package/src/api-wrapper/transport.ts +41 -43
- package/src/api-wrapper/types.ts +185 -18
- package/src/array/index.ts +195 -42
- package/src/cache/createCache.ts +157 -0
- package/src/cache/index.ts +132 -147
- package/src/color/contrast.ts +163 -0
- package/src/color/index.ts +22 -0
- package/src/{math → color}/metricsSeriesColors.ts +47 -49
- package/src/cool-console-log/index.ts +128 -41
- package/src/crypto/encoding.ts +83 -0
- package/src/crypto/hash.ts +170 -0
- package/src/crypto/index.ts +26 -0
- package/src/crypto/sealer.ts +168 -0
- package/src/crypto/webhook.ts +86 -0
- package/src/date/index.ts +109 -34
- package/src/emitter/index.ts +82 -0
- package/src/error/index.ts +63 -0
- package/src/format/index.ts +389 -0
- package/src/function/index.ts +201 -0
- package/src/guard/index.ts +80 -0
- package/src/i18n/index.ts +125 -0
- package/src/index.ts +11 -7
- package/src/jwt/index.ts +199 -125
- package/src/math/index.ts +5 -10
- package/src/math/linearRegressionTrend.ts +33 -8
- package/src/math/scale.ts +17 -0
- package/src/object/index.ts +99 -0
- package/src/p-queue/index.ts +2 -0
- package/src/promise/index.ts +304 -9
- package/src/sitemap/index.ts +293 -130
- package/src/state/index.ts +133 -81
- package/src/storage/index.ts +187 -0
- package/src/string/index.ts +261 -23
- package/src/typedswitch/index.ts +58 -38
- package/src/types/index.ts +14 -0
- package/src/url/index.ts +146 -1
- package/src/zod/index.ts +105 -9
- package/src/zod/introspect.ts +103 -0
- package/src/zod/jsonToZodSchemaMapper.ts +44 -21
- package/src/zod/zodMinMaxFilter.ts +37 -13
- package/src/zod/zodToTypeString.ts +184 -181
- package/dist/api-wrapper/index.js +0 -250
- package/dist/gzip/index.d.ts +0 -2
- package/dist/gzip/index.js +0 -16
- package/dist/index.js +0 -1823
- package/dist/jwt/index.js +0 -19
- package/dist/sitemap/index.js +0 -552
- package/dist/src/gzip/index.js +0 -16
- package/dist/src/secrets/loaders.js +0 -1529
- package/dist/zod/index.js +0 -210
package/dist/src/jwt/index.js
CHANGED
|
@@ -1,110 +1,156 @@
|
|
|
1
|
+
import {
|
|
2
|
+
utf8Encode2,
|
|
3
|
+
utf8Decode2,
|
|
4
|
+
encodeBase64Url2,
|
|
5
|
+
decodeBase64Url2,
|
|
6
|
+
getSubtleCrypto
|
|
7
|
+
} from "../../index-rp5pqghz.js";
|
|
8
|
+
import {
|
|
9
|
+
isRecord2
|
|
10
|
+
} from "../../index-8r2vdzjg.js";
|
|
11
|
+
|
|
1
12
|
// src/jwt/index.ts
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
iat: Math.floor(Date.now() / 1000)
|
|
10
|
-
});
|
|
11
|
-
const data = `${header}.${body}`;
|
|
12
|
-
const signature = await sign(data, secret);
|
|
13
|
-
return `${data}.${encodeBase64Url(signature)}`;
|
|
13
|
+
class JwtVerifyError extends Error {
|
|
14
|
+
code;
|
|
15
|
+
constructor(code) {
|
|
16
|
+
super(`JWT verification failed: ${code}`);
|
|
17
|
+
this.name = "JwtVerifyError";
|
|
18
|
+
this.code = code;
|
|
19
|
+
}
|
|
14
20
|
}
|
|
15
|
-
|
|
21
|
+
var HASHES = {
|
|
22
|
+
HS256: "SHA-256",
|
|
23
|
+
HS384: "SHA-384",
|
|
24
|
+
HS512: "SHA-512"
|
|
25
|
+
};
|
|
26
|
+
var ALGORITHMS = Object.keys(HASHES);
|
|
27
|
+
var BASE64URL = /^[A-Za-z0-9_-]*$/;
|
|
28
|
+
var KEY_CACHE_LIMIT = 100;
|
|
29
|
+
async function jwtSign(payload, secret, options = {}) {
|
|
30
|
+
const {
|
|
31
|
+
expiresIn,
|
|
32
|
+
notBefore,
|
|
33
|
+
issuer,
|
|
34
|
+
audience,
|
|
35
|
+
algorithm = "HS256"
|
|
36
|
+
} = options;
|
|
37
|
+
if (!Object.hasOwn(HASHES, algorithm)) {
|
|
38
|
+
throw new TypeError(`Unsupported JWT algorithm: ${algorithm}`);
|
|
39
|
+
}
|
|
40
|
+
const now = Math.floor(Date.now() / 1000);
|
|
41
|
+
const claims = { ...payload, iat: now };
|
|
42
|
+
if (expiresIn !== undefined)
|
|
43
|
+
claims.exp = now + toSeconds(expiresIn);
|
|
44
|
+
if (notBefore !== undefined)
|
|
45
|
+
claims.nbf = now + toSeconds(notBefore);
|
|
46
|
+
if (issuer !== undefined)
|
|
47
|
+
claims.iss = issuer;
|
|
48
|
+
if (audience !== undefined)
|
|
49
|
+
claims.aud = audience;
|
|
50
|
+
const data = `${encodeJson({ alg: algorithm, typ: "JWT" })}.${encodeJson(claims)}`;
|
|
51
|
+
const key = await getKey(secret, algorithm, "sign");
|
|
52
|
+
const signature = await getSubtleCrypto().sign("HMAC", key, utf8Encode2(data));
|
|
53
|
+
return `${data}.${encodeBase64Url2(signature)}`;
|
|
54
|
+
}
|
|
55
|
+
async function jwtVerify(token, secret, options) {
|
|
16
56
|
try {
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
return null;
|
|
20
|
-
const [encodedHeader, encodedPayload, encodedSignature] = parts;
|
|
21
|
-
const header = decodeJson(encodedHeader);
|
|
22
|
-
if (!isJwtHeader(header))
|
|
23
|
-
return null;
|
|
24
|
-
const data = `${encodedHeader}.${encodedPayload}`;
|
|
25
|
-
const signature = decodeBase64Url(encodedSignature);
|
|
26
|
-
const isValid = await verify(data, signature, secret);
|
|
27
|
-
if (!isValid)
|
|
28
|
-
return null;
|
|
29
|
-
const payload = decodeJson(encodedPayload);
|
|
30
|
-
if (!isJwtPayload(payload) || !isTimeValid(payload))
|
|
31
|
-
return null;
|
|
32
|
-
return payload;
|
|
57
|
+
const result = await jwtVerifyResult(token, secret, options);
|
|
58
|
+
return result.ok ? result.data : null;
|
|
33
59
|
} catch (_error) {
|
|
34
60
|
return null;
|
|
35
61
|
}
|
|
36
62
|
}
|
|
37
|
-
async function
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
async function getKey(secret, usages) {
|
|
46
|
-
return await getSubtleCrypto().importKey("raw", encodeText(secret), {
|
|
47
|
-
name: "HMAC",
|
|
48
|
-
hash: "SHA-256"
|
|
49
|
-
}, false, usages);
|
|
50
|
-
}
|
|
51
|
-
function encodeJson(value) {
|
|
52
|
-
return encodeBase64Url(encodeText(JSON.stringify(value)));
|
|
53
|
-
}
|
|
54
|
-
function decodeJson(value) {
|
|
55
|
-
return JSON.parse(textDecoder.decode(decodeBase64Url(value)));
|
|
56
|
-
}
|
|
57
|
-
function encodeBase64Url(value) {
|
|
58
|
-
const bytes = value instanceof ArrayBuffer ? new Uint8Array(value) : value;
|
|
59
|
-
return btoa(bytesToBinaryString(bytes)).replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/, "");
|
|
60
|
-
}
|
|
61
|
-
function decodeBase64Url(value) {
|
|
62
|
-
const base64 = value.replaceAll("-", "+").replaceAll("_", "/");
|
|
63
|
-
const padded = base64.padEnd(Math.ceil(base64.length / 4) * 4, "=");
|
|
64
|
-
return binaryStringToBytes(atob(padded)).buffer;
|
|
65
|
-
}
|
|
66
|
-
function encodeText(value) {
|
|
67
|
-
return toArrayBuffer(textEncoder.encode(value));
|
|
68
|
-
}
|
|
69
|
-
function toArrayBuffer(bytes) {
|
|
70
|
-
return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
|
71
|
-
}
|
|
72
|
-
function bytesToBinaryString(bytes) {
|
|
73
|
-
let binary = "";
|
|
74
|
-
for (let offset = 0;offset < bytes.length; offset += BYTE_CHUNK_SIZE) {
|
|
75
|
-
binary += String.fromCharCode(...bytes.subarray(offset, offset + BYTE_CHUNK_SIZE));
|
|
63
|
+
async function jwtVerifyResult(token, secret, options = {}) {
|
|
64
|
+
const { leewaySec = 0, issuer, audience, algorithms = ALGORITHMS } = options;
|
|
65
|
+
const decoded = jwtDecode(token);
|
|
66
|
+
if (!decoded)
|
|
67
|
+
return fail("malformed");
|
|
68
|
+
const { alg } = decoded.header;
|
|
69
|
+
if (!Object.hasOwn(HASHES, alg) || !algorithms.includes(alg)) {
|
|
70
|
+
return fail("unsupported_alg");
|
|
76
71
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
72
|
+
const lastDot = token.lastIndexOf(".");
|
|
73
|
+
let signature;
|
|
74
|
+
try {
|
|
75
|
+
signature = decodeBase64Url2(token.slice(lastDot + 1));
|
|
76
|
+
} catch (_error) {
|
|
77
|
+
return fail("malformed");
|
|
78
|
+
}
|
|
79
|
+
const key = await getKey(secret, alg, "verify");
|
|
80
|
+
const isValid = await getSubtleCrypto().verify("HMAC", key, signature, utf8Encode2(token.slice(0, lastDot)));
|
|
81
|
+
if (!isValid)
|
|
82
|
+
return fail("invalid_signature");
|
|
83
|
+
const { payload } = decoded;
|
|
84
|
+
const { exp, nbf } = payload;
|
|
85
|
+
if (exp !== undefined && typeof exp !== "number")
|
|
86
|
+
return fail("malformed");
|
|
87
|
+
if (nbf !== undefined && typeof nbf !== "number")
|
|
88
|
+
return fail("malformed");
|
|
89
|
+
const now = Math.floor(Date.now() / 1000);
|
|
90
|
+
if (exp !== undefined && now >= exp + leewaySec)
|
|
91
|
+
return fail("expired");
|
|
92
|
+
if (nbf !== undefined && now + leewaySec < nbf)
|
|
93
|
+
return fail("not_yet_valid");
|
|
94
|
+
if (issuer !== undefined && payload.iss !== issuer) {
|
|
95
|
+
return fail("invalid_issuer");
|
|
83
96
|
}
|
|
84
|
-
|
|
97
|
+
if (audience !== undefined) {
|
|
98
|
+
const { aud } = payload;
|
|
99
|
+
const matches = Array.isArray(aud) ? aud.includes(audience) : aud === audience;
|
|
100
|
+
if (!matches)
|
|
101
|
+
return fail("invalid_audience");
|
|
102
|
+
}
|
|
103
|
+
return { ok: true, data: payload, error: null };
|
|
85
104
|
}
|
|
86
|
-
function
|
|
87
|
-
const
|
|
88
|
-
if (!
|
|
89
|
-
|
|
105
|
+
function jwtDecode(token) {
|
|
106
|
+
const parts = token.split(".");
|
|
107
|
+
if (parts.length !== 3 || !parts.every((part) => BASE64URL.test(part))) {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
try {
|
|
111
|
+
const header = decodeJson(parts[0]);
|
|
112
|
+
const payload = decodeJson(parts[1]);
|
|
113
|
+
if (!isRecord2(header) || typeof header.alg !== "string")
|
|
114
|
+
return null;
|
|
115
|
+
if (!isRecord2(payload))
|
|
116
|
+
return null;
|
|
117
|
+
return { header, payload };
|
|
118
|
+
} catch (_error) {
|
|
119
|
+
return null;
|
|
90
120
|
}
|
|
91
|
-
return subtle;
|
|
92
121
|
}
|
|
93
|
-
function
|
|
94
|
-
return
|
|
122
|
+
function fail(code) {
|
|
123
|
+
return { ok: false, data: null, error: new JwtVerifyError(code) };
|
|
95
124
|
}
|
|
96
|
-
function
|
|
97
|
-
|
|
125
|
+
function toSeconds(value) {
|
|
126
|
+
if (!Number.isFinite(value)) {
|
|
127
|
+
throw new TypeError(`Expected a finite number of seconds, got ${value}`);
|
|
128
|
+
}
|
|
129
|
+
return Math.floor(value);
|
|
130
|
+
}
|
|
131
|
+
var keyCache = new Map;
|
|
132
|
+
function getKey(secret, algorithm, usage) {
|
|
133
|
+
const cacheKey = `${algorithm}:${usage}:${secret}`;
|
|
134
|
+
const cached = keyCache.get(cacheKey);
|
|
135
|
+
if (cached)
|
|
136
|
+
return cached;
|
|
137
|
+
if (keyCache.size >= KEY_CACHE_LIMIT)
|
|
138
|
+
keyCache.clear();
|
|
139
|
+
const key = getSubtleCrypto().importKey("raw", utf8Encode2(secret), { name: "HMAC", hash: HASHES[algorithm] }, false, [usage]);
|
|
140
|
+
keyCache.set(cacheKey, key);
|
|
141
|
+
key.catch(() => keyCache.delete(cacheKey));
|
|
142
|
+
return key;
|
|
98
143
|
}
|
|
99
|
-
function
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
return false;
|
|
105
|
-
return true;
|
|
144
|
+
function encodeJson(value) {
|
|
145
|
+
return encodeBase64Url2(utf8Encode2(JSON.stringify(value)));
|
|
146
|
+
}
|
|
147
|
+
function decodeJson(value) {
|
|
148
|
+
return JSON.parse(utf8Decode2(decodeBase64Url2(value)));
|
|
106
149
|
}
|
|
107
150
|
export {
|
|
151
|
+
JwtVerifyError,
|
|
152
|
+
jwtDecode,
|
|
108
153
|
jwtSign,
|
|
109
|
-
jwtVerify
|
|
154
|
+
jwtVerify,
|
|
155
|
+
jwtVerifyResult
|
|
110
156
|
};
|
|
@@ -1,439 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
}
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
class PriorityQueue {
|
|
10
|
-
#queue = [];
|
|
11
|
-
enqueue(run, options = {}) {
|
|
12
|
-
const element = {
|
|
13
|
-
priority: options.priority ?? 0,
|
|
14
|
-
id: options.id,
|
|
15
|
-
run
|
|
16
|
-
};
|
|
17
|
-
const index = this.#queue.findIndex((item) => item.priority < element.priority);
|
|
18
|
-
if (index === -1) {
|
|
19
|
-
this.#queue.push(element);
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
this.#queue.splice(index, 0, element);
|
|
23
|
-
}
|
|
24
|
-
dequeue() {
|
|
25
|
-
return this.#queue.shift()?.run;
|
|
26
|
-
}
|
|
27
|
-
filter(options) {
|
|
28
|
-
return this.#queue.filter((item) => Object.entries(options).every(([key, value]) => item[key] === value)).map((item) => item.run);
|
|
29
|
-
}
|
|
30
|
-
setPriority(id, priority) {
|
|
31
|
-
const index = this.#queue.findIndex((item) => item.id === id);
|
|
32
|
-
if (index === -1) {
|
|
33
|
-
throw new ReferenceError(`No promise function with the id "${id}" exists in the queue.`);
|
|
34
|
-
}
|
|
35
|
-
const [item] = this.#queue.splice(index, 1);
|
|
36
|
-
this.enqueue(item.run, { ...item, priority });
|
|
37
|
-
}
|
|
38
|
-
remove(id) {
|
|
39
|
-
const index = this.#queue.findIndex((item) => item.id === id);
|
|
40
|
-
if (index !== -1) {
|
|
41
|
-
this.#queue.splice(index, 1);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
get size() {
|
|
45
|
-
return this.#queue.length;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
class PQueue {
|
|
50
|
-
#listeners = new Map;
|
|
51
|
-
#queue;
|
|
52
|
-
#queueClass;
|
|
53
|
-
#pending = 0;
|
|
54
|
-
#concurrency;
|
|
55
|
-
#isPaused;
|
|
56
|
-
#intervalCap;
|
|
57
|
-
#interval;
|
|
58
|
-
#carryoverIntervalCount;
|
|
59
|
-
#strict;
|
|
60
|
-
#intervalCount = 0;
|
|
61
|
-
#intervalEnd = 0;
|
|
62
|
-
#intervalTimer;
|
|
63
|
-
#strictTimestamps = [];
|
|
64
|
-
#rateLimited = false;
|
|
65
|
-
#idCounter = 1n;
|
|
66
|
-
#runningTasks = new Map;
|
|
67
|
-
timeout;
|
|
68
|
-
constructor(options = {}) {
|
|
69
|
-
const {
|
|
70
|
-
concurrency = Number.POSITIVE_INFINITY,
|
|
71
|
-
autoStart = true,
|
|
72
|
-
queueClass = PriorityQueue,
|
|
73
|
-
intervalCap = Number.POSITIVE_INFINITY,
|
|
74
|
-
interval = 0,
|
|
75
|
-
carryoverIntervalCount,
|
|
76
|
-
carryoverConcurrencyCount,
|
|
77
|
-
strict = false,
|
|
78
|
-
timeout
|
|
79
|
-
} = options;
|
|
80
|
-
if (!(typeof concurrency === "number" && concurrency >= 1)) {
|
|
81
|
-
throw new TypeError("Expected `concurrency` to be a number from 1 and up");
|
|
82
|
-
}
|
|
83
|
-
if (!(typeof intervalCap === "number" && intervalCap >= 1)) {
|
|
84
|
-
throw new TypeError("Expected `intervalCap` to be a number from 1 and up");
|
|
85
|
-
}
|
|
86
|
-
if (!(typeof interval === "number" && Number.isFinite(interval) && interval >= 0)) {
|
|
87
|
-
throw new TypeError("Expected `interval` to be a finite number >= 0");
|
|
88
|
-
}
|
|
89
|
-
if (strict && interval === 0) {
|
|
90
|
-
throw new TypeError("The `strict` option requires a non-zero `interval`");
|
|
91
|
-
}
|
|
92
|
-
if (strict && intervalCap === Number.POSITIVE_INFINITY) {
|
|
93
|
-
throw new TypeError("The `strict` option requires a finite `intervalCap`");
|
|
94
|
-
}
|
|
95
|
-
if (timeout !== undefined && !(Number.isFinite(timeout) && timeout > 0)) {
|
|
96
|
-
throw new TypeError("Expected `timeout` to be a positive finite number");
|
|
97
|
-
}
|
|
98
|
-
this.#queueClass = queueClass;
|
|
99
|
-
this.#queue = new queueClass;
|
|
100
|
-
this.#concurrency = concurrency;
|
|
101
|
-
this.#isPaused = !autoStart;
|
|
102
|
-
this.#intervalCap = intervalCap;
|
|
103
|
-
this.#interval = interval;
|
|
104
|
-
this.#carryoverIntervalCount = carryoverIntervalCount ?? carryoverConcurrencyCount ?? false;
|
|
105
|
-
this.#strict = strict;
|
|
106
|
-
this.timeout = timeout;
|
|
107
|
-
}
|
|
108
|
-
on(event, listener) {
|
|
109
|
-
const listeners = this.#listeners.get(event) ?? new Set;
|
|
110
|
-
listeners.add(listener);
|
|
111
|
-
this.#listeners.set(event, listeners);
|
|
112
|
-
return this;
|
|
113
|
-
}
|
|
114
|
-
off(event, listener) {
|
|
115
|
-
this.#listeners.get(event)?.delete(listener);
|
|
116
|
-
return this;
|
|
117
|
-
}
|
|
118
|
-
emit(event, ...arguments_) {
|
|
119
|
-
const listeners = this.#listeners.get(event);
|
|
120
|
-
if (!listeners || listeners.size === 0) {
|
|
121
|
-
return false;
|
|
122
|
-
}
|
|
123
|
-
for (const listener of [...listeners]) {
|
|
124
|
-
listener(...arguments_);
|
|
125
|
-
}
|
|
126
|
-
return true;
|
|
127
|
-
}
|
|
128
|
-
get concurrency() {
|
|
129
|
-
return this.#concurrency;
|
|
130
|
-
}
|
|
131
|
-
set concurrency(newConcurrency) {
|
|
132
|
-
if (!(typeof newConcurrency === "number" && newConcurrency >= 1)) {
|
|
133
|
-
throw new TypeError("Expected `concurrency` to be a number from 1 and up");
|
|
134
|
-
}
|
|
135
|
-
this.#concurrency = newConcurrency;
|
|
136
|
-
this.#processQueue();
|
|
137
|
-
}
|
|
138
|
-
get size() {
|
|
139
|
-
return this.#queue.size;
|
|
140
|
-
}
|
|
141
|
-
sizeBy(options) {
|
|
142
|
-
return this.#queue.filter(options).length;
|
|
143
|
-
}
|
|
144
|
-
get pending() {
|
|
145
|
-
return this.#pending;
|
|
146
|
-
}
|
|
147
|
-
get isPaused() {
|
|
148
|
-
return this.#isPaused;
|
|
149
|
-
}
|
|
150
|
-
get isRateLimited() {
|
|
151
|
-
this.#updateRateLimitState();
|
|
152
|
-
return this.#rateLimited;
|
|
153
|
-
}
|
|
154
|
-
get isSaturated() {
|
|
155
|
-
return this.#pending >= this.#concurrency && this.#queue.size > 0 || this.isRateLimited && this.#queue.size > 0;
|
|
156
|
-
}
|
|
157
|
-
get runningTasks() {
|
|
158
|
-
return [...this.#runningTasks.values()].map((task) => ({ ...task }));
|
|
159
|
-
}
|
|
160
|
-
setPriority(id, priority) {
|
|
161
|
-
if (typeof priority !== "number" || !Number.isFinite(priority)) {
|
|
162
|
-
throw new TypeError("Expected `priority` to be a finite number");
|
|
163
|
-
}
|
|
164
|
-
this.#queue.setPriority(id, priority);
|
|
165
|
-
}
|
|
166
|
-
async add(function_, options = {}) {
|
|
167
|
-
const taskOptions = {
|
|
168
|
-
timeout: this.timeout,
|
|
169
|
-
...options,
|
|
170
|
-
id: options.id ?? (this.#idCounter++).toString()
|
|
171
|
-
};
|
|
172
|
-
return new Promise((resolve, reject) => {
|
|
173
|
-
const taskSymbol = Symbol(taskOptions.id);
|
|
174
|
-
let didRun = false;
|
|
175
|
-
let queuedAbortHandler;
|
|
176
|
-
let runningAbortHandler;
|
|
177
|
-
const cleanupQueuedAbort = () => {
|
|
178
|
-
if (queuedAbortHandler) {
|
|
179
|
-
taskOptions.signal?.removeEventListener("abort", queuedAbortHandler);
|
|
180
|
-
queuedAbortHandler = undefined;
|
|
181
|
-
}
|
|
182
|
-
};
|
|
183
|
-
const run = async () => {
|
|
184
|
-
didRun = true;
|
|
185
|
-
cleanupQueuedAbort();
|
|
186
|
-
this.#pending++;
|
|
187
|
-
this.#runningTasks.set(taskSymbol, {
|
|
188
|
-
id: taskOptions.id,
|
|
189
|
-
priority: taskOptions.priority ?? 0,
|
|
190
|
-
startTime: Date.now(),
|
|
191
|
-
timeout: taskOptions.timeout
|
|
192
|
-
});
|
|
193
|
-
try {
|
|
194
|
-
taskOptions.signal?.throwIfAborted();
|
|
195
|
-
const operation = Promise.resolve(function_({ signal: taskOptions.signal }));
|
|
196
|
-
const result = await this.#withTimeoutAndAbort(operation, taskOptions, (listener) => {
|
|
197
|
-
runningAbortHandler = listener;
|
|
198
|
-
});
|
|
199
|
-
resolve(result);
|
|
200
|
-
this.emit("completed", result);
|
|
201
|
-
} catch (error) {
|
|
202
|
-
reject(error);
|
|
203
|
-
this.emit("error", error);
|
|
204
|
-
} finally {
|
|
205
|
-
if (runningAbortHandler) {
|
|
206
|
-
taskOptions.signal?.removeEventListener("abort", runningAbortHandler);
|
|
207
|
-
}
|
|
208
|
-
this.#runningTasks.delete(taskSymbol);
|
|
209
|
-
queueMicrotask(() => {
|
|
210
|
-
this.#next();
|
|
211
|
-
});
|
|
212
|
-
}
|
|
213
|
-
};
|
|
214
|
-
if (taskOptions.signal?.aborted) {
|
|
215
|
-
reject(taskOptions.signal.reason);
|
|
216
|
-
return;
|
|
217
|
-
}
|
|
218
|
-
if (taskOptions.signal) {
|
|
219
|
-
queuedAbortHandler = () => {
|
|
220
|
-
if (didRun) {
|
|
221
|
-
return;
|
|
222
|
-
}
|
|
223
|
-
this.#queue.remove?.(taskOptions.id);
|
|
224
|
-
reject(taskOptions.signal?.reason);
|
|
225
|
-
this.emit("next");
|
|
226
|
-
this.#tryToStartAnother();
|
|
227
|
-
};
|
|
228
|
-
taskOptions.signal.addEventListener("abort", queuedAbortHandler, {
|
|
229
|
-
once: true
|
|
230
|
-
});
|
|
231
|
-
}
|
|
232
|
-
this.#queue.enqueue(run, taskOptions);
|
|
233
|
-
this.emit("add");
|
|
234
|
-
this.#processQueue();
|
|
235
|
-
});
|
|
236
|
-
}
|
|
237
|
-
async addAll(functions, options) {
|
|
238
|
-
return Promise.all(functions.map(async (function_) => this.add(function_, options)));
|
|
239
|
-
}
|
|
240
|
-
start() {
|
|
241
|
-
if (!this.#isPaused) {
|
|
242
|
-
return this;
|
|
243
|
-
}
|
|
244
|
-
this.#isPaused = false;
|
|
245
|
-
this.#processQueue();
|
|
246
|
-
return this;
|
|
247
|
-
}
|
|
248
|
-
pause() {
|
|
249
|
-
this.#isPaused = true;
|
|
250
|
-
}
|
|
251
|
-
clear() {
|
|
252
|
-
this.#queue = new this.#queueClass;
|
|
253
|
-
this.#clearIntervalTimer();
|
|
254
|
-
this.#updateRateLimitState();
|
|
255
|
-
this.emit("empty");
|
|
256
|
-
if (this.#pending === 0) {
|
|
257
|
-
this.emit("idle");
|
|
258
|
-
}
|
|
259
|
-
this.emit("next");
|
|
260
|
-
}
|
|
261
|
-
async onEmpty() {
|
|
262
|
-
if (this.#queue.size === 0) {
|
|
263
|
-
return;
|
|
264
|
-
}
|
|
265
|
-
await this.#onEvent("empty");
|
|
266
|
-
}
|
|
267
|
-
async onIdle() {
|
|
268
|
-
if (this.#queue.size === 0 && this.#pending === 0) {
|
|
269
|
-
return;
|
|
270
|
-
}
|
|
271
|
-
await this.#onEvent("idle");
|
|
272
|
-
}
|
|
273
|
-
async onPendingZero() {
|
|
274
|
-
if (this.#pending === 0) {
|
|
275
|
-
return;
|
|
276
|
-
}
|
|
277
|
-
await this.#onEvent("pendingZero");
|
|
278
|
-
}
|
|
279
|
-
async onRateLimit() {
|
|
280
|
-
if (this.isRateLimited) {
|
|
281
|
-
return;
|
|
282
|
-
}
|
|
283
|
-
await this.#onEvent("rateLimit");
|
|
284
|
-
}
|
|
285
|
-
async onRateLimitCleared() {
|
|
286
|
-
if (!this.isRateLimited) {
|
|
287
|
-
return;
|
|
288
|
-
}
|
|
289
|
-
await this.#onEvent("rateLimitCleared");
|
|
290
|
-
}
|
|
291
|
-
onError() {
|
|
292
|
-
return new Promise((_resolve, reject) => {
|
|
293
|
-
const listener = (error) => {
|
|
294
|
-
this.off("error", listener);
|
|
295
|
-
reject(error);
|
|
296
|
-
};
|
|
297
|
-
this.on("error", listener);
|
|
298
|
-
});
|
|
299
|
-
}
|
|
300
|
-
async onSizeLessThan(limit) {
|
|
301
|
-
if (this.#queue.size < limit) {
|
|
302
|
-
return;
|
|
303
|
-
}
|
|
304
|
-
await this.#onEvent("next", () => this.#queue.size < limit);
|
|
305
|
-
}
|
|
306
|
-
async#onEvent(event, filter) {
|
|
307
|
-
return new Promise((resolve) => {
|
|
308
|
-
const listener = () => {
|
|
309
|
-
if (filter && !filter()) {
|
|
310
|
-
return;
|
|
311
|
-
}
|
|
312
|
-
this.off(event, listener);
|
|
313
|
-
resolve();
|
|
314
|
-
};
|
|
315
|
-
this.on(event, listener);
|
|
316
|
-
});
|
|
317
|
-
}
|
|
318
|
-
async#withTimeoutAndAbort(operation, options, setAbortListener) {
|
|
319
|
-
const races = [operation];
|
|
320
|
-
let timeoutId;
|
|
321
|
-
if (options.timeout) {
|
|
322
|
-
races.push(new Promise((_resolve, reject) => {
|
|
323
|
-
timeoutId = setTimeout(() => {
|
|
324
|
-
reject(new TimeoutError(`Task timed out after ${options.timeout}ms`));
|
|
325
|
-
}, options.timeout);
|
|
326
|
-
}));
|
|
327
|
-
}
|
|
328
|
-
if (options.signal) {
|
|
329
|
-
races.push(new Promise((_resolve, reject) => {
|
|
330
|
-
const listener = () => {
|
|
331
|
-
reject(options.signal?.reason);
|
|
332
|
-
};
|
|
333
|
-
setAbortListener(listener);
|
|
334
|
-
options.signal?.addEventListener("abort", listener, { once: true });
|
|
335
|
-
}));
|
|
336
|
-
}
|
|
337
|
-
try {
|
|
338
|
-
return await Promise.race(races);
|
|
339
|
-
} finally {
|
|
340
|
-
if (timeoutId) {
|
|
341
|
-
clearTimeout(timeoutId);
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
#next() {
|
|
346
|
-
this.#pending--;
|
|
347
|
-
if (this.#pending === 0) {
|
|
348
|
-
this.emit("pendingZero");
|
|
349
|
-
}
|
|
350
|
-
this.#processQueue();
|
|
351
|
-
this.emit("next");
|
|
352
|
-
}
|
|
353
|
-
#processQueue() {
|
|
354
|
-
while (this.#tryToStartAnother()) {}
|
|
355
|
-
this.#updateRateLimitState();
|
|
356
|
-
}
|
|
357
|
-
#tryToStartAnother() {
|
|
358
|
-
if (this.#queue.size === 0) {
|
|
359
|
-
this.#clearIntervalTimer();
|
|
360
|
-
this.emit("empty");
|
|
361
|
-
if (this.#pending === 0) {
|
|
362
|
-
this.emit("idle");
|
|
363
|
-
}
|
|
364
|
-
return false;
|
|
365
|
-
}
|
|
366
|
-
if (this.#isPaused || this.#pending >= this.#concurrency || !this.#doesIntervalAllowAnother()) {
|
|
367
|
-
return false;
|
|
368
|
-
}
|
|
369
|
-
const job = this.#queue.dequeue();
|
|
370
|
-
if (!job) {
|
|
371
|
-
return false;
|
|
372
|
-
}
|
|
373
|
-
this.#consumeIntervalSlot();
|
|
374
|
-
this.emit("active");
|
|
375
|
-
job();
|
|
376
|
-
return true;
|
|
377
|
-
}
|
|
378
|
-
#doesIntervalAllowAnother() {
|
|
379
|
-
if (this.#intervalCap === Number.POSITIVE_INFINITY || this.#interval === 0) {
|
|
380
|
-
return true;
|
|
381
|
-
}
|
|
382
|
-
const now = Date.now();
|
|
383
|
-
if (this.#strict) {
|
|
384
|
-
this.#strictTimestamps = this.#strictTimestamps.filter((timestamp) => now - timestamp < this.#interval);
|
|
385
|
-
if (this.#strictTimestamps.length < this.#intervalCap) {
|
|
386
|
-
return true;
|
|
387
|
-
}
|
|
388
|
-
this.#scheduleIntervalResume(this.#interval - (now - this.#strictTimestamps[0]));
|
|
389
|
-
return false;
|
|
390
|
-
}
|
|
391
|
-
if (this.#intervalEnd === 0 || now >= this.#intervalEnd) {
|
|
392
|
-
this.#intervalCount = this.#carryoverIntervalCount ? this.#pending : 0;
|
|
393
|
-
this.#intervalEnd = now + this.#interval;
|
|
394
|
-
}
|
|
395
|
-
if (this.#intervalCount < this.#intervalCap) {
|
|
396
|
-
return true;
|
|
397
|
-
}
|
|
398
|
-
this.#scheduleIntervalResume(this.#intervalEnd - now);
|
|
399
|
-
return false;
|
|
400
|
-
}
|
|
401
|
-
#consumeIntervalSlot() {
|
|
402
|
-
if (this.#intervalCap === Number.POSITIVE_INFINITY || this.#interval === 0) {
|
|
403
|
-
return;
|
|
404
|
-
}
|
|
405
|
-
if (this.#strict) {
|
|
406
|
-
this.#strictTimestamps.push(Date.now());
|
|
407
|
-
return;
|
|
408
|
-
}
|
|
409
|
-
this.#intervalCount++;
|
|
410
|
-
}
|
|
411
|
-
#scheduleIntervalResume(delay) {
|
|
412
|
-
if (this.#intervalTimer) {
|
|
413
|
-
return;
|
|
414
|
-
}
|
|
415
|
-
this.#intervalTimer = setTimeout(() => {
|
|
416
|
-
this.#intervalTimer = undefined;
|
|
417
|
-
this.#processQueue();
|
|
418
|
-
}, Math.max(0, delay));
|
|
419
|
-
}
|
|
420
|
-
#clearIntervalTimer() {
|
|
421
|
-
if (this.#intervalTimer) {
|
|
422
|
-
clearTimeout(this.#intervalTimer);
|
|
423
|
-
this.#intervalTimer = undefined;
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
#updateRateLimitState() {
|
|
427
|
-
const previous = this.#rateLimited;
|
|
428
|
-
const isRateLimited = this.#queue.size > 0 && this.#intervalCap !== Number.POSITIVE_INFINITY && this.#interval > 0 && !this.#doesIntervalAllowAnother();
|
|
429
|
-
this.#rateLimited = isRateLimited;
|
|
430
|
-
if (previous !== isRateLimited) {
|
|
431
|
-
this.emit(isRateLimited ? "rateLimit" : "rateLimitCleared");
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
TimeoutError2,
|
|
3
|
+
PriorityQueue2,
|
|
4
|
+
PQueue2
|
|
5
|
+
} from "../../index-pwt0dz63.js";
|
|
435
6
|
export {
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
7
|
+
PQueue2 as PQueue,
|
|
8
|
+
PriorityQueue2 as PriorityQueue,
|
|
9
|
+
TimeoutError2 as TimeoutError,
|
|
10
|
+
PQueue2 as default
|
|
439
11
|
};
|