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/lib/zod/index.js CHANGED
@@ -1,28 +1,18 @@
1
- import { FunctionReturnTypeKind, } from "../extractor/index.js";
2
- import { allowed_kind_names, createGenerator, Nodes } from "../types.js";
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 = (imports, name) => imports.add(new Nodes.ExternalImport({
9
- name: name ?? "z",
10
- module: "zod",
9
+ const zod = (ctx, name) => ctx.imports.add(new Import({
10
+ from: "zod",
11
+ namedImports: name ? [name] : undefined,
11
12
  typeOnly: false,
12
- star: !name,
13
+ star: name ? undefined : "z",
13
14
  }));
14
- const add = (imports, type) => {
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
- let type = generator.formatType(col.type);
34
- add(imports, col.type);
35
- if (col.type.dimensions > 0)
36
- type += ".array()".repeat(col.type.dimensions);
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 = (imports, attr) => {
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 += ".nullable().optional()";
34
+ out += ".optional()";
50
35
  return out;
51
36
  };
52
37
  const generator = {
53
- formatSchema(name) {
38
+ formatSchemaName(name) {
54
39
  return to_snake_case(name) + "_validators";
55
40
  },
56
- formatSchemaType(type) {
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
- return to_snake_case(type.name);
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
- return format;
68
- opts?.warnings?.push(`(zod) Unknown builtin type: ${name}. Pass customBuiltinMap to map this type. Defaulting to "z.unknown()".`);
69
- return "z.unknown()";
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
- return to_snake_case(type.name);
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(imports, 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.formatSchemaType(table)} = z.object({\n`;
78
- zod(imports, "z");
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(imports, col);
93
+ out += column(ctx, col);
81
94
  out += "});";
82
95
  return out;
83
96
  },
84
- view(imports, view) {
97
+ view(ctx, view) {
85
98
  let out = "";
86
99
  if (view.comment)
87
100
  out += `/** ${view.comment} */\n`;
88
- zod(imports, "z");
89
- out += `export const ${this.formatSchemaType(view)} = z.object({\n`;
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(imports, col);
104
+ out += column(ctx, col);
92
105
  out += "});";
93
106
  return out;
94
107
  },
95
- materializedView(imports, materializedView) {
108
+ materializedView(ctx, materializedView) {
96
109
  let out = "";
97
110
  if (materializedView.comment)
98
111
  out += `/** ${materializedView.comment} */\n`;
99
- zod(imports, "z");
100
- out += `export const ${this.formatSchemaType(materializedView)} = z.object({\n`;
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(imports, col);
115
+ out += column(ctx, col);
103
116
  out += "});";
104
117
  return out;
105
118
  },
106
- enum(imports, en) {
119
+ enum(ctx, en) {
107
120
  let out = "";
108
121
  if (en.comment)
109
122
  out += `/** ${en.comment} */\n`;
110
- out += `export const ${this.formatSchemaType(en)} = z.union([\n`;
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(imports, "z");
126
+ zod(ctx, "z");
114
127
  return out;
115
128
  },
116
- composite(imports, type) {
129
+ composite(ctx, type) {
117
130
  let out = "";
118
131
  if (type.comment)
119
132
  out += `/** ${type.comment} */\n`;
120
- out += `export const ${this.formatSchemaType(type)} = z.object({\n`;
121
- const props = type.canonical.attributes.map(c => composite_attribute(imports, c)).map(t => `\t${t},`);
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(imports, type) {
140
+ domain(ctx, type) {
127
141
  let out = "";
128
- out += `export const ${this.formatSchemaType(type)} = ${this.formatType(type.canonical.domain_base_type)};`;
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(imports, type) {
145
+ range(ctx, type) {
133
146
  let out = "";
134
- out += `export const ${this.formatSchemaType(type)} = z.string();`;
135
- zod(imports, "z");
147
+ out += `export const ${this.formatSchemaMemberName(type)} = z.string();`;
148
+ zod(ctx, "z");
136
149
  return out;
137
150
  },
138
- function(imports, type) {
151
+ function(ctx, type) {
139
152
  let out = "export const ";
140
- out += this.formatSchemaType(type);
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 += ".nullable().optional()";
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
- if (variadic.type.dimensions > 1)
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.formatSchemaType(each);
215
- const file = generator.formatSchemaType(each);
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
- let out = actual_kinds.map(kind => `import * as zod_${kind} from "./${kind}/index.ts";`).join("\n");
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.formatSchema(schema.name)} = {\n`;
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.formatSchemaType(each);
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.formatSchema(s.name)} } from "./${s.name}/index.ts";`)
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.formatSchemaType(each);
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.formatSchema(schema.name)}[${quote(t.kind + "s")}][${quote(t.name)}],`;
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.formatSchema(s.name)} };`).join("\n"));
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.5.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",