@ai-sdk/provider-utils 4.0.27 → 4.0.29

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
@@ -233,59 +233,9 @@ var DownloadError = class extends (_b = AISDKError, _a = symbol, _b) {
233
233
  }
234
234
  };
235
235
 
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;
236
+ // src/is-browser-runtime.ts
237
+ function isBrowserRuntime(globalThisAny = globalThis) {
238
+ return globalThisAny.window != null;
289
239
  }
290
240
 
291
241
  // src/validate-download-url.ts
@@ -308,7 +258,7 @@ function validateDownloadUrl(url) {
308
258
  message: `URL scheme must be http, https, or data, got ${parsed.protocol}`
309
259
  });
310
260
  }
311
- const hostname = parsed.hostname;
261
+ const hostname = parsed.hostname.toLowerCase().replace(/\.+$/, "");
312
262
  if (!hostname) {
313
263
  throw new DownloadError({
314
264
  url,
@@ -351,53 +301,186 @@ function isIPv4(hostname) {
351
301
  }
352
302
  function isPrivateIPv4(ip) {
353
303
  const parts = ip.split(".").map(Number);
354
- const [a, b] = parts;
304
+ const [a, b, c] = parts;
355
305
  if (a === 0) return true;
356
306
  if (a === 10) return true;
307
+ if (a === 100 && b >= 64 && b <= 127) return true;
357
308
  if (a === 127) return true;
358
309
  if (a === 169 && b === 254) return true;
359
310
  if (a === 172 && b >= 16 && b <= 31) return true;
311
+ if (a === 192 && b === 0 && c === 0) return true;
360
312
  if (a === 192 && b === 168) return true;
313
+ if (a === 198 && (b === 18 || b === 19)) return true;
314
+ if (a >= 240) return true;
361
315
  return false;
362
316
  }
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}`);
317
+ function parseIPv6(ip) {
318
+ let address = ip.toLowerCase();
319
+ const zoneIndex = address.indexOf("%");
320
+ if (zoneIndex !== -1) {
321
+ address = address.slice(0, zoneIndex);
322
+ }
323
+ const halves = address.split("::");
324
+ if (halves.length > 2) return null;
325
+ const toGroups = (segment) => {
326
+ if (segment === "") return [];
327
+ const groups = [];
328
+ const parts = segment.split(":");
329
+ for (let i = 0; i < parts.length; i++) {
330
+ const part = parts[i];
331
+ if (part.includes(".")) {
332
+ if (i !== parts.length - 1 || !isIPv4(part)) return null;
333
+ const [a, b, c, d] = part.split(".").map(Number);
334
+ groups.push(a << 8 | b, c << 8 | d);
335
+ continue;
382
336
  }
337
+ if (!/^[0-9a-f]{1,4}$/.test(part)) return null;
338
+ groups.push(parseInt(part, 16));
383
339
  }
340
+ return groups;
341
+ };
342
+ const head = toGroups(halves[0]);
343
+ if (head === null) return null;
344
+ if (halves.length === 2) {
345
+ const tail = toGroups(halves[1]);
346
+ if (tail === null) return null;
347
+ const fill = 8 - head.length - tail.length;
348
+ if (fill < 0) return null;
349
+ return [...head, ...new Array(fill).fill(0), ...tail];
350
+ }
351
+ return head.length === 8 ? head : null;
352
+ }
353
+ function isPrivateIPv6(ip) {
354
+ const groups = parseIPv6(ip);
355
+ if (groups === null) return true;
356
+ const topZero = (count) => groups.slice(0, count).every((group) => group === 0);
357
+ if (topZero(7) && (groups[7] === 0 || groups[7] === 1)) return true;
358
+ if ((groups[0] & 65024) === 64512) return true;
359
+ if ((groups[0] & 65472) === 65152) return true;
360
+ if ((groups[0] & 65472) === 65216) return true;
361
+ if ((groups[0] & 65280) === 65280) return true;
362
+ const embedsIPv4 = (
363
+ // ::/96 — IPv4-compatible (deprecated)
364
+ topZero(6) || // ::ffff:0:0/96 — IPv4-mapped (ffff in group 5)
365
+ topZero(5) && groups[5] === 65535 || // ::ffff:0:0/96 — IPv4-translated form (ffff in group 4, group 5 zero)
366
+ topZero(4) && groups[4] === 65535 && groups[5] === 0 || // 64:ff9b::/96 — NAT64 well-known prefix
367
+ 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
368
+ groups[0] === 100 && groups[1] === 65435 && groups[2] === 1
369
+ );
370
+ if (embedsIPv4) {
371
+ const a = groups[6] >> 8 & 255;
372
+ const b = groups[6] & 255;
373
+ const c = groups[7] >> 8 & 255;
374
+ const d = groups[7] & 255;
375
+ return isPrivateIPv4(`${a}.${b}.${c}.${d}`);
384
376
  }
385
- if (normalized.startsWith("fc") || normalized.startsWith("fd")) return true;
386
- if (normalized.startsWith("fe80")) return true;
387
377
  return false;
388
378
  }
389
379
 
380
+ // src/fetch-with-validated-redirects.ts
381
+ var MAX_DOWNLOAD_REDIRECTS = 10;
382
+ async function fetchWithValidatedRedirects({
383
+ url,
384
+ headers,
385
+ abortSignal,
386
+ maxRedirects = MAX_DOWNLOAD_REDIRECTS
387
+ }) {
388
+ const baseInit = { signal: abortSignal };
389
+ if (headers !== void 0) {
390
+ baseInit.headers = headers;
391
+ }
392
+ let currentUrl = url;
393
+ for (let redirectCount = 0; redirectCount <= maxRedirects; redirectCount++) {
394
+ validateDownloadUrl(currentUrl);
395
+ const response = await fetch(currentUrl, {
396
+ ...baseInit,
397
+ redirect: "manual"
398
+ });
399
+ if (response.type === "opaqueredirect") {
400
+ if (!isBrowserRuntime()) {
401
+ throw new DownloadError({
402
+ url,
403
+ message: `Redirect from ${currentUrl} could not be validated and was blocked`
404
+ });
405
+ }
406
+ return await fetch(currentUrl, { ...baseInit, redirect: "follow" });
407
+ }
408
+ const location = response.headers.get("location");
409
+ if (response.status >= 300 && response.status < 400 && location) {
410
+ currentUrl = new URL(location, currentUrl).toString();
411
+ continue;
412
+ }
413
+ return response;
414
+ }
415
+ throw new DownloadError({
416
+ url,
417
+ message: `Too many redirects (max ${maxRedirects})`
418
+ });
419
+ }
420
+
421
+ // src/read-response-with-size-limit.ts
422
+ var DEFAULT_MAX_DOWNLOAD_SIZE = 2 * 1024 * 1024 * 1024;
423
+ async function readResponseWithSizeLimit({
424
+ response,
425
+ url,
426
+ maxBytes = DEFAULT_MAX_DOWNLOAD_SIZE
427
+ }) {
428
+ const contentLength = response.headers.get("content-length");
429
+ if (contentLength != null) {
430
+ const length = parseInt(contentLength, 10);
431
+ if (!isNaN(length) && length > maxBytes) {
432
+ throw new DownloadError({
433
+ url,
434
+ message: `Download of ${url} exceeded maximum size of ${maxBytes} bytes (Content-Length: ${length}).`
435
+ });
436
+ }
437
+ }
438
+ const body = response.body;
439
+ if (body == null) {
440
+ return new Uint8Array(0);
441
+ }
442
+ const reader = body.getReader();
443
+ const chunks = [];
444
+ let totalBytes = 0;
445
+ try {
446
+ while (true) {
447
+ const { done, value } = await reader.read();
448
+ if (done) {
449
+ break;
450
+ }
451
+ totalBytes += value.length;
452
+ if (totalBytes > maxBytes) {
453
+ throw new DownloadError({
454
+ url,
455
+ message: `Download of ${url} exceeded maximum size of ${maxBytes} bytes.`
456
+ });
457
+ }
458
+ chunks.push(value);
459
+ }
460
+ } finally {
461
+ try {
462
+ await reader.cancel();
463
+ } finally {
464
+ reader.releaseLock();
465
+ }
466
+ }
467
+ const result = new Uint8Array(totalBytes);
468
+ let offset = 0;
469
+ for (const chunk of chunks) {
470
+ result.set(chunk, offset);
471
+ offset += chunk.length;
472
+ }
473
+ return result;
474
+ }
475
+
390
476
  // src/download-blob.ts
391
477
  async function downloadBlob(url, options) {
392
478
  var _a2, _b2;
393
- validateDownloadUrl(url);
394
479
  try {
395
- const response = await fetch(url, {
396
- signal: options == null ? void 0 : options.abortSignal
480
+ const response = await fetchWithValidatedRedirects({
481
+ url,
482
+ abortSignal: options == null ? void 0 : options.abortSignal
397
483
  });
398
- if (response.redirected) {
399
- validateDownloadUrl(response.url);
400
- }
401
484
  if (!response.ok) {
402
485
  throw new DownloadError({
403
486
  url,
@@ -582,7 +665,7 @@ function withUserAgentSuffix(headers, ...userAgentSuffixParts) {
582
665
  }
583
666
 
584
667
  // src/version.ts
585
- var VERSION = true ? "4.0.27" : "0.0.0-test";
668
+ var VERSION = true ? "4.0.29" : "0.0.0-test";
586
669
 
587
670
  // src/get-from-api.ts
588
671
  var getOriginalFetch = () => globalThis.fetch;
@@ -698,6 +781,15 @@ function isNonNullable(value) {
698
781
  return value != null;
699
782
  }
700
783
 
784
+ // src/is-same-origin.ts
785
+ function isSameOrigin(url, baseUrl) {
786
+ try {
787
+ return new URL(url).origin === new URL(baseUrl).origin;
788
+ } catch (e) {
789
+ return false;
790
+ }
791
+ }
792
+
701
793
  // src/is-url-supported.ts
702
794
  function isUrlSupported({
703
795
  mediaType,
@@ -2726,14 +2818,17 @@ export {
2726
2818
  dynamicTool,
2727
2819
  executeTool,
2728
2820
  extractResponseHeaders,
2821
+ fetchWithValidatedRedirects,
2729
2822
  generateId,
2730
2823
  getErrorMessage,
2731
2824
  getFromApi,
2732
2825
  getRuntimeEnvironmentUserAgent,
2733
2826
  injectJsonInstructionIntoMessages,
2734
2827
  isAbortError,
2828
+ isBrowserRuntime,
2735
2829
  isNonNullable,
2736
2830
  isParsableJson,
2831
+ isSameOrigin,
2737
2832
  isUrlSupported,
2738
2833
  jsonSchema,
2739
2834
  lazySchema,