canonize 0.1.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/LICENSE +21 -0
- package/README.md +629 -0
- package/dist/coercers/array.d.ts +7 -0
- package/dist/coercers/array.d.ts.map +1 -0
- package/dist/coercers/bigint.d.ts +5 -0
- package/dist/coercers/bigint.d.ts.map +1 -0
- package/dist/coercers/boolean.d.ts +5 -0
- package/dist/coercers/boolean.d.ts.map +1 -0
- package/dist/coercers/date.d.ts +5 -0
- package/dist/coercers/date.d.ts.map +1 -0
- package/dist/coercers/discriminated-union.d.ts +7 -0
- package/dist/coercers/discriminated-union.d.ts.map +1 -0
- package/dist/coercers/enum.d.ts +9 -0
- package/dist/coercers/enum.d.ts.map +1 -0
- package/dist/coercers/intersection.d.ts +8 -0
- package/dist/coercers/intersection.d.ts.map +1 -0
- package/dist/coercers/literal.d.ts +5 -0
- package/dist/coercers/literal.d.ts.map +1 -0
- package/dist/coercers/map-set.d.ts +11 -0
- package/dist/coercers/map-set.d.ts.map +1 -0
- package/dist/coercers/nan.d.ts +5 -0
- package/dist/coercers/nan.d.ts.map +1 -0
- package/dist/coercers/null.d.ts +5 -0
- package/dist/coercers/null.d.ts.map +1 -0
- package/dist/coercers/number.d.ts +13 -0
- package/dist/coercers/number.d.ts.map +1 -0
- package/dist/coercers/object.d.ts +7 -0
- package/dist/coercers/object.d.ts.map +1 -0
- package/dist/coercers/record.d.ts +7 -0
- package/dist/coercers/record.d.ts.map +1 -0
- package/dist/coercers/string.d.ts +6 -0
- package/dist/coercers/string.d.ts.map +1 -0
- package/dist/coercers/tuple.d.ts +7 -0
- package/dist/coercers/tuple.d.ts.map +1 -0
- package/dist/coercers/union.d.ts +7 -0
- package/dist/coercers/union.d.ts.map +1 -0
- package/dist/constants.d.ts +36 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/create-schema.d.ts +5 -0
- package/dist/create-schema.d.ts.map +1 -0
- package/dist/index.d.ts +51 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1623 -0
- package/dist/index.js.map +33 -0
- package/dist/tool-parameters/index.d.ts +206 -0
- package/dist/tool-parameters/index.d.ts.map +1 -0
- package/dist/tool-parameters/index.js +301 -0
- package/dist/tool-parameters/index.js.map +12 -0
- package/dist/tool-parameters/link-metadata.d.ts +17 -0
- package/dist/tool-parameters/link-metadata.d.ts.map +1 -0
- package/dist/utilities/circular.d.ts +27 -0
- package/dist/utilities/circular.d.ts.map +1 -0
- package/dist/utilities/parsers.d.ts +29 -0
- package/dist/utilities/parsers.d.ts.map +1 -0
- package/dist/utilities/special-methods.d.ts +14 -0
- package/dist/utilities/special-methods.d.ts.map +1 -0
- package/dist/utilities/type-coercion.d.ts +11 -0
- package/dist/utilities/type-coercion.d.ts.map +1 -0
- package/dist/utilities/type-detection.d.ts +26 -0
- package/dist/utilities/type-detection.d.ts.map +1 -0
- package/dist/utilities/type-guards.d.ts +35 -0
- package/dist/utilities/type-guards.d.ts.map +1 -0
- package/package.json +123 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1623 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import * as z2 from "zod";
|
|
3
|
+
|
|
4
|
+
// src/utilities/circular.ts
|
|
5
|
+
class CircularTracker {
|
|
6
|
+
seen = new WeakSet;
|
|
7
|
+
has(obj) {
|
|
8
|
+
return this.seen.has(obj);
|
|
9
|
+
}
|
|
10
|
+
add(obj) {
|
|
11
|
+
this.seen.add(obj);
|
|
12
|
+
}
|
|
13
|
+
child() {
|
|
14
|
+
const child = new CircularTracker;
|
|
15
|
+
return child;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function canBeCircular(value) {
|
|
19
|
+
return typeof value === "object" && value !== null;
|
|
20
|
+
}
|
|
21
|
+
function throwCircular() {
|
|
22
|
+
throw new Error("Circular reference detected");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// src/utilities/parsers.ts
|
|
26
|
+
function tryParseJSON(value) {
|
|
27
|
+
try {
|
|
28
|
+
return JSON.parse(value);
|
|
29
|
+
} catch {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function tryParseCSV(value) {
|
|
34
|
+
if (value.includes(",")) {
|
|
35
|
+
return value.split(",").map((s) => s.trim());
|
|
36
|
+
}
|
|
37
|
+
if (value.includes(";")) {
|
|
38
|
+
return value.split(";").map((s) => s.trim());
|
|
39
|
+
}
|
|
40
|
+
if (value.includes("|")) {
|
|
41
|
+
return value.split("|").map((s) => s.trim());
|
|
42
|
+
}
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
function tryParseSpecialNumber(value) {
|
|
46
|
+
const trimmed = value.trim();
|
|
47
|
+
if (/^0x[0-9a-f]+$/i.test(trimmed)) {
|
|
48
|
+
return parseInt(trimmed, 16);
|
|
49
|
+
}
|
|
50
|
+
if (/^0o[0-7]+$/i.test(trimmed)) {
|
|
51
|
+
return parseInt(trimmed.slice(2), 8);
|
|
52
|
+
}
|
|
53
|
+
if (/^0b[01]+$/i.test(trimmed)) {
|
|
54
|
+
return parseInt(trimmed.slice(2), 2);
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
function tryParseNumber(value) {
|
|
59
|
+
const special = tryParseSpecialNumber(value);
|
|
60
|
+
if (special !== null)
|
|
61
|
+
return special;
|
|
62
|
+
const trimmed = value.trim();
|
|
63
|
+
if (trimmed === "")
|
|
64
|
+
return null;
|
|
65
|
+
const num = Number(trimmed);
|
|
66
|
+
if (isNaN(num))
|
|
67
|
+
return null;
|
|
68
|
+
return num;
|
|
69
|
+
}
|
|
70
|
+
function normalizeString(value) {
|
|
71
|
+
return value.trim().toLowerCase();
|
|
72
|
+
}
|
|
73
|
+
function isFalsyForNull(value) {
|
|
74
|
+
if (value === null)
|
|
75
|
+
return true;
|
|
76
|
+
if (value === undefined)
|
|
77
|
+
return true;
|
|
78
|
+
if (value === false)
|
|
79
|
+
return true;
|
|
80
|
+
if (value === 0)
|
|
81
|
+
return true;
|
|
82
|
+
if (Object.is(value, -0))
|
|
83
|
+
return true;
|
|
84
|
+
if (value === "")
|
|
85
|
+
return true;
|
|
86
|
+
if (typeof value === "bigint" && value === 0n)
|
|
87
|
+
return true;
|
|
88
|
+
if (typeof value === "string") {
|
|
89
|
+
const normalized = value.trim().toLowerCase();
|
|
90
|
+
if (normalized === "null" || normalized === "undefined")
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
if (Array.isArray(value) && value.length === 0)
|
|
94
|
+
return true;
|
|
95
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
96
|
+
const keys = Object.keys(value);
|
|
97
|
+
if (keys.length === 0)
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// src/constants.ts
|
|
104
|
+
var TRUTHY_STRINGS = ["true", "yes", "on", "y", "t", "enabled", "1"];
|
|
105
|
+
var FALSY_STRINGS = ["false", "no", "off", "n", "f", "disabled", "0"];
|
|
106
|
+
var ZodType = {
|
|
107
|
+
STRING: "string",
|
|
108
|
+
NUMBER: "number",
|
|
109
|
+
BOOLEAN: "boolean",
|
|
110
|
+
DATE: "date",
|
|
111
|
+
BIGINT: "bigint",
|
|
112
|
+
NULL: "null",
|
|
113
|
+
UNDEFINED: "undefined",
|
|
114
|
+
NAN: "nan",
|
|
115
|
+
ARRAY: "array",
|
|
116
|
+
OBJECT: "object",
|
|
117
|
+
TUPLE: "tuple",
|
|
118
|
+
RECORD: "record",
|
|
119
|
+
ENUM: "enum",
|
|
120
|
+
NATIVE_ENUM: "nativeEnum",
|
|
121
|
+
LITERAL: "literal",
|
|
122
|
+
UNION: "union",
|
|
123
|
+
DISCRIMINATED_UNION: "discriminatedUnion",
|
|
124
|
+
INTERSECTION: "intersection",
|
|
125
|
+
MAP: "map",
|
|
126
|
+
SET: "set",
|
|
127
|
+
OPTIONAL: "optional",
|
|
128
|
+
NULLABLE: "nullable",
|
|
129
|
+
DEFAULT: "default",
|
|
130
|
+
CATCH: "catch",
|
|
131
|
+
LAZY: "lazy",
|
|
132
|
+
READONLY: "readonly",
|
|
133
|
+
BRANDED: "branded",
|
|
134
|
+
ANY: "any",
|
|
135
|
+
UNKNOWN: "unknown",
|
|
136
|
+
NEVER: "never",
|
|
137
|
+
CUSTOM: "custom"
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
// src/utilities/type-detection.ts
|
|
141
|
+
function getZodTypeName(schema) {
|
|
142
|
+
const candidates = [
|
|
143
|
+
schema._def.type,
|
|
144
|
+
schema._def.typeName,
|
|
145
|
+
schema.type
|
|
146
|
+
];
|
|
147
|
+
const rawType = candidates.find((candidate) => typeof candidate === "string") ?? "";
|
|
148
|
+
const withoutPrefix = rawType.replace(/^Zod/i, "");
|
|
149
|
+
const lower = withoutPrefix.toLowerCase();
|
|
150
|
+
const mapped = {
|
|
151
|
+
nativeenum: ZodType.NATIVE_ENUM,
|
|
152
|
+
discriminatedunion: ZodType.DISCRIMINATED_UNION
|
|
153
|
+
};
|
|
154
|
+
return mapped[lower] ?? lower;
|
|
155
|
+
}
|
|
156
|
+
function unwrapSchema(schema) {
|
|
157
|
+
const typeName = getZodTypeName(schema);
|
|
158
|
+
if (typeName === ZodType.OPTIONAL || typeName === ZodType.NULLABLE) {
|
|
159
|
+
return unwrapSchema(schema._def.innerType);
|
|
160
|
+
}
|
|
161
|
+
if (typeName === ZodType.DEFAULT) {
|
|
162
|
+
return unwrapSchema(schema._def.innerType);
|
|
163
|
+
}
|
|
164
|
+
if (typeName === ZodType.CATCH) {
|
|
165
|
+
return unwrapSchema(schema._def.innerType);
|
|
166
|
+
}
|
|
167
|
+
if (typeName === ZodType.READONLY) {
|
|
168
|
+
return unwrapSchema(schema._def.innerType);
|
|
169
|
+
}
|
|
170
|
+
return schema;
|
|
171
|
+
}
|
|
172
|
+
function isPlainObject(value) {
|
|
173
|
+
if (typeof value !== "object" || value === null)
|
|
174
|
+
return false;
|
|
175
|
+
if (Array.isArray(value))
|
|
176
|
+
return false;
|
|
177
|
+
if (value instanceof Date)
|
|
178
|
+
return false;
|
|
179
|
+
if (value instanceof Map)
|
|
180
|
+
return false;
|
|
181
|
+
if (value instanceof Set)
|
|
182
|
+
return false;
|
|
183
|
+
if (value instanceof RegExp)
|
|
184
|
+
return false;
|
|
185
|
+
if (value instanceof Error)
|
|
186
|
+
return false;
|
|
187
|
+
if (ArrayBuffer.isView(value))
|
|
188
|
+
return false;
|
|
189
|
+
const proto = Object.getPrototypeOf(value);
|
|
190
|
+
return proto === Object.prototype || proto === null;
|
|
191
|
+
}
|
|
192
|
+
function isArrayLike(value) {
|
|
193
|
+
if (!isPlainObject(value))
|
|
194
|
+
return false;
|
|
195
|
+
const obj = value;
|
|
196
|
+
return typeof obj["length"] === "number" && obj["length"] >= 0;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// src/coercers/array.ts
|
|
200
|
+
function coerceToArray(value, _elementSchema, coerceElement, tracker = new CircularTracker) {
|
|
201
|
+
if (value === null)
|
|
202
|
+
return [];
|
|
203
|
+
if (canBeCircular(value) && tracker.has(value)) {
|
|
204
|
+
throwCircular();
|
|
205
|
+
}
|
|
206
|
+
if (Array.isArray(value)) {
|
|
207
|
+
tracker.add(value);
|
|
208
|
+
const result = [];
|
|
209
|
+
for (let i = 0;i < value.length; i++) {
|
|
210
|
+
if (i in value) {
|
|
211
|
+
if (canBeCircular(value[i]) && tracker.has(value[i])) {
|
|
212
|
+
throwCircular();
|
|
213
|
+
}
|
|
214
|
+
result[i] = coerceElement(value[i], tracker);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return result;
|
|
218
|
+
}
|
|
219
|
+
if (canBeCircular(value)) {
|
|
220
|
+
tracker.add(value);
|
|
221
|
+
}
|
|
222
|
+
if (typeof value === "string") {
|
|
223
|
+
const trimmed = value.trim();
|
|
224
|
+
if (trimmed === "")
|
|
225
|
+
return [];
|
|
226
|
+
const json = tryParseJSON(trimmed);
|
|
227
|
+
if (Array.isArray(json)) {
|
|
228
|
+
return json.map((v) => coerceElement(v, tracker));
|
|
229
|
+
}
|
|
230
|
+
const csv = tryParseCSV(trimmed);
|
|
231
|
+
if (csv) {
|
|
232
|
+
return csv.map((v) => coerceElement(v, tracker));
|
|
233
|
+
}
|
|
234
|
+
return [coerceElement(trimmed, tracker)];
|
|
235
|
+
}
|
|
236
|
+
if (value instanceof Set) {
|
|
237
|
+
return Array.from(value).map((v) => coerceElement(v, tracker));
|
|
238
|
+
}
|
|
239
|
+
if (value instanceof Map) {
|
|
240
|
+
return Array.from(value.values()).map((v) => coerceElement(v, tracker));
|
|
241
|
+
}
|
|
242
|
+
if (ArrayBuffer.isView(value)) {
|
|
243
|
+
return Array.from(value).map((v) => coerceElement(v, tracker));
|
|
244
|
+
}
|
|
245
|
+
if (isArrayLike(value)) {
|
|
246
|
+
const obj = value;
|
|
247
|
+
const length = obj.length;
|
|
248
|
+
const result = [];
|
|
249
|
+
for (let i = 0;i < length; i++) {
|
|
250
|
+
if (i in obj) {
|
|
251
|
+
result[i] = coerceElement(obj[i], tracker);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return result;
|
|
255
|
+
}
|
|
256
|
+
if (isPlainObject(value)) {
|
|
257
|
+
const values = Object.values(value);
|
|
258
|
+
return values.map((v) => coerceElement(v, tracker));
|
|
259
|
+
}
|
|
260
|
+
return [coerceElement(value, tracker)];
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// src/utilities/special-methods.ts
|
|
264
|
+
function extractPrimitiveValue(value, hint = "default") {
|
|
265
|
+
if (typeof value !== "object" || value === null) {
|
|
266
|
+
return value;
|
|
267
|
+
}
|
|
268
|
+
const obj = value;
|
|
269
|
+
if (value instanceof Date && typeof obj.toJSON === "function") {
|
|
270
|
+
try {
|
|
271
|
+
return obj.toJSON();
|
|
272
|
+
} catch {}
|
|
273
|
+
}
|
|
274
|
+
if (Symbol.toPrimitive in obj) {
|
|
275
|
+
try {
|
|
276
|
+
return obj[Symbol.toPrimitive](hint);
|
|
277
|
+
} catch {}
|
|
278
|
+
}
|
|
279
|
+
if (typeof obj.toJSON === "function") {
|
|
280
|
+
try {
|
|
281
|
+
return obj.toJSON();
|
|
282
|
+
} catch {}
|
|
283
|
+
}
|
|
284
|
+
if (hint === "string" && typeof obj.toString === "function") {
|
|
285
|
+
try {
|
|
286
|
+
const result = obj.toString();
|
|
287
|
+
if (result !== "[object Object]") {
|
|
288
|
+
return result;
|
|
289
|
+
}
|
|
290
|
+
} catch {}
|
|
291
|
+
}
|
|
292
|
+
if (typeof obj.valueOf === "function") {
|
|
293
|
+
try {
|
|
294
|
+
const result = obj.valueOf();
|
|
295
|
+
if (result !== obj) {
|
|
296
|
+
return result;
|
|
297
|
+
}
|
|
298
|
+
} catch {}
|
|
299
|
+
}
|
|
300
|
+
if (hint !== "string" && typeof obj.toString === "function") {
|
|
301
|
+
try {
|
|
302
|
+
const result = obj.toString();
|
|
303
|
+
if (result !== "[object Object]") {
|
|
304
|
+
return result;
|
|
305
|
+
}
|
|
306
|
+
} catch {}
|
|
307
|
+
}
|
|
308
|
+
return value;
|
|
309
|
+
}
|
|
310
|
+
function extractDateValue(value) {
|
|
311
|
+
if (value instanceof Date)
|
|
312
|
+
return value;
|
|
313
|
+
if (typeof value !== "object" || value === null)
|
|
314
|
+
return null;
|
|
315
|
+
const obj = value;
|
|
316
|
+
if (typeof obj.getTime === "function") {
|
|
317
|
+
try {
|
|
318
|
+
const time = obj.getTime();
|
|
319
|
+
if (typeof time === "number" && !isNaN(time)) {
|
|
320
|
+
return new Date(time);
|
|
321
|
+
}
|
|
322
|
+
} catch {}
|
|
323
|
+
}
|
|
324
|
+
if (typeof obj.toISOString === "function") {
|
|
325
|
+
try {
|
|
326
|
+
const iso = obj.toISOString();
|
|
327
|
+
const date = new Date(iso);
|
|
328
|
+
if (!isNaN(date.getTime())) {
|
|
329
|
+
return date;
|
|
330
|
+
}
|
|
331
|
+
} catch {}
|
|
332
|
+
}
|
|
333
|
+
return null;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// src/coercers/bigint.ts
|
|
337
|
+
function coerceToBigInt(value) {
|
|
338
|
+
if (typeof value === "bigint")
|
|
339
|
+
return value;
|
|
340
|
+
if (typeof value === "function") {
|
|
341
|
+
throw new Error("Cannot convert function to BigInt");
|
|
342
|
+
}
|
|
343
|
+
if (typeof value === "symbol") {
|
|
344
|
+
throw new Error("Cannot convert symbol to BigInt");
|
|
345
|
+
}
|
|
346
|
+
if (typeof value === "number") {
|
|
347
|
+
if (!isFinite(value)) {
|
|
348
|
+
throw new Error("Cannot convert NaN or Infinity to BigInt");
|
|
349
|
+
}
|
|
350
|
+
return BigInt(Math.trunc(value));
|
|
351
|
+
}
|
|
352
|
+
if (typeof value === "boolean")
|
|
353
|
+
return value ? 1n : 0n;
|
|
354
|
+
if (typeof value === "string") {
|
|
355
|
+
const trimmed = value.trim();
|
|
356
|
+
if (trimmed === "")
|
|
357
|
+
return 0n;
|
|
358
|
+
try {
|
|
359
|
+
return BigInt(trimmed);
|
|
360
|
+
} catch {
|
|
361
|
+
const json = tryParseJSON(trimmed);
|
|
362
|
+
if (json !== null) {
|
|
363
|
+
return coerceToBigInt(json);
|
|
364
|
+
}
|
|
365
|
+
throw new Error(`Cannot convert "${trimmed}" to BigInt`);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
if (Array.isArray(value)) {
|
|
369
|
+
if (value.length === 0)
|
|
370
|
+
return 0n;
|
|
371
|
+
return coerceToBigInt(value[0]);
|
|
372
|
+
}
|
|
373
|
+
if (value === null)
|
|
374
|
+
return 0n;
|
|
375
|
+
if (value === undefined) {
|
|
376
|
+
throw new Error("Cannot convert undefined to BigInt");
|
|
377
|
+
}
|
|
378
|
+
const extracted = extractPrimitiveValue(value, "number");
|
|
379
|
+
if (typeof extracted === "bigint")
|
|
380
|
+
return extracted;
|
|
381
|
+
if (typeof extracted !== "object" || extracted === null) {
|
|
382
|
+
return coerceToBigInt(extracted);
|
|
383
|
+
}
|
|
384
|
+
throw new Error("Cannot convert object to BigInt");
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// src/coercers/boolean.ts
|
|
388
|
+
function coerceToBoolean(value) {
|
|
389
|
+
if (typeof value === "boolean") {
|
|
390
|
+
return value;
|
|
391
|
+
}
|
|
392
|
+
if (typeof value === "function" || typeof value === "symbol") {
|
|
393
|
+
return true;
|
|
394
|
+
}
|
|
395
|
+
if (value === null || value === undefined) {
|
|
396
|
+
return false;
|
|
397
|
+
}
|
|
398
|
+
if (typeof value === "number") {
|
|
399
|
+
if (value === 0 || Object.is(value, -0) || isNaN(value)) {
|
|
400
|
+
return false;
|
|
401
|
+
}
|
|
402
|
+
return true;
|
|
403
|
+
}
|
|
404
|
+
if (typeof value === "bigint") {
|
|
405
|
+
return value !== 0n;
|
|
406
|
+
}
|
|
407
|
+
if (typeof value === "string") {
|
|
408
|
+
const normalized = normalizeString(value);
|
|
409
|
+
if (normalized === "") {
|
|
410
|
+
return false;
|
|
411
|
+
}
|
|
412
|
+
if (TRUTHY_STRINGS.includes(normalized)) {
|
|
413
|
+
return true;
|
|
414
|
+
}
|
|
415
|
+
if (FALSY_STRINGS.includes(normalized)) {
|
|
416
|
+
return false;
|
|
417
|
+
}
|
|
418
|
+
throw new Error(`Cannot coerce string "${value}" to boolean`);
|
|
419
|
+
}
|
|
420
|
+
if (Array.isArray(value)) {
|
|
421
|
+
if (value.length === 0) {
|
|
422
|
+
return false;
|
|
423
|
+
}
|
|
424
|
+
if (value.length === 1) {
|
|
425
|
+
return coerceToBoolean(value[0]);
|
|
426
|
+
}
|
|
427
|
+
return true;
|
|
428
|
+
}
|
|
429
|
+
const extracted = extractPrimitiveValue(value, "default");
|
|
430
|
+
if (typeof extracted === "boolean") {
|
|
431
|
+
return extracted;
|
|
432
|
+
}
|
|
433
|
+
if (typeof extracted !== "object" || extracted === null) {
|
|
434
|
+
return coerceToBoolean(extracted);
|
|
435
|
+
}
|
|
436
|
+
return Object.keys(value).length > 0;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// src/coercers/number.ts
|
|
440
|
+
function coerceToNumber(value) {
|
|
441
|
+
if (typeof value === "number")
|
|
442
|
+
return value;
|
|
443
|
+
if (typeof value === "function")
|
|
444
|
+
return NaN;
|
|
445
|
+
if (typeof value === "symbol")
|
|
446
|
+
return NaN;
|
|
447
|
+
if (value === null)
|
|
448
|
+
return 0;
|
|
449
|
+
if (value === undefined)
|
|
450
|
+
return NaN;
|
|
451
|
+
if (typeof value === "boolean")
|
|
452
|
+
return value ? 1 : 0;
|
|
453
|
+
if (typeof value === "bigint") {
|
|
454
|
+
return Number(value);
|
|
455
|
+
}
|
|
456
|
+
if (typeof value === "string") {
|
|
457
|
+
const trimmed = value.trim();
|
|
458
|
+
if (trimmed === "")
|
|
459
|
+
return 0;
|
|
460
|
+
if (trimmed.toLowerCase() === "nan")
|
|
461
|
+
return NaN;
|
|
462
|
+
if (trimmed === "Infinity")
|
|
463
|
+
return Infinity;
|
|
464
|
+
if (trimmed === "-Infinity")
|
|
465
|
+
return -Infinity;
|
|
466
|
+
const parsed = tryParseNumber(trimmed);
|
|
467
|
+
if (parsed !== null)
|
|
468
|
+
return parsed;
|
|
469
|
+
const json = tryParseJSON(trimmed);
|
|
470
|
+
if (typeof json === "number")
|
|
471
|
+
return json;
|
|
472
|
+
return NaN;
|
|
473
|
+
}
|
|
474
|
+
if (Array.isArray(value)) {
|
|
475
|
+
if (value.length === 0)
|
|
476
|
+
return 0;
|
|
477
|
+
if (value.length === 1)
|
|
478
|
+
return coerceToNumber(value[0]);
|
|
479
|
+
return coerceToNumber(value[0]);
|
|
480
|
+
}
|
|
481
|
+
if (value instanceof Date) {
|
|
482
|
+
return value.getTime();
|
|
483
|
+
}
|
|
484
|
+
const extracted = extractPrimitiveValue(value, "number");
|
|
485
|
+
if (typeof extracted === "number")
|
|
486
|
+
return extracted;
|
|
487
|
+
if (typeof extracted !== "object" || extracted === null) {
|
|
488
|
+
return coerceToNumber(extracted);
|
|
489
|
+
}
|
|
490
|
+
return NaN;
|
|
491
|
+
}
|
|
492
|
+
function coerceToInteger(value) {
|
|
493
|
+
const num = coerceToNumber(value);
|
|
494
|
+
if (!isFinite(num))
|
|
495
|
+
return num;
|
|
496
|
+
const truncated = Math.trunc(num);
|
|
497
|
+
if (truncated > Number.MAX_SAFE_INTEGER)
|
|
498
|
+
return Number.MAX_SAFE_INTEGER;
|
|
499
|
+
if (truncated < Number.MIN_SAFE_INTEGER)
|
|
500
|
+
return Number.MIN_SAFE_INTEGER;
|
|
501
|
+
if (Object.is(truncated, -0))
|
|
502
|
+
return 0;
|
|
503
|
+
return truncated;
|
|
504
|
+
}
|
|
505
|
+
function coerceToInt32(value) {
|
|
506
|
+
const num = coerceToNumber(value);
|
|
507
|
+
return num | 0;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// src/coercers/date.ts
|
|
511
|
+
function coerceToDate(value) {
|
|
512
|
+
if (value instanceof Date)
|
|
513
|
+
return value;
|
|
514
|
+
const extracted = extractDateValue(value);
|
|
515
|
+
if (extracted)
|
|
516
|
+
return extracted;
|
|
517
|
+
if (typeof value === "number") {
|
|
518
|
+
return new Date(value);
|
|
519
|
+
}
|
|
520
|
+
if (typeof value === "bigint") {
|
|
521
|
+
return new Date(Number(value));
|
|
522
|
+
}
|
|
523
|
+
if (typeof value === "string") {
|
|
524
|
+
const trimmed = value.trim();
|
|
525
|
+
if (trimmed === "")
|
|
526
|
+
return new Date(NaN);
|
|
527
|
+
const parsed = new Date(trimmed);
|
|
528
|
+
if (!isNaN(parsed.getTime()))
|
|
529
|
+
return parsed;
|
|
530
|
+
const num2 = Number(trimmed);
|
|
531
|
+
if (!isNaN(num2)) {
|
|
532
|
+
if (num2 < 10000000000 && num2 > 0) {
|
|
533
|
+
return new Date(num2 * 1000);
|
|
534
|
+
}
|
|
535
|
+
return new Date(num2);
|
|
536
|
+
}
|
|
537
|
+
const json = tryParseJSON(trimmed);
|
|
538
|
+
if (json !== null) {
|
|
539
|
+
return coerceToDate(json);
|
|
540
|
+
}
|
|
541
|
+
return new Date(NaN);
|
|
542
|
+
}
|
|
543
|
+
if (Array.isArray(value)) {
|
|
544
|
+
if (value.length === 0)
|
|
545
|
+
return new Date(NaN);
|
|
546
|
+
if (value.length === 1) {
|
|
547
|
+
if (Array.isArray(value[0])) {
|
|
548
|
+
return new Date(NaN);
|
|
549
|
+
}
|
|
550
|
+
return coerceToDate(value[0]);
|
|
551
|
+
}
|
|
552
|
+
const parts = value.map(coerceToNumber);
|
|
553
|
+
const [year, month = 0, day = 1, hour = 0, minute = 0, second = 0, ms = 0] = parts;
|
|
554
|
+
return new Date(year, month, day, hour, minute, second, ms);
|
|
555
|
+
}
|
|
556
|
+
if (typeof value === "object" && value !== null) {
|
|
557
|
+
const obj = value;
|
|
558
|
+
if ("timestamp" in obj) {
|
|
559
|
+
return coerceToDate(obj.timestamp);
|
|
560
|
+
}
|
|
561
|
+
if ("year" in obj) {
|
|
562
|
+
if (!("month" in obj)) {
|
|
563
|
+
return new Date(NaN);
|
|
564
|
+
}
|
|
565
|
+
const year = coerceToNumber(obj.year);
|
|
566
|
+
const month = coerceToNumber(obj.month) - 1;
|
|
567
|
+
const day = "day" in obj ? coerceToNumber(obj.day) : 1;
|
|
568
|
+
const hour = "hour" in obj ? coerceToNumber(obj.hour) : 0;
|
|
569
|
+
const minute = "minute" in obj ? coerceToNumber(obj.minute) : 0;
|
|
570
|
+
const second = "second" in obj ? coerceToNumber(obj.second) : 0;
|
|
571
|
+
const ms = "millisecond" in obj ? coerceToNumber(obj.millisecond) : 0;
|
|
572
|
+
return new Date(year, month, day, hour, minute, second, ms);
|
|
573
|
+
}
|
|
574
|
+
if ("date" in obj && typeof obj.date === "string") {
|
|
575
|
+
return coerceToDate(obj.date);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
const num = coerceToNumber(value);
|
|
579
|
+
if (!isNaN(num)) {
|
|
580
|
+
return new Date(num);
|
|
581
|
+
}
|
|
582
|
+
if (typeof value === "object" && value !== null) {
|
|
583
|
+
const obj = value;
|
|
584
|
+
if (typeof obj.toString === "function") {
|
|
585
|
+
try {
|
|
586
|
+
const str = obj.toString();
|
|
587
|
+
if (str && str !== "[object Object]") {
|
|
588
|
+
return coerceToDate(str);
|
|
589
|
+
}
|
|
590
|
+
} catch {}
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
return new Date(NaN);
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
// src/coercers/discriminated-union.ts
|
|
597
|
+
function coerceToDiscriminatedUnion(value, discriminatorKey, options, coerceOption, tracker = new CircularTracker) {
|
|
598
|
+
if (!isPlainObject(value)) {
|
|
599
|
+
throw new Error("Discriminated union requires an object");
|
|
600
|
+
}
|
|
601
|
+
const obj = value;
|
|
602
|
+
if (!(discriminatorKey in obj)) {
|
|
603
|
+
throw new Error(`Missing discriminator key: ${discriminatorKey}`);
|
|
604
|
+
}
|
|
605
|
+
const discriminatorValue = obj[discriminatorKey];
|
|
606
|
+
const optionDiscriminators = [];
|
|
607
|
+
for (const option of options) {
|
|
608
|
+
const optionType = option._def.type || option._def.typeName;
|
|
609
|
+
if (optionType === "object") {
|
|
610
|
+
const shape = typeof option._def.shape === "function" ? option._def.shape() : option._def.shape;
|
|
611
|
+
if (discriminatorKey in shape) {
|
|
612
|
+
const discriminatorSchema = shape[discriminatorKey];
|
|
613
|
+
const discriminatorType = discriminatorSchema._def.type || discriminatorSchema._def.typeName;
|
|
614
|
+
if (discriminatorType === "literal") {
|
|
615
|
+
const value2 = discriminatorSchema._def.values ? discriminatorSchema._def.values[0] : discriminatorSchema._def.value;
|
|
616
|
+
optionDiscriminators.push(value2);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
for (let i = 0;i < optionDiscriminators.length; i++) {
|
|
622
|
+
const expected = optionDiscriminators[i];
|
|
623
|
+
if (discriminatorValue === expected) {
|
|
624
|
+
try {
|
|
625
|
+
return coerceOption(i, value, tracker);
|
|
626
|
+
} catch {}
|
|
627
|
+
}
|
|
628
|
+
if (typeof expected === "string" && typeof discriminatorValue === "string") {
|
|
629
|
+
if (normalizeString(expected) === normalizeString(discriminatorValue)) {
|
|
630
|
+
try {
|
|
631
|
+
const updated = { ...obj, [discriminatorKey]: expected };
|
|
632
|
+
return coerceOption(i, updated, tracker);
|
|
633
|
+
} catch {}
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
if (typeof expected === "number") {
|
|
637
|
+
const coerced = coerceToNumber(discriminatorValue);
|
|
638
|
+
if (coerced === expected) {
|
|
639
|
+
try {
|
|
640
|
+
const updated = { ...obj, [discriminatorKey]: expected };
|
|
641
|
+
return coerceOption(i, updated, tracker);
|
|
642
|
+
} catch {}
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
throw new Error(`No matching discriminated union option for discriminator: ${discriminatorValue}`);
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
// src/coercers/enum.ts
|
|
650
|
+
function coerceToEnum(value, enumValues, enumEntries) {
|
|
651
|
+
if (value === null) {
|
|
652
|
+
for (const enumValue of enumValues) {
|
|
653
|
+
if (enumValue === "null" || enumValue === null) {
|
|
654
|
+
return enumValue;
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
throw new Error(`Cannot coerce null to enum`);
|
|
658
|
+
}
|
|
659
|
+
if (value === undefined) {
|
|
660
|
+
for (const enumValue of enumValues) {
|
|
661
|
+
if (enumValue === "undefined" || enumValue === undefined) {
|
|
662
|
+
return enumValue;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
throw new Error(`Cannot coerce undefined to enum`);
|
|
666
|
+
}
|
|
667
|
+
const isValidEnumIndex = (input) => Number.isInteger(input) && input >= 0 && input < enumValues.length;
|
|
668
|
+
if (Array.isArray(value)) {
|
|
669
|
+
if (value.length === 0) {
|
|
670
|
+
throw new Error(`Cannot coerce empty array to enum`);
|
|
671
|
+
}
|
|
672
|
+
const first = value[0];
|
|
673
|
+
if (typeof first === "number" && isValidEnumIndex(first)) {
|
|
674
|
+
return enumValues[first];
|
|
675
|
+
}
|
|
676
|
+
if (value.length >= 1)
|
|
677
|
+
return coerceToEnum(first, enumValues, enumEntries);
|
|
678
|
+
}
|
|
679
|
+
if (enumEntries && typeof value === "string") {
|
|
680
|
+
if (value in enumEntries) {
|
|
681
|
+
const result = enumEntries[value];
|
|
682
|
+
if (enumValues.includes(result)) {
|
|
683
|
+
return result;
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
const normalized = normalizeString(value);
|
|
687
|
+
for (const key of Object.keys(enumEntries)) {
|
|
688
|
+
if (normalizeString(key) === normalized) {
|
|
689
|
+
const result = enumEntries[key];
|
|
690
|
+
if (enumValues.includes(result)) {
|
|
691
|
+
return result;
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
for (const enumValue of enumValues) {
|
|
697
|
+
if (typeof enumValue === "string" && typeof value === "string") {
|
|
698
|
+
if (normalizeString(enumValue) === normalizeString(value)) {
|
|
699
|
+
return enumValue;
|
|
700
|
+
}
|
|
701
|
+
} else if (enumValue === value) {
|
|
702
|
+
return enumValue;
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
const allNumeric = enumValues.every((v) => typeof v === "number");
|
|
706
|
+
if (!allNumeric && (typeof value === "number" || typeof value === "boolean")) {
|
|
707
|
+
const index = typeof value === "boolean" ? value ? 1 : 0 : value;
|
|
708
|
+
if (Number.isInteger(index) && index >= 0 && index < enumValues.length) {
|
|
709
|
+
return enumValues[index];
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
if (typeof value === "string" && value.trim() !== "") {
|
|
713
|
+
const num = coerceToNumber(value);
|
|
714
|
+
if (!isNaN(num)) {
|
|
715
|
+
if (enumValues.includes(num)) {
|
|
716
|
+
return num;
|
|
717
|
+
}
|
|
718
|
+
if (!allNumeric && isValidEnumIndex(num))
|
|
719
|
+
return enumValues[num];
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
if (!allNumeric && typeof value === "bigint") {
|
|
723
|
+
const num = Number(value);
|
|
724
|
+
if (Number.isInteger(num) && num >= 0 && num < enumValues.length) {
|
|
725
|
+
return enumValues[num];
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
throw new Error(`Cannot coerce value to enum`);
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
// src/coercers/intersection.ts
|
|
732
|
+
function coerceToIntersection(value, left, right, coerceLeft, coerceRight, tracker = new CircularTracker) {
|
|
733
|
+
const leftTypeName = left._def.type || left._def.typeName;
|
|
734
|
+
const rightTypeName = right._def.type || right._def.typeName;
|
|
735
|
+
const leftIsObjectLike = leftTypeName === "object" || leftTypeName === "intersection";
|
|
736
|
+
const rightIsObjectLike = rightTypeName === "object" || rightTypeName === "intersection";
|
|
737
|
+
if (leftIsObjectLike && rightIsObjectLike) {
|
|
738
|
+
const leftResult = coerceLeft(value, tracker);
|
|
739
|
+
return coerceRight(leftResult, tracker);
|
|
740
|
+
}
|
|
741
|
+
if (leftTypeName === "record" && rightTypeName === "object" || leftTypeName === "object" && rightTypeName === "record") {
|
|
742
|
+
const leftResult = coerceLeft(value, tracker);
|
|
743
|
+
return coerceRight(leftResult, tracker);
|
|
744
|
+
}
|
|
745
|
+
if (leftTypeName === "union" && rightTypeName === "union") {
|
|
746
|
+
const leftResult = coerceLeft(value, tracker);
|
|
747
|
+
const rightValidation = right.safeParse(leftResult);
|
|
748
|
+
if (rightValidation.success) {
|
|
749
|
+
return leftResult;
|
|
750
|
+
}
|
|
751
|
+
const rightResult = coerceRight(value, tracker);
|
|
752
|
+
const leftValidation = left.safeParse(rightResult);
|
|
753
|
+
if (leftValidation.success) {
|
|
754
|
+
return rightResult;
|
|
755
|
+
}
|
|
756
|
+
return value;
|
|
757
|
+
}
|
|
758
|
+
if (leftTypeName === "literal" && rightTypeName === "string" || leftTypeName === "string" && rightTypeName === "literal") {
|
|
759
|
+
const literalSchema = leftTypeName === "literal" ? left : right;
|
|
760
|
+
const literalValues = literalSchema._def.values;
|
|
761
|
+
const literalValue = Array.isArray(literalValues) ? literalValues[0] : literalValues;
|
|
762
|
+
if (typeof value === "string" && typeof literalValue === "string") {
|
|
763
|
+
if (value.toLowerCase() === literalValue.toLowerCase()) {
|
|
764
|
+
return literalValue;
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
return value;
|
|
768
|
+
}
|
|
769
|
+
if (leftTypeName === "string" && rightTypeName === "object" || leftTypeName === "object" && rightTypeName === "string") {
|
|
770
|
+
return value;
|
|
771
|
+
}
|
|
772
|
+
if (leftTypeName === "array" && rightTypeName === "object" || leftTypeName === "object" && rightTypeName === "array") {
|
|
773
|
+
return value;
|
|
774
|
+
}
|
|
775
|
+
return value;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
// src/coercers/string.ts
|
|
779
|
+
function coerceToString(value, tracker = new CircularTracker) {
|
|
780
|
+
if (canBeCircular(value) && tracker.has(value)) {
|
|
781
|
+
throwCircular();
|
|
782
|
+
}
|
|
783
|
+
if (typeof value === "string")
|
|
784
|
+
return value;
|
|
785
|
+
if (value === null)
|
|
786
|
+
return "";
|
|
787
|
+
if (value === undefined)
|
|
788
|
+
return "";
|
|
789
|
+
const skipExtract = Array.isArray(value) || value instanceof Set || value instanceof Map || ArrayBuffer.isView(value);
|
|
790
|
+
if (!skipExtract) {
|
|
791
|
+
const extracted = extractPrimitiveValue(value, "string");
|
|
792
|
+
if (typeof extracted === "string")
|
|
793
|
+
return extracted;
|
|
794
|
+
if (typeof extracted !== "object" || extracted === null) {
|
|
795
|
+
return String(extracted);
|
|
796
|
+
}
|
|
797
|
+
if (extracted !== value) {
|
|
798
|
+
return coerceToString(extracted, tracker);
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
if (canBeCircular(value)) {
|
|
802
|
+
tracker.add(value);
|
|
803
|
+
}
|
|
804
|
+
if (typeof value === "bigint")
|
|
805
|
+
return value.toString();
|
|
806
|
+
if (typeof value === "symbol")
|
|
807
|
+
return value.toString();
|
|
808
|
+
if (typeof value === "function")
|
|
809
|
+
return value.toString();
|
|
810
|
+
if (value instanceof RegExp)
|
|
811
|
+
return value.toString();
|
|
812
|
+
if (value instanceof Error)
|
|
813
|
+
return value.message || value.toString();
|
|
814
|
+
if (Array.isArray(value)) {
|
|
815
|
+
if (value.toString !== Array.prototype.toString) {
|
|
816
|
+
return value.toString();
|
|
817
|
+
}
|
|
818
|
+
return value.map((v) => {
|
|
819
|
+
if (Array.isArray(v)) {
|
|
820
|
+
return JSON.stringify(v).replace(/,/g, ", ");
|
|
821
|
+
}
|
|
822
|
+
if (typeof v === "object" && v !== null && !(v instanceof Date) && !(v instanceof RegExp) && !(v instanceof Error)) {
|
|
823
|
+
return String(v);
|
|
824
|
+
}
|
|
825
|
+
return coerceToString(v, tracker);
|
|
826
|
+
}).join(", ");
|
|
827
|
+
}
|
|
828
|
+
if (value instanceof Map) {
|
|
829
|
+
const entries = Array.from(value.entries());
|
|
830
|
+
return entries.map(([k, v]) => `${coerceToString(k, tracker)}: ${coerceToString(v, tracker)}`).join(", ");
|
|
831
|
+
}
|
|
832
|
+
if (value instanceof Set) {
|
|
833
|
+
return Array.from(value).map((v) => coerceToString(v, tracker)).join(", ");
|
|
834
|
+
}
|
|
835
|
+
if (ArrayBuffer.isView(value)) {
|
|
836
|
+
return Array.from(value).join(", ");
|
|
837
|
+
}
|
|
838
|
+
return isPlainObject(value) ? formatPlainObject(value, tracker) : String(value);
|
|
839
|
+
}
|
|
840
|
+
function formatPlainObject(value, tracker) {
|
|
841
|
+
return Object.entries(value).filter(([_, v]) => v !== null && v !== undefined && v !== "").map(([k, v]) => {
|
|
842
|
+
if (typeof v === "object" && v !== null && tracker.has(v)) {
|
|
843
|
+
throwCircular();
|
|
844
|
+
}
|
|
845
|
+
if (typeof v === "object" && v !== null && !(v instanceof Date) && !(v instanceof RegExp) && !(v instanceof Error)) {
|
|
846
|
+
return `${k}: ${String(v)}`;
|
|
847
|
+
}
|
|
848
|
+
return `${k}: ${coerceToString(v, tracker)}`;
|
|
849
|
+
}).join(", ");
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
// src/coercers/literal.ts
|
|
853
|
+
function coerceToLiteral(value, literalValue) {
|
|
854
|
+
if (value === literalValue)
|
|
855
|
+
return literalValue;
|
|
856
|
+
if (typeof literalValue === "string") {
|
|
857
|
+
if (literalValue === "" && value === null) {
|
|
858
|
+
return literalValue;
|
|
859
|
+
}
|
|
860
|
+
const coerced = coerceToString(value);
|
|
861
|
+
if (normalizeString(coerced) === normalizeString(literalValue)) {
|
|
862
|
+
return literalValue;
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
if (typeof literalValue === "number") {
|
|
866
|
+
const coerced = coerceToNumber(value);
|
|
867
|
+
const matchesNumber = coerced === literalValue || isNaN(coerced) && isNaN(literalValue);
|
|
868
|
+
const matchesSignedZero = Object.is(coerced, literalValue);
|
|
869
|
+
if (matchesNumber || matchesSignedZero) {
|
|
870
|
+
return literalValue;
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
if (typeof literalValue === "boolean") {
|
|
874
|
+
const coerced = coerceToBoolean(value);
|
|
875
|
+
if (coerced === literalValue) {
|
|
876
|
+
return literalValue;
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
if (typeof literalValue === "bigint") {
|
|
880
|
+
try {
|
|
881
|
+
const coerced = coerceToBigInt(value);
|
|
882
|
+
if (coerced === literalValue) {
|
|
883
|
+
return literalValue;
|
|
884
|
+
}
|
|
885
|
+
} catch {}
|
|
886
|
+
}
|
|
887
|
+
if (literalValue === null) {
|
|
888
|
+
if (isFalsyForNull(value)) {
|
|
889
|
+
return null;
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
throw new Error(`Cannot coerce value to literal ${literalValue}`);
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
// src/coercers/map-set.ts
|
|
896
|
+
function coerceToMap(value, keySchema, valueSchema, coerceKey, coerceValue, tracker = new CircularTracker) {
|
|
897
|
+
if (canBeCircular(value) && tracker.has(value)) {
|
|
898
|
+
throwCircular();
|
|
899
|
+
}
|
|
900
|
+
if (canBeCircular(value)) {
|
|
901
|
+
tracker.add(value);
|
|
902
|
+
}
|
|
903
|
+
if (value instanceof Map) {
|
|
904
|
+
const result = new Map;
|
|
905
|
+
for (const [k, v] of value.entries()) {
|
|
906
|
+
result.set(coerceKey(k, tracker), coerceValue(v, tracker));
|
|
907
|
+
}
|
|
908
|
+
return result;
|
|
909
|
+
}
|
|
910
|
+
if (isPlainObject(value)) {
|
|
911
|
+
const result = new Map;
|
|
912
|
+
for (const [k, v] of Object.entries(value)) {
|
|
913
|
+
result.set(coerceKey(k, tracker), coerceValue(v, tracker));
|
|
914
|
+
}
|
|
915
|
+
return result;
|
|
916
|
+
}
|
|
917
|
+
if (Array.isArray(value)) {
|
|
918
|
+
if (value.every((item) => Array.isArray(item) && item.length === 2)) {
|
|
919
|
+
const result = new Map;
|
|
920
|
+
for (const [k, v] of value) {
|
|
921
|
+
result.set(coerceKey(k, tracker), coerceValue(v, tracker));
|
|
922
|
+
}
|
|
923
|
+
return result;
|
|
924
|
+
}
|
|
925
|
+
throw new Error("Cannot convert non-pair array to Map");
|
|
926
|
+
}
|
|
927
|
+
if (typeof value === "string")
|
|
928
|
+
return coerceMapFromString(value, keySchema, valueSchema, coerceKey, coerceValue, tracker);
|
|
929
|
+
throw new Error("Cannot convert value to Map");
|
|
930
|
+
}
|
|
931
|
+
function coerceToSet(value, elementSchema, coerceElement, tracker = new CircularTracker) {
|
|
932
|
+
if (canBeCircular(value) && tracker.has(value)) {
|
|
933
|
+
throwCircular();
|
|
934
|
+
}
|
|
935
|
+
if (canBeCircular(value)) {
|
|
936
|
+
tracker.add(value);
|
|
937
|
+
}
|
|
938
|
+
if (value instanceof Set) {
|
|
939
|
+
const result = new Set;
|
|
940
|
+
for (const item of value) {
|
|
941
|
+
result.add(coerceElement(item, tracker));
|
|
942
|
+
}
|
|
943
|
+
return result;
|
|
944
|
+
}
|
|
945
|
+
if (Array.isArray(value)) {
|
|
946
|
+
const result = new Set;
|
|
947
|
+
for (const item of value) {
|
|
948
|
+
result.add(coerceElement(item, tracker));
|
|
949
|
+
}
|
|
950
|
+
return result;
|
|
951
|
+
}
|
|
952
|
+
if (typeof value === "string")
|
|
953
|
+
return coerceSetFromString(value, elementSchema, coerceElement, tracker);
|
|
954
|
+
if (isPlainObject(value)) {
|
|
955
|
+
const result = new Set;
|
|
956
|
+
for (const val of Object.values(value)) {
|
|
957
|
+
result.add(coerceElement(val, tracker));
|
|
958
|
+
}
|
|
959
|
+
return result;
|
|
960
|
+
}
|
|
961
|
+
throw new Error("Cannot convert value to Set");
|
|
962
|
+
}
|
|
963
|
+
function coerceMapFromString(raw, keySchema, valueSchema, coerceKey, coerceValue, tracker) {
|
|
964
|
+
const json = tryParseJSON(raw);
|
|
965
|
+
if (json === null) {
|
|
966
|
+
throw new Error("Cannot convert string to Map");
|
|
967
|
+
}
|
|
968
|
+
return coerceToMap(json, keySchema, valueSchema, coerceKey, coerceValue, tracker);
|
|
969
|
+
}
|
|
970
|
+
function coerceSetFromString(raw, elementSchema, coerceElement, tracker) {
|
|
971
|
+
const trimmed = raw.trim();
|
|
972
|
+
if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
|
|
973
|
+
const json = tryParseJSON(trimmed);
|
|
974
|
+
if (!Array.isArray(json)) {
|
|
975
|
+
throw new Error("Cannot convert string to Set");
|
|
976
|
+
}
|
|
977
|
+
return coerceToSet(json, elementSchema, coerceElement, tracker);
|
|
978
|
+
}
|
|
979
|
+
const csv = tryParseCSV(trimmed);
|
|
980
|
+
if (csv) {
|
|
981
|
+
const result = new Set;
|
|
982
|
+
for (const item of csv) {
|
|
983
|
+
result.add(coerceElement(item, tracker));
|
|
984
|
+
}
|
|
985
|
+
return result;
|
|
986
|
+
}
|
|
987
|
+
throw new Error("Cannot convert string to Set");
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
// src/coercers/nan.ts
|
|
991
|
+
function coerceToNaN(value) {
|
|
992
|
+
if (typeof value === "number" && isNaN(value))
|
|
993
|
+
return NaN;
|
|
994
|
+
if (value === undefined)
|
|
995
|
+
return NaN;
|
|
996
|
+
if (typeof value === "function")
|
|
997
|
+
return NaN;
|
|
998
|
+
if (typeof value === "symbol")
|
|
999
|
+
return NaN;
|
|
1000
|
+
if (typeof value === "string") {
|
|
1001
|
+
const trimmed = value.trim().toLowerCase();
|
|
1002
|
+
if (trimmed === "nan")
|
|
1003
|
+
return NaN;
|
|
1004
|
+
const num = Number(trimmed);
|
|
1005
|
+
if (isNaN(num))
|
|
1006
|
+
return NaN;
|
|
1007
|
+
throw new Error("Cannot coerce valid number to NaN");
|
|
1008
|
+
}
|
|
1009
|
+
if (Array.isArray(value) || typeof value === "object") {
|
|
1010
|
+
const num = Number(value);
|
|
1011
|
+
if (isNaN(num))
|
|
1012
|
+
return NaN;
|
|
1013
|
+
throw new Error("Cannot coerce valid value to NaN");
|
|
1014
|
+
}
|
|
1015
|
+
throw new Error("Cannot coerce valid value to NaN");
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
// src/coercers/null.ts
|
|
1019
|
+
function coerceToNull(value) {
|
|
1020
|
+
if (value === null)
|
|
1021
|
+
return null;
|
|
1022
|
+
if (isFalsyForNull(value))
|
|
1023
|
+
return null;
|
|
1024
|
+
throw new Error("Cannot coerce non-falsy value to null");
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
// src/coercers/object.ts
|
|
1028
|
+
function coerceToObject(value, shape, coerceProperty, tracker = new CircularTracker) {
|
|
1029
|
+
if (canBeCircular(value) && tracker.has(value)) {
|
|
1030
|
+
throwCircular();
|
|
1031
|
+
}
|
|
1032
|
+
if (canBeCircular(value)) {
|
|
1033
|
+
tracker.add(value);
|
|
1034
|
+
}
|
|
1035
|
+
if (isPlainObject(value)) {
|
|
1036
|
+
const extracted = extractPrimitiveValue(value, "default");
|
|
1037
|
+
if (extracted !== value) {
|
|
1038
|
+
return coerceToObject(extracted, shape, coerceProperty, tracker);
|
|
1039
|
+
}
|
|
1040
|
+
const result = {};
|
|
1041
|
+
for (const [key, val] of Object.entries(value)) {
|
|
1042
|
+
result[key] = coerceProperty(key, val, tracker);
|
|
1043
|
+
}
|
|
1044
|
+
return result;
|
|
1045
|
+
}
|
|
1046
|
+
if (typeof value === "string") {
|
|
1047
|
+
const trimmed = value.trim();
|
|
1048
|
+
if (trimmed === "")
|
|
1049
|
+
return {};
|
|
1050
|
+
const json = tryParseJSON(trimmed);
|
|
1051
|
+
if (isPlainObject(json)) {
|
|
1052
|
+
return coerceToObject(json, shape, coerceProperty, tracker);
|
|
1053
|
+
}
|
|
1054
|
+
throw new Error("Cannot convert string to object");
|
|
1055
|
+
}
|
|
1056
|
+
if (Array.isArray(value)) {
|
|
1057
|
+
if (value.length === 0)
|
|
1058
|
+
return {};
|
|
1059
|
+
if (value.every((item) => Array.isArray(item) && item.length === 2)) {
|
|
1060
|
+
const result2 = {};
|
|
1061
|
+
for (const [key, val] of value) {
|
|
1062
|
+
const keyStr = String(key);
|
|
1063
|
+
result2[keyStr] = coerceProperty(keyStr, val, tracker);
|
|
1064
|
+
}
|
|
1065
|
+
return result2;
|
|
1066
|
+
}
|
|
1067
|
+
const result = {};
|
|
1068
|
+
value.forEach((val, i) => {
|
|
1069
|
+
const key = String(i);
|
|
1070
|
+
result[key] = coerceProperty(key, val, tracker);
|
|
1071
|
+
});
|
|
1072
|
+
return result;
|
|
1073
|
+
}
|
|
1074
|
+
if (value instanceof Map) {
|
|
1075
|
+
const result = {};
|
|
1076
|
+
for (const [key, val] of value.entries()) {
|
|
1077
|
+
const keyStr = String(key);
|
|
1078
|
+
result[keyStr] = coerceProperty(keyStr, val, tracker);
|
|
1079
|
+
}
|
|
1080
|
+
return result;
|
|
1081
|
+
}
|
|
1082
|
+
if (value instanceof Set) {
|
|
1083
|
+
const result = {};
|
|
1084
|
+
let index = 0;
|
|
1085
|
+
for (const val of value) {
|
|
1086
|
+
const key = String(index);
|
|
1087
|
+
result[key] = coerceProperty(key, val, tracker);
|
|
1088
|
+
index++;
|
|
1089
|
+
}
|
|
1090
|
+
return result;
|
|
1091
|
+
}
|
|
1092
|
+
if (value === null || value === undefined)
|
|
1093
|
+
return {};
|
|
1094
|
+
if (typeof value === "object") {
|
|
1095
|
+
const result = {};
|
|
1096
|
+
for (const key of Object.keys(value)) {
|
|
1097
|
+
result[key] = coerceProperty(key, value[key], tracker);
|
|
1098
|
+
}
|
|
1099
|
+
return result;
|
|
1100
|
+
}
|
|
1101
|
+
throw new Error("Cannot convert primitive to object");
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
// src/coercers/record.ts
|
|
1105
|
+
function coerceToRecord(value, valueSchema, coerceValue, tracker = new CircularTracker) {
|
|
1106
|
+
if (canBeCircular(value) && tracker.has(value)) {
|
|
1107
|
+
throwCircular();
|
|
1108
|
+
}
|
|
1109
|
+
if (canBeCircular(value)) {
|
|
1110
|
+
tracker.add(value);
|
|
1111
|
+
}
|
|
1112
|
+
if (isPlainObject(value)) {
|
|
1113
|
+
const result = {};
|
|
1114
|
+
for (const [key, val] of Object.entries(value)) {
|
|
1115
|
+
if (canBeCircular(val) && tracker.has(val)) {
|
|
1116
|
+
throwCircular();
|
|
1117
|
+
}
|
|
1118
|
+
result[key] = coerceValue(val, tracker);
|
|
1119
|
+
}
|
|
1120
|
+
return result;
|
|
1121
|
+
}
|
|
1122
|
+
if (typeof value === "string") {
|
|
1123
|
+
const trimmed = value.trim();
|
|
1124
|
+
if (trimmed === "")
|
|
1125
|
+
return {};
|
|
1126
|
+
const json = tryParseJSON(trimmed);
|
|
1127
|
+
if (isPlainObject(json)) {
|
|
1128
|
+
return coerceToRecord(json, valueSchema, coerceValue, tracker);
|
|
1129
|
+
}
|
|
1130
|
+
throw new Error("Cannot convert string to record");
|
|
1131
|
+
}
|
|
1132
|
+
if (Array.isArray(value)) {
|
|
1133
|
+
if (value.length === 0)
|
|
1134
|
+
return {};
|
|
1135
|
+
if (value.every((item) => Array.isArray(item) && item.length === 2)) {
|
|
1136
|
+
const result2 = {};
|
|
1137
|
+
for (const [key, val] of value) {
|
|
1138
|
+
if (canBeCircular(val) && tracker.has(val)) {
|
|
1139
|
+
throwCircular();
|
|
1140
|
+
}
|
|
1141
|
+
result2[String(key)] = coerceValue(val, tracker);
|
|
1142
|
+
}
|
|
1143
|
+
return result2;
|
|
1144
|
+
}
|
|
1145
|
+
const result = {};
|
|
1146
|
+
value.forEach((val, index) => {
|
|
1147
|
+
if (canBeCircular(val) && tracker.has(val)) {
|
|
1148
|
+
throwCircular();
|
|
1149
|
+
}
|
|
1150
|
+
result[String(index)] = coerceValue(val, tracker);
|
|
1151
|
+
});
|
|
1152
|
+
return result;
|
|
1153
|
+
}
|
|
1154
|
+
if (value instanceof Map) {
|
|
1155
|
+
const result = {};
|
|
1156
|
+
for (const [key, val] of value.entries()) {
|
|
1157
|
+
if (canBeCircular(val) && tracker.has(val)) {
|
|
1158
|
+
throwCircular();
|
|
1159
|
+
}
|
|
1160
|
+
result[String(key)] = coerceValue(val, tracker);
|
|
1161
|
+
}
|
|
1162
|
+
return result;
|
|
1163
|
+
}
|
|
1164
|
+
if (value instanceof Set) {
|
|
1165
|
+
const result = {};
|
|
1166
|
+
let index = 0;
|
|
1167
|
+
for (const val of value) {
|
|
1168
|
+
if (canBeCircular(val) && tracker.has(val)) {
|
|
1169
|
+
throwCircular();
|
|
1170
|
+
}
|
|
1171
|
+
result[String(index)] = coerceValue(val, tracker);
|
|
1172
|
+
index++;
|
|
1173
|
+
}
|
|
1174
|
+
return result;
|
|
1175
|
+
}
|
|
1176
|
+
if (value === null || value === undefined)
|
|
1177
|
+
return {};
|
|
1178
|
+
if (typeof value === "object") {
|
|
1179
|
+
const result = {};
|
|
1180
|
+
for (const key of Object.keys(value)) {
|
|
1181
|
+
const val = value[key];
|
|
1182
|
+
if (canBeCircular(val) && tracker.has(val)) {
|
|
1183
|
+
throwCircular();
|
|
1184
|
+
}
|
|
1185
|
+
result[key] = coerceValue(val, tracker);
|
|
1186
|
+
}
|
|
1187
|
+
return result;
|
|
1188
|
+
}
|
|
1189
|
+
throw new Error("Cannot convert primitive to record");
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
// src/coercers/tuple.ts
|
|
1193
|
+
function coerceToTuple(value, items, coerceElement, tracker = new CircularTracker) {
|
|
1194
|
+
if (canBeCircular(value) && tracker.has(value)) {
|
|
1195
|
+
throwCircular();
|
|
1196
|
+
}
|
|
1197
|
+
if (Array.isArray(value)) {
|
|
1198
|
+
tracker.add(value);
|
|
1199
|
+
const result = [];
|
|
1200
|
+
for (let i = 0;i < items.length; i++) {
|
|
1201
|
+
if (i < value.length) {
|
|
1202
|
+
result.push(coerceElement(i, value[i], tracker));
|
|
1203
|
+
} else {
|
|
1204
|
+
result.push(undefined);
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
return result;
|
|
1208
|
+
}
|
|
1209
|
+
if (canBeCircular(value)) {
|
|
1210
|
+
tracker.add(value);
|
|
1211
|
+
}
|
|
1212
|
+
if (typeof value === "string") {
|
|
1213
|
+
const trimmed = value.trim();
|
|
1214
|
+
if (trimmed === "" || trimmed === "null") {
|
|
1215
|
+
const result = [];
|
|
1216
|
+
for (let i = 0;i < items.length; i++) {
|
|
1217
|
+
result.push(undefined);
|
|
1218
|
+
}
|
|
1219
|
+
return result;
|
|
1220
|
+
}
|
|
1221
|
+
const json = tryParseJSON(trimmed);
|
|
1222
|
+
if (Array.isArray(json)) {
|
|
1223
|
+
return coerceToTuple(json, items, coerceElement, tracker);
|
|
1224
|
+
}
|
|
1225
|
+
const csv = tryParseCSV(trimmed);
|
|
1226
|
+
if (csv) {
|
|
1227
|
+
return coerceToTuple(csv, items, coerceElement, tracker);
|
|
1228
|
+
}
|
|
1229
|
+
throw new Error("Cannot convert non-array string to tuple");
|
|
1230
|
+
}
|
|
1231
|
+
if (value === null) {
|
|
1232
|
+
const result = [];
|
|
1233
|
+
for (let i = 0;i < items.length; i++) {
|
|
1234
|
+
result.push(undefined);
|
|
1235
|
+
}
|
|
1236
|
+
return result;
|
|
1237
|
+
}
|
|
1238
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
1239
|
+
const obj = value;
|
|
1240
|
+
if (isArrayLike(value)) {
|
|
1241
|
+
const result = [];
|
|
1242
|
+
for (let i = 0;i < items.length; i++) {
|
|
1243
|
+
if (i in obj) {
|
|
1244
|
+
result.push(coerceElement(i, obj[i], tracker));
|
|
1245
|
+
} else {
|
|
1246
|
+
result.push(undefined);
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
return result;
|
|
1250
|
+
}
|
|
1251
|
+
let hasNumericKeys = false;
|
|
1252
|
+
for (let i = 0;i < items.length; i++) {
|
|
1253
|
+
if (i in obj) {
|
|
1254
|
+
hasNumericKeys = true;
|
|
1255
|
+
break;
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
if (hasNumericKeys)
|
|
1259
|
+
return items.map((_, i) => (i in obj) ? coerceElement(i, obj[i], tracker) : undefined);
|
|
1260
|
+
}
|
|
1261
|
+
throw new Error("Cannot convert non-array to tuple");
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
// src/coercers/union.ts
|
|
1265
|
+
function coerceToUnion(value, options, coerceOption, tracker = new CircularTracker) {
|
|
1266
|
+
const errors = [];
|
|
1267
|
+
const isPrimitive = typeof value !== "object" || value === null;
|
|
1268
|
+
const isString = typeof value === "string";
|
|
1269
|
+
const isDate = value instanceof Date;
|
|
1270
|
+
if (isPrimitive && !isString || isDate) {
|
|
1271
|
+
for (let i = 0;i < options.length; i++) {
|
|
1272
|
+
const directResult = options[i].safeParse(value);
|
|
1273
|
+
if (directResult.success) {
|
|
1274
|
+
return value;
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
const objectCandidate = tryObjectFirstCoercion(value, options, tracker, coerceOption);
|
|
1279
|
+
if (objectCandidate !== undefined) {
|
|
1280
|
+
return objectCandidate;
|
|
1281
|
+
}
|
|
1282
|
+
if (Array.isArray(value)) {
|
|
1283
|
+
for (let i = 0;i < options.length; i++) {
|
|
1284
|
+
const option = options[i];
|
|
1285
|
+
const type = option._def?.type || option._def.typeName;
|
|
1286
|
+
if (type === "array" || type === "ZodArray") {
|
|
1287
|
+
try {
|
|
1288
|
+
const coerced = coerceOption(i, value, tracker);
|
|
1289
|
+
const coercedResult = options[i].safeParse(coerced);
|
|
1290
|
+
if (coercedResult.success) {
|
|
1291
|
+
return coerced;
|
|
1292
|
+
}
|
|
1293
|
+
} catch {}
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
if (typeof value === "string" && value.trim() !== "") {
|
|
1298
|
+
const trimmed = value.trim();
|
|
1299
|
+
if (trimmed.includes(",") || trimmed.startsWith("[") && trimmed.endsWith("]")) {
|
|
1300
|
+
for (let i = 0;i < options.length; i++) {
|
|
1301
|
+
const option = options[i];
|
|
1302
|
+
const type = option._def?.type || option._def.typeName;
|
|
1303
|
+
if (type === "array" || type === "ZodArray") {
|
|
1304
|
+
try {
|
|
1305
|
+
const coerced = coerceOption(i, value, tracker);
|
|
1306
|
+
const coercedResult = options[i].safeParse(coerced);
|
|
1307
|
+
if (coercedResult.success) {
|
|
1308
|
+
return coerced;
|
|
1309
|
+
}
|
|
1310
|
+
} catch {}
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
if (typeof value === "string" && value.trim() !== "") {
|
|
1316
|
+
const trimmed = value.trim().toLowerCase();
|
|
1317
|
+
const nonNumericBooleanStrings = [
|
|
1318
|
+
"true",
|
|
1319
|
+
"false",
|
|
1320
|
+
"yes",
|
|
1321
|
+
"no",
|
|
1322
|
+
"on",
|
|
1323
|
+
"off",
|
|
1324
|
+
"y",
|
|
1325
|
+
"n",
|
|
1326
|
+
"t",
|
|
1327
|
+
"f",
|
|
1328
|
+
"enabled",
|
|
1329
|
+
"disabled"
|
|
1330
|
+
];
|
|
1331
|
+
if (nonNumericBooleanStrings.includes(trimmed)) {
|
|
1332
|
+
for (let i = 0;i < options.length; i++) {
|
|
1333
|
+
const option = options[i];
|
|
1334
|
+
const type = option._def?.type || option._def.typeName;
|
|
1335
|
+
if (type === "boolean") {
|
|
1336
|
+
try {
|
|
1337
|
+
const coerced = coerceOption(i, value, tracker);
|
|
1338
|
+
const coercedResult = options[i].safeParse(coerced);
|
|
1339
|
+
if (coercedResult.success) {
|
|
1340
|
+
return coerced;
|
|
1341
|
+
}
|
|
1342
|
+
} catch {}
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
if (options.length > 0) {
|
|
1348
|
+
const attempt = executeUnionAttempt(0, value, options, tracker, coerceOption);
|
|
1349
|
+
if (attempt.status === "success") {
|
|
1350
|
+
return attempt.value;
|
|
1351
|
+
}
|
|
1352
|
+
if (attempt.status === "validation") {
|
|
1353
|
+
errors.push(new Error(`Coercion to first type failed validation`));
|
|
1354
|
+
} else if (attempt.error) {
|
|
1355
|
+
errors.push(attempt.error);
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
for (let i = 1;i < options.length; i++) {
|
|
1359
|
+
const attempt = executeUnionAttempt(i, value, options, tracker, coerceOption);
|
|
1360
|
+
if (attempt.status === "success") {
|
|
1361
|
+
return attempt.value;
|
|
1362
|
+
}
|
|
1363
|
+
if (attempt.status === "validation") {
|
|
1364
|
+
errors.push(new Error(`Coerced value does not match schema`));
|
|
1365
|
+
} else if (attempt.error) {
|
|
1366
|
+
errors.push(attempt.error);
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
throw new Error(`Cannot coerce value to any union type: ${errors.map((e) => e.message).join(", ")}`);
|
|
1370
|
+
}
|
|
1371
|
+
function tryObjectFirstCoercion(value, options, _tracker, coerceOption) {
|
|
1372
|
+
if (typeof value !== "object" || value === null || Array.isArray(value) || value instanceof Date) {
|
|
1373
|
+
return;
|
|
1374
|
+
}
|
|
1375
|
+
for (let i = 0;i < options.length; i++) {
|
|
1376
|
+
const option = options[i];
|
|
1377
|
+
const type = option._def?.type || option._def.typeName;
|
|
1378
|
+
if (type === "object" || type === "ZodObject" || type === "record") {
|
|
1379
|
+
try {
|
|
1380
|
+
const attemptTracker = new CircularTracker;
|
|
1381
|
+
const coerced = coerceOption(i, value, attemptTracker);
|
|
1382
|
+
const coercedResult = options[i].safeParse(coerced);
|
|
1383
|
+
if (coercedResult.success) {
|
|
1384
|
+
return coerced;
|
|
1385
|
+
}
|
|
1386
|
+
} catch {}
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
return;
|
|
1390
|
+
}
|
|
1391
|
+
function executeUnionAttempt(index, value, options, tracker, coerceOption) {
|
|
1392
|
+
try {
|
|
1393
|
+
const coerced = coerceOption(index, value, tracker);
|
|
1394
|
+
const coercedResult = options[index].safeParse(coerced);
|
|
1395
|
+
if (coercedResult.success) {
|
|
1396
|
+
return { status: "success", value: coerced };
|
|
1397
|
+
}
|
|
1398
|
+
return { status: "validation" };
|
|
1399
|
+
} catch (error) {
|
|
1400
|
+
if (error instanceof Error && error.message.includes("Circular reference")) {
|
|
1401
|
+
throw error;
|
|
1402
|
+
}
|
|
1403
|
+
return { status: "error", error };
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
// src/create-schema.ts
|
|
1407
|
+
import * as z from "zod";
|
|
1408
|
+
var primitiveFactories = {
|
|
1409
|
+
string: () => z.coerce.string(),
|
|
1410
|
+
number: () => z.coerce.number(),
|
|
1411
|
+
boolean: () => getStringBool(),
|
|
1412
|
+
null: () => getNullCoercion()
|
|
1413
|
+
};
|
|
1414
|
+
function createCanonizePrimitive(primitive) {
|
|
1415
|
+
return primitiveFactories[primitive]();
|
|
1416
|
+
}
|
|
1417
|
+
function createCanonizeSchema(schema) {
|
|
1418
|
+
const shape = Object.fromEntries(Object.entries(schema).map(([key, primitive]) => [
|
|
1419
|
+
key,
|
|
1420
|
+
createCanonizePrimitive(primitive)
|
|
1421
|
+
]));
|
|
1422
|
+
return z.object(shape);
|
|
1423
|
+
}
|
|
1424
|
+
var coerceHelpers = z.coerce;
|
|
1425
|
+
var stringBoolFactory = typeof coerceHelpers.stringbool === "function" ? () => coerceHelpers.stringbool() : () => z.preprocess((value) => coerceToBoolean(value), z.boolean());
|
|
1426
|
+
var nullFactory = typeof coerceHelpers.null === "function" ? () => coerceHelpers.null() : () => z.preprocess((value) => coerceToNull(value), z.null());
|
|
1427
|
+
function getStringBool() {
|
|
1428
|
+
return stringBoolFactory();
|
|
1429
|
+
}
|
|
1430
|
+
function getNullCoercion() {
|
|
1431
|
+
return nullFactory();
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
// src/index.ts
|
|
1435
|
+
function coerceValue(schema, value, tracker = new CircularTracker) {
|
|
1436
|
+
const typeName = getZodTypeName(schema);
|
|
1437
|
+
if (typeName === ZodType.OPTIONAL) {
|
|
1438
|
+
if (value === undefined)
|
|
1439
|
+
return;
|
|
1440
|
+
return coerceValue(schema._def.innerType, value, tracker);
|
|
1441
|
+
}
|
|
1442
|
+
if (typeName === ZodType.NULLABLE) {
|
|
1443
|
+
if (value === null)
|
|
1444
|
+
return null;
|
|
1445
|
+
if (value === "") {
|
|
1446
|
+
return null;
|
|
1447
|
+
}
|
|
1448
|
+
return coerceValue(schema._def.innerType, value, tracker);
|
|
1449
|
+
}
|
|
1450
|
+
if (typeName === ZodType.DEFAULT) {
|
|
1451
|
+
if (value === undefined)
|
|
1452
|
+
return schema._def.defaultValue();
|
|
1453
|
+
return coerceValue(schema._def.innerType, value, tracker);
|
|
1454
|
+
}
|
|
1455
|
+
if (typeName === ZodType.CATCH) {
|
|
1456
|
+
return coerceValue(schema._def.innerType, value, tracker);
|
|
1457
|
+
}
|
|
1458
|
+
if (typeName === ZodType.READONLY) {
|
|
1459
|
+
const result = coerceValue(schema._def.innerType, value, tracker);
|
|
1460
|
+
if (typeof result === "object" && result !== null) {
|
|
1461
|
+
return Object.freeze(result);
|
|
1462
|
+
}
|
|
1463
|
+
return result;
|
|
1464
|
+
}
|
|
1465
|
+
if (typeName === ZodType.LAZY) {
|
|
1466
|
+
const lazySchema = schema._def.getter();
|
|
1467
|
+
return coerceValue(lazySchema, value, tracker);
|
|
1468
|
+
}
|
|
1469
|
+
if (typeName === ZodType.STRING) {
|
|
1470
|
+
return coerceToString(value, tracker);
|
|
1471
|
+
}
|
|
1472
|
+
if (typeName === ZodType.NUMBER) {
|
|
1473
|
+
const num = coerceToNumber(value);
|
|
1474
|
+
const defData = schema._def;
|
|
1475
|
+
if (defData.check === "number_format" && defData.format === "safeint") {
|
|
1476
|
+
return coerceToInteger(num);
|
|
1477
|
+
}
|
|
1478
|
+
if (defData.check === "number_format" && defData.format === "int32") {
|
|
1479
|
+
return coerceToInt32(num);
|
|
1480
|
+
}
|
|
1481
|
+
return num;
|
|
1482
|
+
}
|
|
1483
|
+
if (typeName === ZodType.BOOLEAN) {
|
|
1484
|
+
return coerceToBoolean(value);
|
|
1485
|
+
}
|
|
1486
|
+
if (typeName === ZodType.DATE) {
|
|
1487
|
+
return coerceToDate(value);
|
|
1488
|
+
}
|
|
1489
|
+
if (typeName === ZodType.BIGINT) {
|
|
1490
|
+
return coerceToBigInt(value);
|
|
1491
|
+
}
|
|
1492
|
+
if (typeName === ZodType.NULL) {
|
|
1493
|
+
return coerceToNull(value);
|
|
1494
|
+
}
|
|
1495
|
+
if (typeName === ZodType.NAN) {
|
|
1496
|
+
return coerceToNaN(value);
|
|
1497
|
+
}
|
|
1498
|
+
if (typeName === ZodType.ANY || typeName === ZodType.UNKNOWN) {
|
|
1499
|
+
return value;
|
|
1500
|
+
}
|
|
1501
|
+
if (typeName === ZodType.CUSTOM) {
|
|
1502
|
+
return coerceCustomSchema(schema, value, tracker);
|
|
1503
|
+
}
|
|
1504
|
+
if (typeName === ZodType.NEVER) {
|
|
1505
|
+
throw new Error("Cannot coerce to never type");
|
|
1506
|
+
}
|
|
1507
|
+
if (typeName === ZodType.ARRAY) {
|
|
1508
|
+
const elementSchema = schema._def.element;
|
|
1509
|
+
return coerceToArray(value, elementSchema, (v, t) => coerceValue(elementSchema, v, t), tracker);
|
|
1510
|
+
}
|
|
1511
|
+
if (typeName === ZodType.OBJECT) {
|
|
1512
|
+
const shape = schema._def.shape;
|
|
1513
|
+
return coerceToObject(value, shape, (key, v, t) => {
|
|
1514
|
+
if (key in shape) {
|
|
1515
|
+
return coerceValue(shape[key], v, t);
|
|
1516
|
+
}
|
|
1517
|
+
return v;
|
|
1518
|
+
}, tracker);
|
|
1519
|
+
}
|
|
1520
|
+
if (typeName === ZodType.TUPLE) {
|
|
1521
|
+
const items = schema._def.items;
|
|
1522
|
+
return coerceToTuple(value, items, (index, v, t) => coerceValue(items[index], v, t), tracker);
|
|
1523
|
+
}
|
|
1524
|
+
if (typeName === ZodType.RECORD) {
|
|
1525
|
+
const valueType = schema._def.valueType;
|
|
1526
|
+
return coerceToRecord(value, valueType, (v, t) => coerceValue(valueType, v, t), tracker);
|
|
1527
|
+
}
|
|
1528
|
+
if (typeName === ZodType.ENUM) {
|
|
1529
|
+
const defData = schema._def;
|
|
1530
|
+
const entries = defData.entries;
|
|
1531
|
+
const allValues = Object.values(entries);
|
|
1532
|
+
const hasNumbers = allValues.some((v) => typeof v === "number");
|
|
1533
|
+
const validValues = hasNumbers ? allValues.filter((v) => typeof v === "number") : Array.from(new Set(allValues));
|
|
1534
|
+
return coerceToEnum(value, validValues, entries);
|
|
1535
|
+
}
|
|
1536
|
+
if (typeName === ZodType.LITERAL) {
|
|
1537
|
+
const defData = schema._def;
|
|
1538
|
+
const literalValue = defData.values[0];
|
|
1539
|
+
return coerceToLiteral(value, literalValue);
|
|
1540
|
+
}
|
|
1541
|
+
if (typeName === ZodType.UNION && schema._def.discriminator) {
|
|
1542
|
+
const discriminator = schema._def.discriminator;
|
|
1543
|
+
const options = schema._def.options;
|
|
1544
|
+
return coerceToDiscriminatedUnion(value, discriminator, options, (index, v, t) => coerceValue(options[index], v, t), tracker);
|
|
1545
|
+
}
|
|
1546
|
+
if (typeName === ZodType.UNION) {
|
|
1547
|
+
const options = schema._def.options;
|
|
1548
|
+
return coerceToUnion(value, options, (index, v, t) => coerceValue(options[index], v, t), tracker);
|
|
1549
|
+
}
|
|
1550
|
+
if (typeName === ZodType.INTERSECTION) {
|
|
1551
|
+
const left = schema._def.left;
|
|
1552
|
+
const right = schema._def.right;
|
|
1553
|
+
return coerceToIntersection(value, left, right, (v, t) => coerceValue(left, v, t), (v, t) => coerceValue(right, v, t), tracker);
|
|
1554
|
+
}
|
|
1555
|
+
if (typeName === ZodType.MAP) {
|
|
1556
|
+
const keyType = schema._def.keyType;
|
|
1557
|
+
const valueType = schema._def.valueType;
|
|
1558
|
+
return coerceToMap(value, keyType, valueType, (v, t) => coerceValue(keyType, v, t), (v, t) => coerceValue(valueType, v, t), tracker);
|
|
1559
|
+
}
|
|
1560
|
+
if (typeName === ZodType.SET) {
|
|
1561
|
+
return coerceSetSchema(value, schema, tracker);
|
|
1562
|
+
}
|
|
1563
|
+
return value;
|
|
1564
|
+
}
|
|
1565
|
+
function coerceSetSchema(value, schema, tracker) {
|
|
1566
|
+
const valueType = schema._def.valueType;
|
|
1567
|
+
return coerceToSet(value, valueType, (v, t) => coerceValue(valueType, v, t), tracker);
|
|
1568
|
+
}
|
|
1569
|
+
function coerceCustomSchema(schema, value, tracker) {
|
|
1570
|
+
const defData = schema._def;
|
|
1571
|
+
const predicate = typeof defData?.fn === "function" ? defData.fn : null;
|
|
1572
|
+
if (!predicate) {
|
|
1573
|
+
return value;
|
|
1574
|
+
}
|
|
1575
|
+
const attempts = [
|
|
1576
|
+
() => value,
|
|
1577
|
+
() => coerceToDate(value),
|
|
1578
|
+
() => coerceToNumber(value),
|
|
1579
|
+
() => coerceToBigInt(value),
|
|
1580
|
+
() => coerceToBoolean(value),
|
|
1581
|
+
() => coerceToString(value, tracker)
|
|
1582
|
+
];
|
|
1583
|
+
for (const attempt of attempts) {
|
|
1584
|
+
let coerced;
|
|
1585
|
+
try {
|
|
1586
|
+
coerced = attempt();
|
|
1587
|
+
} catch {
|
|
1588
|
+
continue;
|
|
1589
|
+
}
|
|
1590
|
+
try {
|
|
1591
|
+
if (predicate(coerced)) {
|
|
1592
|
+
return coerced;
|
|
1593
|
+
}
|
|
1594
|
+
} catch {
|
|
1595
|
+
continue;
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
return value;
|
|
1599
|
+
}
|
|
1600
|
+
function canonize(schema) {
|
|
1601
|
+
const preprocessed = z2.preprocess((value) => {
|
|
1602
|
+
try {
|
|
1603
|
+
return coerceValue(schema, value);
|
|
1604
|
+
} catch (error) {
|
|
1605
|
+
if (error instanceof Error && error.message.includes("Circular reference")) {
|
|
1606
|
+
throw error;
|
|
1607
|
+
}
|
|
1608
|
+
return value;
|
|
1609
|
+
}
|
|
1610
|
+
}, schema);
|
|
1611
|
+
return preprocessed;
|
|
1612
|
+
}
|
|
1613
|
+
export {
|
|
1614
|
+
unwrapSchema,
|
|
1615
|
+
getZodTypeName,
|
|
1616
|
+
createCanonizeSchema,
|
|
1617
|
+
createCanonizePrimitive,
|
|
1618
|
+
canonize,
|
|
1619
|
+
ZodType,
|
|
1620
|
+
CircularTracker
|
|
1621
|
+
};
|
|
1622
|
+
|
|
1623
|
+
//# debugId=4135B5B30AC609E164756E2164756E21
|