@ai-sdk/provider-utils 4.0.28 → 4.0.30

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.mjs CHANGED
@@ -208,6 +208,15 @@ function convertToFormData(input, options = {}) {
208
208
  return formData;
209
209
  }
210
210
 
211
+ // src/cancel-response-body.ts
212
+ async function cancelResponseBody(response) {
213
+ var _a2;
214
+ try {
215
+ await ((_a2 = response.body) == null ? void 0 : _a2.cancel());
216
+ } catch (e) {
217
+ }
218
+ }
219
+
211
220
  // src/download-error.ts
212
221
  import { AISDKError } from "@ai-sdk/provider";
213
222
  var name = "AI_DownloadError";
@@ -233,59 +242,9 @@ var DownloadError = class extends (_b = AISDKError, _a = symbol, _b) {
233
242
  }
234
243
  };
235
244
 
236
- // src/read-response-with-size-limit.ts
237
- var DEFAULT_MAX_DOWNLOAD_SIZE = 2 * 1024 * 1024 * 1024;
238
- async function readResponseWithSizeLimit({
239
- response,
240
- url,
241
- maxBytes = DEFAULT_MAX_DOWNLOAD_SIZE
242
- }) {
243
- const contentLength = response.headers.get("content-length");
244
- if (contentLength != null) {
245
- const length = parseInt(contentLength, 10);
246
- if (!isNaN(length) && length > maxBytes) {
247
- throw new DownloadError({
248
- url,
249
- message: `Download of ${url} exceeded maximum size of ${maxBytes} bytes (Content-Length: ${length}).`
250
- });
251
- }
252
- }
253
- const body = response.body;
254
- if (body == null) {
255
- return new Uint8Array(0);
256
- }
257
- const reader = body.getReader();
258
- const chunks = [];
259
- let totalBytes = 0;
260
- try {
261
- while (true) {
262
- const { done, value } = await reader.read();
263
- if (done) {
264
- break;
265
- }
266
- totalBytes += value.length;
267
- if (totalBytes > maxBytes) {
268
- throw new DownloadError({
269
- url,
270
- message: `Download of ${url} exceeded maximum size of ${maxBytes} bytes.`
271
- });
272
- }
273
- chunks.push(value);
274
- }
275
- } finally {
276
- try {
277
- await reader.cancel();
278
- } finally {
279
- reader.releaseLock();
280
- }
281
- }
282
- const result = new Uint8Array(totalBytes);
283
- let offset = 0;
284
- for (const chunk of chunks) {
285
- result.set(chunk, offset);
286
- offset += chunk.length;
287
- }
288
- return result;
245
+ // src/is-browser-runtime.ts
246
+ function isBrowserRuntime(globalThisAny = globalThis) {
247
+ return globalThisAny.window != null;
289
248
  }
290
249
 
291
250
  // src/validate-download-url.ts
@@ -308,7 +267,7 @@ function validateDownloadUrl(url) {
308
267
  message: `URL scheme must be http, https, or data, got ${parsed.protocol}`
309
268
  });
310
269
  }
311
- const hostname = parsed.hostname;
270
+ const hostname = parsed.hostname.toLowerCase().replace(/\.+$/, "");
312
271
  if (!hostname) {
313
272
  throw new DownloadError({
314
273
  url,
@@ -351,54 +310,190 @@ function isIPv4(hostname) {
351
310
  }
352
311
  function isPrivateIPv4(ip) {
353
312
  const parts = ip.split(".").map(Number);
354
- const [a, b] = parts;
313
+ const [a, b, c] = parts;
355
314
  if (a === 0) return true;
356
315
  if (a === 10) return true;
316
+ if (a === 100 && b >= 64 && b <= 127) return true;
357
317
  if (a === 127) return true;
358
318
  if (a === 169 && b === 254) return true;
359
319
  if (a === 172 && b >= 16 && b <= 31) return true;
320
+ if (a === 192 && b === 0 && c === 0) return true;
360
321
  if (a === 192 && b === 168) return true;
322
+ if (a === 198 && (b === 18 || b === 19)) return true;
323
+ if (a >= 240) return true;
361
324
  return false;
362
325
  }
363
- function isPrivateIPv6(ip) {
364
- const normalized = ip.toLowerCase();
365
- if (normalized === "::1") return true;
366
- if (normalized === "::") return true;
367
- if (normalized.startsWith("::ffff:")) {
368
- const mappedPart = normalized.slice(7);
369
- if (isIPv4(mappedPart)) {
370
- return isPrivateIPv4(mappedPart);
371
- }
372
- const hexParts = mappedPart.split(":");
373
- if (hexParts.length === 2) {
374
- const high = parseInt(hexParts[0], 16);
375
- const low = parseInt(hexParts[1], 16);
376
- if (!isNaN(high) && !isNaN(low)) {
377
- const a = high >> 8 & 255;
378
- const b = high & 255;
379
- const c = low >> 8 & 255;
380
- const d = low & 255;
381
- return isPrivateIPv4(`${a}.${b}.${c}.${d}`);
326
+ function parseIPv6(ip) {
327
+ let address = ip.toLowerCase();
328
+ const zoneIndex = address.indexOf("%");
329
+ if (zoneIndex !== -1) {
330
+ address = address.slice(0, zoneIndex);
331
+ }
332
+ const halves = address.split("::");
333
+ if (halves.length > 2) return null;
334
+ const toGroups = (segment) => {
335
+ if (segment === "") return [];
336
+ const groups = [];
337
+ const parts = segment.split(":");
338
+ for (let i = 0; i < parts.length; i++) {
339
+ const part = parts[i];
340
+ if (part.includes(".")) {
341
+ if (i !== parts.length - 1 || !isIPv4(part)) return null;
342
+ const [a, b, c, d] = part.split(".").map(Number);
343
+ groups.push(a << 8 | b, c << 8 | d);
344
+ continue;
382
345
  }
346
+ if (!/^[0-9a-f]{1,4}$/.test(part)) return null;
347
+ groups.push(parseInt(part, 16));
383
348
  }
349
+ return groups;
350
+ };
351
+ const head = toGroups(halves[0]);
352
+ if (head === null) return null;
353
+ if (halves.length === 2) {
354
+ const tail = toGroups(halves[1]);
355
+ if (tail === null) return null;
356
+ const fill = 8 - head.length - tail.length;
357
+ if (fill < 0) return null;
358
+ return [...head, ...new Array(fill).fill(0), ...tail];
359
+ }
360
+ return head.length === 8 ? head : null;
361
+ }
362
+ function isPrivateIPv6(ip) {
363
+ const groups = parseIPv6(ip);
364
+ if (groups === null) return true;
365
+ const topZero = (count) => groups.slice(0, count).every((group) => group === 0);
366
+ if (topZero(7) && (groups[7] === 0 || groups[7] === 1)) return true;
367
+ if ((groups[0] & 65024) === 64512) return true;
368
+ if ((groups[0] & 65472) === 65152) return true;
369
+ if ((groups[0] & 65472) === 65216) return true;
370
+ if ((groups[0] & 65280) === 65280) return true;
371
+ const embedsIPv4 = (
372
+ // ::/96 — IPv4-compatible (deprecated)
373
+ topZero(6) || // ::ffff:0:0/96 — IPv4-mapped (ffff in group 5)
374
+ topZero(5) && groups[5] === 65535 || // ::ffff:0:0/96 — IPv4-translated form (ffff in group 4, group 5 zero)
375
+ topZero(4) && groups[4] === 65535 && groups[5] === 0 || // 64:ff9b::/96 — NAT64 well-known prefix
376
+ 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
377
+ groups[0] === 100 && groups[1] === 65435 && groups[2] === 1
378
+ );
379
+ if (embedsIPv4) {
380
+ const a = groups[6] >> 8 & 255;
381
+ const b = groups[6] & 255;
382
+ const c = groups[7] >> 8 & 255;
383
+ const d = groups[7] & 255;
384
+ return isPrivateIPv4(`${a}.${b}.${c}.${d}`);
384
385
  }
385
- if (normalized.startsWith("fc") || normalized.startsWith("fd")) return true;
386
- if (normalized.startsWith("fe80")) return true;
387
386
  return false;
388
387
  }
389
388
 
389
+ // src/fetch-with-validated-redirects.ts
390
+ var MAX_DOWNLOAD_REDIRECTS = 10;
391
+ async function fetchWithValidatedRedirects({
392
+ url,
393
+ headers,
394
+ abortSignal,
395
+ maxRedirects = MAX_DOWNLOAD_REDIRECTS
396
+ }) {
397
+ const baseInit = { signal: abortSignal };
398
+ if (headers !== void 0) {
399
+ baseInit.headers = headers;
400
+ }
401
+ let currentUrl = url;
402
+ for (let redirectCount = 0; redirectCount <= maxRedirects; redirectCount++) {
403
+ validateDownloadUrl(currentUrl);
404
+ const response = await fetch(currentUrl, {
405
+ ...baseInit,
406
+ redirect: "manual"
407
+ });
408
+ if (response.type === "opaqueredirect") {
409
+ if (!isBrowserRuntime()) {
410
+ throw new DownloadError({
411
+ url,
412
+ message: `Redirect from ${currentUrl} could not be validated and was blocked`
413
+ });
414
+ }
415
+ return await fetch(currentUrl, { ...baseInit, redirect: "follow" });
416
+ }
417
+ const location = response.headers.get("location");
418
+ if (response.status >= 300 && response.status < 400 && location) {
419
+ await cancelResponseBody(response);
420
+ currentUrl = new URL(location, currentUrl).toString();
421
+ continue;
422
+ }
423
+ return response;
424
+ }
425
+ throw new DownloadError({
426
+ url,
427
+ message: `Too many redirects (max ${maxRedirects})`
428
+ });
429
+ }
430
+
431
+ // src/read-response-with-size-limit.ts
432
+ var DEFAULT_MAX_DOWNLOAD_SIZE = 2 * 1024 * 1024 * 1024;
433
+ async function readResponseWithSizeLimit({
434
+ response,
435
+ url,
436
+ maxBytes = DEFAULT_MAX_DOWNLOAD_SIZE
437
+ }) {
438
+ const contentLength = response.headers.get("content-length");
439
+ if (contentLength != null) {
440
+ const length = parseInt(contentLength, 10);
441
+ if (!isNaN(length) && length > maxBytes) {
442
+ await cancelResponseBody(response);
443
+ throw new DownloadError({
444
+ url,
445
+ message: `Download of ${url} exceeded maximum size of ${maxBytes} bytes (Content-Length: ${length}).`
446
+ });
447
+ }
448
+ }
449
+ const body = response.body;
450
+ if (body == null) {
451
+ return new Uint8Array(0);
452
+ }
453
+ const reader = body.getReader();
454
+ const chunks = [];
455
+ let totalBytes = 0;
456
+ try {
457
+ while (true) {
458
+ const { done, value } = await reader.read();
459
+ if (done) {
460
+ break;
461
+ }
462
+ totalBytes += value.length;
463
+ if (totalBytes > maxBytes) {
464
+ throw new DownloadError({
465
+ url,
466
+ message: `Download of ${url} exceeded maximum size of ${maxBytes} bytes.`
467
+ });
468
+ }
469
+ chunks.push(value);
470
+ }
471
+ } finally {
472
+ try {
473
+ await reader.cancel();
474
+ } finally {
475
+ reader.releaseLock();
476
+ }
477
+ }
478
+ const result = new Uint8Array(totalBytes);
479
+ let offset = 0;
480
+ for (const chunk of chunks) {
481
+ result.set(chunk, offset);
482
+ offset += chunk.length;
483
+ }
484
+ return result;
485
+ }
486
+
390
487
  // src/download-blob.ts
391
488
  async function downloadBlob(url, options) {
392
489
  var _a2, _b2;
393
- validateDownloadUrl(url);
394
490
  try {
395
- const response = await fetch(url, {
396
- signal: options == null ? void 0 : options.abortSignal
491
+ const response = await fetchWithValidatedRedirects({
492
+ url,
493
+ abortSignal: options == null ? void 0 : options.abortSignal
397
494
  });
398
- if (response.redirected) {
399
- validateDownloadUrl(response.url);
400
- }
401
495
  if (!response.ok) {
496
+ await cancelResponseBody(response);
402
497
  throw new DownloadError({
403
498
  url,
404
499
  statusCode: response.status,
@@ -582,7 +677,7 @@ function withUserAgentSuffix(headers, ...userAgentSuffixParts) {
582
677
  }
583
678
 
584
679
  // src/version.ts
585
- var VERSION = true ? "4.0.28" : "0.0.0-test";
680
+ var VERSION = true ? "4.0.30" : "0.0.0-test";
586
681
 
587
682
  // src/get-from-api.ts
588
683
  var getOriginalFetch = () => globalThis.fetch;
@@ -698,6 +793,15 @@ function isNonNullable(value) {
698
793
  return value != null;
699
794
  }
700
795
 
796
+ // src/is-same-origin.ts
797
+ function isSameOrigin(url, baseUrl) {
798
+ try {
799
+ return new URL(url).origin === new URL(baseUrl).origin;
800
+ } catch (e) {
801
+ return false;
802
+ }
803
+ }
804
+
701
805
  // src/is-url-supported.ts
702
806
  function isUrlSupported({
703
807
  mediaType,
@@ -2705,6 +2809,7 @@ export {
2705
2809
  EventSourceParserStream2 as EventSourceParserStream,
2706
2810
  VERSION,
2707
2811
  asSchema,
2812
+ cancelResponseBody,
2708
2813
  combineHeaders,
2709
2814
  convertAsyncIteratorToReadableStream,
2710
2815
  convertBase64ToUint8Array,
@@ -2726,14 +2831,17 @@ export {
2726
2831
  dynamicTool,
2727
2832
  executeTool,
2728
2833
  extractResponseHeaders,
2834
+ fetchWithValidatedRedirects,
2729
2835
  generateId,
2730
2836
  getErrorMessage,
2731
2837
  getFromApi,
2732
2838
  getRuntimeEnvironmentUserAgent,
2733
2839
  injectJsonInstructionIntoMessages,
2734
2840
  isAbortError,
2841
+ isBrowserRuntime,
2735
2842
  isNonNullable,
2736
2843
  isParsableJson,
2844
+ isSameOrigin,
2737
2845
  isUrlSupported,
2738
2846
  jsonSchema,
2739
2847
  lazySchema,