@ai-sdk/provider-utils 5.0.0-beta.4 → 5.0.0-beta.49
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 +373 -11
- package/dist/index.d.ts +1460 -553
- package/dist/index.js +1044 -361
- package/dist/index.js.map +1 -1
- package/dist/test/index.d.ts +2 -1
- package/dist/test/index.js +18 -37
- package/dist/test/index.js.map +1 -1
- package/package.json +16 -16
- package/src/add-additional-properties-to-json-schema.ts +1 -1
- package/src/as-array.ts +12 -0
- package/src/cancel-response-body.ts +19 -0
- package/src/convert-image-model-file-to-data-uri.ts +1 -1
- package/src/convert-inline-file-data-to-uint8-array.ts +30 -0
- package/src/create-tool-name-mapping.ts +1 -1
- package/src/detect-media-type.ts +312 -0
- package/src/download-blob.ts +8 -9
- package/src/extract-lines.ts +31 -0
- package/src/fetch-with-validated-redirects.ts +87 -0
- package/src/filter-nullable.ts +11 -0
- package/src/get-error-message.ts +1 -15
- package/src/get-from-api.ts +2 -2
- package/src/has-required-key.ts +6 -0
- package/src/index.ts +47 -12
- package/src/inject-json-instruction.ts +1 -1
- package/src/is-browser-runtime.ts +13 -0
- package/src/is-buffer.ts +9 -0
- package/src/is-json-serializable.ts +29 -0
- package/src/is-provider-reference.ts +21 -0
- package/src/is-same-origin.ts +19 -0
- package/src/is-url-supported.ts +17 -2
- package/src/load-api-key.ts +1 -1
- package/src/load-setting.ts +1 -1
- package/src/map-reasoning-to-provider.ts +108 -0
- package/src/maybe-promise-like.ts +3 -0
- package/src/parse-json-event-stream.ts +3 -3
- package/src/parse-json.ts +3 -3
- package/src/parse-provider-options.ts +1 -1
- package/src/post-to-api.ts +4 -4
- package/src/provider-defined-tool-factory.ts +129 -0
- package/src/provider-executed-tool-factory.ts +69 -0
- package/src/read-response-with-size-limit.ts +4 -0
- package/src/resolve-full-media-type.ts +49 -0
- package/src/resolve-provider-reference.ts +26 -0
- package/src/resolve.ts +16 -1
- package/src/response-handler.ts +3 -3
- package/src/schema.ts +11 -4
- package/src/secure-json-parse.ts +1 -1
- package/src/serialize-model-options.ts +63 -0
- package/src/streaming-tool-call-tracker.ts +241 -0
- package/src/test/convert-response-stream-to-array.ts +1 -1
- package/src/test/is-node-version.ts +22 -1
- package/src/to-json-schema/zod3-to-json-schema/options.ts +3 -3
- package/src/to-json-schema/zod3-to-json-schema/parse-def.ts +3 -3
- package/src/to-json-schema/zod3-to-json-schema/parse-types.ts +22 -22
- package/src/to-json-schema/zod3-to-json-schema/parsers/array.ts +3 -3
- package/src/to-json-schema/zod3-to-json-schema/parsers/bigint.ts +1 -1
- package/src/to-json-schema/zod3-to-json-schema/parsers/branded.ts +2 -2
- package/src/to-json-schema/zod3-to-json-schema/parsers/catch.ts +2 -2
- package/src/to-json-schema/zod3-to-json-schema/parsers/date.ts +4 -4
- package/src/to-json-schema/zod3-to-json-schema/parsers/default.ts +3 -3
- package/src/to-json-schema/zod3-to-json-schema/parsers/effects.ts +3 -3
- package/src/to-json-schema/zod3-to-json-schema/parsers/enum.ts +1 -1
- package/src/to-json-schema/zod3-to-json-schema/parsers/intersection.ts +5 -5
- package/src/to-json-schema/zod3-to-json-schema/parsers/literal.ts +1 -1
- package/src/to-json-schema/zod3-to-json-schema/parsers/map.ts +4 -5
- package/src/to-json-schema/zod3-to-json-schema/parsers/native-enum.ts +1 -1
- package/src/to-json-schema/zod3-to-json-schema/parsers/never.ts +1 -2
- package/src/to-json-schema/zod3-to-json-schema/parsers/nullable.ts +4 -4
- package/src/to-json-schema/zod3-to-json-schema/parsers/number.ts +1 -1
- package/src/to-json-schema/zod3-to-json-schema/parsers/object.ts +3 -3
- package/src/to-json-schema/zod3-to-json-schema/parsers/optional.ts +3 -3
- package/src/to-json-schema/zod3-to-json-schema/parsers/pipeline.ts +10 -8
- package/src/to-json-schema/zod3-to-json-schema/parsers/promise.ts +3 -3
- package/src/to-json-schema/zod3-to-json-schema/parsers/readonly.ts +2 -2
- package/src/to-json-schema/zod3-to-json-schema/parsers/record.ts +9 -10
- package/src/to-json-schema/zod3-to-json-schema/parsers/set.ts +3 -3
- package/src/to-json-schema/zod3-to-json-schema/parsers/string.ts +2 -2
- package/src/to-json-schema/zod3-to-json-schema/parsers/tuple.ts +3 -3
- package/src/to-json-schema/zod3-to-json-schema/parsers/undefined.ts +1 -2
- package/src/to-json-schema/zod3-to-json-schema/parsers/union.ts +3 -3
- package/src/to-json-schema/zod3-to-json-schema/parsers/unknown.ts +1 -2
- package/src/to-json-schema/zod3-to-json-schema/refs.ts +3 -3
- package/src/to-json-schema/zod3-to-json-schema/select-parser.ts +2 -2
- package/src/to-json-schema/zod3-to-json-schema/zod3-to-json-schema.ts +3 -3
- package/src/types/assistant-model-message.ts +5 -3
- package/src/types/content-part.ts +158 -19
- package/src/types/context.ts +4 -0
- package/src/types/executable-tool.ts +17 -0
- package/src/types/execute-tool.ts +29 -9
- package/src/types/file-data.ts +48 -0
- package/src/types/index.ts +29 -11
- package/src/types/infer-tool-context.ts +41 -0
- package/src/types/infer-tool-input.ts +7 -0
- package/src/types/infer-tool-output.ts +7 -0
- package/src/types/infer-tool-set-context.ts +44 -0
- package/src/types/model-message.ts +4 -4
- package/src/types/never-optional.ts +7 -0
- package/src/types/provider-options.ts +1 -1
- package/src/types/provider-reference.ts +10 -0
- package/src/types/sandbox.ts +217 -0
- package/src/types/system-model-message.ts +1 -1
- package/src/types/tool-approval-request.ts +13 -0
- package/src/types/tool-execute-function.ts +56 -0
- package/src/types/tool-model-message.ts +3 -3
- package/src/types/tool-needs-approval-function.ts +39 -0
- package/src/types/tool-set.ts +22 -0
- package/src/types/tool.ts +278 -225
- package/src/types/user-model-message.ts +2 -2
- package/src/validate-download-url.ts +120 -33
- package/src/validate-types.ts +5 -3
- package/dist/index.d.mts +0 -1455
- package/dist/index.mjs +0 -2754
- package/dist/index.mjs.map +0 -1
- package/dist/test/index.d.mts +0 -17
- package/dist/test/index.mjs +0 -77
- package/dist/test/index.mjs.map +0 -1
- package/src/provider-tool-factory.ts +0 -125
|
@@ -4,6 +4,10 @@ import { DownloadError } from './download-error';
|
|
|
4
4
|
* Validates that a URL is safe to download from, blocking private/internal addresses
|
|
5
5
|
* to prevent SSRF attacks.
|
|
6
6
|
*
|
|
7
|
+
* Note: this performs string/literal-IP checks only. It does not resolve DNS, so a
|
|
8
|
+
* hostname that resolves to a private address is not blocked here (see callers, which
|
|
9
|
+
* should additionally constrain egress at the network layer when handling untrusted URLs).
|
|
10
|
+
*
|
|
7
11
|
* @param url - The URL string to validate.
|
|
8
12
|
* @throws DownloadError if the URL is unsafe.
|
|
9
13
|
*/
|
|
@@ -18,15 +22,22 @@ export function validateDownloadUrl(url: string): void {
|
|
|
18
22
|
});
|
|
19
23
|
}
|
|
20
24
|
|
|
21
|
-
//
|
|
25
|
+
// data: URLs are inline content, so they do not trigger a network fetch or SSRF risk.
|
|
26
|
+
if (parsed.protocol === 'data:') {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Only allow http and https network protocols
|
|
22
31
|
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
|
|
23
32
|
throw new DownloadError({
|
|
24
33
|
url,
|
|
25
|
-
message: `URL scheme must be http or
|
|
34
|
+
message: `URL scheme must be http, https, or data, got ${parsed.protocol}`,
|
|
26
35
|
});
|
|
27
36
|
}
|
|
28
37
|
|
|
29
|
-
|
|
38
|
+
// Strip a trailing dot so a fully-qualified name like `localhost.` (which resolves
|
|
39
|
+
// identically to `localhost`) cannot bypass the hostname blocklist below.
|
|
40
|
+
const hostname = parsed.hostname.toLowerCase().replace(/\.+$/, '');
|
|
30
41
|
|
|
31
42
|
// Block empty hostname
|
|
32
43
|
if (!hostname) {
|
|
@@ -85,59 +96,135 @@ function isIPv4(hostname: string): boolean {
|
|
|
85
96
|
|
|
86
97
|
function isPrivateIPv4(ip: string): boolean {
|
|
87
98
|
const parts = ip.split('.').map(Number);
|
|
88
|
-
const [a, b] = parts;
|
|
99
|
+
const [a, b, c] = parts;
|
|
89
100
|
|
|
90
101
|
// 0.0.0.0/8
|
|
91
102
|
if (a === 0) return true;
|
|
92
103
|
// 10.0.0.0/8
|
|
93
104
|
if (a === 10) return true;
|
|
105
|
+
// 100.64.0.0/10 (CGNAT, used by some cloud providers for internal traffic)
|
|
106
|
+
if (a === 100 && b >= 64 && b <= 127) return true;
|
|
94
107
|
// 127.0.0.0/8
|
|
95
108
|
if (a === 127) return true;
|
|
96
109
|
// 169.254.0.0/16
|
|
97
110
|
if (a === 169 && b === 254) return true;
|
|
98
111
|
// 172.16.0.0/12
|
|
99
112
|
if (a === 172 && b >= 16 && b <= 31) return true;
|
|
113
|
+
// 192.0.0.0/24 (IETF protocol assignments)
|
|
114
|
+
if (a === 192 && b === 0 && c === 0) return true;
|
|
100
115
|
// 192.168.0.0/16
|
|
101
116
|
if (a === 192 && b === 168) return true;
|
|
117
|
+
// 198.18.0.0/15 (benchmarking)
|
|
118
|
+
if (a === 198 && (b === 18 || b === 19)) return true;
|
|
119
|
+
// 240.0.0.0/4 (reserved, includes 255.255.255.255 broadcast)
|
|
120
|
+
if (a >= 240) return true;
|
|
102
121
|
|
|
103
122
|
return false;
|
|
104
123
|
}
|
|
105
124
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
125
|
+
/**
|
|
126
|
+
* Expands an IPv6 address string into its 8 16-bit groups, handling `::`
|
|
127
|
+
* compression and an optional dotted-decimal IPv4 tail (e.g. `::ffff:127.0.0.1`).
|
|
128
|
+
*
|
|
129
|
+
* @returns the 8 groups, or null if the input is not a parseable IPv6 address.
|
|
130
|
+
*/
|
|
131
|
+
function parseIPv6(ip: string): number[] | null {
|
|
132
|
+
// Strip an optional zone id (e.g. `fe80::1%eth0`).
|
|
133
|
+
let address = ip.toLowerCase();
|
|
134
|
+
const zoneIndex = address.indexOf('%');
|
|
135
|
+
if (zoneIndex !== -1) {
|
|
136
|
+
address = address.slice(0, zoneIndex);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// At most one `::` compression marker is allowed.
|
|
140
|
+
const halves = address.split('::');
|
|
141
|
+
if (halves.length > 2) return null;
|
|
142
|
+
|
|
143
|
+
const toGroups = (segment: string): number[] | null => {
|
|
144
|
+
if (segment === '') return [];
|
|
145
|
+
const groups: number[] = [];
|
|
146
|
+
const parts = segment.split(':');
|
|
147
|
+
for (let i = 0; i < parts.length; i++) {
|
|
148
|
+
const part = parts[i];
|
|
149
|
+
// A dotted-decimal IPv4 tail is only valid as the final part.
|
|
150
|
+
if (part.includes('.')) {
|
|
151
|
+
if (i !== parts.length - 1 || !isIPv4(part)) return null;
|
|
152
|
+
const [a, b, c, d] = part.split('.').map(Number);
|
|
153
|
+
groups.push((a << 8) | b, (c << 8) | d);
|
|
154
|
+
continue;
|
|
132
155
|
}
|
|
156
|
+
if (!/^[0-9a-f]{1,4}$/.test(part)) return null;
|
|
157
|
+
groups.push(parseInt(part, 16));
|
|
133
158
|
}
|
|
159
|
+
return groups;
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
const head = toGroups(halves[0]);
|
|
163
|
+
if (head === null) return null;
|
|
164
|
+
|
|
165
|
+
if (halves.length === 2) {
|
|
166
|
+
const tail = toGroups(halves[1]);
|
|
167
|
+
if (tail === null) return null;
|
|
168
|
+
const fill = 8 - head.length - tail.length;
|
|
169
|
+
if (fill < 0) return null;
|
|
170
|
+
return [...head, ...new Array<number>(fill).fill(0), ...tail];
|
|
134
171
|
}
|
|
135
172
|
|
|
136
|
-
//
|
|
137
|
-
|
|
173
|
+
// No `::` compression: the address must contain exactly 8 groups.
|
|
174
|
+
return head.length === 8 ? head : null;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function isPrivateIPv6(ip: string): boolean {
|
|
178
|
+
const groups = parseIPv6(ip);
|
|
179
|
+
|
|
180
|
+
// Fail closed: if the address cannot be parsed, treat it as unsafe.
|
|
181
|
+
if (groups === null) return true;
|
|
182
|
+
|
|
183
|
+
const topZero = (count: number) =>
|
|
184
|
+
groups.slice(0, count).every(group => group === 0);
|
|
185
|
+
|
|
186
|
+
// ::1 (loopback) and :: (unspecified)
|
|
187
|
+
if (topZero(7) && (groups[7] === 0 || groups[7] === 1)) return true;
|
|
188
|
+
|
|
189
|
+
// fc00::/7 (unique local addresses)
|
|
190
|
+
if ((groups[0] & 0xfe00) === 0xfc00) return true;
|
|
138
191
|
|
|
139
192
|
// fe80::/10 (link-local)
|
|
140
|
-
if (
|
|
193
|
+
if ((groups[0] & 0xffc0) === 0xfe80) return true;
|
|
194
|
+
|
|
195
|
+
// fec0::/10 (site-local, deprecated but still routable internally)
|
|
196
|
+
if ((groups[0] & 0xffc0) === 0xfec0) return true;
|
|
197
|
+
|
|
198
|
+
// ff00::/8 (multicast)
|
|
199
|
+
if ((groups[0] & 0xff00) === 0xff00) return true;
|
|
200
|
+
|
|
201
|
+
// Addresses that embed an IPv4 address in their last 32 bits. For these we
|
|
202
|
+
// extract the embedded IPv4 and reuse the IPv4 private-range checks, so that
|
|
203
|
+
// e.g. ::ffff:127.0.0.1 or 64:ff9b::169.254.169.254 are blocked.
|
|
204
|
+
const embedsIPv4 =
|
|
205
|
+
// ::/96 — IPv4-compatible (deprecated)
|
|
206
|
+
topZero(6) ||
|
|
207
|
+
// ::ffff:0:0/96 — IPv4-mapped (ffff in group 5)
|
|
208
|
+
(topZero(5) && groups[5] === 0xffff) ||
|
|
209
|
+
// ::ffff:0:0/96 — IPv4-translated form (ffff in group 4, group 5 zero)
|
|
210
|
+
(topZero(4) && groups[4] === 0xffff && groups[5] === 0) ||
|
|
211
|
+
// 64:ff9b::/96 — NAT64 well-known prefix
|
|
212
|
+
(groups[0] === 0x0064 &&
|
|
213
|
+
groups[1] === 0xff9b &&
|
|
214
|
+
groups[2] === 0 &&
|
|
215
|
+
groups[3] === 0 &&
|
|
216
|
+
groups[4] === 0 &&
|
|
217
|
+
groups[5] === 0) ||
|
|
218
|
+
// 64:ff9b:1::/48 — NAT64 local-use prefix
|
|
219
|
+
(groups[0] === 0x0064 && groups[1] === 0xff9b && groups[2] === 0x0001);
|
|
220
|
+
|
|
221
|
+
if (embedsIPv4) {
|
|
222
|
+
const a = (groups[6] >> 8) & 0xff;
|
|
223
|
+
const b = groups[6] & 0xff;
|
|
224
|
+
const c = (groups[7] >> 8) & 0xff;
|
|
225
|
+
const d = groups[7] & 0xff;
|
|
226
|
+
return isPrivateIPv4(`${a}.${b}.${c}.${d}`);
|
|
227
|
+
}
|
|
141
228
|
|
|
142
229
|
return false;
|
|
143
230
|
}
|
package/src/validate-types.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import {
|
|
2
|
+
TypeValidationError,
|
|
3
|
+
type TypeValidationContext,
|
|
4
|
+
} from '@ai-sdk/provider';
|
|
5
|
+
import { asSchema, type FlexibleSchema } from './schema';
|
|
4
6
|
/**
|
|
5
7
|
* Validates the types of an unknown object using a schema and
|
|
6
8
|
* return a strongly-typed object.
|