true-pg 0.0.2 → 0.1.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.d.ts +1 -0
- package/lib/bin.js +67 -0
- package/lib/consumer.d.ts +23 -0
- package/lib/consumer.js +1 -0
- package/lib/index.d.ts +4 -0
- package/lib/index.js +150 -0
- package/lib/kysely/builtins.d.ts +1 -0
- package/lib/kysely/builtins.js +35 -0
- package/lib/kysely/index.d.ts +2 -0
- package/lib/kysely/index.js +273 -0
- package/lib/types.d.ts +126 -0
- package/lib/types.js +167 -0
- package/lib/util.d.ts +1 -0
- package/lib/util.js +1 -0
- package/lib/zod/builtins.d.ts +1 -0
- package/lib/zod/builtins.js +35 -0
- package/lib/zod/index.d.ts +2 -0
- package/lib/zod/index.js +253 -0
- package/package.json +4 -1
- package/.github/workflows/releases.yml +0 -29
- package/src/bin.ts +0 -81
- package/src/consumer.ts +0 -38
- package/src/index.ts +0 -185
- package/src/kysely/builtins.ts +0 -38
- package/src/kysely/index.ts +0 -315
- package/src/types.ts +0 -297
- package/src/util.ts +0 -1
- package/src/zod/builtins.ts +0 -38
- package/src/zod/index.ts +0 -301
- package/tsconfig.json +0 -29
package/lib/types.d.ts
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
import type { CanonicalType, CompositeTypeDetails, EnumDetails, FunctionDetails, TableDetails, SchemaType, Schema } from "pg-extract";
|
2
|
+
export declare const allowed_kind_names: readonly ["tables", "enums", "composites", "functions"];
|
3
|
+
export type allowed_kind_names = (typeof allowed_kind_names)[number];
|
4
|
+
export interface FolderStructure {
|
5
|
+
name: string;
|
6
|
+
type: "root";
|
7
|
+
children: {
|
8
|
+
[realname: string]: {
|
9
|
+
name: string;
|
10
|
+
type: "schema";
|
11
|
+
children: {
|
12
|
+
[kind: string]: {
|
13
|
+
kind: allowed_kind_names;
|
14
|
+
type: "kind";
|
15
|
+
children: {
|
16
|
+
[realname: string]: {
|
17
|
+
name: string;
|
18
|
+
type: "type";
|
19
|
+
};
|
20
|
+
};
|
21
|
+
};
|
22
|
+
};
|
23
|
+
};
|
24
|
+
};
|
25
|
+
}
|
26
|
+
export declare namespace Nodes {
|
27
|
+
class ExternalImport {
|
28
|
+
name: string;
|
29
|
+
typeOnly: boolean;
|
30
|
+
star: boolean;
|
31
|
+
external: true;
|
32
|
+
module: string;
|
33
|
+
constructor(args: {
|
34
|
+
name: string;
|
35
|
+
module: string;
|
36
|
+
typeOnly: boolean;
|
37
|
+
star: boolean;
|
38
|
+
});
|
39
|
+
}
|
40
|
+
class InternalImport {
|
41
|
+
name: string;
|
42
|
+
canonical_type: CanonicalType;
|
43
|
+
typeOnly: boolean;
|
44
|
+
star: boolean;
|
45
|
+
external: false;
|
46
|
+
constructor(args: {
|
47
|
+
name: string;
|
48
|
+
canonical_type: CanonicalType;
|
49
|
+
typeOnly: boolean;
|
50
|
+
star: boolean;
|
51
|
+
});
|
52
|
+
}
|
53
|
+
class ImportList {
|
54
|
+
imports: (ExternalImport | InternalImport)[];
|
55
|
+
constructor(imports: (ExternalImport | InternalImport)[]);
|
56
|
+
static merge(lists: ImportList[]): ImportList;
|
57
|
+
add(item: ExternalImport | InternalImport): void;
|
58
|
+
stringify(context_file: string, files: FolderStructure): string;
|
59
|
+
}
|
60
|
+
interface Export {
|
61
|
+
name: string;
|
62
|
+
kind: SchemaType["kind"];
|
63
|
+
schema: string;
|
64
|
+
star: boolean;
|
65
|
+
}
|
66
|
+
}
|
67
|
+
export interface TruePGOpts {
|
68
|
+
uri: string;
|
69
|
+
out: string;
|
70
|
+
adapters: string[];
|
71
|
+
defaultSchema?: string;
|
72
|
+
}
|
73
|
+
export interface CreateGeneratorOpts {
|
74
|
+
defaultSchema?: string;
|
75
|
+
warnings: string[];
|
76
|
+
}
|
77
|
+
export interface createGenerator {
|
78
|
+
(opts?: CreateGeneratorOpts): SchemaGenerator;
|
79
|
+
}
|
80
|
+
export declare const createGenerator: (generatorCreator: createGenerator) => createGenerator;
|
81
|
+
export interface SchemaGenerator {
|
82
|
+
/**
|
83
|
+
* Use this function to define a name mapping for schema names.
|
84
|
+
* This is useful if you want to use a different name for a schema in the generated code.
|
85
|
+
* Example: "public" -> "PublicSchema"
|
86
|
+
*/
|
87
|
+
formatSchema(name: string): string;
|
88
|
+
/**
|
89
|
+
* Use this function to define a name mapping for schema types.
|
90
|
+
* This is useful if you want to use a different name for a type in the generated code.
|
91
|
+
* Example: "users" -> "UsersTable"
|
92
|
+
*/
|
93
|
+
formatSchemaType(type: SchemaType): string;
|
94
|
+
/**
|
95
|
+
* Use this function to define a name mapping for type names.
|
96
|
+
* This is useful if you want to use a different name for a type in the generated code.
|
97
|
+
* Example: "users" -> "UsersTable"
|
98
|
+
*/
|
99
|
+
formatType(type: CanonicalType): string;
|
100
|
+
table(
|
101
|
+
/** @out Append used types to this array */
|
102
|
+
imports: Nodes.ImportList,
|
103
|
+
/** Information about the table */
|
104
|
+
table: TableDetails): string;
|
105
|
+
enum(
|
106
|
+
/** @out Append used types to this array */
|
107
|
+
imports: Nodes.ImportList,
|
108
|
+
/** Information about the enum */
|
109
|
+
en: EnumDetails): string;
|
110
|
+
composite(
|
111
|
+
/** @out Append used types to this array */
|
112
|
+
imports: Nodes.ImportList,
|
113
|
+
/** Information about the composite type */
|
114
|
+
type: CompositeTypeDetails): string;
|
115
|
+
function(
|
116
|
+
/** @out Append used types to this array */
|
117
|
+
imports: Nodes.ImportList,
|
118
|
+
/** Information about the function */
|
119
|
+
type: FunctionDetails): string;
|
120
|
+
/** create the file `$out/$schema.name/$kind/index.ts` */
|
121
|
+
schemaKindIndex(schema: Schema, kind: Exclude<keyof Schema, "name">, main_generator?: SchemaGenerator): string;
|
122
|
+
/** create the file `$out/$schema.name/index.ts` */
|
123
|
+
schemaIndex(schema: Schema, main_generator?: SchemaGenerator): string;
|
124
|
+
/** create the file `$out/index.ts` */
|
125
|
+
fullIndex(schemas: Schema[], main_generator?: SchemaGenerator): string;
|
126
|
+
}
|
package/lib/types.js
ADDED
@@ -0,0 +1,167 @@
|
|
1
|
+
import { dirname, relative } from "node:path";
|
2
|
+
import { join } from "./util.js";
|
3
|
+
// To be updated when we add support for other kinds
|
4
|
+
export const allowed_kind_names = ["tables", "enums", "composites", "functions"];
|
5
|
+
export var Nodes;
|
6
|
+
(function (Nodes) {
|
7
|
+
class ExternalImport {
|
8
|
+
// what to import
|
9
|
+
name;
|
10
|
+
// use `type` syntax?
|
11
|
+
typeOnly;
|
12
|
+
// use `* as` syntax?
|
13
|
+
star;
|
14
|
+
// this is an external import
|
15
|
+
external;
|
16
|
+
// what module to import from
|
17
|
+
module;
|
18
|
+
constructor(args) {
|
19
|
+
this.name = args.name;
|
20
|
+
this.typeOnly = args.typeOnly;
|
21
|
+
this.star = args.star;
|
22
|
+
this.external = true;
|
23
|
+
this.module = args.module;
|
24
|
+
}
|
25
|
+
}
|
26
|
+
Nodes.ExternalImport = ExternalImport;
|
27
|
+
class InternalImport {
|
28
|
+
// what to import
|
29
|
+
name;
|
30
|
+
// underlying type that is being imported
|
31
|
+
canonical_type;
|
32
|
+
// use `type` syntax?
|
33
|
+
typeOnly;
|
34
|
+
// use `* as` syntax?
|
35
|
+
star;
|
36
|
+
// this is an internal import
|
37
|
+
external;
|
38
|
+
constructor(args) {
|
39
|
+
this.name = args.name;
|
40
|
+
this.canonical_type = args.canonical_type;
|
41
|
+
this.star = args.star;
|
42
|
+
this.typeOnly = args.typeOnly;
|
43
|
+
this.external = false;
|
44
|
+
}
|
45
|
+
}
|
46
|
+
Nodes.InternalImport = InternalImport;
|
47
|
+
class ImportList {
|
48
|
+
imports;
|
49
|
+
constructor(imports) {
|
50
|
+
this.imports = imports;
|
51
|
+
}
|
52
|
+
static merge(lists) {
|
53
|
+
return new ImportList(lists.flatMap(l => l.imports));
|
54
|
+
}
|
55
|
+
add(item) {
|
56
|
+
this.imports.push(item);
|
57
|
+
}
|
58
|
+
stringify(context_file, files) {
|
59
|
+
const externals = this.imports.filter(i => i.external);
|
60
|
+
const internals = this.imports.filter(i => !i.external);
|
61
|
+
const modulegroups = {};
|
62
|
+
for (const item of externals) {
|
63
|
+
const group = modulegroups[item.module];
|
64
|
+
if (group)
|
65
|
+
group.push(item);
|
66
|
+
else
|
67
|
+
modulegroups[item.module] = [item];
|
68
|
+
}
|
69
|
+
const out = [];
|
70
|
+
// TODO: normalise external and internal imports and handle the stringification of the imports in a single place
|
71
|
+
{
|
72
|
+
// EXTERNAL IMPORTS
|
73
|
+
const imports = [];
|
74
|
+
for (const module in modulegroups) {
|
75
|
+
const items = modulegroups[module];
|
76
|
+
const star = items.find(i => i.star);
|
77
|
+
const unique = items.filter((i, index, arr) => {
|
78
|
+
if (i.star)
|
79
|
+
return false;
|
80
|
+
if (arr.findIndex(i2 => i2.name === i.name) !== index)
|
81
|
+
return false;
|
82
|
+
return true;
|
83
|
+
});
|
84
|
+
const bits = [];
|
85
|
+
const typeOnlys = unique.filter(i => i.typeOnly);
|
86
|
+
const values = unique.filter(i => !i.typeOnly);
|
87
|
+
// if no values to import, use `import type { ... }` instead of `import { type ... }`
|
88
|
+
const typeInline = values.length !== 0;
|
89
|
+
let import_line = `import `;
|
90
|
+
for (const type of typeOnlys)
|
91
|
+
bits.push(typeInline ? "type " : "" + type.name);
|
92
|
+
for (const type of values)
|
93
|
+
bits.push(type.name);
|
94
|
+
if (bits.length)
|
95
|
+
import_line += (typeInline ? "" : "type ") + "{ " + bits.join(", ") + " }";
|
96
|
+
if (bits.length && star)
|
97
|
+
import_line += `, `;
|
98
|
+
if (star)
|
99
|
+
import_line += `* as ${star.name}`;
|
100
|
+
if (bits.length || star)
|
101
|
+
import_line += ` from`;
|
102
|
+
import_line += `"${module}";`;
|
103
|
+
imports.push(import_line);
|
104
|
+
}
|
105
|
+
out.push(join(imports, "\n"));
|
106
|
+
}
|
107
|
+
{
|
108
|
+
// INTERNAL IMPORTS
|
109
|
+
const imports = [];
|
110
|
+
const unique_types = internals
|
111
|
+
.filter(({ name: name1, canonical_type: int }, index, arr) => {
|
112
|
+
return (arr.findIndex(({ name: name2, canonical_type: int2 }) => {
|
113
|
+
return (
|
114
|
+
// adapter-assigned name
|
115
|
+
name2 === name1 &&
|
116
|
+
// canonical type details
|
117
|
+
int2.name === int.name &&
|
118
|
+
int2.schema === int.schema &&
|
119
|
+
int2.kind === int.kind);
|
120
|
+
}) === index);
|
121
|
+
})
|
122
|
+
.map(imp => {
|
123
|
+
const t = imp.canonical_type;
|
124
|
+
const schema = files.children[t.schema];
|
125
|
+
const kind = schema.children[`${t.kind}s`];
|
126
|
+
const type = kind.children[t.name];
|
127
|
+
const located_file = `${files.name}/${schema.name}/${kind.kind}/${type.name}.ts`;
|
128
|
+
return { ...imp, located_file };
|
129
|
+
});
|
130
|
+
const group_by_file = {};
|
131
|
+
for (const type of unique_types) {
|
132
|
+
const file = group_by_file[type.located_file] || [];
|
133
|
+
file.push(type);
|
134
|
+
group_by_file[type.located_file] = file;
|
135
|
+
}
|
136
|
+
for (const group in group_by_file) {
|
137
|
+
let relative_path = relative(dirname(context_file), group);
|
138
|
+
if (/^[^\.+\/]/.test(relative_path))
|
139
|
+
relative_path = "./" + relative_path;
|
140
|
+
const items = group_by_file[group];
|
141
|
+
const typeOnlys = items.filter(i => i.typeOnly);
|
142
|
+
const values = items.filter(i => !i.typeOnly);
|
143
|
+
const star = values.find(i => i.star);
|
144
|
+
let import_line = "import ";
|
145
|
+
const bits = [];
|
146
|
+
// if no values to import, use `import type { ... }` instead of `import { type ... }`
|
147
|
+
const typeInline = values.length !== 0;
|
148
|
+
for (const type of typeOnlys)
|
149
|
+
bits.push((typeInline ? "type " : "") + type.name);
|
150
|
+
for (const type of values)
|
151
|
+
bits.push(type.name);
|
152
|
+
if (bits.length)
|
153
|
+
import_line += (typeInline ? "" : "type ") + "{ " + bits.join(", ") + " }";
|
154
|
+
if (star)
|
155
|
+
import_line += `* as ${star.name}`;
|
156
|
+
import_line += ` from "${relative_path}";`;
|
157
|
+
imports.push(import_line);
|
158
|
+
}
|
159
|
+
out.push(join(imports, "\n"));
|
160
|
+
}
|
161
|
+
return join(out);
|
162
|
+
}
|
163
|
+
}
|
164
|
+
Nodes.ImportList = ImportList;
|
165
|
+
})(Nodes || (Nodes = {}));
|
166
|
+
/* convenience function to create a generator with type inference */
|
167
|
+
export const createGenerator = (generatorCreator) => generatorCreator;
|
package/lib/util.d.ts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
export declare const join: (parts: Iterable<string>, joiner?: string) => string;
|
package/lib/util.js
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
export const join = (parts, joiner = "\n\n") => Array.from(parts).filter(Boolean).join(joiner);
|
@@ -0,0 +1 @@
|
|
1
|
+
export declare const builtins: Record<string, string>;
|
@@ -0,0 +1,35 @@
|
|
1
|
+
// extended from https://github.com/kristiandupont/kanel/blob/e9332f03ff5e38f5b844dd7a4563580c0d9d1444/packages/kanel/src/defaultTypeMap.ts
|
2
|
+
export const builtins = {
|
3
|
+
"pg_catalog.int2": "z.number()",
|
4
|
+
"pg_catalog.int4": "z.number()",
|
5
|
+
// JS numbers are always floating point, so there is only 53 bits of precision
|
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": "z.string()",
|
10
|
+
"pg_catalog.float4": "z.number()",
|
11
|
+
"pg_catalog.float8": "z.number()",
|
12
|
+
"pg_catalog.numeric": "z.string()",
|
13
|
+
"pg_catalog.bool": "z.boolean()",
|
14
|
+
"pg_catalog.json": "z.unknown()",
|
15
|
+
"pg_catalog.jsonb": "z.unknown()",
|
16
|
+
"pg_catalog.char": "z.string()",
|
17
|
+
"pg_catalog.bpchar": "z.string()",
|
18
|
+
"pg_catalog.varchar": "z.string()",
|
19
|
+
"pg_catalog.text": "z.string()",
|
20
|
+
"pg_catalog.uuid": "z.string()",
|
21
|
+
"pg_catalog.inet": "z.string()",
|
22
|
+
"pg_catalog.date": "z.date()",
|
23
|
+
"pg_catalog.time": "z.date()",
|
24
|
+
"pg_catalog.timetz": "z.date()",
|
25
|
+
"pg_catalog.timestamp": "z.date()",
|
26
|
+
"pg_catalog.timestamptz": "z.date()",
|
27
|
+
"pg_catalog.int4range": "z.string()",
|
28
|
+
"pg_catalog.int8range": "z.string()",
|
29
|
+
"pg_catalog.numrange": "z.string()",
|
30
|
+
"pg_catalog.tsrange": "z.string()",
|
31
|
+
"pg_catalog.tstzrange": "z.string()",
|
32
|
+
"pg_catalog.daterange": "z.string()",
|
33
|
+
"pg_catalog.record": "z.record(z.string(), z.unknown())",
|
34
|
+
"pg_catalog.void": "z.void()",
|
35
|
+
};
|
package/lib/zod/index.js
ADDED
@@ -0,0 +1,253 @@
|
|
1
|
+
import { allowed_kind_names, createGenerator, Nodes } from "../types.js";
|
2
|
+
import { builtins } from "./builtins.js";
|
3
|
+
import { join } from "../util.js";
|
4
|
+
const isIdentifierInvalid = (str) => {
|
5
|
+
const invalid = str.match(/[^a-zA-Z0-9_]/);
|
6
|
+
return invalid !== null;
|
7
|
+
};
|
8
|
+
const to_snake_case = (str) => str
|
9
|
+
.replace(/^[^a-zA-Z]+/, "") // remove leading non-alphabetic characters
|
10
|
+
.replace(/[^a-zA-Z0-9]+/g, "_") // replace non-alphanumeric characters with underscores
|
11
|
+
.replace(/([A-Z])/g, "_$1") // insert underscores before uppercase letters
|
12
|
+
.toLowerCase();
|
13
|
+
// TODO: create an insert and update zod interface for each type
|
14
|
+
export const Zod = createGenerator(opts => {
|
15
|
+
const defaultSchema = opts?.defaultSchema ?? "public";
|
16
|
+
const zod = (imports, name) => imports.add(new Nodes.ExternalImport({
|
17
|
+
name: name ?? "z",
|
18
|
+
module: "zod",
|
19
|
+
typeOnly: false,
|
20
|
+
star: !name,
|
21
|
+
}));
|
22
|
+
const add = (imports, type) => {
|
23
|
+
if (type.schema === "pg_catalog")
|
24
|
+
zod(imports, "z");
|
25
|
+
else
|
26
|
+
imports.add(new Nodes.InternalImport({
|
27
|
+
name: generator.formatType(type),
|
28
|
+
canonical_type: type,
|
29
|
+
typeOnly: false,
|
30
|
+
star: false,
|
31
|
+
}));
|
32
|
+
};
|
33
|
+
const column = (imports,
|
34
|
+
/** Information about the column */
|
35
|
+
col) => {
|
36
|
+
// don't create a property for always generated columns
|
37
|
+
if (col.generated === "ALWAYS")
|
38
|
+
return "";
|
39
|
+
let out = col.comment ? `/** ${col.comment} */\n\t` : "";
|
40
|
+
out += col.name;
|
41
|
+
let type = generator.formatType(col.type);
|
42
|
+
add(imports, col.type);
|
43
|
+
if (col.type.dimensions > 0)
|
44
|
+
type += ".array()".repeat(col.type.dimensions);
|
45
|
+
if (col.isNullable || col.generated === "BY DEFAULT" || col.defaultValue)
|
46
|
+
type += `.nullable().optional()`;
|
47
|
+
out += `: ${type}`;
|
48
|
+
return `\t${out},\n`;
|
49
|
+
};
|
50
|
+
const composite_attribute = (imports, attr) => {
|
51
|
+
let out = attr.name;
|
52
|
+
out += `: ${generator.formatType(attr.type)}`;
|
53
|
+
add(imports, attr.type);
|
54
|
+
if (attr.type.dimensions > 0)
|
55
|
+
out += ".array()".repeat(attr.type.dimensions);
|
56
|
+
if (attr.isNullable)
|
57
|
+
out += ".nullable().optional()";
|
58
|
+
return out;
|
59
|
+
};
|
60
|
+
const generator = {
|
61
|
+
formatSchema(name) {
|
62
|
+
return to_snake_case(name) + "_validators";
|
63
|
+
},
|
64
|
+
formatSchemaType(type) {
|
65
|
+
return to_snake_case(type.name);
|
66
|
+
},
|
67
|
+
formatType(type) {
|
68
|
+
if (type.schema === "pg_catalog") {
|
69
|
+
const name = type.canonical_name;
|
70
|
+
const format = builtins[name];
|
71
|
+
if (format)
|
72
|
+
return format;
|
73
|
+
opts?.warnings?.push(`Unknown builtin type: ${name}! Pass customBuiltinMap to map this type. Defaulting to "z.unknown()".`);
|
74
|
+
return "z.unknown()";
|
75
|
+
}
|
76
|
+
return to_snake_case(type.name);
|
77
|
+
},
|
78
|
+
table(imports, table) {
|
79
|
+
let out = "";
|
80
|
+
if (table.comment)
|
81
|
+
out += `/** ${table.comment} */\n`;
|
82
|
+
out += `export const ${this.formatSchemaType(table)} = z.object({\n`;
|
83
|
+
zod(imports, "z");
|
84
|
+
for (const col of table.columns)
|
85
|
+
out += column(imports, col);
|
86
|
+
out += "});";
|
87
|
+
return out;
|
88
|
+
},
|
89
|
+
enum(imports, en) {
|
90
|
+
let out = "";
|
91
|
+
if (en.comment)
|
92
|
+
out += `/** ${en.comment} */\n`;
|
93
|
+
out += `export const ${this.formatSchemaType(en)} = z.union([\n`;
|
94
|
+
out += en.values.map(v => `\tz.literal("${v}")`).join(",\n");
|
95
|
+
out += "\n]);";
|
96
|
+
zod(imports, "z");
|
97
|
+
return out;
|
98
|
+
},
|
99
|
+
composite(imports, type) {
|
100
|
+
let out = "";
|
101
|
+
if (type.comment)
|
102
|
+
out += `/** ${type.comment} */\n`;
|
103
|
+
out += `export const ${this.formatSchemaType(type)} = z.object({\n`;
|
104
|
+
const props = type.canonical.attributes.map(c => composite_attribute(imports, c)).map(t => `\t${t},`);
|
105
|
+
out += props.join("\n");
|
106
|
+
out += "\n});";
|
107
|
+
return out;
|
108
|
+
},
|
109
|
+
function(imports, type) {
|
110
|
+
let out = "export const ";
|
111
|
+
out += this.formatSchemaType(type);
|
112
|
+
out += " = {\n";
|
113
|
+
out += `\tparameters: z.tuple([`;
|
114
|
+
// Get the input parameters (those that appear in function signature)
|
115
|
+
const inputParams = type.parameters.filter(p => p.mode === "IN" || p.mode === "INOUT");
|
116
|
+
if (inputParams.length === 0) {
|
117
|
+
out += "])";
|
118
|
+
}
|
119
|
+
else {
|
120
|
+
out += "\n";
|
121
|
+
for (const param of inputParams) {
|
122
|
+
// TODO: update imports for non-primitive types based on typeInfo.kind
|
123
|
+
out += "\t\t" + this.formatType(param.type);
|
124
|
+
add(imports, param.type);
|
125
|
+
if (param.type.dimensions > 0)
|
126
|
+
out += ".array()".repeat(param.type.dimensions);
|
127
|
+
if (param.hasDefault)
|
128
|
+
out += ".nullable().optional()";
|
129
|
+
out += `, // ${param.name}\n`;
|
130
|
+
}
|
131
|
+
out += "\t])";
|
132
|
+
}
|
133
|
+
const variadic = type.parameters.find(p => p.mode === "VARIADIC");
|
134
|
+
if (variadic) {
|
135
|
+
out += ".rest(";
|
136
|
+
out += this.formatType(variadic.type);
|
137
|
+
// reduce by 1 because it's already a rest parameter
|
138
|
+
if (variadic.type.dimensions > 1)
|
139
|
+
out += ".array()".repeat(variadic.type.dimensions - 1);
|
140
|
+
out += ")" + ", // " + variadic.name + "\n";
|
141
|
+
}
|
142
|
+
else
|
143
|
+
out += ",\n";
|
144
|
+
out += "\treturnType: ";
|
145
|
+
if (type.returnType.kind === "table") {
|
146
|
+
out += "z.object({\n";
|
147
|
+
for (const col of type.returnType.columns) {
|
148
|
+
out += `\t\t${col.name}: `;
|
149
|
+
out += this.formatType(col.type);
|
150
|
+
add(imports, col.type);
|
151
|
+
if (col.type.dimensions > 0)
|
152
|
+
out += ".array()".repeat(col.type.dimensions);
|
153
|
+
out += `,\n`;
|
154
|
+
}
|
155
|
+
out += "\t})";
|
156
|
+
}
|
157
|
+
else {
|
158
|
+
out += this.formatType(type.returnType.type);
|
159
|
+
add(imports, type.returnType.type);
|
160
|
+
if (type.returnType.type.dimensions > 0)
|
161
|
+
out += ".array()".repeat(type.returnType.type.dimensions);
|
162
|
+
}
|
163
|
+
// Add additional array brackets if it returns a set
|
164
|
+
if (type.returnType.isSet)
|
165
|
+
out += ".array()";
|
166
|
+
out += ",\n};";
|
167
|
+
return out;
|
168
|
+
},
|
169
|
+
schemaKindIndex(schema, kind, main_generator) {
|
170
|
+
const imports = schema[kind];
|
171
|
+
if (imports.length === 0)
|
172
|
+
return "";
|
173
|
+
const generator = main_generator ?? this;
|
174
|
+
return imports
|
175
|
+
.map(each => {
|
176
|
+
const name = this.formatSchemaType(each);
|
177
|
+
const file = generator.formatSchemaType(each);
|
178
|
+
return `export { ${name} } from "./${file}.ts";`;
|
179
|
+
})
|
180
|
+
.join("\n");
|
181
|
+
},
|
182
|
+
schemaIndex(schema, main_generator) {
|
183
|
+
let out = allowed_kind_names.map(kind => `import * as zod_${kind} from "./${kind}/index.ts";`).join("\n");
|
184
|
+
out += "\n\n";
|
185
|
+
out += `export const ${this.formatSchema(schema.name)} = {\n`;
|
186
|
+
for (const kind of allowed_kind_names) {
|
187
|
+
const items = schema[kind];
|
188
|
+
if (items.length === 0)
|
189
|
+
continue;
|
190
|
+
out += `\t${kind}: {\n`;
|
191
|
+
const formatted = items
|
192
|
+
.map(each => {
|
193
|
+
const formatted = this.formatSchemaType(each);
|
194
|
+
return { ...each, formatted };
|
195
|
+
})
|
196
|
+
.filter(x => x !== undefined);
|
197
|
+
out += formatted
|
198
|
+
.map(t => {
|
199
|
+
let name = t.name;
|
200
|
+
if (isIdentifierInvalid(name))
|
201
|
+
name = `"${name}"`;
|
202
|
+
return `\t\t${name}: zod_${t.kind}s.${t.formatted},`;
|
203
|
+
})
|
204
|
+
.join("\n");
|
205
|
+
out += "\n\t},\n";
|
206
|
+
}
|
207
|
+
out += "}";
|
208
|
+
return out;
|
209
|
+
},
|
210
|
+
fullIndex(schemas, main_generator) {
|
211
|
+
const generator = main_generator ?? this;
|
212
|
+
let out = "";
|
213
|
+
out += schemas
|
214
|
+
.map(s => `import { ${generator.formatSchema(s.name)} } from "./${s.name}/index.ts";`)
|
215
|
+
.join("\n");
|
216
|
+
out += "\n\n";
|
217
|
+
out += `export const Validators = {\n`;
|
218
|
+
out += join(schemas.map(schema => {
|
219
|
+
const schema_validators = join(allowed_kind_names.map(kind => {
|
220
|
+
const current = schema[kind];
|
221
|
+
const seen = new Set();
|
222
|
+
const formatted = current
|
223
|
+
.map(each => {
|
224
|
+
const formatted = generator.formatSchemaType(each);
|
225
|
+
// skip clashing names
|
226
|
+
if (seen.has(formatted))
|
227
|
+
return;
|
228
|
+
seen.add(formatted);
|
229
|
+
return { ...each, formatted };
|
230
|
+
})
|
231
|
+
.filter(x => x !== undefined);
|
232
|
+
if (!formatted.length)
|
233
|
+
return "";
|
234
|
+
let out = "";
|
235
|
+
out += "\t// " + kind + "\n";
|
236
|
+
out += join(formatted.map(t => {
|
237
|
+
const prefix = defaultSchema === schema.name ? "" : schema.name + ".";
|
238
|
+
let qualified = prefix + t.name;
|
239
|
+
if (isIdentifierInvalid(qualified))
|
240
|
+
qualified = `"${qualified}"`;
|
241
|
+
return `\t${qualified}: ${this.formatSchema(schema.name)}["${t.kind}s"]["${t.name}"],`;
|
242
|
+
}), "\n");
|
243
|
+
return out;
|
244
|
+
}));
|
245
|
+
return `\t/* -- ${schema.name} --*/\n\n` + schema_validators || "\t-- no validators\n\n";
|
246
|
+
}));
|
247
|
+
out += "\n}\n\n";
|
248
|
+
out += schemas.map(s => `export type { ${this.formatSchema(s.name)} };`).join("\n");
|
249
|
+
return out;
|
250
|
+
},
|
251
|
+
};
|
252
|
+
return generator;
|
253
|
+
});
|
package/package.json
CHANGED
@@ -1,12 +1,15 @@
|
|
1
1
|
{
|
2
2
|
"name": "true-pg",
|
3
|
-
"version": "0.0
|
3
|
+
"version": "0.1.0",
|
4
4
|
"type": "module",
|
5
5
|
"module": "lib/index.js",
|
6
6
|
"main": "lib/index.js",
|
7
7
|
"bin": {
|
8
8
|
"true-pg": "lib/bin.js"
|
9
9
|
},
|
10
|
+
"files": [
|
11
|
+
"lib"
|
12
|
+
],
|
10
13
|
"scripts": {
|
11
14
|
"check": "tsc --noEmit",
|
12
15
|
"build": "tsc"
|
@@ -1,29 +0,0 @@
|
|
1
|
-
name: Release
|
2
|
-
|
3
|
-
on:
|
4
|
-
push:
|
5
|
-
tags:
|
6
|
-
- v*
|
7
|
-
|
8
|
-
jobs:
|
9
|
-
release:
|
10
|
-
runs-on: ubuntu-latest
|
11
|
-
|
12
|
-
permissions:
|
13
|
-
contents: read
|
14
|
-
id-token: write
|
15
|
-
|
16
|
-
steps:
|
17
|
-
- uses: actions/checkout@v4
|
18
|
-
- uses: oven-sh/setup-bun@v2
|
19
|
-
- run : bun install
|
20
|
-
- run : bun run prepare
|
21
|
-
- name: Publish to npm
|
22
|
-
run : |
|
23
|
-
bun run version.ts
|
24
|
-
bun publish --ignore-scripts --access=public
|
25
|
-
env :
|
26
|
-
NPM_CONFIG_TOKEN: ${{ secrets.NPM_CONFIG_TOKEN }}
|
27
|
-
NPM_CONFIG_PROVENANCE: true
|
28
|
-
- name: Publish to JSR
|
29
|
-
run: bunx jsr publish --allow-dirty
|