kinetex 1.0.0 → 1.1.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.
Files changed (51) hide show
  1. package/README.md +104 -16
  2. package/dist/browser/kinetex.esm.js +18 -18
  3. package/dist/browser/kinetex.js +509 -240
  4. package/dist/browser/kinetex.min.js +18 -18
  5. package/dist/cjs/client.js +180 -11
  6. package/dist/cjs/cookie-store.js +10 -0
  7. package/dist/cjs/core.js +25 -8
  8. package/dist/cjs/graphql.js +18 -4
  9. package/dist/cjs/logging.js +10 -2
  10. package/dist/cjs/mod.js +1 -1
  11. package/dist/cjs/pagination.js +6 -1
  12. package/dist/cjs/response.js +8 -6
  13. package/dist/cjs/sse.js +5 -2
  14. package/dist/cjs/utils.js +238 -57
  15. package/dist/cjs/ws.js +6 -1
  16. package/dist/esm/client.js +180 -11
  17. package/dist/esm/client.js.map +1 -1
  18. package/dist/esm/cookie-store.js +10 -0
  19. package/dist/esm/cookie-store.js.map +1 -1
  20. package/dist/esm/core.js +25 -8
  21. package/dist/esm/core.js.map +1 -1
  22. package/dist/esm/graphql.js +18 -4
  23. package/dist/esm/graphql.js.map +1 -1
  24. package/dist/esm/logging.js +10 -2
  25. package/dist/esm/logging.js.map +1 -1
  26. package/dist/esm/mod.js +1 -1
  27. package/dist/esm/mod.js.map +1 -1
  28. package/dist/esm/pagination.js +6 -1
  29. package/dist/esm/pagination.js.map +1 -1
  30. package/dist/esm/response.js +8 -6
  31. package/dist/esm/response.js.map +1 -1
  32. package/dist/esm/sse.js +5 -2
  33. package/dist/esm/sse.js.map +1 -1
  34. package/dist/esm/utils.js +238 -57
  35. package/dist/esm/utils.js.map +1 -1
  36. package/dist/esm/ws.js +6 -1
  37. package/dist/esm/ws.js.map +1 -1
  38. package/dist/types/client.d.ts.map +1 -1
  39. package/dist/types/cookie-store.d.ts.map +1 -1
  40. package/dist/types/core.d.ts.map +1 -1
  41. package/dist/types/graphql.d.ts.map +1 -1
  42. package/dist/types/logging.d.ts.map +1 -1
  43. package/dist/types/mod.d.ts +1 -1
  44. package/dist/types/mod.d.ts.map +1 -1
  45. package/dist/types/pagination.d.ts.map +1 -1
  46. package/dist/types/response.d.ts.map +1 -1
  47. package/dist/types/sse.d.ts.map +1 -1
  48. package/dist/types/utils.d.ts +18 -2
  49. package/dist/types/utils.d.ts.map +1 -1
  50. package/dist/types/ws.d.ts.map +1 -1
  51. package/package.json +5 -5
@@ -1,4 +1,4 @@
1
- /* kinetex v1.0.0 | MIT | https://github.com/kinetexjs/kinetex */
1
+ /* kinetex v1.1.0 | MIT | https://github.com/kinetexjs/kinetex */
2
2
  "use strict";
3
3
  var kinetex = (() => {
4
4
  var __create = Object.create;
@@ -51,160 +51,6 @@ var kinetex = (() => {
51
51
  var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
52
52
  var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
53
53
 
54
- // dist/esm/types.js
55
- function toRequestId(id) {
56
- return id;
57
- }
58
- function validateErrorCode(code) {
59
- if (typeof code === "string") {
60
- for (const valid of VALID_ERROR_CODES) {
61
- if (valid === code)
62
- return valid;
63
- }
64
- }
65
- return void 0;
66
- }
67
- var VALID_ERROR_CODES, KinetexError, SizeLimitError, HTTPStatusError, AbortError, NetworkError, ValidationError, AuthError, ProxyError, RedirectError, TimeoutError;
68
- var init_types = __esm({
69
- "dist/esm/types.js"() {
70
- "use strict";
71
- VALID_ERROR_CODES = [
72
- "ENETWORK",
73
- "ETIMEOUT",
74
- "EABORT",
75
- "EHTTPSTATUS",
76
- "ESIZELIMIT",
77
- "EPARSE",
78
- "EVALIDATION",
79
- "EAUTH",
80
- "EPROXY",
81
- "EREDIRECT",
82
- "EUNKNOWN"
83
- ];
84
- KinetexError = class extends Error {
85
- /**
86
- * @param message - Human-readable error description.
87
- * @param code - Machine-readable error code.
88
- * @param options - Additional context (request, response, cause).
89
- */
90
- constructor(message, code, options) {
91
- super(message);
92
- /** Machine-readable error code. */
93
- __publicField(this, "code");
94
- /** Original request, if available. */
95
- __publicField(this, "request");
96
- /** Response, if available (for HTTP-level errors). */
97
- __publicField(this, "response");
98
- /** Underlying cause. */
99
- __publicField(this, "cause");
100
- this.name = "KinetexError";
101
- this.code = code;
102
- if (options?.request !== void 0)
103
- this.request = options.request;
104
- if (options?.response !== void 0)
105
- this.response = options.response;
106
- if (options?.cause !== void 0)
107
- this.cause = options.cause;
108
- }
109
- /** True if the error is a network/transport failure (not proxy-related). */
110
- get isNetwork() {
111
- return this.code === "ENETWORK";
112
- }
113
- /** True if the error is a proxy-related failure. */
114
- get isProxy() {
115
- return this.code === "EPROXY";
116
- }
117
- /** True if the error is a timeout. */
118
- get isTimeout() {
119
- return this.code === "ETIMEOUT";
120
- }
121
- /** True if the request was aborted by the caller. */
122
- get isAbort() {
123
- return this.code === "EABORT";
124
- }
125
- /** True if the server returned a 4xx or 5xx status. */
126
- get isHTTPError() {
127
- return this.code === "EHTTPSTATUS";
128
- }
129
- /** HTTP status code shorthand (null for non-HTTP errors). */
130
- get status() {
131
- return this.response?.status ?? null;
132
- }
133
- };
134
- SizeLimitError = class extends KinetexError {
135
- constructor(bytesRead, limit, request) {
136
- super(`Response size limit exceeded: ${bytesRead} bytes read, limit is ${limit} bytes`, "ESIZELIMIT", request !== void 0 ? { request } : {});
137
- __publicField(this, "bytesRead");
138
- __publicField(this, "limit");
139
- this.bytesRead = bytesRead;
140
- this.limit = limit;
141
- this.name = "SizeLimitError";
142
- }
143
- };
144
- HTTPStatusError = class extends KinetexError {
145
- constructor(response, request) {
146
- super(`HTTP ${response.status} ${response.statusText} \u2014 ${response.url}`, "EHTTPSTATUS", {
147
- request,
148
- response
149
- });
150
- this.name = "HTTPStatusError";
151
- }
152
- /** True if the status is a 4xx client error. */
153
- get isClientError() {
154
- return (this.status ?? 0) >= 400 && (this.status ?? 0) < 500;
155
- }
156
- /** True if the status is a 5xx server error. */
157
- get isServerError() {
158
- return (this.status ?? 0) >= 500;
159
- }
160
- };
161
- AbortError = class extends KinetexError {
162
- constructor(request) {
163
- super("Request was aborted", "EABORT", request !== void 0 ? { request } : {});
164
- this.name = "AbortError";
165
- }
166
- };
167
- NetworkError = class extends KinetexError {
168
- constructor(message, request) {
169
- super(message, "ENETWORK", request !== void 0 ? { request } : {});
170
- this.name = "NetworkError";
171
- }
172
- };
173
- ValidationError = class extends KinetexError {
174
- constructor(message, request) {
175
- super(message, "EVALIDATION", request !== void 0 ? { request } : {});
176
- this.name = "ValidationError";
177
- }
178
- };
179
- AuthError = class extends KinetexError {
180
- constructor(message, request) {
181
- super(message, "EAUTH", request !== void 0 ? { request } : {});
182
- this.name = "AuthError";
183
- }
184
- };
185
- ProxyError = class extends KinetexError {
186
- constructor(message, request) {
187
- super(message, "EPROXY", request !== void 0 ? { request } : {});
188
- this.name = "ProxyError";
189
- }
190
- };
191
- RedirectError = class extends KinetexError {
192
- constructor(message, request) {
193
- super(message, "EREDIRECT", request !== void 0 ? { request } : {});
194
- this.name = "RedirectError";
195
- }
196
- };
197
- TimeoutError = class extends KinetexError {
198
- constructor(timeoutMs, request) {
199
- super(`Request timed out after ${timeoutMs}ms`, "ETIMEOUT", request !== void 0 ? { request } : {});
200
- __publicField(this, "timeoutMs");
201
- this.timeoutMs = timeoutMs;
202
- this.name = "TimeoutError";
203
- }
204
- };
205
- }
206
- });
207
-
208
54
  // dist/esm/utils.js
209
55
  function getProcess() {
210
56
  if (_process)
@@ -346,6 +192,25 @@ var kinetex = (() => {
346
192
  const result = safeJSONParse(text);
347
193
  return result.success ? result.value : text;
348
194
  }
195
+ function sanitizeParsedJSON(value) {
196
+ if (value === null || typeof value !== "object")
197
+ return value;
198
+ if (Array.isArray(value)) {
199
+ for (let i = 0; i < value.length; i++) {
200
+ value[i] = sanitizeParsedJSON(value[i]);
201
+ }
202
+ return value;
203
+ }
204
+ const obj = value;
205
+ for (const key of Object.keys(obj)) {
206
+ if (key === "__proto__" || key === "constructor" || key === "prototype") {
207
+ delete obj[key];
208
+ continue;
209
+ }
210
+ sanitizeParsedJSON(obj[key]);
211
+ }
212
+ return value;
213
+ }
349
214
  function parseUntrustedJSON(text) {
350
215
  return safeJSONParse(text, {
351
216
  maxDepth: 16,
@@ -401,6 +266,123 @@ var kinetex = (() => {
401
266
  }
402
267
  return true;
403
268
  }
269
+ function isBlockedIPv4(n) {
270
+ return IPV4_BLOCKED_RANGES.some(([lo, hi]) => n >= lo && n <= hi);
271
+ }
272
+ function parseIPv4Component(p) {
273
+ if (p === "")
274
+ return null;
275
+ if (/^0[xX][0-9a-fA-F]+$/.test(p))
276
+ return parseInt(p, 16);
277
+ if (/^0[0-7]+$/.test(p))
278
+ return parseInt(p, 8);
279
+ if (/^\d+$/.test(p))
280
+ return parseInt(p, 10);
281
+ return null;
282
+ }
283
+ function parseIPv4Host(host) {
284
+ if (host.includes(":") || !/^[0-9a-fA-FxX.]+$/.test(host))
285
+ return null;
286
+ const parts = host.split(".");
287
+ if (parts.length > 4)
288
+ return null;
289
+ if (parts.length === 1) {
290
+ const v = parseIPv4Component(parts[0]);
291
+ if (v === null || v > 4294967295)
292
+ return null;
293
+ return v >>> 0;
294
+ }
295
+ const nums = [];
296
+ for (const p of parts) {
297
+ const v = parseIPv4Component(p);
298
+ const isLast = p === parts[parts.length - 1];
299
+ if (v === null || v > (isLast && nums.length === 3 ? 255 : 255))
300
+ return null;
301
+ nums.push(v);
302
+ }
303
+ while (nums.length < 4)
304
+ nums.push(0);
305
+ return (nums[0] << 24 | nums[1] << 16 | nums[2] << 8 | nums[3]) >>> 0;
306
+ }
307
+ function parseIPv6Host(host) {
308
+ if (!host.includes(":"))
309
+ return null;
310
+ const bare = host.split("%")[0];
311
+ const halves = bare.split("::");
312
+ if (halves.length > 2)
313
+ return null;
314
+ const expandGroups = (part) => {
315
+ if (part === "")
316
+ return [];
317
+ const groups = part.split(":");
318
+ const out = [];
319
+ for (let i = 0; i < groups.length; i++) {
320
+ const g2 = groups[i];
321
+ if (g2 === "")
322
+ return null;
323
+ if (g2.includes(".")) {
324
+ if (i !== groups.length - 1)
325
+ return null;
326
+ const n = parseIPv4Host(g2);
327
+ if (n === null)
328
+ return null;
329
+ out.push(n >>> 24 & 255, n >>> 16 & 255, n >>> 8 & 255, n & 255);
330
+ } else {
331
+ if (!/^[0-9a-fA-F]{1,4}$/.test(g2))
332
+ return null;
333
+ const v = parseInt(g2, 16);
334
+ out.push(v >>> 8 & 255, v & 255);
335
+ }
336
+ }
337
+ return out;
338
+ };
339
+ let bytes;
340
+ if (halves.length === 2) {
341
+ const left = expandGroups(halves[0]);
342
+ const right = expandGroups(halves[1]);
343
+ if (!left || !right)
344
+ return null;
345
+ const fill = 16 - left.length - right.length;
346
+ if (fill < 0)
347
+ return null;
348
+ bytes = [...left, ...new Array(fill).fill(0), ...right];
349
+ } else {
350
+ const all = expandGroups(bare);
351
+ if (!all || all.length !== 16)
352
+ return null;
353
+ bytes = all;
354
+ }
355
+ return bytes.length === 16 ? new Uint8Array(bytes) : null;
356
+ }
357
+ function isBlockedIPv6(b) {
358
+ const read32 = (i) => (b[i] << 24 | b[i + 1] << 16 | b[i + 2] << 8 | b[i + 3]) >>> 0;
359
+ if (b.every((x) => x === 0))
360
+ return true;
361
+ if (b.slice(0, 15).every((x) => x === 0) && b[15] === 1)
362
+ return true;
363
+ const first = b[0];
364
+ if ((first & 254) === 252)
365
+ return true;
366
+ if (first === 254 && (b[1] & 192) === 128)
367
+ return true;
368
+ if (first === 255)
369
+ return true;
370
+ if (b[0] === 32 && b[1] === 1 && b[2] === 13 && b[3] === 184)
371
+ return true;
372
+ if (b.slice(0, 10).every((x) => x === 0) && b[10] === 255 && b[11] === 255) {
373
+ return isBlockedIPv4(read32(12));
374
+ }
375
+ if (b.slice(0, 12).every((x) => x === 0)) {
376
+ return isBlockedIPv4(read32(12));
377
+ }
378
+ if (b[0] === 0 && b[1] === 100 && b[2] === 255 && b[3] === 155 && b.slice(4, 12).every((x) => x === 0)) {
379
+ return isBlockedIPv4(read32(12));
380
+ }
381
+ if (b[0] === 32 && b[1] === 2) {
382
+ return isBlockedIPv4(read32(2));
383
+ }
384
+ return false;
385
+ }
404
386
  function isSafeURL(url, allowedSchemes = ["http", "https"]) {
405
387
  try {
406
388
  const parsed = typeof url === "string" ? new URL(url) : url;
@@ -412,14 +394,20 @@ var kinetex = (() => {
412
394
  return false;
413
395
  }
414
396
  let host = parsed.hostname.toLowerCase();
415
- if (host.startsWith("[") && host.endsWith("]"))
397
+ const isV6Literal = host.startsWith("[") && host.endsWith("]");
398
+ if (isV6Literal)
416
399
  host = host.slice(1, -1);
417
- if (host === "0:0:0:0:0:0:0:1")
418
- host = "::1";
419
- if (host === "localhost" || host === "0.0.0.0")
420
- return false;
421
- if (PRIVATE_IP_RANGES.some((r) => r.test(host)))
400
+ if (host === "localhost" || host.endsWith(".localhost") || host === "0.0.0.0")
422
401
  return false;
402
+ if (isV6Literal || host.includes(":")) {
403
+ const v6 = parseIPv6Host(host);
404
+ if (v6 === null || isBlockedIPv6(v6))
405
+ return false;
406
+ } else {
407
+ const v4 = parseIPv4Host(host);
408
+ if (v4 !== null && isBlockedIPv4(v4))
409
+ return false;
410
+ }
423
411
  if (parsed.hostname.includes("..") || parsed.pathname.includes("..")) {
424
412
  return false;
425
413
  }
@@ -463,6 +451,8 @@ var kinetex = (() => {
463
451
  }
464
452
  const cloned = {};
465
453
  for (const key in value) {
454
+ if (key === "__proto__" || key === "constructor" || key === "prototype")
455
+ continue;
466
456
  if (Object.prototype.hasOwnProperty.call(value, key)) {
467
457
  cloned[key] = deepClone(value[key]);
468
458
  }
@@ -619,17 +609,14 @@ var kinetex = (() => {
619
609
  return false;
620
610
  }
621
611
  function randomBytes(byteCount) {
612
+ if (!Number.isInteger(byteCount) || byteCount < 0 || byteCount > 65536) {
613
+ throw new Error(`randomBytes: invalid byteCount ${byteCount}`);
614
+ }
622
615
  const arr = new Uint8Array(byteCount);
623
616
  if (typeof crypto !== "undefined" && typeof crypto.getRandomValues === "function") {
624
617
  crypto.getRandomValues(arr);
625
618
  } else {
626
- const uuid = typeof crypto !== "undefined" && typeof crypto.randomUUID === "function" ? crypto.randomUUID().replace(/-/g, "") : "";
627
- if (uuid.length >= byteCount * 2) {
628
- return uuid.slice(0, byteCount * 2);
629
- }
630
- for (let i = 0; i < arr.length; i++) {
631
- arr[i] = Math.floor(Math.random() * 256);
632
- }
619
+ throw new Error("randomBytes: no CSPRNG available in this runtime \u2014 crypto.getRandomValues is required");
633
620
  }
634
621
  return Array.from(arr).map((b) => b.toString(16).padStart(2, "0")).join("");
635
622
  }
@@ -681,7 +668,7 @@ var kinetex = (() => {
681
668
  });
682
669
  return result;
683
670
  }
684
- var _process, _Buffer, DEFAULT_LIMITS, HEADER_NAME_PATTERN, FORBIDDEN_SCHEMES, PRIVATE_IP_RANGES, cachedRuntime;
671
+ var _process, _Buffer, DEFAULT_LIMITS, HEADER_NAME_PATTERN, FORBIDDEN_SCHEMES, IPV4_BLOCKED_RANGES, cachedRuntime;
685
672
  var init_utils = __esm({
686
673
  "dist/esm/utils.js"() {
687
674
  "use strict";
@@ -717,53 +704,194 @@ var kinetex = (() => {
717
704
  "webcal",
718
705
  "urn"
719
706
  ]);
720
- PRIVATE_IP_RANGES = [
721
- /^127\./,
722
- // loopback
723
- /^10\./,
724
- // RFC 1918
725
- /^192\.168\./,
726
- // RFC 1918
727
- /^172\.(1[6-9]|2\d|3[01])\./,
728
- // RFC 1918
729
- /^169\.254\./,
730
- // link-local (AWS IMDS, etc.)
731
- /^0\./,
732
- // this-network
733
- /^::1$/i,
734
- // IPv6 loopback
735
- /^fc00:/i,
736
- // IPv6 ULA (fc00::/7 - includes fc00: and fd00:)
737
- /^fd00:/i,
738
- // IPv6 ULA (fd00::/8 - unique local)
739
- /^fe80:/i,
740
- // IPv6 link-local (fe80::/10)
741
- /^2001:db8:/i,
742
- // IPv6 documentation range
743
- /^::0?ffff:/i,
744
- // IPv4-mapped IPv6 (::ffff:...)
745
- /^0:0:0:0:0:ffff:/i,
746
- // IPv4-mapped compressed form
747
- // FIX 4: IPv4-mapped IPv6 SSRF bypass — e.g. http://[::ffff:127.0.0.1]
748
- /^::ffff:127\./i,
749
- // IPv4-mapped loopback
750
- /^::ffff:10\./i,
751
- // IPv4-mapped RFC 1918
752
- /^::ffff:192\.168\./i,
753
- // IPv4-mapped RFC 1918
754
- /^::ffff:172\.(1[6-9]|2\d|3[01])\./i,
755
- // IPv4-mapped RFC 1918
756
- /^::ffff:169\.254\./i,
757
- // IPv4-mapped link-local
758
- /^0:0:0:0:0:ffff:7f/i,
759
- // compressed form of ::ffff:127.x
760
- /^::$/i
761
- // IPv4-compatible :: (any ::/96)
707
+ IPV4_BLOCKED_RANGES = [
708
+ [0, 16777215],
709
+ // 0.0.0.0/8 — this-network
710
+ [167772160, 184549375],
711
+ // 10.0.0.0/8 — RFC 1918 private
712
+ [1681915904, 1686110207],
713
+ // 100.64.0.0/10 — CGNAT (RFC 6598)
714
+ [2130706432, 2147483647],
715
+ // 127.0.0.0/8 — loopback
716
+ [2851995648, 2852061183],
717
+ // 169.254.0.0/16 — link-local (AWS IMDS etc.)
718
+ [2886729728, 2887778303],
719
+ // 172.16.0.0/12 — RFC 1918 private
720
+ [3221225472, 3221225727],
721
+ // 192.0.0.0/24 — IETF protocol assignments
722
+ [3221225984, 3221226239],
723
+ // 192.0.2.0/24 — TEST-NET-1
724
+ [3232235520, 3232301055],
725
+ // 192.168.0.0/16 — RFC 1918 private
726
+ [3323068416, 3323199487],
727
+ // 198.18.0.0/15 — benchmarking
728
+ [3325256704, 3325256959],
729
+ // 198.51.100.0/24 — TEST-NET-2
730
+ [3405803776, 3405804031],
731
+ // 203.0.113.0/24 — TEST-NET-3
732
+ [3758096384, 4026531839],
733
+ // 224.0.0.0/4 — multicast
734
+ [4026531840, 4294967295]
735
+ // 240.0.0.0/4 — reserved (incl. 255.255.255.255)
762
736
  ];
763
737
  cachedRuntime = null;
764
738
  }
765
739
  });
766
740
 
741
+ // dist/esm/types.js
742
+ function toRequestId(id) {
743
+ return id;
744
+ }
745
+ function validateErrorCode(code) {
746
+ if (typeof code === "string") {
747
+ for (const valid of VALID_ERROR_CODES) {
748
+ if (valid === code)
749
+ return valid;
750
+ }
751
+ }
752
+ return void 0;
753
+ }
754
+ var VALID_ERROR_CODES, KinetexError, SizeLimitError, HTTPStatusError, AbortError, NetworkError, ValidationError, AuthError, ProxyError, RedirectError, TimeoutError;
755
+ var init_types = __esm({
756
+ "dist/esm/types.js"() {
757
+ "use strict";
758
+ VALID_ERROR_CODES = [
759
+ "ENETWORK",
760
+ "ETIMEOUT",
761
+ "EABORT",
762
+ "EHTTPSTATUS",
763
+ "ESIZELIMIT",
764
+ "EPARSE",
765
+ "EVALIDATION",
766
+ "EAUTH",
767
+ "EPROXY",
768
+ "EREDIRECT",
769
+ "EUNKNOWN"
770
+ ];
771
+ KinetexError = class extends Error {
772
+ /**
773
+ * @param message - Human-readable error description.
774
+ * @param code - Machine-readable error code.
775
+ * @param options - Additional context (request, response, cause).
776
+ */
777
+ constructor(message, code, options) {
778
+ super(message);
779
+ /** Machine-readable error code. */
780
+ __publicField(this, "code");
781
+ /** Original request, if available. */
782
+ __publicField(this, "request");
783
+ /** Response, if available (for HTTP-level errors). */
784
+ __publicField(this, "response");
785
+ /** Underlying cause. */
786
+ __publicField(this, "cause");
787
+ this.name = "KinetexError";
788
+ this.code = code;
789
+ if (options?.request !== void 0)
790
+ this.request = options.request;
791
+ if (options?.response !== void 0)
792
+ this.response = options.response;
793
+ if (options?.cause !== void 0)
794
+ this.cause = options.cause;
795
+ }
796
+ /** True if the error is a network/transport failure (not proxy-related). */
797
+ get isNetwork() {
798
+ return this.code === "ENETWORK";
799
+ }
800
+ /** True if the error is a proxy-related failure. */
801
+ get isProxy() {
802
+ return this.code === "EPROXY";
803
+ }
804
+ /** True if the error is a timeout. */
805
+ get isTimeout() {
806
+ return this.code === "ETIMEOUT";
807
+ }
808
+ /** True if the request was aborted by the caller. */
809
+ get isAbort() {
810
+ return this.code === "EABORT";
811
+ }
812
+ /** True if the server returned a 4xx or 5xx status. */
813
+ get isHTTPError() {
814
+ return this.code === "EHTTPSTATUS";
815
+ }
816
+ /** HTTP status code shorthand (null for non-HTTP errors). */
817
+ get status() {
818
+ return this.response?.status ?? null;
819
+ }
820
+ };
821
+ SizeLimitError = class extends KinetexError {
822
+ constructor(bytesRead, limit, request) {
823
+ super(`Response size limit exceeded: ${bytesRead} bytes read, limit is ${limit} bytes`, "ESIZELIMIT", request !== void 0 ? { request } : {});
824
+ __publicField(this, "bytesRead");
825
+ __publicField(this, "limit");
826
+ this.bytesRead = bytesRead;
827
+ this.limit = limit;
828
+ this.name = "SizeLimitError";
829
+ }
830
+ };
831
+ HTTPStatusError = class extends KinetexError {
832
+ constructor(response, request) {
833
+ super(`HTTP ${response.status} ${response.statusText} \u2014 ${response.url}`, "EHTTPSTATUS", {
834
+ request,
835
+ response
836
+ });
837
+ this.name = "HTTPStatusError";
838
+ }
839
+ /** True if the status is a 4xx client error. */
840
+ get isClientError() {
841
+ return (this.status ?? 0) >= 400 && (this.status ?? 0) < 500;
842
+ }
843
+ /** True if the status is a 5xx server error. */
844
+ get isServerError() {
845
+ return (this.status ?? 0) >= 500;
846
+ }
847
+ };
848
+ AbortError = class extends KinetexError {
849
+ constructor(request) {
850
+ super("Request was aborted", "EABORT", request !== void 0 ? { request } : {});
851
+ this.name = "AbortError";
852
+ }
853
+ };
854
+ NetworkError = class extends KinetexError {
855
+ constructor(message, request) {
856
+ super(message, "ENETWORK", request !== void 0 ? { request } : {});
857
+ this.name = "NetworkError";
858
+ }
859
+ };
860
+ ValidationError = class extends KinetexError {
861
+ constructor(message, request) {
862
+ super(message, "EVALIDATION", request !== void 0 ? { request } : {});
863
+ this.name = "ValidationError";
864
+ }
865
+ };
866
+ AuthError = class extends KinetexError {
867
+ constructor(message, request) {
868
+ super(message, "EAUTH", request !== void 0 ? { request } : {});
869
+ this.name = "AuthError";
870
+ }
871
+ };
872
+ ProxyError = class extends KinetexError {
873
+ constructor(message, request) {
874
+ super(message, "EPROXY", request !== void 0 ? { request } : {});
875
+ this.name = "ProxyError";
876
+ }
877
+ };
878
+ RedirectError = class extends KinetexError {
879
+ constructor(message, request) {
880
+ super(message, "EREDIRECT", request !== void 0 ? { request } : {});
881
+ this.name = "RedirectError";
882
+ }
883
+ };
884
+ TimeoutError = class extends KinetexError {
885
+ constructor(timeoutMs, request) {
886
+ super(`Request timed out after ${timeoutMs}ms`, "ETIMEOUT", request !== void 0 ? { request } : {});
887
+ __publicField(this, "timeoutMs");
888
+ this.timeoutMs = timeoutMs;
889
+ this.name = "TimeoutError";
890
+ }
891
+ };
892
+ }
893
+ });
894
+
767
895
  // dist/esm/cache.js
768
896
  var cache_exports = {};
769
897
  __export(cache_exports, {
@@ -3706,7 +3834,7 @@ var kinetex = (() => {
3706
3834
  const charset = options.charset ?? parseContentType2(ct ?? "")?.charset ?? null;
3707
3835
  const text = decodeBody(bytes, charset, url);
3708
3836
  try {
3709
- return JSON.parse(text);
3837
+ return sanitizeParsedJSON(JSON.parse(text));
3710
3838
  } catch (err) {
3711
3839
  throw new ResponseDecodeError(`JSON parse failed: ${err}`, "json", url);
3712
3840
  }
@@ -3761,7 +3889,7 @@ var kinetex = (() => {
3761
3889
  const trimmed = buffer.trim();
3762
3890
  if (trimmed) {
3763
3891
  try {
3764
- yield JSON.parse(trimmed);
3892
+ yield sanitizeParsedJSON(JSON.parse(trimmed));
3765
3893
  } catch (err) {
3766
3894
  options.onParseError?.(err, trimmed);
3767
3895
  }
@@ -3776,7 +3904,7 @@ var kinetex = (() => {
3776
3904
  if (!trimmed || trimmed.startsWith("#"))
3777
3905
  continue;
3778
3906
  try {
3779
- yield JSON.parse(trimmed);
3907
+ yield sanitizeParsedJSON(JSON.parse(trimmed));
3780
3908
  } catch (err) {
3781
3909
  options.onParseError?.(err, trimmed);
3782
3910
  }
@@ -3809,7 +3937,7 @@ var kinetex = (() => {
3809
3937
  const remaining = flushBuffer();
3810
3938
  if (remaining) {
3811
3939
  try {
3812
- const obj = JSON.parse(remaining);
3940
+ const obj = sanitizeParsedJSON(JSON.parse(remaining));
3813
3941
  options.onObject?.(obj);
3814
3942
  yield obj;
3815
3943
  } catch (err) {
@@ -3830,7 +3958,7 @@ var kinetex = (() => {
3830
3958
  if (depth === 0 && startIdx !== -1) {
3831
3959
  const objStr = buffer.slice(startIdx, i + 1);
3832
3960
  try {
3833
- const obj = JSON.parse(objStr);
3961
+ const obj = sanitizeParsedJSON(JSON.parse(objStr));
3834
3962
  options.onObject?.(obj);
3835
3963
  yield obj;
3836
3964
  } catch (err) {
@@ -4374,7 +4502,7 @@ var kinetex = (() => {
4374
4502
  }
4375
4503
  }
4376
4504
  function createTransport(fetchFn, preferHTTP2 = true, sessionOptions, transportOptions) {
4377
- if (IS_NODE && preferHTTP2) {
4505
+ if (IS_NODE && preferHTTP2 && !fetchFn) {
4378
4506
  return new NodeHTTP2Transport({
4379
4507
  ...sessionOptions?.sessionTTLMs !== void 0 ? { sessionTTLMs: sessionOptions.sessionTTLMs } : {},
4380
4508
  ...sessionOptions?.pingIntervalMs !== void 0 ? { pingIntervalMs: sessionOptions.pingIntervalMs } : {},
@@ -4399,8 +4527,11 @@ var kinetex = (() => {
4399
4527
  }, timeoutMs);
4400
4528
  const signal = mergeSignals(request.signal, controller.signal) ?? null;
4401
4529
  const req = { ...request, signal };
4530
+ const timeoutPromise = new Promise((_, reject) => {
4531
+ controller.signal.addEventListener("abort", () => reject(new TimeoutError(timeoutMs, request)), { once: true });
4532
+ });
4402
4533
  try {
4403
- const result = await transport.send(req);
4534
+ const result = await Promise.race([transport.send(req), timeoutPromise]);
4404
4535
  clearTimeout(timer);
4405
4536
  if (controller.signal.aborted) {
4406
4537
  throw new TimeoutError(timeoutMs, request);
@@ -4408,7 +4539,7 @@ var kinetex = (() => {
4408
4539
  return result;
4409
4540
  } catch (err) {
4410
4541
  clearTimeout(timer);
4411
- if (controller.signal.aborted) {
4542
+ if (controller.signal.aborted && !(err instanceof TimeoutError)) {
4412
4543
  throw new TimeoutError(timeoutMs, request);
4413
4544
  }
4414
4545
  throw err;
@@ -4969,12 +5100,15 @@ var kinetex = (() => {
4969
5100
  }
4970
5101
  }
4971
5102
  if (hasForbidden) {
5103
+ if (this._strict) {
5104
+ throw new KinetexError(`Strict mode: header "${hName}" contains forbidden control characters`, "EVALIDATION", { request: currentReq });
5105
+ }
4972
5106
  if (this._onDroppedHeader) {
4973
5107
  this._onDroppedHeader(hName, hStr);
4974
- delete h2ReqHeaders[hName];
4975
- } else {
4976
- throw new KinetexError(`Strict mode: header "${hName}" contains forbidden control characters`, "EVALIDATION", { request: currentReq });
5108
+ } else if (typeof console !== "undefined") {
5109
+ console.warn(`[kinetex] Invalid header dropped (HTTP/2): "${hName}" \u2014 value contains illegal control characters. Pass strictHeaders: true to throw instead.`);
4977
5110
  }
5111
+ delete h2ReqHeaders[hName];
4978
5112
  }
4979
5113
  }
4980
5114
  }
@@ -7325,6 +7459,10 @@ var kinetex = (() => {
7325
7459
  continue;
7326
7460
  if (!sameSiteAllows(cookie.sameSite, context))
7327
7461
  continue;
7462
+ if (cookie.name.startsWith("__Secure-") && !(cookie.secure && isSecure))
7463
+ continue;
7464
+ if (cookie.name.startsWith("__Host-") && !(cookie.secure && isSecure && cookie.path === "/" && cookie.hostOnly))
7465
+ continue;
7328
7466
  result.push(cookie);
7329
7467
  }
7330
7468
  }
@@ -7797,9 +7935,11 @@ var kinetex = (() => {
7797
7935
  const [head, ...rest] = path;
7798
7936
  if (head === void 0)
7799
7937
  return;
7938
+ if (head === "__proto__" || head === "constructor" || head === "prototype")
7939
+ return;
7800
7940
  const o = obj;
7801
7941
  if (rest.length === 0) {
7802
- if (head in o)
7942
+ if (Object.prototype.hasOwnProperty.call(o, head))
7803
7943
  o[head] = "***";
7804
7944
  return;
7805
7945
  }
@@ -7882,6 +8022,7 @@ var kinetex = (() => {
7882
8022
  var init_logging = __esm({
7883
8023
  "dist/esm/logging.js"() {
7884
8024
  "use strict";
8025
+ init_utils();
7885
8026
  getNodeEnv = () => {
7886
8027
  try {
7887
8028
  return globalThis.process?.env?.["NODE_ENV"];
@@ -8241,7 +8382,7 @@ var kinetex = (() => {
8241
8382
  const size = str.length;
8242
8383
  if (ct.includes("application/json") && this.bodyFields.length > 0) {
8243
8384
  try {
8244
- const parsed = JSON.parse(str);
8385
+ const parsed = sanitizeParsedJSON(JSON.parse(str));
8245
8386
  for (const path of this.bodyFields)
8246
8387
  redactObjectPath(parsed, path.split("."));
8247
8388
  str = JSON.stringify(parsed);
@@ -8512,7 +8653,7 @@ var kinetex = (() => {
8512
8653
  if (!evt.data && evt.data !== "")
8513
8654
  continue;
8514
8655
  try {
8515
- const data = JSON.parse(evt.data, options.reviver);
8656
+ const data = sanitizeParsedJSON(JSON.parse(evt.data, options.reviver));
8516
8657
  yield { event: evt.event, data, id: evt.id };
8517
8658
  } catch (err) {
8518
8659
  options.onError?.(err, evt);
@@ -8552,6 +8693,7 @@ var kinetex = (() => {
8552
8693
  var init_sse = __esm({
8553
8694
  "dist/esm/sse.js"() {
8554
8695
  "use strict";
8696
+ init_utils();
8555
8697
  SSEParser = class {
8556
8698
  constructor() {
8557
8699
  __publicField(this, "lines", []);
@@ -8991,7 +9133,7 @@ var kinetex = (() => {
8991
9133
  onJSON(eventType, handler) {
8992
9134
  return this.on(eventType, async (data, evt) => {
8993
9135
  try {
8994
- const parsed = JSON.parse(data);
9136
+ const parsed = sanitizeParsedJSON(JSON.parse(data));
8995
9137
  await handler(parsed, evt);
8996
9138
  } catch {
8997
9139
  }
@@ -9315,11 +9457,18 @@ var kinetex = (() => {
9315
9457
  let current = obj;
9316
9458
  for (let i = 0; i < parts.length - 1; i++) {
9317
9459
  const key = parts[i];
9460
+ if (key === "__proto__" || key === "constructor" || key === "prototype") {
9461
+ throw new ValidationError(`Invalid upload path: "${path}" \u2014 reserved key "${key}"`);
9462
+ }
9318
9463
  if (!current[key] || typeof current[key] !== "object")
9319
9464
  current[key] = {};
9320
9465
  current = current[key];
9321
9466
  }
9322
- current[parts[parts.length - 1]] = value;
9467
+ const leaf = parts[parts.length - 1];
9468
+ if (leaf === "__proto__" || leaf === "constructor" || leaf === "prototype") {
9469
+ throw new ValidationError(`Invalid upload path: "${path}" \u2014 reserved key "${leaf}"`);
9470
+ }
9471
+ current[leaf] = value;
9323
9472
  }
9324
9473
  async function parseGraphQLResponse(response, req) {
9325
9474
  const ct = response.headers.get("content-type") ?? "";
@@ -9330,7 +9479,7 @@ ${text}`, "ENETWORK", void 0, req, void 0, new Error(`HTTP ${response.status}`))
9330
9479
  }
9331
9480
  let json;
9332
9481
  try {
9333
- json = await response.json();
9482
+ json = sanitizeParsedJSON(await response.json());
9334
9483
  } catch (err) {
9335
9484
  throw new GraphQLClientError("Failed to parse GraphQL response as JSON", "EPARSE", void 0, req, void 0, err);
9336
9485
  }
@@ -9477,6 +9626,7 @@ ${text}`, "ENETWORK", void 0, req, void 0, new Error(`HTTP ${response.status}`))
9477
9626
  "dist/esm/graphql.js"() {
9478
9627
  "use strict";
9479
9628
  init_types();
9629
+ init_utils();
9480
9630
  GraphQLClientError = class extends Error {
9481
9631
  /**
9482
9632
  * @param message - Human-readable error description
@@ -9798,7 +9948,7 @@ ${text}`, "ENETWORK", void 0, req, void 0, new Error(`HTTP ${response.status}`))
9798
9948
  } catch (err) {
9799
9949
  throw new GraphQLClientError(err instanceof Error ? err.message : "Batch network error", "ENETWORK", void 0, requests[0], void 0, err);
9800
9950
  }
9801
- const rawResults = await response.json();
9951
+ const rawResults = sanitizeParsedJSON(await response.json());
9802
9952
  if (!Array.isArray(rawResults)) {
9803
9953
  throw new GraphQLClientError("Batch response must be an array of GraphQL responses", "EINVALIDRESPONSE", void 0, requests[0], rawResults);
9804
9954
  }
@@ -9886,7 +10036,7 @@ ${text}`, "ENETWORK", void 0, req, void 0, new Error(`HTTP ${response.status}`))
9886
10036
  continue;
9887
10037
  let gqlRes;
9888
10038
  try {
9889
- gqlRes = JSON.parse(event.data);
10039
+ gqlRes = sanitizeParsedJSON(JSON.parse(event.data));
9890
10040
  } catch {
9891
10041
  continue;
9892
10042
  }
@@ -10413,7 +10563,7 @@ ${text}`, "ENETWORK", void 0, req, void 0, new Error(`HTTP ${response.status}`))
10413
10563
  }
10414
10564
  function deserializePaginationState(serialized) {
10415
10565
  try {
10416
- return JSON.parse(atob(serialized));
10566
+ return sanitizeParsedJSON(JSON.parse(atob(serialized)));
10417
10567
  } catch {
10418
10568
  throw new Error("Invalid pagination state string");
10419
10569
  }
@@ -10460,6 +10610,7 @@ ${text}`, "ENETWORK", void 0, req, void 0, new Error(`HTTP ${response.status}`))
10460
10610
  var init_pagination = __esm({
10461
10611
  "dist/esm/pagination.js"() {
10462
10612
  "use strict";
10613
+ init_utils();
10463
10614
  }
10464
10615
  });
10465
10616
 
@@ -10768,6 +10919,7 @@ ${text}`, "ENETWORK", void 0, req, void 0, new Error(`HTTP ${response.status}`))
10768
10919
  retryLink: () => retryLink,
10769
10920
  safeJSONParse: () => safeJSONParse,
10770
10921
  safeParseURL: () => safeParseURL,
10922
+ sanitizeParsedJSON: () => sanitizeParsedJSON,
10771
10923
  sanitizeURL: () => sanitizeURL,
10772
10924
  securityHeaders: () => securityHeaders,
10773
10925
  sendWithTimeout: () => sendWithTimeout,
@@ -11782,6 +11934,7 @@ ${text}`, "ENETWORK", void 0, req, void 0, new Error(`HTTP ${response.status}`))
11782
11934
  }
11783
11935
 
11784
11936
  // dist/esm/ws.js
11937
+ init_utils();
11785
11938
  var WSError = class extends Error {
11786
11939
  /**
11787
11940
  * @param message - Human-readable error description
@@ -12452,7 +12605,7 @@ ${text}`, "ENETWORK", void 0, req, void 0, new Error(`HTTP ${response.status}`))
12452
12605
  data = evt.data;
12453
12606
  bytes = new TextEncoder().encode(data).byteLength;
12454
12607
  try {
12455
- json = JSON.parse(data);
12608
+ json = sanitizeParsedJSON(JSON.parse(data));
12456
12609
  } catch {
12457
12610
  }
12458
12611
  } else if (evt.data instanceof ArrayBuffer) {
@@ -14868,6 +15021,34 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
14868
15021
  function randomHex(len) {
14869
15022
  return randomBytes(len / 2);
14870
15023
  }
15024
+ var HAR_REDACT_HEADERS = /* @__PURE__ */ new Set([
15025
+ "authorization",
15026
+ "proxy-authorization",
15027
+ "cookie",
15028
+ "set-cookie",
15029
+ "x-api-key",
15030
+ "x-auth-token",
15031
+ "x-access-token",
15032
+ "x-refresh-token",
15033
+ "x-csrf-token",
15034
+ "x-session-id",
15035
+ "x-session-token",
15036
+ "x-secret",
15037
+ "x-secret-key",
15038
+ "x-private-key",
15039
+ "api-key",
15040
+ "apikey",
15041
+ "bearer",
15042
+ "token",
15043
+ "authentication",
15044
+ "credentials",
15045
+ "password",
15046
+ "passwd",
15047
+ "secret"
15048
+ ]);
15049
+ function redactHARHeader(name, value) {
15050
+ return HAR_REDACT_HEADERS.has(name.toLowerCase()) ? { name, value: "***REDACTED***" } : { name, value };
15051
+ }
14871
15052
  var HARRecorder = class {
14872
15053
  /**
14873
15054
  * @param maxEntries - Maximum number of entries before eviction (default 10000).
@@ -14919,7 +15100,7 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
14919
15100
  method: req.method,
14920
15101
  url: req.url,
14921
15102
  httpVersion: res.httpVersion,
14922
- headers: Object.entries(req.headers).map(([name, value]) => ({ name, value })),
15103
+ headers: Object.entries(req.headers).map(([name, value]) => redactHARHeader(name, value)),
14923
15104
  queryString: (() => {
14924
15105
  try {
14925
15106
  return Array.from(new URL(req.url).searchParams.entries()).map(([name, value]) => ({
@@ -14946,7 +15127,7 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
14946
15127
  status: res.status,
14947
15128
  statusText: res.statusText,
14948
15129
  httpVersion: res.httpVersion,
14949
- headers: Object.entries(res.headers).map(([name, value]) => ({ name, value })),
15130
+ headers: Object.entries(res.headers).map(([name, value]) => redactHARHeader(name, value)),
14950
15131
  content: {
14951
15132
  size: res.rawBody?.byteLength ?? 0,
14952
15133
  mimeType: res.headers["content-type"] ?? "application/octet-stream",
@@ -14994,7 +15175,11 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
14994
15175
  switch (auth.type) {
14995
15176
  case "bearer": {
14996
15177
  const token = typeof auth.token === "function" ? await auth.token() : auth.token;
14997
- headers["authorization"] = `Bearer ${token}`;
15178
+ const headerValue = `Bearer ${token}`;
15179
+ if (!isValidHeaderValue(headerValue)) {
15180
+ throw new KinetexError("Invalid bearer token \u2014 contains forbidden characters (CRLF/CTL)", "EVALIDATION");
15181
+ }
15182
+ headers["authorization"] = headerValue;
14998
15183
  break;
14999
15184
  }
15000
15185
  case "basic": {
@@ -15015,7 +15200,13 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
15015
15200
  }
15016
15201
  case "apikey": {
15017
15202
  const key = typeof auth.key === "function" ? await auth.key() : auth.key;
15018
- headers[auth.header.toLowerCase()] = key;
15203
+ if (!isValidHeaderName(auth.header)) {
15204
+ throw new KinetexError(`Invalid apikey auth header name: "${auth.header}"`, "EVALIDATION");
15205
+ }
15206
+ if (!isValidHeaderValue(String(key))) {
15207
+ throw new KinetexError(`Invalid apikey value for "${auth.header}" \u2014 contains forbidden characters`, "EVALIDATION");
15208
+ }
15209
+ headers[auth.header.toLowerCase()] = String(key);
15019
15210
  break;
15020
15211
  }
15021
15212
  case "digest": {
@@ -15034,6 +15225,37 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
15034
15225
  }
15035
15226
  return { ...req, headers };
15036
15227
  }
15228
+ var CROSS_ORIGIN_STRIP_HEADERS = /* @__PURE__ */ new Set([
15229
+ "authorization",
15230
+ "cookie",
15231
+ "proxy-authorization",
15232
+ "x-api-key",
15233
+ "x-auth-token",
15234
+ "x-access-token",
15235
+ "x-refresh-token",
15236
+ "x-csrf-token",
15237
+ "x-session-id",
15238
+ "x-session-token",
15239
+ "x-secret",
15240
+ "x-secret-key",
15241
+ "x-private-key",
15242
+ "api-key",
15243
+ "apikey",
15244
+ "www-authenticate"
15245
+ ]);
15246
+ function redactUserInfo(url) {
15247
+ try {
15248
+ const u = new URL(url);
15249
+ if (u.username || u.password) {
15250
+ u.username = "";
15251
+ u.password = "";
15252
+ return u.toString();
15253
+ }
15254
+ return url;
15255
+ } catch {
15256
+ return url.replace(/\/\/[^/@]*@/, "//");
15257
+ }
15258
+ }
15037
15259
  function buildURL(base, url, params) {
15038
15260
  const MAX_QUERY_PARAM_COUNT = 100;
15039
15261
  const MAX_URL_LENGTH = 8192;
@@ -15054,7 +15276,7 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
15054
15276
  }
15055
15277
  if (!params || Object.keys(params).length === 0) {
15056
15278
  if (!isSafeURL(full)) {
15057
- throw new KinetexError(`URL "${full}" failed safety check \u2014 blocked private/loopback address or forbidden scheme`, "EVALIDATION");
15279
+ throw new KinetexError(`URL "${redactUserInfo(full)}" failed safety check \u2014 blocked private/loopback address or forbidden scheme`, "EVALIDATION");
15058
15280
  }
15059
15281
  return full;
15060
15282
  }
@@ -15088,7 +15310,7 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
15088
15310
  throw new KinetexError(`URL length ${result.length} bytes exceeds limit of ${MAX_URL_LENGTH} bytes`, "EVALIDATION");
15089
15311
  }
15090
15312
  if (!isSafeURL(result)) {
15091
- throw new KinetexError(`URL "${result}" failed safety check \u2014 blocked private/loopback address or forbidden scheme`, "EVALIDATION");
15313
+ throw new KinetexError(`URL "${redactUserInfo(result)}" failed safety check \u2014 blocked private/loopback address or forbidden scheme`, "EVALIDATION");
15092
15314
  }
15093
15315
  return result;
15094
15316
  } catch (err) {
@@ -15126,6 +15348,9 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
15126
15348
  if (result.length > MAX_URL_LENGTH) {
15127
15349
  throw new KinetexError(`URL length ${result.length} bytes exceeds limit of ${MAX_URL_LENGTH} bytes`, "EVALIDATION");
15128
15350
  }
15351
+ if (!isSafeURL(result)) {
15352
+ throw new KinetexError(`URL "${redactUserInfo(result)}" failed safety check \u2014 blocked private/loopback address or forbidden scheme`, "EVALIDATION");
15353
+ }
15129
15354
  return result;
15130
15355
  }
15131
15356
  }
@@ -15873,6 +16098,10 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
15873
16098
  throw new KinetexError(`Invalid HTTP method '${method}'. Valid methods: ${VALID_METHODS.join(", ")}`, "EVALIDATION");
15874
16099
  }
15875
16100
  const fullUrl = buildURL(options.baseURL ?? this.cfg.baseURL, url, mergeParams(this.cfg.params, options.params));
16101
+ const proxy = options.proxy ?? this.cfg.proxy;
16102
+ if (proxy) {
16103
+ throw new KinetexError("proxy is configured but kinetex's built-in transports cannot route through it: HTTP(S) proxies require a custom fetch with a proxy agent (e.g. undici ProxyAgent passed via the `fetch` option), and SOCKS5 requires createSocks5Tunnel() from kinetex/socks5. Set up one of those instead of relying on `proxy` silently doing nothing.", "EVALIDATION");
16104
+ }
15876
16105
  if (this.cfg.httpsOnly) {
15877
16106
  try {
15878
16107
  const parsedUrl = new URL(fullUrl);
@@ -15896,9 +16125,25 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
15896
16125
  bodySize2 = options.body.byteLength;
15897
16126
  } else if (options.body instanceof Blob) {
15898
16127
  bodySize2 = options.body.size;
16128
+ } else if (ArrayBuffer.isView(options.body)) {
16129
+ bodySize2 = options.body.byteLength;
16130
+ } else if (options.body instanceof URLSearchParams) {
16131
+ bodySize2 = new TextEncoder().encode(options.body.toString()).byteLength;
15899
16132
  } else if (options.body instanceof FormData) {
16133
+ const boundaryOverhead = 76;
16134
+ for (const [name, value] of options.body) {
16135
+ bodySize2 += new TextEncoder().encode(name).byteLength + boundaryOverhead;
16136
+ if (typeof value === "string") {
16137
+ bodySize2 += new TextEncoder().encode(value).byteLength;
16138
+ } else {
16139
+ bodySize2 += value.size;
16140
+ }
16141
+ }
16142
+ bodySize2 += boundaryOverhead;
16143
+ } else if (options.body instanceof ReadableStream) {
16144
+ throw new KinetexError(`maxRequestSize cannot be enforced for ReadableStream bodies \u2014 pass maxRequestSize: 0 to opt out, or buffer the body first`, "EVALIDATION");
15900
16145
  } else if (options.body && typeof options.body === "object") {
15901
- bodySize2 = JSON.stringify(options.body).length;
16146
+ bodySize2 = new TextEncoder().encode(JSON.stringify(options.body)).byteLength;
15902
16147
  }
15903
16148
  if (bodySize2 > maxRequestSize) {
15904
16149
  throw new KinetexError(`Request body size ${bodySize2} bytes exceeds limit of ${maxRequestSize} bytes`, "EVALIDATION");
@@ -16078,10 +16323,26 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
16078
16323
  async _sendFollowingRedirects(req, timeout, jar, appliedAuth) {
16079
16324
  const MAX_REDIRECTS = 20;
16080
16325
  let currentReq = { ...req, redirect: "manual" };
16326
+ const origin0 = (() => {
16327
+ try {
16328
+ return new URL(req.url).origin;
16329
+ } catch {
16330
+ return null;
16331
+ }
16332
+ })();
16081
16333
  const visited = /* @__PURE__ */ new Set();
16082
16334
  for (let hop = 0; hop <= MAX_REDIRECTS; hop++) {
16083
16335
  if (hop > 0 && appliedAuth) {
16084
- currentReq = await applyAuth(currentReq, appliedAuth);
16336
+ const sameOrigin = (() => {
16337
+ try {
16338
+ return new URL(currentReq.url).origin === origin0;
16339
+ } catch {
16340
+ return false;
16341
+ }
16342
+ })();
16343
+ if (sameOrigin) {
16344
+ currentReq = await applyAuth(currentReq, appliedAuth);
16345
+ }
16085
16346
  }
16086
16347
  if (hop > 0 && this.cfg.hooks?.onBeforeRequest) {
16087
16348
  for (const fn of this.cfg.hooks.onBeforeRequest) {
@@ -16153,6 +16414,14 @@ ${errors.map((e) => ` - ${e.message}`).join("\n")}`);
16153
16414
  } else {
16154
16415
  delete nextHeaders["cookie"];
16155
16416
  }
16417
+ try {
16418
+ if (new URL(nextUrl).origin !== new URL(currentReq.url).origin) {
16419
+ for (const h of CROSS_ORIGIN_STRIP_HEADERS) {
16420
+ delete nextHeaders[h];
16421
+ }
16422
+ }
16423
+ } catch {
16424
+ }
16156
16425
  currentReq = {
16157
16426
  ...currentReq,
16158
16427
  url: nextUrl,