@ai-sdk/provider-utils 3.0.24 → 3.0.26

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
@@ -56,13 +56,16 @@ __export(index_exports, {
56
56
  dynamicTool: () => dynamicTool,
57
57
  executeTool: () => executeTool,
58
58
  extractResponseHeaders: () => extractResponseHeaders,
59
+ fetchWithValidatedRedirects: () => fetchWithValidatedRedirects,
59
60
  generateId: () => generateId,
60
61
  getErrorMessage: () => getErrorMessage,
61
62
  getFromApi: () => getFromApi,
62
63
  getRuntimeEnvironmentUserAgent: () => getRuntimeEnvironmentUserAgent,
63
64
  injectJsonInstructionIntoMessages: () => injectJsonInstructionIntoMessages,
64
65
  isAbortError: () => isAbortError,
66
+ isBrowserRuntime: () => isBrowserRuntime,
65
67
  isParsableJson: () => isParsableJson,
68
+ isSameOrigin: () => isSameOrigin,
66
69
  isUrlSupported: () => isUrlSupported,
67
70
  isValidator: () => isValidator,
68
71
  jsonSchema: () => jsonSchema,
@@ -251,6 +254,191 @@ var DownloadError = class extends (_b = import_provider.AISDKError, _a = symbol,
251
254
  }
252
255
  };
253
256
 
257
+ // src/is-browser-runtime.ts
258
+ function isBrowserRuntime(globalThisAny = globalThis) {
259
+ return globalThisAny.window != null;
260
+ }
261
+
262
+ // src/validate-download-url.ts
263
+ function validateDownloadUrl(url) {
264
+ let parsed;
265
+ try {
266
+ parsed = new URL(url);
267
+ } catch (e) {
268
+ throw new DownloadError({
269
+ url,
270
+ message: `Invalid URL: ${url}`
271
+ });
272
+ }
273
+ if (parsed.protocol === "data:") {
274
+ return;
275
+ }
276
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
277
+ throw new DownloadError({
278
+ url,
279
+ message: `URL scheme must be http, https, or data, got ${parsed.protocol}`
280
+ });
281
+ }
282
+ const hostname = parsed.hostname.toLowerCase().replace(/\.+$/, "");
283
+ if (!hostname) {
284
+ throw new DownloadError({
285
+ url,
286
+ message: `URL must have a hostname`
287
+ });
288
+ }
289
+ if (hostname === "localhost" || hostname.endsWith(".local") || hostname.endsWith(".localhost")) {
290
+ throw new DownloadError({
291
+ url,
292
+ message: `URL with hostname ${hostname} is not allowed`
293
+ });
294
+ }
295
+ if (hostname.startsWith("[") && hostname.endsWith("]")) {
296
+ const ipv6 = hostname.slice(1, -1);
297
+ if (isPrivateIPv6(ipv6)) {
298
+ throw new DownloadError({
299
+ url,
300
+ message: `URL with IPv6 address ${hostname} is not allowed`
301
+ });
302
+ }
303
+ return;
304
+ }
305
+ if (isIPv4(hostname)) {
306
+ if (isPrivateIPv4(hostname)) {
307
+ throw new DownloadError({
308
+ url,
309
+ message: `URL with IP address ${hostname} is not allowed`
310
+ });
311
+ }
312
+ return;
313
+ }
314
+ }
315
+ function isIPv4(hostname) {
316
+ const parts = hostname.split(".");
317
+ if (parts.length !== 4) return false;
318
+ return parts.every((part) => {
319
+ const num = Number(part);
320
+ return Number.isInteger(num) && num >= 0 && num <= 255 && String(num) === part;
321
+ });
322
+ }
323
+ function isPrivateIPv4(ip) {
324
+ const parts = ip.split(".").map(Number);
325
+ const [a, b, c] = parts;
326
+ if (a === 0) return true;
327
+ if (a === 10) return true;
328
+ if (a === 100 && b >= 64 && b <= 127) return true;
329
+ if (a === 127) return true;
330
+ if (a === 169 && b === 254) return true;
331
+ if (a === 172 && b >= 16 && b <= 31) return true;
332
+ if (a === 192 && b === 0 && c === 0) return true;
333
+ if (a === 192 && b === 168) return true;
334
+ if (a === 198 && (b === 18 || b === 19)) return true;
335
+ if (a >= 240) return true;
336
+ return false;
337
+ }
338
+ function parseIPv6(ip) {
339
+ let address = ip.toLowerCase();
340
+ const zoneIndex = address.indexOf("%");
341
+ if (zoneIndex !== -1) {
342
+ address = address.slice(0, zoneIndex);
343
+ }
344
+ const halves = address.split("::");
345
+ if (halves.length > 2) return null;
346
+ const toGroups = (segment) => {
347
+ if (segment === "") return [];
348
+ const groups = [];
349
+ const parts = segment.split(":");
350
+ for (let i = 0; i < parts.length; i++) {
351
+ const part = parts[i];
352
+ if (part.includes(".")) {
353
+ if (i !== parts.length - 1 || !isIPv4(part)) return null;
354
+ const [a, b, c, d] = part.split(".").map(Number);
355
+ groups.push(a << 8 | b, c << 8 | d);
356
+ continue;
357
+ }
358
+ if (!/^[0-9a-f]{1,4}$/.test(part)) return null;
359
+ groups.push(parseInt(part, 16));
360
+ }
361
+ return groups;
362
+ };
363
+ const head = toGroups(halves[0]);
364
+ if (head === null) return null;
365
+ if (halves.length === 2) {
366
+ const tail = toGroups(halves[1]);
367
+ if (tail === null) return null;
368
+ const fill = 8 - head.length - tail.length;
369
+ if (fill < 0) return null;
370
+ return [...head, ...new Array(fill).fill(0), ...tail];
371
+ }
372
+ return head.length === 8 ? head : null;
373
+ }
374
+ function isPrivateIPv6(ip) {
375
+ const groups = parseIPv6(ip);
376
+ if (groups === null) return true;
377
+ const topZero = (count) => groups.slice(0, count).every((group) => group === 0);
378
+ if (topZero(7) && (groups[7] === 0 || groups[7] === 1)) return true;
379
+ if ((groups[0] & 65024) === 64512) return true;
380
+ if ((groups[0] & 65472) === 65152) return true;
381
+ if ((groups[0] & 65472) === 65216) return true;
382
+ if ((groups[0] & 65280) === 65280) return true;
383
+ const embedsIPv4 = (
384
+ // ::/96 — IPv4-compatible (deprecated)
385
+ topZero(6) || // ::ffff:0:0/96 — IPv4-mapped (ffff in group 5)
386
+ topZero(5) && groups[5] === 65535 || // ::ffff:0:0/96 — IPv4-translated form (ffff in group 4, group 5 zero)
387
+ topZero(4) && groups[4] === 65535 && groups[5] === 0 || // 64:ff9b::/96 — NAT64 well-known prefix
388
+ 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
389
+ groups[0] === 100 && groups[1] === 65435 && groups[2] === 1
390
+ );
391
+ if (embedsIPv4) {
392
+ const a = groups[6] >> 8 & 255;
393
+ const b = groups[6] & 255;
394
+ const c = groups[7] >> 8 & 255;
395
+ const d = groups[7] & 255;
396
+ return isPrivateIPv4(`${a}.${b}.${c}.${d}`);
397
+ }
398
+ return false;
399
+ }
400
+
401
+ // src/fetch-with-validated-redirects.ts
402
+ var MAX_DOWNLOAD_REDIRECTS = 10;
403
+ async function fetchWithValidatedRedirects({
404
+ url,
405
+ headers,
406
+ abortSignal,
407
+ maxRedirects = MAX_DOWNLOAD_REDIRECTS
408
+ }) {
409
+ const baseInit = { signal: abortSignal };
410
+ if (headers !== void 0) {
411
+ baseInit.headers = headers;
412
+ }
413
+ let currentUrl = url;
414
+ for (let redirectCount = 0; redirectCount <= maxRedirects; redirectCount++) {
415
+ validateDownloadUrl(currentUrl);
416
+ const response = await fetch(currentUrl, {
417
+ ...baseInit,
418
+ redirect: "manual"
419
+ });
420
+ if (response.type === "opaqueredirect") {
421
+ if (!isBrowserRuntime()) {
422
+ throw new DownloadError({
423
+ url,
424
+ message: `Redirect from ${currentUrl} could not be validated and was blocked`
425
+ });
426
+ }
427
+ return await fetch(currentUrl, { ...baseInit, redirect: "follow" });
428
+ }
429
+ const location = response.headers.get("location");
430
+ if (response.status >= 300 && response.status < 400 && location) {
431
+ currentUrl = new URL(location, currentUrl).toString();
432
+ continue;
433
+ }
434
+ return response;
435
+ }
436
+ throw new DownloadError({
437
+ url,
438
+ message: `Too many redirects (max ${maxRedirects})`
439
+ });
440
+ }
441
+
254
442
  // src/read-response-with-size-limit.ts
255
443
  var DEFAULT_MAX_DOWNLOAD_SIZE = 2 * 1024 * 1024 * 1024;
256
444
  async function readResponseWithSizeLimit({
@@ -440,7 +628,7 @@ function withUserAgentSuffix(headers, ...userAgentSuffixParts) {
440
628
  }
441
629
 
442
630
  // src/version.ts
443
- var VERSION = true ? "3.0.24" : "0.0.0-test";
631
+ var VERSION = true ? "3.0.26" : "0.0.0-test";
444
632
 
445
633
  // src/get-from-api.ts
446
634
  var getOriginalFetch = () => globalThis.fetch;
@@ -450,10 +638,10 @@ var getFromApi = async ({
450
638
  successfulResponseHandler,
451
639
  failedResponseHandler,
452
640
  abortSignal,
453
- fetch = getOriginalFetch()
641
+ fetch: fetch2 = getOriginalFetch()
454
642
  }) => {
455
643
  try {
456
- const response = await fetch(url, {
644
+ const response = await fetch2(url, {
457
645
  method: "GET",
458
646
  headers: withUserAgentSuffix(
459
647
  headers,
@@ -551,6 +739,15 @@ function injectJsonInstructionIntoMessages({
551
739
  ];
552
740
  }
553
741
 
742
+ // src/is-same-origin.ts
743
+ function isSameOrigin(url, baseUrl) {
744
+ try {
745
+ return new URL(url).origin === new URL(baseUrl).origin;
746
+ } catch (e) {
747
+ return false;
748
+ }
749
+ }
750
+
554
751
  // src/is-url-supported.ts
555
752
  function isUrlSupported({
556
753
  mediaType,
@@ -890,7 +1087,7 @@ var postJsonToApi = async ({
890
1087
  failedResponseHandler,
891
1088
  successfulResponseHandler,
892
1089
  abortSignal,
893
- fetch
1090
+ fetch: fetch2
894
1091
  }) => postToApi({
895
1092
  url,
896
1093
  headers: {
@@ -904,7 +1101,7 @@ var postJsonToApi = async ({
904
1101
  failedResponseHandler,
905
1102
  successfulResponseHandler,
906
1103
  abortSignal,
907
- fetch
1104
+ fetch: fetch2
908
1105
  });
909
1106
  var postFormDataToApi = async ({
910
1107
  url,
@@ -913,7 +1110,7 @@ var postFormDataToApi = async ({
913
1110
  failedResponseHandler,
914
1111
  successfulResponseHandler,
915
1112
  abortSignal,
916
- fetch
1113
+ fetch: fetch2
917
1114
  }) => postToApi({
918
1115
  url,
919
1116
  headers,
@@ -924,7 +1121,7 @@ var postFormDataToApi = async ({
924
1121
  failedResponseHandler,
925
1122
  successfulResponseHandler,
926
1123
  abortSignal,
927
- fetch
1124
+ fetch: fetch2
928
1125
  });
929
1126
  var postToApi = async ({
930
1127
  url,
@@ -933,10 +1130,10 @@ var postToApi = async ({
933
1130
  successfulResponseHandler,
934
1131
  failedResponseHandler,
935
1132
  abortSignal,
936
- fetch = getOriginalFetch2()
1133
+ fetch: fetch2 = getOriginalFetch2()
937
1134
  }) => {
938
1135
  try {
939
- const response = await fetch(url, {
1136
+ const response = await fetch2(url, {
940
1137
  method: "POST",
941
1138
  headers: withUserAgentSuffix(
942
1139
  headers,
@@ -1241,6 +1438,35 @@ var createStatusCodeErrorResponseHandler = () => async ({ response, url, request
1241
1438
  };
1242
1439
  };
1243
1440
 
1441
+ // src/schema.ts
1442
+ var schemaSymbol = /* @__PURE__ */ Symbol.for("vercel.ai.schema");
1443
+ function lazySchema(createSchema) {
1444
+ let schema;
1445
+ return () => {
1446
+ if (schema == null) {
1447
+ schema = createSchema();
1448
+ }
1449
+ return schema;
1450
+ };
1451
+ }
1452
+ function jsonSchema(jsonSchema2, {
1453
+ validate
1454
+ } = {}) {
1455
+ return {
1456
+ [schemaSymbol]: true,
1457
+ _type: void 0,
1458
+ // should never be used directly
1459
+ [validatorSymbol]: true,
1460
+ get jsonSchema() {
1461
+ if (typeof jsonSchema2 === "function") {
1462
+ jsonSchema2 = jsonSchema2();
1463
+ }
1464
+ return jsonSchema2;
1465
+ },
1466
+ validate
1467
+ };
1468
+ }
1469
+
1244
1470
  // src/zod-schema.ts
1245
1471
  var z4 = __toESM(require("zod/v4"));
1246
1472
 
@@ -1271,15 +1497,6 @@ function addAdditionalPropertiesToJsonSchema(jsonSchema2) {
1271
1497
  return jsonSchema2;
1272
1498
  }
1273
1499
 
1274
- // src/zod-to-json-schema/get-relative-path.ts
1275
- var getRelativePath = (pathA, pathB) => {
1276
- let i = 0;
1277
- for (; i < pathA.length && i < pathB.length; i++) {
1278
- if (pathA[i] !== pathB[i]) break;
1279
- }
1280
- return [(pathA.length - i).toString(), ...pathB.slice(i)].join("/");
1281
- };
1282
-
1283
1500
  // src/zod-to-json-schema/options.ts
1284
1501
  var ignoreOverride = /* @__PURE__ */ Symbol(
1285
1502
  "Let zodToJsonSchema decide on which parser to use"
@@ -2311,6 +2528,15 @@ var selectParser = (def, typeName, refs) => {
2311
2528
  }
2312
2529
  };
2313
2530
 
2531
+ // src/zod-to-json-schema/get-relative-path.ts
2532
+ var getRelativePath = (pathA, pathB) => {
2533
+ let i = 0;
2534
+ for (; i < pathA.length && i < pathB.length; i++) {
2535
+ if (pathA[i] !== pathB[i]) break;
2536
+ }
2537
+ return [(pathA.length - i).toString(), ...pathB.slice(i)].join("/");
2538
+ };
2539
+
2314
2540
  // src/zod-to-json-schema/parse-def.ts
2315
2541
  function parseDef(def, refs, forceResolution = false) {
2316
2542
  var _a2;
@@ -2500,34 +2726,7 @@ function zodSchema(zodSchema2, options) {
2500
2726
  }
2501
2727
  }
2502
2728
 
2503
- // src/schema.ts
2504
- var schemaSymbol = /* @__PURE__ */ Symbol.for("vercel.ai.schema");
2505
- function lazySchema(createSchema) {
2506
- let schema;
2507
- return () => {
2508
- if (schema == null) {
2509
- schema = createSchema();
2510
- }
2511
- return schema;
2512
- };
2513
- }
2514
- function jsonSchema(jsonSchema2, {
2515
- validate
2516
- } = {}) {
2517
- return {
2518
- [schemaSymbol]: true,
2519
- _type: void 0,
2520
- // should never be used directly
2521
- [validatorSymbol]: true,
2522
- get jsonSchema() {
2523
- if (typeof jsonSchema2 === "function") {
2524
- jsonSchema2 = jsonSchema2();
2525
- }
2526
- return jsonSchema2;
2527
- },
2528
- validate
2529
- };
2530
- }
2729
+ // src/as-schema.ts
2531
2730
  function isSchema(value) {
2532
2731
  return typeof value === "object" && value !== null && schemaSymbol in value && value[schemaSymbol] === true && "jsonSchema" in value && "validate" in value;
2533
2732
  }
@@ -2556,105 +2755,6 @@ function convertToBase64(value) {
2556
2755
  return value instanceof Uint8Array ? convertUint8ArrayToBase64(value) : value;
2557
2756
  }
2558
2757
 
2559
- // src/validate-download-url.ts
2560
- function validateDownloadUrl(url) {
2561
- let parsed;
2562
- try {
2563
- parsed = new URL(url);
2564
- } catch (e) {
2565
- throw new DownloadError({
2566
- url,
2567
- message: `Invalid URL: ${url}`
2568
- });
2569
- }
2570
- if (parsed.protocol === "data:") {
2571
- return;
2572
- }
2573
- if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
2574
- throw new DownloadError({
2575
- url,
2576
- message: `URL scheme must be http, https, or data, got ${parsed.protocol}`
2577
- });
2578
- }
2579
- const hostname = parsed.hostname;
2580
- if (!hostname) {
2581
- throw new DownloadError({
2582
- url,
2583
- message: `URL must have a hostname`
2584
- });
2585
- }
2586
- if (hostname === "localhost" || hostname.endsWith(".local") || hostname.endsWith(".localhost")) {
2587
- throw new DownloadError({
2588
- url,
2589
- message: `URL with hostname ${hostname} is not allowed`
2590
- });
2591
- }
2592
- if (hostname.startsWith("[") && hostname.endsWith("]")) {
2593
- const ipv6 = hostname.slice(1, -1);
2594
- if (isPrivateIPv6(ipv6)) {
2595
- throw new DownloadError({
2596
- url,
2597
- message: `URL with IPv6 address ${hostname} is not allowed`
2598
- });
2599
- }
2600
- return;
2601
- }
2602
- if (isIPv4(hostname)) {
2603
- if (isPrivateIPv4(hostname)) {
2604
- throw new DownloadError({
2605
- url,
2606
- message: `URL with IP address ${hostname} is not allowed`
2607
- });
2608
- }
2609
- return;
2610
- }
2611
- }
2612
- function isIPv4(hostname) {
2613
- const parts = hostname.split(".");
2614
- if (parts.length !== 4) return false;
2615
- return parts.every((part) => {
2616
- const num = Number(part);
2617
- return Number.isInteger(num) && num >= 0 && num <= 255 && String(num) === part;
2618
- });
2619
- }
2620
- function isPrivateIPv4(ip) {
2621
- const parts = ip.split(".").map(Number);
2622
- const [a, b] = parts;
2623
- if (a === 0) return true;
2624
- if (a === 10) return true;
2625
- if (a === 127) return true;
2626
- if (a === 169 && b === 254) return true;
2627
- if (a === 172 && b >= 16 && b <= 31) return true;
2628
- if (a === 192 && b === 168) return true;
2629
- return false;
2630
- }
2631
- function isPrivateIPv6(ip) {
2632
- const normalized = ip.toLowerCase();
2633
- if (normalized === "::1") return true;
2634
- if (normalized === "::") return true;
2635
- if (normalized.startsWith("::ffff:")) {
2636
- const mappedPart = normalized.slice(7);
2637
- if (isIPv4(mappedPart)) {
2638
- return isPrivateIPv4(mappedPart);
2639
- }
2640
- const hexParts = mappedPart.split(":");
2641
- if (hexParts.length === 2) {
2642
- const high = parseInt(hexParts[0], 16);
2643
- const low = parseInt(hexParts[1], 16);
2644
- if (!isNaN(high) && !isNaN(low)) {
2645
- const a = high >> 8 & 255;
2646
- const b = high & 255;
2647
- const c = low >> 8 & 255;
2648
- const d = low & 255;
2649
- return isPrivateIPv4(`${a}.${b}.${c}.${d}`);
2650
- }
2651
- }
2652
- }
2653
- if (normalized.startsWith("fc") || normalized.startsWith("fd")) return true;
2654
- if (normalized.startsWith("fe80")) return true;
2655
- return false;
2656
- }
2657
-
2658
2758
  // src/without-trailing-slash.ts
2659
2759
  function withoutTrailingSlash(url) {
2660
2760
  return url == null ? void 0 : url.replace(/\/$/, "");
@@ -2714,13 +2814,16 @@ var import_stream2 = require("eventsource-parser/stream");
2714
2814
  dynamicTool,
2715
2815
  executeTool,
2716
2816
  extractResponseHeaders,
2817
+ fetchWithValidatedRedirects,
2717
2818
  generateId,
2718
2819
  getErrorMessage,
2719
2820
  getFromApi,
2720
2821
  getRuntimeEnvironmentUserAgent,
2721
2822
  injectJsonInstructionIntoMessages,
2722
2823
  isAbortError,
2824
+ isBrowserRuntime,
2723
2825
  isParsableJson,
2826
+ isSameOrigin,
2724
2827
  isUrlSupported,
2725
2828
  isValidator,
2726
2829
  jsonSchema,