true-pg 0.2.2 → 0.3.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.
Files changed (48) hide show
  1. package/lib/bin.js +4 -9
  2. package/lib/extractor/adapter.d.ts +15 -0
  3. package/lib/extractor/adapter.js +53 -0
  4. package/lib/extractor/canonicalise.d.ts +64 -0
  5. package/lib/extractor/canonicalise.js +245 -0
  6. package/lib/extractor/fetchExtensionItemIds.d.ts +12 -0
  7. package/lib/extractor/fetchExtensionItemIds.js +43 -0
  8. package/lib/extractor/fetchTypes.d.ts +4 -0
  9. package/lib/extractor/fetchTypes.js +65 -0
  10. package/lib/extractor/index.d.ts +95 -0
  11. package/lib/extractor/index.js +140 -0
  12. package/lib/extractor/kinds/composite.d.ts +15 -0
  13. package/lib/extractor/kinds/composite.js +13 -0
  14. package/lib/extractor/kinds/domain.d.ts +15 -0
  15. package/lib/extractor/kinds/domain.js +13 -0
  16. package/lib/extractor/kinds/enum.d.ts +9 -0
  17. package/lib/extractor/kinds/enum.js +20 -0
  18. package/lib/extractor/kinds/function.d.ts +73 -0
  19. package/lib/extractor/kinds/function.js +179 -0
  20. package/lib/extractor/kinds/materialized-view.d.ts +17 -0
  21. package/lib/extractor/kinds/materialized-view.js +64 -0
  22. package/lib/extractor/kinds/parts/commentMapQueryPart.d.ts +2 -0
  23. package/lib/extractor/kinds/parts/commentMapQueryPart.js +14 -0
  24. package/lib/extractor/kinds/parts/indexMapQueryPart.d.ts +2 -0
  25. package/lib/extractor/kinds/parts/indexMapQueryPart.js +27 -0
  26. package/lib/extractor/kinds/range.d.ts +15 -0
  27. package/lib/extractor/kinds/range.js +13 -0
  28. package/lib/extractor/kinds/table.d.ts +212 -0
  29. package/lib/extractor/kinds/table.js +217 -0
  30. package/lib/extractor/kinds/util/parseInlineTable.d.ts +9 -0
  31. package/lib/extractor/kinds/util/parseInlineTable.js +135 -0
  32. package/lib/extractor/kinds/util/parseInlineTable.test.d.ts +1 -0
  33. package/lib/extractor/kinds/util/parseInlineTable.test.js +26 -0
  34. package/lib/extractor/kinds/view.d.ts +17 -0
  35. package/lib/extractor/kinds/view.js +63 -0
  36. package/lib/extractor/pgtype.d.ts +41 -0
  37. package/lib/extractor/pgtype.js +30 -0
  38. package/lib/index.d.ts +2 -2
  39. package/lib/index.js +38 -28
  40. package/lib/kysely/builtins.js +6 -4
  41. package/lib/kysely/index.js +103 -59
  42. package/lib/types.d.ts +38 -10
  43. package/lib/types.js +14 -3
  44. package/lib/util.d.ts +11 -0
  45. package/lib/util.js +6 -0
  46. package/lib/zod/builtins.js +11 -9
  47. package/lib/zod/index.js +107 -60
  48. package/package.json +3 -4
@@ -0,0 +1,41 @@
1
+ export declare const typeKindMap: {
2
+ readonly d: "domain";
3
+ readonly e: "enum";
4
+ readonly r: "range";
5
+ };
6
+ type TypeKind = (typeof typeKindMap)[keyof typeof typeKindMap];
7
+ export declare const classKindMap: {
8
+ readonly r: "table";
9
+ readonly p: "table";
10
+ readonly v: "view";
11
+ readonly m: "materializedView";
12
+ readonly c: "composite";
13
+ };
14
+ type ClassKind = (typeof classKindMap)[keyof typeof classKindMap];
15
+ export declare const routineKindMap: {
16
+ readonly f: "function";
17
+ };
18
+ type RoutineKind = (typeof routineKindMap)[keyof typeof routineKindMap];
19
+ export type Kind = TypeKind | ClassKind | RoutineKind;
20
+ /**
21
+ * Base type for Postgres objects.
22
+ */
23
+ export type PgType<K extends Kind = Kind> = {
24
+ /**
25
+ * The name of the object.
26
+ */
27
+ name: string;
28
+ /**
29
+ * The name of the schema that the object is in.
30
+ */
31
+ schemaName: string;
32
+ /**
33
+ * The kind of the object.
34
+ */
35
+ kind: K;
36
+ /**
37
+ * The comment on the object, if any.
38
+ */
39
+ comment: string | null;
40
+ };
41
+ export {};
@@ -0,0 +1,30 @@
1
+ export const typeKindMap = {
2
+ d: "domain",
3
+ e: "enum",
4
+ r: "range",
5
+ // Not supported (yet):
6
+ // m: 'multiRange',
7
+ // b: 'base',
8
+ // p: 'pseudo',
9
+ // c: 'composite', -- is also a class, handled below.
10
+ };
11
+ export const classKindMap = {
12
+ r: "table",
13
+ p: "table", // Treat partitioned tables as tables
14
+ v: "view",
15
+ m: "materializedView",
16
+ c: "composite",
17
+ // f: "foreignTable",
18
+ // Not supported (yet):
19
+ // i: 'index',
20
+ // S: 'sequence',
21
+ // t: 'toastTable',
22
+ // I: 'partitionedIndex',
23
+ };
24
+ export const routineKindMap = {
25
+ // p: "procedure",
26
+ f: "function",
27
+ // Not supported (yet):
28
+ // a: 'aggregate',
29
+ // w: 'windowFunction',
30
+ };
package/lib/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { type TruePGOpts, type createGenerator } from "./types.ts";
1
+ import { type TruePGConfig, type createGenerator } from "./types.ts";
2
2
  export { config } from "./types.ts";
3
3
  export declare const adapters: Record<string, createGenerator>;
4
- export declare function generate(opts: TruePGOpts, generators?: createGenerator[]): Promise<void>;
4
+ export declare function generate(opts: TruePGConfig, generators?: createGenerator[]): Promise<void>;
package/lib/index.js CHANGED
@@ -1,17 +1,16 @@
1
- import { Extractor } from "pg-extract";
1
+ import { Extractor, FunctionReturnTypeKind } from "./extractor/index.js";
2
2
  import { rm, mkdir, writeFile } from "fs/promises";
3
3
  import { Nodes, allowed_kind_names } from "./types.js";
4
4
  import { existsSync } from "fs";
5
5
  import { join } from "./util.js";
6
6
  export { config } from "./types.js";
7
- // import { Pool } from "pg";
8
7
  import { Kysely } from "./kysely/index.js";
9
8
  import { Zod } from "./zod/index.js";
10
9
  export const adapters = {
11
10
  kysely: Kysely,
12
11
  zod: Zod,
13
12
  };
14
- const filter_function = (func, warnings) => {
13
+ const filter_function = (func) => {
15
14
  const typesToFilter = [
16
15
  "pg_catalog.trigger",
17
16
  "pg_catalog.event_trigger",
@@ -21,24 +20,25 @@ const filter_function = (func, warnings) => {
21
20
  "pg_catalog.index_am_handler",
22
21
  "pg_catalog.tsm_handler",
23
22
  ];
24
- const warn = (type) => warnings.push(`Skipping function ${func.name}: cannot represent ${type} (safe to ignore)`);
25
- if (func.returnType.kind === "table") {
23
+ if (func.returnType.kind === FunctionReturnTypeKind.InlineTable) {
26
24
  for (const col of func.returnType.columns) {
27
25
  if (typesToFilter.includes(col.type.canonical_name)) {
28
- warn(col.type.canonical_name);
29
26
  return false;
30
27
  }
31
28
  }
32
29
  }
30
+ else if (func.returnType.kind === FunctionReturnTypeKind.ExistingTable) {
31
+ if (typesToFilter.includes(func.returnType.schema + "." + func.returnType.name)) {
32
+ return false;
33
+ }
34
+ }
33
35
  else {
34
36
  if (typesToFilter.includes(func.returnType.type.canonical_name)) {
35
- warn(func.returnType.type.canonical_name);
36
37
  return false;
37
38
  }
38
39
  }
39
40
  for (const param of func.parameters) {
40
41
  if (typesToFilter.includes(param.type.canonical_name)) {
41
- warn(param.type.canonical_name);
42
42
  return false;
43
43
  }
44
44
  }
@@ -75,12 +75,18 @@ const multifile = async (generators, schemas, opts) => {
75
75
  },
76
76
  ])),
77
77
  };
78
- const start = performance.now();
79
78
  for (const schema of Object.values(schemas)) {
80
79
  console.log("Selected schema '%s':\n", schema.name);
81
80
  const schemaDir = `${out}/${schema.name}`;
82
81
  // skip functions that cannot be represented in JavaScript
83
- schema.functions = schema.functions.filter(f => filter_function(f, warnings));
82
+ schema.functions = schema.functions.filter(filter_function);
83
+ {
84
+ const skipped = schema.functions.filter(f => !filter_function(f));
85
+ const skipped_functions = skipped.map(f => ` - ${f.name}`).join("\n");
86
+ if (skipped.length) {
87
+ warnings.push(`Skipping ${skipped.length} functions because they cannot be represented in JavaScript (safe to ignore):\n${skipped_functions}`);
88
+ }
89
+ }
84
90
  let createIndex = false;
85
91
  for (const kind of allowed_kind_names) {
86
92
  if (schema[kind].length < 1)
@@ -101,10 +107,19 @@ const multifile = async (generators, schemas, opts) => {
101
107
  const imports = new Nodes.ImportList([]);
102
108
  if (item.kind === "table")
103
109
  file += join(gens.map(gen => gen.table(imports, item)));
104
- if (item.kind === "composite")
105
- file += join(gens.map(gen => gen.composite(imports, item)));
110
+ if (item.kind === "view")
111
+ file += join(gens.map(gen => gen.view(imports, item)));
112
+ // prettier-ignore
113
+ if (item.kind === "materializedView")
114
+ file += join(gens.map(gen => gen.materializedView(imports, item)));
106
115
  if (item.kind === "enum")
107
116
  file += join(gens.map(gen => gen.enum(imports, item)));
117
+ if (item.kind === "composite")
118
+ file += join(gens.map(gen => gen.composite(imports, item)));
119
+ if (item.kind === "domain")
120
+ file += join(gens.map(gen => gen.domain(imports, item)));
121
+ if (item.kind === "range")
122
+ file += join(gens.map(gen => gen.range(imports, item)));
108
123
  if (item.kind === "function")
109
124
  file += join(gens.map(gen => gen.function(imports, item)));
110
125
  const parts = [];
@@ -131,8 +146,6 @@ const multifile = async (generators, schemas, opts) => {
131
146
  const fullIndexFilename = `${out}/index.ts`;
132
147
  await write(fullIndexFilename, fullIndex);
133
148
  console.log("Created full index: %s", fullIndexFilename);
134
- const end = performance.now();
135
- console.log("Completed in \x1b[32m%sms\x1b[0m", (end - start).toFixed(2));
136
149
  if (warnings.length > 0) {
137
150
  console.log("\nWarnings generated:");
138
151
  console.log(warnings.map(warning => "* " + warning).join("\n"));
@@ -140,23 +153,16 @@ const multifile = async (generators, schemas, opts) => {
140
153
  };
141
154
  export async function generate(opts, generators) {
142
155
  const out = opts.out || "./models";
143
- // let pg;
144
- // if (opts.pg) pg = opts.pg;
145
- // else if (opts.uri) pg = new Pool({ connectionString: opts.uri });
146
- // else if (opts.config) pg = new Pool(opts.config);
147
- // else {
148
- // console.error(
149
- // "One of these options are required in your config file: pg, uri, config. See documentation for more information.",
150
- // );
151
- // process.exit(1);
152
- // }
153
- const config = opts.uri ?? opts.config;
154
- if (!config) {
155
- console.error("One of these options are required in your config file: uri, config. See documentation for more information.");
156
+ if (!("uri" in opts) && !("config" in opts) && !("pg" in opts)) {
157
+ console.error("One of these options are required in your config file: uri, config, pg. See documentation for more information.");
156
158
  process.exit(1);
157
159
  }
158
- const extractor = new Extractor(config);
160
+ const extractor = new Extractor(opts);
161
+ const start = performance.now();
159
162
  const schemas = await extractor.extractSchemas();
163
+ const end = performance.now();
164
+ console.log("Extracted schemas in \x1b[32m%sms\x1b[0m", (end - start).toFixed(2));
165
+ console.info("Adapters enabled: %s\n", opts.adapters.join(", "));
160
166
  generators ??= opts.adapters.map(adapter => {
161
167
  const selected = adapters[adapter];
162
168
  if (!selected)
@@ -167,4 +173,8 @@ export async function generate(opts, generators) {
167
173
  await rm(out, { recursive: true, force: true });
168
174
  await mkdir(out, { recursive: true });
169
175
  await multifile(generators, schemas, { ...opts, out });
176
+ {
177
+ const end = performance.now();
178
+ console.log("Completed in \x1b[32m%sms\x1b[0m", (end - start).toFixed(2));
179
+ }
170
180
  }
@@ -4,12 +4,11 @@ export const builtins = {
4
4
  "pg_catalog.int4": "number",
5
5
  // JS numbers are always floating point, so there is only 53 bits of precision
6
6
  // for the integer part. Thus, storing a 64-bit integer in a JS number will
7
- // result in potential data loss. We therefore use strings for 64-bit integers
8
- // the same way that the pg driver does.
9
- "pg_catalog.int8": "string",
7
+ // result in potential data loss.
8
+ "pg_catalog.int8": "bigint",
9
+ "pg_catalog.numeric": "bigint",
10
10
  "pg_catalog.float4": "number",
11
11
  "pg_catalog.float8": "number",
12
- "pg_catalog.numeric": "string",
13
12
  "pg_catalog.bool": "boolean",
14
13
  "pg_catalog.json": "unknown",
15
14
  "pg_catalog.jsonb": "unknown",
@@ -32,4 +31,7 @@ export const builtins = {
32
31
  "pg_catalog.daterange": "string",
33
32
  "pg_catalog.record": "Record<string, unknown>",
34
33
  "pg_catalog.void": "void",
34
+ "pg_catalog.bytea": "string",
35
+ "pg_catalog.vector": "number[]",
36
+ "pg_catalog.tsvector": "string[]",
35
37
  };
@@ -1,9 +1,7 @@
1
+ import { Canonical, FunctionReturnTypeKind, } from "../extractor/index.js";
1
2
  import { allowed_kind_names, createGenerator, Nodes } from "../types.js";
2
3
  import { builtins } from "./builtins.js";
3
- const isIdentifierInvalid = (str) => {
4
- const invalid = str.match(/[^a-zA-Z0-9_]/);
5
- return invalid !== null;
6
- };
4
+ import { join, quote, quoteI } from "../util.js";
7
5
  const toPascalCase = (str) => str
8
6
  .replace(/^[^a-zA-Z]+/, "") // remove leading non-alphabetic characters
9
7
  .replace(/[^a-zA-Z0-9_]+/g, "") // remove non-alphanumeric/underscore characters
@@ -54,14 +52,14 @@ export const Kysely = createGenerator(opts => {
54
52
  ky(imports, "Generated");
55
53
  }
56
54
  let out = col.comment ? `/** ${col.comment} */\n\t` : "";
57
- out += col.name;
55
+ out += quoteI(col.name);
58
56
  // TODO: update imports for non-primitive types
59
57
  out += `: ${qualified}`;
60
58
  add(imports, col.type);
61
59
  return `\t${out};\n`;
62
60
  };
63
61
  const composite_attribute = (generator, imports, attr) => {
64
- let out = attr.name;
62
+ let out = quoteI(attr.name);
65
63
  if (attr.isNullable)
66
64
  out += "?";
67
65
  out += `: ${generator.formatType(attr.type)}`;
@@ -80,12 +78,15 @@ export const Kysely = createGenerator(opts => {
80
78
  return toPascalCase(type.name);
81
79
  },
82
80
  formatType(type) {
83
- if (type.schema === "pg_catalog") {
81
+ if (type.kind === FunctionReturnTypeKind.ExistingTable) {
82
+ return toPascalCase(type.name);
83
+ }
84
+ else if (type.schema === "pg_catalog") {
84
85
  const name = type.canonical_name;
85
86
  const format = builtins[name];
86
87
  if (format)
87
88
  return format;
88
- opts?.warnings?.push(`Unknown builtin type: ${name}! Pass customBuiltinMap to map this type. Defaulting to "unknown".`);
89
+ opts?.warnings?.push(`(kysely) Unknown builtin type: ${name}. Pass customBuiltinMap to map this type. Defaulting to "unknown".`);
89
90
  return "unknown";
90
91
  }
91
92
  return toPascalCase(type.name);
@@ -100,6 +101,26 @@ export const Kysely = createGenerator(opts => {
100
101
  out += "}";
101
102
  return out;
102
103
  },
104
+ view(imports, view) {
105
+ let out = "";
106
+ if (view.comment)
107
+ out += `/** ${view.comment} */\n`;
108
+ out += `export interface ${this.formatSchemaType(view)} {\n`;
109
+ for (const col of view.columns)
110
+ out += column(this, imports, col);
111
+ out += "}";
112
+ return out;
113
+ },
114
+ materializedView(imports, materializedView) {
115
+ let out = "";
116
+ if (materializedView.comment)
117
+ out += `/** ${materializedView.comment} */\n`;
118
+ out += `export interface ${this.formatSchemaType(materializedView)} {\n`;
119
+ for (const col of materializedView.columns)
120
+ out += column(this, imports, col);
121
+ out += "}";
122
+ return out;
123
+ },
103
124
  enum(imports, en) {
104
125
  let out = "";
105
126
  if (en.comment)
@@ -117,6 +138,17 @@ export const Kysely = createGenerator(opts => {
117
138
  out += "\n}";
118
139
  return out;
119
140
  },
141
+ domain(imports, type) {
142
+ let out = "";
143
+ out += `export type ${this.formatSchemaType(type)} = ${this.formatType(type.canonical.domain_base_type)};`;
144
+ return out;
145
+ },
146
+ range(imports, type) {
147
+ let out = "";
148
+ // force this to be string because range has to be passed as a string to Kysely
149
+ out += `export type ${this.formatSchemaType(type)} = string;`;
150
+ return out;
151
+ },
120
152
  function(imports, type) {
121
153
  let out = "";
122
154
  out += "/**\n";
@@ -161,17 +193,25 @@ export const Kysely = createGenerator(opts => {
161
193
  }
162
194
  out += "\t): ";
163
195
  }
164
- if (type.returnType.kind === "table") {
165
- out += "{\n";
166
- for (const col of type.returnType.columns) {
167
- out += `\t\t${col.name}: `;
168
- out += this.formatType(col.type);
169
- add(imports, col.type);
170
- if (col.type.dimensions > 0)
171
- out += "[]".repeat(col.type.dimensions);
172
- out += `;\n`;
196
+ if (type.returnType.kind === FunctionReturnTypeKind.InlineTable) {
197
+ if (type.returnType.columns.length === 0)
198
+ out += "void/* RETURNS TABLE with no columns */";
199
+ else {
200
+ out += "{\n";
201
+ for (const col of type.returnType.columns) {
202
+ out += `\t\t"${col.name}": `;
203
+ out += this.formatType(col.type);
204
+ add(imports, col.type);
205
+ if (col.type.dimensions > 0)
206
+ out += "[]".repeat(col.type.dimensions);
207
+ out += `;\n`;
208
+ }
209
+ out += "\t}";
173
210
  }
174
- out += "\t}";
211
+ }
212
+ else if (type.returnType.kind === FunctionReturnTypeKind.ExistingTable) {
213
+ out += this.formatType(type.returnType);
214
+ add(imports, type.returnType);
175
215
  }
176
216
  else {
177
217
  out += this.formatType(type.returnType.type);
@@ -200,10 +240,11 @@ export const Kysely = createGenerator(opts => {
200
240
  .join("\n");
201
241
  },
202
242
  schemaIndex(schema) {
203
- let out = allowed_kind_names.map(kind => `import type * as ${kind} from "./${kind}/index.ts";`).join("\n");
243
+ const actual_kinds = allowed_kind_names.filter(kind => schema[kind].length);
244
+ let out = actual_kinds.map(kind => `import type * as ${kind} from "./${kind}/index.ts";`).join("\n");
204
245
  out += "\n\n";
205
246
  out += `export interface ${this.formatSchema(schema.name)} {\n`;
206
- for (const kind of allowed_kind_names) {
247
+ for (const kind of actual_kinds) {
207
248
  const items = schema[kind];
208
249
  if (items.length === 0)
209
250
  continue;
@@ -216,9 +257,7 @@ export const Kysely = createGenerator(opts => {
216
257
  .filter(x => x !== undefined);
217
258
  out += formatted
218
259
  .map(t => {
219
- let name = t.name;
220
- if (isIdentifierInvalid(name))
221
- name = `"${name}"`;
260
+ let name = quoteI(t.name);
222
261
  return `\t\t${name}: ${t.kind}s.${t.formatted};`;
223
262
  })
224
263
  .join("\n");
@@ -228,45 +267,50 @@ export const Kysely = createGenerator(opts => {
228
267
  return out;
229
268
  },
230
269
  fullIndex(schemas) {
231
- let out = "";
232
- out += schemas
270
+ const parts = [];
271
+ parts.push(schemas
233
272
  .map(s => `import type { ${this.formatSchema(s.name)} } from "./${s.name}/index.ts";`)
234
- .join("\n");
235
- out += "\n\n";
236
- out += `export interface Database {\n`;
237
- out += schemas
238
- .map(schema => {
239
- // Kysely only wants tables
240
- const tables = schema.tables;
241
- let out = "";
242
- const seen = new Set();
243
- const formatted = tables
244
- .map(each => {
245
- const formatted = this.formatSchemaType(each);
246
- // skip clashing names
247
- if (seen.has(formatted))
248
- return;
249
- seen.add(formatted);
250
- return { ...each, formatted };
251
- })
252
- .filter(x => x !== undefined);
253
- if (out.length)
254
- out += "\n\n";
255
- out += formatted
256
- .map(t => {
257
- const prefix = defaultSchema === schema.name ? "" : schema.name + ".";
258
- let qualified = prefix + t.name;
259
- if (isIdentifierInvalid(qualified))
260
- qualified = `"${qualified}"`;
261
- return `\t${qualified}: ${this.formatSchema(schema.name)}["${t.kind}s"]["${t.name}"];`;
273
+ .join("\n"));
274
+ {
275
+ let iface = `export interface Database {\n`;
276
+ iface += schemas
277
+ .map(schema => {
278
+ // only tables, views, and materialized views are queryable
279
+ const tables = [...schema.tables, ...schema.views, ...schema.materializedViews];
280
+ let out = "";
281
+ const seen = new Set();
282
+ const formatted = tables
283
+ .map(each => {
284
+ const formatted = this.formatSchemaType(each);
285
+ // skip clashing names
286
+ if (seen.has(formatted))
287
+ return;
288
+ seen.add(formatted);
289
+ return { ...each, formatted };
290
+ })
291
+ .filter(x => x !== undefined);
292
+ if (out.length)
293
+ out += "\n\n";
294
+ out += formatted
295
+ .map(t => {
296
+ const isDefault = defaultSchema === schema.name;
297
+ let qualified = "";
298
+ if (!isDefault)
299
+ qualified = schema.name + "." + t.name;
300
+ else
301
+ qualified = t.name;
302
+ qualified = quoteI(qualified);
303
+ return `\t${qualified}: ${this.formatSchema(schema.name)}[${quote(t.kind + "s")}][${quote(t.name)}];`;
304
+ })
305
+ .join("\n");
306
+ return out;
262
307
  })
263
308
  .join("\n");
264
- return out;
265
- })
266
- .join("");
267
- out += "\n}\n\n";
268
- out += schemas.map(s => `export type { ${this.formatSchema(s.name)} };`).join("\n");
269
- return out;
309
+ iface += "\n}";
310
+ parts.push(iface);
311
+ }
312
+ parts.push(schemas.map(s => `export type { ${this.formatSchema(s.name)} };`).join("\n"));
313
+ return join(parts);
270
314
  },
271
315
  };
272
316
  return generator;
package/lib/types.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import type { CanonicalType, CompositeTypeDetails, EnumDetails, FunctionDetails, TableDetails, SchemaType, Schema, Extractor } from "pg-extract";
2
- export declare const allowed_kind_names: readonly ["tables", "enums", "composites", "functions"];
1
+ import { Canonical, type Extractor, type TableDetails, type ViewDetails, type MaterializedViewDetails, type EnumDetails, type CompositeTypeDetails, type DomainDetails, type RangeDetails, type FunctionDetails, type SchemaType, type Schema, type FunctionReturnType } from "./extractor/index.ts";
2
+ export declare const allowed_kind_names: readonly ["tables", "views", "materializedViews", "enums", "composites", "functions", "domains", "ranges"];
3
3
  export type allowed_kind_names = (typeof allowed_kind_names)[number];
4
4
  export interface FolderStructure {
5
5
  name: string;
@@ -39,13 +39,13 @@ export declare namespace Nodes {
39
39
  }
40
40
  class InternalImport {
41
41
  name: string;
42
- canonical_type: CanonicalType;
42
+ canonical_type: Canonical | FunctionReturnType.ExistingTable;
43
43
  typeOnly: boolean;
44
44
  star: boolean;
45
45
  external: false;
46
46
  constructor(args: {
47
47
  name: string;
48
- canonical_type: CanonicalType;
48
+ canonical_type: Canonical | FunctionReturnType.ExistingTable;
49
49
  typeOnly: boolean;
50
50
  star: boolean;
51
51
  });
@@ -64,15 +64,23 @@ export declare namespace Nodes {
64
64
  star: boolean;
65
65
  }
66
66
  }
67
- export type ConnectionConfig = Exclude<ConstructorParameters<typeof Extractor>[0], string | undefined>;
68
- export interface TruePGOpts {
69
- uri?: string;
70
- config?: ConnectionConfig;
67
+ export type ExtractorConfig = Exclude<ConstructorParameters<typeof Extractor>[0], string | undefined>;
68
+ export interface BaseConfig {
71
69
  out: string;
72
70
  adapters: string[];
73
71
  defaultSchema?: string;
74
72
  }
75
- export declare function config(opts: TruePGOpts): TruePGOpts;
73
+ export interface PgConfig extends BaseConfig {
74
+ pg: ExtractorConfig["pg"];
75
+ }
76
+ export interface UriConfig extends BaseConfig {
77
+ uri: ExtractorConfig["uri"];
78
+ }
79
+ export interface ConfigConfig extends BaseConfig {
80
+ config: ExtractorConfig["config"];
81
+ }
82
+ export type TruePGConfig = PgConfig | UriConfig | ConfigConfig;
83
+ export declare function config(opts: TruePGConfig): TruePGConfig;
76
84
  export interface CreateGeneratorOpts {
77
85
  defaultSchema?: string;
78
86
  warnings: string[];
@@ -99,12 +107,22 @@ export interface SchemaGenerator {
99
107
  * This is useful if you want to use a different name for a type in the generated code.
100
108
  * Example: "users" -> "UsersTable"
101
109
  */
102
- formatType(type: CanonicalType): string;
110
+ formatType(type: Canonical | FunctionReturnType.ExistingTable): string;
103
111
  table(
104
112
  /** @out Append used types to this array */
105
113
  imports: Nodes.ImportList,
106
114
  /** Information about the table */
107
115
  table: TableDetails): string;
116
+ view(
117
+ /** @out Append used types to this array */
118
+ imports: Nodes.ImportList,
119
+ /** Information about the view */
120
+ view: ViewDetails): string;
121
+ materializedView(
122
+ /** @out Append used types to this array */
123
+ imports: Nodes.ImportList,
124
+ /** Information about the materialized view */
125
+ materializedView: MaterializedViewDetails): string;
108
126
  enum(
109
127
  /** @out Append used types to this array */
110
128
  imports: Nodes.ImportList,
@@ -115,6 +133,16 @@ export interface SchemaGenerator {
115
133
  imports: Nodes.ImportList,
116
134
  /** Information about the composite type */
117
135
  type: CompositeTypeDetails): string;
136
+ domain(
137
+ /** @out Append used types to this array */
138
+ imports: Nodes.ImportList,
139
+ /** Information about the domain */
140
+ type: DomainDetails): string;
141
+ range(
142
+ /** @out Append used types to this array */
143
+ imports: Nodes.ImportList,
144
+ /** Information about the range */
145
+ type: RangeDetails): string;
118
146
  function(
119
147
  /** @out Append used types to this array */
120
148
  imports: Nodes.ImportList,
package/lib/types.js CHANGED
@@ -1,8 +1,17 @@
1
- // import type { ClientConfig, Pool, PoolConfig } from "pg";
2
- import { dirname, relative } from "node:path";
1
+ import { Canonical, } from "./extractor/index.js";
2
+ import { dirname, relative } from "node:path/posix";
3
3
  import { join } from "./util.js";
4
4
  // To be updated when we add support for other kinds
5
- export const allowed_kind_names = ["tables", "enums", "composites", "functions"];
5
+ export const allowed_kind_names = [
6
+ "tables",
7
+ "views",
8
+ "materializedViews",
9
+ "enums",
10
+ "composites",
11
+ "functions",
12
+ "domains",
13
+ "ranges",
14
+ ];
6
15
  export var Nodes;
7
16
  (function (Nodes) {
8
17
  class ExternalImport {
@@ -120,6 +129,8 @@ export var Nodes;
120
129
  int2.kind === int.kind);
121
130
  }) === index);
122
131
  })
132
+ .filter(imp => imp.canonical_type.kind === Canonical.Kind.Base ||
133
+ allowed_kind_names.includes(`${imp.canonical_type.kind}s`))
123
134
  .map(imp => {
124
135
  const t = imp.canonical_type;
125
136
  const schema = files.children[t.schema];
package/lib/util.d.ts CHANGED
@@ -1 +1,12 @@
1
1
  export declare const join: (parts: Iterable<string>, joiner?: string) => string;
2
+ export type UnionKeys<T> = T extends unknown ? keyof T : never;
3
+ type AddOptionalKeys<K extends PropertyKey> = {
4
+ [P in K]?: never;
5
+ };
6
+ export type Deunionise<B extends object | undefined, T = B> = Simplify<T extends object ? T & AddOptionalKeys<Exclude<UnionKeys<B>, keyof T>> : T>;
7
+ export type Simplify<T> = {
8
+ [KeyType in keyof T]: T[KeyType];
9
+ } & {};
10
+ export declare const quote: (str: string, using?: string) => string;
11
+ export declare const quoteI: (str: string, using?: string) => string;
12
+ export {};
package/lib/util.js CHANGED
@@ -1 +1,7 @@
1
1
  export const join = (parts, joiner = "\n\n") => Array.from(parts).filter(Boolean).join(joiner);
2
+ const isIdentifierInvalid = (str) => {
3
+ const invalid = str.match(/[^a-zA-Z0-9_]/);
4
+ return invalid !== null;
5
+ };
6
+ export const quote = (str, using = '"') => `${using}${str.replaceAll(using, "\\" + using)}${using}`;
7
+ export const quoteI = (str, using = '"') => (isIdentifierInvalid(str) ? quote(str, using) : str);