pg2zod 2.0.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/CHANGELOG.md +131 -0
- package/README.md +404 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +180 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +216 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/src-18pV45Fu.js +835 -0
- package/dist/src-18pV45Fu.js.map +1 -0
- package/package.json +73 -0
|
@@ -0,0 +1,835 @@
|
|
|
1
|
+
import pg from "pg";
|
|
2
|
+
|
|
3
|
+
//#region src/introspect.ts
|
|
4
|
+
const { Pool } = pg;
|
|
5
|
+
/**
|
|
6
|
+
* Introspect a PostgreSQL database and return complete metadata
|
|
7
|
+
*/
|
|
8
|
+
async function introspectDatabase(config, options = {}) {
|
|
9
|
+
const pool = new Pool(config);
|
|
10
|
+
try {
|
|
11
|
+
const schemas = options.schemas ?? ["public"];
|
|
12
|
+
const [tables, enums, compositeTypes, rangeTypes, domains] = await Promise.all([
|
|
13
|
+
introspectTables(pool, schemas, options),
|
|
14
|
+
introspectEnums(pool, schemas),
|
|
15
|
+
introspectCompositeTypes(pool, schemas),
|
|
16
|
+
introspectRangeTypes(pool, schemas),
|
|
17
|
+
introspectDomains(pool, schemas)
|
|
18
|
+
]);
|
|
19
|
+
return {
|
|
20
|
+
tables,
|
|
21
|
+
enums,
|
|
22
|
+
compositeTypes,
|
|
23
|
+
rangeTypes,
|
|
24
|
+
domains
|
|
25
|
+
};
|
|
26
|
+
} finally {
|
|
27
|
+
await pool.end();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Introspect tables and their columns
|
|
32
|
+
*/
|
|
33
|
+
async function introspectTables(pool, schemas, options) {
|
|
34
|
+
const schemaFilter = schemas.map((_, i) => `$${i + 1}`).join(", ");
|
|
35
|
+
const columnsQuery = `
|
|
36
|
+
SELECT
|
|
37
|
+
c.table_schema,
|
|
38
|
+
c.table_name,
|
|
39
|
+
c.column_name,
|
|
40
|
+
c.data_type,
|
|
41
|
+
c.is_nullable,
|
|
42
|
+
c.column_default,
|
|
43
|
+
c.character_maximum_length,
|
|
44
|
+
c.numeric_precision,
|
|
45
|
+
c.numeric_scale,
|
|
46
|
+
c.datetime_precision,
|
|
47
|
+
c.udt_name,
|
|
48
|
+
c.domain_name,
|
|
49
|
+
COALESCE(
|
|
50
|
+
(SELECT array_ndims(ARRAY[]::text[]) FROM information_schema.element_types e
|
|
51
|
+
WHERE e.object_schema = c.table_schema
|
|
52
|
+
AND e.object_name = c.table_name
|
|
53
|
+
AND e.object_type = 'TABLE'
|
|
54
|
+
AND e.collection_type_identifier = c.dtd_identifier
|
|
55
|
+
), 0
|
|
56
|
+
) as array_dimensions
|
|
57
|
+
FROM information_schema.columns c
|
|
58
|
+
WHERE c.table_schema IN (${schemaFilter})
|
|
59
|
+
ORDER BY c.table_schema, c.table_name, c.ordinal_position
|
|
60
|
+
`;
|
|
61
|
+
const columnsResult = await pool.query(columnsQuery, schemas);
|
|
62
|
+
const constraintsQuery = `
|
|
63
|
+
SELECT
|
|
64
|
+
tc.table_schema,
|
|
65
|
+
tc.table_name,
|
|
66
|
+
tc.constraint_name,
|
|
67
|
+
cc.check_clause,
|
|
68
|
+
ccu.column_name
|
|
69
|
+
FROM information_schema.table_constraints tc
|
|
70
|
+
JOIN information_schema.check_constraints cc
|
|
71
|
+
ON tc.constraint_name = cc.constraint_name
|
|
72
|
+
AND tc.constraint_schema = cc.constraint_schema
|
|
73
|
+
LEFT JOIN information_schema.constraint_column_usage ccu
|
|
74
|
+
ON tc.constraint_name = ccu.constraint_name
|
|
75
|
+
AND tc.constraint_schema = ccu.constraint_schema
|
|
76
|
+
WHERE tc.constraint_type = 'CHECK'
|
|
77
|
+
AND tc.table_schema IN (${schemaFilter})
|
|
78
|
+
`;
|
|
79
|
+
const constraintsResult = await pool.query(constraintsQuery, schemas);
|
|
80
|
+
const primaryKeysQuery = `
|
|
81
|
+
SELECT
|
|
82
|
+
tc.table_schema,
|
|
83
|
+
tc.table_name,
|
|
84
|
+
kcu.column_name
|
|
85
|
+
FROM information_schema.table_constraints tc
|
|
86
|
+
JOIN information_schema.key_column_usage kcu
|
|
87
|
+
ON tc.constraint_name = kcu.constraint_name
|
|
88
|
+
AND tc.table_schema = kcu.table_schema
|
|
89
|
+
WHERE tc.constraint_type = 'PRIMARY KEY'
|
|
90
|
+
AND tc.table_schema IN (${schemaFilter})
|
|
91
|
+
ORDER BY kcu.ordinal_position
|
|
92
|
+
`;
|
|
93
|
+
const primaryKeysResult = await pool.query(primaryKeysQuery, schemas);
|
|
94
|
+
const uniqueConstraintsQuery = `
|
|
95
|
+
SELECT
|
|
96
|
+
tc.table_schema,
|
|
97
|
+
tc.table_name,
|
|
98
|
+
tc.constraint_name,
|
|
99
|
+
array_agg(kcu.column_name ORDER BY kcu.ordinal_position) as columns
|
|
100
|
+
FROM information_schema.table_constraints tc
|
|
101
|
+
JOIN information_schema.key_column_usage kcu
|
|
102
|
+
ON tc.constraint_name = kcu.constraint_name
|
|
103
|
+
AND tc.table_schema = kcu.table_schema
|
|
104
|
+
WHERE tc.constraint_type = 'UNIQUE'
|
|
105
|
+
AND tc.table_schema IN (${schemaFilter})
|
|
106
|
+
GROUP BY tc.table_schema, tc.table_name, tc.constraint_name
|
|
107
|
+
`;
|
|
108
|
+
const uniqueConstraintsResult = await pool.query(uniqueConstraintsQuery, schemas);
|
|
109
|
+
const tableMap = /* @__PURE__ */ new Map();
|
|
110
|
+
for (const row of columnsResult.rows) {
|
|
111
|
+
const tableKey = `${row.table_schema}.${row.table_name}`;
|
|
112
|
+
if (!tableMap.has(tableKey)) tableMap.set(tableKey, {
|
|
113
|
+
tableName: row.table_name,
|
|
114
|
+
schemaName: row.table_schema,
|
|
115
|
+
columns: [],
|
|
116
|
+
checkConstraints: [],
|
|
117
|
+
primaryKeys: [],
|
|
118
|
+
uniqueConstraints: []
|
|
119
|
+
});
|
|
120
|
+
const table = tableMap.get(tableKey);
|
|
121
|
+
const isArray = row.data_type === "ARRAY";
|
|
122
|
+
table.columns.push({
|
|
123
|
+
columnName: row.column_name,
|
|
124
|
+
dataType: row.data_type,
|
|
125
|
+
isNullable: row.is_nullable === "YES",
|
|
126
|
+
columnDefault: row.column_default,
|
|
127
|
+
characterMaximumLength: row.character_maximum_length,
|
|
128
|
+
numericPrecision: row.numeric_precision,
|
|
129
|
+
numericScale: row.numeric_scale,
|
|
130
|
+
datetimePrecision: row.datetime_precision,
|
|
131
|
+
udtName: row.udt_name,
|
|
132
|
+
domainName: row.domain_name,
|
|
133
|
+
arrayDimensions: row.array_dimensions || 0,
|
|
134
|
+
isArray
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
for (const row of constraintsResult.rows) {
|
|
138
|
+
const tableKey = `${row.table_schema}.${row.table_name}`;
|
|
139
|
+
const table = tableMap.get(tableKey);
|
|
140
|
+
if (table) table.checkConstraints.push({
|
|
141
|
+
constraintName: row.constraint_name,
|
|
142
|
+
checkClause: row.check_clause,
|
|
143
|
+
columnName: row.column_name
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
for (const row of primaryKeysResult.rows) {
|
|
147
|
+
const tableKey = `${row.table_schema}.${row.table_name}`;
|
|
148
|
+
const table = tableMap.get(tableKey);
|
|
149
|
+
if (table) table.primaryKeys.push(row.column_name);
|
|
150
|
+
}
|
|
151
|
+
for (const row of uniqueConstraintsResult.rows) {
|
|
152
|
+
const tableKey = `${row.table_schema}.${row.table_name}`;
|
|
153
|
+
const table = tableMap.get(tableKey);
|
|
154
|
+
if (table) table.uniqueConstraints.push({
|
|
155
|
+
constraintName: row.constraint_name,
|
|
156
|
+
columns: row.columns
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
let tables = Array.from(tableMap.values());
|
|
160
|
+
if (options.tables) tables = tables.filter((t) => options.tables.includes(t.tableName));
|
|
161
|
+
if (options.excludeTables) tables = tables.filter((t) => !options.excludeTables.includes(t.tableName));
|
|
162
|
+
return tables;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Introspect enum types
|
|
166
|
+
*/
|
|
167
|
+
async function introspectEnums(pool, schemas) {
|
|
168
|
+
const query = `
|
|
169
|
+
SELECT
|
|
170
|
+
t.typname as enum_name,
|
|
171
|
+
n.nspname as schema_name,
|
|
172
|
+
array_agg(e.enumlabel ORDER BY e.enumsortorder) as enum_values
|
|
173
|
+
FROM pg_type t
|
|
174
|
+
JOIN pg_enum e ON t.oid = e.enumtypid
|
|
175
|
+
JOIN pg_namespace n ON t.typnamespace = n.oid
|
|
176
|
+
WHERE n.nspname IN (${schemas.map((_, i) => `$${i + 1}`).join(", ")})
|
|
177
|
+
GROUP BY t.typname, n.nspname
|
|
178
|
+
ORDER BY t.typname
|
|
179
|
+
`;
|
|
180
|
+
return (await pool.query(query, schemas)).rows.map((row) => {
|
|
181
|
+
let enumValues;
|
|
182
|
+
if (Array.isArray(row.enum_values)) enumValues = row.enum_values;
|
|
183
|
+
else if (typeof row.enum_values === "string") if (row.enum_values.startsWith("{") && row.enum_values.endsWith("}")) enumValues = row.enum_values.slice(1, -1).split(",").map((v) => v.trim());
|
|
184
|
+
else enumValues = [row.enum_values];
|
|
185
|
+
else enumValues = [row.enum_values];
|
|
186
|
+
return {
|
|
187
|
+
enumName: row.enum_name,
|
|
188
|
+
enumValues,
|
|
189
|
+
schemaName: row.schema_name
|
|
190
|
+
};
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Introspect composite types
|
|
195
|
+
*/
|
|
196
|
+
async function introspectCompositeTypes(pool, schemas) {
|
|
197
|
+
const query = `
|
|
198
|
+
SELECT
|
|
199
|
+
t.typname as type_name,
|
|
200
|
+
n.nspname as schema_name,
|
|
201
|
+
a.attname as attribute_name,
|
|
202
|
+
a.attnum as attribute_number,
|
|
203
|
+
format_type(a.atttypid, a.atttypmod) as data_type
|
|
204
|
+
FROM pg_type t
|
|
205
|
+
JOIN pg_namespace n ON t.typnamespace = n.oid
|
|
206
|
+
JOIN pg_class c ON t.typrelid = c.oid
|
|
207
|
+
JOIN pg_attribute a ON c.oid = a.attrelid
|
|
208
|
+
WHERE t.typtype = 'c'
|
|
209
|
+
AND n.nspname IN (${schemas.map((_, i) => `$${i + 1}`).join(", ")})
|
|
210
|
+
AND a.attnum > 0
|
|
211
|
+
AND NOT a.attisdropped
|
|
212
|
+
ORDER BY t.typname, a.attnum
|
|
213
|
+
`;
|
|
214
|
+
const result = await pool.query(query, schemas);
|
|
215
|
+
const typeMap = /* @__PURE__ */ new Map();
|
|
216
|
+
for (const row of result.rows) {
|
|
217
|
+
const typeKey = `${row.schema_name}.${row.type_name}`;
|
|
218
|
+
if (!typeMap.has(typeKey)) typeMap.set(typeKey, {
|
|
219
|
+
typeName: row.type_name,
|
|
220
|
+
schemaName: row.schema_name,
|
|
221
|
+
attributes: []
|
|
222
|
+
});
|
|
223
|
+
typeMap.get(typeKey).attributes.push({
|
|
224
|
+
attributeName: row.attribute_name,
|
|
225
|
+
dataType: row.data_type,
|
|
226
|
+
attributeNumber: row.attribute_number
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
return Array.from(typeMap.values());
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Introspect range types
|
|
233
|
+
*/
|
|
234
|
+
async function introspectRangeTypes(pool, schemas) {
|
|
235
|
+
const query = `
|
|
236
|
+
SELECT
|
|
237
|
+
t.typname as range_name,
|
|
238
|
+
n.nspname as schema_name,
|
|
239
|
+
format_type(r.rngsubtype, NULL) as subtype
|
|
240
|
+
FROM pg_type t
|
|
241
|
+
JOIN pg_namespace n ON t.typnamespace = n.oid
|
|
242
|
+
JOIN pg_range r ON t.oid = r.rngtypid
|
|
243
|
+
WHERE n.nspname IN (${schemas.map((_, i) => `$${i + 1}`).join(", ")})
|
|
244
|
+
ORDER BY t.typname
|
|
245
|
+
`;
|
|
246
|
+
return (await pool.query(query, schemas)).rows.map((row) => ({
|
|
247
|
+
rangeName: row.range_name,
|
|
248
|
+
subtype: row.subtype,
|
|
249
|
+
schemaName: row.schema_name
|
|
250
|
+
}));
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Introspect domain types
|
|
254
|
+
*/
|
|
255
|
+
async function introspectDomains(pool, schemas) {
|
|
256
|
+
const schemaFilter = schemas.map((_, i) => `$${i + 1}`).join(", ");
|
|
257
|
+
const domainsQuery = `
|
|
258
|
+
SELECT
|
|
259
|
+
t.typname as domain_name,
|
|
260
|
+
n.nspname as schema_name,
|
|
261
|
+
format_type(t.typbasetype, t.typtypmod) as data_type,
|
|
262
|
+
t.typnotnull as is_not_null,
|
|
263
|
+
t.typdefault as domain_default,
|
|
264
|
+
information_schema._pg_char_max_length(t.typbasetype, t.typtypmod) as character_maximum_length,
|
|
265
|
+
information_schema._pg_numeric_precision(t.typbasetype, t.typtypmod) as numeric_precision,
|
|
266
|
+
information_schema._pg_numeric_scale(t.typbasetype, t.typtypmod) as numeric_scale
|
|
267
|
+
FROM pg_type t
|
|
268
|
+
JOIN pg_namespace n ON t.typnamespace = n.oid
|
|
269
|
+
WHERE t.typtype = 'd'
|
|
270
|
+
AND n.nspname IN (${schemaFilter})
|
|
271
|
+
ORDER BY t.typname
|
|
272
|
+
`;
|
|
273
|
+
const domainsResult = await pool.query(domainsQuery, schemas);
|
|
274
|
+
const constraintsQuery = `
|
|
275
|
+
SELECT
|
|
276
|
+
t.typname as domain_name,
|
|
277
|
+
n.nspname as schema_name,
|
|
278
|
+
c.conname as constraint_name,
|
|
279
|
+
pg_get_constraintdef(c.oid) as check_clause
|
|
280
|
+
FROM pg_constraint c
|
|
281
|
+
JOIN pg_type t ON c.contypid = t.oid
|
|
282
|
+
JOIN pg_namespace n ON t.typnamespace = n.oid
|
|
283
|
+
WHERE c.contype = 'c'
|
|
284
|
+
AND n.nspname IN (${schemaFilter})
|
|
285
|
+
`;
|
|
286
|
+
const constraintsResult = await pool.query(constraintsQuery, schemas);
|
|
287
|
+
const domains = domainsResult.rows.map((row) => ({
|
|
288
|
+
domainName: row.domain_name,
|
|
289
|
+
dataType: row.data_type,
|
|
290
|
+
schemaName: row.schema_name,
|
|
291
|
+
characterMaximumLength: row.character_maximum_length,
|
|
292
|
+
numericPrecision: row.numeric_precision,
|
|
293
|
+
numericScale: row.numeric_scale,
|
|
294
|
+
isNullable: !row.is_not_null,
|
|
295
|
+
domainDefault: row.domain_default,
|
|
296
|
+
checkConstraints: []
|
|
297
|
+
}));
|
|
298
|
+
for (const row of constraintsResult.rows) {
|
|
299
|
+
const domain = domains.find((d) => d.domainName === row.domain_name && d.schemaName === row.schema_name);
|
|
300
|
+
if (domain) domain.checkConstraints.push({
|
|
301
|
+
constraintName: row.constraint_name,
|
|
302
|
+
checkClause: row.check_clause,
|
|
303
|
+
columnName: null
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
return domains;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
//#endregion
|
|
310
|
+
//#region src/type-mapper.ts
|
|
311
|
+
/**
|
|
312
|
+
* Map PostgreSQL column to Zod schema string with strict validation
|
|
313
|
+
*/
|
|
314
|
+
function mapColumnToZod(column, metadata, options, warnings) {
|
|
315
|
+
if (column.domainName) {
|
|
316
|
+
const domain = metadata.domains.find((d) => d.domainName === column.domainName);
|
|
317
|
+
if (domain) {
|
|
318
|
+
let schema = `${toPascalCase(domain.schemaName)}${toPascalCase(domain.domainName)}Schema`;
|
|
319
|
+
return column.isNullable ? `${schema}.nullable()` : schema;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
if (column.isArray) {
|
|
323
|
+
let arraySchema = `z.array(${mapBaseTypeToZod(column, metadata, options, warnings)})`;
|
|
324
|
+
if (column.arrayDimensions > 1) for (let i = 1; i < column.arrayDimensions; i++) arraySchema = `z.array(${arraySchema})`;
|
|
325
|
+
return column.isNullable ? `${arraySchema}.nullable()` : arraySchema;
|
|
326
|
+
}
|
|
327
|
+
const baseSchema = mapBaseTypeToZod(column, metadata, options, warnings);
|
|
328
|
+
return column.isNullable ? `${baseSchema}.nullable()` : baseSchema;
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Map base PostgreSQL type to Zod
|
|
332
|
+
*/
|
|
333
|
+
function mapBaseTypeToZod(column, metadata, options, warnings) {
|
|
334
|
+
let udtName = column.udtName;
|
|
335
|
+
const dataType = column.dataType.toLowerCase();
|
|
336
|
+
if (column.isArray && udtName.startsWith("_")) udtName = udtName.substring(1);
|
|
337
|
+
if (options.customTypeMappings?.[udtName]) return options.customTypeMappings[udtName];
|
|
338
|
+
const enumType = metadata.enums.find((e) => e.enumName === udtName);
|
|
339
|
+
if (enumType) return `${toPascalCase(enumType.schemaName)}${toPascalCase(enumType.enumName)}Schema`;
|
|
340
|
+
const compositeType = metadata.compositeTypes.find((t) => t.typeName === udtName);
|
|
341
|
+
if (compositeType) return `${toPascalCase(compositeType.schemaName)}${toPascalCase(compositeType.typeName)}CompositeSchema`;
|
|
342
|
+
const rangeType = metadata.rangeTypes.find((r) => r.rangeName === udtName);
|
|
343
|
+
if (rangeType) return `${toPascalCase(rangeType.schemaName)}${toPascalCase(rangeType.rangeName)}Schema`;
|
|
344
|
+
switch (dataType === "array" ? udtName : dataType) {
|
|
345
|
+
case "smallint":
|
|
346
|
+
case "integer":
|
|
347
|
+
case "int":
|
|
348
|
+
case "int2":
|
|
349
|
+
case "int4": return "z.number().int()";
|
|
350
|
+
case "bigint":
|
|
351
|
+
case "int8": return "z.bigint()";
|
|
352
|
+
case "decimal":
|
|
353
|
+
case "numeric":
|
|
354
|
+
if (column.numericPrecision !== null && column.numericScale !== null) return `z.number() /* precision: ${column.numericPrecision}, scale: ${column.numericScale} */`;
|
|
355
|
+
return "z.number()";
|
|
356
|
+
case "real":
|
|
357
|
+
case "float4": return "z.number()";
|
|
358
|
+
case "double precision":
|
|
359
|
+
case "float8": return "z.number()";
|
|
360
|
+
case "money": return "z.string().regex(/^\\$?[0-9,]+(\\.\\d{2})?$/)";
|
|
361
|
+
case "character varying":
|
|
362
|
+
case "varchar":
|
|
363
|
+
if (column.characterMaximumLength) return `z.string().max(${column.characterMaximumLength})`;
|
|
364
|
+
return "z.string()";
|
|
365
|
+
case "character":
|
|
366
|
+
case "char":
|
|
367
|
+
if (column.characterMaximumLength) return `z.string().length(${column.characterMaximumLength})`;
|
|
368
|
+
return "z.string()";
|
|
369
|
+
case "text": return "z.string()";
|
|
370
|
+
case "citext": return "z.string()";
|
|
371
|
+
case "boolean":
|
|
372
|
+
case "bool": return "z.boolean()";
|
|
373
|
+
case "timestamp":
|
|
374
|
+
case "timestamp without time zone": return "z.date()";
|
|
375
|
+
case "timestamp with time zone":
|
|
376
|
+
case "timestamptz": return "z.date()";
|
|
377
|
+
case "date": return "z.date()";
|
|
378
|
+
case "time":
|
|
379
|
+
case "time without time zone": return "z.iso.time()";
|
|
380
|
+
case "time with time zone":
|
|
381
|
+
case "timetz": return "z.string().regex(/^\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?[+-]\\d{2}:\\d{2}$/)";
|
|
382
|
+
case "interval": return "z.iso.duration()";
|
|
383
|
+
case "uuid": return "z.uuid()";
|
|
384
|
+
case "json": return "z.record(z.string(), z.unknown())";
|
|
385
|
+
case "jsonb": return "z.record(z.string(), z.unknown())";
|
|
386
|
+
case "inet": return "z.union([z.ipv4(), z.ipv6()])";
|
|
387
|
+
case "cidr": return "z.union([z.cidrv4(), z.cidrv6()])";
|
|
388
|
+
case "macaddr": return "z.mac()";
|
|
389
|
+
case "macaddr8": return "z.string().regex(/^([0-9A-Fa-f]{2}[:-]){7}([0-9A-Fa-f]{2})$/)";
|
|
390
|
+
case "bit":
|
|
391
|
+
if (column.characterMaximumLength) return `z.string().regex(/^[01]{${column.characterMaximumLength}}$/)`;
|
|
392
|
+
return "z.string().regex(/^[01]+$/)";
|
|
393
|
+
case "bit varying":
|
|
394
|
+
case "varbit":
|
|
395
|
+
if (column.characterMaximumLength) return `z.string().regex(/^[01]{0,${column.characterMaximumLength}}$/)`;
|
|
396
|
+
return "z.string().regex(/^[01]*$/)";
|
|
397
|
+
case "point": return "z.tuple([z.number(), z.number()])";
|
|
398
|
+
case "line": return "z.object({ a: z.number(), b: z.number(), c: z.number() })";
|
|
399
|
+
case "lseg": return "z.tuple([z.tuple([z.number(), z.number()]), z.tuple([z.number(), z.number()])])";
|
|
400
|
+
case "box": return "z.tuple([z.tuple([z.number(), z.number()]), z.tuple([z.number(), z.number()])])";
|
|
401
|
+
case "path": return "z.array(z.tuple([z.number(), z.number()]))";
|
|
402
|
+
case "polygon": return "z.array(z.tuple([z.number(), z.number()]))";
|
|
403
|
+
case "circle": return "z.object({ center: z.tuple([z.number(), z.number()]), radius: z.number() })";
|
|
404
|
+
case "tsvector": return "z.string() /* tsvector */";
|
|
405
|
+
case "tsquery": return "z.string() /* tsquery */";
|
|
406
|
+
case "xml": return "z.string() /* XML */";
|
|
407
|
+
case "bytea": return "z.instanceof(Buffer)";
|
|
408
|
+
case "oid": return "z.number().int().positive()";
|
|
409
|
+
case "regproc":
|
|
410
|
+
case "regprocedure":
|
|
411
|
+
case "regoper":
|
|
412
|
+
case "regoperator":
|
|
413
|
+
case "regclass":
|
|
414
|
+
case "regtype":
|
|
415
|
+
case "regrole":
|
|
416
|
+
case "regnamespace":
|
|
417
|
+
case "regconfig":
|
|
418
|
+
case "regdictionary": return "z.string() /* PostgreSQL OID reference */";
|
|
419
|
+
case "pg_lsn": return "z.string().regex(/^[0-9A-F]+\\/[0-9A-F]+$/)";
|
|
420
|
+
default:
|
|
421
|
+
const warning = `Unknown type: ${dataType} (udt: ${udtName}) in column ${column.columnName}`;
|
|
422
|
+
warnings.push(warning);
|
|
423
|
+
if (options.strictMode) throw new Error(warning);
|
|
424
|
+
return "z.unknown() /* unmapped type */";
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Apply check constraints as Zod refinements
|
|
429
|
+
*/
|
|
430
|
+
function applyCheckConstraints(columnName, baseSchema, constraints) {
|
|
431
|
+
let schema = baseSchema;
|
|
432
|
+
for (const constraint of constraints) {
|
|
433
|
+
const checkClause = constraint.checkClause.toLowerCase();
|
|
434
|
+
const geMatch = checkClause.match(/* @__PURE__ */ new RegExp(`${columnName}\\s*>=\\s*([\\d.]+)`));
|
|
435
|
+
if (geMatch) {
|
|
436
|
+
const value = geMatch[1];
|
|
437
|
+
if (schema.includes("z.number()")) schema = schema.replace("z.number()", `z.number().min(${value})`);
|
|
438
|
+
else if (schema.includes("z.bigint()")) schema = schema.replace("z.bigint()", `z.bigint().min(${value}n)`);
|
|
439
|
+
continue;
|
|
440
|
+
}
|
|
441
|
+
const gtMatch = checkClause.match(/* @__PURE__ */ new RegExp(`${columnName}\\s*>\\s*([\\d.]+)`));
|
|
442
|
+
if (gtMatch) {
|
|
443
|
+
const value = parseFloat(gtMatch[1]);
|
|
444
|
+
if (schema.includes("z.number()")) schema = schema.replace("z.number()", `z.number().min(${value + Number.EPSILON})`);
|
|
445
|
+
continue;
|
|
446
|
+
}
|
|
447
|
+
const leMatch = checkClause.match(/* @__PURE__ */ new RegExp(`${columnName}\\s*<=\\s*([\\d.]+)`));
|
|
448
|
+
if (leMatch) {
|
|
449
|
+
const value = leMatch[1];
|
|
450
|
+
if (schema.includes("z.number()")) schema = schema.replace("z.number()", `z.number().max(${value})`);
|
|
451
|
+
else if (schema.includes("z.bigint()")) schema = schema.replace("z.bigint()", `z.bigint().max(${value}n)`);
|
|
452
|
+
continue;
|
|
453
|
+
}
|
|
454
|
+
const ltMatch = checkClause.match(/* @__PURE__ */ new RegExp(`${columnName}\\s*<\\s*([\\d.]+)`));
|
|
455
|
+
if (ltMatch) {
|
|
456
|
+
const value = parseFloat(ltMatch[1]);
|
|
457
|
+
if (schema.includes("z.number()")) schema = schema.replace("z.number()", `z.number().max(${value - Number.EPSILON})`);
|
|
458
|
+
continue;
|
|
459
|
+
}
|
|
460
|
+
const betweenMatch = checkClause.match(/* @__PURE__ */ new RegExp(`${columnName}\\s*between\\s*([\\d.]+)\\s*and\\s*([\\d.]+)`));
|
|
461
|
+
if (betweenMatch) {
|
|
462
|
+
const [, min, max] = betweenMatch;
|
|
463
|
+
if (schema.includes("z.number()")) schema = schema.replace("z.number()", `z.number().min(${min}).max(${max})`);
|
|
464
|
+
continue;
|
|
465
|
+
}
|
|
466
|
+
const inMatch = checkClause.match(/* @__PURE__ */ new RegExp(`${columnName}\\s*in\\s*\\(([^)]+)\\)`));
|
|
467
|
+
if (inMatch) {
|
|
468
|
+
const values = inMatch[1].split(",").map((v) => v.trim().replace(/'/g, ""));
|
|
469
|
+
if (schema.includes("z.string()")) schema = `z.enum([${values.map((v) => `'${v}'`).join(", ")}])`;
|
|
470
|
+
continue;
|
|
471
|
+
}
|
|
472
|
+
const anyArrayMatch = checkClause.match(/* @__PURE__ */ new RegExp(`\\(?${columnName}\\s*=\\s*any\\s*\\(array\\[([^\\]]+)\\]`));
|
|
473
|
+
if (anyArrayMatch) {
|
|
474
|
+
const values = anyArrayMatch[1].split(",").map((v) => {
|
|
475
|
+
const match = v.trim().match(/'([^']+)'/);
|
|
476
|
+
return match ? match[1] : null;
|
|
477
|
+
}).filter((v) => v !== null);
|
|
478
|
+
if (values.length > 0 && schema.includes("z.string()")) {
|
|
479
|
+
schema = `z.enum([${values.map((v) => `'${v}'`).join(", ")}])`;
|
|
480
|
+
continue;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
const regexMatch = checkClause.match(/* @__PURE__ */ new RegExp(`${columnName}\\s*~\\s*'([^']+)'`));
|
|
484
|
+
if (regexMatch) {
|
|
485
|
+
const pattern = regexMatch[1];
|
|
486
|
+
if (schema.includes("z.string()")) schema = schema.replace("z.string()", `z.string().regex(/${pattern}/)`);
|
|
487
|
+
continue;
|
|
488
|
+
}
|
|
489
|
+
const lengthMatch = checkClause.match(/* @__PURE__ */ new RegExp(`length\\(${columnName}\\)\\s*([><=]+)\\s*([\\d]+)`));
|
|
490
|
+
if (lengthMatch) {
|
|
491
|
+
const [, operator, value] = lengthMatch;
|
|
492
|
+
if (schema.includes("z.string()")) {
|
|
493
|
+
if (operator === ">=" || operator === ">") schema = schema.replace("z.string()", `z.string().min(${value})`);
|
|
494
|
+
else if (operator === "<=" || operator === "<") schema = schema.replace("z.string()", `z.string().max(${value})`);
|
|
495
|
+
}
|
|
496
|
+
continue;
|
|
497
|
+
}
|
|
498
|
+
schema += ` /* CHECK: ${constraint.checkClause} */`;
|
|
499
|
+
}
|
|
500
|
+
return schema;
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* Convert snake_case to PascalCase
|
|
504
|
+
*/
|
|
505
|
+
function toPascalCase(str) {
|
|
506
|
+
return str.split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("");
|
|
507
|
+
}
|
|
508
|
+
/**
|
|
509
|
+
* Convert snake_case to camelCase
|
|
510
|
+
*/
|
|
511
|
+
function toCamelCase(str) {
|
|
512
|
+
const pascal = toPascalCase(str);
|
|
513
|
+
return pascal.charAt(0).toLowerCase() + pascal.slice(1);
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
//#endregion
|
|
517
|
+
//#region src/generator.ts
|
|
518
|
+
/**
|
|
519
|
+
* Generate Zod schemas from database metadata
|
|
520
|
+
*/
|
|
521
|
+
function generateSchemas(metadata, options = {}) {
|
|
522
|
+
const warnings = [];
|
|
523
|
+
const schemas = [];
|
|
524
|
+
const enums = metadata.enums.map((enumType) => ({
|
|
525
|
+
name: toPascalCase(enumType.schemaName) + toPascalCase(enumType.enumName),
|
|
526
|
+
code: generateEnumSchema(enumType.enumName, enumType.enumValues, options, enumType.schemaName)
|
|
527
|
+
}));
|
|
528
|
+
const ranges = metadata.rangeTypes.map((rangeType) => ({
|
|
529
|
+
name: toPascalCase(rangeType.schemaName) + toPascalCase(rangeType.rangeName),
|
|
530
|
+
code: generateRangeSchema(rangeType.rangeName, rangeType.subtype, metadata, options, warnings, rangeType.schemaName)
|
|
531
|
+
}));
|
|
532
|
+
const compositeTypes = options.includeCompositeTypes ? metadata.compositeTypes.map((compositeType) => ({
|
|
533
|
+
name: toPascalCase(compositeType.schemaName) + toPascalCase(compositeType.typeName) + "Composite",
|
|
534
|
+
code: generateCompositeTypeSchema(compositeType, metadata, options, warnings)
|
|
535
|
+
})) : [];
|
|
536
|
+
const domains = metadata.domains.map((domain) => ({
|
|
537
|
+
name: toPascalCase(domain.schemaName) + toPascalCase(domain.domainName),
|
|
538
|
+
code: generateDomainSchema(domain, metadata, options, warnings)
|
|
539
|
+
}));
|
|
540
|
+
for (const table of metadata.tables) {
|
|
541
|
+
const schema = generateTableSchema(table, metadata, options, warnings);
|
|
542
|
+
schemas.push(schema);
|
|
543
|
+
}
|
|
544
|
+
return {
|
|
545
|
+
schemas,
|
|
546
|
+
enums,
|
|
547
|
+
compositeTypes,
|
|
548
|
+
domains,
|
|
549
|
+
ranges,
|
|
550
|
+
warnings
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
/**
|
|
554
|
+
* Generate enum schema
|
|
555
|
+
*/
|
|
556
|
+
function generateEnumSchema(enumName, values, options, schemaPrefix) {
|
|
557
|
+
const baseName = toPascalCase(enumName);
|
|
558
|
+
const fullName = schemaPrefix ? `${toPascalCase(schemaPrefix)}${baseName}` : baseName;
|
|
559
|
+
const schemaName = `${fullName}Schema`;
|
|
560
|
+
const typeName = fullName;
|
|
561
|
+
const valuesStr = values.map((v) => `'${v}'`).join(", ");
|
|
562
|
+
let code = "";
|
|
563
|
+
if (options.includeComments) code += `/** PostgreSQL enum: ${enumName} */\n`;
|
|
564
|
+
code += `export const ${schemaName} = z.enum([${valuesStr}]);\n`;
|
|
565
|
+
code += `export type ${typeName} = z.infer<typeof ${schemaName}>;\n`;
|
|
566
|
+
return code;
|
|
567
|
+
}
|
|
568
|
+
/**
|
|
569
|
+
* Generate range type schema
|
|
570
|
+
*/
|
|
571
|
+
function generateRangeSchema(rangeName, subtype, _metadata, options, _warnings, schemaPrefix) {
|
|
572
|
+
const baseName = toPascalCase(rangeName);
|
|
573
|
+
const fullName = schemaPrefix ? `${toPascalCase(schemaPrefix)}${baseName}` : baseName;
|
|
574
|
+
const schemaName = `${fullName}Schema`;
|
|
575
|
+
const typeName = fullName;
|
|
576
|
+
const subtypeSchema = mapSubtypeToZod(subtype, _metadata);
|
|
577
|
+
let code = "";
|
|
578
|
+
if (options.includeComments) code += `/** PostgreSQL range type: ${rangeName}<${subtype}> */\n`;
|
|
579
|
+
code += `export const ${schemaName} = z.tuple([${subtypeSchema}.nullable(), ${subtypeSchema}.nullable()]);\n`;
|
|
580
|
+
code += `export type ${typeName} = z.infer<typeof ${schemaName}>;\n`;
|
|
581
|
+
return code;
|
|
582
|
+
}
|
|
583
|
+
/**
|
|
584
|
+
* Map PostgreSQL subtype to Zod for range types
|
|
585
|
+
*/
|
|
586
|
+
function mapSubtypeToZod(subtype, _metadata) {
|
|
587
|
+
switch (subtype.toLowerCase()) {
|
|
588
|
+
case "integer":
|
|
589
|
+
case "int":
|
|
590
|
+
case "int4": return "z.number().int()";
|
|
591
|
+
case "bigint":
|
|
592
|
+
case "int8": return "z.bigint()";
|
|
593
|
+
case "numeric":
|
|
594
|
+
case "decimal": return "z.number()";
|
|
595
|
+
case "date": return "z.date()";
|
|
596
|
+
case "timestamp":
|
|
597
|
+
case "timestamp without time zone":
|
|
598
|
+
case "timestamp with time zone":
|
|
599
|
+
case "timestamptz": return "z.date()";
|
|
600
|
+
default: return "z.unknown()";
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
/**
|
|
604
|
+
* Generate composite type schema
|
|
605
|
+
*/
|
|
606
|
+
function generateCompositeTypeSchema(compositeType, metadata, options, warnings) {
|
|
607
|
+
const baseName = toPascalCase(compositeType.typeName);
|
|
608
|
+
const fullName = `${toPascalCase(compositeType.schemaName)}${baseName}Composite`;
|
|
609
|
+
const schemaName = `${fullName}Schema`;
|
|
610
|
+
const typeName = fullName;
|
|
611
|
+
let code = "";
|
|
612
|
+
if (options.includeComments) code += `/** PostgreSQL composite type: ${compositeType.typeName} */\n`;
|
|
613
|
+
code += `export const ${schemaName} = z.object({\n`;
|
|
614
|
+
for (const attr of compositeType.attributes) {
|
|
615
|
+
const fieldName = options.useCamelCase ? toCamelCase(attr.attributeName) : attr.attributeName;
|
|
616
|
+
const zodType = mapColumnToZod({
|
|
617
|
+
columnName: attr.attributeName,
|
|
618
|
+
dataType: attr.dataType,
|
|
619
|
+
isNullable: true,
|
|
620
|
+
columnDefault: null,
|
|
621
|
+
characterMaximumLength: null,
|
|
622
|
+
numericPrecision: null,
|
|
623
|
+
numericScale: null,
|
|
624
|
+
datetimePrecision: null,
|
|
625
|
+
udtName: attr.dataType,
|
|
626
|
+
domainName: null,
|
|
627
|
+
arrayDimensions: 0,
|
|
628
|
+
isArray: false
|
|
629
|
+
}, metadata, options, warnings);
|
|
630
|
+
if (options.includeComments) code += ` /** ${attr.dataType} */\n`;
|
|
631
|
+
code += ` ${fieldName}: ${zodType},\n`;
|
|
632
|
+
}
|
|
633
|
+
code += `});\n`;
|
|
634
|
+
code += `export type ${typeName} = z.infer<typeof ${schemaName}>;\n`;
|
|
635
|
+
return code;
|
|
636
|
+
}
|
|
637
|
+
/**
|
|
638
|
+
* Generate domain schema
|
|
639
|
+
*/
|
|
640
|
+
function generateDomainSchema(domain, metadata, options, warnings) {
|
|
641
|
+
const baseName = toPascalCase(domain.domainName);
|
|
642
|
+
const fullName = `${toPascalCase(domain.schemaName)}${baseName}`;
|
|
643
|
+
const schemaName = `${fullName}Schema`;
|
|
644
|
+
const typeName = fullName;
|
|
645
|
+
let zodType = mapColumnToZod({
|
|
646
|
+
columnName: domain.domainName,
|
|
647
|
+
dataType: domain.dataType,
|
|
648
|
+
isNullable: domain.isNullable,
|
|
649
|
+
columnDefault: domain.domainDefault,
|
|
650
|
+
characterMaximumLength: domain.characterMaximumLength,
|
|
651
|
+
numericPrecision: domain.numericPrecision,
|
|
652
|
+
numericScale: domain.numericScale,
|
|
653
|
+
datetimePrecision: null,
|
|
654
|
+
udtName: domain.dataType,
|
|
655
|
+
domainName: null,
|
|
656
|
+
arrayDimensions: 0,
|
|
657
|
+
isArray: false
|
|
658
|
+
}, metadata, options, warnings);
|
|
659
|
+
if (domain.checkConstraints.length > 0) zodType = applyCheckConstraints(domain.domainName, zodType, domain.checkConstraints);
|
|
660
|
+
let code = "";
|
|
661
|
+
if (options.includeComments) code += `/** PostgreSQL domain: ${domain.domainName} (base: ${domain.dataType}) */\n`;
|
|
662
|
+
code += `export const ${schemaName} = ${zodType};\n`;
|
|
663
|
+
code += `export type ${typeName} = z.infer<typeof ${schemaName}>;\n`;
|
|
664
|
+
return code;
|
|
665
|
+
}
|
|
666
|
+
/**
|
|
667
|
+
* Generate table schema
|
|
668
|
+
*/
|
|
669
|
+
function generateTableSchema(table, metadata, options, warnings) {
|
|
670
|
+
const schemaName = `${toPascalCase(table.schemaName)}${toPascalCase(table.tableName)}`;
|
|
671
|
+
const readSchemaName = `${schemaName}Schema`;
|
|
672
|
+
const insertSchemaName = `${schemaName}InsertSchema`;
|
|
673
|
+
const updateSchemaName = `${schemaName}UpdateSchema`;
|
|
674
|
+
const typeName = schemaName;
|
|
675
|
+
const insertTypeName = `${schemaName}Insert`;
|
|
676
|
+
const updateTypeName = `${schemaName}Update`;
|
|
677
|
+
let readCode = "";
|
|
678
|
+
if (options.includeComments) readCode += `/** Table: ${table.schemaName}.${table.tableName} */\n`;
|
|
679
|
+
readCode += `export const ${readSchemaName} = z.object({\n`;
|
|
680
|
+
for (const column of table.columns) {
|
|
681
|
+
const fieldName = options.useCamelCase ? toCamelCase(column.columnName) : column.columnName;
|
|
682
|
+
let zodType = mapColumnToZod(column, metadata, options, warnings);
|
|
683
|
+
const columnConstraints = table.checkConstraints.filter((c) => c.columnName === column.columnName);
|
|
684
|
+
if (columnConstraints.length > 0) zodType = applyCheckConstraints(column.columnName, zodType, columnConstraints);
|
|
685
|
+
if (options.includeComments) {
|
|
686
|
+
const commentParts = [column.dataType];
|
|
687
|
+
if (column.columnDefault) commentParts.push(`default: ${column.columnDefault}`);
|
|
688
|
+
readCode += ` /** ${commentParts.join(", ")} */\n`;
|
|
689
|
+
}
|
|
690
|
+
readCode += ` ${fieldName}: ${zodType},\n`;
|
|
691
|
+
}
|
|
692
|
+
readCode += `});\n`;
|
|
693
|
+
readCode += `export type ${typeName} = z.infer<typeof ${readSchemaName}>;\n`;
|
|
694
|
+
let insertCode;
|
|
695
|
+
let updateCode;
|
|
696
|
+
if (options.generateInputSchemas !== false) {
|
|
697
|
+
const optionalFields = [];
|
|
698
|
+
for (const column of table.columns) {
|
|
699
|
+
const fieldName = options.useCamelCase ? toCamelCase(column.columnName) : column.columnName;
|
|
700
|
+
const hasDefault = column.columnDefault !== null;
|
|
701
|
+
const isSerial = column.columnDefault?.includes("nextval") ?? false;
|
|
702
|
+
if (isSerial || column.columnDefault?.includes("gen_random_uuid()") || hasDefault) {
|
|
703
|
+
let zodType = mapColumnToZod(column, metadata, options, warnings);
|
|
704
|
+
const columnConstraints = table.checkConstraints.filter((c) => c.columnName === column.columnName);
|
|
705
|
+
if (columnConstraints.length > 0) zodType = applyCheckConstraints(column.columnName, zodType, columnConstraints);
|
|
706
|
+
zodType = `${zodType}.optional()`;
|
|
707
|
+
let comment;
|
|
708
|
+
if (options.includeComments) {
|
|
709
|
+
const commentParts = [column.dataType];
|
|
710
|
+
if (hasDefault) commentParts.push(`default: ${column.columnDefault}`);
|
|
711
|
+
if (isSerial) commentParts.push("auto-generated");
|
|
712
|
+
comment = commentParts.join(", ");
|
|
713
|
+
}
|
|
714
|
+
optionalFields.push({
|
|
715
|
+
fieldName,
|
|
716
|
+
zodType,
|
|
717
|
+
comment
|
|
718
|
+
});
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
insertCode = "";
|
|
722
|
+
if (options.includeComments) insertCode += `/** Insert schema for ${table.tableName} */\n`;
|
|
723
|
+
if (optionalFields.length === 0) insertCode += `export const ${insertSchemaName} = ${readSchemaName};\n`;
|
|
724
|
+
else {
|
|
725
|
+
insertCode += `export const ${insertSchemaName} = ${readSchemaName}.extend({\n`;
|
|
726
|
+
for (const field of optionalFields) {
|
|
727
|
+
if (field.comment) insertCode += ` /** ${field.comment} */\n`;
|
|
728
|
+
insertCode += ` ${field.fieldName}: ${field.zodType},\n`;
|
|
729
|
+
}
|
|
730
|
+
insertCode += `});\n`;
|
|
731
|
+
}
|
|
732
|
+
insertCode += `export type ${insertTypeName} = z.infer<typeof ${insertSchemaName}>;\n`;
|
|
733
|
+
updateCode = "";
|
|
734
|
+
if (options.includeComments) updateCode += `/** Update schema for ${table.tableName} (all fields optional) */\n`;
|
|
735
|
+
updateCode += `export const ${updateSchemaName} = ${readSchemaName}.partial();\n`;
|
|
736
|
+
updateCode += `export type ${updateTypeName} = z.infer<typeof ${updateSchemaName}>;\n`;
|
|
737
|
+
}
|
|
738
|
+
return {
|
|
739
|
+
tableName: table.tableName,
|
|
740
|
+
schemaName: table.schemaName,
|
|
741
|
+
readSchema: readCode,
|
|
742
|
+
inputSchema: insertCode,
|
|
743
|
+
typeDefinitions: `${readCode}${insertCode ? "\n" + insertCode : ""}${updateCode ? "\n" + updateCode : ""}`
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
/**
|
|
747
|
+
* Format the complete output file
|
|
748
|
+
*/
|
|
749
|
+
function formatOutput(result) {
|
|
750
|
+
let output = `/**\n`;
|
|
751
|
+
output += ` * ==========================================\n`;
|
|
752
|
+
output += ` * | GENERATED BY PG-TO-ZOD (TBP) |\n`;
|
|
753
|
+
output += ` * ==========================================\n`;
|
|
754
|
+
output += ` *\n`;
|
|
755
|
+
output += ` * ⚠️ DO NOT EDIT THIS FILE MANUALLY!\n`;
|
|
756
|
+
output += ` *\n`;
|
|
757
|
+
output += ` * This file was automatically generated from\n`;
|
|
758
|
+
output += ` * your PostgreSQL database schema.\n`;
|
|
759
|
+
output += ` *\n`;
|
|
760
|
+
output += ` * To regenerate, run:\n`;
|
|
761
|
+
output += ` * pg-to-zod --url <connection-url> -o <file>\n`;
|
|
762
|
+
output += ` *\n`;
|
|
763
|
+
output += ` * Any manual changes will be overwritten when\n`;
|
|
764
|
+
output += ` * the code is regenerated.\n`;
|
|
765
|
+
output += ` * ==========================================\n`;
|
|
766
|
+
output += ` */\n\n`;
|
|
767
|
+
output += `import { z } from 'zod';\n\n`;
|
|
768
|
+
if (result.enums.length > 0) {
|
|
769
|
+
output += `// ============================================\n`;
|
|
770
|
+
output += `// Enums\n`;
|
|
771
|
+
output += `// ============================================\n\n`;
|
|
772
|
+
for (const enumSchema of result.enums) output += enumSchema.code + "\n";
|
|
773
|
+
}
|
|
774
|
+
if (result.domains.length > 0) {
|
|
775
|
+
output += `// ============================================\n`;
|
|
776
|
+
output += `// Domains\n`;
|
|
777
|
+
output += `// ============================================\n\n`;
|
|
778
|
+
for (const domain of result.domains) output += domain.code + "\n";
|
|
779
|
+
}
|
|
780
|
+
if (result.ranges.length > 0) {
|
|
781
|
+
output += `// ============================================\n`;
|
|
782
|
+
output += `// Range Types\n`;
|
|
783
|
+
output += `// ============================================\n\n`;
|
|
784
|
+
for (const range of result.ranges) output += range.code + "\n";
|
|
785
|
+
}
|
|
786
|
+
if (result.compositeTypes.length > 0) {
|
|
787
|
+
output += `// ============================================\n`;
|
|
788
|
+
output += `// Composite Types\n`;
|
|
789
|
+
output += `// ============================================\n\n`;
|
|
790
|
+
for (const compositeType of result.compositeTypes) output += compositeType.code + "\n";
|
|
791
|
+
}
|
|
792
|
+
if (result.schemas.length > 0) {
|
|
793
|
+
output += `// ============================================\n`;
|
|
794
|
+
output += `// Tables\n`;
|
|
795
|
+
output += `// ============================================\n\n`;
|
|
796
|
+
for (const schema of result.schemas) output += schema.typeDefinitions + "\n";
|
|
797
|
+
}
|
|
798
|
+
if (result.warnings.length > 0) {
|
|
799
|
+
output += `// ============================================\n`;
|
|
800
|
+
output += `// Warnings\n`;
|
|
801
|
+
output += `// ============================================\n`;
|
|
802
|
+
output += `// The following warnings were generated:\n`;
|
|
803
|
+
for (const warning of result.warnings) output += `// - ${warning}\n`;
|
|
804
|
+
}
|
|
805
|
+
return output;
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
//#endregion
|
|
809
|
+
//#region src/index.ts
|
|
810
|
+
/**
|
|
811
|
+
* Main function: introspect database and generate Zod schemas
|
|
812
|
+
*/
|
|
813
|
+
async function generateZodSchemas(config, options = {}) {
|
|
814
|
+
return generateSchemas(await introspectDatabase(config, options), options);
|
|
815
|
+
}
|
|
816
|
+
/**
|
|
817
|
+
* Convenience function: generate schemas and return formatted output string
|
|
818
|
+
*/
|
|
819
|
+
async function generateZodSchemasString(config, options = {}) {
|
|
820
|
+
return formatOutput(await generateZodSchemas(config, options));
|
|
821
|
+
}
|
|
822
|
+
/**
|
|
823
|
+
* Default export
|
|
824
|
+
*/
|
|
825
|
+
var src_default = {
|
|
826
|
+
generateZodSchemas,
|
|
827
|
+
generateZodSchemasString,
|
|
828
|
+
introspectDatabase,
|
|
829
|
+
generateSchemas,
|
|
830
|
+
formatOutput
|
|
831
|
+
};
|
|
832
|
+
|
|
833
|
+
//#endregion
|
|
834
|
+
export { generateSchemas as a, toPascalCase as c, formatOutput as i, introspectDatabase as l, generateZodSchemasString as n, mapColumnToZod as o, src_default as r, toCamelCase as s, generateZodSchemas as t };
|
|
835
|
+
//# sourceMappingURL=src-18pV45Fu.js.map
|