@ai-sdk/provider-utils 4.0.5 → 4.0.6

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.
Files changed (163) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/index.js +1 -1
  3. package/dist/index.mjs +1 -1
  4. package/package.json +4 -2
  5. package/src/__snapshots__/schema.test.ts.snap +346 -0
  6. package/src/add-additional-properties-to-json-schema.test.ts +289 -0
  7. package/src/add-additional-properties-to-json-schema.ts +53 -0
  8. package/src/combine-headers.ts +11 -0
  9. package/src/convert-async-iterator-to-readable-stream.test.ts +78 -0
  10. package/src/convert-async-iterator-to-readable-stream.ts +47 -0
  11. package/src/convert-image-model-file-to-data-uri.test.ts +85 -0
  12. package/src/convert-image-model-file-to-data-uri.ts +19 -0
  13. package/src/convert-to-form-data.test.ts +167 -0
  14. package/src/convert-to-form-data.ts +61 -0
  15. package/src/create-tool-name-mapping.test.ts +163 -0
  16. package/src/create-tool-name-mapping.ts +66 -0
  17. package/src/delay.test.ts +212 -0
  18. package/src/delay.ts +47 -0
  19. package/src/delayed-promise.test.ts +132 -0
  20. package/src/delayed-promise.ts +61 -0
  21. package/src/download-blob.test.ts +145 -0
  22. package/src/download-blob.ts +31 -0
  23. package/src/download-error.ts +39 -0
  24. package/src/extract-response-headers.ts +9 -0
  25. package/src/fetch-function.ts +4 -0
  26. package/src/generate-id.test.ts +31 -0
  27. package/src/generate-id.ts +57 -0
  28. package/src/get-error-message.ts +15 -0
  29. package/src/get-from-api.test.ts +199 -0
  30. package/src/get-from-api.ts +97 -0
  31. package/src/get-runtime-environment-user-agent.test.ts +47 -0
  32. package/src/get-runtime-environment-user-agent.ts +24 -0
  33. package/src/handle-fetch-error.ts +39 -0
  34. package/src/index.ts +67 -0
  35. package/src/inject-json-instruction.test.ts +404 -0
  36. package/src/inject-json-instruction.ts +63 -0
  37. package/src/is-abort-error.ts +8 -0
  38. package/src/is-async-iterable.ts +3 -0
  39. package/src/is-non-nullable.ts +12 -0
  40. package/src/is-url-supported.test.ts +282 -0
  41. package/src/is-url-supported.ts +40 -0
  42. package/src/load-api-key.ts +45 -0
  43. package/src/load-optional-setting.ts +30 -0
  44. package/src/load-setting.ts +62 -0
  45. package/src/maybe-promise-like.ts +3 -0
  46. package/src/media-type-to-extension.test.ts +26 -0
  47. package/src/media-type-to-extension.ts +22 -0
  48. package/src/normalize-headers.test.ts +64 -0
  49. package/src/normalize-headers.ts +38 -0
  50. package/src/parse-json-event-stream.ts +33 -0
  51. package/src/parse-json.test.ts +191 -0
  52. package/src/parse-json.ts +122 -0
  53. package/src/parse-provider-options.ts +32 -0
  54. package/src/post-to-api.ts +166 -0
  55. package/src/provider-tool-factory.ts +125 -0
  56. package/src/remove-undefined-entries.test.ts +57 -0
  57. package/src/remove-undefined-entries.ts +12 -0
  58. package/src/resolve.test.ts +125 -0
  59. package/src/resolve.ts +17 -0
  60. package/src/response-handler.test.ts +89 -0
  61. package/src/response-handler.ts +187 -0
  62. package/src/schema.test-d.ts +11 -0
  63. package/src/schema.test.ts +502 -0
  64. package/src/schema.ts +267 -0
  65. package/src/secure-json-parse.test.ts +59 -0
  66. package/src/secure-json-parse.ts +92 -0
  67. package/src/test/convert-array-to-async-iterable.ts +9 -0
  68. package/src/test/convert-array-to-readable-stream.ts +15 -0
  69. package/src/test/convert-async-iterable-to-array.ts +9 -0
  70. package/src/test/convert-readable-stream-to-array.ts +14 -0
  71. package/src/test/convert-response-stream-to-array.ts +9 -0
  72. package/src/test/index.ts +7 -0
  73. package/src/test/is-node-version.ts +4 -0
  74. package/src/test/mock-id.ts +8 -0
  75. package/src/to-json-schema/zod3-to-json-schema/LICENSE +16 -0
  76. package/src/to-json-schema/zod3-to-json-schema/README.md +24 -0
  77. package/src/to-json-schema/zod3-to-json-schema/get-relative-path.ts +7 -0
  78. package/src/to-json-schema/zod3-to-json-schema/index.ts +1 -0
  79. package/src/to-json-schema/zod3-to-json-schema/options.ts +98 -0
  80. package/src/to-json-schema/zod3-to-json-schema/parse-def.test.ts +224 -0
  81. package/src/to-json-schema/zod3-to-json-schema/parse-def.ts +109 -0
  82. package/src/to-json-schema/zod3-to-json-schema/parse-types.ts +57 -0
  83. package/src/to-json-schema/zod3-to-json-schema/parsers/any.ts +5 -0
  84. package/src/to-json-schema/zod3-to-json-schema/parsers/array.test.ts +98 -0
  85. package/src/to-json-schema/zod3-to-json-schema/parsers/array.ts +38 -0
  86. package/src/to-json-schema/zod3-to-json-schema/parsers/bigint.test.ts +51 -0
  87. package/src/to-json-schema/zod3-to-json-schema/parsers/bigint.ts +44 -0
  88. package/src/to-json-schema/zod3-to-json-schema/parsers/boolean.ts +7 -0
  89. package/src/to-json-schema/zod3-to-json-schema/parsers/branded.test.ts +16 -0
  90. package/src/to-json-schema/zod3-to-json-schema/parsers/branded.ts +7 -0
  91. package/src/to-json-schema/zod3-to-json-schema/parsers/catch.test.ts +15 -0
  92. package/src/to-json-schema/zod3-to-json-schema/parsers/catch.ts +7 -0
  93. package/src/to-json-schema/zod3-to-json-schema/parsers/date.test.ts +97 -0
  94. package/src/to-json-schema/zod3-to-json-schema/parsers/date.ts +64 -0
  95. package/src/to-json-schema/zod3-to-json-schema/parsers/default.test.ts +54 -0
  96. package/src/to-json-schema/zod3-to-json-schema/parsers/default.ts +14 -0
  97. package/src/to-json-schema/zod3-to-json-schema/parsers/effects.test.ts +41 -0
  98. package/src/to-json-schema/zod3-to-json-schema/parsers/effects.ts +14 -0
  99. package/src/to-json-schema/zod3-to-json-schema/parsers/enum.ts +13 -0
  100. package/src/to-json-schema/zod3-to-json-schema/parsers/intersection.test.ts +92 -0
  101. package/src/to-json-schema/zod3-to-json-schema/parsers/intersection.ts +52 -0
  102. package/src/to-json-schema/zod3-to-json-schema/parsers/literal.ts +29 -0
  103. package/src/to-json-schema/zod3-to-json-schema/parsers/map.test.ts +48 -0
  104. package/src/to-json-schema/zod3-to-json-schema/parsers/map.ts +47 -0
  105. package/src/to-json-schema/zod3-to-json-schema/parsers/native-enum.test.ts +102 -0
  106. package/src/to-json-schema/zod3-to-json-schema/parsers/native-enum.ts +31 -0
  107. package/src/to-json-schema/zod3-to-json-schema/parsers/never.ts +9 -0
  108. package/src/to-json-schema/zod3-to-json-schema/parsers/null.ts +9 -0
  109. package/src/to-json-schema/zod3-to-json-schema/parsers/nullable.test.ts +67 -0
  110. package/src/to-json-schema/zod3-to-json-schema/parsers/nullable.ts +42 -0
  111. package/src/to-json-schema/zod3-to-json-schema/parsers/number.test.ts +65 -0
  112. package/src/to-json-schema/zod3-to-json-schema/parsers/number.ts +44 -0
  113. package/src/to-json-schema/zod3-to-json-schema/parsers/object.test.ts +149 -0
  114. package/src/to-json-schema/zod3-to-json-schema/parsers/object.ts +88 -0
  115. package/src/to-json-schema/zod3-to-json-schema/parsers/optional.test.ts +147 -0
  116. package/src/to-json-schema/zod3-to-json-schema/parsers/optional.ts +23 -0
  117. package/src/to-json-schema/zod3-to-json-schema/parsers/pipe.test.ts +35 -0
  118. package/src/to-json-schema/zod3-to-json-schema/parsers/pipeline.ts +29 -0
  119. package/src/to-json-schema/zod3-to-json-schema/parsers/promise.test.ts +15 -0
  120. package/src/to-json-schema/zod3-to-json-schema/parsers/promise.ts +11 -0
  121. package/src/to-json-schema/zod3-to-json-schema/parsers/readonly.test.ts +20 -0
  122. package/src/to-json-schema/zod3-to-json-schema/parsers/readonly.ts +7 -0
  123. package/src/to-json-schema/zod3-to-json-schema/parsers/record.test.ts +108 -0
  124. package/src/to-json-schema/zod3-to-json-schema/parsers/record.ts +71 -0
  125. package/src/to-json-schema/zod3-to-json-schema/parsers/set.test.ts +20 -0
  126. package/src/to-json-schema/zod3-to-json-schema/parsers/set.ts +35 -0
  127. package/src/to-json-schema/zod3-to-json-schema/parsers/string.test.ts +438 -0
  128. package/src/to-json-schema/zod3-to-json-schema/parsers/string.ts +426 -0
  129. package/src/to-json-schema/zod3-to-json-schema/parsers/tuple.test.ts +33 -0
  130. package/src/to-json-schema/zod3-to-json-schema/parsers/tuple.ts +61 -0
  131. package/src/to-json-schema/zod3-to-json-schema/parsers/undefined.ts +11 -0
  132. package/src/to-json-schema/zod3-to-json-schema/parsers/union.test.ts +226 -0
  133. package/src/to-json-schema/zod3-to-json-schema/parsers/union.ts +144 -0
  134. package/src/to-json-schema/zod3-to-json-schema/parsers/unknown.ts +7 -0
  135. package/src/to-json-schema/zod3-to-json-schema/refs.test.ts +919 -0
  136. package/src/to-json-schema/zod3-to-json-schema/refs.ts +39 -0
  137. package/src/to-json-schema/zod3-to-json-schema/select-parser.ts +115 -0
  138. package/src/to-json-schema/zod3-to-json-schema/zod3-to-json-schema.test.ts +862 -0
  139. package/src/to-json-schema/zod3-to-json-schema/zod3-to-json-schema.ts +93 -0
  140. package/src/types/assistant-model-message.ts +39 -0
  141. package/src/types/content-part.ts +379 -0
  142. package/src/types/data-content.ts +4 -0
  143. package/src/types/execute-tool.ts +27 -0
  144. package/src/types/index.ts +40 -0
  145. package/src/types/model-message.ts +14 -0
  146. package/src/types/provider-options.ts +9 -0
  147. package/src/types/system-model-message.ts +20 -0
  148. package/src/types/tool-approval-request.ts +16 -0
  149. package/src/types/tool-approval-response.ts +27 -0
  150. package/src/types/tool-call.ts +31 -0
  151. package/src/types/tool-model-message.ts +23 -0
  152. package/src/types/tool-result.ts +35 -0
  153. package/src/types/tool.test-d.ts +193 -0
  154. package/src/types/tool.ts +324 -0
  155. package/src/types/user-model-message.ts +22 -0
  156. package/src/uint8-utils.ts +26 -0
  157. package/src/validate-types.test.ts +105 -0
  158. package/src/validate-types.ts +81 -0
  159. package/src/version.ts +6 -0
  160. package/src/with-user-agent-suffix.test.ts +84 -0
  161. package/src/with-user-agent-suffix.ts +27 -0
  162. package/src/without-trailing-slash.ts +3 -0
  163. package/LICENSE +0 -13
package/src/schema.ts ADDED
@@ -0,0 +1,267 @@
1
+ import { JSONSchema7, TypeValidationError } from '@ai-sdk/provider';
2
+ import { StandardSchemaV1, StandardJSONSchemaV1 } from '@standard-schema/spec';
3
+ import * as z3 from 'zod/v3';
4
+ import * as z4 from 'zod/v4';
5
+ import { addAdditionalPropertiesToJsonSchema } from './add-additional-properties-to-json-schema';
6
+ import { zod3ToJsonSchema } from './to-json-schema/zod3-to-json-schema';
7
+
8
+ /**
9
+ * Used to mark schemas so we can support both Zod and custom schemas.
10
+ */
11
+ const schemaSymbol = Symbol.for('vercel.ai.schema');
12
+
13
+ export type ValidationResult<OBJECT> =
14
+ | { success: true; value: OBJECT }
15
+ | { success: false; error: Error };
16
+
17
+ export type Schema<OBJECT = unknown> = {
18
+ /**
19
+ * Used to mark schemas so we can support both Zod and custom schemas.
20
+ */
21
+ [schemaSymbol]: true;
22
+
23
+ /**
24
+ * Schema type for inference.
25
+ */
26
+ _type: OBJECT;
27
+
28
+ /**
29
+ * Optional. Validates that the structure of a value matches this schema,
30
+ * and returns a typed version of the value if it does.
31
+ */
32
+ readonly validate?: (
33
+ value: unknown,
34
+ ) => ValidationResult<OBJECT> | PromiseLike<ValidationResult<OBJECT>>;
35
+
36
+ /**
37
+ * The JSON Schema for the schema. It is passed to the providers.
38
+ */
39
+ readonly jsonSchema: JSONSchema7 | PromiseLike<JSONSchema7>;
40
+ };
41
+
42
+ /**
43
+ * Creates a schema with deferred creation.
44
+ * This is important to reduce the startup time of the library
45
+ * and to avoid initializing unused validators.
46
+ *
47
+ * @param createValidator A function that creates a schema.
48
+ * @returns A function that returns a schema.
49
+ */
50
+ export function lazySchema<SCHEMA>(
51
+ createSchema: () => Schema<SCHEMA>,
52
+ ): LazySchema<SCHEMA> {
53
+ // cache the validator to avoid initializing it multiple times
54
+ let schema: Schema<SCHEMA> | undefined;
55
+ return () => {
56
+ if (schema == null) {
57
+ schema = createSchema();
58
+ }
59
+ return schema;
60
+ };
61
+ }
62
+
63
+ export type LazySchema<SCHEMA> = () => Schema<SCHEMA>;
64
+
65
+ export type ZodSchema<SCHEMA = any> =
66
+ | z3.Schema<SCHEMA, z3.ZodTypeDef, any>
67
+ | z4.core.$ZodType<SCHEMA, any>;
68
+
69
+ export type StandardSchema<SCHEMA = any> = StandardSchemaV1<unknown, SCHEMA> &
70
+ StandardJSONSchemaV1<unknown, SCHEMA>;
71
+
72
+ export type FlexibleSchema<SCHEMA = any> =
73
+ | Schema<SCHEMA>
74
+ | LazySchema<SCHEMA>
75
+ | ZodSchema<SCHEMA>
76
+ | StandardSchema<SCHEMA>;
77
+
78
+ export type InferSchema<SCHEMA> =
79
+ SCHEMA extends ZodSchema<infer T>
80
+ ? T
81
+ : SCHEMA extends StandardSchema<infer T>
82
+ ? T
83
+ : SCHEMA extends LazySchema<infer T>
84
+ ? T
85
+ : SCHEMA extends Schema<infer T>
86
+ ? T
87
+ : never;
88
+
89
+ /**
90
+ * Create a schema using a JSON Schema.
91
+ *
92
+ * @param jsonSchema The JSON Schema for the schema.
93
+ * @param options.validate Optional. A validation function for the schema.
94
+ */
95
+ export function jsonSchema<OBJECT = unknown>(
96
+ jsonSchema:
97
+ | JSONSchema7
98
+ | PromiseLike<JSONSchema7>
99
+ | (() => JSONSchema7 | PromiseLike<JSONSchema7>),
100
+ {
101
+ validate,
102
+ }: {
103
+ validate?: (
104
+ value: unknown,
105
+ ) => ValidationResult<OBJECT> | PromiseLike<ValidationResult<OBJECT>>;
106
+ } = {},
107
+ ): Schema<OBJECT> {
108
+ return {
109
+ [schemaSymbol]: true,
110
+ _type: undefined as OBJECT, // should never be used directly
111
+ get jsonSchema() {
112
+ if (typeof jsonSchema === 'function') {
113
+ jsonSchema = jsonSchema(); // cache the function results
114
+ }
115
+ return jsonSchema;
116
+ },
117
+ validate,
118
+ };
119
+ }
120
+
121
+ function isSchema(value: unknown): value is Schema {
122
+ return (
123
+ typeof value === 'object' &&
124
+ value !== null &&
125
+ schemaSymbol in value &&
126
+ value[schemaSymbol] === true &&
127
+ 'jsonSchema' in value &&
128
+ 'validate' in value
129
+ );
130
+ }
131
+
132
+ export function asSchema<OBJECT>(
133
+ schema: FlexibleSchema<OBJECT> | undefined,
134
+ ): Schema<OBJECT> {
135
+ return schema == null
136
+ ? jsonSchema({ properties: {}, additionalProperties: false })
137
+ : isSchema(schema)
138
+ ? schema
139
+ : '~standard' in schema
140
+ ? schema['~standard'].vendor === 'zod'
141
+ ? zodSchema(schema as ZodSchema<OBJECT>)
142
+ : standardSchema(schema as StandardSchema<OBJECT>)
143
+ : schema();
144
+ }
145
+
146
+ function standardSchema<OBJECT>(
147
+ standardSchema: StandardSchema<OBJECT>,
148
+ ): Schema<OBJECT> {
149
+ return jsonSchema(
150
+ () =>
151
+ addAdditionalPropertiesToJsonSchema(
152
+ standardSchema['~standard'].jsonSchema.input({
153
+ target: 'draft-07',
154
+ }) as JSONSchema7,
155
+ ),
156
+ {
157
+ validate: async value => {
158
+ const result = await standardSchema['~standard'].validate(value);
159
+ return 'value' in result
160
+ ? { success: true, value: result.value }
161
+ : {
162
+ success: false,
163
+ error: new TypeValidationError({
164
+ value,
165
+ cause: result.issues,
166
+ }),
167
+ };
168
+ },
169
+ },
170
+ );
171
+ }
172
+
173
+ export function zod3Schema<OBJECT>(
174
+ zodSchema: z3.Schema<OBJECT, z3.ZodTypeDef, any>,
175
+ options?: {
176
+ /**
177
+ * Enables support for references in the schema.
178
+ * This is required for recursive schemas, e.g. with `z.lazy`.
179
+ * However, not all language models and providers support such references.
180
+ * Defaults to `false`.
181
+ */
182
+ useReferences?: boolean;
183
+ },
184
+ ): Schema<OBJECT> {
185
+ // default to no references (to support openapi conversion for google)
186
+ const useReferences = options?.useReferences ?? false;
187
+
188
+ return jsonSchema(
189
+ // defer json schema creation to avoid unnecessary computation when only validation is needed
190
+ () =>
191
+ zod3ToJsonSchema(zodSchema, {
192
+ $refStrategy: useReferences ? 'root' : 'none',
193
+ }) as JSONSchema7,
194
+ {
195
+ validate: async value => {
196
+ const result = await zodSchema.safeParseAsync(value);
197
+ return result.success
198
+ ? { success: true, value: result.data }
199
+ : { success: false, error: result.error };
200
+ },
201
+ },
202
+ );
203
+ }
204
+
205
+ export function zod4Schema<OBJECT>(
206
+ zodSchema: z4.core.$ZodType<OBJECT, any>,
207
+ options?: {
208
+ /**
209
+ * Enables support for references in the schema.
210
+ * This is required for recursive schemas, e.g. with `z.lazy`.
211
+ * However, not all language models and providers support such references.
212
+ * Defaults to `false`.
213
+ */
214
+ useReferences?: boolean;
215
+ },
216
+ ): Schema<OBJECT> {
217
+ // default to no references (to support openapi conversion for google)
218
+ const useReferences = options?.useReferences ?? false;
219
+
220
+ return jsonSchema(
221
+ // defer json schema creation to avoid unnecessary computation when only validation is needed
222
+ () =>
223
+ addAdditionalPropertiesToJsonSchema(
224
+ z4.toJSONSchema(zodSchema, {
225
+ target: 'draft-7',
226
+ io: 'input',
227
+ reused: useReferences ? 'ref' : 'inline',
228
+ }) as JSONSchema7,
229
+ ),
230
+ {
231
+ validate: async value => {
232
+ const result = await z4.safeParseAsync(zodSchema, value);
233
+ return result.success
234
+ ? { success: true, value: result.data }
235
+ : { success: false, error: result.error };
236
+ },
237
+ },
238
+ );
239
+ }
240
+
241
+ export function isZod4Schema(
242
+ zodSchema: z4.core.$ZodType<any, any> | z3.Schema<any, z3.ZodTypeDef, any>,
243
+ ): zodSchema is z4.core.$ZodType<any, any> {
244
+ // https://zod.dev/library-authors?id=how-to-support-zod-3-and-zod-4-simultaneously
245
+ return '_zod' in zodSchema;
246
+ }
247
+
248
+ export function zodSchema<OBJECT>(
249
+ zodSchema:
250
+ | z4.core.$ZodType<OBJECT, any>
251
+ | z3.Schema<OBJECT, z3.ZodTypeDef, any>,
252
+ options?: {
253
+ /**
254
+ * Enables support for references in the schema.
255
+ * This is required for recursive schemas, e.g. with `z.lazy`.
256
+ * However, not all language models and providers support such references.
257
+ * Defaults to `false`.
258
+ */
259
+ useReferences?: boolean;
260
+ },
261
+ ): Schema<OBJECT> {
262
+ if (isZod4Schema(zodSchema)) {
263
+ return zod4Schema(zodSchema, options);
264
+ } else {
265
+ return zod3Schema(zodSchema, options);
266
+ }
267
+ }
@@ -0,0 +1,59 @@
1
+ // Licensed under BSD-3-Clause (this file only)
2
+ // Code adapted from https://github.com/fastify/secure-json-parse/blob/783fcb1b5434709466759847cec974381939673a/test/index.test.js
3
+ //
4
+ // Copyright (c) Vercel, Inc. (https://vercel.com)
5
+ // Copyright (c) 2019 The Fastify Team
6
+ // Copyright (c) 2019, Sideway Inc, and project contributors
7
+ // All rights reserved.
8
+ //
9
+ // The complete list of contributors can be found at:
10
+ // - https://github.com/hapijs/bourne/graphs/contributors
11
+ // - https://github.com/fastify/secure-json-parse/graphs/contributors
12
+ // - https://github.com/vercel/ai/commits/main/packages/provider-utils/src/secure-parse-json.test.ts
13
+ //
14
+ // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
15
+ //
16
+ // 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
17
+ //
18
+ // 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
19
+ //
20
+ // 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
21
+ //
22
+ // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23
+
24
+ import { describe, it, expect } from 'vitest';
25
+ import { secureJsonParse } from './secure-json-parse';
26
+
27
+ describe('secureJsonParse', () => {
28
+ it('parses object string', () => {
29
+ expect(secureJsonParse('{"a": 5, "b": 6}')).toStrictEqual(
30
+ JSON.parse('{"a": 5, "b": 6}'),
31
+ );
32
+ });
33
+
34
+ it('parses null string', () => {
35
+ expect(secureJsonParse('null')).toStrictEqual(JSON.parse('null'));
36
+ });
37
+
38
+ it('parses 0 string', () => {
39
+ expect(secureJsonParse('0')).toStrictEqual(JSON.parse('0'));
40
+ });
41
+
42
+ it('parses string string', () => {
43
+ expect(secureJsonParse('"X"')).toStrictEqual(JSON.parse('"X"'));
44
+ });
45
+
46
+ it('errors on constructor property', () => {
47
+ const text =
48
+ '{ "a": 5, "b": 6, "constructor": { "x": 7 }, "c": { "d": 0, "e": "text", "__proto__": { "y": 8 }, "f": { "g": 2 } } }';
49
+
50
+ expect(() => secureJsonParse(text)).toThrow(SyntaxError);
51
+ });
52
+
53
+ it('errors on proto property', () => {
54
+ const text =
55
+ '{ "a": 5, "b": 6, "__proto__": { "x": 7 }, "c": { "d": 0, "e": "text", "__proto__": { "y": 8 }, "f": { "g": 2 } } }';
56
+
57
+ expect(() => secureJsonParse(text)).toThrow(SyntaxError);
58
+ });
59
+ });
@@ -0,0 +1,92 @@
1
+ // Licensed under BSD-3-Clause (this file only)
2
+ // Code adapted from https://github.com/fastify/secure-json-parse/blob/783fcb1b5434709466759847cec974381939673a/index.js
3
+ //
4
+ // Copyright (c) Vercel, Inc. (https://vercel.com)
5
+ // Copyright (c) 2019 The Fastify Team
6
+ // Copyright (c) 2019, Sideway Inc, and project contributors
7
+ // All rights reserved.
8
+ //
9
+ // The complete list of contributors can be found at:
10
+ // - https://github.com/hapijs/bourne/graphs/contributors
11
+ // - https://github.com/fastify/secure-json-parse/graphs/contributors
12
+ // - https://github.com/vercel/ai/commits/main/packages/provider-utils/src/secure-parse-json.ts
13
+ //
14
+ // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
15
+ //
16
+ // 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
17
+ //
18
+ // 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
19
+ //
20
+ // 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
21
+ //
22
+ // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23
+
24
+ const suspectProtoRx = /"__proto__"\s*:/;
25
+ const suspectConstructorRx = /"constructor"\s*:/;
26
+
27
+ function _parse(text: string) {
28
+ // Parse normally
29
+ const obj = JSON.parse(text);
30
+
31
+ // Ignore null and non-objects
32
+ if (obj === null || typeof obj !== 'object') {
33
+ return obj;
34
+ }
35
+
36
+ if (
37
+ suspectProtoRx.test(text) === false &&
38
+ suspectConstructorRx.test(text) === false
39
+ ) {
40
+ return obj;
41
+ }
42
+
43
+ // Scan result for proto keys
44
+ return filter(obj);
45
+ }
46
+
47
+ function filter(obj: any) {
48
+ let next = [obj];
49
+
50
+ while (next.length) {
51
+ const nodes = next;
52
+ next = [];
53
+
54
+ for (const node of nodes) {
55
+ if (Object.prototype.hasOwnProperty.call(node, '__proto__')) {
56
+ throw new SyntaxError('Object contains forbidden prototype property');
57
+ }
58
+
59
+ if (
60
+ Object.prototype.hasOwnProperty.call(node, 'constructor') &&
61
+ Object.prototype.hasOwnProperty.call(node.constructor, 'prototype')
62
+ ) {
63
+ throw new SyntaxError('Object contains forbidden prototype property');
64
+ }
65
+
66
+ for (const key in node) {
67
+ const value = node[key];
68
+ if (value && typeof value === 'object') {
69
+ next.push(value);
70
+ }
71
+ }
72
+ }
73
+ }
74
+ return obj;
75
+ }
76
+
77
+ export function secureJsonParse(text: string) {
78
+ const { stackTraceLimit } = Error;
79
+ try {
80
+ // Performance optimization, see https://github.com/fastify/secure-json-parse/pull/90
81
+ Error.stackTraceLimit = 0;
82
+ } catch (e) {
83
+ // Fallback in case Error is immutable (v8 readonly)
84
+ return _parse(text);
85
+ }
86
+
87
+ try {
88
+ return _parse(text);
89
+ } finally {
90
+ Error.stackTraceLimit = stackTraceLimit;
91
+ }
92
+ }
@@ -0,0 +1,9 @@
1
+ export function convertArrayToAsyncIterable<T>(values: T[]): AsyncIterable<T> {
2
+ return {
3
+ async *[Symbol.asyncIterator]() {
4
+ for (const value of values) {
5
+ yield value;
6
+ }
7
+ },
8
+ };
9
+ }
@@ -0,0 +1,15 @@
1
+ export function convertArrayToReadableStream<T>(
2
+ values: T[],
3
+ ): ReadableStream<T> {
4
+ return new ReadableStream({
5
+ start(controller) {
6
+ try {
7
+ for (const value of values) {
8
+ controller.enqueue(value);
9
+ }
10
+ } finally {
11
+ controller.close();
12
+ }
13
+ },
14
+ });
15
+ }
@@ -0,0 +1,9 @@
1
+ export async function convertAsyncIterableToArray<T>(
2
+ iterable: AsyncIterable<T>,
3
+ ): Promise<T[]> {
4
+ const result: T[] = [];
5
+ for await (const item of iterable) {
6
+ result.push(item);
7
+ }
8
+ return result;
9
+ }
@@ -0,0 +1,14 @@
1
+ export async function convertReadableStreamToArray<T>(
2
+ stream: ReadableStream<T>,
3
+ ): Promise<T[]> {
4
+ const reader = stream.getReader();
5
+ const result: T[] = [];
6
+
7
+ while (true) {
8
+ const { done, value } = await reader.read();
9
+ if (done) break;
10
+ result.push(value);
11
+ }
12
+
13
+ return result;
14
+ }
@@ -0,0 +1,9 @@
1
+ import { convertReadableStreamToArray } from './convert-readable-stream-to-array';
2
+
3
+ export async function convertResponseStreamToArray(
4
+ response: Response,
5
+ ): Promise<string[]> {
6
+ return convertReadableStreamToArray(
7
+ response.body!.pipeThrough(new TextDecoderStream()),
8
+ );
9
+ }
@@ -0,0 +1,7 @@
1
+ export * from './convert-array-to-async-iterable';
2
+ export * from './convert-array-to-readable-stream';
3
+ export * from './convert-async-iterable-to-array';
4
+ export * from './convert-readable-stream-to-array';
5
+ export * from './convert-response-stream-to-array';
6
+ export * from './is-node-version';
7
+ export * from './mock-id';
@@ -0,0 +1,4 @@
1
+ export function isNodeVersion(version: number) {
2
+ const nodeMajorVersion = parseInt(process.version.slice(1).split('.')[0], 10);
3
+ return nodeMajorVersion === version;
4
+ }
@@ -0,0 +1,8 @@
1
+ export function mockId({
2
+ prefix = 'id',
3
+ }: {
4
+ prefix?: string;
5
+ } = {}): () => string {
6
+ let counter = 0;
7
+ return () => `${prefix}-${counter++}`;
8
+ }
@@ -0,0 +1,16 @@
1
+ ISC License
2
+
3
+ Copyright (c) 2020, Stefan Terdell
4
+ Copyright (c) 2025, Vercel Inc.
5
+
6
+ Permission to use, copy, modify, and/or distribute this software for any
7
+ purpose with or without fee is hereby granted, provided that the above
8
+ copyright notice and this permission notice appear in all copies.
9
+
10
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
@@ -0,0 +1,24 @@
1
+ # `zod-to-json-schema`
2
+
3
+ Originally copied from https://github.com/StefanTerdell/zod-to-json-schema because its `peerDependency` on `"zod": "^3.24.1"` while `ai` needs to support `zod@4` if users already use it. We want to avoid having both `zod@3` and `zod@4` in the dependency tree of our users.
4
+
5
+ The code in this directory and sub-directories is released under the ISC license:
6
+
7
+ ```
8
+ ISC License
9
+
10
+ Copyright (c) 2020, Stefan Terdell
11
+ Copyright (c) 2025, Vercel Inc.
12
+
13
+ Permission to use, copy, modify, and/or distribute this software for any
14
+ purpose with or without fee is hereby granted, provided that the above
15
+ copyright notice and this permission notice appear in all copies.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
18
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
19
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
20
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
21
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
22
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
23
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
24
+ ```
@@ -0,0 +1,7 @@
1
+ export const getRelativePath = (pathA: string[], pathB: string[]) => {
2
+ let i = 0;
3
+ for (; i < pathA.length && i < pathB.length; i++) {
4
+ if (pathA[i] !== pathB[i]) break;
5
+ }
6
+ return [(pathA.length - i).toString(), ...pathB.slice(i)].join('/');
7
+ };
@@ -0,0 +1 @@
1
+ export { zod3ToJsonSchema } from './zod3-to-json-schema';
@@ -0,0 +1,98 @@
1
+ import { ZodSchema, ZodTypeDef } from 'zod/v3';
2
+ import { Refs, Seen } from './refs';
3
+ import { JsonSchema7Type } from './parse-types';
4
+
5
+ export type DateStrategy =
6
+ | 'format:date-time'
7
+ | 'format:date'
8
+ | 'string'
9
+ | 'integer';
10
+
11
+ export const ignoreOverride = Symbol(
12
+ 'Let zodToJsonSchema decide on which parser to use',
13
+ );
14
+
15
+ export type OverrideCallback = (
16
+ def: ZodTypeDef,
17
+ refs: Refs,
18
+ seen: Seen | undefined,
19
+ forceResolution?: boolean,
20
+ ) => JsonSchema7Type | undefined | typeof ignoreOverride;
21
+
22
+ export type PostProcessCallback = (
23
+ jsonSchema: JsonSchema7Type | undefined,
24
+ def: ZodTypeDef,
25
+ refs: Refs,
26
+ ) => JsonSchema7Type | undefined;
27
+
28
+ export const jsonDescription: PostProcessCallback = (jsonSchema, def) => {
29
+ if (def.description) {
30
+ try {
31
+ return {
32
+ ...jsonSchema,
33
+ ...JSON.parse(def.description),
34
+ };
35
+ } catch {}
36
+ }
37
+
38
+ return jsonSchema;
39
+ };
40
+
41
+ export type Options = {
42
+ name: string | undefined;
43
+ $refStrategy: 'root' | 'relative' | 'none' | 'seen';
44
+ basePath: string[];
45
+ effectStrategy: 'input' | 'any';
46
+ pipeStrategy: 'input' | 'output' | 'all';
47
+ dateStrategy: DateStrategy | DateStrategy[];
48
+ mapStrategy: 'entries' | 'record';
49
+ removeAdditionalStrategy: 'passthrough' | 'strict';
50
+ allowedAdditionalProperties: true | undefined;
51
+ rejectedAdditionalProperties: false | undefined;
52
+ strictUnions: boolean;
53
+ definitionPath: string;
54
+ definitions: Record<string, ZodSchema>;
55
+ errorMessages: boolean;
56
+ patternStrategy: 'escape' | 'preserve';
57
+ applyRegexFlags: boolean;
58
+ emailStrategy: 'format:email' | 'format:idn-email' | 'pattern:zod';
59
+ base64Strategy: 'format:binary' | 'contentEncoding:base64' | 'pattern:zod';
60
+ nameStrategy: 'ref' | 'title';
61
+ override?: OverrideCallback;
62
+ postProcess?: PostProcessCallback;
63
+ };
64
+
65
+ export const defaultOptions: Options = {
66
+ name: undefined,
67
+ $refStrategy: 'root',
68
+ basePath: ['#'],
69
+ effectStrategy: 'input',
70
+ pipeStrategy: 'all',
71
+ dateStrategy: 'format:date-time',
72
+ mapStrategy: 'entries',
73
+ removeAdditionalStrategy: 'passthrough',
74
+ allowedAdditionalProperties: true,
75
+ rejectedAdditionalProperties: false,
76
+ definitionPath: 'definitions',
77
+ strictUnions: false,
78
+ definitions: {},
79
+ errorMessages: false,
80
+ patternStrategy: 'escape',
81
+ applyRegexFlags: false,
82
+ emailStrategy: 'format:email',
83
+ base64Strategy: 'contentEncoding:base64',
84
+ nameStrategy: 'ref',
85
+ };
86
+
87
+ export const getDefaultOptions = (
88
+ options: Partial<Options> | string | undefined,
89
+ ) =>
90
+ (typeof options === 'string'
91
+ ? {
92
+ ...defaultOptions,
93
+ name: options,
94
+ }
95
+ : {
96
+ ...defaultOptions,
97
+ ...options,
98
+ }) as Options;