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