true-pg 0.5.0 → 0.6.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/README.md +17 -17
- package/lib/bin.js +16 -16
- package/lib/config.d.ts +6 -6
- package/lib/config.js +8 -8
- package/lib/extractor/index.d.ts +9 -8
- package/lib/extractor/index.js +10 -12
- package/lib/extractor/pgtype.d.ts +6 -6
- package/lib/extractor/pgtype.js +7 -1
- package/lib/imports.d.ts +41 -0
- package/lib/imports.js +102 -0
- package/lib/index.js +75 -63
- package/lib/kysely/index.js +87 -95
- package/lib/types.d.ts +32 -77
- package/lib/types.js +2 -176
- package/lib/util.d.ts +2 -0
- package/lib/util.js +13 -0
- package/lib/zod/index.js +100 -98
- package/package.json +3 -2
package/lib/zod/index.js
CHANGED
@@ -1,28 +1,18 @@
|
|
1
|
-
import { FunctionReturnTypeKind, } from "../extractor/index.js";
|
2
|
-
import { allowed_kind_names, createGenerator
|
1
|
+
import { Canonical, FunctionReturnTypeKind, } from "../extractor/index.js";
|
2
|
+
import { allowed_kind_names, createGenerator } from "../types.js";
|
3
|
+
import { Import } from "../imports.js";
|
3
4
|
import { builtins } from "./builtins.js";
|
4
5
|
import { to_snake_case, join, quote, quoteI } from "../util.js";
|
5
6
|
// TODO: create an insert and update zod interface for each type?
|
6
7
|
export const Zod = createGenerator(opts => {
|
7
8
|
const defaultSchema = opts?.defaultSchema ?? "public";
|
8
|
-
const zod = (
|
9
|
-
|
10
|
-
|
9
|
+
const zod = (ctx, name) => ctx.imports.add(new Import({
|
10
|
+
from: "zod",
|
11
|
+
namedImports: name ? [name] : undefined,
|
11
12
|
typeOnly: false,
|
12
|
-
star:
|
13
|
+
star: name ? undefined : "z",
|
13
14
|
}));
|
14
|
-
const
|
15
|
-
if (type.schema === "pg_catalog")
|
16
|
-
zod(imports, "z");
|
17
|
-
else
|
18
|
-
imports.add(new Nodes.InternalImport({
|
19
|
-
name: generator.formatType(type),
|
20
|
-
canonical_type: type,
|
21
|
-
typeOnly: false,
|
22
|
-
star: false,
|
23
|
-
}));
|
24
|
-
};
|
25
|
-
const column = (imports,
|
15
|
+
const column = (ctx,
|
26
16
|
/** Information about the column */
|
27
17
|
col) => {
|
28
18
|
// don't create a property for always generated columns
|
@@ -30,114 +20,137 @@ export const Zod = createGenerator(opts => {
|
|
30
20
|
return "";
|
31
21
|
let out = col.comment ? `/** ${col.comment} */\n\t` : "";
|
32
22
|
out += quoteI(col.name);
|
33
|
-
|
34
|
-
|
35
|
-
if (
|
36
|
-
type +=
|
37
|
-
if (col.isNullable || col.generated === "BY DEFAULT" || col.defaultValue)
|
38
|
-
type += `.nullable().optional()`;
|
23
|
+
const nullable = col.isNullable || col.generated === "BY DEFAULT" || col.defaultValue;
|
24
|
+
let type = generator.formatType(ctx, col.type, { nullable });
|
25
|
+
if (nullable)
|
26
|
+
type += `.optional()`;
|
39
27
|
out += `: ${type}`;
|
40
28
|
return `\t${out},\n`;
|
41
29
|
};
|
42
|
-
const composite_attribute = (
|
30
|
+
const composite_attribute = (ctx, attr) => {
|
43
31
|
let out = quoteI(attr.name);
|
44
|
-
out += `: ${generator.formatType(attr.type)}`;
|
45
|
-
add(imports, attr.type);
|
46
|
-
if (attr.type.dimensions > 0)
|
47
|
-
out += ".array()".repeat(attr.type.dimensions);
|
32
|
+
out += `: ${generator.formatType(ctx, attr.type, { nullable: attr.isNullable })}`;
|
48
33
|
if (attr.isNullable)
|
49
|
-
out += ".
|
34
|
+
out += ".optional()";
|
50
35
|
return out;
|
51
36
|
};
|
52
37
|
const generator = {
|
53
|
-
|
38
|
+
formatSchemaName(name) {
|
54
39
|
return to_snake_case(name) + "_validators";
|
55
40
|
},
|
56
|
-
|
41
|
+
formatSchemaMemberName(type) {
|
57
42
|
return to_snake_case(type.name);
|
58
43
|
},
|
59
|
-
formatType(type) {
|
44
|
+
formatType(ctx, type, attr) {
|
45
|
+
let base;
|
60
46
|
if (type.kind === FunctionReturnTypeKind.ExistingTable) {
|
61
|
-
|
47
|
+
base = to_snake_case(type.name);
|
48
|
+
ctx.imports.add(Import.fromInternal({
|
49
|
+
source: ctx.source,
|
50
|
+
type,
|
51
|
+
withName: base,
|
52
|
+
typeOnly: false,
|
53
|
+
}));
|
62
54
|
}
|
63
|
-
else if (type.schema === "pg_catalog"
|
55
|
+
else if (type.schema === "pg_catalog" ||
|
56
|
+
type.kind === Canonical.Kind.Base ||
|
57
|
+
type.kind === Canonical.Kind.Pseudo) {
|
64
58
|
const name = type.canonical_name;
|
65
59
|
const format = builtins[name];
|
66
60
|
if (format)
|
67
|
-
|
68
|
-
|
69
|
-
|
61
|
+
base = format;
|
62
|
+
else {
|
63
|
+
opts?.warnings?.add(`(zod) Unknown base type: ${name}. Pass 'zod.builtinMap' to map this type. Defaulting to "z.unknown()".`);
|
64
|
+
base = "z.unknown()";
|
65
|
+
}
|
66
|
+
ctx.imports.add(new Import({
|
67
|
+
from: "zod",
|
68
|
+
namedImports: ["z"],
|
69
|
+
}));
|
70
70
|
}
|
71
|
-
|
71
|
+
else {
|
72
|
+
base = to_snake_case(type.name);
|
73
|
+
ctx.imports.add(Import.fromInternal({
|
74
|
+
source: ctx.source,
|
75
|
+
type,
|
76
|
+
withName: base,
|
77
|
+
typeOnly: false,
|
78
|
+
}));
|
79
|
+
}
|
80
|
+
if ("dimensions" in type)
|
81
|
+
base += ".array()".repeat(type.dimensions);
|
82
|
+
if (attr?.nullable)
|
83
|
+
base += ".nullable()";
|
84
|
+
return base;
|
72
85
|
},
|
73
|
-
table(
|
86
|
+
table(ctx, table) {
|
74
87
|
let out = "";
|
75
88
|
if (table.comment)
|
76
89
|
out += `/** ${table.comment} */\n`;
|
77
|
-
out += `export const ${this.
|
78
|
-
zod(
|
90
|
+
out += `export const ${this.formatSchemaMemberName(table)} = z.object({\n`;
|
91
|
+
zod(ctx, "z");
|
79
92
|
for (const col of table.columns)
|
80
|
-
out += column(
|
93
|
+
out += column(ctx, col);
|
81
94
|
out += "});";
|
82
95
|
return out;
|
83
96
|
},
|
84
|
-
view(
|
97
|
+
view(ctx, view) {
|
85
98
|
let out = "";
|
86
99
|
if (view.comment)
|
87
100
|
out += `/** ${view.comment} */\n`;
|
88
|
-
zod(
|
89
|
-
out += `export const ${this.
|
101
|
+
zod(ctx, "z");
|
102
|
+
out += `export const ${this.formatSchemaMemberName(view)} = z.object({\n`;
|
90
103
|
for (const col of view.columns)
|
91
|
-
out += column(
|
104
|
+
out += column(ctx, col);
|
92
105
|
out += "});";
|
93
106
|
return out;
|
94
107
|
},
|
95
|
-
materializedView(
|
108
|
+
materializedView(ctx, materializedView) {
|
96
109
|
let out = "";
|
97
110
|
if (materializedView.comment)
|
98
111
|
out += `/** ${materializedView.comment} */\n`;
|
99
|
-
zod(
|
100
|
-
out += `export const ${this.
|
112
|
+
zod(ctx, "z");
|
113
|
+
out += `export const ${this.formatSchemaMemberName(materializedView)} = z.object({\n`;
|
101
114
|
for (const col of materializedView.columns)
|
102
|
-
out += column(
|
115
|
+
out += column(ctx, col);
|
103
116
|
out += "});";
|
104
117
|
return out;
|
105
118
|
},
|
106
|
-
enum(
|
119
|
+
enum(ctx, en) {
|
107
120
|
let out = "";
|
108
121
|
if (en.comment)
|
109
122
|
out += `/** ${en.comment} */\n`;
|
110
|
-
out += `export const ${this.
|
123
|
+
out += `export const ${this.formatSchemaMemberName(en)} = z.union([\n`;
|
111
124
|
out += en.values.map(v => `\tz.literal("${v}")`).join(",\n");
|
112
125
|
out += "\n]);";
|
113
|
-
zod(
|
126
|
+
zod(ctx, "z");
|
114
127
|
return out;
|
115
128
|
},
|
116
|
-
composite(
|
129
|
+
composite(ctx, type) {
|
117
130
|
let out = "";
|
118
131
|
if (type.comment)
|
119
132
|
out += `/** ${type.comment} */\n`;
|
120
|
-
out += `export const ${this.
|
121
|
-
|
133
|
+
out += `export const ${this.formatSchemaMemberName(type)} = z.object({\n`;
|
134
|
+
zod(ctx, "z");
|
135
|
+
const props = type.canonical.attributes.map(c => composite_attribute(ctx, c)).map(t => `\t${t},`);
|
122
136
|
out += props.join("\n");
|
123
137
|
out += "\n});";
|
124
138
|
return out;
|
125
139
|
},
|
126
|
-
domain(
|
140
|
+
domain(ctx, type) {
|
127
141
|
let out = "";
|
128
|
-
out += `export const ${this.
|
129
|
-
zod(imports, "z");
|
142
|
+
out += `export const ${this.formatSchemaMemberName(type)} = ${this.formatType(ctx, type.canonical.domain_base_type)};`;
|
130
143
|
return out;
|
131
144
|
},
|
132
|
-
range(
|
145
|
+
range(ctx, type) {
|
133
146
|
let out = "";
|
134
|
-
out += `export const ${this.
|
135
|
-
zod(
|
147
|
+
out += `export const ${this.formatSchemaMemberName(type)} = z.string();`;
|
148
|
+
zod(ctx, "z");
|
136
149
|
return out;
|
137
150
|
},
|
138
|
-
function(
|
151
|
+
function(ctx, type) {
|
139
152
|
let out = "export const ";
|
140
|
-
out += this.
|
153
|
+
out += this.formatSchemaMemberName(type);
|
141
154
|
out += " = {\n";
|
142
155
|
out += `\tparameters: z.tuple([`;
|
143
156
|
// Get the input parameters (those that appear in function signature)
|
@@ -149,12 +162,9 @@ export const Zod = createGenerator(opts => {
|
|
149
162
|
out += "\n";
|
150
163
|
for (const param of inputParams) {
|
151
164
|
// TODO: update imports for non-primitive types based on typeInfo.kind
|
152
|
-
out += "\t\t" + this.formatType(param.type);
|
153
|
-
add(imports, param.type);
|
154
|
-
if (param.type.dimensions > 0)
|
155
|
-
out += ".array()".repeat(param.type.dimensions);
|
165
|
+
out += "\t\t" + this.formatType(ctx, param.type, { nullable: param.hasDefault });
|
156
166
|
if (param.hasDefault)
|
157
|
-
out += ".
|
167
|
+
out += ".optional()";
|
158
168
|
out += `, // ${param.name}\n`;
|
159
169
|
}
|
160
170
|
out += "\t])";
|
@@ -162,10 +172,8 @@ export const Zod = createGenerator(opts => {
|
|
162
172
|
const variadic = type.parameters.find(p => p.mode === "VARIADIC");
|
163
173
|
if (variadic) {
|
164
174
|
out += ".rest(";
|
165
|
-
out += this.formatType(variadic.type);
|
166
175
|
// reduce by 1 because it's already a rest parameter
|
167
|
-
|
168
|
-
out += ".array()".repeat(variadic.type.dimensions - 1);
|
176
|
+
out += this.formatType(ctx, { ...variadic.type, dimensions: variadic.type.dimensions - 1 });
|
169
177
|
out += ")" + ", // " + variadic.name + "\n";
|
170
178
|
}
|
171
179
|
else
|
@@ -176,60 +184,54 @@ export const Zod = createGenerator(opts => {
|
|
176
184
|
out += "z.void()/* RETURNS TABLE with no columns */";
|
177
185
|
else {
|
178
186
|
out += "z.object({\n";
|
187
|
+
zod(ctx, "z");
|
179
188
|
for (const col of type.returnType.columns) {
|
180
189
|
out += `\t\t"${col.name}": `;
|
181
|
-
out += this.formatType(col.type);
|
182
|
-
add(imports, col.type);
|
183
|
-
if (col.type.dimensions > 0)
|
184
|
-
out += ".array()".repeat(col.type.dimensions);
|
190
|
+
out += this.formatType(ctx, col.type); // ignore nullability of inline table columns
|
185
191
|
out += `,\n`;
|
186
192
|
}
|
187
193
|
out += "\t})";
|
188
194
|
}
|
189
195
|
}
|
190
196
|
else if (type.returnType.kind === FunctionReturnTypeKind.ExistingTable) {
|
191
|
-
out += this.formatType(type.returnType);
|
192
|
-
add(imports, type.returnType);
|
197
|
+
out += this.formatType(ctx, type.returnType);
|
193
198
|
}
|
194
199
|
else {
|
195
|
-
out += this.formatType(type.returnType.type);
|
196
|
-
add(imports, type.returnType.type);
|
197
|
-
if (type.returnType.type.dimensions > 0)
|
198
|
-
out += ".array()".repeat(type.returnType.type.dimensions);
|
200
|
+
out += this.formatType(ctx, type.returnType.type);
|
199
201
|
}
|
200
202
|
// Add additional array brackets if it returns a set
|
201
203
|
if (type.returnType.isSet)
|
202
204
|
out += ".array()";
|
203
205
|
out += ",\n};";
|
204
|
-
zod(imports, "z");
|
205
206
|
return out;
|
206
207
|
},
|
207
|
-
schemaKindIndex(schema, kind, main_generator) {
|
208
|
+
schemaKindIndex(ctx, schema, kind, main_generator) {
|
208
209
|
const imports = schema[kind];
|
209
210
|
if (imports.length === 0)
|
210
211
|
return "";
|
211
212
|
const generator = main_generator ?? this;
|
212
213
|
return imports
|
213
214
|
.map(each => {
|
214
|
-
const name = this.
|
215
|
-
const file = generator.
|
215
|
+
const name = this.formatSchemaMemberName(each);
|
216
|
+
const file = generator.formatSchemaMemberName(each);
|
216
217
|
return `export { ${name} } from "./${file}.ts";`;
|
217
218
|
})
|
218
219
|
.join("\n");
|
219
220
|
},
|
220
|
-
schemaIndex(schema, main_generator) {
|
221
|
+
schemaIndex(ctx, schema, main_generator) {
|
221
222
|
const actual_kinds = allowed_kind_names.filter(kind => schema[kind].length);
|
222
|
-
|
223
|
+
// we could in theory use the imports from GeneratorContext here, but this works fine
|
224
|
+
let out = actual_kinds.map(kind => `import * as zod_${kind}s from "./${kind}s/index.ts";`).join("\n");
|
223
225
|
out += "\n\n";
|
224
|
-
out += `export const ${this.
|
226
|
+
out += `export const ${this.formatSchemaName(schema.name)} = {\n`;
|
225
227
|
for (const kind of actual_kinds) {
|
226
228
|
const items = schema[kind];
|
227
229
|
if (items.length === 0)
|
228
230
|
continue;
|
229
|
-
out += `\t${kind}: {\n`;
|
231
|
+
out += `\t${kind}s: {\n`;
|
230
232
|
const formatted = items
|
231
233
|
.map(each => {
|
232
|
-
const formatted = this.
|
234
|
+
const formatted = this.formatSchemaMemberName(each);
|
233
235
|
return { ...each, formatted };
|
234
236
|
})
|
235
237
|
.filter(x => x !== undefined);
|
@@ -244,11 +246,11 @@ export const Zod = createGenerator(opts => {
|
|
244
246
|
out += "}";
|
245
247
|
return out;
|
246
248
|
},
|
247
|
-
fullIndex(schemas, main_generator) {
|
249
|
+
fullIndex(ctx, schemas, main_generator) {
|
248
250
|
const generator = main_generator ?? this;
|
249
251
|
const parts = [];
|
250
252
|
parts.push(schemas
|
251
|
-
.map(s => `import { ${generator.
|
253
|
+
.map(s => `import { ${generator.formatSchemaName(s.name)} } from "./${s.name}/index.ts";`)
|
252
254
|
.join("\n"));
|
253
255
|
{
|
254
256
|
let validator = `export const Validators = {\n`;
|
@@ -258,7 +260,7 @@ export const Zod = createGenerator(opts => {
|
|
258
260
|
const seen = new Set();
|
259
261
|
const formatted = current
|
260
262
|
.map(each => {
|
261
|
-
const formatted = generator.
|
263
|
+
const formatted = generator.formatSchemaMemberName(each);
|
262
264
|
// skip clashing names
|
263
265
|
if (seen.has(formatted))
|
264
266
|
return;
|
@@ -269,7 +271,7 @@ export const Zod = createGenerator(opts => {
|
|
269
271
|
if (!formatted.length)
|
270
272
|
return "";
|
271
273
|
let out = "";
|
272
|
-
out += "\t// " + kind + "\n";
|
274
|
+
out += "\t// " + kind + "s\n";
|
273
275
|
out += join(formatted.map(t => {
|
274
276
|
const isDefault = defaultSchema === schema.name;
|
275
277
|
let qualified = "";
|
@@ -278,7 +280,7 @@ export const Zod = createGenerator(opts => {
|
|
278
280
|
else
|
279
281
|
qualified = t.name;
|
280
282
|
qualified = quoteI(qualified);
|
281
|
-
return `\t${qualified}: ${this.
|
283
|
+
return `\t${qualified}: ${this.formatSchemaName(schema.name)}[${quote(t.kind + "s")}][${quote(t.name)}],`;
|
282
284
|
}), "\n");
|
283
285
|
return out;
|
284
286
|
}));
|
@@ -287,7 +289,7 @@ export const Zod = createGenerator(opts => {
|
|
287
289
|
validator += "\n};";
|
288
290
|
parts.push(validator);
|
289
291
|
}
|
290
|
-
parts.push(schemas.map(s => `export type { ${this.
|
292
|
+
parts.push(schemas.map(s => `export type { ${this.formatSchemaName(s.name)} };`).join("\n"));
|
291
293
|
return join(parts);
|
292
294
|
},
|
293
295
|
};
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "true-pg",
|
3
|
-
"version": "0.
|
3
|
+
"version": "0.6.0",
|
4
4
|
"type": "module",
|
5
5
|
"module": "lib/index.js",
|
6
6
|
"main": "lib/index.js",
|
@@ -22,7 +22,8 @@
|
|
22
22
|
},
|
23
23
|
"devDependencies": {
|
24
24
|
"@types/bun": "latest",
|
25
|
-
"@types/pg": "^8.11.13"
|
25
|
+
"@types/pg": "^8.11.13",
|
26
|
+
"kysely-pglite": "^0.6.1"
|
26
27
|
},
|
27
28
|
"peerDependencies": {
|
28
29
|
"kysely": "^0.27",
|