@ai-sdk/provider-utils 5.0.0-canary.47 → 5.0.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.
- package/CHANGELOG.md +218 -0
- package/dist/index.d.ts +73 -1
- package/dist/index.js +191 -83
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/cancel-response-body.ts +19 -0
- package/src/download-blob.ts +8 -9
- package/src/fetch-with-validated-redirects.ts +87 -0
- package/src/index.ts +4 -0
- package/src/is-browser-runtime.ts +13 -0
- package/src/is-same-origin.ts +19 -0
- package/src/read-response-with-size-limit.ts +4 -0
- package/src/validate-download-url.ts +113 -31
package/dist/index.js
CHANGED
|
@@ -489,6 +489,15 @@ function isFullMediaType(mediaType) {
|
|
|
489
489
|
return subtype.length > 0 && subtype !== "*";
|
|
490
490
|
}
|
|
491
491
|
|
|
492
|
+
// src/cancel-response-body.ts
|
|
493
|
+
async function cancelResponseBody(response) {
|
|
494
|
+
var _a2;
|
|
495
|
+
try {
|
|
496
|
+
await ((_a2 = response.body) == null ? void 0 : _a2.cancel());
|
|
497
|
+
} catch (e) {
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
492
501
|
// src/download-error.ts
|
|
493
502
|
import { AISDKError } from "@ai-sdk/provider";
|
|
494
503
|
var name = "AI_DownloadError";
|
|
@@ -514,59 +523,9 @@ var DownloadError = class extends (_b = AISDKError, _a = symbol, _b) {
|
|
|
514
523
|
}
|
|
515
524
|
};
|
|
516
525
|
|
|
517
|
-
// src/
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
response,
|
|
521
|
-
url,
|
|
522
|
-
maxBytes = DEFAULT_MAX_DOWNLOAD_SIZE
|
|
523
|
-
}) {
|
|
524
|
-
const contentLength = response.headers.get("content-length");
|
|
525
|
-
if (contentLength != null) {
|
|
526
|
-
const length = parseInt(contentLength, 10);
|
|
527
|
-
if (!isNaN(length) && length > maxBytes) {
|
|
528
|
-
throw new DownloadError({
|
|
529
|
-
url,
|
|
530
|
-
message: `Download of ${url} exceeded maximum size of ${maxBytes} bytes (Content-Length: ${length}).`
|
|
531
|
-
});
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
const body = response.body;
|
|
535
|
-
if (body == null) {
|
|
536
|
-
return new Uint8Array(0);
|
|
537
|
-
}
|
|
538
|
-
const reader = body.getReader();
|
|
539
|
-
const chunks = [];
|
|
540
|
-
let totalBytes = 0;
|
|
541
|
-
try {
|
|
542
|
-
while (true) {
|
|
543
|
-
const { done, value } = await reader.read();
|
|
544
|
-
if (done) {
|
|
545
|
-
break;
|
|
546
|
-
}
|
|
547
|
-
totalBytes += value.length;
|
|
548
|
-
if (totalBytes > maxBytes) {
|
|
549
|
-
throw new DownloadError({
|
|
550
|
-
url,
|
|
551
|
-
message: `Download of ${url} exceeded maximum size of ${maxBytes} bytes.`
|
|
552
|
-
});
|
|
553
|
-
}
|
|
554
|
-
chunks.push(value);
|
|
555
|
-
}
|
|
556
|
-
} finally {
|
|
557
|
-
try {
|
|
558
|
-
await reader.cancel();
|
|
559
|
-
} finally {
|
|
560
|
-
reader.releaseLock();
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
const result = new Uint8Array(totalBytes);
|
|
564
|
-
let offset = 0;
|
|
565
|
-
for (const chunk of chunks) {
|
|
566
|
-
result.set(chunk, offset);
|
|
567
|
-
offset += chunk.length;
|
|
568
|
-
}
|
|
569
|
-
return result;
|
|
526
|
+
// src/is-browser-runtime.ts
|
|
527
|
+
function isBrowserRuntime(globalThisAny = globalThis) {
|
|
528
|
+
return globalThisAny.window != null;
|
|
570
529
|
}
|
|
571
530
|
|
|
572
531
|
// src/validate-download-url.ts
|
|
@@ -589,7 +548,7 @@ function validateDownloadUrl(url) {
|
|
|
589
548
|
message: `URL scheme must be http, https, or data, got ${parsed.protocol}`
|
|
590
549
|
});
|
|
591
550
|
}
|
|
592
|
-
const hostname = parsed.hostname;
|
|
551
|
+
const hostname = parsed.hostname.toLowerCase().replace(/\.+$/, "");
|
|
593
552
|
if (!hostname) {
|
|
594
553
|
throw new DownloadError({
|
|
595
554
|
url,
|
|
@@ -632,54 +591,190 @@ function isIPv4(hostname) {
|
|
|
632
591
|
}
|
|
633
592
|
function isPrivateIPv4(ip) {
|
|
634
593
|
const parts = ip.split(".").map(Number);
|
|
635
|
-
const [a, b] = parts;
|
|
594
|
+
const [a, b, c] = parts;
|
|
636
595
|
if (a === 0) return true;
|
|
637
596
|
if (a === 10) return true;
|
|
597
|
+
if (a === 100 && b >= 64 && b <= 127) return true;
|
|
638
598
|
if (a === 127) return true;
|
|
639
599
|
if (a === 169 && b === 254) return true;
|
|
640
600
|
if (a === 172 && b >= 16 && b <= 31) return true;
|
|
601
|
+
if (a === 192 && b === 0 && c === 0) return true;
|
|
641
602
|
if (a === 192 && b === 168) return true;
|
|
603
|
+
if (a === 198 && (b === 18 || b === 19)) return true;
|
|
604
|
+
if (a >= 240) return true;
|
|
642
605
|
return false;
|
|
643
606
|
}
|
|
644
|
-
function
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
if (
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
const
|
|
661
|
-
|
|
662
|
-
|
|
607
|
+
function parseIPv6(ip) {
|
|
608
|
+
let address = ip.toLowerCase();
|
|
609
|
+
const zoneIndex = address.indexOf("%");
|
|
610
|
+
if (zoneIndex !== -1) {
|
|
611
|
+
address = address.slice(0, zoneIndex);
|
|
612
|
+
}
|
|
613
|
+
const halves = address.split("::");
|
|
614
|
+
if (halves.length > 2) return null;
|
|
615
|
+
const toGroups = (segment) => {
|
|
616
|
+
if (segment === "") return [];
|
|
617
|
+
const groups = [];
|
|
618
|
+
const parts = segment.split(":");
|
|
619
|
+
for (let i = 0; i < parts.length; i++) {
|
|
620
|
+
const part = parts[i];
|
|
621
|
+
if (part.includes(".")) {
|
|
622
|
+
if (i !== parts.length - 1 || !isIPv4(part)) return null;
|
|
623
|
+
const [a, b, c, d] = part.split(".").map(Number);
|
|
624
|
+
groups.push(a << 8 | b, c << 8 | d);
|
|
625
|
+
continue;
|
|
663
626
|
}
|
|
627
|
+
if (!/^[0-9a-f]{1,4}$/.test(part)) return null;
|
|
628
|
+
groups.push(parseInt(part, 16));
|
|
664
629
|
}
|
|
630
|
+
return groups;
|
|
631
|
+
};
|
|
632
|
+
const head = toGroups(halves[0]);
|
|
633
|
+
if (head === null) return null;
|
|
634
|
+
if (halves.length === 2) {
|
|
635
|
+
const tail = toGroups(halves[1]);
|
|
636
|
+
if (tail === null) return null;
|
|
637
|
+
const fill = 8 - head.length - tail.length;
|
|
638
|
+
if (fill < 0) return null;
|
|
639
|
+
return [...head, ...new Array(fill).fill(0), ...tail];
|
|
640
|
+
}
|
|
641
|
+
return head.length === 8 ? head : null;
|
|
642
|
+
}
|
|
643
|
+
function isPrivateIPv6(ip) {
|
|
644
|
+
const groups = parseIPv6(ip);
|
|
645
|
+
if (groups === null) return true;
|
|
646
|
+
const topZero = (count) => groups.slice(0, count).every((group) => group === 0);
|
|
647
|
+
if (topZero(7) && (groups[7] === 0 || groups[7] === 1)) return true;
|
|
648
|
+
if ((groups[0] & 65024) === 64512) return true;
|
|
649
|
+
if ((groups[0] & 65472) === 65152) return true;
|
|
650
|
+
if ((groups[0] & 65472) === 65216) return true;
|
|
651
|
+
if ((groups[0] & 65280) === 65280) return true;
|
|
652
|
+
const embedsIPv4 = (
|
|
653
|
+
// ::/96 — IPv4-compatible (deprecated)
|
|
654
|
+
topZero(6) || // ::ffff:0:0/96 — IPv4-mapped (ffff in group 5)
|
|
655
|
+
topZero(5) && groups[5] === 65535 || // ::ffff:0:0/96 — IPv4-translated form (ffff in group 4, group 5 zero)
|
|
656
|
+
topZero(4) && groups[4] === 65535 && groups[5] === 0 || // 64:ff9b::/96 — NAT64 well-known prefix
|
|
657
|
+
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
|
|
658
|
+
groups[0] === 100 && groups[1] === 65435 && groups[2] === 1
|
|
659
|
+
);
|
|
660
|
+
if (embedsIPv4) {
|
|
661
|
+
const a = groups[6] >> 8 & 255;
|
|
662
|
+
const b = groups[6] & 255;
|
|
663
|
+
const c = groups[7] >> 8 & 255;
|
|
664
|
+
const d = groups[7] & 255;
|
|
665
|
+
return isPrivateIPv4(`${a}.${b}.${c}.${d}`);
|
|
665
666
|
}
|
|
666
|
-
if (normalized.startsWith("fc") || normalized.startsWith("fd")) return true;
|
|
667
|
-
if (normalized.startsWith("fe80")) return true;
|
|
668
667
|
return false;
|
|
669
668
|
}
|
|
670
669
|
|
|
670
|
+
// src/fetch-with-validated-redirects.ts
|
|
671
|
+
var MAX_DOWNLOAD_REDIRECTS = 10;
|
|
672
|
+
async function fetchWithValidatedRedirects({
|
|
673
|
+
url,
|
|
674
|
+
headers,
|
|
675
|
+
abortSignal,
|
|
676
|
+
maxRedirects = MAX_DOWNLOAD_REDIRECTS
|
|
677
|
+
}) {
|
|
678
|
+
const baseInit = { signal: abortSignal };
|
|
679
|
+
if (headers !== void 0) {
|
|
680
|
+
baseInit.headers = headers;
|
|
681
|
+
}
|
|
682
|
+
let currentUrl = url;
|
|
683
|
+
for (let redirectCount = 0; redirectCount <= maxRedirects; redirectCount++) {
|
|
684
|
+
validateDownloadUrl(currentUrl);
|
|
685
|
+
const response = await fetch(currentUrl, {
|
|
686
|
+
...baseInit,
|
|
687
|
+
redirect: "manual"
|
|
688
|
+
});
|
|
689
|
+
if (response.type === "opaqueredirect") {
|
|
690
|
+
if (!isBrowserRuntime()) {
|
|
691
|
+
throw new DownloadError({
|
|
692
|
+
url,
|
|
693
|
+
message: `Redirect from ${currentUrl} could not be validated and was blocked`
|
|
694
|
+
});
|
|
695
|
+
}
|
|
696
|
+
return await fetch(currentUrl, { ...baseInit, redirect: "follow" });
|
|
697
|
+
}
|
|
698
|
+
const location = response.headers.get("location");
|
|
699
|
+
if (response.status >= 300 && response.status < 400 && location) {
|
|
700
|
+
await cancelResponseBody(response);
|
|
701
|
+
currentUrl = new URL(location, currentUrl).toString();
|
|
702
|
+
continue;
|
|
703
|
+
}
|
|
704
|
+
return response;
|
|
705
|
+
}
|
|
706
|
+
throw new DownloadError({
|
|
707
|
+
url,
|
|
708
|
+
message: `Too many redirects (max ${maxRedirects})`
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
// src/read-response-with-size-limit.ts
|
|
713
|
+
var DEFAULT_MAX_DOWNLOAD_SIZE = 2 * 1024 * 1024 * 1024;
|
|
714
|
+
async function readResponseWithSizeLimit({
|
|
715
|
+
response,
|
|
716
|
+
url,
|
|
717
|
+
maxBytes = DEFAULT_MAX_DOWNLOAD_SIZE
|
|
718
|
+
}) {
|
|
719
|
+
const contentLength = response.headers.get("content-length");
|
|
720
|
+
if (contentLength != null) {
|
|
721
|
+
const length = parseInt(contentLength, 10);
|
|
722
|
+
if (!isNaN(length) && length > maxBytes) {
|
|
723
|
+
await cancelResponseBody(response);
|
|
724
|
+
throw new DownloadError({
|
|
725
|
+
url,
|
|
726
|
+
message: `Download of ${url} exceeded maximum size of ${maxBytes} bytes (Content-Length: ${length}).`
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
const body = response.body;
|
|
731
|
+
if (body == null) {
|
|
732
|
+
return new Uint8Array(0);
|
|
733
|
+
}
|
|
734
|
+
const reader = body.getReader();
|
|
735
|
+
const chunks = [];
|
|
736
|
+
let totalBytes = 0;
|
|
737
|
+
try {
|
|
738
|
+
while (true) {
|
|
739
|
+
const { done, value } = await reader.read();
|
|
740
|
+
if (done) {
|
|
741
|
+
break;
|
|
742
|
+
}
|
|
743
|
+
totalBytes += value.length;
|
|
744
|
+
if (totalBytes > maxBytes) {
|
|
745
|
+
throw new DownloadError({
|
|
746
|
+
url,
|
|
747
|
+
message: `Download of ${url} exceeded maximum size of ${maxBytes} bytes.`
|
|
748
|
+
});
|
|
749
|
+
}
|
|
750
|
+
chunks.push(value);
|
|
751
|
+
}
|
|
752
|
+
} finally {
|
|
753
|
+
try {
|
|
754
|
+
await reader.cancel();
|
|
755
|
+
} finally {
|
|
756
|
+
reader.releaseLock();
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
const result = new Uint8Array(totalBytes);
|
|
760
|
+
let offset = 0;
|
|
761
|
+
for (const chunk of chunks) {
|
|
762
|
+
result.set(chunk, offset);
|
|
763
|
+
offset += chunk.length;
|
|
764
|
+
}
|
|
765
|
+
return result;
|
|
766
|
+
}
|
|
767
|
+
|
|
671
768
|
// src/download-blob.ts
|
|
672
769
|
async function downloadBlob(url, options) {
|
|
673
770
|
var _a2, _b2;
|
|
674
|
-
validateDownloadUrl(url);
|
|
675
771
|
try {
|
|
676
|
-
const response = await
|
|
677
|
-
|
|
772
|
+
const response = await fetchWithValidatedRedirects({
|
|
773
|
+
url,
|
|
774
|
+
abortSignal: options == null ? void 0 : options.abortSignal
|
|
678
775
|
});
|
|
679
|
-
if (response.redirected) {
|
|
680
|
-
validateDownloadUrl(response.url);
|
|
681
|
-
}
|
|
682
776
|
if (!response.ok) {
|
|
777
|
+
await cancelResponseBody(response);
|
|
683
778
|
throw new DownloadError({
|
|
684
779
|
url,
|
|
685
780
|
statusCode: response.status,
|
|
@@ -876,7 +971,7 @@ function withUserAgentSuffix(headers, ...userAgentSuffixParts) {
|
|
|
876
971
|
}
|
|
877
972
|
|
|
878
973
|
// src/version.ts
|
|
879
|
-
var VERSION = true ? "5.0.0
|
|
974
|
+
var VERSION = true ? "5.0.0" : "0.0.0-test";
|
|
880
975
|
|
|
881
976
|
// src/get-from-api.ts
|
|
882
977
|
var getOriginalFetch = () => globalThis.fetch;
|
|
@@ -993,6 +1088,15 @@ function isBuffer(value) {
|
|
|
993
1088
|
return (_b2 = (_a2 = globalThis.Buffer) == null ? void 0 : _a2.isBuffer(value)) != null ? _b2 : false;
|
|
994
1089
|
}
|
|
995
1090
|
|
|
1091
|
+
// src/is-same-origin.ts
|
|
1092
|
+
function isSameOrigin(url, baseUrl) {
|
|
1093
|
+
try {
|
|
1094
|
+
return new URL(url).origin === new URL(baseUrl).origin;
|
|
1095
|
+
} catch (e) {
|
|
1096
|
+
return false;
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
|
|
996
1100
|
// src/is-non-nullable.ts
|
|
997
1101
|
function isNonNullable(value) {
|
|
998
1102
|
return value != null;
|
|
@@ -3342,6 +3446,7 @@ export {
|
|
|
3342
3446
|
WORKFLOW_SERIALIZE,
|
|
3343
3447
|
asArray,
|
|
3344
3448
|
asSchema,
|
|
3449
|
+
cancelResponseBody,
|
|
3345
3450
|
combineHeaders,
|
|
3346
3451
|
convertAsyncIteratorToReadableStream,
|
|
3347
3452
|
convertBase64ToUint8Array,
|
|
@@ -3367,6 +3472,7 @@ export {
|
|
|
3367
3472
|
executeTool,
|
|
3368
3473
|
extractLines,
|
|
3369
3474
|
extractResponseHeaders,
|
|
3475
|
+
fetchWithValidatedRedirects,
|
|
3370
3476
|
filterNullable,
|
|
3371
3477
|
generateId,
|
|
3372
3478
|
getErrorMessage,
|
|
@@ -3375,6 +3481,7 @@ export {
|
|
|
3375
3481
|
getTopLevelMediaType,
|
|
3376
3482
|
injectJsonInstructionIntoMessages,
|
|
3377
3483
|
isAbortError,
|
|
3484
|
+
isBrowserRuntime,
|
|
3378
3485
|
isBuffer,
|
|
3379
3486
|
isCustomReasoning,
|
|
3380
3487
|
isExecutableTool,
|
|
@@ -3382,6 +3489,7 @@ export {
|
|
|
3382
3489
|
isNonNullable,
|
|
3383
3490
|
isParsableJson,
|
|
3384
3491
|
isProviderReference,
|
|
3492
|
+
isSameOrigin,
|
|
3385
3493
|
isUrlSupported,
|
|
3386
3494
|
jsonSchema,
|
|
3387
3495
|
lazySchema,
|