orchid-orm-schema-to-zod 0.0.1
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/README.md +5 -0
- package/dist/index.d.ts +77 -0
- package/dist/index.esm.js +368 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/index.js +374 -0
- package/dist/index.js.map +1 -0
- package/jest-setup.ts +3 -0
- package/package.json +62 -0
- package/rollup.config.js +3 -0
- package/src/index.test.ts +1249 -0
- package/src/index.ts +667 -0
- package/tsconfig.json +12 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,667 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ArrayColumn,
|
|
3
|
+
ColumnsShape,
|
|
4
|
+
ColumnType,
|
|
5
|
+
DateColumn,
|
|
6
|
+
EnumColumn,
|
|
7
|
+
JSONColumn,
|
|
8
|
+
NumberColumn,
|
|
9
|
+
TextColumn,
|
|
10
|
+
JSONTypeAny,
|
|
11
|
+
Primitive,
|
|
12
|
+
JSONArray,
|
|
13
|
+
JSONEnum,
|
|
14
|
+
JSONInstanceOf,
|
|
15
|
+
JSONLiteral,
|
|
16
|
+
JSONMap,
|
|
17
|
+
EnumLike,
|
|
18
|
+
JSONNativeEnum,
|
|
19
|
+
JSONDate,
|
|
20
|
+
JSONNumber,
|
|
21
|
+
JSONString,
|
|
22
|
+
JSONSet,
|
|
23
|
+
JSONTuple,
|
|
24
|
+
JSONObject,
|
|
25
|
+
UnknownKeysParam,
|
|
26
|
+
JSONDiscriminatedObject,
|
|
27
|
+
JSONDiscriminatedUnion,
|
|
28
|
+
JSONRecord,
|
|
29
|
+
JSONRecordKeyType,
|
|
30
|
+
JSONIntersection,
|
|
31
|
+
JSONUnion,
|
|
32
|
+
JSONLazy,
|
|
33
|
+
} from 'pqb';
|
|
34
|
+
import { z, ZodTypeAny } from 'zod';
|
|
35
|
+
import { Buffer } from 'node:buffer';
|
|
36
|
+
|
|
37
|
+
type NumberType =
|
|
38
|
+
| 'smallint'
|
|
39
|
+
| 'integer'
|
|
40
|
+
| 'real'
|
|
41
|
+
| 'smallserial'
|
|
42
|
+
| 'serial'
|
|
43
|
+
| 'money';
|
|
44
|
+
|
|
45
|
+
type BigIntType =
|
|
46
|
+
| 'bigint'
|
|
47
|
+
| 'numeric'
|
|
48
|
+
| 'decimal'
|
|
49
|
+
| 'double precision'
|
|
50
|
+
| 'bigserial';
|
|
51
|
+
|
|
52
|
+
type StringType = 'varchar' | 'char' | 'text' | 'string' | 'xml' | 'json';
|
|
53
|
+
|
|
54
|
+
type DateTimeType = 'date' | 'timestamp' | 'timestamp with time zone';
|
|
55
|
+
|
|
56
|
+
type TimeType = 'time' | 'time with time zone';
|
|
57
|
+
|
|
58
|
+
type GeometryType =
|
|
59
|
+
| 'point'
|
|
60
|
+
| 'line'
|
|
61
|
+
| 'lseg'
|
|
62
|
+
| 'box'
|
|
63
|
+
| 'path'
|
|
64
|
+
| 'polygon'
|
|
65
|
+
| 'circle';
|
|
66
|
+
|
|
67
|
+
type NetworkType = 'cidr' | 'inet' | 'macaddr' | 'macaddr8';
|
|
68
|
+
|
|
69
|
+
type BitStringType = 'bit' | 'bit varying';
|
|
70
|
+
|
|
71
|
+
type FullTextSearchType = 'tsvector' | 'tsquery';
|
|
72
|
+
|
|
73
|
+
type UUIDType = 'uuid';
|
|
74
|
+
|
|
75
|
+
type ByteaType = 'bytea';
|
|
76
|
+
|
|
77
|
+
type SchemaToZod<
|
|
78
|
+
T extends ColumnType,
|
|
79
|
+
D = T['dataType'],
|
|
80
|
+
> = T['isNullable'] extends true
|
|
81
|
+
? z.ZodNullable<SchemaToZod<Omit<T, 'isNullable'> & { isNullable: false }>>
|
|
82
|
+
: D extends NumberType
|
|
83
|
+
? z.ZodNumber
|
|
84
|
+
: D extends
|
|
85
|
+
| BigIntType
|
|
86
|
+
| StringType
|
|
87
|
+
| TimeType
|
|
88
|
+
| GeometryType
|
|
89
|
+
| NetworkType
|
|
90
|
+
| BitStringType
|
|
91
|
+
| FullTextSearchType
|
|
92
|
+
| UUIDType
|
|
93
|
+
? z.ZodString
|
|
94
|
+
: D extends ByteaType
|
|
95
|
+
? z.ZodType<Buffer>
|
|
96
|
+
: D extends DateTimeType
|
|
97
|
+
? z.ZodDate
|
|
98
|
+
: D extends 'interval'
|
|
99
|
+
? typeof interval
|
|
100
|
+
: D extends 'boolean'
|
|
101
|
+
? z.ZodBoolean
|
|
102
|
+
: T extends EnumColumn<string, infer U>
|
|
103
|
+
? z.ZodEnum<U>
|
|
104
|
+
: T extends ArrayColumn<infer U>
|
|
105
|
+
? z.ZodArray<SchemaToZod<U>>
|
|
106
|
+
: T extends JSONColumn
|
|
107
|
+
? JsonToZod<T['data']['schema']>
|
|
108
|
+
: never;
|
|
109
|
+
|
|
110
|
+
type JsonToZod<T extends JSONTypeAny, D = T['dataType']> = T extends {
|
|
111
|
+
types: [JSONTypeAny, JSONTypeAny, ...JSONTypeAny[]];
|
|
112
|
+
}
|
|
113
|
+
? z.ZodUnion<MapJsonTuple<T['types']>>
|
|
114
|
+
: T['data'] extends {
|
|
115
|
+
nullable: true;
|
|
116
|
+
}
|
|
117
|
+
? T['data'] extends { optional: true }
|
|
118
|
+
? z.ZodNullable<
|
|
119
|
+
z.ZodOptional<
|
|
120
|
+
JsonToZod<
|
|
121
|
+
Omit<T, 'data'> & { data: Omit<T['data'], 'nullable' | 'optional'> }
|
|
122
|
+
>
|
|
123
|
+
>
|
|
124
|
+
>
|
|
125
|
+
: z.ZodNullable<
|
|
126
|
+
JsonToZod<Omit<T, 'data'> & { data: Omit<T['data'], 'nullable'> }>
|
|
127
|
+
>
|
|
128
|
+
: T['data'] extends { optional: true }
|
|
129
|
+
? z.ZodOptional<
|
|
130
|
+
JsonToZod<Omit<T, 'data'> & { data: Omit<T['data'], 'optional'> }>
|
|
131
|
+
>
|
|
132
|
+
: D extends 'bigint'
|
|
133
|
+
? z.ZodString
|
|
134
|
+
: D extends 'boolean'
|
|
135
|
+
? z.ZodBoolean
|
|
136
|
+
: D extends 'date'
|
|
137
|
+
? z.ZodDate
|
|
138
|
+
: D extends 'nan'
|
|
139
|
+
? z.ZodNaN
|
|
140
|
+
: D extends 'never'
|
|
141
|
+
? z.ZodNever
|
|
142
|
+
: D extends 'null'
|
|
143
|
+
? z.ZodNull
|
|
144
|
+
: D extends 'number'
|
|
145
|
+
? z.ZodNumber
|
|
146
|
+
: D extends 'string'
|
|
147
|
+
? z.ZodString
|
|
148
|
+
: D extends 'undefined'
|
|
149
|
+
? z.ZodUndefined
|
|
150
|
+
: D extends 'unknown'
|
|
151
|
+
? z.ZodUnknown
|
|
152
|
+
: D extends 'void'
|
|
153
|
+
? z.ZodVoid
|
|
154
|
+
: T extends JSONArray<infer U>
|
|
155
|
+
? z.ZodArray<JsonToZod<U>>
|
|
156
|
+
: T extends JSONEnum<string, infer U>
|
|
157
|
+
? z.ZodEnum<U>
|
|
158
|
+
: T extends JSONInstanceOf<infer U>
|
|
159
|
+
? z.ZodType<InstanceType<U>, z.ZodTypeDef, InstanceType<U>>
|
|
160
|
+
: T extends JSONLiteral<infer U>
|
|
161
|
+
? z.ZodLiteral<U>
|
|
162
|
+
: T extends JSONMap<infer K, infer V>
|
|
163
|
+
? z.ZodMap<JsonToZod<K>, JsonToZod<V>>
|
|
164
|
+
: T extends JSONSet<infer U>
|
|
165
|
+
? z.ZodSet<JsonToZod<U>>
|
|
166
|
+
: T extends JSONNativeEnum<infer U>
|
|
167
|
+
? z.ZodNativeEnum<U>
|
|
168
|
+
: T extends JSONTuple<infer U, infer R>
|
|
169
|
+
? z.ZodTuple<MapJsonTuple<U>, R extends JSONTypeAny ? JsonToZod<R> : null>
|
|
170
|
+
: T extends JSONObject<Record<string, JSONTypeAny>, UnknownKeysParam>
|
|
171
|
+
? z.ZodObject<
|
|
172
|
+
{ [K in keyof T['shape']]: JsonToZod<T['shape'][K]> },
|
|
173
|
+
T['unknownKeys'],
|
|
174
|
+
JsonToZod<T['catchAllType']>
|
|
175
|
+
>
|
|
176
|
+
: T extends JSONRecord<JSONRecordKeyType, JSONTypeAny>
|
|
177
|
+
? z.ZodRecord<
|
|
178
|
+
JsonToZod<T['keyType']> extends z.ZodType<
|
|
179
|
+
string | number | symbol,
|
|
180
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
181
|
+
any,
|
|
182
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
183
|
+
any
|
|
184
|
+
>
|
|
185
|
+
? JsonToZod<T['keyType']>
|
|
186
|
+
: never,
|
|
187
|
+
JsonToZod<T['valueType']>
|
|
188
|
+
>
|
|
189
|
+
: T extends JSONIntersection<JSONTypeAny, JSONTypeAny>
|
|
190
|
+
? z.ZodIntersection<JsonToZod<T['left']>, JsonToZod<T['right']>>
|
|
191
|
+
: // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
192
|
+
T extends JSONDiscriminatedUnion<string, Primitive, any>
|
|
193
|
+
? z.ZodDiscriminatedUnion<
|
|
194
|
+
T['discriminator'],
|
|
195
|
+
T['discriminatorValue'],
|
|
196
|
+
JsonToZod<T['_option']> extends z.ZodDiscriminatedUnionOption<
|
|
197
|
+
T['discriminator'],
|
|
198
|
+
T['discriminatorValue']
|
|
199
|
+
>
|
|
200
|
+
? JsonToZod<T['_option']>
|
|
201
|
+
: never
|
|
202
|
+
>
|
|
203
|
+
: D extends 'any'
|
|
204
|
+
? z.ZodTypeAny
|
|
205
|
+
: z.ZodType<T['type']>;
|
|
206
|
+
|
|
207
|
+
type MapJsonTuple<T extends unknown[]> = T extends [infer Head, ...infer Tail]
|
|
208
|
+
? [Head extends JSONTypeAny ? JsonToZod<Head> : never, ...MapJsonTuple<Tail>]
|
|
209
|
+
: [];
|
|
210
|
+
|
|
211
|
+
export type ModelToZod<
|
|
212
|
+
T extends new () => { columns: { shape: ColumnsShape } },
|
|
213
|
+
> = InstanceToZod<InstanceType<T>['columns']>;
|
|
214
|
+
|
|
215
|
+
export const modelToZod = <
|
|
216
|
+
T extends new () => { columns: { shape: ColumnsShape } },
|
|
217
|
+
>(
|
|
218
|
+
model: T,
|
|
219
|
+
): ModelToZod<T> => {
|
|
220
|
+
return instanceToZod(new model().columns) as unknown as ModelToZod<T>;
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
export type InstanceToZod<T extends { shape: ColumnsShape }> = z.ZodObject<{
|
|
224
|
+
[K in keyof T['shape']]: SchemaToZod<T['shape'][K]>;
|
|
225
|
+
}>;
|
|
226
|
+
|
|
227
|
+
export const instanceToZod = <T extends { shape: ColumnsShape }>({
|
|
228
|
+
shape,
|
|
229
|
+
}: T): InstanceToZod<T> => {
|
|
230
|
+
const result = {} as z.ZodRawShape;
|
|
231
|
+
for (const key in shape) {
|
|
232
|
+
result[key as keyof typeof result] = columnToZod(shape[key]);
|
|
233
|
+
}
|
|
234
|
+
return z.object(result) as InstanceToZod<T>;
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
export const columnToZod = <T extends ColumnType>(
|
|
238
|
+
column: T,
|
|
239
|
+
): SchemaToZod<T> => {
|
|
240
|
+
const dataType = column.data.as?.dataType || column.dataType;
|
|
241
|
+
const converter = converters[dataType];
|
|
242
|
+
if (!converter) throw new Error(`Cannot parse column ${dataType}`);
|
|
243
|
+
return converter(column) as SchemaToZod<T>;
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
const typeHandler = <Type extends ColumnType | JSONTypeAny>(
|
|
247
|
+
fn: (column: Type) => z.ZodTypeAny,
|
|
248
|
+
) => {
|
|
249
|
+
return (column: ColumnType | JSONTypeAny) => {
|
|
250
|
+
let type = fn(column as Type);
|
|
251
|
+
|
|
252
|
+
column.chain.forEach((item) => {
|
|
253
|
+
if (item[0] === 'transform') {
|
|
254
|
+
type = type.transform(item[1]);
|
|
255
|
+
} else if (item[0] === 'to') {
|
|
256
|
+
type = z.preprocess(item[1], itemToZod(item[2]));
|
|
257
|
+
} else if (item[0] === 'refine') {
|
|
258
|
+
type = type.refine(item[1]);
|
|
259
|
+
} else if (item[0] === 'superRefine') {
|
|
260
|
+
type = type.superRefine(item[1]);
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
if (
|
|
265
|
+
('nullable' in column.data && column.data.nullable) ||
|
|
266
|
+
(column as ColumnType).isNullable
|
|
267
|
+
) {
|
|
268
|
+
if ('optional' in column.data && column.data.optional) {
|
|
269
|
+
type = type.nullish();
|
|
270
|
+
} else {
|
|
271
|
+
type = type.nullable();
|
|
272
|
+
}
|
|
273
|
+
} else if ('optional' in column.data && column.data.optional) {
|
|
274
|
+
type = type.optional();
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (column instanceof ColumnType) {
|
|
278
|
+
if (column.data.validationDefault !== undefined) {
|
|
279
|
+
type = type.default(column.data.validationDefault);
|
|
280
|
+
}
|
|
281
|
+
} else if (column.data.default !== undefined) {
|
|
282
|
+
type = type.default(column.data.default);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return type;
|
|
286
|
+
};
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
const stringParams = [
|
|
290
|
+
'min',
|
|
291
|
+
'max',
|
|
292
|
+
'length',
|
|
293
|
+
'regex',
|
|
294
|
+
'startsWith',
|
|
295
|
+
'endsWith',
|
|
296
|
+
];
|
|
297
|
+
const stringEmptyParams = ['email', 'url', 'uuid', 'cuid', 'trim'];
|
|
298
|
+
const handleString = typeHandler((column: TextColumn | JSONString) => {
|
|
299
|
+
let type = z.string();
|
|
300
|
+
stringParams.forEach((key) => {
|
|
301
|
+
const value = (column.data as Record<string, unknown>)[key];
|
|
302
|
+
if (value !== undefined) {
|
|
303
|
+
type = (
|
|
304
|
+
type as unknown as Record<string, (value: unknown) => z.ZodString>
|
|
305
|
+
)[key](value);
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
stringEmptyParams.forEach((key) => {
|
|
309
|
+
const value = (column.data as Record<string, unknown>)[key];
|
|
310
|
+
if (value) {
|
|
311
|
+
type = (type as unknown as Record<string, () => z.ZodString>)[key]();
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
return type;
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
const numberParams = ['lt', 'lte', 'gt', 'gte', 'multipleOf'];
|
|
318
|
+
const handleNumber = typeHandler((column: NumberColumn | JSONNumber) => {
|
|
319
|
+
let type = z.number();
|
|
320
|
+
numberParams.forEach((key) => {
|
|
321
|
+
const value = (column.data as Record<string, unknown>)[key];
|
|
322
|
+
if (value !== undefined) {
|
|
323
|
+
type = (
|
|
324
|
+
type as unknown as Record<string, (value: unknown) => z.ZodNumber>
|
|
325
|
+
)[key](value);
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
if ((column.data as Record<string, boolean>).int) {
|
|
329
|
+
type = type.int();
|
|
330
|
+
}
|
|
331
|
+
return type;
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
const handleBigInt = typeHandler(() => {
|
|
335
|
+
return z.string().refine(
|
|
336
|
+
(value) => {
|
|
337
|
+
try {
|
|
338
|
+
BigInt(value);
|
|
339
|
+
return true;
|
|
340
|
+
} catch (_) {
|
|
341
|
+
return false;
|
|
342
|
+
}
|
|
343
|
+
},
|
|
344
|
+
{
|
|
345
|
+
message: 'Failed to parse bigint',
|
|
346
|
+
},
|
|
347
|
+
);
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
const handleBuffer = typeHandler(() => {
|
|
351
|
+
return z.instanceof(Buffer);
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
const dateParams = ['min', 'max'];
|
|
355
|
+
const handleDate = typeHandler((column: DateColumn | JSONDate) => {
|
|
356
|
+
let type = z.date();
|
|
357
|
+
dateParams.forEach((key) => {
|
|
358
|
+
const value = (column.data as Record<string, unknown>)[key];
|
|
359
|
+
if (value !== undefined) {
|
|
360
|
+
type = (type as unknown as Record<string, (value: unknown) => z.ZodDate>)[
|
|
361
|
+
key
|
|
362
|
+
](value);
|
|
363
|
+
}
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
return z.preprocess(
|
|
367
|
+
(val) => (typeof val === 'string' ? new Date(val) : val),
|
|
368
|
+
type,
|
|
369
|
+
);
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
const handleTime = typeHandler(() => {
|
|
373
|
+
return z.string().refine(
|
|
374
|
+
(val) => {
|
|
375
|
+
return !isNaN(new Date(`2000-01-01 ${val}`).getTime());
|
|
376
|
+
},
|
|
377
|
+
{
|
|
378
|
+
message: 'Invalid time',
|
|
379
|
+
},
|
|
380
|
+
);
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
const interval = z
|
|
384
|
+
.object({
|
|
385
|
+
years: z.number().optional(),
|
|
386
|
+
months: z.number().optional(),
|
|
387
|
+
days: z.number().optional(),
|
|
388
|
+
hours: z.number().optional(),
|
|
389
|
+
seconds: z.number().optional(),
|
|
390
|
+
})
|
|
391
|
+
.strict();
|
|
392
|
+
|
|
393
|
+
const handleInterval = typeHandler(() => {
|
|
394
|
+
return interval;
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
const handleBoolean = typeHandler(() => {
|
|
398
|
+
return z.boolean();
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
const handleEnum = typeHandler((column: EnumColumn | JSONEnum) => {
|
|
402
|
+
const enumColumn = column as
|
|
403
|
+
| EnumColumn<string, [string, ...string[]]>
|
|
404
|
+
| JSONEnum<string, [string, ...string[]]>;
|
|
405
|
+
return z.enum(enumColumn.options);
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
const handleBitString = typeHandler(() => {
|
|
409
|
+
return z.string().regex(/[10]/g);
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
const handleUUID = typeHandler(() => {
|
|
413
|
+
return z.string().uuid();
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
const arrayParams = ['min', 'max', 'length'];
|
|
417
|
+
const handleArray = typeHandler(
|
|
418
|
+
(array: ArrayColumn<ColumnType> | JSONArray<JSONTypeAny>) => {
|
|
419
|
+
let type: z.ZodArray<z.ZodTypeAny>;
|
|
420
|
+
if ('element' in array) {
|
|
421
|
+
type = z.array(jsonItemToZod(array.element));
|
|
422
|
+
} else {
|
|
423
|
+
type = z.array(columnToZod(array.data.item));
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
arrayParams.forEach((key) => {
|
|
427
|
+
const value = (array.data as Record<string, unknown>)[key];
|
|
428
|
+
if (value !== undefined) {
|
|
429
|
+
type = (
|
|
430
|
+
type as unknown as Record<
|
|
431
|
+
string,
|
|
432
|
+
(value: unknown) => z.ZodArray<z.ZodTypeAny>
|
|
433
|
+
>
|
|
434
|
+
)[key](value);
|
|
435
|
+
}
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
return type;
|
|
439
|
+
},
|
|
440
|
+
);
|
|
441
|
+
|
|
442
|
+
const handleJson = typeHandler((column: JSONColumn) => {
|
|
443
|
+
const type = column.data.schema;
|
|
444
|
+
return jsonItemToZod(type);
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
const jsonItemToZod = (type: JSONTypeAny) => {
|
|
448
|
+
const converter = jsonConverters[type.dataType];
|
|
449
|
+
if (!converter) throw new Error(`Cannot parse column ${type.dataType}`);
|
|
450
|
+
return converter(type);
|
|
451
|
+
};
|
|
452
|
+
|
|
453
|
+
const itemToZod = (item: ColumnType | JSONTypeAny) => {
|
|
454
|
+
return item instanceof ColumnType ? columnToZod(item) : jsonItemToZod(item);
|
|
455
|
+
};
|
|
456
|
+
|
|
457
|
+
const converters: Record<string, (column: ColumnType) => z.ZodType> = {
|
|
458
|
+
varchar: handleString,
|
|
459
|
+
char: handleString,
|
|
460
|
+
text: handleString,
|
|
461
|
+
smallint: handleNumber,
|
|
462
|
+
integer: handleNumber,
|
|
463
|
+
real: handleNumber,
|
|
464
|
+
smallserial: handleNumber,
|
|
465
|
+
serial: handleNumber,
|
|
466
|
+
money: handleNumber,
|
|
467
|
+
bigint: handleBigInt,
|
|
468
|
+
decimal: handleBigInt,
|
|
469
|
+
'double precision': handleBigInt,
|
|
470
|
+
bigserial: handleBigInt,
|
|
471
|
+
bytea: handleBuffer,
|
|
472
|
+
date: handleDate,
|
|
473
|
+
timestamp: handleDate,
|
|
474
|
+
'timestamp with time zone': handleDate,
|
|
475
|
+
time: handleTime,
|
|
476
|
+
'time with time zone': handleTime,
|
|
477
|
+
interval: handleInterval,
|
|
478
|
+
boolean: handleBoolean,
|
|
479
|
+
enum: handleEnum,
|
|
480
|
+
point: handleString,
|
|
481
|
+
line: handleString,
|
|
482
|
+
lseg: handleString,
|
|
483
|
+
box: handleString,
|
|
484
|
+
path: handleString,
|
|
485
|
+
polygon: handleString,
|
|
486
|
+
circle: handleString,
|
|
487
|
+
cidr: handleString,
|
|
488
|
+
inet: handleString,
|
|
489
|
+
macaddr: handleString,
|
|
490
|
+
macaddr8: handleString,
|
|
491
|
+
bit: handleBitString,
|
|
492
|
+
'bit varying': handleBitString,
|
|
493
|
+
tsvector: handleString,
|
|
494
|
+
tsquery: handleString,
|
|
495
|
+
xml: handleString,
|
|
496
|
+
json: handleString,
|
|
497
|
+
uuid: handleUUID,
|
|
498
|
+
array: handleArray,
|
|
499
|
+
jsonb: handleJson,
|
|
500
|
+
};
|
|
501
|
+
|
|
502
|
+
const handleAny = typeHandler(() => {
|
|
503
|
+
return z.any();
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
const handleNaN = typeHandler(() => {
|
|
507
|
+
return z.nan();
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
const handleNever = typeHandler(() => {
|
|
511
|
+
return z.never();
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
const handleNull = typeHandler(() => {
|
|
515
|
+
return z.null();
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
const handleUndefined = typeHandler(() => {
|
|
519
|
+
return z.undefined();
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
const handleUnknown = typeHandler(() => {
|
|
523
|
+
return z.unknown();
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
const handleVoid = typeHandler(() => {
|
|
527
|
+
return z.void();
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
const handleInstanceOf = typeHandler(
|
|
531
|
+
(type: JSONInstanceOf<new () => unknown>) => {
|
|
532
|
+
return z.instanceof(type.class);
|
|
533
|
+
},
|
|
534
|
+
);
|
|
535
|
+
|
|
536
|
+
const handleLiteral = typeHandler((type: JSONTypeAny) => {
|
|
537
|
+
return z.literal((type as JSONLiteral<string>).value);
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
const handleMap = typeHandler((type: JSONMap<JSONTypeAny, JSONTypeAny>) => {
|
|
541
|
+
const { keyType, valueType } = type;
|
|
542
|
+
return z.map(jsonItemToZod(keyType), jsonItemToZod(valueType));
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
const setParams = ['min', 'max', 'size'];
|
|
546
|
+
const handleSet = typeHandler((column: JSONSet<JSONTypeAny>) => {
|
|
547
|
+
const { valueType } = column;
|
|
548
|
+
let type = z.set(jsonItemToZod(valueType));
|
|
549
|
+
setParams.forEach((key) => {
|
|
550
|
+
const value = (column.data as Record<string, unknown>)[key];
|
|
551
|
+
if (value !== undefined) {
|
|
552
|
+
type = (
|
|
553
|
+
type as unknown as Record<
|
|
554
|
+
string,
|
|
555
|
+
(value: unknown) => z.ZodSet<z.ZodTypeAny>
|
|
556
|
+
>
|
|
557
|
+
)[key](value);
|
|
558
|
+
}
|
|
559
|
+
});
|
|
560
|
+
return type;
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
const handleNativeEnum = typeHandler((type: JSONTypeAny) => {
|
|
564
|
+
return z.nativeEnum((type as JSONNativeEnum<EnumLike>).enum);
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
const handleTuple = typeHandler((column: JSONTuple) => {
|
|
568
|
+
let type: z.ZodTuple<[], ZodTypeAny | null> = z.tuple(
|
|
569
|
+
column.items.map((item) => jsonItemToZod(item)) as [],
|
|
570
|
+
);
|
|
571
|
+
if (column.restType) {
|
|
572
|
+
type = type.rest(jsonItemToZod(column.restType));
|
|
573
|
+
}
|
|
574
|
+
return type;
|
|
575
|
+
});
|
|
576
|
+
|
|
577
|
+
const handleObject = typeHandler(
|
|
578
|
+
(type: JSONObject<Record<string, JSONTypeAny>, UnknownKeysParam>) => {
|
|
579
|
+
const shape: Record<string, z.ZodTypeAny> = {};
|
|
580
|
+
for (const key in type.shape) {
|
|
581
|
+
shape[key] = jsonItemToZod(type.shape[key]);
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
let object: z.ZodObject<z.ZodRawShape, UnknownKeysParam, z.ZodTypeAny> =
|
|
585
|
+
z.object(shape);
|
|
586
|
+
|
|
587
|
+
if (type.unknownKeys === 'passthrough') {
|
|
588
|
+
object = object.passthrough();
|
|
589
|
+
} else if (type.unknownKeys === 'strict') {
|
|
590
|
+
object = object.strict();
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
if (type.catchAllType) {
|
|
594
|
+
object = object.catchall(jsonItemToZod(type.catchAllType));
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
return object;
|
|
598
|
+
},
|
|
599
|
+
);
|
|
600
|
+
|
|
601
|
+
const handleRecord = typeHandler(
|
|
602
|
+
(type: JSONRecord<JSONRecordKeyType, JSONTypeAny>) => {
|
|
603
|
+
return z.record(jsonItemToZod(type.keyType), jsonItemToZod(type.valueType));
|
|
604
|
+
},
|
|
605
|
+
);
|
|
606
|
+
|
|
607
|
+
const handleUnion = typeHandler(
|
|
608
|
+
(type: JSONUnion<[JSONTypeAny, JSONTypeAny, ...JSONTypeAny[]]>) => {
|
|
609
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
610
|
+
return z.union(type.types.map(jsonItemToZod) as any);
|
|
611
|
+
},
|
|
612
|
+
);
|
|
613
|
+
|
|
614
|
+
const handleIntersection = typeHandler(
|
|
615
|
+
(type: JSONIntersection<JSONTypeAny, JSONTypeAny>) => {
|
|
616
|
+
return z.intersection(jsonItemToZod(type.left), jsonItemToZod(type.right));
|
|
617
|
+
},
|
|
618
|
+
);
|
|
619
|
+
|
|
620
|
+
const handleDiscriminatedUnion = typeHandler(
|
|
621
|
+
(
|
|
622
|
+
type: JSONDiscriminatedUnion<
|
|
623
|
+
string,
|
|
624
|
+
Primitive,
|
|
625
|
+
JSONDiscriminatedObject<string, Primitive>
|
|
626
|
+
>,
|
|
627
|
+
) => {
|
|
628
|
+
return z.discriminatedUnion(
|
|
629
|
+
type.discriminator,
|
|
630
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
631
|
+
[...type.options.values()].map(jsonItemToZod) as any,
|
|
632
|
+
);
|
|
633
|
+
},
|
|
634
|
+
);
|
|
635
|
+
|
|
636
|
+
const handleLazy = typeHandler((type: JSONLazy<JSONTypeAny>) => {
|
|
637
|
+
return z.lazy(() => jsonItemToZod(type.getter()));
|
|
638
|
+
});
|
|
639
|
+
|
|
640
|
+
const jsonConverters: Record<string, (type: JSONTypeAny) => z.ZodType> = {
|
|
641
|
+
any: handleAny,
|
|
642
|
+
bigint: handleBigInt,
|
|
643
|
+
boolean: handleBoolean,
|
|
644
|
+
date: handleDate,
|
|
645
|
+
nan: handleNaN,
|
|
646
|
+
never: handleNever,
|
|
647
|
+
null: handleNull,
|
|
648
|
+
number: handleNumber,
|
|
649
|
+
string: handleString,
|
|
650
|
+
undefined: handleUndefined,
|
|
651
|
+
unknown: handleUnknown,
|
|
652
|
+
void: handleVoid,
|
|
653
|
+
array: handleArray,
|
|
654
|
+
enum: handleEnum,
|
|
655
|
+
instanceOf: handleInstanceOf,
|
|
656
|
+
literal: handleLiteral,
|
|
657
|
+
map: handleMap,
|
|
658
|
+
set: handleSet,
|
|
659
|
+
nativeEnum: handleNativeEnum,
|
|
660
|
+
tuple: handleTuple,
|
|
661
|
+
object: handleObject,
|
|
662
|
+
record: handleRecord,
|
|
663
|
+
union: handleUnion,
|
|
664
|
+
intersection: handleIntersection,
|
|
665
|
+
discriminatedUnion: handleDiscriminatedUnion,
|
|
666
|
+
lazy: handleLazy,
|
|
667
|
+
};
|