@ai-sdk/provider-utils 4.0.4 → 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 (165) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/index.js +6 -4
  3. package/dist/index.js.map +1 -1
  4. package/dist/index.mjs +6 -4
  5. package/dist/index.mjs.map +1 -1
  6. package/package.json +4 -2
  7. package/src/__snapshots__/schema.test.ts.snap +346 -0
  8. package/src/add-additional-properties-to-json-schema.test.ts +289 -0
  9. package/src/add-additional-properties-to-json-schema.ts +53 -0
  10. package/src/combine-headers.ts +11 -0
  11. package/src/convert-async-iterator-to-readable-stream.test.ts +78 -0
  12. package/src/convert-async-iterator-to-readable-stream.ts +47 -0
  13. package/src/convert-image-model-file-to-data-uri.test.ts +85 -0
  14. package/src/convert-image-model-file-to-data-uri.ts +19 -0
  15. package/src/convert-to-form-data.test.ts +167 -0
  16. package/src/convert-to-form-data.ts +61 -0
  17. package/src/create-tool-name-mapping.test.ts +163 -0
  18. package/src/create-tool-name-mapping.ts +66 -0
  19. package/src/delay.test.ts +212 -0
  20. package/src/delay.ts +47 -0
  21. package/src/delayed-promise.test.ts +132 -0
  22. package/src/delayed-promise.ts +61 -0
  23. package/src/download-blob.test.ts +145 -0
  24. package/src/download-blob.ts +31 -0
  25. package/src/download-error.ts +39 -0
  26. package/src/extract-response-headers.ts +9 -0
  27. package/src/fetch-function.ts +4 -0
  28. package/src/generate-id.test.ts +31 -0
  29. package/src/generate-id.ts +57 -0
  30. package/src/get-error-message.ts +15 -0
  31. package/src/get-from-api.test.ts +199 -0
  32. package/src/get-from-api.ts +97 -0
  33. package/src/get-runtime-environment-user-agent.test.ts +47 -0
  34. package/src/get-runtime-environment-user-agent.ts +24 -0
  35. package/src/handle-fetch-error.ts +39 -0
  36. package/src/index.ts +67 -0
  37. package/src/inject-json-instruction.test.ts +404 -0
  38. package/src/inject-json-instruction.ts +63 -0
  39. package/src/is-abort-error.ts +8 -0
  40. package/src/is-async-iterable.ts +3 -0
  41. package/src/is-non-nullable.ts +12 -0
  42. package/src/is-url-supported.test.ts +282 -0
  43. package/src/is-url-supported.ts +40 -0
  44. package/src/load-api-key.ts +45 -0
  45. package/src/load-optional-setting.ts +30 -0
  46. package/src/load-setting.ts +62 -0
  47. package/src/maybe-promise-like.ts +3 -0
  48. package/src/media-type-to-extension.test.ts +26 -0
  49. package/src/media-type-to-extension.ts +22 -0
  50. package/src/normalize-headers.test.ts +64 -0
  51. package/src/normalize-headers.ts +38 -0
  52. package/src/parse-json-event-stream.ts +33 -0
  53. package/src/parse-json.test.ts +191 -0
  54. package/src/parse-json.ts +122 -0
  55. package/src/parse-provider-options.ts +32 -0
  56. package/src/post-to-api.ts +166 -0
  57. package/src/provider-tool-factory.ts +125 -0
  58. package/src/remove-undefined-entries.test.ts +57 -0
  59. package/src/remove-undefined-entries.ts +12 -0
  60. package/src/resolve.test.ts +125 -0
  61. package/src/resolve.ts +17 -0
  62. package/src/response-handler.test.ts +89 -0
  63. package/src/response-handler.ts +187 -0
  64. package/src/schema.test-d.ts +11 -0
  65. package/src/schema.test.ts +502 -0
  66. package/src/schema.ts +267 -0
  67. package/src/secure-json-parse.test.ts +59 -0
  68. package/src/secure-json-parse.ts +92 -0
  69. package/src/test/convert-array-to-async-iterable.ts +9 -0
  70. package/src/test/convert-array-to-readable-stream.ts +15 -0
  71. package/src/test/convert-async-iterable-to-array.ts +9 -0
  72. package/src/test/convert-readable-stream-to-array.ts +14 -0
  73. package/src/test/convert-response-stream-to-array.ts +9 -0
  74. package/src/test/index.ts +7 -0
  75. package/src/test/is-node-version.ts +4 -0
  76. package/src/test/mock-id.ts +8 -0
  77. package/src/to-json-schema/zod3-to-json-schema/LICENSE +16 -0
  78. package/src/to-json-schema/zod3-to-json-schema/README.md +24 -0
  79. package/src/to-json-schema/zod3-to-json-schema/get-relative-path.ts +7 -0
  80. package/src/to-json-schema/zod3-to-json-schema/index.ts +1 -0
  81. package/src/to-json-schema/zod3-to-json-schema/options.ts +98 -0
  82. package/src/to-json-schema/zod3-to-json-schema/parse-def.test.ts +224 -0
  83. package/src/to-json-schema/zod3-to-json-schema/parse-def.ts +109 -0
  84. package/src/to-json-schema/zod3-to-json-schema/parse-types.ts +57 -0
  85. package/src/to-json-schema/zod3-to-json-schema/parsers/any.ts +5 -0
  86. package/src/to-json-schema/zod3-to-json-schema/parsers/array.test.ts +98 -0
  87. package/src/to-json-schema/zod3-to-json-schema/parsers/array.ts +38 -0
  88. package/src/to-json-schema/zod3-to-json-schema/parsers/bigint.test.ts +51 -0
  89. package/src/to-json-schema/zod3-to-json-schema/parsers/bigint.ts +44 -0
  90. package/src/to-json-schema/zod3-to-json-schema/parsers/boolean.ts +7 -0
  91. package/src/to-json-schema/zod3-to-json-schema/parsers/branded.test.ts +16 -0
  92. package/src/to-json-schema/zod3-to-json-schema/parsers/branded.ts +7 -0
  93. package/src/to-json-schema/zod3-to-json-schema/parsers/catch.test.ts +15 -0
  94. package/src/to-json-schema/zod3-to-json-schema/parsers/catch.ts +7 -0
  95. package/src/to-json-schema/zod3-to-json-schema/parsers/date.test.ts +97 -0
  96. package/src/to-json-schema/zod3-to-json-schema/parsers/date.ts +64 -0
  97. package/src/to-json-schema/zod3-to-json-schema/parsers/default.test.ts +54 -0
  98. package/src/to-json-schema/zod3-to-json-schema/parsers/default.ts +14 -0
  99. package/src/to-json-schema/zod3-to-json-schema/parsers/effects.test.ts +41 -0
  100. package/src/to-json-schema/zod3-to-json-schema/parsers/effects.ts +14 -0
  101. package/src/to-json-schema/zod3-to-json-schema/parsers/enum.ts +13 -0
  102. package/src/to-json-schema/zod3-to-json-schema/parsers/intersection.test.ts +92 -0
  103. package/src/to-json-schema/zod3-to-json-schema/parsers/intersection.ts +52 -0
  104. package/src/to-json-schema/zod3-to-json-schema/parsers/literal.ts +29 -0
  105. package/src/to-json-schema/zod3-to-json-schema/parsers/map.test.ts +48 -0
  106. package/src/to-json-schema/zod3-to-json-schema/parsers/map.ts +47 -0
  107. package/src/to-json-schema/zod3-to-json-schema/parsers/native-enum.test.ts +102 -0
  108. package/src/to-json-schema/zod3-to-json-schema/parsers/native-enum.ts +31 -0
  109. package/src/to-json-schema/zod3-to-json-schema/parsers/never.ts +9 -0
  110. package/src/to-json-schema/zod3-to-json-schema/parsers/null.ts +9 -0
  111. package/src/to-json-schema/zod3-to-json-schema/parsers/nullable.test.ts +67 -0
  112. package/src/to-json-schema/zod3-to-json-schema/parsers/nullable.ts +42 -0
  113. package/src/to-json-schema/zod3-to-json-schema/parsers/number.test.ts +65 -0
  114. package/src/to-json-schema/zod3-to-json-schema/parsers/number.ts +44 -0
  115. package/src/to-json-schema/zod3-to-json-schema/parsers/object.test.ts +149 -0
  116. package/src/to-json-schema/zod3-to-json-schema/parsers/object.ts +88 -0
  117. package/src/to-json-schema/zod3-to-json-schema/parsers/optional.test.ts +147 -0
  118. package/src/to-json-schema/zod3-to-json-schema/parsers/optional.ts +23 -0
  119. package/src/to-json-schema/zod3-to-json-schema/parsers/pipe.test.ts +35 -0
  120. package/src/to-json-schema/zod3-to-json-schema/parsers/pipeline.ts +29 -0
  121. package/src/to-json-schema/zod3-to-json-schema/parsers/promise.test.ts +15 -0
  122. package/src/to-json-schema/zod3-to-json-schema/parsers/promise.ts +11 -0
  123. package/src/to-json-schema/zod3-to-json-schema/parsers/readonly.test.ts +20 -0
  124. package/src/to-json-schema/zod3-to-json-schema/parsers/readonly.ts +7 -0
  125. package/src/to-json-schema/zod3-to-json-schema/parsers/record.test.ts +108 -0
  126. package/src/to-json-schema/zod3-to-json-schema/parsers/record.ts +71 -0
  127. package/src/to-json-schema/zod3-to-json-schema/parsers/set.test.ts +20 -0
  128. package/src/to-json-schema/zod3-to-json-schema/parsers/set.ts +35 -0
  129. package/src/to-json-schema/zod3-to-json-schema/parsers/string.test.ts +438 -0
  130. package/src/to-json-schema/zod3-to-json-schema/parsers/string.ts +426 -0
  131. package/src/to-json-schema/zod3-to-json-schema/parsers/tuple.test.ts +33 -0
  132. package/src/to-json-schema/zod3-to-json-schema/parsers/tuple.ts +61 -0
  133. package/src/to-json-schema/zod3-to-json-schema/parsers/undefined.ts +11 -0
  134. package/src/to-json-schema/zod3-to-json-schema/parsers/union.test.ts +226 -0
  135. package/src/to-json-schema/zod3-to-json-schema/parsers/union.ts +144 -0
  136. package/src/to-json-schema/zod3-to-json-schema/parsers/unknown.ts +7 -0
  137. package/src/to-json-schema/zod3-to-json-schema/refs.test.ts +919 -0
  138. package/src/to-json-schema/zod3-to-json-schema/refs.ts +39 -0
  139. package/src/to-json-schema/zod3-to-json-schema/select-parser.ts +115 -0
  140. package/src/to-json-schema/zod3-to-json-schema/zod3-to-json-schema.test.ts +862 -0
  141. package/src/to-json-schema/zod3-to-json-schema/zod3-to-json-schema.ts +93 -0
  142. package/src/types/assistant-model-message.ts +39 -0
  143. package/src/types/content-part.ts +379 -0
  144. package/src/types/data-content.ts +4 -0
  145. package/src/types/execute-tool.ts +27 -0
  146. package/src/types/index.ts +40 -0
  147. package/src/types/model-message.ts +14 -0
  148. package/src/types/provider-options.ts +9 -0
  149. package/src/types/system-model-message.ts +20 -0
  150. package/src/types/tool-approval-request.ts +16 -0
  151. package/src/types/tool-approval-response.ts +27 -0
  152. package/src/types/tool-call.ts +31 -0
  153. package/src/types/tool-model-message.ts +23 -0
  154. package/src/types/tool-result.ts +35 -0
  155. package/src/types/tool.test-d.ts +193 -0
  156. package/src/types/tool.ts +324 -0
  157. package/src/types/user-model-message.ts +22 -0
  158. package/src/uint8-utils.ts +26 -0
  159. package/src/validate-types.test.ts +105 -0
  160. package/src/validate-types.ts +81 -0
  161. package/src/version.ts +6 -0
  162. package/src/with-user-agent-suffix.test.ts +84 -0
  163. package/src/with-user-agent-suffix.ts +27 -0
  164. package/src/without-trailing-slash.ts +3 -0
  165. package/LICENSE +0 -13
@@ -0,0 +1,426 @@
1
+ import { ZodStringDef } from 'zod/v3';
2
+ import { Refs } from '../refs';
3
+
4
+ let emojiRegex: RegExp | undefined = undefined;
5
+
6
+ /**
7
+ * Generated from the regular expressions found here as of 2024-05-22:
8
+ * https://github.com/colinhacks/zod/blob/master/src/types.ts.
9
+ *
10
+ * Expressions with /i flag have been changed accordingly.
11
+ */
12
+ export const zodPatterns = {
13
+ /**
14
+ * `c` was changed to `[cC]` to replicate /i flag
15
+ */
16
+ cuid: /^[cC][^\s-]{8,}$/,
17
+ cuid2: /^[0-9a-z]+$/,
18
+ ulid: /^[0-9A-HJKMNP-TV-Z]{26}$/,
19
+ /**
20
+ * `a-z` was added to replicate /i flag
21
+ */
22
+ email:
23
+ /^(?!\.)(?!.*\.\.)([a-zA-Z0-9_'+\-\.]*)[a-zA-Z0-9_+-]@([a-zA-Z0-9][a-zA-Z0-9\-]*\.)+[a-zA-Z]{2,}$/,
24
+ /**
25
+ * Constructed a valid Unicode RegExp
26
+ *
27
+ * Lazily instantiate since this type of regex isn't supported
28
+ * in all envs (e.g. React Native).
29
+ *
30
+ * See:
31
+ * https://github.com/colinhacks/zod/issues/2433
32
+ * Fix in Zod:
33
+ * https://github.com/colinhacks/zod/commit/9340fd51e48576a75adc919bff65dbc4a5d4c99b
34
+ */
35
+ emoji: () => {
36
+ if (emojiRegex === undefined) {
37
+ emojiRegex = RegExp(
38
+ '^(\\p{Extended_Pictographic}|\\p{Emoji_Component})+$',
39
+ 'u',
40
+ );
41
+ }
42
+ return emojiRegex;
43
+ },
44
+ /**
45
+ * Unused
46
+ */
47
+ uuid: /^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/,
48
+ /**
49
+ * Unused
50
+ */
51
+ ipv4: /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/,
52
+ ipv4Cidr:
53
+ /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\/(3[0-2]|[12]?[0-9])$/,
54
+ /**
55
+ * Unused
56
+ */
57
+ ipv6: /^(([a-f0-9]{1,4}:){7}|::([a-f0-9]{1,4}:){0,6}|([a-f0-9]{1,4}:){1}:([a-f0-9]{1,4}:){0,5}|([a-f0-9]{1,4}:){2}:([a-f0-9]{1,4}:){0,4}|([a-f0-9]{1,4}:){3}:([a-f0-9]{1,4}:){0,3}|([a-f0-9]{1,4}:){4}:([a-f0-9]{1,4}:){0,2}|([a-f0-9]{1,4}:){5}:([a-f0-9]{1,4}:){0,1})([a-f0-9]{1,4}|(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2})))$/,
58
+ ipv6Cidr:
59
+ /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])$/,
60
+ base64: /^([0-9a-zA-Z+/]{4})*(([0-9a-zA-Z+/]{2}==)|([0-9a-zA-Z+/]{3}=))?$/,
61
+ base64url:
62
+ /^([0-9a-zA-Z-_]{4})*(([0-9a-zA-Z-_]{2}(==)?)|([0-9a-zA-Z-_]{3}(=)?))?$/,
63
+ nanoid: /^[a-zA-Z0-9_-]{21}$/,
64
+ jwt: /^[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+\.[A-Za-z0-9-_]*$/,
65
+ } as const;
66
+
67
+ export type JsonSchema7StringType = {
68
+ type: 'string';
69
+ minLength?: number;
70
+ maxLength?: number;
71
+ format?:
72
+ | 'email'
73
+ | 'idn-email'
74
+ | 'uri'
75
+ | 'uuid'
76
+ | 'date-time'
77
+ | 'ipv4'
78
+ | 'ipv6'
79
+ | 'date'
80
+ | 'time'
81
+ | 'duration';
82
+ pattern?: string;
83
+ allOf?: {
84
+ pattern: string;
85
+ }[];
86
+ anyOf?: {
87
+ format: string;
88
+ }[];
89
+ contentEncoding?: string;
90
+ };
91
+
92
+ export function parseStringDef(
93
+ def: ZodStringDef,
94
+ refs: Refs,
95
+ ): JsonSchema7StringType {
96
+ const res: JsonSchema7StringType = {
97
+ type: 'string',
98
+ };
99
+
100
+ if (def.checks) {
101
+ for (const check of def.checks) {
102
+ switch (check.kind) {
103
+ case 'min':
104
+ res.minLength =
105
+ typeof res.minLength === 'number'
106
+ ? Math.max(res.minLength, check.value)
107
+ : check.value;
108
+ break;
109
+ case 'max':
110
+ res.maxLength =
111
+ typeof res.maxLength === 'number'
112
+ ? Math.min(res.maxLength, check.value)
113
+ : check.value;
114
+
115
+ break;
116
+ case 'email':
117
+ switch (refs.emailStrategy) {
118
+ case 'format:email':
119
+ addFormat(res, 'email', check.message, refs);
120
+ break;
121
+ case 'format:idn-email':
122
+ addFormat(res, 'idn-email', check.message, refs);
123
+ break;
124
+ case 'pattern:zod':
125
+ addPattern(res, zodPatterns.email, check.message, refs);
126
+ break;
127
+ }
128
+
129
+ break;
130
+ case 'url':
131
+ addFormat(res, 'uri', check.message, refs);
132
+ break;
133
+ case 'uuid':
134
+ addFormat(res, 'uuid', check.message, refs);
135
+ break;
136
+ case 'regex':
137
+ addPattern(res, check.regex, check.message, refs);
138
+ break;
139
+ case 'cuid':
140
+ addPattern(res, zodPatterns.cuid, check.message, refs);
141
+ break;
142
+ case 'cuid2':
143
+ addPattern(res, zodPatterns.cuid2, check.message, refs);
144
+ break;
145
+ case 'startsWith':
146
+ addPattern(
147
+ res,
148
+ RegExp(`^${escapeLiteralCheckValue(check.value, refs)}`),
149
+ check.message,
150
+ refs,
151
+ );
152
+ break;
153
+ case 'endsWith':
154
+ addPattern(
155
+ res,
156
+ RegExp(`${escapeLiteralCheckValue(check.value, refs)}$`),
157
+ check.message,
158
+ refs,
159
+ );
160
+ break;
161
+ case 'datetime':
162
+ addFormat(res, 'date-time', check.message, refs);
163
+ break;
164
+ case 'date':
165
+ addFormat(res, 'date', check.message, refs);
166
+ break;
167
+ case 'time':
168
+ addFormat(res, 'time', check.message, refs);
169
+ break;
170
+ case 'duration':
171
+ addFormat(res, 'duration', check.message, refs);
172
+ break;
173
+ case 'length':
174
+ res.minLength =
175
+ typeof res.minLength === 'number'
176
+ ? Math.max(res.minLength, check.value)
177
+ : check.value;
178
+ res.maxLength =
179
+ typeof res.maxLength === 'number'
180
+ ? Math.min(res.maxLength, check.value)
181
+ : check.value;
182
+ break;
183
+ case 'includes': {
184
+ addPattern(
185
+ res,
186
+ RegExp(escapeLiteralCheckValue(check.value, refs)),
187
+ check.message,
188
+ refs,
189
+ );
190
+ break;
191
+ }
192
+ case 'ip': {
193
+ if (check.version !== 'v6') {
194
+ addFormat(res, 'ipv4', check.message, refs);
195
+ }
196
+ if (check.version !== 'v4') {
197
+ addFormat(res, 'ipv6', check.message, refs);
198
+ }
199
+ break;
200
+ }
201
+ case 'base64url':
202
+ addPattern(res, zodPatterns.base64url, check.message, refs);
203
+ break;
204
+ case 'jwt':
205
+ addPattern(res, zodPatterns.jwt, check.message, refs);
206
+ break;
207
+ case 'cidr': {
208
+ if (check.version !== 'v6') {
209
+ addPattern(res, zodPatterns.ipv4Cidr, check.message, refs);
210
+ }
211
+ if (check.version !== 'v4') {
212
+ addPattern(res, zodPatterns.ipv6Cidr, check.message, refs);
213
+ }
214
+ break;
215
+ }
216
+ case 'emoji':
217
+ addPattern(res, zodPatterns.emoji(), check.message, refs);
218
+ break;
219
+ case 'ulid': {
220
+ addPattern(res, zodPatterns.ulid, check.message, refs);
221
+ break;
222
+ }
223
+ case 'base64': {
224
+ switch (refs.base64Strategy) {
225
+ case 'format:binary': {
226
+ addFormat(res, 'binary' as any, check.message, refs);
227
+ break;
228
+ }
229
+
230
+ case 'contentEncoding:base64': {
231
+ res.contentEncoding = 'base64';
232
+ break;
233
+ }
234
+
235
+ case 'pattern:zod': {
236
+ addPattern(res, zodPatterns.base64, check.message, refs);
237
+ break;
238
+ }
239
+ }
240
+ break;
241
+ }
242
+ case 'nanoid': {
243
+ addPattern(res, zodPatterns.nanoid, check.message, refs);
244
+ }
245
+ case 'toLowerCase':
246
+ case 'toUpperCase':
247
+ case 'trim':
248
+ break;
249
+ default:
250
+ /* c8 ignore next */
251
+ ((_: never) => {})(check);
252
+ }
253
+ }
254
+ }
255
+
256
+ return res;
257
+ }
258
+
259
+ function escapeLiteralCheckValue(literal: string, refs: Refs): string {
260
+ return refs.patternStrategy === 'escape'
261
+ ? escapeNonAlphaNumeric(literal)
262
+ : literal;
263
+ }
264
+
265
+ const ALPHA_NUMERIC = new Set(
266
+ 'ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvxyz0123456789',
267
+ );
268
+
269
+ function escapeNonAlphaNumeric(source: string) {
270
+ let result = '';
271
+
272
+ for (let i = 0; i < source.length; i++) {
273
+ if (!ALPHA_NUMERIC.has(source[i])) {
274
+ result += '\\';
275
+ }
276
+
277
+ result += source[i];
278
+ }
279
+
280
+ return result;
281
+ }
282
+
283
+ // Adds a "format" keyword to the schema. If a format exists, both formats will be joined in an allOf-node, along with subsequent ones.
284
+ function addFormat(
285
+ schema: JsonSchema7StringType,
286
+ value: Required<JsonSchema7StringType>['format'],
287
+ message: string | undefined,
288
+ refs: Refs,
289
+ ) {
290
+ if (schema.format || schema.anyOf?.some(x => x.format)) {
291
+ if (!schema.anyOf) {
292
+ schema.anyOf = [];
293
+ }
294
+
295
+ if (schema.format) {
296
+ schema.anyOf!.push({
297
+ format: schema.format,
298
+ });
299
+ delete schema.format;
300
+ }
301
+
302
+ schema.anyOf!.push({
303
+ format: value,
304
+ ...(message &&
305
+ refs.errorMessages && { errorMessage: { format: message } }),
306
+ });
307
+ } else {
308
+ schema.format = value;
309
+ }
310
+ }
311
+
312
+ // Adds a "pattern" keyword to the schema. If a pattern exists, both patterns will be joined in an allOf-node, along with subsequent ones.
313
+ function addPattern(
314
+ schema: JsonSchema7StringType,
315
+ regex: RegExp,
316
+ message: string | undefined,
317
+ refs: Refs,
318
+ ) {
319
+ if (schema.pattern || schema.allOf?.some(x => x.pattern)) {
320
+ if (!schema.allOf) {
321
+ schema.allOf = [];
322
+ }
323
+
324
+ if (schema.pattern) {
325
+ schema.allOf!.push({
326
+ pattern: schema.pattern,
327
+ });
328
+ delete schema.pattern;
329
+ }
330
+
331
+ schema.allOf!.push({
332
+ pattern: stringifyRegExpWithFlags(regex, refs),
333
+ ...(message &&
334
+ refs.errorMessages && { errorMessage: { pattern: message } }),
335
+ });
336
+ } else {
337
+ schema.pattern = stringifyRegExpWithFlags(regex, refs);
338
+ }
339
+ }
340
+
341
+ // Mutate z.string.regex() in a best attempt to accommodate for regex flags when applyRegexFlags is true
342
+ function stringifyRegExpWithFlags(regex: RegExp, refs: Refs): string {
343
+ if (!refs.applyRegexFlags || !regex.flags) {
344
+ return regex.source;
345
+ }
346
+
347
+ // Currently handled flags
348
+ const flags = {
349
+ i: regex.flags.includes('i'), // Case-insensitive
350
+ m: regex.flags.includes('m'), // `^` and `$` matches adjacent to newline characters
351
+ s: regex.flags.includes('s'), // `.` matches newlines
352
+ };
353
+
354
+ // The general principle here is to step through each character, one at a time, applying mutations as flags require. We keep track when the current character is escaped, and when it's inside a group /like [this]/ or (also) a range like /[a-z]/. The following is fairly brittle imperative code; edit at your peril!
355
+ const source = flags.i ? regex.source.toLowerCase() : regex.source;
356
+ let pattern = '';
357
+ let isEscaped = false;
358
+ let inCharGroup = false;
359
+ let inCharRange = false;
360
+
361
+ for (let i = 0; i < source.length; i++) {
362
+ if (isEscaped) {
363
+ pattern += source[i];
364
+ isEscaped = false;
365
+ continue;
366
+ }
367
+
368
+ if (flags.i) {
369
+ if (inCharGroup) {
370
+ if (source[i].match(/[a-z]/)) {
371
+ if (inCharRange) {
372
+ pattern += source[i];
373
+ pattern += `${source[i - 2]}-${source[i]}`.toUpperCase();
374
+ inCharRange = false;
375
+ } else if (source[i + 1] === '-' && source[i + 2]?.match(/[a-z]/)) {
376
+ pattern += source[i];
377
+ inCharRange = true;
378
+ } else {
379
+ pattern += `${source[i]}${source[i].toUpperCase()}`;
380
+ }
381
+ continue;
382
+ }
383
+ } else if (source[i].match(/[a-z]/)) {
384
+ pattern += `[${source[i]}${source[i].toUpperCase()}]`;
385
+ continue;
386
+ }
387
+ }
388
+
389
+ if (flags.m) {
390
+ if (source[i] === '^') {
391
+ pattern += `(^|(?<=[\r\n]))`;
392
+ continue;
393
+ } else if (source[i] === '$') {
394
+ pattern += `($|(?=[\r\n]))`;
395
+ continue;
396
+ }
397
+ }
398
+
399
+ if (flags.s && source[i] === '.') {
400
+ pattern += inCharGroup ? `${source[i]}\r\n` : `[${source[i]}\r\n]`;
401
+ continue;
402
+ }
403
+
404
+ pattern += source[i];
405
+ if (source[i] === '\\') {
406
+ isEscaped = true;
407
+ } else if (inCharGroup && source[i] === ']') {
408
+ inCharGroup = false;
409
+ } else if (!inCharGroup && source[i] === '[') {
410
+ inCharGroup = true;
411
+ }
412
+ }
413
+
414
+ try {
415
+ new RegExp(pattern);
416
+ } catch {
417
+ console.warn(
418
+ `Could not convert regex pattern at ${refs.currentPath.join(
419
+ '/',
420
+ )} to a flag-independent form! Falling back to the flag-ignorant source`,
421
+ );
422
+ return regex.source;
423
+ }
424
+
425
+ return pattern;
426
+ }
@@ -0,0 +1,33 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { z } from 'zod/v3';
3
+ import { parseTupleDef } from './tuple';
4
+ import { getRefs } from '../refs';
5
+ import { JSONSchema7 } from '@ai-sdk/provider';
6
+
7
+ describe('tuple', () => {
8
+ it('should be possible to describe a simple tuple schema', () => {
9
+ const schema = z.tuple([z.string(), z.number()]);
10
+ const parsedSchema = parseTupleDef(schema._def, getRefs());
11
+
12
+ expect(parsedSchema).toStrictEqual({
13
+ type: 'array',
14
+ items: [{ type: 'string' }, { type: 'number' }],
15
+ minItems: 2,
16
+ maxItems: 2,
17
+ } satisfies JSONSchema7);
18
+ });
19
+
20
+ it('should be possible to describe a tuple schema with rest()', () => {
21
+ const schema = z.tuple([z.string(), z.number()]).rest(z.boolean());
22
+ const parsedSchema = parseTupleDef(schema._def, getRefs());
23
+
24
+ expect(parsedSchema).toStrictEqual({
25
+ type: 'array',
26
+ items: [{ type: 'string' }, { type: 'number' }],
27
+ minItems: 2,
28
+ additionalItems: {
29
+ type: 'boolean',
30
+ },
31
+ } satisfies JSONSchema7);
32
+ });
33
+ });
@@ -0,0 +1,61 @@
1
+ import { ZodTupleDef, ZodTupleItems, ZodTypeAny } from 'zod/v3';
2
+ import { parseDef } from '../parse-def';
3
+ import { JsonSchema7Type } from '../parse-types';
4
+ import { Refs } from '../refs';
5
+
6
+ export type JsonSchema7TupleType = {
7
+ type: 'array';
8
+ minItems: number;
9
+ items: JsonSchema7Type[];
10
+ } & (
11
+ | {
12
+ maxItems: number;
13
+ }
14
+ | {
15
+ additionalItems?: JsonSchema7Type;
16
+ }
17
+ );
18
+
19
+ export function parseTupleDef(
20
+ def: ZodTupleDef<ZodTupleItems | [], ZodTypeAny | null>,
21
+ refs: Refs,
22
+ ): JsonSchema7TupleType {
23
+ if (def.rest) {
24
+ return {
25
+ type: 'array',
26
+ minItems: def.items.length,
27
+ items: def.items
28
+ .map((x, i) =>
29
+ parseDef(x._def, {
30
+ ...refs,
31
+ currentPath: [...refs.currentPath, 'items', `${i}`],
32
+ }),
33
+ )
34
+ .reduce(
35
+ (acc: JsonSchema7Type[], x) => (x === undefined ? acc : [...acc, x]),
36
+ [],
37
+ ),
38
+ additionalItems: parseDef(def.rest._def, {
39
+ ...refs,
40
+ currentPath: [...refs.currentPath, 'additionalItems'],
41
+ }),
42
+ };
43
+ } else {
44
+ return {
45
+ type: 'array',
46
+ minItems: def.items.length,
47
+ maxItems: def.items.length,
48
+ items: def.items
49
+ .map((x, i) =>
50
+ parseDef(x._def, {
51
+ ...refs,
52
+ currentPath: [...refs.currentPath, 'items', `${i}`],
53
+ }),
54
+ )
55
+ .reduce(
56
+ (acc: JsonSchema7Type[], x) => (x === undefined ? acc : [...acc, x]),
57
+ [],
58
+ ),
59
+ };
60
+ }
61
+ }
@@ -0,0 +1,11 @@
1
+ import { JsonSchema7AnyType, parseAnyDef } from './any';
2
+
3
+ export type JsonSchema7UndefinedType = {
4
+ not: JsonSchema7AnyType;
5
+ };
6
+
7
+ export function parseUndefinedDef(): JsonSchema7UndefinedType {
8
+ return {
9
+ not: parseAnyDef(),
10
+ };
11
+ }