@ai-sdk/provider-utils 3.0.25 → 3.0.27

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/dist/index.js CHANGED
@@ -38,6 +38,7 @@ __export(index_exports, {
38
38
  VERSION: () => VERSION,
39
39
  asSchema: () => asSchema,
40
40
  asValidator: () => asValidator,
41
+ cancelResponseBody: () => cancelResponseBody,
41
42
  combineHeaders: () => combineHeaders,
42
43
  convertAsyncIteratorToReadableStream: () => convertAsyncIteratorToReadableStream,
43
44
  convertBase64ToUint8Array: () => convertBase64ToUint8Array,
@@ -56,13 +57,16 @@ __export(index_exports, {
56
57
  dynamicTool: () => dynamicTool,
57
58
  executeTool: () => executeTool,
58
59
  extractResponseHeaders: () => extractResponseHeaders,
60
+ fetchWithValidatedRedirects: () => fetchWithValidatedRedirects,
59
61
  generateId: () => generateId,
60
62
  getErrorMessage: () => getErrorMessage,
61
63
  getFromApi: () => getFromApi,
62
64
  getRuntimeEnvironmentUserAgent: () => getRuntimeEnvironmentUserAgent,
63
65
  injectJsonInstructionIntoMessages: () => injectJsonInstructionIntoMessages,
64
66
  isAbortError: () => isAbortError,
67
+ isBrowserRuntime: () => isBrowserRuntime,
65
68
  isParsableJson: () => isParsableJson,
69
+ isSameOrigin: () => isSameOrigin,
66
70
  isUrlSupported: () => isUrlSupported,
67
71
  isValidator: () => isValidator,
68
72
  jsonSchema: () => jsonSchema,
@@ -251,6 +255,201 @@ var DownloadError = class extends (_b = import_provider.AISDKError, _a = symbol,
251
255
  }
252
256
  };
253
257
 
258
+ // src/cancel-response-body.ts
259
+ async function cancelResponseBody(response) {
260
+ var _a2;
261
+ try {
262
+ await ((_a2 = response.body) == null ? void 0 : _a2.cancel());
263
+ } catch (e) {
264
+ }
265
+ }
266
+
267
+ // src/is-browser-runtime.ts
268
+ function isBrowserRuntime(globalThisAny = globalThis) {
269
+ return globalThisAny.window != null;
270
+ }
271
+
272
+ // src/validate-download-url.ts
273
+ function validateDownloadUrl(url) {
274
+ let parsed;
275
+ try {
276
+ parsed = new URL(url);
277
+ } catch (e) {
278
+ throw new DownloadError({
279
+ url,
280
+ message: `Invalid URL: ${url}`
281
+ });
282
+ }
283
+ if (parsed.protocol === "data:") {
284
+ return;
285
+ }
286
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
287
+ throw new DownloadError({
288
+ url,
289
+ message: `URL scheme must be http, https, or data, got ${parsed.protocol}`
290
+ });
291
+ }
292
+ const hostname = parsed.hostname.toLowerCase().replace(/\.+$/, "");
293
+ if (!hostname) {
294
+ throw new DownloadError({
295
+ url,
296
+ message: `URL must have a hostname`
297
+ });
298
+ }
299
+ if (hostname === "localhost" || hostname.endsWith(".local") || hostname.endsWith(".localhost")) {
300
+ throw new DownloadError({
301
+ url,
302
+ message: `URL with hostname ${hostname} is not allowed`
303
+ });
304
+ }
305
+ if (hostname.startsWith("[") && hostname.endsWith("]")) {
306
+ const ipv6 = hostname.slice(1, -1);
307
+ if (isPrivateIPv6(ipv6)) {
308
+ throw new DownloadError({
309
+ url,
310
+ message: `URL with IPv6 address ${hostname} is not allowed`
311
+ });
312
+ }
313
+ return;
314
+ }
315
+ if (isIPv4(hostname)) {
316
+ if (isPrivateIPv4(hostname)) {
317
+ throw new DownloadError({
318
+ url,
319
+ message: `URL with IP address ${hostname} is not allowed`
320
+ });
321
+ }
322
+ return;
323
+ }
324
+ }
325
+ function isIPv4(hostname) {
326
+ const parts = hostname.split(".");
327
+ if (parts.length !== 4) return false;
328
+ return parts.every((part) => {
329
+ const num = Number(part);
330
+ return Number.isInteger(num) && num >= 0 && num <= 255 && String(num) === part;
331
+ });
332
+ }
333
+ function isPrivateIPv4(ip) {
334
+ const parts = ip.split(".").map(Number);
335
+ const [a, b, c] = parts;
336
+ if (a === 0) return true;
337
+ if (a === 10) return true;
338
+ if (a === 100 && b >= 64 && b <= 127) return true;
339
+ if (a === 127) return true;
340
+ if (a === 169 && b === 254) return true;
341
+ if (a === 172 && b >= 16 && b <= 31) return true;
342
+ if (a === 192 && b === 0 && c === 0) return true;
343
+ if (a === 192 && b === 168) return true;
344
+ if (a === 198 && (b === 18 || b === 19)) return true;
345
+ if (a >= 240) return true;
346
+ return false;
347
+ }
348
+ function parseIPv6(ip) {
349
+ let address = ip.toLowerCase();
350
+ const zoneIndex = address.indexOf("%");
351
+ if (zoneIndex !== -1) {
352
+ address = address.slice(0, zoneIndex);
353
+ }
354
+ const halves = address.split("::");
355
+ if (halves.length > 2) return null;
356
+ const toGroups = (segment) => {
357
+ if (segment === "") return [];
358
+ const groups = [];
359
+ const parts = segment.split(":");
360
+ for (let i = 0; i < parts.length; i++) {
361
+ const part = parts[i];
362
+ if (part.includes(".")) {
363
+ if (i !== parts.length - 1 || !isIPv4(part)) return null;
364
+ const [a, b, c, d] = part.split(".").map(Number);
365
+ groups.push(a << 8 | b, c << 8 | d);
366
+ continue;
367
+ }
368
+ if (!/^[0-9a-f]{1,4}$/.test(part)) return null;
369
+ groups.push(parseInt(part, 16));
370
+ }
371
+ return groups;
372
+ };
373
+ const head = toGroups(halves[0]);
374
+ if (head === null) return null;
375
+ if (halves.length === 2) {
376
+ const tail = toGroups(halves[1]);
377
+ if (tail === null) return null;
378
+ const fill = 8 - head.length - tail.length;
379
+ if (fill < 0) return null;
380
+ return [...head, ...new Array(fill).fill(0), ...tail];
381
+ }
382
+ return head.length === 8 ? head : null;
383
+ }
384
+ function isPrivateIPv6(ip) {
385
+ const groups = parseIPv6(ip);
386
+ if (groups === null) return true;
387
+ const topZero = (count) => groups.slice(0, count).every((group) => group === 0);
388
+ if (topZero(7) && (groups[7] === 0 || groups[7] === 1)) return true;
389
+ if ((groups[0] & 65024) === 64512) return true;
390
+ if ((groups[0] & 65472) === 65152) return true;
391
+ if ((groups[0] & 65472) === 65216) return true;
392
+ if ((groups[0] & 65280) === 65280) return true;
393
+ const embedsIPv4 = (
394
+ // ::/96 — IPv4-compatible (deprecated)
395
+ topZero(6) || // ::ffff:0:0/96 — IPv4-mapped (ffff in group 5)
396
+ topZero(5) && groups[5] === 65535 || // ::ffff:0:0/96 — IPv4-translated form (ffff in group 4, group 5 zero)
397
+ topZero(4) && groups[4] === 65535 && groups[5] === 0 || // 64:ff9b::/96 — NAT64 well-known prefix
398
+ groups[0] === 100 && groups[1] === 65435 && groups[2] === 0 && groups[3] === 0 && groups[4] === 0 && groups[5] === 0 || // 64:ff9b:1::/48 — NAT64 local-use prefix
399
+ groups[0] === 100 && groups[1] === 65435 && groups[2] === 1
400
+ );
401
+ if (embedsIPv4) {
402
+ const a = groups[6] >> 8 & 255;
403
+ const b = groups[6] & 255;
404
+ const c = groups[7] >> 8 & 255;
405
+ const d = groups[7] & 255;
406
+ return isPrivateIPv4(`${a}.${b}.${c}.${d}`);
407
+ }
408
+ return false;
409
+ }
410
+
411
+ // src/fetch-with-validated-redirects.ts
412
+ var MAX_DOWNLOAD_REDIRECTS = 10;
413
+ async function fetchWithValidatedRedirects({
414
+ url,
415
+ headers,
416
+ abortSignal,
417
+ maxRedirects = MAX_DOWNLOAD_REDIRECTS
418
+ }) {
419
+ const baseInit = { signal: abortSignal };
420
+ if (headers !== void 0) {
421
+ baseInit.headers = headers;
422
+ }
423
+ let currentUrl = url;
424
+ for (let redirectCount = 0; redirectCount <= maxRedirects; redirectCount++) {
425
+ validateDownloadUrl(currentUrl);
426
+ const response = await fetch(currentUrl, {
427
+ ...baseInit,
428
+ redirect: "manual"
429
+ });
430
+ if (response.type === "opaqueredirect") {
431
+ if (!isBrowserRuntime()) {
432
+ throw new DownloadError({
433
+ url,
434
+ message: `Redirect from ${currentUrl} could not be validated and was blocked`
435
+ });
436
+ }
437
+ return await fetch(currentUrl, { ...baseInit, redirect: "follow" });
438
+ }
439
+ const location = response.headers.get("location");
440
+ if (response.status >= 300 && response.status < 400 && location) {
441
+ await cancelResponseBody(response);
442
+ currentUrl = new URL(location, currentUrl).toString();
443
+ continue;
444
+ }
445
+ return response;
446
+ }
447
+ throw new DownloadError({
448
+ url,
449
+ message: `Too many redirects (max ${maxRedirects})`
450
+ });
451
+ }
452
+
254
453
  // src/read-response-with-size-limit.ts
255
454
  var DEFAULT_MAX_DOWNLOAD_SIZE = 2 * 1024 * 1024 * 1024;
256
455
  async function readResponseWithSizeLimit({
@@ -262,6 +461,7 @@ async function readResponseWithSizeLimit({
262
461
  if (contentLength != null) {
263
462
  const length = parseInt(contentLength, 10);
264
463
  if (!isNaN(length) && length > maxBytes) {
464
+ await cancelResponseBody(response);
265
465
  throw new DownloadError({
266
466
  url,
267
467
  message: `Download of ${url} exceeded maximum size of ${maxBytes} bytes (Content-Length: ${length}).`
@@ -440,7 +640,7 @@ function withUserAgentSuffix(headers, ...userAgentSuffixParts) {
440
640
  }
441
641
 
442
642
  // src/version.ts
443
- var VERSION = true ? "3.0.25" : "0.0.0-test";
643
+ var VERSION = true ? "3.0.27" : "0.0.0-test";
444
644
 
445
645
  // src/get-from-api.ts
446
646
  var getOriginalFetch = () => globalThis.fetch;
@@ -450,10 +650,10 @@ var getFromApi = async ({
450
650
  successfulResponseHandler,
451
651
  failedResponseHandler,
452
652
  abortSignal,
453
- fetch = getOriginalFetch()
653
+ fetch: fetch2 = getOriginalFetch()
454
654
  }) => {
455
655
  try {
456
- const response = await fetch(url, {
656
+ const response = await fetch2(url, {
457
657
  method: "GET",
458
658
  headers: withUserAgentSuffix(
459
659
  headers,
@@ -551,6 +751,15 @@ function injectJsonInstructionIntoMessages({
551
751
  ];
552
752
  }
553
753
 
754
+ // src/is-same-origin.ts
755
+ function isSameOrigin(url, baseUrl) {
756
+ try {
757
+ return new URL(url).origin === new URL(baseUrl).origin;
758
+ } catch (e) {
759
+ return false;
760
+ }
761
+ }
762
+
554
763
  // src/is-url-supported.ts
555
764
  function isUrlSupported({
556
765
  mediaType,
@@ -890,7 +1099,7 @@ var postJsonToApi = async ({
890
1099
  failedResponseHandler,
891
1100
  successfulResponseHandler,
892
1101
  abortSignal,
893
- fetch
1102
+ fetch: fetch2
894
1103
  }) => postToApi({
895
1104
  url,
896
1105
  headers: {
@@ -904,7 +1113,7 @@ var postJsonToApi = async ({
904
1113
  failedResponseHandler,
905
1114
  successfulResponseHandler,
906
1115
  abortSignal,
907
- fetch
1116
+ fetch: fetch2
908
1117
  });
909
1118
  var postFormDataToApi = async ({
910
1119
  url,
@@ -913,7 +1122,7 @@ var postFormDataToApi = async ({
913
1122
  failedResponseHandler,
914
1123
  successfulResponseHandler,
915
1124
  abortSignal,
916
- fetch
1125
+ fetch: fetch2
917
1126
  }) => postToApi({
918
1127
  url,
919
1128
  headers,
@@ -924,7 +1133,7 @@ var postFormDataToApi = async ({
924
1133
  failedResponseHandler,
925
1134
  successfulResponseHandler,
926
1135
  abortSignal,
927
- fetch
1136
+ fetch: fetch2
928
1137
  });
929
1138
  var postToApi = async ({
930
1139
  url,
@@ -933,10 +1142,10 @@ var postToApi = async ({
933
1142
  successfulResponseHandler,
934
1143
  failedResponseHandler,
935
1144
  abortSignal,
936
- fetch = getOriginalFetch2()
1145
+ fetch: fetch2 = getOriginalFetch2()
937
1146
  }) => {
938
1147
  try {
939
- const response = await fetch(url, {
1148
+ const response = await fetch2(url, {
940
1149
  method: "POST",
941
1150
  headers: withUserAgentSuffix(
942
1151
  headers,
@@ -2558,105 +2767,6 @@ function convertToBase64(value) {
2558
2767
  return value instanceof Uint8Array ? convertUint8ArrayToBase64(value) : value;
2559
2768
  }
2560
2769
 
2561
- // src/validate-download-url.ts
2562
- function validateDownloadUrl(url) {
2563
- let parsed;
2564
- try {
2565
- parsed = new URL(url);
2566
- } catch (e) {
2567
- throw new DownloadError({
2568
- url,
2569
- message: `Invalid URL: ${url}`
2570
- });
2571
- }
2572
- if (parsed.protocol === "data:") {
2573
- return;
2574
- }
2575
- if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
2576
- throw new DownloadError({
2577
- url,
2578
- message: `URL scheme must be http, https, or data, got ${parsed.protocol}`
2579
- });
2580
- }
2581
- const hostname = parsed.hostname;
2582
- if (!hostname) {
2583
- throw new DownloadError({
2584
- url,
2585
- message: `URL must have a hostname`
2586
- });
2587
- }
2588
- if (hostname === "localhost" || hostname.endsWith(".local") || hostname.endsWith(".localhost")) {
2589
- throw new DownloadError({
2590
- url,
2591
- message: `URL with hostname ${hostname} is not allowed`
2592
- });
2593
- }
2594
- if (hostname.startsWith("[") && hostname.endsWith("]")) {
2595
- const ipv6 = hostname.slice(1, -1);
2596
- if (isPrivateIPv6(ipv6)) {
2597
- throw new DownloadError({
2598
- url,
2599
- message: `URL with IPv6 address ${hostname} is not allowed`
2600
- });
2601
- }
2602
- return;
2603
- }
2604
- if (isIPv4(hostname)) {
2605
- if (isPrivateIPv4(hostname)) {
2606
- throw new DownloadError({
2607
- url,
2608
- message: `URL with IP address ${hostname} is not allowed`
2609
- });
2610
- }
2611
- return;
2612
- }
2613
- }
2614
- function isIPv4(hostname) {
2615
- const parts = hostname.split(".");
2616
- if (parts.length !== 4) return false;
2617
- return parts.every((part) => {
2618
- const num = Number(part);
2619
- return Number.isInteger(num) && num >= 0 && num <= 255 && String(num) === part;
2620
- });
2621
- }
2622
- function isPrivateIPv4(ip) {
2623
- const parts = ip.split(".").map(Number);
2624
- const [a, b] = parts;
2625
- if (a === 0) return true;
2626
- if (a === 10) return true;
2627
- if (a === 127) return true;
2628
- if (a === 169 && b === 254) return true;
2629
- if (a === 172 && b >= 16 && b <= 31) return true;
2630
- if (a === 192 && b === 168) return true;
2631
- return false;
2632
- }
2633
- function isPrivateIPv6(ip) {
2634
- const normalized = ip.toLowerCase();
2635
- if (normalized === "::1") return true;
2636
- if (normalized === "::") return true;
2637
- if (normalized.startsWith("::ffff:")) {
2638
- const mappedPart = normalized.slice(7);
2639
- if (isIPv4(mappedPart)) {
2640
- return isPrivateIPv4(mappedPart);
2641
- }
2642
- const hexParts = mappedPart.split(":");
2643
- if (hexParts.length === 2) {
2644
- const high = parseInt(hexParts[0], 16);
2645
- const low = parseInt(hexParts[1], 16);
2646
- if (!isNaN(high) && !isNaN(low)) {
2647
- const a = high >> 8 & 255;
2648
- const b = high & 255;
2649
- const c = low >> 8 & 255;
2650
- const d = low & 255;
2651
- return isPrivateIPv4(`${a}.${b}.${c}.${d}`);
2652
- }
2653
- }
2654
- }
2655
- if (normalized.startsWith("fc") || normalized.startsWith("fd")) return true;
2656
- if (normalized.startsWith("fe80")) return true;
2657
- return false;
2658
- }
2659
-
2660
2770
  // src/without-trailing-slash.ts
2661
2771
  function withoutTrailingSlash(url) {
2662
2772
  return url == null ? void 0 : url.replace(/\/$/, "");
@@ -2698,6 +2808,7 @@ var import_stream2 = require("eventsource-parser/stream");
2698
2808
  VERSION,
2699
2809
  asSchema,
2700
2810
  asValidator,
2811
+ cancelResponseBody,
2701
2812
  combineHeaders,
2702
2813
  convertAsyncIteratorToReadableStream,
2703
2814
  convertBase64ToUint8Array,
@@ -2716,13 +2827,16 @@ var import_stream2 = require("eventsource-parser/stream");
2716
2827
  dynamicTool,
2717
2828
  executeTool,
2718
2829
  extractResponseHeaders,
2830
+ fetchWithValidatedRedirects,
2719
2831
  generateId,
2720
2832
  getErrorMessage,
2721
2833
  getFromApi,
2722
2834
  getRuntimeEnvironmentUserAgent,
2723
2835
  injectJsonInstructionIntoMessages,
2724
2836
  isAbortError,
2837
+ isBrowserRuntime,
2725
2838
  isParsableJson,
2839
+ isSameOrigin,
2726
2840
  isUrlSupported,
2727
2841
  isValidator,
2728
2842
  jsonSchema,