kinetex 1.4.0 → 1.5.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 +170 -46
- package/dist/browser/kinetex.esm.js +29 -29
- package/dist/browser/kinetex.js +1143 -791
- package/dist/browser/kinetex.min.js +29 -29
- package/dist/cjs/aws-sigv4.js +7 -14
- package/dist/cjs/cache.js +9 -0
- package/dist/cjs/circuit-breaker.js +8 -1
- package/dist/cjs/client.js +350 -95
- package/dist/cjs/cookie-parser.js +28 -1
- package/dist/cjs/core.js +227 -5
- package/dist/cjs/dedup.js +93 -10
- package/dist/cjs/digest.js +36 -15
- package/dist/cjs/graphql.js +2 -11
- package/dist/cjs/headers.js +24 -1
- package/dist/cjs/interceptors.js +6 -17
- package/dist/cjs/lifecycle.js +3 -19
- package/dist/cjs/logging.js +12 -29
- package/dist/cjs/progress.js +1 -3
- package/dist/cjs/proxy.js +19 -2
- package/dist/cjs/response.js +202 -75
- package/dist/cjs/socks5.js +5 -0
- package/dist/cjs/utils.js +115 -27
- package/dist/cjs/ws.js +43 -2
- package/dist/esm/aws-sigv4.js +7 -14
- package/dist/esm/aws-sigv4.js.map +1 -1
- package/dist/esm/cache.js +9 -0
- package/dist/esm/cache.js.map +1 -1
- package/dist/esm/circuit-breaker.js +8 -1
- package/dist/esm/circuit-breaker.js.map +1 -1
- package/dist/esm/client.js +350 -95
- package/dist/esm/client.js.map +1 -1
- package/dist/esm/cookie-parser.js +28 -1
- package/dist/esm/cookie-parser.js.map +1 -1
- package/dist/esm/core.js +227 -5
- package/dist/esm/core.js.map +1 -1
- package/dist/esm/dedup.js +93 -10
- package/dist/esm/dedup.js.map +1 -1
- package/dist/esm/digest.js +36 -15
- package/dist/esm/digest.js.map +1 -1
- package/dist/esm/graphql.js +2 -11
- package/dist/esm/graphql.js.map +1 -1
- package/dist/esm/headers.js +24 -1
- package/dist/esm/headers.js.map +1 -1
- package/dist/esm/interceptors.js +6 -17
- package/dist/esm/interceptors.js.map +1 -1
- package/dist/esm/lifecycle.js +3 -19
- package/dist/esm/lifecycle.js.map +1 -1
- package/dist/esm/logging.js +12 -29
- package/dist/esm/logging.js.map +1 -1
- package/dist/esm/progress.js +1 -3
- package/dist/esm/progress.js.map +1 -1
- package/dist/esm/proxy.js +19 -2
- package/dist/esm/proxy.js.map +1 -1
- package/dist/esm/response.js +202 -75
- package/dist/esm/response.js.map +1 -1
- package/dist/esm/socks5.js +5 -0
- package/dist/esm/socks5.js.map +1 -1
- package/dist/esm/types.js.map +1 -1
- package/dist/esm/url.js.map +1 -1
- package/dist/esm/utils.js +115 -27
- package/dist/esm/utils.js.map +1 -1
- package/dist/esm/ws.js +43 -2
- package/dist/esm/ws.js.map +1 -1
- package/dist/types/aws-sigv4.d.ts.map +1 -1
- package/dist/types/cache.d.ts.map +1 -1
- package/dist/types/circuit-breaker.d.ts +1 -1
- package/dist/types/circuit-breaker.d.ts.map +1 -1
- package/dist/types/client.d.ts.map +1 -1
- package/dist/types/cookie-parser.d.ts.map +1 -1
- package/dist/types/core.d.ts +113 -0
- package/dist/types/core.d.ts.map +1 -1
- package/dist/types/dedup.d.ts +38 -3
- package/dist/types/dedup.d.ts.map +1 -1
- package/dist/types/digest.d.ts.map +1 -1
- package/dist/types/graphql.d.ts.map +1 -1
- package/dist/types/headers.d.ts +23 -4
- package/dist/types/headers.d.ts.map +1 -1
- package/dist/types/interceptors.d.ts +24 -38
- package/dist/types/interceptors.d.ts.map +1 -1
- package/dist/types/lifecycle.d.ts.map +1 -1
- package/dist/types/logging.d.ts.map +1 -1
- package/dist/types/progress.d.ts.map +1 -1
- package/dist/types/proxy.d.ts.map +1 -1
- package/dist/types/response.d.ts +42 -1
- package/dist/types/response.d.ts.map +1 -1
- package/dist/types/socks5.d.ts.map +1 -1
- package/dist/types/types.d.ts +105 -23
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/url.d.ts +0 -11
- package/dist/types/url.d.ts.map +1 -1
- package/dist/types/utils.d.ts +69 -1
- package/dist/types/utils.d.ts.map +1 -1
- package/dist/types/ws.d.ts +0 -1
- package/dist/types/ws.d.ts.map +1 -1
- package/package.json +10 -4
package/dist/browser/kinetex.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* kinetex v1.
|
|
1
|
+
/* kinetex v1.5.0 | MIT | https://github.com/kinetexjs/kinetex */
|
|
2
2
|
"use strict";
|
|
3
3
|
var kinetex = (() => {
|
|
4
4
|
var __create = Object.create;
|
|
@@ -422,7 +422,7 @@ var kinetex = (() => {
|
|
|
422
422
|
}
|
|
423
423
|
return false;
|
|
424
424
|
}
|
|
425
|
-
function isSafeURL(url, allowedSchemes = ["http", "https"]) {
|
|
425
|
+
function isSafeURL(url, allowedSchemes = ["http", "https"], options = {}) {
|
|
426
426
|
try {
|
|
427
427
|
const parsed = typeof url === "string" ? new URL(url) : url;
|
|
428
428
|
const scheme = parsed.protocol.replace(/:$/, "").toLowerCase();
|
|
@@ -432,20 +432,22 @@ var kinetex = (() => {
|
|
|
432
432
|
if (FORBIDDEN_SCHEMES.has(scheme)) {
|
|
433
433
|
return false;
|
|
434
434
|
}
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
if (isV6Literal || host.includes(":")) {
|
|
442
|
-
const v6 = parseIPv6Host(host);
|
|
443
|
-
if (v6 === null || isBlockedIPv6(v6))
|
|
444
|
-
return false;
|
|
445
|
-
} else {
|
|
446
|
-
const v4 = parseIPv4Host(host);
|
|
447
|
-
if (v4 !== null && isBlockedIPv4(v4))
|
|
435
|
+
if (!options.allowPrivateNetwork) {
|
|
436
|
+
let host = parsed.hostname.toLowerCase();
|
|
437
|
+
const isV6Literal = host.startsWith("[") && host.endsWith("]");
|
|
438
|
+
if (isV6Literal)
|
|
439
|
+
host = host.slice(1, -1);
|
|
440
|
+
if (host === "localhost" || host.endsWith(".localhost") || host === "0.0.0.0")
|
|
448
441
|
return false;
|
|
442
|
+
if (isV6Literal || host.includes(":")) {
|
|
443
|
+
const v6 = parseIPv6Host(host);
|
|
444
|
+
if (v6 === null || isBlockedIPv6(v6))
|
|
445
|
+
return false;
|
|
446
|
+
} else {
|
|
447
|
+
const v4 = parseIPv4Host(host);
|
|
448
|
+
if (v4 !== null && isBlockedIPv4(v4))
|
|
449
|
+
return false;
|
|
450
|
+
}
|
|
449
451
|
}
|
|
450
452
|
if (parsed.hostname.includes("..") || parsed.pathname.includes("..")) {
|
|
451
453
|
return false;
|
|
@@ -465,6 +467,19 @@ var kinetex = (() => {
|
|
|
465
467
|
return false;
|
|
466
468
|
}
|
|
467
469
|
}
|
|
470
|
+
function isBlockedAddress(address) {
|
|
471
|
+
const host = address.trim().toLowerCase();
|
|
472
|
+
if (host.startsWith("[") && host.endsWith("]"))
|
|
473
|
+
return true;
|
|
474
|
+
if (host === "" || host === "localhost" || host.endsWith(".localhost"))
|
|
475
|
+
return true;
|
|
476
|
+
if (host.includes(":")) {
|
|
477
|
+
const v6 = parseIPv6Host(host);
|
|
478
|
+
return v6 === null || isBlockedIPv6(v6);
|
|
479
|
+
}
|
|
480
|
+
const v4 = parseIPv4Host(host);
|
|
481
|
+
return v4 === null || isBlockedIPv4(v4);
|
|
482
|
+
}
|
|
468
483
|
function deepClone(value) {
|
|
469
484
|
if (value === null || typeof value !== "object") {
|
|
470
485
|
return value;
|
|
@@ -530,7 +545,7 @@ var kinetex = (() => {
|
|
|
530
545
|
}
|
|
531
546
|
return String(error);
|
|
532
547
|
}
|
|
533
|
-
function safeStringify(value) {
|
|
548
|
+
function safeStringify(value, circularMarker = "[Circular]") {
|
|
534
549
|
const seen = /* @__PURE__ */ new Set();
|
|
535
550
|
try {
|
|
536
551
|
return JSON.stringify(value, (_key, v) => {
|
|
@@ -538,7 +553,7 @@ var kinetex = (() => {
|
|
|
538
553
|
return v.toString();
|
|
539
554
|
if (typeof v === "object" && v !== null) {
|
|
540
555
|
if (seen.has(v))
|
|
541
|
-
return
|
|
556
|
+
return circularMarker;
|
|
542
557
|
seen.add(v);
|
|
543
558
|
}
|
|
544
559
|
return v;
|
|
@@ -564,7 +579,7 @@ var kinetex = (() => {
|
|
|
564
579
|
return null;
|
|
565
580
|
}
|
|
566
581
|
}
|
|
567
|
-
function
|
|
582
|
+
function perfNow() {
|
|
568
583
|
const p = getProcess();
|
|
569
584
|
if (p?.hrtime) {
|
|
570
585
|
const [sec, ns] = p.hrtime();
|
|
@@ -578,12 +593,12 @@ var kinetex = (() => {
|
|
|
578
593
|
}
|
|
579
594
|
function sleep(ms, signal) {
|
|
580
595
|
if (signal?.aborted) {
|
|
581
|
-
return Promise.reject(
|
|
596
|
+
return Promise.reject(abortError());
|
|
582
597
|
}
|
|
583
598
|
return new Promise((resolve, reject) => {
|
|
584
599
|
const onAbort = () => {
|
|
585
600
|
clearTimeout(timer);
|
|
586
|
-
reject(
|
|
601
|
+
reject(abortError());
|
|
587
602
|
};
|
|
588
603
|
const timer = setTimeout(() => {
|
|
589
604
|
signal?.removeEventListener("abort", onAbort);
|
|
@@ -620,6 +635,22 @@ var kinetex = (() => {
|
|
|
620
635
|
}
|
|
621
636
|
return null;
|
|
622
637
|
}
|
|
638
|
+
function isSensitiveHeader(name, sensitive) {
|
|
639
|
+
return sensitive.has(name.toLowerCase());
|
|
640
|
+
}
|
|
641
|
+
function redactHeaders(headers, sensitive, mask) {
|
|
642
|
+
const out = {};
|
|
643
|
+
for (const [k, v] of Object.entries(headers)) {
|
|
644
|
+
out[k] = isSensitiveHeader(k, sensitive) ? mask : v;
|
|
645
|
+
}
|
|
646
|
+
return out;
|
|
647
|
+
}
|
|
648
|
+
async function sha256Hex(data) {
|
|
649
|
+
const raw = typeof data === "string" ? new TextEncoder().encode(data) : data;
|
|
650
|
+
const buf = raw.buffer.slice(raw.byteOffset, raw.byteOffset + raw.byteLength);
|
|
651
|
+
const hash = await crypto.subtle.digest("SHA-256", buf);
|
|
652
|
+
return Array.from(new Uint8Array(hash)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
653
|
+
}
|
|
623
654
|
function uint8ArrayToBase64(bytes) {
|
|
624
655
|
const CHUNK = 24576;
|
|
625
656
|
if (bytes.byteLength <= CHUNK) {
|
|
@@ -661,7 +692,7 @@ var kinetex = (() => {
|
|
|
661
692
|
const abort = () => {
|
|
662
693
|
for (const s of validSignals)
|
|
663
694
|
s.removeEventListener("abort", abort);
|
|
664
|
-
controller.abort(
|
|
695
|
+
controller.abort(abortError());
|
|
665
696
|
};
|
|
666
697
|
for (const s of validSignals)
|
|
667
698
|
s.addEventListener("abort", abort, { once: true });
|
|
@@ -691,11 +722,11 @@ var kinetex = (() => {
|
|
|
691
722
|
}
|
|
692
723
|
return Array.from(arr).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
693
724
|
}
|
|
694
|
-
function
|
|
725
|
+
function abortError(message = "Aborted") {
|
|
695
726
|
if (typeof DOMException !== "undefined") {
|
|
696
|
-
return new DOMException(
|
|
727
|
+
return new DOMException(message, "AbortError");
|
|
697
728
|
}
|
|
698
|
-
const err = new Error(
|
|
729
|
+
const err = new Error(message);
|
|
699
730
|
err.name = "AbortError";
|
|
700
731
|
return err;
|
|
701
732
|
}
|
|
@@ -1124,6 +1155,9 @@ var kinetex = (() => {
|
|
|
1124
1155
|
if (honor && d.noStore) {
|
|
1125
1156
|
return { ttlMs: 0, swrMs: 0, staleOnError: 0, shouldCache: false };
|
|
1126
1157
|
}
|
|
1158
|
+
if (honor && d.private) {
|
|
1159
|
+
return { ttlMs: 0, swrMs: 0, staleOnError: 0, shouldCache: false };
|
|
1160
|
+
}
|
|
1127
1161
|
const maxAgeSecs = d.sMaxAge ?? d.maxAge;
|
|
1128
1162
|
let ttlMs;
|
|
1129
1163
|
if (maxAgeSecs !== null) {
|
|
@@ -2300,7 +2334,7 @@ var kinetex = (() => {
|
|
|
2300
2334
|
continue;
|
|
2301
2335
|
const k = t.slice(0, eq).trim().toLowerCase();
|
|
2302
2336
|
let v = t.slice(eq + 1).trim();
|
|
2303
|
-
if (v.startsWith('"') && v.endsWith('"'))
|
|
2337
|
+
if (v.length >= 2 && v.startsWith('"') && v.endsWith('"'))
|
|
2304
2338
|
v = v.slice(1, -1);
|
|
2305
2339
|
params.set(k, v);
|
|
2306
2340
|
}
|
|
@@ -3895,522 +3929,217 @@ var kinetex = (() => {
|
|
|
3895
3929
|
}
|
|
3896
3930
|
});
|
|
3897
3931
|
|
|
3898
|
-
// dist/esm/
|
|
3899
|
-
var
|
|
3900
|
-
__export(
|
|
3901
|
-
|
|
3932
|
+
// dist/esm/response.js
|
|
3933
|
+
var response_exports = {};
|
|
3934
|
+
__export(response_exports, {
|
|
3935
|
+
ContentTypeError: () => ContentTypeError,
|
|
3936
|
+
HTTPResponseError: () => HTTPResponseError,
|
|
3937
|
+
ResponseCache: () => ResponseCache,
|
|
3938
|
+
ResponseDecodeError: () => ResponseDecodeError,
|
|
3939
|
+
ResponseSizeLimitError: () => ResponseSizeLimitError,
|
|
3940
|
+
applyDecompression: () => applyDecompression,
|
|
3941
|
+
assertOk: () => assertOk,
|
|
3942
|
+
assertOkJSON: () => assertOkJSON,
|
|
3943
|
+
createLimitedReader: () => createLimitedReader,
|
|
3944
|
+
decodeBody: () => decodeBody,
|
|
3945
|
+
decompressStream: () => decompressStream,
|
|
3946
|
+
diffResponses: () => diffResponses,
|
|
3947
|
+
extractServerTiming: () => extractServerTiming,
|
|
3948
|
+
isBinary: () => isBinary,
|
|
3949
|
+
isJSON: () => isJSON,
|
|
3950
|
+
isText: () => isText,
|
|
3951
|
+
limitAction: () => limitAction,
|
|
3952
|
+
normalizeHeaders: () => normalizeHeaders2,
|
|
3953
|
+
normalizeResponse: () => normalizeResponse,
|
|
3954
|
+
parseContentType: () => parseContentType2,
|
|
3955
|
+
parseMultipartResponse: () => parseMultipartResponse,
|
|
3956
|
+
readBlob: () => readBlob,
|
|
3957
|
+
readBodyWithLimit: () => readBodyWithLimit,
|
|
3958
|
+
readBytes: () => readBytes,
|
|
3959
|
+
readFormData: () => readFormData,
|
|
3960
|
+
readJSON: () => readJSON,
|
|
3961
|
+
readJSONStream: () => readJSONStream,
|
|
3962
|
+
readNDJSON: () => readNDJSON,
|
|
3963
|
+
readStream: () => readStream,
|
|
3964
|
+
readText: () => readText
|
|
3902
3965
|
});
|
|
3903
|
-
function
|
|
3904
|
-
return
|
|
3966
|
+
function normalizeHeaders2(headers) {
|
|
3967
|
+
return normalizeHeaders(headers);
|
|
3905
3968
|
}
|
|
3906
|
-
function
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
|
-
|
|
3910
|
-
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
|
|
3916
|
-
Buffer.from(bytes).toString("base64")
|
|
3917
|
-
);
|
|
3918
|
-
return `Basic ${b64}`;
|
|
3969
|
+
function normalizeResponse(response) {
|
|
3970
|
+
return {
|
|
3971
|
+
status: response.status,
|
|
3972
|
+
statusText: response.statusText,
|
|
3973
|
+
headers: normalizeHeaders2(response.headers),
|
|
3974
|
+
url: response.url,
|
|
3975
|
+
redirected: response.redirected,
|
|
3976
|
+
ok: response.ok,
|
|
3977
|
+
bodyUsed: response.bodyUsed
|
|
3978
|
+
};
|
|
3919
3979
|
}
|
|
3920
|
-
function
|
|
3921
|
-
|
|
3922
|
-
const match = /^HTTP\/1\.[01] (\d{3})(?: (.*))?$/.exec(statusLine);
|
|
3923
|
-
if (!match)
|
|
3980
|
+
function parseContentType2(value) {
|
|
3981
|
+
if (!value)
|
|
3924
3982
|
return null;
|
|
3925
|
-
|
|
3983
|
+
if (value.length > MAX_HEADER_LENGTH)
|
|
3984
|
+
return null;
|
|
3985
|
+
const parsed = parseContentType(value);
|
|
3986
|
+
if (!parsed)
|
|
3987
|
+
return null;
|
|
3988
|
+
return {
|
|
3989
|
+
mediaType: parsed.mediaType,
|
|
3990
|
+
type: parsed.type,
|
|
3991
|
+
subtype: parsed.subtype,
|
|
3992
|
+
charset: parsed.charset,
|
|
3993
|
+
boundary: parsed.boundary
|
|
3994
|
+
};
|
|
3926
3995
|
}
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
|
|
3931
|
-
|
|
3932
|
-
|
|
3933
|
-
|
|
3934
|
-
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
3996
|
+
function isJSON(contentType) {
|
|
3997
|
+
if (!contentType)
|
|
3998
|
+
return false;
|
|
3999
|
+
const info = parseContentType2(contentType);
|
|
4000
|
+
if (!info)
|
|
4001
|
+
return false;
|
|
4002
|
+
return info.mediaType === "application/json" || info.subtype.endsWith("+json");
|
|
4003
|
+
}
|
|
4004
|
+
function isText(contentType) {
|
|
4005
|
+
if (!contentType)
|
|
4006
|
+
return false;
|
|
4007
|
+
const info = parseContentType2(contentType);
|
|
4008
|
+
return info?.type === "text";
|
|
4009
|
+
}
|
|
4010
|
+
function isBinary(contentType) {
|
|
4011
|
+
return !isText(contentType) && !isJSON(contentType);
|
|
4012
|
+
}
|
|
4013
|
+
function readWithAbort(reader, signal) {
|
|
4014
|
+
if (!signal)
|
|
4015
|
+
return reader.read();
|
|
4016
|
+
if (signal.aborted)
|
|
4017
|
+
return Promise.resolve({ done: true, value: void 0 });
|
|
4018
|
+
return new Promise((resolve, reject) => {
|
|
4019
|
+
const onAbort = () => {
|
|
4020
|
+
void reader.cancel("aborted").catch(() => {
|
|
4021
|
+
});
|
|
4022
|
+
resolve({ done: true, value: void 0 });
|
|
4023
|
+
};
|
|
4024
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
4025
|
+
reader.read().then(resolve, reject).finally(() => {
|
|
4026
|
+
signal.removeEventListener("abort", onAbort);
|
|
3939
4027
|
});
|
|
4028
|
+
});
|
|
4029
|
+
}
|
|
4030
|
+
function normaliseOnExceed(mode) {
|
|
4031
|
+
return mode === "truncate" || mode === "abort" ? mode : "throw";
|
|
4032
|
+
}
|
|
4033
|
+
function limitAction(chunk, bytesRead, limit, onExceed, makeError) {
|
|
4034
|
+
const newTotal = bytesRead + chunk.byteLength;
|
|
4035
|
+
if (newTotal <= limit.maxBytes)
|
|
4036
|
+
return { kind: "keep" };
|
|
4037
|
+
limit.onExceedCallback?.(newTotal, limit.maxBytes);
|
|
4038
|
+
if (onExceed === "throw")
|
|
4039
|
+
throw makeError(newTotal, limit.maxBytes);
|
|
4040
|
+
if (onExceed === "truncate") {
|
|
4041
|
+
const remaining = limit.maxBytes - bytesRead;
|
|
4042
|
+
return { kind: "truncate", chunk: chunk.slice(0, remaining) };
|
|
3940
4043
|
}
|
|
3941
|
-
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
const
|
|
3945
|
-
const
|
|
3946
|
-
const
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
servername: proxyUrl.hostname,
|
|
3954
|
-
...options.ca !== void 0 ? { ca: options.ca } : {}
|
|
3955
|
-
}, timeoutMs, options.signal, errMeta, `proxy ${proxyUrl.host}`) : await openTcpSocket(net, { host: proxyUrl.hostname, port: proxyPort }, timeoutMs, options.signal, errMeta, `proxy ${proxyUrl.host}`);
|
|
3956
|
-
const auth = proxyAuthHeader(proxy);
|
|
3957
|
-
const connectLines = [
|
|
3958
|
-
`CONNECT ${authority} HTTP/1.1`,
|
|
3959
|
-
`Host: ${authority}`,
|
|
3960
|
-
// RFC 9110 §9.3.6: proxies that support persistent tunnels are expected to
|
|
3961
|
-
// treat a tunnel as still-open even without this, but sending it is what
|
|
3962
|
-
// every real client does and some proxies misbehave without it.
|
|
3963
|
-
"Proxy-Connection: Keep-Alive",
|
|
3964
|
-
...auth !== void 0 ? [`Proxy-Authorization: ${auth}`] : [],
|
|
3965
|
-
"",
|
|
3966
|
-
""
|
|
3967
|
-
];
|
|
3968
|
-
baseSocket.write(connectLines.join("\r\n"));
|
|
3969
|
-
let head = "";
|
|
3970
|
-
const headResult = await new Promise((resolve) => {
|
|
3971
|
-
const drain = () => {
|
|
3972
|
-
for (; ; ) {
|
|
3973
|
-
const chunk = baseSocket.read();
|
|
3974
|
-
if (chunk === null)
|
|
3975
|
-
return;
|
|
3976
|
-
head += chunk.toString("latin1");
|
|
4044
|
+
return { kind: "stop" };
|
|
4045
|
+
}
|
|
4046
|
+
async function readBodyWithLimit(stream, url, limit, signal) {
|
|
4047
|
+
const onExceed = normaliseOnExceed(limit.onExceed);
|
|
4048
|
+
const reader = stream.getReader();
|
|
4049
|
+
const chunks = [];
|
|
4050
|
+
let bytesRead = 0;
|
|
4051
|
+
try {
|
|
4052
|
+
while (true) {
|
|
4053
|
+
if (signal?.aborted) {
|
|
4054
|
+
await reader.cancel("aborted");
|
|
4055
|
+
throw new DOMException("Response reading aborted", "AbortError");
|
|
3977
4056
|
}
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
if (end === -1) {
|
|
3983
|
-
if (head.length > MAX_HEAD_BYTES) {
|
|
3984
|
-
cleanup();
|
|
3985
|
-
resolve({
|
|
3986
|
-
ok: false,
|
|
3987
|
-
err: new KinetexError("Proxy sent an oversized CONNECT response", "EPROXY", errMeta)
|
|
3988
|
-
});
|
|
4057
|
+
const { done, value } = await readWithAbort(reader, signal);
|
|
4058
|
+
if (done) {
|
|
4059
|
+
if (signal?.aborted) {
|
|
4060
|
+
throw new DOMException("Response reading aborted", "AbortError");
|
|
3989
4061
|
}
|
|
3990
|
-
|
|
4062
|
+
break;
|
|
3991
4063
|
}
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
});
|
|
3999
|
-
return;
|
|
4064
|
+
let action;
|
|
4065
|
+
try {
|
|
4066
|
+
action = limitAction(value, bytesRead, limit, onExceed, (n, max) => new ResponseSizeLimitError(n, max, url));
|
|
4067
|
+
} catch (err) {
|
|
4068
|
+
await reader.cancel("size limit exceeded");
|
|
4069
|
+
throw err;
|
|
4000
4070
|
}
|
|
4001
|
-
if (
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
baseSocket.unshift(Buffer.from(tail, "latin1"));
|
|
4005
|
-
resolve({ ok: true });
|
|
4006
|
-
return;
|
|
4071
|
+
if (action.kind === "stop") {
|
|
4072
|
+
await reader.cancel("size limit exceeded");
|
|
4073
|
+
break;
|
|
4007
4074
|
}
|
|
4008
|
-
if (
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
4012
|
-
});
|
|
4013
|
-
return;
|
|
4075
|
+
if (action.kind === "truncate") {
|
|
4076
|
+
chunks.push(action.chunk);
|
|
4077
|
+
bytesRead += action.chunk.byteLength;
|
|
4078
|
+
break;
|
|
4014
4079
|
}
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
|
|
4080
|
+
chunks.push(value);
|
|
4081
|
+
bytesRead += value.byteLength;
|
|
4082
|
+
}
|
|
4083
|
+
} finally {
|
|
4084
|
+
if (signal?.aborted) {
|
|
4085
|
+
await reader.cancel("aborted").catch(() => {
|
|
4018
4086
|
});
|
|
4019
|
-
};
|
|
4020
|
-
const onError = (e) => {
|
|
4021
|
-
cleanup();
|
|
4022
|
-
resolve({
|
|
4023
|
-
ok: false,
|
|
4024
|
-
err: new KinetexError(`Proxy connection failed: ${e.message}`, "ENETWORK", {
|
|
4025
|
-
...errMeta,
|
|
4026
|
-
cause: e
|
|
4027
|
-
})
|
|
4028
|
-
});
|
|
4029
|
-
};
|
|
4030
|
-
const onClosedEarly = () => {
|
|
4031
|
-
cleanup();
|
|
4032
|
-
resolve({
|
|
4033
|
-
ok: false,
|
|
4034
|
-
err: new KinetexError(`Proxy closed the connection before answering CONNECT for ${authority}`, "EPROXY", errMeta)
|
|
4035
|
-
});
|
|
4036
|
-
};
|
|
4037
|
-
const onTimeout = () => {
|
|
4038
|
-
cleanup();
|
|
4039
|
-
baseSocket.destroy();
|
|
4040
|
-
resolve({
|
|
4041
|
-
ok: false,
|
|
4042
|
-
err: new KinetexError(`Proxy did not respond to CONNECT for ${authority} within ${timeoutMs}ms`, "ETIMEOUT", errMeta)
|
|
4043
|
-
});
|
|
4044
|
-
};
|
|
4045
|
-
const onAbort = () => {
|
|
4046
|
-
cleanup();
|
|
4047
|
-
baseSocket.destroy();
|
|
4048
|
-
resolve({
|
|
4049
|
-
ok: false,
|
|
4050
|
-
err: new KinetexError("Proxy connection aborted", "EABORT", errMeta)
|
|
4051
|
-
});
|
|
4052
|
-
};
|
|
4053
|
-
const timer = setTimeout(onTimeout, timeoutMs);
|
|
4054
|
-
if (typeof timer.unref === "function") {
|
|
4055
|
-
timer.unref();
|
|
4056
|
-
}
|
|
4057
|
-
function cleanup() {
|
|
4058
|
-
clearTimeout(timer);
|
|
4059
|
-
baseSocket.off("readable", onReadable);
|
|
4060
|
-
baseSocket.off("error", onError);
|
|
4061
|
-
baseSocket.off("end", onClosedEarly);
|
|
4062
|
-
baseSocket.off("close", onClosedEarly);
|
|
4063
|
-
baseSocket.off("timeout", onTimeout);
|
|
4064
|
-
options.signal?.removeEventListener("abort", onAbort);
|
|
4065
4087
|
}
|
|
4066
|
-
|
|
4067
|
-
baseSocket.once("error", onError);
|
|
4068
|
-
baseSocket.once("end", onClosedEarly);
|
|
4069
|
-
baseSocket.once("close", onClosedEarly);
|
|
4070
|
-
baseSocket.setTimeout(timeoutMs, onTimeout);
|
|
4071
|
-
options.signal?.addEventListener("abort", onAbort, { once: true });
|
|
4072
|
-
drain();
|
|
4073
|
-
});
|
|
4074
|
-
if (!headResult.ok) {
|
|
4075
|
-
baseSocket.destroy();
|
|
4076
|
-
throw headResult.err;
|
|
4088
|
+
reader.releaseLock();
|
|
4077
4089
|
}
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
|
|
4090
|
+
return concatUint8Arrays2(chunks);
|
|
4091
|
+
}
|
|
4092
|
+
function resolveCharset(charset) {
|
|
4093
|
+
if (!charset)
|
|
4094
|
+
return "utf-8";
|
|
4095
|
+
const key = charset.toLowerCase().trim();
|
|
4096
|
+
if (Object.prototype.hasOwnProperty.call(CHARSET_ALIASES, key)) {
|
|
4097
|
+
return CHARSET_ALIASES[key];
|
|
4081
4098
|
}
|
|
4082
|
-
return
|
|
4083
|
-
const onTunnelTimeout = () => {
|
|
4084
|
-
done();
|
|
4085
|
-
baseSocket.destroy();
|
|
4086
|
-
reject(new KinetexError(`TLS handshake through proxy to ${target.hostname} did not complete within ${timeoutMs}ms`, "ETIMEOUT", errMeta));
|
|
4087
|
-
};
|
|
4088
|
-
const onTunnelAbort = () => {
|
|
4089
|
-
done();
|
|
4090
|
-
baseSocket.destroy();
|
|
4091
|
-
reject(new KinetexError("Proxy connection aborted", "EABORT", errMeta));
|
|
4092
|
-
};
|
|
4093
|
-
const tunnelTimer = setTimeout(onTunnelTimeout, timeoutMs);
|
|
4094
|
-
if (typeof tunnelTimer.unref === "function") {
|
|
4095
|
-
tunnelTimer.unref();
|
|
4096
|
-
}
|
|
4097
|
-
options.signal?.addEventListener("abort", onTunnelAbort, { once: true });
|
|
4098
|
-
function done() {
|
|
4099
|
-
clearTimeout(tunnelTimer);
|
|
4100
|
-
options.signal?.removeEventListener("abort", onTunnelAbort);
|
|
4101
|
-
}
|
|
4102
|
-
const secure = tls.connect(
|
|
4103
|
-
{
|
|
4104
|
-
socket: baseSocket,
|
|
4105
|
-
servername: options.servername ?? target.hostname,
|
|
4106
|
-
...options.ca !== void 0 ? { ca: options.ca } : {}
|
|
4107
|
-
},
|
|
4108
|
-
// `rejectUnauthorized` is left at its default (true), so a peer that
|
|
4109
|
-
// fails verification emits `error` rather than reaching this callback.
|
|
4110
|
-
// Verification therefore cannot be bypassed here, and the callback
|
|
4111
|
-
// only ever runs for a verified handshake.
|
|
4112
|
-
() => {
|
|
4113
|
-
done();
|
|
4114
|
-
baseSocket.setTimeout(0);
|
|
4115
|
-
resolve(secure);
|
|
4116
|
-
}
|
|
4117
|
-
);
|
|
4118
|
-
secure.once("error", (e) => {
|
|
4119
|
-
done();
|
|
4120
|
-
baseSocket.destroy();
|
|
4121
|
-
reject(new KinetexError(`TLS handshake through proxy failed for ${target.hostname}: ${e.message}`, "ENETWORK", { ...errMeta, cause: e }));
|
|
4122
|
-
});
|
|
4123
|
-
});
|
|
4099
|
+
return "utf-8";
|
|
4124
4100
|
}
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
}));
|
|
4134
|
-
};
|
|
4135
|
-
const timer = setTimeout(() => {
|
|
4136
|
-
cleanup();
|
|
4137
|
-
socket.destroy();
|
|
4138
|
-
reject(new KinetexError(`Connecting to ${what} timed out`, "ETIMEOUT", errMeta));
|
|
4139
|
-
}, timeoutMs);
|
|
4140
|
-
if (typeof timer.unref === "function") {
|
|
4141
|
-
timer.unref();
|
|
4142
|
-
}
|
|
4143
|
-
const onAbort = () => {
|
|
4144
|
-
cleanup();
|
|
4145
|
-
socket.destroy();
|
|
4146
|
-
reject(new KinetexError("Proxy connection aborted", "EABORT", errMeta));
|
|
4147
|
-
};
|
|
4148
|
-
function cleanup() {
|
|
4149
|
-
clearTimeout(timer);
|
|
4150
|
-
socket.off("error", onError);
|
|
4151
|
-
signal?.removeEventListener("abort", onAbort);
|
|
4152
|
-
}
|
|
4153
|
-
socket.once("error", onError);
|
|
4154
|
-
socket.once("connect", () => {
|
|
4155
|
-
cleanup();
|
|
4156
|
-
resolve(socket);
|
|
4157
|
-
});
|
|
4158
|
-
if (signal?.aborted)
|
|
4159
|
-
onAbort();
|
|
4160
|
-
else
|
|
4161
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
4162
|
-
});
|
|
4101
|
+
function detectBOM(bytes) {
|
|
4102
|
+
if (bytes[0] === 239 && bytes[1] === 187 && bytes[2] === 191)
|
|
4103
|
+
return "utf-8";
|
|
4104
|
+
if (bytes[0] === 255 && bytes[1] === 254)
|
|
4105
|
+
return "utf-16le";
|
|
4106
|
+
if (bytes[0] === 254 && bytes[1] === 255)
|
|
4107
|
+
return "utf-16be";
|
|
4108
|
+
return null;
|
|
4163
4109
|
}
|
|
4164
|
-
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
const onError = (e) => {
|
|
4176
|
-
cleanup();
|
|
4177
|
-
reject(new KinetexError(`Cannot reach ${what}: ${e.message}`, "ENETWORK", {
|
|
4178
|
-
...errMeta,
|
|
4179
|
-
cause: e
|
|
4180
|
-
}));
|
|
4181
|
-
};
|
|
4182
|
-
const timer = setTimeout(() => {
|
|
4183
|
-
cleanup();
|
|
4184
|
-
socket.destroy();
|
|
4185
|
-
reject(new KinetexError(`Connecting to ${what} timed out`, "ETIMEOUT", errMeta));
|
|
4186
|
-
}, timeoutMs);
|
|
4187
|
-
if (typeof timer.unref === "function") {
|
|
4188
|
-
timer.unref();
|
|
4110
|
+
function decodeBody(bytes, charset, url) {
|
|
4111
|
+
const detected = detectBOM(bytes);
|
|
4112
|
+
const resolved = detected ?? resolveCharset(charset);
|
|
4113
|
+
const data = detected ? bytes.slice(detected === "utf-8" ? 3 : 2) : bytes;
|
|
4114
|
+
try {
|
|
4115
|
+
return new TextDecoder(resolved, { fatal: true }).decode(data);
|
|
4116
|
+
} catch {
|
|
4117
|
+
try {
|
|
4118
|
+
return new TextDecoder(resolved, { fatal: false }).decode(data);
|
|
4119
|
+
} catch (err2) {
|
|
4120
|
+
throw new ResponseDecodeError(String(err2), resolved, url ?? "<unknown url>");
|
|
4189
4121
|
}
|
|
4190
|
-
|
|
4191
|
-
|
|
4192
|
-
|
|
4193
|
-
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
|
|
4122
|
+
}
|
|
4123
|
+
}
|
|
4124
|
+
function createSizeLimitedDecompression(stream, encoding, maxDecompressedSize = 1e8) {
|
|
4125
|
+
const decompressed = decompressStream(stream, encoding);
|
|
4126
|
+
let bytesRead = 0;
|
|
4127
|
+
const sizeLimitTransform = new TransformStream({
|
|
4128
|
+
transform(chunk, controller) {
|
|
4129
|
+
bytesRead += chunk.byteLength;
|
|
4130
|
+
if (bytesRead > maxDecompressedSize) {
|
|
4131
|
+
controller.error(new ResponseSizeLimitError(bytesRead, maxDecompressedSize, "decompression"));
|
|
4132
|
+
return;
|
|
4133
|
+
}
|
|
4134
|
+
controller.enqueue(chunk);
|
|
4199
4135
|
}
|
|
4200
|
-
socket.once("error", onError);
|
|
4201
|
-
if (signal?.aborted)
|
|
4202
|
-
onAbort();
|
|
4203
|
-
else
|
|
4204
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
4205
4136
|
});
|
|
4137
|
+
return decompressed.pipeThrough(sizeLimitTransform);
|
|
4206
4138
|
}
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
"
|
|
4210
|
-
|
|
4211
|
-
init_types();
|
|
4212
|
-
DEFAULT_CONNECT_TIMEOUT_MS = 3e4;
|
|
4213
|
-
MAX_HEAD_BYTES = 64 * 1024;
|
|
4214
|
-
}
|
|
4215
|
-
});
|
|
4216
|
-
|
|
4217
|
-
// dist/esm/response.js
|
|
4218
|
-
var response_exports = {};
|
|
4219
|
-
__export(response_exports, {
|
|
4220
|
-
ContentTypeError: () => ContentTypeError,
|
|
4221
|
-
HTTPResponseError: () => HTTPResponseError,
|
|
4222
|
-
ResponseCache: () => ResponseCache,
|
|
4223
|
-
ResponseDecodeError: () => ResponseDecodeError,
|
|
4224
|
-
ResponseSizeLimitError: () => ResponseSizeLimitError,
|
|
4225
|
-
applyDecompression: () => applyDecompression,
|
|
4226
|
-
assertOk: () => assertOk,
|
|
4227
|
-
assertOkJSON: () => assertOkJSON,
|
|
4228
|
-
createLimitedReader: () => createLimitedReader,
|
|
4229
|
-
decodeBody: () => decodeBody,
|
|
4230
|
-
decompressStream: () => decompressStream,
|
|
4231
|
-
diffResponses: () => diffResponses,
|
|
4232
|
-
extractServerTiming: () => extractServerTiming,
|
|
4233
|
-
isBinary: () => isBinary,
|
|
4234
|
-
isJSON: () => isJSON,
|
|
4235
|
-
isText: () => isText,
|
|
4236
|
-
normalizeHeaders: () => normalizeHeaders2,
|
|
4237
|
-
normalizeResponse: () => normalizeResponse,
|
|
4238
|
-
parseContentType: () => parseContentType2,
|
|
4239
|
-
parseMultipartResponse: () => parseMultipartResponse,
|
|
4240
|
-
readBlob: () => readBlob,
|
|
4241
|
-
readBodyWithLimit: () => readBodyWithLimit,
|
|
4242
|
-
readBytes: () => readBytes,
|
|
4243
|
-
readFormData: () => readFormData,
|
|
4244
|
-
readJSON: () => readJSON,
|
|
4245
|
-
readJSONStream: () => readJSONStream,
|
|
4246
|
-
readNDJSON: () => readNDJSON,
|
|
4247
|
-
readStream: () => readStream,
|
|
4248
|
-
readText: () => readText
|
|
4249
|
-
});
|
|
4250
|
-
function normalizeHeaders2(headers) {
|
|
4251
|
-
return normalizeHeaders(headers);
|
|
4252
|
-
}
|
|
4253
|
-
function normalizeResponse(response) {
|
|
4254
|
-
return {
|
|
4255
|
-
status: response.status,
|
|
4256
|
-
statusText: response.statusText,
|
|
4257
|
-
headers: normalizeHeaders2(response.headers),
|
|
4258
|
-
url: response.url,
|
|
4259
|
-
redirected: response.redirected,
|
|
4260
|
-
ok: response.ok,
|
|
4261
|
-
bodyUsed: response.bodyUsed
|
|
4262
|
-
};
|
|
4263
|
-
}
|
|
4264
|
-
function parseContentType2(value) {
|
|
4265
|
-
if (!value)
|
|
4266
|
-
return null;
|
|
4267
|
-
if (value.length > MAX_HEADER_LENGTH)
|
|
4268
|
-
return null;
|
|
4269
|
-
const parsed = parseContentType(value);
|
|
4270
|
-
if (!parsed)
|
|
4271
|
-
return null;
|
|
4272
|
-
return {
|
|
4273
|
-
mediaType: parsed.mediaType,
|
|
4274
|
-
type: parsed.type,
|
|
4275
|
-
subtype: parsed.subtype,
|
|
4276
|
-
charset: parsed.charset,
|
|
4277
|
-
boundary: parsed.boundary
|
|
4278
|
-
};
|
|
4279
|
-
}
|
|
4280
|
-
function isJSON(contentType) {
|
|
4281
|
-
if (!contentType)
|
|
4282
|
-
return false;
|
|
4283
|
-
const info = parseContentType2(contentType);
|
|
4284
|
-
if (!info)
|
|
4285
|
-
return false;
|
|
4286
|
-
return info.mediaType === "application/json" || info.subtype.endsWith("+json");
|
|
4287
|
-
}
|
|
4288
|
-
function isText(contentType) {
|
|
4289
|
-
if (!contentType)
|
|
4290
|
-
return false;
|
|
4291
|
-
const info = parseContentType2(contentType);
|
|
4292
|
-
return info?.type === "text";
|
|
4293
|
-
}
|
|
4294
|
-
function isBinary(contentType) {
|
|
4295
|
-
return !isText(contentType) && !isJSON(contentType);
|
|
4296
|
-
}
|
|
4297
|
-
function readWithAbort(reader, signal) {
|
|
4298
|
-
if (!signal)
|
|
4299
|
-
return reader.read();
|
|
4300
|
-
if (signal.aborted)
|
|
4301
|
-
return Promise.resolve({ done: true, value: void 0 });
|
|
4302
|
-
return new Promise((resolve, reject) => {
|
|
4303
|
-
const onAbort = () => {
|
|
4304
|
-
void reader.cancel("aborted").catch(() => {
|
|
4305
|
-
});
|
|
4306
|
-
resolve({ done: true, value: void 0 });
|
|
4307
|
-
};
|
|
4308
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
4309
|
-
reader.read().then(resolve, reject).finally(() => {
|
|
4310
|
-
signal.removeEventListener("abort", onAbort);
|
|
4311
|
-
});
|
|
4312
|
-
});
|
|
4313
|
-
}
|
|
4314
|
-
function normaliseOnExceed(mode) {
|
|
4315
|
-
return mode === "truncate" || mode === "abort" ? mode : "throw";
|
|
4316
|
-
}
|
|
4317
|
-
async function readBodyWithLimit(stream, url, limit, signal) {
|
|
4318
|
-
const onExceed = normaliseOnExceed(limit.onExceed);
|
|
4319
|
-
const reader = stream.getReader();
|
|
4320
|
-
const chunks = [];
|
|
4321
|
-
let bytesRead = 0;
|
|
4322
|
-
try {
|
|
4323
|
-
while (true) {
|
|
4324
|
-
if (signal?.aborted) {
|
|
4325
|
-
await reader.cancel("aborted");
|
|
4326
|
-
throw new DOMException("Response reading aborted", "AbortError");
|
|
4327
|
-
}
|
|
4328
|
-
const { done, value } = await readWithAbort(reader, signal);
|
|
4329
|
-
if (done) {
|
|
4330
|
-
if (signal?.aborted) {
|
|
4331
|
-
throw new DOMException("Response reading aborted", "AbortError");
|
|
4332
|
-
}
|
|
4333
|
-
break;
|
|
4334
|
-
}
|
|
4335
|
-
const newTotal = bytesRead + value.byteLength;
|
|
4336
|
-
if (newTotal > limit.maxBytes) {
|
|
4337
|
-
limit.onExceedCallback?.(newTotal, limit.maxBytes);
|
|
4338
|
-
if (onExceed === "throw") {
|
|
4339
|
-
await reader.cancel("size limit exceeded");
|
|
4340
|
-
throw new ResponseSizeLimitError(newTotal, limit.maxBytes, url);
|
|
4341
|
-
}
|
|
4342
|
-
if (onExceed === "abort") {
|
|
4343
|
-
await reader.cancel("size limit exceeded");
|
|
4344
|
-
break;
|
|
4345
|
-
}
|
|
4346
|
-
if (onExceed === "truncate") {
|
|
4347
|
-
const remaining = limit.maxBytes - bytesRead;
|
|
4348
|
-
if (remaining > 0) {
|
|
4349
|
-
chunks.push(value.slice(0, remaining));
|
|
4350
|
-
bytesRead += remaining;
|
|
4351
|
-
}
|
|
4352
|
-
break;
|
|
4353
|
-
}
|
|
4354
|
-
}
|
|
4355
|
-
chunks.push(value);
|
|
4356
|
-
bytesRead += value.byteLength;
|
|
4357
|
-
}
|
|
4358
|
-
} finally {
|
|
4359
|
-
reader.releaseLock();
|
|
4360
|
-
}
|
|
4361
|
-
return concatUint8Arrays2(chunks);
|
|
4362
|
-
}
|
|
4363
|
-
function resolveCharset(charset) {
|
|
4364
|
-
if (!charset)
|
|
4365
|
-
return "utf-8";
|
|
4366
|
-
const key = charset.toLowerCase().trim();
|
|
4367
|
-
if (Object.prototype.hasOwnProperty.call(CHARSET_ALIASES, key)) {
|
|
4368
|
-
return CHARSET_ALIASES[key];
|
|
4369
|
-
}
|
|
4370
|
-
return "utf-8";
|
|
4371
|
-
}
|
|
4372
|
-
function detectBOM(bytes) {
|
|
4373
|
-
if (bytes[0] === 239 && bytes[1] === 187 && bytes[2] === 191)
|
|
4374
|
-
return "utf-8";
|
|
4375
|
-
if (bytes[0] === 255 && bytes[1] === 254)
|
|
4376
|
-
return "utf-16le";
|
|
4377
|
-
if (bytes[0] === 254 && bytes[1] === 255)
|
|
4378
|
-
return "utf-16be";
|
|
4379
|
-
return null;
|
|
4380
|
-
}
|
|
4381
|
-
function decodeBody(bytes, charset, url) {
|
|
4382
|
-
const detected = detectBOM(bytes);
|
|
4383
|
-
const resolved = detected ?? resolveCharset(charset);
|
|
4384
|
-
const data = detected ? bytes.slice(detected === "utf-8" ? 3 : 2) : bytes;
|
|
4385
|
-
try {
|
|
4386
|
-
return new TextDecoder(resolved, { fatal: true }).decode(data);
|
|
4387
|
-
} catch {
|
|
4388
|
-
try {
|
|
4389
|
-
return new TextDecoder(resolved, { fatal: false }).decode(data);
|
|
4390
|
-
} catch (err2) {
|
|
4391
|
-
throw new ResponseDecodeError(String(err2), resolved, url);
|
|
4392
|
-
}
|
|
4393
|
-
}
|
|
4394
|
-
}
|
|
4395
|
-
function createSizeLimitedDecompression(stream, encoding, maxDecompressedSize = 1e8) {
|
|
4396
|
-
const decompressed = decompressStream(stream, encoding);
|
|
4397
|
-
let bytesRead = 0;
|
|
4398
|
-
const sizeLimitTransform = new TransformStream({
|
|
4399
|
-
transform(chunk, controller) {
|
|
4400
|
-
bytesRead += chunk.byteLength;
|
|
4401
|
-
if (bytesRead > maxDecompressedSize) {
|
|
4402
|
-
controller.error(new ResponseSizeLimitError(bytesRead, maxDecompressedSize, "decompression"));
|
|
4403
|
-
return;
|
|
4404
|
-
}
|
|
4405
|
-
controller.enqueue(chunk);
|
|
4406
|
-
}
|
|
4407
|
-
});
|
|
4408
|
-
return decompressed.pipeThrough(sizeLimitTransform);
|
|
4409
|
-
}
|
|
4410
|
-
function decompressStream(stream, encoding) {
|
|
4411
|
-
const normalized = encoding.trim().toLowerCase();
|
|
4412
|
-
if (normalized === "br") {
|
|
4413
|
-
return brotliDecompressStream(stream);
|
|
4139
|
+
function decompressStream(stream, encoding) {
|
|
4140
|
+
const normalized = encoding.trim().toLowerCase();
|
|
4141
|
+
if (normalized === "br") {
|
|
4142
|
+
return brotliDecompressStream(stream);
|
|
4414
4143
|
}
|
|
4415
4144
|
if (normalized === "zstd") {
|
|
4416
4145
|
return zstdDecompressStream(stream);
|
|
@@ -4419,12 +4148,65 @@ var kinetex = (() => {
|
|
|
4419
4148
|
return stream;
|
|
4420
4149
|
try {
|
|
4421
4150
|
const format = normalized;
|
|
4422
|
-
const
|
|
4423
|
-
return stream
|
|
4151
|
+
const decoder = new DecompressionStream(format);
|
|
4152
|
+
return decodeUnlessEmpty(stream, decoder);
|
|
4424
4153
|
} catch {
|
|
4425
4154
|
return stream;
|
|
4426
4155
|
}
|
|
4427
4156
|
}
|
|
4157
|
+
function decodeUnlessEmpty(source, decoder) {
|
|
4158
|
+
const reader = source.getReader();
|
|
4159
|
+
return new ReadableStream({
|
|
4160
|
+
async start(controller) {
|
|
4161
|
+
let first = null;
|
|
4162
|
+
for (; ; ) {
|
|
4163
|
+
const next = await reader.read().catch(() => ({ done: true, value: void 0 }));
|
|
4164
|
+
if (next.done || next.value.byteLength > 0) {
|
|
4165
|
+
first = next;
|
|
4166
|
+
break;
|
|
4167
|
+
}
|
|
4168
|
+
}
|
|
4169
|
+
if (first.done) {
|
|
4170
|
+
controller.close();
|
|
4171
|
+
return;
|
|
4172
|
+
}
|
|
4173
|
+
const rehydrated = new ReadableStream({
|
|
4174
|
+
start(c) {
|
|
4175
|
+
c.enqueue(first.value);
|
|
4176
|
+
},
|
|
4177
|
+
async pull(c) {
|
|
4178
|
+
const next = await reader.read();
|
|
4179
|
+
if (next.done)
|
|
4180
|
+
c.close();
|
|
4181
|
+
else
|
|
4182
|
+
c.enqueue(next.value);
|
|
4183
|
+
},
|
|
4184
|
+
cancel(reason) {
|
|
4185
|
+
void reader.cancel(reason).catch(() => void 0);
|
|
4186
|
+
}
|
|
4187
|
+
});
|
|
4188
|
+
const out = rehydrated.pipeThrough(decoder);
|
|
4189
|
+
const pump = out.getReader();
|
|
4190
|
+
void (async () => {
|
|
4191
|
+
try {
|
|
4192
|
+
for (; ; ) {
|
|
4193
|
+
const chunk = await pump.read();
|
|
4194
|
+
if (chunk.done) {
|
|
4195
|
+
controller.close();
|
|
4196
|
+
return;
|
|
4197
|
+
}
|
|
4198
|
+
controller.enqueue(chunk.value);
|
|
4199
|
+
}
|
|
4200
|
+
} catch (err) {
|
|
4201
|
+
controller.error(err);
|
|
4202
|
+
}
|
|
4203
|
+
})();
|
|
4204
|
+
},
|
|
4205
|
+
cancel(reason) {
|
|
4206
|
+
void reader.cancel(reason).catch(() => void 0);
|
|
4207
|
+
}
|
|
4208
|
+
});
|
|
4209
|
+
}
|
|
4428
4210
|
function brotliDecompressStream(stream) {
|
|
4429
4211
|
if (!isNodeRuntime())
|
|
4430
4212
|
return stream;
|
|
@@ -4441,6 +4223,7 @@ var kinetex = (() => {
|
|
|
4441
4223
|
let settled = false;
|
|
4442
4224
|
let reader = null;
|
|
4443
4225
|
let cancelRequested = false;
|
|
4226
|
+
let sawBytes = false;
|
|
4444
4227
|
return new ReadableStream({
|
|
4445
4228
|
async start(controller) {
|
|
4446
4229
|
const zlib = await import("node:zlib");
|
|
@@ -4474,9 +4257,19 @@ var kinetex = (() => {
|
|
|
4474
4257
|
while (true) {
|
|
4475
4258
|
const { done, value } = await reader.read();
|
|
4476
4259
|
if (done) {
|
|
4260
|
+
if (!sawBytes) {
|
|
4261
|
+
if (!settled) {
|
|
4262
|
+
settled = true;
|
|
4263
|
+
controller.close();
|
|
4264
|
+
}
|
|
4265
|
+
br.destroy();
|
|
4266
|
+
break;
|
|
4267
|
+
}
|
|
4477
4268
|
br.end();
|
|
4478
4269
|
break;
|
|
4479
4270
|
}
|
|
4271
|
+
if (value.byteLength > 0)
|
|
4272
|
+
sawBytes = true;
|
|
4480
4273
|
br.write(value);
|
|
4481
4274
|
}
|
|
4482
4275
|
} catch (e) {
|
|
@@ -4874,9 +4667,9 @@ var kinetex = (() => {
|
|
|
4874
4667
|
};
|
|
4875
4668
|
}).filter((m) => m.name);
|
|
4876
4669
|
}
|
|
4877
|
-
|
|
4670
|
+
function readBodyBytes(response, url, options) {
|
|
4878
4671
|
if (!response.body)
|
|
4879
|
-
return new Uint8Array(0);
|
|
4672
|
+
return Promise.resolve(new Uint8Array(0));
|
|
4880
4673
|
const headers = normalizeHeaders2(response.headers);
|
|
4881
4674
|
let stream = response.body;
|
|
4882
4675
|
if (options.decompress !== false) {
|
|
@@ -4888,30 +4681,7 @@ var kinetex = (() => {
|
|
|
4888
4681
|
if (options.sizeLimit) {
|
|
4889
4682
|
return readBodyWithLimit(stream, url, options.sizeLimit, options.signal);
|
|
4890
4683
|
}
|
|
4891
|
-
|
|
4892
|
-
const chunks = [];
|
|
4893
|
-
try {
|
|
4894
|
-
while (true) {
|
|
4895
|
-
if (options.signal?.aborted) {
|
|
4896
|
-
throw new DOMException("Response reading aborted", "AbortError");
|
|
4897
|
-
}
|
|
4898
|
-
const { done, value } = await readWithAbort(reader, options.signal);
|
|
4899
|
-
if (done) {
|
|
4900
|
-
if (options.signal?.aborted) {
|
|
4901
|
-
throw new DOMException("Response reading aborted", "AbortError");
|
|
4902
|
-
}
|
|
4903
|
-
break;
|
|
4904
|
-
}
|
|
4905
|
-
chunks.push(value);
|
|
4906
|
-
}
|
|
4907
|
-
} finally {
|
|
4908
|
-
if (options.signal?.aborted) {
|
|
4909
|
-
await reader.cancel("aborted").catch(() => {
|
|
4910
|
-
});
|
|
4911
|
-
}
|
|
4912
|
-
reader.releaseLock();
|
|
4913
|
-
}
|
|
4914
|
-
return concatUint8Arrays2(chunks);
|
|
4684
|
+
return readBodyWithLimit(stream, url, { maxBytes: Infinity }, options.signal);
|
|
4915
4685
|
}
|
|
4916
4686
|
function applySizeLimit(stream, url, limit, signal) {
|
|
4917
4687
|
const onExceed = normaliseOnExceed(limit.onExceed);
|
|
@@ -4937,27 +4707,20 @@ var kinetex = (() => {
|
|
|
4937
4707
|
controller.close();
|
|
4938
4708
|
break;
|
|
4939
4709
|
}
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
limit
|
|
4943
|
-
|
|
4944
|
-
|
|
4945
|
-
|
|
4946
|
-
|
|
4947
|
-
|
|
4948
|
-
|
|
4949
|
-
|
|
4950
|
-
controller.
|
|
4951
|
-
|
|
4952
|
-
|
|
4953
|
-
|
|
4954
|
-
const remaining = limit.maxBytes - bytesRead;
|
|
4955
|
-
if (remaining > 0)
|
|
4956
|
-
controller.enqueue(value.slice(0, remaining));
|
|
4957
|
-
controller.close();
|
|
4958
|
-
await reader.cancel("size limit");
|
|
4959
|
-
return;
|
|
4960
|
-
}
|
|
4710
|
+
let action;
|
|
4711
|
+
try {
|
|
4712
|
+
action = limitAction(value, bytesRead, limit, onExceed, (n, max) => new ResponseSizeLimitError(n, max, url));
|
|
4713
|
+
} catch (err) {
|
|
4714
|
+
await reader.cancel("size limit exceeded");
|
|
4715
|
+
controller.error(err);
|
|
4716
|
+
return;
|
|
4717
|
+
}
|
|
4718
|
+
if (action.kind !== "keep") {
|
|
4719
|
+
if (action.kind === "truncate")
|
|
4720
|
+
controller.enqueue(action.chunk);
|
|
4721
|
+
controller.close();
|
|
4722
|
+
await reader.cancel("size limit exceeded");
|
|
4723
|
+
return;
|
|
4961
4724
|
}
|
|
4962
4725
|
bytesRead += value.byteLength;
|
|
4963
4726
|
controller.enqueue(value);
|
|
@@ -5195,6 +4958,330 @@ var kinetex = (() => {
|
|
|
5195
4958
|
}
|
|
5196
4959
|
});
|
|
5197
4960
|
|
|
4961
|
+
// dist/esm/proxy.js
|
|
4962
|
+
var proxy_exports = {};
|
|
4963
|
+
__export(proxy_exports, {
|
|
4964
|
+
connectThroughProxy: () => connectThroughProxy
|
|
4965
|
+
});
|
|
4966
|
+
function isIpLiteral(host) {
|
|
4967
|
+
const h = host.startsWith("[") && host.endsWith("]") ? host.slice(1, -1) : host;
|
|
4968
|
+
return h.includes(":") || /^\d+(\.\d+)*$/.test(h);
|
|
4969
|
+
}
|
|
4970
|
+
function isSocksProxy(url) {
|
|
4971
|
+
return /^socks5h?:/i.test(url);
|
|
4972
|
+
}
|
|
4973
|
+
function proxyAuthHeader(proxy) {
|
|
4974
|
+
if (proxy.username === void 0)
|
|
4975
|
+
return void 0;
|
|
4976
|
+
const raw = `${proxy.username}:${proxy.password ?? ""}`;
|
|
4977
|
+
const bytes = new TextEncoder().encode(raw);
|
|
4978
|
+
let binary = "";
|
|
4979
|
+
for (const b of bytes)
|
|
4980
|
+
binary += String.fromCharCode(b);
|
|
4981
|
+
const b64 = typeof globalThis.btoa === "function" ? globalThis.btoa(binary) : (
|
|
4982
|
+
// Node without btoa (or a non-Latin1 username) — encode explicitly.
|
|
4983
|
+
Buffer.from(bytes).toString("base64")
|
|
4984
|
+
);
|
|
4985
|
+
return `Basic ${b64}`;
|
|
4986
|
+
}
|
|
4987
|
+
function parseConnectStatus(head) {
|
|
4988
|
+
const statusLine = head.split("\r\n", 1)[0] ?? "";
|
|
4989
|
+
const match = /^HTTP\/1\.[01] (\d{3})(?: (.*))?$/.exec(statusLine);
|
|
4990
|
+
if (!match)
|
|
4991
|
+
return null;
|
|
4992
|
+
return { status: Number(match[1]), reason: match[2] ?? "" };
|
|
4993
|
+
}
|
|
4994
|
+
async function connectThroughProxy(proxy, target, options = {}) {
|
|
4995
|
+
const errMeta = options.request !== void 0 ? { request: options.request } : {};
|
|
4996
|
+
if (isSocksProxy(proxy.url)) {
|
|
4997
|
+
throw new KinetexError(`proxy url "${proxy.url}" is a SOCKS proxy, which needs a different mechanism. Use createSocks5Tunnel() from kinetex/socks5 and supply your own fetch over the tunnel.`, "EVALIDATION", errMeta);
|
|
4998
|
+
}
|
|
4999
|
+
let proxyUrl;
|
|
5000
|
+
try {
|
|
5001
|
+
proxyUrl = new URL(proxy.url);
|
|
5002
|
+
} catch (err) {
|
|
5003
|
+
throw new KinetexError(`proxy url "${proxy.url}" is not a valid URL`, "EVALIDATION", {
|
|
5004
|
+
...errMeta,
|
|
5005
|
+
cause: err
|
|
5006
|
+
});
|
|
5007
|
+
}
|
|
5008
|
+
if (proxyUrl.protocol !== "http:" && proxyUrl.protocol !== "https:") {
|
|
5009
|
+
throw new KinetexError(`proxy url scheme "${proxyUrl.protocol}" is not supported; use http:, https:, socks5: or socks5h:`, "EVALIDATION", errMeta);
|
|
5010
|
+
}
|
|
5011
|
+
const net = await import("node:net");
|
|
5012
|
+
const tls = await import("node:tls");
|
|
5013
|
+
const proxyPort = proxyUrl.port ? Number(proxyUrl.port) : proxyUrl.protocol === "https:" ? 443 : 80;
|
|
5014
|
+
const targetPort = target.port ? Number(target.port) : target.protocol === "https:" ? 443 : 80;
|
|
5015
|
+
const authority = `${target.hostname}:${targetPort}`;
|
|
5016
|
+
const timeoutMs = options.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS;
|
|
5017
|
+
const baseSocket = proxyUrl.protocol === "https:" ? await openTlsSocket(tls, {
|
|
5018
|
+
host: proxyUrl.hostname,
|
|
5019
|
+
port: proxyPort,
|
|
5020
|
+
servername: proxyUrl.hostname,
|
|
5021
|
+
...options.ca !== void 0 ? { ca: options.ca } : {}
|
|
5022
|
+
}, timeoutMs, options.signal, errMeta, `proxy ${proxyUrl.host}`) : await openTcpSocket(net, { host: proxyUrl.hostname, port: proxyPort }, timeoutMs, options.signal, errMeta, `proxy ${proxyUrl.host}`);
|
|
5023
|
+
const auth = proxyAuthHeader(proxy);
|
|
5024
|
+
const connectLines = [
|
|
5025
|
+
`CONNECT ${authority} HTTP/1.1`,
|
|
5026
|
+
`Host: ${authority}`,
|
|
5027
|
+
// RFC 9110 §9.3.6: proxies that support persistent tunnels are expected to
|
|
5028
|
+
// treat a tunnel as still-open even without this, but sending it is what
|
|
5029
|
+
// every real client does and some proxies misbehave without it.
|
|
5030
|
+
"Proxy-Connection: Keep-Alive",
|
|
5031
|
+
...auth !== void 0 ? [`Proxy-Authorization: ${auth}`] : [],
|
|
5032
|
+
"",
|
|
5033
|
+
""
|
|
5034
|
+
];
|
|
5035
|
+
baseSocket.write(connectLines.join("\r\n"));
|
|
5036
|
+
let head = "";
|
|
5037
|
+
const headResult = await new Promise((resolve) => {
|
|
5038
|
+
const drain = () => {
|
|
5039
|
+
for (; ; ) {
|
|
5040
|
+
const chunk = baseSocket.read();
|
|
5041
|
+
if (chunk === null)
|
|
5042
|
+
return;
|
|
5043
|
+
head += chunk.toString("latin1");
|
|
5044
|
+
}
|
|
5045
|
+
};
|
|
5046
|
+
const onReadable = () => {
|
|
5047
|
+
drain();
|
|
5048
|
+
const end = head.indexOf("\r\n\r\n");
|
|
5049
|
+
if (end === -1) {
|
|
5050
|
+
if (head.length > MAX_HEAD_BYTES) {
|
|
5051
|
+
cleanup();
|
|
5052
|
+
resolve({
|
|
5053
|
+
ok: false,
|
|
5054
|
+
err: new KinetexError("Proxy sent an oversized CONNECT response", "EPROXY", errMeta)
|
|
5055
|
+
});
|
|
5056
|
+
}
|
|
5057
|
+
return;
|
|
5058
|
+
}
|
|
5059
|
+
cleanup();
|
|
5060
|
+
const parsed = parseConnectStatus(head.slice(0, end));
|
|
5061
|
+
if (!parsed) {
|
|
5062
|
+
resolve({
|
|
5063
|
+
ok: false,
|
|
5064
|
+
err: new KinetexError(`Proxy returned a malformed CONNECT response: ${JSON.stringify(head.slice(0, end).split("\r\n", 1)[0] ?? "")}`, "EPROXY", errMeta)
|
|
5065
|
+
});
|
|
5066
|
+
return;
|
|
5067
|
+
}
|
|
5068
|
+
if (parsed.status >= 200 && parsed.status < 300) {
|
|
5069
|
+
const tail = head.slice(end + 4);
|
|
5070
|
+
if (tail.length > 0)
|
|
5071
|
+
baseSocket.unshift(Buffer.from(tail, "latin1"));
|
|
5072
|
+
resolve({ ok: true });
|
|
5073
|
+
return;
|
|
5074
|
+
}
|
|
5075
|
+
if (parsed.status === 407) {
|
|
5076
|
+
resolve({
|
|
5077
|
+
ok: false,
|
|
5078
|
+
err: new KinetexError("Proxy requires authentication (407). Set `username`/`password` on the proxy config, or embed them in the proxy URL.", "EPROXY", errMeta)
|
|
5079
|
+
});
|
|
5080
|
+
return;
|
|
5081
|
+
}
|
|
5082
|
+
resolve({
|
|
5083
|
+
ok: false,
|
|
5084
|
+
err: new KinetexError(`Proxy refused CONNECT to ${authority}: ${parsed.status} ${parsed.reason}`.trim(), "EPROXY", errMeta)
|
|
5085
|
+
});
|
|
5086
|
+
};
|
|
5087
|
+
const onError = (e) => {
|
|
5088
|
+
cleanup();
|
|
5089
|
+
resolve({
|
|
5090
|
+
ok: false,
|
|
5091
|
+
err: new KinetexError(`Proxy connection failed: ${e.message}`, "ENETWORK", {
|
|
5092
|
+
...errMeta,
|
|
5093
|
+
cause: e
|
|
5094
|
+
})
|
|
5095
|
+
});
|
|
5096
|
+
};
|
|
5097
|
+
const onClosedEarly = () => {
|
|
5098
|
+
cleanup();
|
|
5099
|
+
resolve({
|
|
5100
|
+
ok: false,
|
|
5101
|
+
err: new KinetexError(`Proxy closed the connection before answering CONNECT for ${authority}`, "EPROXY", errMeta)
|
|
5102
|
+
});
|
|
5103
|
+
};
|
|
5104
|
+
const onTimeout = () => {
|
|
5105
|
+
cleanup();
|
|
5106
|
+
baseSocket.destroy();
|
|
5107
|
+
resolve({
|
|
5108
|
+
ok: false,
|
|
5109
|
+
err: new KinetexError(`Proxy did not respond to CONNECT for ${authority} within ${timeoutMs}ms`, "ETIMEOUT", errMeta)
|
|
5110
|
+
});
|
|
5111
|
+
};
|
|
5112
|
+
const onAbort = () => {
|
|
5113
|
+
cleanup();
|
|
5114
|
+
baseSocket.destroy();
|
|
5115
|
+
resolve({
|
|
5116
|
+
ok: false,
|
|
5117
|
+
err: new KinetexError("Proxy connection aborted", "EABORT", errMeta)
|
|
5118
|
+
});
|
|
5119
|
+
};
|
|
5120
|
+
const timer = setTimeout(onTimeout, timeoutMs);
|
|
5121
|
+
if (typeof timer.unref === "function") {
|
|
5122
|
+
timer.unref();
|
|
5123
|
+
}
|
|
5124
|
+
function cleanup() {
|
|
5125
|
+
clearTimeout(timer);
|
|
5126
|
+
baseSocket.off("readable", onReadable);
|
|
5127
|
+
baseSocket.off("error", onError);
|
|
5128
|
+
baseSocket.off("end", onClosedEarly);
|
|
5129
|
+
baseSocket.off("close", onClosedEarly);
|
|
5130
|
+
baseSocket.off("timeout", onTimeout);
|
|
5131
|
+
options.signal?.removeEventListener("abort", onAbort);
|
|
5132
|
+
}
|
|
5133
|
+
baseSocket.on("readable", onReadable);
|
|
5134
|
+
baseSocket.once("error", onError);
|
|
5135
|
+
baseSocket.once("end", onClosedEarly);
|
|
5136
|
+
baseSocket.once("close", onClosedEarly);
|
|
5137
|
+
baseSocket.setTimeout(timeoutMs, onTimeout);
|
|
5138
|
+
options.signal?.addEventListener("abort", onAbort, { once: true });
|
|
5139
|
+
drain();
|
|
5140
|
+
});
|
|
5141
|
+
if (!headResult.ok) {
|
|
5142
|
+
baseSocket.destroy();
|
|
5143
|
+
throw headResult.err;
|
|
5144
|
+
}
|
|
5145
|
+
if (target.protocol !== "https:") {
|
|
5146
|
+
baseSocket.setTimeout(0);
|
|
5147
|
+
return baseSocket;
|
|
5148
|
+
}
|
|
5149
|
+
return await new Promise((resolve, reject) => {
|
|
5150
|
+
const onTunnelTimeout = () => {
|
|
5151
|
+
done();
|
|
5152
|
+
baseSocket.destroy();
|
|
5153
|
+
reject(new KinetexError(`TLS handshake through proxy to ${target.hostname} did not complete within ${timeoutMs}ms`, "ETIMEOUT", errMeta));
|
|
5154
|
+
};
|
|
5155
|
+
const onTunnelAbort = () => {
|
|
5156
|
+
done();
|
|
5157
|
+
baseSocket.destroy();
|
|
5158
|
+
reject(new KinetexError("Proxy connection aborted", "EABORT", errMeta));
|
|
5159
|
+
};
|
|
5160
|
+
const tunnelTimer = setTimeout(onTunnelTimeout, timeoutMs);
|
|
5161
|
+
if (typeof tunnelTimer.unref === "function") {
|
|
5162
|
+
tunnelTimer.unref();
|
|
5163
|
+
}
|
|
5164
|
+
options.signal?.addEventListener("abort", onTunnelAbort, { once: true });
|
|
5165
|
+
function done() {
|
|
5166
|
+
clearTimeout(tunnelTimer);
|
|
5167
|
+
options.signal?.removeEventListener("abort", onTunnelAbort);
|
|
5168
|
+
}
|
|
5169
|
+
const sni = options.servername ?? target.hostname;
|
|
5170
|
+
const secure = tls.connect(
|
|
5171
|
+
{
|
|
5172
|
+
socket: baseSocket,
|
|
5173
|
+
...sni !== void 0 && !isIpLiteral(sni) ? { servername: sni } : {},
|
|
5174
|
+
...options.ca !== void 0 ? { ca: options.ca } : {}
|
|
5175
|
+
},
|
|
5176
|
+
// `rejectUnauthorized` is left at its default (true), so a peer that
|
|
5177
|
+
// fails verification emits `error` rather than reaching this callback.
|
|
5178
|
+
// Verification therefore cannot be bypassed here, and the callback
|
|
5179
|
+
// only ever runs for a verified handshake.
|
|
5180
|
+
() => {
|
|
5181
|
+
done();
|
|
5182
|
+
baseSocket.setTimeout(0);
|
|
5183
|
+
resolve(secure);
|
|
5184
|
+
}
|
|
5185
|
+
);
|
|
5186
|
+
secure.once("error", (e) => {
|
|
5187
|
+
done();
|
|
5188
|
+
baseSocket.destroy();
|
|
5189
|
+
reject(new KinetexError(`TLS handshake through proxy failed for ${target.hostname}: ${e.message}`, "ENETWORK", { ...errMeta, cause: e }));
|
|
5190
|
+
});
|
|
5191
|
+
});
|
|
5192
|
+
}
|
|
5193
|
+
async function openTcpSocket(net, opts, timeoutMs, signal, errMeta, what) {
|
|
5194
|
+
return await new Promise((resolve, reject) => {
|
|
5195
|
+
const socket = net.connect({ host: opts.host, port: opts.port });
|
|
5196
|
+
const onError = (e) => {
|
|
5197
|
+
cleanup();
|
|
5198
|
+
reject(new KinetexError(`Cannot reach ${what}: ${e.message}`, "ENETWORK", {
|
|
5199
|
+
...errMeta,
|
|
5200
|
+
cause: e
|
|
5201
|
+
}));
|
|
5202
|
+
};
|
|
5203
|
+
const timer = setTimeout(() => {
|
|
5204
|
+
cleanup();
|
|
5205
|
+
socket.destroy();
|
|
5206
|
+
reject(new KinetexError(`Connecting to ${what} timed out`, "ETIMEOUT", errMeta));
|
|
5207
|
+
}, timeoutMs);
|
|
5208
|
+
if (typeof timer.unref === "function") {
|
|
5209
|
+
timer.unref();
|
|
5210
|
+
}
|
|
5211
|
+
const onAbort = () => {
|
|
5212
|
+
cleanup();
|
|
5213
|
+
socket.destroy();
|
|
5214
|
+
reject(new KinetexError("Proxy connection aborted", "EABORT", errMeta));
|
|
5215
|
+
};
|
|
5216
|
+
function cleanup() {
|
|
5217
|
+
clearTimeout(timer);
|
|
5218
|
+
socket.off("error", onError);
|
|
5219
|
+
signal?.removeEventListener("abort", onAbort);
|
|
5220
|
+
}
|
|
5221
|
+
socket.once("error", onError);
|
|
5222
|
+
socket.once("connect", () => {
|
|
5223
|
+
cleanup();
|
|
5224
|
+
resolve(socket);
|
|
5225
|
+
});
|
|
5226
|
+
if (signal?.aborted)
|
|
5227
|
+
onAbort();
|
|
5228
|
+
else
|
|
5229
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
5230
|
+
});
|
|
5231
|
+
}
|
|
5232
|
+
async function openTlsSocket(tls, opts, timeoutMs, signal, errMeta, what) {
|
|
5233
|
+
return await new Promise((resolve, reject) => {
|
|
5234
|
+
const socket = tls.connect({
|
|
5235
|
+
host: opts.host,
|
|
5236
|
+
port: opts.port,
|
|
5237
|
+
...isIpLiteral(opts.servername) ? {} : { servername: opts.servername },
|
|
5238
|
+
...opts.ca !== void 0 ? { ca: opts.ca } : {}
|
|
5239
|
+
}, () => {
|
|
5240
|
+
cleanup();
|
|
5241
|
+
resolve(socket);
|
|
5242
|
+
});
|
|
5243
|
+
const onError = (e) => {
|
|
5244
|
+
cleanup();
|
|
5245
|
+
reject(new KinetexError(`Cannot reach ${what}: ${e.message}`, "ENETWORK", {
|
|
5246
|
+
...errMeta,
|
|
5247
|
+
cause: e
|
|
5248
|
+
}));
|
|
5249
|
+
};
|
|
5250
|
+
const timer = setTimeout(() => {
|
|
5251
|
+
cleanup();
|
|
5252
|
+
socket.destroy();
|
|
5253
|
+
reject(new KinetexError(`Connecting to ${what} timed out`, "ETIMEOUT", errMeta));
|
|
5254
|
+
}, timeoutMs);
|
|
5255
|
+
if (typeof timer.unref === "function") {
|
|
5256
|
+
timer.unref();
|
|
5257
|
+
}
|
|
5258
|
+
const onAbort = () => {
|
|
5259
|
+
cleanup();
|
|
5260
|
+
socket.destroy();
|
|
5261
|
+
reject(new KinetexError("Proxy connection aborted", "EABORT", errMeta));
|
|
5262
|
+
};
|
|
5263
|
+
function cleanup() {
|
|
5264
|
+
clearTimeout(timer);
|
|
5265
|
+
socket.off("error", onError);
|
|
5266
|
+
signal?.removeEventListener("abort", onAbort);
|
|
5267
|
+
}
|
|
5268
|
+
socket.once("error", onError);
|
|
5269
|
+
if (signal?.aborted)
|
|
5270
|
+
onAbort();
|
|
5271
|
+
else
|
|
5272
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
5273
|
+
});
|
|
5274
|
+
}
|
|
5275
|
+
var DEFAULT_CONNECT_TIMEOUT_MS, MAX_HEAD_BYTES;
|
|
5276
|
+
var init_proxy = __esm({
|
|
5277
|
+
"dist/esm/proxy.js"() {
|
|
5278
|
+
"use strict";
|
|
5279
|
+
init_types();
|
|
5280
|
+
DEFAULT_CONNECT_TIMEOUT_MS = 3e4;
|
|
5281
|
+
MAX_HEAD_BYTES = 64 * 1024;
|
|
5282
|
+
}
|
|
5283
|
+
});
|
|
5284
|
+
|
|
5198
5285
|
// dist/esm/core.js
|
|
5199
5286
|
var core_exports = {};
|
|
5200
5287
|
__export(core_exports, {
|
|
@@ -5205,10 +5292,13 @@ var kinetex = (() => {
|
|
|
5205
5292
|
IS_NODE: () => IS_NODE,
|
|
5206
5293
|
NodeHTTP2Transport: () => NodeHTTP2Transport,
|
|
5207
5294
|
RUNTIME: () => RUNTIME,
|
|
5295
|
+
blockedHostError: () => blockedHostError,
|
|
5296
|
+
createPinnedLookup: () => createPinnedLookup,
|
|
5208
5297
|
createTransport: () => createTransport,
|
|
5209
5298
|
decompressBodyStream: () => decompressBodyStream,
|
|
5210
5299
|
detectRuntime: () => detectRuntime,
|
|
5211
5300
|
encodeMultipart: () => encodeMultipart,
|
|
5301
|
+
filterSafeAddresses: () => filterSafeAddresses,
|
|
5212
5302
|
getEffectiveRuntime: () => getEffectiveRuntime,
|
|
5213
5303
|
normalizeHeaders: () => normalizeHeaders3,
|
|
5214
5304
|
parseBody: () => parseBody,
|
|
@@ -5274,6 +5364,7 @@ var kinetex = (() => {
|
|
|
5274
5364
|
...sessionOptions?.connectTimeoutMs !== void 0 ? { connectTimeoutMs: sessionOptions.connectTimeoutMs } : {},
|
|
5275
5365
|
...sessionOptions?.requestTimeoutMs !== void 0 ? { requestTimeoutMs: sessionOptions.requestTimeoutMs } : {},
|
|
5276
5366
|
...transportOptions?.strict !== void 0 ? { strict: transportOptions.strict } : {},
|
|
5367
|
+
...transportOptions?.allowPrivateNetwork !== void 0 ? { allowPrivateNetwork: transportOptions.allowPrivateNetwork } : {},
|
|
5277
5368
|
...transportOptions?.onDroppedHeader !== void 0 ? { onDroppedHeader: transportOptions.onDroppedHeader } : {},
|
|
5278
5369
|
...transportOptions?.ca !== void 0 ? { ca: transportOptions.ca } : {},
|
|
5279
5370
|
...transportOptions?.proxy !== void 0 ? { proxy: transportOptions.proxy } : {}
|
|
@@ -5283,7 +5374,11 @@ var kinetex = (() => {
|
|
|
5283
5374
|
fetchFn: fetchFn ?? globalThis.fetch,
|
|
5284
5375
|
...transportOptions?.strict !== void 0 ? { strict: transportOptions.strict } : {},
|
|
5285
5376
|
...transportOptions?.onDroppedHeader !== void 0 ? { onDroppedHeader: transportOptions.onDroppedHeader } : {},
|
|
5286
|
-
...transportOptions?.dispatcher !== void 0 ? { dispatcher: transportOptions.dispatcher } : {}
|
|
5377
|
+
...transportOptions?.dispatcher !== void 0 ? { dispatcher: transportOptions.dispatcher } : {},
|
|
5378
|
+
// Reaches the pre-flight DNS screen in FetchTransport. It was accepted by
|
|
5379
|
+
// createTransport and then dropped, so every fetch-based runtime silently
|
|
5380
|
+
// had the screen off.
|
|
5381
|
+
...transportOptions?.allowPrivateNetwork !== void 0 ? { allowPrivateNetwork: transportOptions.allowPrivateNetwork } : {}
|
|
5287
5382
|
});
|
|
5288
5383
|
}
|
|
5289
5384
|
async function sendWithTimeout(transport, request, timeoutMs) {
|
|
@@ -5348,10 +5443,11 @@ var kinetex = (() => {
|
|
|
5348
5443
|
if (done)
|
|
5349
5444
|
break;
|
|
5350
5445
|
if (maxBytes > 0) {
|
|
5351
|
-
|
|
5352
|
-
|
|
5446
|
+
try {
|
|
5447
|
+
limitAction(value, total, { maxBytes }, "throw", (n, max) => new SizeLimitError(n, max));
|
|
5448
|
+
} catch (err) {
|
|
5353
5449
|
await reader.cancel("size limit exceeded");
|
|
5354
|
-
throw
|
|
5450
|
+
throw err;
|
|
5355
5451
|
}
|
|
5356
5452
|
}
|
|
5357
5453
|
chunks.push(value);
|
|
@@ -5410,6 +5506,41 @@ var kinetex = (() => {
|
|
|
5410
5506
|
}
|
|
5411
5507
|
return raw;
|
|
5412
5508
|
}
|
|
5509
|
+
function filterSafeAddresses(addresses) {
|
|
5510
|
+
return addresses.filter((a) => a.family !== 0 && !isBlockedAddress(a.address));
|
|
5511
|
+
}
|
|
5512
|
+
function blockedHostError(url, addresses) {
|
|
5513
|
+
return new KinetexError(`URL "${url}" resolved only to blocked addresses (${addresses.map((a) => a.address).join(", ") || "none"})`, "EBLOCKEDHOST");
|
|
5514
|
+
}
|
|
5515
|
+
function createPinnedLookup() {
|
|
5516
|
+
return (hostname, options, callback) => {
|
|
5517
|
+
const fail = (err) => {
|
|
5518
|
+
callback(err);
|
|
5519
|
+
};
|
|
5520
|
+
const wantsAll = options.all === true;
|
|
5521
|
+
const resolved = { ...options, all: true };
|
|
5522
|
+
(0, import_node_dns.lookup)(hostname, resolved, (err, addresses) => {
|
|
5523
|
+
if (err) {
|
|
5524
|
+
fail(err);
|
|
5525
|
+
return;
|
|
5526
|
+
}
|
|
5527
|
+
const safe = filterSafeAddresses(addresses ?? []);
|
|
5528
|
+
if (safe.length === 0) {
|
|
5529
|
+
const blocked = (addresses ?? []).map((a) => a.address).join(", ") || "no addresses";
|
|
5530
|
+
const e = new Error(`Refusing to connect to ${hostname}: it resolves only to a blocked address (${blocked})`);
|
|
5531
|
+
e.code = "EBLOCKEDHOST";
|
|
5532
|
+
fail(e);
|
|
5533
|
+
return;
|
|
5534
|
+
}
|
|
5535
|
+
if (wantsAll) {
|
|
5536
|
+
callback(null, safe);
|
|
5537
|
+
return;
|
|
5538
|
+
}
|
|
5539
|
+
const first = safe[0];
|
|
5540
|
+
callback(null, first.address, first.family);
|
|
5541
|
+
});
|
|
5542
|
+
};
|
|
5543
|
+
}
|
|
5413
5544
|
function normalizeHeaders3(headers) {
|
|
5414
5545
|
return normalizeHeaders(headers);
|
|
5415
5546
|
}
|
|
@@ -5615,14 +5746,17 @@ Content-Type: ${value.type || "application/octet-stream"}\r
|
|
|
5615
5746
|
const { applyDecompression: applyDecompression2 } = await Promise.resolve().then(() => (init_response(), response_exports));
|
|
5616
5747
|
return applyDecompression2(body, headers);
|
|
5617
5748
|
}
|
|
5618
|
-
var g, RUNTIME, _runtimeOverride, KNOWN_RUNTIMES, IS_NODE, HAS_NATIVE_FETCH, DEFAULT_ACCEPT_ENCODING, FetchTransport, NodeHTTP2Transport, nodeReadableToWebStream, HTTP_STATUS_TEXTS;
|
|
5749
|
+
var import_node_dns, import_node_net, g, RUNTIME, _runtimeOverride, KNOWN_RUNTIMES, IS_NODE, HAS_NATIVE_FETCH, DEFAULT_ACCEPT_ENCODING, FetchTransport, NodeHTTP2Transport, PINNED_LOOKUP, nodeReadableToWebStream, HTTP_STATUS_TEXTS;
|
|
5619
5750
|
var init_core = __esm({
|
|
5620
5751
|
"dist/esm/core.js"() {
|
|
5621
5752
|
"use strict";
|
|
5622
5753
|
init_types();
|
|
5623
5754
|
init_utils();
|
|
5624
5755
|
init_headers();
|
|
5756
|
+
import_node_dns = __require("node:dns");
|
|
5757
|
+
import_node_net = __require("node:net");
|
|
5625
5758
|
init_utils();
|
|
5759
|
+
init_response();
|
|
5626
5760
|
g = globalThis;
|
|
5627
5761
|
RUNTIME = detectRuntime();
|
|
5628
5762
|
_runtimeOverride = null;
|
|
@@ -5647,17 +5781,72 @@ Content-Type: ${value.type || "application/octet-stream"}\r
|
|
|
5647
5781
|
__publicField(this, "strict");
|
|
5648
5782
|
__publicField(this, "onDroppedHeader");
|
|
5649
5783
|
__publicField(this, "dispatcher");
|
|
5784
|
+
/** Per-request timeout; omitted means no timeout. */
|
|
5785
|
+
__publicField(this, "requestTimeoutMs");
|
|
5786
|
+
/**
|
|
5787
|
+
* Permit connections to hosts resolving into private/loopback space.
|
|
5788
|
+
* Default: false.
|
|
5789
|
+
*/
|
|
5790
|
+
__publicField(this, "_allowPrivateNetwork");
|
|
5650
5791
|
if (typeof fetchFnOrOptions === "function") {
|
|
5651
5792
|
this.fetchFn = fetchFnOrOptions;
|
|
5652
5793
|
this.strict = false;
|
|
5653
5794
|
this.onDroppedHeader = void 0;
|
|
5654
5795
|
this.dispatcher = void 0;
|
|
5796
|
+
this.requestTimeoutMs = void 0;
|
|
5797
|
+
this._allowPrivateNetwork = false;
|
|
5655
5798
|
} else {
|
|
5656
5799
|
this.fetchFn = fetchFnOrOptions.fetchFn ?? globalThis.fetch;
|
|
5657
5800
|
this.strict = fetchFnOrOptions.strict ?? false;
|
|
5658
5801
|
this.onDroppedHeader = fetchFnOrOptions.onDroppedHeader;
|
|
5659
5802
|
this.dispatcher = fetchFnOrOptions.dispatcher;
|
|
5803
|
+
this.requestTimeoutMs = fetchFnOrOptions.requestTimeoutMs;
|
|
5804
|
+
this._allowPrivateNetwork = fetchFnOrOptions.allowPrivateNetwork ?? false;
|
|
5805
|
+
}
|
|
5806
|
+
}
|
|
5807
|
+
/**
|
|
5808
|
+
* Refuse a host that resolves into blocked address space.
|
|
5809
|
+
*
|
|
5810
|
+
* The Node transports close DNS rebinding properly: they resolve once and
|
|
5811
|
+
* pin the checked address to the socket (`createPinnedLookup`). `fetch` has no
|
|
5812
|
+
* resolver hook — undici owns the connection and exposes no way to install
|
|
5813
|
+
* one — so the only place to intervene is before the call.
|
|
5814
|
+
*
|
|
5815
|
+
* That closes the case that matters in practice: a name resolving to loopback
|
|
5816
|
+
* or the cloud metadata address is refused outright, which is the vector
|
|
5817
|
+
* behind the classic SSRF-through-DNS attacks. What it cannot close is a
|
|
5818
|
+
* resolver that answers public on this lookup and private on the connect a
|
|
5819
|
+
* moment later. Closing *that* needs a resolver hook `fetch` does not offer,
|
|
5820
|
+
* and claiming otherwise would be worse than saying so.
|
|
5821
|
+
*
|
|
5822
|
+
* The host has already passed the lexical screen by this point, so this is a
|
|
5823
|
+
* second line of defence rather than the first.
|
|
5824
|
+
*/
|
|
5825
|
+
async assertReachableHost(url) {
|
|
5826
|
+
if (this._allowPrivateNetwork)
|
|
5827
|
+
return;
|
|
5828
|
+
if (this.fetchFn !== globalThis.fetch)
|
|
5829
|
+
return;
|
|
5830
|
+
let parsed;
|
|
5831
|
+
try {
|
|
5832
|
+
parsed = new URL(url);
|
|
5833
|
+
} catch {
|
|
5834
|
+
return;
|
|
5835
|
+
}
|
|
5836
|
+
if (!parsed.hostname || (0, import_node_net.isIP)(parsed.hostname))
|
|
5837
|
+
return;
|
|
5838
|
+
let addresses;
|
|
5839
|
+
try {
|
|
5840
|
+
addresses = await new Promise((resolve, reject) => {
|
|
5841
|
+
(0, import_node_dns.lookup)(parsed.hostname, { all: true }, (err, res) => err ? reject(err) : resolve(res));
|
|
5842
|
+
});
|
|
5843
|
+
} catch {
|
|
5844
|
+
return;
|
|
5660
5845
|
}
|
|
5846
|
+
const safe = filterSafeAddresses(addresses);
|
|
5847
|
+
if (safe.length > 0)
|
|
5848
|
+
return;
|
|
5849
|
+
throw blockedHostError(url, addresses);
|
|
5661
5850
|
}
|
|
5662
5851
|
/**
|
|
5663
5852
|
* Send a request via the native fetch() API.
|
|
@@ -5670,6 +5859,7 @@ Content-Type: ${value.type || "application/octet-stream"}\r
|
|
|
5670
5859
|
* @returns Raw response from the server
|
|
5671
5860
|
*/
|
|
5672
5861
|
async send(req) {
|
|
5862
|
+
await this.assertReachableHost(req.url);
|
|
5673
5863
|
const sanitizedHeaders = {};
|
|
5674
5864
|
for (const [name, value] of Object.entries(req.headers ?? {})) {
|
|
5675
5865
|
if (typeof name !== "string" || typeof value !== "string")
|
|
@@ -5716,15 +5906,41 @@ Content-Type: ${value.type || "application/octet-stream"}\r
|
|
|
5716
5906
|
if (this.dispatcher !== void 0) {
|
|
5717
5907
|
init.dispatcher = this.dispatcher;
|
|
5718
5908
|
}
|
|
5909
|
+
const timeoutMs = this.requestTimeoutMs;
|
|
5910
|
+
const timeoutCtl = timeoutMs === void 0 ? void 0 : new AbortController();
|
|
5911
|
+
let timedOut = false;
|
|
5912
|
+
const onCallerAbort = () => timeoutCtl?.abort();
|
|
5913
|
+
if (timeoutCtl && req.signal) {
|
|
5914
|
+
if (req.signal.aborted)
|
|
5915
|
+
timeoutCtl.abort();
|
|
5916
|
+
else
|
|
5917
|
+
req.signal.addEventListener("abort", onCallerAbort, { once: true });
|
|
5918
|
+
}
|
|
5919
|
+
const timer = timeoutCtl && timeoutMs !== void 0 ? setTimeout(() => {
|
|
5920
|
+
timedOut = true;
|
|
5921
|
+
timeoutCtl.abort();
|
|
5922
|
+
}, timeoutMs) : void 0;
|
|
5923
|
+
const initWithSignal = timeoutCtl && !init.signal ? { ...init, signal: timeoutCtl.signal } : init;
|
|
5719
5924
|
let response;
|
|
5720
5925
|
try {
|
|
5721
|
-
response = await this.fetchFn(req.url,
|
|
5926
|
+
response = await this.fetchFn(req.url, initWithSignal);
|
|
5722
5927
|
} catch (err) {
|
|
5928
|
+
if (timer !== void 0)
|
|
5929
|
+
clearTimeout(timer);
|
|
5930
|
+
if (timeoutCtl && req.signal)
|
|
5931
|
+
req.signal.removeEventListener("abort", onCallerAbort);
|
|
5932
|
+
if (timedOut) {
|
|
5933
|
+
throw new KinetexError(`HTTP/1.1 request to ${req.url} timed out after ${timeoutMs}ms`, "ETIMEOUT", { request: req, cause: err });
|
|
5934
|
+
}
|
|
5723
5935
|
if (isAbortError(err)) {
|
|
5724
5936
|
throw new KinetexError("Request was aborted", "EABORT", { request: req, cause: err });
|
|
5725
5937
|
}
|
|
5726
5938
|
throw new KinetexError(err instanceof Error ? err.message : "Network request failed", "ENETWORK", { request: req, cause: err });
|
|
5727
5939
|
}
|
|
5940
|
+
if (timer !== void 0)
|
|
5941
|
+
clearTimeout(timer);
|
|
5942
|
+
if (timeoutCtl && req.signal)
|
|
5943
|
+
req.signal.removeEventListener("abort", onCallerAbort);
|
|
5728
5944
|
const headers = normalizeHeaders3(response.headers);
|
|
5729
5945
|
const httpVersion = detectHTTPVersion(response, headers);
|
|
5730
5946
|
let alreadyDecompressed = true;
|
|
@@ -5787,6 +6003,8 @@ Content-Type: ${value.type || "application/octet-stream"}\r
|
|
|
5787
6003
|
* sockets, which is a lot to hold open for a legacy fallback), and gives
|
|
5788
6004
|
* callers a way to opt out.
|
|
5789
6005
|
*/
|
|
6006
|
+
/** Whether to permit connections into private/loopback address space. */
|
|
6007
|
+
__publicField(this, "_allowPrivateNetwork");
|
|
5790
6008
|
__publicField(this, "_http1KeepAlive");
|
|
5791
6009
|
__publicField(this, "_http1MaxSockets");
|
|
5792
6010
|
__publicField(this, "_http1Agent", null);
|
|
@@ -5795,6 +6013,7 @@ Content-Type: ${value.type || "application/octet-stream"}\r
|
|
|
5795
6013
|
this.sessionTTLMs = options.sessionTTLMs ?? 5 * 6e4;
|
|
5796
6014
|
this.pingIntervalMs = options.pingIntervalMs ?? 3e4;
|
|
5797
6015
|
this.maxSessions = options.maxSessions ?? 100;
|
|
6016
|
+
this._allowPrivateNetwork = options.allowPrivateNetwork ?? false;
|
|
5798
6017
|
this._http1KeepAlive = options.http1KeepAlive ?? true;
|
|
5799
6018
|
this._http1MaxSockets = options.http1MaxSockets ?? 16;
|
|
5800
6019
|
this._proxy = options.proxy;
|
|
@@ -5856,6 +6075,9 @@ Content-Type: ${value.type || "application/octet-stream"}\r
|
|
|
5856
6075
|
const session = await new Promise((resolve, reject) => {
|
|
5857
6076
|
const s = http2.connect(origin, {
|
|
5858
6077
|
rejectUnauthorized: true,
|
|
6078
|
+
// Resolve-and-check, so a hostname that points at a private address is
|
|
6079
|
+
// refused before anything is dialled. See `createPinnedLookup`.
|
|
6080
|
+
...this._allowPrivateNetwork ? {} : { lookup: PINNED_LOOKUP },
|
|
5859
6081
|
...this._ca !== void 0 ? { ca: this._ca } : {},
|
|
5860
6082
|
// Already connected (and already TLS-wrapped for an https origin),
|
|
5861
6083
|
// so node:http2 must use it as-is rather than negotiating again.
|
|
@@ -6154,7 +6376,11 @@ Content-Type: ${value.type || "application/octet-stream"}\r
|
|
|
6154
6376
|
if (!this._http1Fallback) {
|
|
6155
6377
|
const opts = {
|
|
6156
6378
|
fetchFn: globalThis.fetch,
|
|
6157
|
-
strict: this._strict
|
|
6379
|
+
strict: this._strict,
|
|
6380
|
+
// The h2 path enforces `_requestTimeoutMs` itself; without passing it
|
|
6381
|
+
// down, `new NodeHTTP2Transport({ requestTimeoutMs: 500 })` hung for
|
|
6382
|
+
// the full response time on any non-https URL or HTTP/1.1 request.
|
|
6383
|
+
requestTimeoutMs: this._requestTimeoutMs
|
|
6158
6384
|
};
|
|
6159
6385
|
if (this._onDroppedHeader !== void 0)
|
|
6160
6386
|
opts.onDroppedHeader = this._onDroppedHeader;
|
|
@@ -6191,7 +6417,9 @@ Content-Type: ${value.type || "application/octet-stream"}\r
|
|
|
6191
6417
|
// `ca` was accepted by the transport but never reached this path, so a
|
|
6192
6418
|
// private or self-signed peer could not be reached without disabling
|
|
6193
6419
|
// verification process-wide.
|
|
6194
|
-
...this._ca !== void 0 ? { ca: this._ca } : {}
|
|
6420
|
+
...this._ca !== void 0 ? { ca: this._ca } : {},
|
|
6421
|
+
// Same reason as the HTTP/2 path above.
|
|
6422
|
+
...this._allowPrivateNetwork ? {} : { lookup: PINNED_LOOKUP }
|
|
6195
6423
|
};
|
|
6196
6424
|
return new Promise((resolve, reject) => {
|
|
6197
6425
|
const httpReq = https.request(options, (httpRes) => {
|
|
@@ -6306,6 +6534,7 @@ Content-Type: ${value.type || "application/octet-stream"}\r
|
|
|
6306
6534
|
this._http1Agent = null;
|
|
6307
6535
|
}
|
|
6308
6536
|
};
|
|
6537
|
+
PINNED_LOOKUP = createPinnedLookup();
|
|
6309
6538
|
nodeReadableToWebStream = nodeDuplexToReadable;
|
|
6310
6539
|
HTTP_STATUS_TEXTS = {
|
|
6311
6540
|
100: "Continue",
|
|
@@ -6534,7 +6763,7 @@ Content-Type: ${value.type || "application/octet-stream"}\r
|
|
|
6534
6763
|
const basic = delim >= 0 ? input.slice(0, delim) : "";
|
|
6535
6764
|
for (let j = 0; j < basic.length; j++) {
|
|
6536
6765
|
const cp = basic.charCodeAt(j);
|
|
6537
|
-
if (cp
|
|
6766
|
+
if (cp >= 128 || cp < 32 || cp === 127)
|
|
6538
6767
|
return input;
|
|
6539
6768
|
output.push(cp);
|
|
6540
6769
|
}
|
|
@@ -6590,6 +6819,9 @@ Content-Type: ${value.type || "application/octet-stream"}\r
|
|
|
6590
6819
|
}
|
|
6591
6820
|
}
|
|
6592
6821
|
function decodeIDNLabel(label) {
|
|
6822
|
+
if (label.includes(".")) {
|
|
6823
|
+
return label.split(".").map((part) => decodeIDNLabel(part)).join(".");
|
|
6824
|
+
}
|
|
6593
6825
|
if (!label.toLowerCase().startsWith("xn--")) {
|
|
6594
6826
|
return label;
|
|
6595
6827
|
}
|
|
@@ -9083,21 +9315,6 @@ Content-Type: ${value.type || "application/octet-stream"}\r
|
|
|
9083
9315
|
}
|
|
9084
9316
|
return { name: "Error", message: String(err) };
|
|
9085
9317
|
}
|
|
9086
|
-
function safeStringify2(value) {
|
|
9087
|
-
try {
|
|
9088
|
-
const seen = /* @__PURE__ */ new WeakSet();
|
|
9089
|
-
return JSON.stringify(value, (_k, v) => {
|
|
9090
|
-
if (v !== null && typeof v === "object") {
|
|
9091
|
-
if (seen.has(v))
|
|
9092
|
-
return "[circular]";
|
|
9093
|
-
seen.add(v);
|
|
9094
|
-
}
|
|
9095
|
-
return v;
|
|
9096
|
-
}) ?? String(value);
|
|
9097
|
-
} catch {
|
|
9098
|
-
return String(value);
|
|
9099
|
-
}
|
|
9100
|
-
}
|
|
9101
9318
|
function truncateToBytes(str, maxBytes) {
|
|
9102
9319
|
const enc = new TextEncoder();
|
|
9103
9320
|
const total = enc.encode(str).byteLength;
|
|
@@ -9114,9 +9331,6 @@ Content-Type: ${value.type || "application/octet-stream"}\r
|
|
|
9114
9331
|
}
|
|
9115
9332
|
return { text: str.slice(0, end), removed: total - used };
|
|
9116
9333
|
}
|
|
9117
|
-
function perfNow3() {
|
|
9118
|
-
return typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
9119
|
-
}
|
|
9120
9334
|
function createLogger(config) {
|
|
9121
9335
|
return new HTTPLogger(config);
|
|
9122
9336
|
}
|
|
@@ -9148,7 +9362,7 @@ Content-Type: ${value.type || "application/octet-stream"}\r
|
|
|
9148
9362
|
}
|
|
9149
9363
|
});
|
|
9150
9364
|
}
|
|
9151
|
-
var getNodeEnv, LogLevel, DEFAULT_REDACT_HEADERS, DEFAULT_REDACT_PARAMS, DEFAULT_ALLOWED_BODY_TYPES, ConsoleTransport, hasStdout, JSONTransport, BatchingTransport, RemoteTransport, MultiTransport, _logIdSeq, Redactor, _HTTPLogger, HTTPLogger;
|
|
9365
|
+
var getNodeEnv, LogLevel, DEFAULT_REDACT_HEADERS, DEFAULT_REDACT_PARAMS, DEFAULT_ALLOWED_BODY_TYPES, ConsoleTransport, hasStdout, JSONTransport, BatchingTransport, RemoteTransport, MultiTransport, _logIdSeq, Redactor, _HTTPLogger, HTTPLogger, CIRCULAR_MARKER, safeStringify2;
|
|
9152
9366
|
var init_logging = __esm({
|
|
9153
9367
|
"dist/esm/logging.js"() {
|
|
9154
9368
|
"use strict";
|
|
@@ -9468,11 +9682,7 @@ Content-Type: ${value.type || "application/octet-stream"}\r
|
|
|
9468
9682
|
}
|
|
9469
9683
|
/** Redact sensitive header values, replacing them with "***". */
|
|
9470
9684
|
redactHeaders(headers) {
|
|
9471
|
-
|
|
9472
|
-
for (const [k, v] of Object.entries(headers)) {
|
|
9473
|
-
out[k] = this.headerSet.has(k.toLowerCase()) ? "***" : v;
|
|
9474
|
-
}
|
|
9475
|
-
return out;
|
|
9685
|
+
return redactHeaders(headers, this.headerSet, "***");
|
|
9476
9686
|
}
|
|
9477
9687
|
/**
|
|
9478
9688
|
* Redact sensitive query parameters from a URL.
|
|
@@ -9519,7 +9729,7 @@ Content-Type: ${value.type || "application/octet-stream"}\r
|
|
|
9519
9729
|
}
|
|
9520
9730
|
if (!this.paramSet.has(decoded.toLowerCase()))
|
|
9521
9731
|
return pair;
|
|
9522
|
-
return
|
|
9732
|
+
return `${name}=***`;
|
|
9523
9733
|
});
|
|
9524
9734
|
return `${base}?${params.join("&")}${fragment}`;
|
|
9525
9735
|
}
|
|
@@ -9597,7 +9807,7 @@ Content-Type: ${value.type || "application/octet-stream"}\r
|
|
|
9597
9807
|
}
|
|
9598
9808
|
/** Evict oldest or stale (abandoned) active IDs to prevent unbounded memory growth. */
|
|
9599
9809
|
_cleanupActiveIds() {
|
|
9600
|
-
const now2 =
|
|
9810
|
+
const now2 = perfNow();
|
|
9601
9811
|
if (this.activeIds.size > _HTTPLogger.MAX_ACTIVE_IDS) {
|
|
9602
9812
|
const entries = Array.from(this.activeIds.entries());
|
|
9603
9813
|
const toRemove = entries.slice(0, entries.length - Math.floor(_HTTPLogger.MAX_ACTIVE_IDS * 0.8));
|
|
@@ -9628,7 +9838,7 @@ Content-Type: ${value.type || "application/octet-stream"}\r
|
|
|
9628
9838
|
if (this.activeIds.size > 500) {
|
|
9629
9839
|
this._cleanupActiveIds();
|
|
9630
9840
|
}
|
|
9631
|
-
this.activeIds.set(requestId, { startMs:
|
|
9841
|
+
this.activeIds.set(requestId, { startMs: perfNow(), method, url });
|
|
9632
9842
|
const ct = headers["content-type"] ?? headers["Content-Type"] ?? null;
|
|
9633
9843
|
const redacted = this.redactor.redactBody(body, ct, false);
|
|
9634
9844
|
const entry = {
|
|
@@ -9661,7 +9871,7 @@ Content-Type: ${value.type || "application/octet-stream"}\r
|
|
|
9661
9871
|
*/
|
|
9662
9872
|
logResponse(requestId, status, statusText, headers, body, attempt, cached, meta = {}) {
|
|
9663
9873
|
const active = this.activeIds.get(requestId);
|
|
9664
|
-
const dur = active ?
|
|
9874
|
+
const dur = active ? perfNow() - active.startMs : 0;
|
|
9665
9875
|
const method = active?.method ?? "GET";
|
|
9666
9876
|
const url = active?.url ?? "";
|
|
9667
9877
|
this.activeIds.delete(requestId);
|
|
@@ -9701,7 +9911,7 @@ Content-Type: ${value.type || "application/octet-stream"}\r
|
|
|
9701
9911
|
*/
|
|
9702
9912
|
logError(requestId, error, status, attempt, meta = {}) {
|
|
9703
9913
|
const active = this.activeIds.get(requestId);
|
|
9704
|
-
const dur = active ?
|
|
9914
|
+
const dur = active ? perfNow() - active.startMs : 0;
|
|
9705
9915
|
const method = active?.method ?? "GET";
|
|
9706
9916
|
const url = active?.url ?? "";
|
|
9707
9917
|
this.activeIds.delete(requestId);
|
|
@@ -9801,6 +10011,8 @@ Content-Type: ${value.type || "application/octet-stream"}\r
|
|
|
9801
10011
|
/** Time after which an abandoned active ID is evicted */
|
|
9802
10012
|
__publicField(_HTTPLogger, "IDLE_TIMEOUT_MS", 6e4);
|
|
9803
10013
|
HTTPLogger = _HTTPLogger;
|
|
10014
|
+
CIRCULAR_MARKER = "[circular]";
|
|
10015
|
+
safeStringify2 = (value) => safeStringify(value, CIRCULAR_MARKER);
|
|
9804
10016
|
}
|
|
9805
10017
|
});
|
|
9806
10018
|
|
|
@@ -10588,9 +10800,6 @@ Content-Type: ${value.type || "application/octet-stream"}\r
|
|
|
10588
10800
|
const message = graphQLErrors.map((e) => e.message).join("; ");
|
|
10589
10801
|
return new GraphQLClientError(message, "EGRAPHQL", graphQLErrors, req, res);
|
|
10590
10802
|
}
|
|
10591
|
-
function sleep3(ms) {
|
|
10592
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
10593
|
-
}
|
|
10594
10803
|
function buildLinkChain(links, terminal) {
|
|
10595
10804
|
return links.reduceRight((next, link) => (op) => link(op, next), terminal);
|
|
10596
10805
|
}
|
|
@@ -10607,12 +10816,6 @@ Content-Type: ${value.type || "application/octet-stream"}\r
|
|
|
10607
10816
|
const m = trimmed.match(/^\s*(?:query|mutation|subscription)\s+([A-Za-z_][A-Za-z0-9_]*)/im);
|
|
10608
10817
|
return m ? m[1] : null;
|
|
10609
10818
|
}
|
|
10610
|
-
async function sha256Hex3(str) {
|
|
10611
|
-
const encoded = new TextEncoder().encode(str);
|
|
10612
|
-
const buf = encoded.buffer.slice(encoded.byteOffset, encoded.byteOffset + encoded.byteLength);
|
|
10613
|
-
const hash = await crypto.subtle.digest("SHA-256", buf);
|
|
10614
|
-
return Array.from(new Uint8Array(hash)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
10615
|
-
}
|
|
10616
10819
|
async function buildHeaders(config, extra = {}) {
|
|
10617
10820
|
const base = {
|
|
10618
10821
|
"content-type": "application/json",
|
|
@@ -10784,7 +10987,7 @@ ${text}`, "ENETWORK", void 0, req, void 0, new Error(`HTTP ${response.status}`))
|
|
|
10784
10987
|
let fetchBody = null;
|
|
10785
10988
|
const fetchHeaders = { ...headers };
|
|
10786
10989
|
if (apqMode === "omitQuery" || apqMode === "full") {
|
|
10787
|
-
const hash = getAPQHashFn ? await getAPQHashFn(req.query) : await
|
|
10990
|
+
const hash = getAPQHashFn ? await getAPQHashFn(req.query) : await sha256Hex(req.query);
|
|
10788
10991
|
const extensions = {
|
|
10789
10992
|
...req.extensions,
|
|
10790
10993
|
persistedQuery: { version: 1, sha256Hash: hash }
|
|
@@ -10901,7 +11104,7 @@ ${text}`, "ENETWORK", void 0, req, void 0, new Error(`HTTP ${response.status}`))
|
|
|
10901
11104
|
let lastErr;
|
|
10902
11105
|
for (let attempt = 0; attempt <= max; attempt++) {
|
|
10903
11106
|
if (attempt > 0)
|
|
10904
|
-
await
|
|
11107
|
+
await sleep(delay * Math.pow(2, attempt - 1));
|
|
10905
11108
|
try {
|
|
10906
11109
|
return await next(op);
|
|
10907
11110
|
} catch (err) {
|
|
@@ -10926,6 +11129,8 @@ ${text}`, "ENETWORK", void 0, req, void 0, new Error(`HTTP ${response.status}`))
|
|
|
10926
11129
|
"use strict";
|
|
10927
11130
|
init_types();
|
|
10928
11131
|
init_utils();
|
|
11132
|
+
init_utils();
|
|
11133
|
+
init_utils();
|
|
10929
11134
|
GraphQLClientError = class extends Error {
|
|
10930
11135
|
/**
|
|
10931
11136
|
* @param message - Human-readable error description
|
|
@@ -11056,7 +11261,7 @@ ${text}`, "ENETWORK", void 0, req, void 0, new Error(`HTTP ${response.status}`))
|
|
|
11056
11261
|
_GraphQLClient.apqHashToQuery.delete(cached.hash);
|
|
11057
11262
|
}
|
|
11058
11263
|
_GraphQLClient._apqMisses++;
|
|
11059
|
-
const hash = await
|
|
11264
|
+
const hash = await sha256Hex(query);
|
|
11060
11265
|
const now2 = Date.now();
|
|
11061
11266
|
const existingEntry = _GraphQLClient.apqHashToQuery.get(hash);
|
|
11062
11267
|
if (existingEntry && existingEntry.query !== query) {
|
|
@@ -11424,7 +11629,7 @@ ${text}`, "ENETWORK", void 0, requests[0], void 0, new Error(`HTTP ${response.st
|
|
|
11424
11629
|
let lastErr;
|
|
11425
11630
|
for (let attempt = 0; attempt <= this.config.retries; attempt++) {
|
|
11426
11631
|
if (attempt > 0)
|
|
11427
|
-
await
|
|
11632
|
+
await sleep(this.config.retryDelayMs * Math.pow(2, attempt - 1));
|
|
11428
11633
|
try {
|
|
11429
11634
|
return await this._executeRaw(req, signal);
|
|
11430
11635
|
} catch (err) {
|
|
@@ -11586,7 +11791,7 @@ ${text}`, "ENETWORK", void 0, requests[0], void 0, new Error(`HTTP ${response.st
|
|
|
11586
11791
|
if (config.maxPages && pageNum >= config.maxPages)
|
|
11587
11792
|
break;
|
|
11588
11793
|
if (pageNum > 0 && config.delayMs && config.delayMs > 0) {
|
|
11589
|
-
await
|
|
11794
|
+
await sleep2(config.delayMs, config.signal);
|
|
11590
11795
|
}
|
|
11591
11796
|
let response;
|
|
11592
11797
|
try {
|
|
@@ -11967,7 +12172,7 @@ ${text}`, "ENETWORK", void 0, requests[0], void 0, new Error(`HTTP ${response.st
|
|
|
11967
12172
|
}
|
|
11968
12173
|
return state;
|
|
11969
12174
|
}
|
|
11970
|
-
function
|
|
12175
|
+
function sleep2(ms, signal) {
|
|
11971
12176
|
return new Promise((r) => {
|
|
11972
12177
|
if (signal?.aborted) {
|
|
11973
12178
|
r();
|
|
@@ -12333,7 +12538,7 @@ ${text}`, "ENETWORK", void 0, requests[0], void 0, new Error(`HTTP ${response.st
|
|
|
12333
12538
|
pathSegments: () => pathSegments,
|
|
12334
12539
|
percentDecode: () => percentDecode,
|
|
12335
12540
|
percentEncode: () => percentEncode,
|
|
12336
|
-
perfNow: () =>
|
|
12541
|
+
perfNow: () => perfNow,
|
|
12337
12542
|
pickQuery: () => pickQuery,
|
|
12338
12543
|
presignRequest: () => presignRequest,
|
|
12339
12544
|
readBlob: () => readBlob,
|
|
@@ -12396,6 +12601,7 @@ ${text}`, "ENETWORK", void 0, requests[0], void 0, new Error(`HTTP ${response.st
|
|
|
12396
12601
|
});
|
|
12397
12602
|
|
|
12398
12603
|
// dist/esm/progress.js
|
|
12604
|
+
init_utils();
|
|
12399
12605
|
var ProgressTracker = class {
|
|
12400
12606
|
constructor(total, options = {}) {
|
|
12401
12607
|
__publicField(this, "loaded", 0);
|
|
@@ -12986,11 +13192,43 @@ ${text}`, "ENETWORK", void 0, requests[0], void 0, new Error(`HTTP ${response.st
|
|
|
12986
13192
|
}
|
|
12987
13193
|
};
|
|
12988
13194
|
}
|
|
12989
|
-
function perfNow() {
|
|
12990
|
-
return typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
12991
|
-
}
|
|
12992
13195
|
|
|
12993
13196
|
// dist/esm/dedup.js
|
|
13197
|
+
var NON_IDENTITY_HEADERS = [
|
|
13198
|
+
"traceparent",
|
|
13199
|
+
"tracestate",
|
|
13200
|
+
"baggage",
|
|
13201
|
+
"x-request-id",
|
|
13202
|
+
"x-correlation-id",
|
|
13203
|
+
"x-trace-id"
|
|
13204
|
+
];
|
|
13205
|
+
async function identityFingerprint(headers) {
|
|
13206
|
+
if (headers === void 0)
|
|
13207
|
+
return "";
|
|
13208
|
+
const lowered = /* @__PURE__ */ new Map();
|
|
13209
|
+
for (const [name, value] of Object.entries(headers)) {
|
|
13210
|
+
lowered.set(name.toLowerCase(), value);
|
|
13211
|
+
}
|
|
13212
|
+
const parts = [];
|
|
13213
|
+
for (const name of NON_IDENTITY_HEADERS)
|
|
13214
|
+
lowered.delete(name);
|
|
13215
|
+
for (const name of [...lowered.keys()].sort()) {
|
|
13216
|
+
parts.push(`${name}=${lowered.get(name)}`);
|
|
13217
|
+
}
|
|
13218
|
+
if (parts.length === 0)
|
|
13219
|
+
return "";
|
|
13220
|
+
const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(parts.join("|")));
|
|
13221
|
+
return Array.from(new Uint8Array(digest)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
13222
|
+
}
|
|
13223
|
+
function dedupKeyFrom(method, url, fingerprint) {
|
|
13224
|
+
return fingerprint ? `${method}:${url}:${fingerprint}` : `${method}:${url}`;
|
|
13225
|
+
}
|
|
13226
|
+
function defaultDedupKey(method, url, headers) {
|
|
13227
|
+
if (headers === void 0)
|
|
13228
|
+
return `${method}::${url}`;
|
|
13229
|
+
const identity = Object.entries(headers).filter(([name]) => !NON_IDENTITY_HEADERS.includes(name.toLowerCase())).map(([name, value]) => `${name.toLowerCase()}=${value}`).sort();
|
|
13230
|
+
return identity.length === 0 ? `${method}::${url}` : `${method}::${url}::${identity.join("&")}`;
|
|
13231
|
+
}
|
|
12994
13232
|
var DedupMap = class {
|
|
12995
13233
|
/**
|
|
12996
13234
|
* @param options - Deduplication configuration
|
|
@@ -12998,6 +13236,9 @@ ${text}`, "ENETWORK", void 0, requests[0], void 0, new Error(`HTTP ${response.st
|
|
|
12998
13236
|
constructor(options = {}) {
|
|
12999
13237
|
__publicField(this, "inflight", /* @__PURE__ */ new Map());
|
|
13000
13238
|
__publicField(this, "timeouts", /* @__PURE__ */ new Map());
|
|
13239
|
+
/** Window timers, overridable for tests. See {@link DedupOptions.timers}. */
|
|
13240
|
+
__publicField(this, "_setTimeout");
|
|
13241
|
+
__publicField(this, "_clearTimeout");
|
|
13001
13242
|
__publicField(this, "windowMs");
|
|
13002
13243
|
__publicField(this, "methods");
|
|
13003
13244
|
__publicField(this, "keyFn");
|
|
@@ -13007,8 +13248,10 @@ ${text}`, "ENETWORK", void 0, requests[0], void 0, new Error(`HTTP ${response.st
|
|
|
13007
13248
|
__publicField(this, "_misses", 0);
|
|
13008
13249
|
this.windowMs = options.windowMs ?? 0;
|
|
13009
13250
|
this.methods = new Set((options.methods ?? ["GET", "HEAD"]).map((m) => m.toUpperCase()));
|
|
13010
|
-
this.keyFn = options.keyFn ??
|
|
13251
|
+
this.keyFn = options.keyFn ?? defaultDedupKey;
|
|
13011
13252
|
this._globalSignal = options.signal ?? null;
|
|
13253
|
+
this._setTimeout = options.timers?.setTimeout ?? ((fn, ms) => setTimeout(fn, ms));
|
|
13254
|
+
this._clearTimeout = options.timers?.clearTimeout ?? ((h) => clearTimeout(h));
|
|
13012
13255
|
}
|
|
13013
13256
|
/**
|
|
13014
13257
|
* Execute `factory` for the given method/url, or return the in-flight
|
|
@@ -13032,9 +13275,12 @@ ${text}`, "ENETWORK", void 0, requests[0], void 0, new Error(`HTTP ${response.st
|
|
|
13032
13275
|
* entry and creating a new one. The first caller becomes the "leader" and all
|
|
13033
13276
|
* subsequent callers within the same tick share the same promise.
|
|
13034
13277
|
*
|
|
13035
|
-
* Note:
|
|
13036
|
-
*
|
|
13037
|
-
*
|
|
13278
|
+
* Note: the default key includes the request headers, so two requests to the
|
|
13279
|
+
* same URL carrying different `Authorization` or `Cookie` are separate
|
|
13280
|
+
* requests and are never coalesced. A key of method + URL alone was the
|
|
13281
|
+
* default once, and it handed one caller's response to another: 50 callers
|
|
13282
|
+
* aside, even two callers for different tenants shared a single response.
|
|
13283
|
+
* See {@link defaultDedupKey}.
|
|
13038
13284
|
*/
|
|
13039
13285
|
execute(method, url, factory, headers, windowMs, signal) {
|
|
13040
13286
|
const effectiveWindowMs = windowMs ?? this.windowMs;
|
|
@@ -13077,7 +13323,7 @@ ${text}`, "ENETWORK", void 0, requests[0], void 0, new Error(`HTTP ${response.st
|
|
|
13077
13323
|
this.inflight.delete(key);
|
|
13078
13324
|
const existingTimeout = this.timeouts.get(key);
|
|
13079
13325
|
if (existingTimeout !== void 0) {
|
|
13080
|
-
|
|
13326
|
+
this._clearTimeout(existingTimeout);
|
|
13081
13327
|
this.timeouts.delete(key);
|
|
13082
13328
|
}
|
|
13083
13329
|
} : null;
|
|
@@ -13102,10 +13348,10 @@ ${text}`, "ENETWORK", void 0, requests[0], void 0, new Error(`HTTP ${response.st
|
|
|
13102
13348
|
} else {
|
|
13103
13349
|
const stale = this.timeouts.get(key);
|
|
13104
13350
|
if (stale !== void 0) {
|
|
13105
|
-
|
|
13351
|
+
this._clearTimeout(stale);
|
|
13106
13352
|
this.timeouts.delete(key);
|
|
13107
13353
|
}
|
|
13108
|
-
const timeoutId =
|
|
13354
|
+
const timeoutId = this._setTimeout(() => {
|
|
13109
13355
|
this.inflight.delete(key);
|
|
13110
13356
|
this.timeouts.delete(key);
|
|
13111
13357
|
}, effectiveWindowMs);
|
|
@@ -13119,7 +13365,7 @@ ${text}`, "ENETWORK", void 0, requests[0], void 0, new Error(`HTTP ${response.st
|
|
|
13119
13365
|
this.inflight.delete(key);
|
|
13120
13366
|
const existingTimeout = this.timeouts.get(key);
|
|
13121
13367
|
if (existingTimeout !== void 0) {
|
|
13122
|
-
|
|
13368
|
+
this._clearTimeout(existingTimeout);
|
|
13123
13369
|
this.timeouts.delete(key);
|
|
13124
13370
|
}
|
|
13125
13371
|
throw err;
|
|
@@ -13176,7 +13422,7 @@ ${text}`, "ENETWORK", void 0, requests[0], void 0, new Error(`HTTP ${response.st
|
|
|
13176
13422
|
clear() {
|
|
13177
13423
|
this.inflight.clear();
|
|
13178
13424
|
for (const timeoutId of this.timeouts.values()) {
|
|
13179
|
-
|
|
13425
|
+
this._clearTimeout(timeoutId);
|
|
13180
13426
|
}
|
|
13181
13427
|
this.timeouts.clear();
|
|
13182
13428
|
}
|
|
@@ -13190,7 +13436,7 @@ ${text}`, "ENETWORK", void 0, requests[0], void 0, new Error(`HTTP ${response.st
|
|
|
13190
13436
|
invalidate(key) {
|
|
13191
13437
|
const timeoutId = this.timeouts.get(key);
|
|
13192
13438
|
if (timeoutId) {
|
|
13193
|
-
|
|
13439
|
+
this._clearTimeout(timeoutId);
|
|
13194
13440
|
this.timeouts.delete(key);
|
|
13195
13441
|
}
|
|
13196
13442
|
return this.inflight.delete(key);
|
|
@@ -13467,7 +13713,7 @@ ${text}`, "ENETWORK", void 0, requests[0], void 0, new Error(`HTTP ${response.st
|
|
|
13467
13713
|
const code = err.code;
|
|
13468
13714
|
if (this.failureFilter.networkErrors && code === "ENETWORK")
|
|
13469
13715
|
return true;
|
|
13470
|
-
if (this.failureFilter.timeouts && code === "ETIMEOUT")
|
|
13716
|
+
if (this.failureFilter.timeouts && (code === "ETIMEOUT" || code === "ETIMEDOUT"))
|
|
13471
13717
|
return true;
|
|
13472
13718
|
if (this.failureFilter.serverErrors) {
|
|
13473
13719
|
const status = err.status;
|
|
@@ -13547,6 +13793,18 @@ ${text}`, "ENETWORK", void 0, requests[0], void 0, new Error(`HTTP ${response.st
|
|
|
13547
13793
|
|
|
13548
13794
|
// dist/esm/ws.js
|
|
13549
13795
|
init_utils();
|
|
13796
|
+
function validateHandshakeHeaders(headers) {
|
|
13797
|
+
if (!headers)
|
|
13798
|
+
return;
|
|
13799
|
+
for (const [name, value] of Object.entries(headers)) {
|
|
13800
|
+
if (!isValidHeaderName(name)) {
|
|
13801
|
+
throw new WSError(`Invalid WebSocket handshake header name: ${JSON.stringify(name)}`);
|
|
13802
|
+
}
|
|
13803
|
+
if (!isValidHeaderValue(value)) {
|
|
13804
|
+
throw new WSError(`Invalid value for WebSocket handshake header ${JSON.stringify(name)}: must be a string of printable characters with no control characters`);
|
|
13805
|
+
}
|
|
13806
|
+
}
|
|
13807
|
+
}
|
|
13550
13808
|
var WSError = class extends Error {
|
|
13551
13809
|
/**
|
|
13552
13810
|
* @param message - Human-readable error description
|
|
@@ -13679,6 +13937,7 @@ ${text}`, "ENETWORK", void 0, requests[0], void 0, new Error(`HTTP ${response.st
|
|
|
13679
13937
|
this._url = cfg.url;
|
|
13680
13938
|
this._protocols = cfg.protocols;
|
|
13681
13939
|
this._headers = cfg.headers;
|
|
13940
|
+
validateHandshakeHeaders(cfg.headers);
|
|
13682
13941
|
this._maxReconnects = cfg.maxReconnects ?? 10;
|
|
13683
13942
|
this._reconnectBaseMs = cfg.reconnectBaseMs ?? 1e3;
|
|
13684
13943
|
this._reconnectMaxMs = cfg.reconnectMaxMs ?? 3e4;
|
|
@@ -14396,7 +14655,13 @@ ${text}`, "ENETWORK", void 0, requests[0], void 0, new Error(`HTTP ${response.st
|
|
|
14396
14655
|
}
|
|
14397
14656
|
/** Buffer an outgoing message and track bytes for backpressure. */
|
|
14398
14657
|
_bufferMessage(data, byteLen) {
|
|
14399
|
-
if (this._bufferMessages
|
|
14658
|
+
if (!this._bufferMessages) {
|
|
14659
|
+
if (!this._aborted) {
|
|
14660
|
+
this._cbError?.(new WSError(`WebSocket message dropped: the socket is not open and bufferMessages is disabled (${byteLen} bytes). Reconnect, or enable bufferMessages to queue sends made while reconnecting.`));
|
|
14661
|
+
}
|
|
14662
|
+
return;
|
|
14663
|
+
}
|
|
14664
|
+
if (!this._aborted) {
|
|
14400
14665
|
const evicted = this._buffer.length >= this._maxBufferSize ? this._buffer.shift() : void 0;
|
|
14401
14666
|
if (evicted !== void 0) {
|
|
14402
14667
|
this._bufferedBytes -= typeof evicted === "string" ? new TextEncoder().encode(evicted).byteLength : evicted.byteLength;
|
|
@@ -14516,6 +14781,7 @@ ${text}`, "ENETWORK", void 0, requests[0], void 0, new Error(`HTTP ${response.st
|
|
|
14516
14781
|
|
|
14517
14782
|
// dist/esm/interceptors.js
|
|
14518
14783
|
init_cache();
|
|
14784
|
+
init_utils();
|
|
14519
14785
|
init_types();
|
|
14520
14786
|
init_headers();
|
|
14521
14787
|
var _idSeq = 0;
|
|
@@ -14824,7 +15090,7 @@ ${text}`, "ENETWORK", void 0, requests[0], void 0, new Error(`HTTP ${response.st
|
|
|
14824
15090
|
return;
|
|
14825
15091
|
const delay = computeDelay(ctx.attempt, retryAfterMs(ctx));
|
|
14826
15092
|
cfg.onRetry?.(ctx, delay);
|
|
14827
|
-
await
|
|
15093
|
+
await sleep(delay);
|
|
14828
15094
|
return { ...ctx.request };
|
|
14829
15095
|
};
|
|
14830
15096
|
const errorInterceptor = async (ctx) => {
|
|
@@ -14832,7 +15098,7 @@ ${text}`, "ENETWORK", void 0, requests[0], void 0, new Error(`HTTP ${response.st
|
|
|
14832
15098
|
return;
|
|
14833
15099
|
const delay = computeDelay(ctx.attempt, null);
|
|
14834
15100
|
cfg.onRetry?.(ctx, delay);
|
|
14835
|
-
await
|
|
15101
|
+
await sleep(delay);
|
|
14836
15102
|
ctx.error = null;
|
|
14837
15103
|
throw new RetrySignal(ctx.request);
|
|
14838
15104
|
};
|
|
@@ -14997,13 +15263,7 @@ ${text}`, "ENETWORK", void 0, requests[0], void 0, new Error(`HTTP ${response.st
|
|
|
14997
15263
|
function createLoggingInterceptor(config = {}) {
|
|
14998
15264
|
const cfg = { ...LOG_DEFAULTS, ...config };
|
|
14999
15265
|
const redactSet = new Set(cfg.redactHeaders.map((h) => h.toLowerCase()));
|
|
15000
|
-
|
|
15001
|
-
const out = {};
|
|
15002
|
-
for (const [k, v] of Object.entries(headers)) {
|
|
15003
|
-
out[k] = redactSet.has(k.toLowerCase()) ? "**REDACTED**" : v;
|
|
15004
|
-
}
|
|
15005
|
-
return out;
|
|
15006
|
-
}
|
|
15266
|
+
const redactHeaders2 = (headers) => redactHeaders(headers, redactSet, "**REDACTED**");
|
|
15007
15267
|
function makeEntry(type, ctx) {
|
|
15008
15268
|
const requestId = ctx.store.get(REQUEST_ID_KEY) ?? "unknown";
|
|
15009
15269
|
return {
|
|
@@ -15016,7 +15276,7 @@ ${text}`, "ENETWORK", void 0, requests[0], void 0, new Error(`HTTP ${response.st
|
|
|
15016
15276
|
durationMs: type !== "request" ? now() - ctx.startedAt : null,
|
|
15017
15277
|
attempt: ctx.attempt,
|
|
15018
15278
|
error: ctx.error instanceof Error ? ctx.error.message : null,
|
|
15019
|
-
headers:
|
|
15279
|
+
headers: redactHeaders2(ctx.request.headers ?? {})
|
|
15020
15280
|
};
|
|
15021
15281
|
}
|
|
15022
15282
|
const requestInterceptor = (ctx) => {
|
|
@@ -15552,12 +15812,7 @@ ${text}`, "ENETWORK", void 0, requests[0], void 0, new Error(`HTTP ${response.st
|
|
|
15552
15812
|
};
|
|
15553
15813
|
return { requestInterceptor, responseInterceptor, errorInterceptor, snapshot, reset };
|
|
15554
15814
|
}
|
|
15555
|
-
|
|
15556
|
-
return typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
15557
|
-
}
|
|
15558
|
-
function sleep2(ms) {
|
|
15559
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
15560
|
-
}
|
|
15815
|
+
var now = perfNow;
|
|
15561
15816
|
function computeBodySize(body) {
|
|
15562
15817
|
if (!body)
|
|
15563
15818
|
return 0;
|
|
@@ -15624,6 +15879,10 @@ ${text}`, "ENETWORK", void 0, requests[0], void 0, new Error(`HTTP ${response.st
|
|
|
15624
15879
|
|
|
15625
15880
|
// dist/esm/aws-sigv4.js
|
|
15626
15881
|
init_types();
|
|
15882
|
+
init_utils();
|
|
15883
|
+
function toHex(bytes) {
|
|
15884
|
+
return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
15885
|
+
}
|
|
15627
15886
|
function staticCredentials(creds) {
|
|
15628
15887
|
return () => Promise.resolve(creds);
|
|
15629
15888
|
}
|
|
@@ -15793,16 +16052,6 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
15793
16052
|
const sig = await crypto.subtle.sign("HMAC", cryptoKey, enc.encode(data));
|
|
15794
16053
|
return new Uint8Array(sig);
|
|
15795
16054
|
}
|
|
15796
|
-
async function sha256Hex(data) {
|
|
15797
|
-
const enc = new TextEncoder();
|
|
15798
|
-
const raw = typeof data === "string" ? enc.encode(data) : data;
|
|
15799
|
-
const buf = raw.buffer.slice(raw.byteOffset, raw.byteOffset + raw.byteLength);
|
|
15800
|
-
const hashBuf = await crypto.subtle.digest("SHA-256", buf);
|
|
15801
|
-
return toHex(new Uint8Array(hashBuf));
|
|
15802
|
-
}
|
|
15803
|
-
function toHex(bytes) {
|
|
15804
|
-
return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
15805
|
-
}
|
|
15806
16055
|
var DEFAULT_UNSIGNED_HEADERS = /* @__PURE__ */ new Set([
|
|
15807
16056
|
"user-agent",
|
|
15808
16057
|
"x-forwarded-for",
|
|
@@ -16294,6 +16543,7 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
16294
16543
|
|
|
16295
16544
|
// dist/esm/digest.js
|
|
16296
16545
|
init_utils();
|
|
16546
|
+
init_utils();
|
|
16297
16547
|
function md5(input) {
|
|
16298
16548
|
const bytes = [];
|
|
16299
16549
|
for (let i = 0; i < input.length; i++) {
|
|
@@ -16458,11 +16708,11 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
16458
16708
|
for (let offset = 0; offset < bytes.length; offset += 64) {
|
|
16459
16709
|
const M = [];
|
|
16460
16710
|
for (let i = 0; i < 16; i++) {
|
|
16461
|
-
const
|
|
16462
|
-
const
|
|
16463
|
-
const
|
|
16464
|
-
const
|
|
16465
|
-
M[i] = (
|
|
16711
|
+
const w0 = bytes[offset + i * 4];
|
|
16712
|
+
const w1 = bytes[offset + i * 4 + 1];
|
|
16713
|
+
const w2 = bytes[offset + i * 4 + 2];
|
|
16714
|
+
const w3 = bytes[offset + i * 4 + 3];
|
|
16715
|
+
M[i] = (w0 | w1 << 8 | w2 << 16 | w3 << 24) >>> 0;
|
|
16466
16716
|
}
|
|
16467
16717
|
let A = a0, B = b0, C = c0, D = d0;
|
|
16468
16718
|
for (let i = 0; i < 64; i++) {
|
|
@@ -16503,7 +16753,7 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
16503
16753
|
function hexToBytes(hex) {
|
|
16504
16754
|
const out = new Uint8Array(hex.length / 2);
|
|
16505
16755
|
for (let i = 0; i < out.length; i++) {
|
|
16506
|
-
out[i] = parseInt(hex.
|
|
16756
|
+
out[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
16507
16757
|
}
|
|
16508
16758
|
return out;
|
|
16509
16759
|
}
|
|
@@ -16557,12 +16807,6 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
16557
16807
|
throw new Error("Digest challenge missing 'nonce'");
|
|
16558
16808
|
return challenge;
|
|
16559
16809
|
}
|
|
16560
|
-
async function sha256Hex2(data) {
|
|
16561
|
-
const enc = new TextEncoder();
|
|
16562
|
-
const buf = await crypto.subtle.digest("SHA-256", enc.encode(data));
|
|
16563
|
-
const bytes = new Uint8Array(buf);
|
|
16564
|
-
return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
16565
|
-
}
|
|
16566
16810
|
function sha512256Hex(data) {
|
|
16567
16811
|
const K = [
|
|
16568
16812
|
0x428a2f98d728ae22n,
|
|
@@ -16747,7 +16991,7 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
16747
16991
|
return md5(s);
|
|
16748
16992
|
if (algo === "SHA-512-256")
|
|
16749
16993
|
return sha512256Hex(s);
|
|
16750
|
-
return await
|
|
16994
|
+
return await sha256Hex(s);
|
|
16751
16995
|
}
|
|
16752
16996
|
const user = challenge.userhash ? uint8ArrayToBase64(hexToBytes(await hash(`${username}:${realm}`))) : username;
|
|
16753
16997
|
let ha1 = await hash(`${user}:${realm}:${password}`);
|
|
@@ -16778,7 +17022,7 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
16778
17022
|
throw new Error(`Unsupported digest algorithm: ${challenge.algorithm}`);
|
|
16779
17023
|
}
|
|
16780
17024
|
const { algo } = resolved;
|
|
16781
|
-
const digest = algo === "MD5" ? md5(`${username}:${challenge.realm}`) : algo === "SHA-512-256" ? sha512256Hex(`${username}:${challenge.realm}`) : await
|
|
17025
|
+
const digest = algo === "MD5" ? md5(`${username}:${challenge.realm}`) : algo === "SHA-512-256" ? sha512256Hex(`${username}:${challenge.realm}`) : await sha256Hex(`${username}:${challenge.realm}`);
|
|
16782
17026
|
return uint8ArrayToBase64(hexToBytes(digest));
|
|
16783
17027
|
}
|
|
16784
17028
|
function resolveHashAlgo(algorithm) {
|
|
@@ -16836,12 +17080,26 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
16836
17080
|
}
|
|
16837
17081
|
function createDigestAuthorizer() {
|
|
16838
17082
|
const counters = /* @__PURE__ */ new Map();
|
|
17083
|
+
const MAX_TRACKED_NONCES = 64;
|
|
17084
|
+
const rememberNonce = (scope, value) => {
|
|
17085
|
+
if (!counters.has(scope)) {
|
|
17086
|
+
counters.set(scope, value);
|
|
17087
|
+
if (counters.size > MAX_TRACKED_NONCES) {
|
|
17088
|
+
const oldest = counters.keys().next();
|
|
17089
|
+
if (!oldest.done)
|
|
17090
|
+
counters.delete(oldest.value);
|
|
17091
|
+
}
|
|
17092
|
+
return;
|
|
17093
|
+
}
|
|
17094
|
+
counters.delete(scope);
|
|
17095
|
+
counters.set(scope, value);
|
|
17096
|
+
};
|
|
16839
17097
|
return async (wwwAuth, username, password, method, uri) => {
|
|
16840
17098
|
const challenge = parseDigestChallenge(wwwAuth);
|
|
16841
17099
|
const cnonce = randomBytes(5);
|
|
16842
17100
|
const scope = `${challenge.realm}\0${challenge.nonce}`;
|
|
16843
17101
|
const next = (counters.get(scope) ?? 0) + 1;
|
|
16844
|
-
|
|
17102
|
+
rememberNonce(scope, next);
|
|
16845
17103
|
const nc = next.toString(16).padStart(8, "0");
|
|
16846
17104
|
const response = await computeDigestResponse(challenge, username, password, method, uri, cnonce, nc);
|
|
16847
17105
|
const usernameStar = await computeUsernameStar(challenge, username);
|
|
@@ -16947,6 +17205,7 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
16947
17205
|
function randomHex(len) {
|
|
16948
17206
|
return randomBytes(len / 2);
|
|
16949
17207
|
}
|
|
17208
|
+
var DEFAULT_MAX_RESPONSE_SIZE = 100 * 1024 * 1024;
|
|
16950
17209
|
var HAR_REDACT_HEADERS = /* @__PURE__ */ new Set([
|
|
16951
17210
|
"authorization",
|
|
16952
17211
|
"proxy-authorization",
|
|
@@ -17000,6 +17259,63 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
17000
17259
|
"jwt",
|
|
17001
17260
|
"code"
|
|
17002
17261
|
]);
|
|
17262
|
+
function scrubKnownSecrets(text, req) {
|
|
17263
|
+
const secrets = [];
|
|
17264
|
+
const add = (v) => {
|
|
17265
|
+
if (typeof v === "string" && v.length >= 8)
|
|
17266
|
+
secrets.push(v);
|
|
17267
|
+
};
|
|
17268
|
+
for (const [name, value] of Object.entries(req.headers ?? {})) {
|
|
17269
|
+
if (isSensitiveHeader(name, HAR_REDACT_HEADERS) && typeof value === "string") {
|
|
17270
|
+
add(value);
|
|
17271
|
+
const sp = value.indexOf(" ");
|
|
17272
|
+
if (sp !== -1)
|
|
17273
|
+
add(value.slice(sp + 1));
|
|
17274
|
+
}
|
|
17275
|
+
}
|
|
17276
|
+
let query;
|
|
17277
|
+
try {
|
|
17278
|
+
query = new URL(req.url).searchParams;
|
|
17279
|
+
} catch {
|
|
17280
|
+
query = new URLSearchParams();
|
|
17281
|
+
}
|
|
17282
|
+
for (const [key, value] of query.entries()) {
|
|
17283
|
+
if (HAR_REDACT_PARAMS.has(key.toLowerCase()) && typeof value === "string" && value.length >= 8) {
|
|
17284
|
+
secrets.push(value);
|
|
17285
|
+
}
|
|
17286
|
+
}
|
|
17287
|
+
let out = text;
|
|
17288
|
+
for (const secret of secrets)
|
|
17289
|
+
out = out.split(secret).join(HAR_REDACT_MASK);
|
|
17290
|
+
return out;
|
|
17291
|
+
}
|
|
17292
|
+
function redactHARBody(text, mimeType) {
|
|
17293
|
+
if (!/json/i.test(mimeType))
|
|
17294
|
+
return text;
|
|
17295
|
+
let parsed;
|
|
17296
|
+
try {
|
|
17297
|
+
parsed = JSON.parse(text);
|
|
17298
|
+
} catch {
|
|
17299
|
+
return text;
|
|
17300
|
+
}
|
|
17301
|
+
const walk = (node) => {
|
|
17302
|
+
if (Array.isArray(node))
|
|
17303
|
+
return node.map(walk);
|
|
17304
|
+
if (node !== null && typeof node === "object") {
|
|
17305
|
+
const out = {};
|
|
17306
|
+
for (const [key, value] of Object.entries(node)) {
|
|
17307
|
+
out[key] = HAR_REDACT_PARAMS.has(key.toLowerCase()) ? HAR_REDACT_MASK : walk(value);
|
|
17308
|
+
}
|
|
17309
|
+
return out;
|
|
17310
|
+
}
|
|
17311
|
+
return node;
|
|
17312
|
+
};
|
|
17313
|
+
try {
|
|
17314
|
+
return JSON.stringify(walk(parsed));
|
|
17315
|
+
} catch {
|
|
17316
|
+
return text;
|
|
17317
|
+
}
|
|
17318
|
+
}
|
|
17003
17319
|
var HAR_MAX_BODY_CHARS = 8192;
|
|
17004
17320
|
function isHARBodySafeToRecord(contentType) {
|
|
17005
17321
|
if (!contentType)
|
|
@@ -17026,8 +17342,12 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
17026
17342
|
return url.replace(/([?&])(api_key|apikey|access_token|refresh_token|token|secret|password|signature|sig)=([^&#]*)/gi, "$1$2=***REDACTED***");
|
|
17027
17343
|
}
|
|
17028
17344
|
}
|
|
17345
|
+
var HAR_REDACT_MASK = "***REDACTED***";
|
|
17029
17346
|
function redactHARHeader(name, value) {
|
|
17030
|
-
return
|
|
17347
|
+
return {
|
|
17348
|
+
name,
|
|
17349
|
+
value: isSensitiveHeader(name, HAR_REDACT_HEADERS) ? HAR_REDACT_MASK : value
|
|
17350
|
+
};
|
|
17031
17351
|
}
|
|
17032
17352
|
function harPostData(req) {
|
|
17033
17353
|
if (!req.body)
|
|
@@ -17045,7 +17365,8 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
17045
17365
|
} else {
|
|
17046
17366
|
return {};
|
|
17047
17367
|
}
|
|
17048
|
-
|
|
17368
|
+
const redacted = scrubKnownSecrets(redactHARBody(text, mimeType), req);
|
|
17369
|
+
return { postData: { mimeType, text: redacted.slice(0, HAR_MAX_BODY_CHARS) } };
|
|
17049
17370
|
}
|
|
17050
17371
|
var HARRecorder = class {
|
|
17051
17372
|
/**
|
|
@@ -17146,7 +17467,12 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
17146
17467
|
mimeType: res.headers["content-type"] ?? "application/octet-stream",
|
|
17147
17468
|
// Body text is only kept for non-HTML payloads and is truncated:
|
|
17148
17469
|
// response bodies routinely carry tokens and PII.
|
|
17149
|
-
...typeof res.data === "string" && isHARBodySafeToRecord(res.headers["content-type"]) ? {
|
|
17470
|
+
...typeof res.data === "string" && isHARBodySafeToRecord(res.headers["content-type"]) ? {
|
|
17471
|
+
// Same reason as the request side: a `text/plain` body has no
|
|
17472
|
+
// field names to redact by, and a server that echoes the token
|
|
17473
|
+
// back would otherwise put it in the export verbatim.
|
|
17474
|
+
text: scrubKnownSecrets(res.data, req).slice(0, HAR_MAX_BODY_CHARS)
|
|
17475
|
+
} : {}
|
|
17150
17476
|
},
|
|
17151
17477
|
// The Location header can itself carry a signed URL — redact it too.
|
|
17152
17478
|
redirectURL: res.headers["location"] ? redactHARUrl(res.headers["location"]) : "",
|
|
@@ -17261,8 +17587,32 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
17261
17587
|
return url.replace(/\/\/[^/@]*@/, "//");
|
|
17262
17588
|
}
|
|
17263
17589
|
}
|
|
17264
|
-
function buildURL(base, url, params, allowedSchemes = ["http", "https"]) {
|
|
17590
|
+
function buildURL(base, url, params, allowedSchemes = ["http", "https"], allowPrivateNetwork = false) {
|
|
17265
17591
|
const MAX_QUERY_PARAM_COUNT = 100;
|
|
17592
|
+
const forEachQueryParam = (params2, emit) => {
|
|
17593
|
+
let paramCount = 0;
|
|
17594
|
+
const count = () => {
|
|
17595
|
+
paramCount++;
|
|
17596
|
+
if (paramCount > MAX_QUERY_PARAM_COUNT) {
|
|
17597
|
+
throw new KinetexError(`Query parameter count ${paramCount} exceeds limit of ${MAX_QUERY_PARAM_COUNT}`, "EVALIDATION");
|
|
17598
|
+
}
|
|
17599
|
+
};
|
|
17600
|
+
for (const [key, value] of Object.entries(params2)) {
|
|
17601
|
+
if (value === null || value === void 0)
|
|
17602
|
+
continue;
|
|
17603
|
+
if (Array.isArray(value)) {
|
|
17604
|
+
for (const v of value) {
|
|
17605
|
+
if (v === null || v === void 0)
|
|
17606
|
+
continue;
|
|
17607
|
+
count();
|
|
17608
|
+
emit(key, String(v), true);
|
|
17609
|
+
}
|
|
17610
|
+
} else {
|
|
17611
|
+
count();
|
|
17612
|
+
emit(key, String(value), false);
|
|
17613
|
+
}
|
|
17614
|
+
}
|
|
17615
|
+
};
|
|
17266
17616
|
const MAX_URL_LENGTH = 8192;
|
|
17267
17617
|
let full;
|
|
17268
17618
|
if (/^https?:\/\//i.test(url)) {
|
|
@@ -17280,44 +17630,27 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
17280
17630
|
}
|
|
17281
17631
|
}
|
|
17282
17632
|
if (!params || Object.keys(params).length === 0) {
|
|
17283
|
-
if (!isSafeURL(full, allowedSchemes)) {
|
|
17633
|
+
if (!isSafeURL(full, allowedSchemes, { allowPrivateNetwork })) {
|
|
17284
17634
|
throw new KinetexError(`URL "${redactUserInfo(full)}" failed safety check \u2014 blocked private/loopback address or forbidden scheme`, "EVALIDATION");
|
|
17285
17635
|
}
|
|
17286
17636
|
return full;
|
|
17287
17637
|
}
|
|
17288
17638
|
try {
|
|
17289
17639
|
const u = new URL(full);
|
|
17290
|
-
if (!isSafeURL(u, allowedSchemes)) {
|
|
17640
|
+
if (!isSafeURL(u, allowedSchemes, { allowPrivateNetwork })) {
|
|
17291
17641
|
throw new KinetexError(`URL "${redactUserInfo(full)}" failed safety check \u2014 blocked private/loopback address or forbidden scheme`, "EVALIDATION");
|
|
17292
17642
|
}
|
|
17293
|
-
|
|
17294
|
-
|
|
17295
|
-
|
|
17296
|
-
|
|
17297
|
-
|
|
17298
|
-
|
|
17299
|
-
for (const v of value) {
|
|
17300
|
-
if (v !== null && v !== void 0) {
|
|
17301
|
-
paramCount++;
|
|
17302
|
-
if (paramCount > MAX_QUERY_PARAM_COUNT) {
|
|
17303
|
-
throw new KinetexError(`Query parameter count ${paramCount} exceeds limit of ${MAX_QUERY_PARAM_COUNT}`, "EVALIDATION");
|
|
17304
|
-
}
|
|
17305
|
-
u.searchParams.append(key, String(v));
|
|
17306
|
-
}
|
|
17307
|
-
}
|
|
17308
|
-
} else {
|
|
17309
|
-
paramCount++;
|
|
17310
|
-
if (paramCount > MAX_QUERY_PARAM_COUNT) {
|
|
17311
|
-
throw new KinetexError(`Query parameter count ${paramCount} exceeds limit of ${MAX_QUERY_PARAM_COUNT}`, "EVALIDATION");
|
|
17312
|
-
}
|
|
17313
|
-
u.searchParams.set(key, strValue);
|
|
17314
|
-
}
|
|
17315
|
-
}
|
|
17643
|
+
forEachQueryParam(params, (key, value, fromArray) => {
|
|
17644
|
+
if (fromArray)
|
|
17645
|
+
u.searchParams.append(key, value);
|
|
17646
|
+
else
|
|
17647
|
+
u.searchParams.set(key, value);
|
|
17648
|
+
});
|
|
17316
17649
|
const result = u.toString();
|
|
17317
17650
|
if (result.length > MAX_URL_LENGTH) {
|
|
17318
17651
|
throw new KinetexError(`URL length ${result.length} bytes exceeds limit of ${MAX_URL_LENGTH} bytes`, "EVALIDATION");
|
|
17319
17652
|
}
|
|
17320
|
-
if (!isSafeURL(result)) {
|
|
17653
|
+
if (!isSafeURL(result, allowedSchemes, { allowPrivateNetwork })) {
|
|
17321
17654
|
throw new KinetexError(`URL "${redactUserInfo(result)}" failed safety check \u2014 blocked private/loopback address or forbidden scheme`, "EVALIDATION");
|
|
17322
17655
|
}
|
|
17323
17656
|
return result;
|
|
@@ -17330,33 +17663,14 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
17330
17663
|
full += "?";
|
|
17331
17664
|
}
|
|
17332
17665
|
const paramParts = [];
|
|
17333
|
-
|
|
17334
|
-
|
|
17335
|
-
|
|
17336
|
-
continue;
|
|
17337
|
-
if (Array.isArray(value)) {
|
|
17338
|
-
for (const v of value) {
|
|
17339
|
-
if (v !== null && v !== void 0) {
|
|
17340
|
-
paramCount++;
|
|
17341
|
-
if (paramCount > MAX_QUERY_PARAM_COUNT) {
|
|
17342
|
-
throw new KinetexError(`Query parameter count ${paramCount} exceeds limit of ${MAX_QUERY_PARAM_COUNT}`, "EVALIDATION");
|
|
17343
|
-
}
|
|
17344
|
-
paramParts.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(v))}`);
|
|
17345
|
-
}
|
|
17346
|
-
}
|
|
17347
|
-
} else {
|
|
17348
|
-
paramCount++;
|
|
17349
|
-
if (paramCount > MAX_QUERY_PARAM_COUNT) {
|
|
17350
|
-
throw new KinetexError(`Query parameter count ${paramCount} exceeds limit of ${MAX_QUERY_PARAM_COUNT}`, "EVALIDATION");
|
|
17351
|
-
}
|
|
17352
|
-
paramParts.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`);
|
|
17353
|
-
}
|
|
17354
|
-
}
|
|
17666
|
+
forEachQueryParam(params ?? {}, (key, value) => {
|
|
17667
|
+
paramParts.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
|
|
17668
|
+
});
|
|
17355
17669
|
const result = full + paramParts.join("&");
|
|
17356
17670
|
if (result.length > MAX_URL_LENGTH) {
|
|
17357
17671
|
throw new KinetexError(`URL length ${result.length} bytes exceeds limit of ${MAX_URL_LENGTH} bytes`, "EVALIDATION");
|
|
17358
17672
|
}
|
|
17359
|
-
if (!isSafeURL(result)) {
|
|
17673
|
+
if (!isSafeURL(result, allowedSchemes, { allowPrivateNetwork })) {
|
|
17360
17674
|
throw new KinetexError(`URL "${redactUserInfo(result)}" failed safety check \u2014 blocked private/loopback address or forbidden scheme`, "EVALIDATION");
|
|
17361
17675
|
}
|
|
17362
17676
|
return result;
|
|
@@ -17386,11 +17700,26 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
17386
17700
|
}
|
|
17387
17701
|
return out;
|
|
17388
17702
|
}
|
|
17703
|
+
function loggableBody(body) {
|
|
17704
|
+
if (body === null || body === void 0)
|
|
17705
|
+
return null;
|
|
17706
|
+
if (typeof body === "string")
|
|
17707
|
+
return body;
|
|
17708
|
+
if (body instanceof Uint8Array)
|
|
17709
|
+
return body;
|
|
17710
|
+
try {
|
|
17711
|
+
return JSON.stringify(body);
|
|
17712
|
+
} catch {
|
|
17713
|
+
return null;
|
|
17714
|
+
}
|
|
17715
|
+
}
|
|
17389
17716
|
function shouldRetry(cfg, ctx) {
|
|
17390
17717
|
if (ctx.attempt > cfg.maxRetries)
|
|
17391
17718
|
return false;
|
|
17392
|
-
|
|
17719
|
+
const hasIdempotencyKey = Object.keys(ctx.request.headers ?? {}).some((name) => name.toLowerCase() === "idempotency-key" && ctx.request.headers[name] !== "");
|
|
17720
|
+
if (!hasIdempotencyKey && !cfg.methods.includes(ctx.request.method)) {
|
|
17393
17721
|
return false;
|
|
17722
|
+
}
|
|
17394
17723
|
if (cfg.shouldRetry)
|
|
17395
17724
|
return cfg.shouldRetry(ctx);
|
|
17396
17725
|
if (ctx.error) {
|
|
@@ -17417,6 +17746,10 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
17417
17746
|
return false;
|
|
17418
17747
|
case "EREDIRECT":
|
|
17419
17748
|
return false;
|
|
17749
|
+
// A blocked host is a permanent refusal, not a transport hiccup; retrying
|
|
17750
|
+
// resolves the same name to the same blocked address.
|
|
17751
|
+
case "EBLOCKEDHOST":
|
|
17752
|
+
return false;
|
|
17420
17753
|
case "EUNKNOWN":
|
|
17421
17754
|
return false;
|
|
17422
17755
|
default: {
|
|
@@ -17542,7 +17875,9 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
17542
17875
|
{
|
|
17543
17876
|
...config.strictHeaders ? { strict: true } : {},
|
|
17544
17877
|
...config.dispatcher !== void 0 ? { dispatcher: config.dispatcher } : {},
|
|
17545
|
-
...config.proxy !== void 0 ? { proxy: config.proxy } : {}
|
|
17878
|
+
...config.proxy !== void 0 ? { proxy: config.proxy } : {},
|
|
17879
|
+
...config.allowPrivateNetwork !== void 0 ? { allowPrivateNetwork: config.allowPrivateNetwork } : {},
|
|
17880
|
+
...config.ca !== void 0 ? { ca: config.ca } : {}
|
|
17546
17881
|
}
|
|
17547
17882
|
);
|
|
17548
17883
|
if (config.interceptors) {
|
|
@@ -17639,7 +17974,7 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
17639
17974
|
status: ctx.response.status,
|
|
17640
17975
|
statusText: ctx.response.statusText,
|
|
17641
17976
|
headers: ctx.response.headers,
|
|
17642
|
-
body: ctx.response.
|
|
17977
|
+
body: ctx.response.body ?? null,
|
|
17643
17978
|
request: {
|
|
17644
17979
|
url: ctx.request.url,
|
|
17645
17980
|
method: ctx.request.method,
|
|
@@ -18011,12 +18346,7 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
18011
18346
|
* ```
|
|
18012
18347
|
*/
|
|
18013
18348
|
async ws(url, options = {}) {
|
|
18014
|
-
const fullURL = buildURL(this.cfg.baseURL, url, this.cfg.params, [
|
|
18015
|
-
"http",
|
|
18016
|
-
"https",
|
|
18017
|
-
"ws",
|
|
18018
|
-
"wss"
|
|
18019
|
-
]);
|
|
18349
|
+
const fullURL = buildURL(this.cfg.baseURL, url, this.cfg.params, ["http", "https", "ws", "wss"], this.cfg.allowPrivateNetwork);
|
|
18020
18350
|
const headers = mergeHeaders(this.cfg.headers, options.headers);
|
|
18021
18351
|
const authHeaders = {};
|
|
18022
18352
|
const auth = this.cfg.auth;
|
|
@@ -18044,7 +18374,11 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
18044
18374
|
}
|
|
18045
18375
|
const jar = await this.getCookieJar();
|
|
18046
18376
|
if (jar) {
|
|
18047
|
-
const cookieHeader = jar.getCookieHeader({
|
|
18377
|
+
const cookieHeader = jar.getCookieHeader({
|
|
18378
|
+
url: fullURL,
|
|
18379
|
+
http: false,
|
|
18380
|
+
sameSiteContext: "strict"
|
|
18381
|
+
});
|
|
18048
18382
|
if (cookieHeader)
|
|
18049
18383
|
headers["cookie"] = cookieHeader;
|
|
18050
18384
|
}
|
|
@@ -18052,7 +18386,7 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
18052
18386
|
const wsUrl = new URL(fullURL);
|
|
18053
18387
|
if (this.cfg.baseURL) {
|
|
18054
18388
|
const baseUrl = new URL(this.cfg.baseURL);
|
|
18055
|
-
const wsIsSecure = wsUrl.protocol === "wss:";
|
|
18389
|
+
const wsIsSecure = wsUrl.protocol === "wss:" || wsUrl.protocol === "https:";
|
|
18056
18390
|
const httpIsSecure = baseUrl.protocol === "https:" || baseUrl.protocol === "wss:";
|
|
18057
18391
|
if (wsIsSecure !== httpIsSecure || wsUrl.host !== baseUrl.host) {
|
|
18058
18392
|
throw new KinetexError(`WebSocket origin ${wsUrl.origin} does not match baseURL origin ${baseUrl.origin}`, "EVALIDATION");
|
|
@@ -18100,6 +18434,7 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
18100
18434
|
this._wsClients.add(client);
|
|
18101
18435
|
await client.connect().catch((err) => {
|
|
18102
18436
|
this._wsClients.delete(client);
|
|
18437
|
+
client.destroy();
|
|
18103
18438
|
throw err;
|
|
18104
18439
|
});
|
|
18105
18440
|
return client;
|
|
@@ -18118,10 +18453,14 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
18118
18453
|
* @returns A promise resolving to the parsed response.
|
|
18119
18454
|
*/
|
|
18120
18455
|
async send(url, method, options = {}) {
|
|
18121
|
-
const startMs =
|
|
18456
|
+
const startMs = perfNow();
|
|
18122
18457
|
const wallClockMs = Date.now();
|
|
18123
18458
|
const timeoutMs = options.timeout !== void 0 ? options.timeout : this.cfg.timeout ?? 3e4;
|
|
18124
|
-
const retryCfg = options.retry === false ? false : {
|
|
18459
|
+
const retryCfg = options.retry === false ? false : {
|
|
18460
|
+
...this.retryConfig,
|
|
18461
|
+
...options.retry ?? {},
|
|
18462
|
+
...options.retry?.totalTimeoutMs !== void 0 ? {} : { totalTimeoutMs: options.totalTimeoutMs ?? this.cfg.totalTimeoutMs ?? timeoutMs }
|
|
18463
|
+
};
|
|
18125
18464
|
const VALID_METHODS = [
|
|
18126
18465
|
"GET",
|
|
18127
18466
|
"POST",
|
|
@@ -18137,7 +18476,7 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
18137
18476
|
if (!VALID_METHODS.includes(normalizedMethod)) {
|
|
18138
18477
|
throw new KinetexError(`Invalid HTTP method '${method}'. Valid methods: ${VALID_METHODS.join(", ")}`, "EVALIDATION");
|
|
18139
18478
|
}
|
|
18140
|
-
const fullUrl = buildURL(options.baseURL ?? this.cfg.baseURL, url, mergeParams(this.cfg.params, options.params));
|
|
18479
|
+
const fullUrl = buildURL(options.baseURL ?? this.cfg.baseURL, url, mergeParams(this.cfg.params, options.params), void 0, this.cfg.allowPrivateNetwork === true);
|
|
18141
18480
|
const perRequestProxy = options.proxy;
|
|
18142
18481
|
if (perRequestProxy) {
|
|
18143
18482
|
throw new KinetexError("A per-request `proxy` is not supported because the transport pools one connection per origin \u2014 set `proxy` on the client instead, or use a custom `fetch` with a proxy agent for per-request routing. SOCKS5 requires createSocks5Tunnel() from kinetex/socks5.", "EVALIDATION");
|
|
@@ -18238,11 +18577,16 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
18238
18577
|
req = await this.cfg.transformRequest(req);
|
|
18239
18578
|
const jar = await this.getCookieJar();
|
|
18240
18579
|
if (jar) {
|
|
18241
|
-
const cookieHeader = jar.getCookieHeader({
|
|
18580
|
+
const cookieHeader = jar.getCookieHeader({
|
|
18581
|
+
url: req.url,
|
|
18582
|
+
http: true,
|
|
18583
|
+
sameSiteContext: "strict"
|
|
18584
|
+
});
|
|
18242
18585
|
if (cookieHeader)
|
|
18243
18586
|
req = { ...req, headers: { ...req.headers, cookie: cookieHeader } };
|
|
18244
18587
|
}
|
|
18245
18588
|
let _otelSpan = null;
|
|
18589
|
+
let _dedupKey = "";
|
|
18246
18590
|
try {
|
|
18247
18591
|
if (this._otelTracer) {
|
|
18248
18592
|
_otelSpan = this._otelTracer.startSpan(`HTTP ${req.method}`, {
|
|
@@ -18270,6 +18614,10 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
18270
18614
|
meta: { ...req.meta, spanId }
|
|
18271
18615
|
};
|
|
18272
18616
|
}
|
|
18617
|
+
if (this._dedup) {
|
|
18618
|
+
const identity = await identityFingerprint(req.headers);
|
|
18619
|
+
_dedupKey = dedupKeyFrom(req.method, req.url, identity);
|
|
18620
|
+
}
|
|
18273
18621
|
} catch (tracingErr) {
|
|
18274
18622
|
if (_otelSpan) {
|
|
18275
18623
|
_otelSpan.setStatus({
|
|
@@ -18297,11 +18645,6 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
18297
18645
|
}
|
|
18298
18646
|
}
|
|
18299
18647
|
const _execFactory = () => this._executeWithRetry(req, retryCfg, timeoutMs, options, startMs, wallClockMs);
|
|
18300
|
-
let _dedupKey = "";
|
|
18301
|
-
if (this._dedup) {
|
|
18302
|
-
const authFp = await getAuthFingerprint(req.headers ?? {});
|
|
18303
|
-
_dedupKey = `${req.method}:${req.url}${authFp ? ":" + authFp : ""}`;
|
|
18304
|
-
}
|
|
18305
18648
|
const _dedupedFactory = this._dedup ? () => this._dedup.execute(req.method, _dedupKey, _execFactory).then((r) => r) : _execFactory;
|
|
18306
18649
|
const _guardedFactory = this._circuitBreakers ? () => this._circuitBreakers.execute(_cbOrigin, _dedupedFactory).then((r) => r) : _dedupedFactory;
|
|
18307
18650
|
try {
|
|
@@ -18416,12 +18759,16 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
18416
18759
|
const doRetry = retryCfg.shouldRetry ? await retryCfg.shouldRetry(retryCtx) : shouldRetry(retryCfg, retryCtx);
|
|
18417
18760
|
if (doRetry) {
|
|
18418
18761
|
const delay = computeRetryDelay(retryCfg, attempt, getRetryAfterMs(res.headers));
|
|
18762
|
+
if (retryCfg.totalTimeoutMs !== void 0 && retryCfg.totalTimeoutMs > 0) {
|
|
18763
|
+
if (perfNow() - startMs + delay >= retryCfg.totalTimeoutMs)
|
|
18764
|
+
return res;
|
|
18765
|
+
}
|
|
18419
18766
|
await retryCfg.onRetry?.(retryCtx, delay);
|
|
18420
18767
|
await this.cfg.hooks?.onRetry?.reduce(async (p, fn) => {
|
|
18421
18768
|
await p;
|
|
18422
18769
|
await fn(retryCtx);
|
|
18423
18770
|
}, Promise.resolve());
|
|
18424
|
-
await
|
|
18771
|
+
await sleep3(delay, req.signal);
|
|
18425
18772
|
continue;
|
|
18426
18773
|
}
|
|
18427
18774
|
}
|
|
@@ -18459,12 +18806,17 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
18459
18806
|
const doRetry = clockSkewCorrected ? true : retryCfg.shouldRetry ? await retryCfg.shouldRetry(retryCtx) : shouldRetry(retryCfg, retryCtx);
|
|
18460
18807
|
if (doRetry) {
|
|
18461
18808
|
const delay = computeRetryDelay(retryCfg, attempt, errResponse ? getRetryAfterMs(errResponse.headers) : null);
|
|
18809
|
+
if (retryCfg.totalTimeoutMs !== void 0 && retryCfg.totalTimeoutMs > 0) {
|
|
18810
|
+
if (perfNow() - startMs + delay >= retryCfg.totalTimeoutMs) {
|
|
18811
|
+
throw err;
|
|
18812
|
+
}
|
|
18813
|
+
}
|
|
18462
18814
|
await retryCfg.onRetry?.(retryCtx, delay);
|
|
18463
18815
|
await this.cfg.hooks?.onRetry?.reduce(async (p, fn) => {
|
|
18464
18816
|
await p;
|
|
18465
18817
|
await fn(retryCtx);
|
|
18466
18818
|
}, Promise.resolve());
|
|
18467
|
-
await
|
|
18819
|
+
await sleep3(delay, req.signal);
|
|
18468
18820
|
continue;
|
|
18469
18821
|
}
|
|
18470
18822
|
}
|
|
@@ -18530,7 +18882,7 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
18530
18882
|
request: currentReq,
|
|
18531
18883
|
response: null,
|
|
18532
18884
|
error: null,
|
|
18533
|
-
startedAt:
|
|
18885
|
+
startedAt: perfNow(),
|
|
18534
18886
|
attempt: 1,
|
|
18535
18887
|
meta: currentReq.meta
|
|
18536
18888
|
});
|
|
@@ -18575,7 +18927,9 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
18575
18927
|
if (protocol !== "http:" && protocol !== "https:") {
|
|
18576
18928
|
throw new KinetexError(`Unsafe redirect to ${protocol} detected \u2014 only HTTP(S) allowed`, "ENETWORK", { request: req });
|
|
18577
18929
|
}
|
|
18578
|
-
if (!isSafeURL(nextUrl
|
|
18930
|
+
if (!isSafeURL(nextUrl, void 0, {
|
|
18931
|
+
allowPrivateNetwork: this.cfg.allowPrivateNetwork === true
|
|
18932
|
+
})) {
|
|
18579
18933
|
throw new KinetexError(`Unsafe redirect target blocked: ${redactUserInfo(location)}`, "EVALIDATION", { request: req });
|
|
18580
18934
|
}
|
|
18581
18935
|
if (this.cfg.httpsOnly && protocol !== "https:") {
|
|
@@ -18591,6 +18945,9 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
18591
18945
|
}
|
|
18592
18946
|
const nextMethod = raw.status === 301 || raw.status === 302 || raw.status === 303 ? "GET" : currentReq.method;
|
|
18593
18947
|
const nextBody = nextMethod === "GET" || nextMethod === "HEAD" ? null : currentReq.body;
|
|
18948
|
+
if (nextBody !== null && isNonReplayableBody(nextBody)) {
|
|
18949
|
+
throw new KinetexError("Cannot follow this redirect: a 307/308 preserves the method and body, and this request's body is a stream or Blob that the first attempt already consumed. Buffer it first, or disable redirects for this request.", "EVALIDATION", { request: currentReq });
|
|
18950
|
+
}
|
|
18594
18951
|
const nextHeaders = { ...currentReq.headers };
|
|
18595
18952
|
let crossOrigin = false;
|
|
18596
18953
|
try {
|
|
@@ -18606,7 +18963,11 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
18606
18963
|
}
|
|
18607
18964
|
}
|
|
18608
18965
|
if (jar) {
|
|
18609
|
-
const cookieHeader = jar.getCookieHeader({
|
|
18966
|
+
const cookieHeader = jar.getCookieHeader({
|
|
18967
|
+
url: nextUrl,
|
|
18968
|
+
http: true,
|
|
18969
|
+
sameSiteContext: "strict"
|
|
18970
|
+
});
|
|
18610
18971
|
if (cookieHeader) {
|
|
18611
18972
|
nextHeaders["cookie"] = cookieHeader;
|
|
18612
18973
|
} else {
|
|
@@ -18659,6 +19020,10 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
18659
19020
|
}
|
|
18660
19021
|
}
|
|
18661
19022
|
this._trace(_traceId, "lifecycle_before", "end", startMs, attempt);
|
|
19023
|
+
const _logger = await this.getLogger();
|
|
19024
|
+
if (_logger) {
|
|
19025
|
+
_logger.logRequest(_traceId, req.method, req.url, req.headers, loggableBody(req.body), attempt);
|
|
19026
|
+
}
|
|
18662
19027
|
const forceRefresh = options.cache !== false && options.cache?.forceRefresh === true;
|
|
18663
19028
|
if (options.cache !== false && this.cfg.cache) {
|
|
18664
19029
|
const cache = await this.getCache();
|
|
@@ -18879,7 +19244,7 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
18879
19244
|
}
|
|
18880
19245
|
});
|
|
18881
19246
|
}
|
|
18882
|
-
const maxSize = options.maxResponseSize ?? this.cfg.maxResponseSize ??
|
|
19247
|
+
const maxSize = options.maxResponseSize ?? this.cfg.maxResponseSize ?? DEFAULT_MAX_RESPONSE_SIZE;
|
|
18883
19248
|
const rawBody = await readRawBody(bodyStream, maxSize, raw.url, req.signal);
|
|
18884
19249
|
const jar = await this.getCookieJar();
|
|
18885
19250
|
if (jar) {
|
|
@@ -18891,7 +19256,7 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
18891
19256
|
this._trace(_traceId, "response_parse", "start", startMs, attempt);
|
|
18892
19257
|
const data = await parseBody(rawBody, ct, options.parseResponse, options.parseFailure, raw.headers, raw.url);
|
|
18893
19258
|
this._trace(_traceId, "response_parse", "end", startMs, attempt);
|
|
18894
|
-
const durationMs =
|
|
19259
|
+
const durationMs = perfNow() - startMs;
|
|
18895
19260
|
const res = {
|
|
18896
19261
|
status: raw.status,
|
|
18897
19262
|
statusText: raw.statusText,
|
|
@@ -18900,6 +19265,9 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
18900
19265
|
// the real response object instead of the `{}` placeholder it used to get.
|
|
18901
19266
|
data,
|
|
18902
19267
|
rawBody,
|
|
19268
|
+
// The same bytes in the shape an interceptor sees, so one response
|
|
19269
|
+
// object satisfies both the public and the pipeline views.
|
|
19270
|
+
body: rawBody,
|
|
18903
19271
|
url: raw.url,
|
|
18904
19272
|
cached: false,
|
|
18905
19273
|
redirected: raw.redirected,
|
|
@@ -18917,9 +19285,9 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
18917
19285
|
await cache.set({ url: req.url, method: req.method, headers: req.headers }, { status: raw.status, statusText: raw.statusText, headers: raw.headers, body: rawBody }, { tags: options.tags ?? [] });
|
|
18918
19286
|
}
|
|
18919
19287
|
}
|
|
18920
|
-
const logger = await this.getLogger();
|
|
19288
|
+
const logger = _logger ?? await this.getLogger();
|
|
18921
19289
|
if (logger) {
|
|
18922
|
-
const requestId = ctx.store.get("requestId") ?? req.meta["requestId"] ??
|
|
19290
|
+
const requestId = ctx.store.get("requestId") ?? req.meta["requestId"] ?? _traceId;
|
|
18923
19291
|
logger.logResponse(requestId, res.status, res.statusText, res.headers, rawBody, attempt, false);
|
|
18924
19292
|
}
|
|
18925
19293
|
this.harRecorder?.record(req, res, wallClockMs ?? Date.now() - res.durationMs);
|
|
@@ -18997,7 +19365,7 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
18997
19365
|
await p;
|
|
18998
19366
|
await fn2(retryCtx);
|
|
18999
19367
|
}, Promise.resolve());
|
|
19000
|
-
await
|
|
19368
|
+
await sleep3(delay, _req.signal);
|
|
19001
19369
|
}
|
|
19002
19370
|
return this._executeOnce({
|
|
19003
19371
|
...result,
|
|
@@ -19051,7 +19419,7 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
19051
19419
|
const bodyData = raw.body;
|
|
19052
19420
|
const rawBody = bodyData instanceof Uint8Array ? bodyData : typeof bodyData === "string" ? new TextEncoder().encode(bodyData) : null;
|
|
19053
19421
|
const data = rawBody ? await parseBody(rawBody, raw.headers["content-type"] ?? null, options.parseResponse, options.parseFailure, raw.headers, "url" in raw ? raw.url : req.url) : null;
|
|
19054
|
-
const durationMs =
|
|
19422
|
+
const durationMs = perfNow() - startMs;
|
|
19055
19423
|
const responseURL = "url" in raw ? raw.url : req.url;
|
|
19056
19424
|
const wasRedirected = "redirected" in raw ? raw.redirected : false;
|
|
19057
19425
|
const responseHTTPVersion = "httpVersion" in raw ? raw.httpVersion : "HTTP/1.1";
|
|
@@ -19059,6 +19427,8 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
19059
19427
|
status: raw.status,
|
|
19060
19428
|
statusText: raw.statusText,
|
|
19061
19429
|
headers: raw.headers,
|
|
19430
|
+
// Present so the object satisfies both response views at once.
|
|
19431
|
+
body: rawBody,
|
|
19062
19432
|
data,
|
|
19063
19433
|
rawBody,
|
|
19064
19434
|
url: responseURL,
|
|
@@ -19175,7 +19545,7 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
19175
19545
|
async sse(url, options = {}) {
|
|
19176
19546
|
const { SSEClient: SSEClient2 } = await Promise.resolve().then(() => (init_sse(), sse_exports));
|
|
19177
19547
|
const headers = mergeHeaders(this.cfg.headers, options.headers);
|
|
19178
|
-
const fullURL = buildURL(this.cfg.baseURL, url, this.cfg.params);
|
|
19548
|
+
const fullURL = buildURL(this.cfg.baseURL, url, this.cfg.params, void 0, this.cfg.allowPrivateNetwork);
|
|
19179
19549
|
const pipeFetch = async (input, init) => {
|
|
19180
19550
|
const reqUrl = typeof input === "string" ? input : input.url;
|
|
19181
19551
|
const fi = init;
|
|
@@ -19214,7 +19584,7 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
19214
19584
|
async graphql(url, options = {}) {
|
|
19215
19585
|
const { GraphQLClient: GraphQLClient2 } = await Promise.resolve().then(() => (init_graphql(), graphql_exports));
|
|
19216
19586
|
const headers = mergeHeaders(this.cfg.headers, options.headers);
|
|
19217
|
-
const fullURL = buildURL(this.cfg.baseURL, url, this.cfg.params);
|
|
19587
|
+
const fullURL = buildURL(this.cfg.baseURL, url, this.cfg.params, void 0, this.cfg.allowPrivateNetwork);
|
|
19218
19588
|
const pipeFetch = async (input, init) => {
|
|
19219
19589
|
const reqUrl = typeof input === "string" ? input : input.url;
|
|
19220
19590
|
const fi = init;
|
|
@@ -19252,7 +19622,7 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
19252
19622
|
*/
|
|
19253
19623
|
async paginate(url, options) {
|
|
19254
19624
|
const { createPagePaginator: createPagePaginator2 } = await Promise.resolve().then(() => (init_pagination(), pagination_exports));
|
|
19255
|
-
const fullURL = buildURL(this.cfg.baseURL, url, this.cfg.params);
|
|
19625
|
+
const fullURL = buildURL(this.cfg.baseURL, url, this.cfg.params, void 0, this.cfg.allowPrivateNetwork);
|
|
19256
19626
|
const pipeFetch = async (input, init) => {
|
|
19257
19627
|
const reqUrl = typeof input === "string" ? input : input.url;
|
|
19258
19628
|
const fi = init;
|
|
@@ -19579,7 +19949,7 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
19579
19949
|
return void 0;
|
|
19580
19950
|
return { ...a, ...b };
|
|
19581
19951
|
}
|
|
19582
|
-
function
|
|
19952
|
+
function sleep3(ms, signal) {
|
|
19583
19953
|
return new Promise((resolve, reject) => {
|
|
19584
19954
|
if (signal?.aborted) {
|
|
19585
19955
|
reject(createAbortError());
|
|
@@ -19601,9 +19971,6 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
19601
19971
|
return true;
|
|
19602
19972
|
return typeof Blob !== "undefined" && body instanceof Blob;
|
|
19603
19973
|
}
|
|
19604
|
-
function perfNow4() {
|
|
19605
|
-
return typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
19606
|
-
}
|
|
19607
19974
|
function createMethodCircuitBreakerKey(req) {
|
|
19608
19975
|
try {
|
|
19609
19976
|
return `${req.method}:${new URL(req.url).origin}`;
|
|
@@ -19739,6 +20106,7 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
19739
20106
|
init_headers();
|
|
19740
20107
|
|
|
19741
20108
|
// dist/esm/lifecycle.js
|
|
20109
|
+
init_utils();
|
|
19742
20110
|
var HookEmitter = class {
|
|
19743
20111
|
constructor() {
|
|
19744
20112
|
__publicField(this, "listeners", /* @__PURE__ */ new Map());
|
|
@@ -20286,12 +20654,7 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
20286
20654
|
function createLoggingHooks(options = {}) {
|
|
20287
20655
|
const log = options.logger ?? ((msg, data) => console.log(msg, JSON.stringify(data)));
|
|
20288
20656
|
const redact = new Set((options.redactHeaders ?? ["authorization", "cookie", "set-cookie", "proxy-authorization"]).map((h) => h.toLowerCase()));
|
|
20289
|
-
|
|
20290
|
-
const out = {};
|
|
20291
|
-
for (const [k, v] of Object.entries(h))
|
|
20292
|
-
out[k] = redact.has(k.toLowerCase()) ? "***" : v;
|
|
20293
|
-
return out;
|
|
20294
|
-
}
|
|
20657
|
+
const safeHeaders = (h) => redactHeaders(h, redact, "***");
|
|
20295
20658
|
return {
|
|
20296
20659
|
beforeRequest: (req) => {
|
|
20297
20660
|
log("\u2192 request", {
|
|
@@ -20320,11 +20683,11 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
20320
20683
|
const TIMING_KEY = "__kinetex_timing__";
|
|
20321
20684
|
return {
|
|
20322
20685
|
beforeRequest: (_req, ctx) => {
|
|
20323
|
-
ctx.meta[TIMING_KEY] =
|
|
20686
|
+
ctx.meta[TIMING_KEY] = perfNow();
|
|
20324
20687
|
},
|
|
20325
20688
|
afterResponse: (_res, ctx) => {
|
|
20326
20689
|
const start = ctx.meta[TIMING_KEY];
|
|
20327
|
-
const elapsed = start !== void 0 ?
|
|
20690
|
+
const elapsed = start !== void 0 ? perfNow() - start : null;
|
|
20328
20691
|
if (elapsed !== null) {
|
|
20329
20692
|
ctx.meta.durationMs = elapsed;
|
|
20330
20693
|
}
|
|
@@ -20347,7 +20710,7 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
20347
20710
|
beforeRequest: (req, ctx) => {
|
|
20348
20711
|
if (req.signal?.aborted) {
|
|
20349
20712
|
ctx.meta.aborted = true;
|
|
20350
|
-
throw
|
|
20713
|
+
throw abortError("Request aborted before sending");
|
|
20351
20714
|
}
|
|
20352
20715
|
},
|
|
20353
20716
|
onCancel: (evt) => {
|
|
@@ -20355,23 +20718,12 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
20355
20718
|
}
|
|
20356
20719
|
};
|
|
20357
20720
|
}
|
|
20358
|
-
function createAbortError2(message) {
|
|
20359
|
-
if (typeof DOMException !== "undefined") {
|
|
20360
|
-
return new DOMException(message, "AbortError");
|
|
20361
|
-
}
|
|
20362
|
-
const err = new Error(message);
|
|
20363
|
-
err.name = "AbortError";
|
|
20364
|
-
return err;
|
|
20365
|
-
}
|
|
20366
|
-
function perfNow5() {
|
|
20367
|
-
return typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
20368
|
-
}
|
|
20369
20721
|
function createHookContext(request, overrides = {}) {
|
|
20370
20722
|
return {
|
|
20371
20723
|
request,
|
|
20372
20724
|
response: null,
|
|
20373
20725
|
error: null,
|
|
20374
|
-
startedAt:
|
|
20726
|
+
startedAt: perfNow(),
|
|
20375
20727
|
attempt: 1,
|
|
20376
20728
|
meta: {},
|
|
20377
20729
|
...overrides
|
|
@@ -20656,12 +21008,12 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
|
|
|
20656
21008
|
const retriable = err instanceof Socks5Error && err.retriable;
|
|
20657
21009
|
if (!retriable || attempt === maxRetries)
|
|
20658
21010
|
break;
|
|
20659
|
-
await
|
|
21011
|
+
await sleep4(delayMs * Math.pow(2, attempt));
|
|
20660
21012
|
}
|
|
20661
21013
|
}
|
|
20662
21014
|
throw lastError;
|
|
20663
21015
|
}
|
|
20664
|
-
function
|
|
21016
|
+
function sleep4(ms) {
|
|
20665
21017
|
return new Promise((r) => setTimeout(r, ms));
|
|
20666
21018
|
}
|
|
20667
21019
|
var TRANSIENT_DIAL_CODES = /* @__PURE__ */ new Set([
|