true-pg 0.2.3 → 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.
- package/lib/bin.js +3 -7
- package/lib/extractor/adapter.d.ts +15 -0
- package/lib/extractor/adapter.js +53 -0
- package/lib/extractor/canonicalise.d.ts +64 -0
- package/lib/extractor/canonicalise.js +245 -0
- package/lib/extractor/fetchExtensionItemIds.d.ts +12 -0
- package/lib/extractor/fetchExtensionItemIds.js +43 -0
- package/lib/extractor/fetchTypes.d.ts +4 -0
- package/lib/extractor/fetchTypes.js +65 -0
- package/lib/extractor/index.d.ts +95 -0
- package/lib/extractor/index.js +140 -0
- package/lib/extractor/kinds/composite.d.ts +15 -0
- package/lib/extractor/kinds/composite.js +13 -0
- package/lib/extractor/kinds/domain.d.ts +15 -0
- package/lib/extractor/kinds/domain.js +13 -0
- package/lib/extractor/kinds/enum.d.ts +9 -0
- package/lib/extractor/kinds/enum.js +20 -0
- package/lib/extractor/kinds/function.d.ts +73 -0
- package/lib/extractor/kinds/function.js +179 -0
- package/lib/extractor/kinds/materialized-view.d.ts +17 -0
- package/lib/extractor/kinds/materialized-view.js +64 -0
- package/lib/extractor/kinds/parts/commentMapQueryPart.d.ts +2 -0
- package/lib/extractor/kinds/parts/commentMapQueryPart.js +14 -0
- package/lib/extractor/kinds/parts/indexMapQueryPart.d.ts +2 -0
- package/lib/extractor/kinds/parts/indexMapQueryPart.js +27 -0
- package/lib/extractor/kinds/range.d.ts +15 -0
- package/lib/extractor/kinds/range.js +13 -0
- package/lib/extractor/kinds/table.d.ts +212 -0
- package/lib/extractor/kinds/table.js +217 -0
- package/lib/extractor/kinds/util/parseInlineTable.d.ts +9 -0
- package/lib/extractor/kinds/util/parseInlineTable.js +135 -0
- package/lib/extractor/kinds/util/parseInlineTable.test.d.ts +1 -0
- package/lib/extractor/kinds/util/parseInlineTable.test.js +26 -0
- package/lib/extractor/kinds/view.d.ts +17 -0
- package/lib/extractor/kinds/view.js +63 -0
- package/lib/extractor/pgtype.d.ts +41 -0
- package/lib/extractor/pgtype.js +30 -0
- package/lib/index.d.ts +2 -2
- package/lib/index.js +38 -28
- package/lib/kysely/builtins.js +6 -4
- package/lib/kysely/index.js +103 -59
- package/lib/types.d.ts +38 -10
- package/lib/types.js +14 -3
- package/lib/util.d.ts +11 -0
- package/lib/util.js +6 -0
- package/lib/zod/builtins.js +11 -9
- package/lib/zod/index.js +107 -60
- 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
|
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:
|
4
|
+
export declare function generate(opts: TruePGConfig, generators?: createGenerator[]): Promise<void>;
|
package/lib/index.js
CHANGED
@@ -1,17 +1,16 @@
|
|
1
|
-
import { Extractor } from "
|
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
|
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
|
-
|
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(
|
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 === "
|
105
|
-
file += join(gens.map(gen => gen.
|
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
|
-
|
144
|
-
|
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(
|
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
|
}
|
package/lib/kysely/builtins.js
CHANGED
@@ -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.
|
8
|
-
|
9
|
-
"pg_catalog.
|
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
|
};
|
package/lib/kysely/index.js
CHANGED
@@ -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
|
-
|
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.
|
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}
|
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 ===
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
out +=
|
169
|
-
|
170
|
-
|
171
|
-
out +=
|
172
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
232
|
-
|
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
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
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
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
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
|
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:
|
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:
|
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
|
68
|
-
export interface
|
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
|
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:
|
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
|
-
|
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 = [
|
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);
|