@pythia-software/query-table-codegen 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/LICENSE +21 -0
- package/README.md +40 -0
- package/dist/chunk-RJYRZ372.js +201 -0
- package/dist/chunk-RJYRZ372.js.map +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +103 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/package.json +39 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 query-table contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# @pythia-software/query-table-codegen
|
|
2
|
+
|
|
3
|
+
Projects a query-table JSON schema document into frontend TypeScript and a
|
|
4
|
+
direct Go backend schema literal, keeping both projections in lockstep.
|
|
5
|
+
|
|
6
|
+
## Install
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
npm install --save-dev @pythia-software/query-table-codegen
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Generate
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
runs.schema.json
|
|
16
|
+
├── --ts runs.fields.generated.ts → export const RUNS_SCHEMA / RUNS_FIELDS
|
|
17
|
+
└── --go runs_schema_generated.go → func RunsSchema() querytable.Schema
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
This keeps the frontend and backend projections of a field catalog in lockstep.
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
query-table-codegen runs.schema.json \
|
|
24
|
+
--ts src/runs.fields.generated.ts \
|
|
25
|
+
--go internal/catalog/runs_schema_generated.go \
|
|
26
|
+
--go-package catalog
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The TS output drops backend `bindings` (the frontend never sees SQL) and leaves
|
|
30
|
+
`render` as a string key (the consumer maps it to a real renderer at runtime).
|
|
31
|
+
The Go output reads `bindings.postgres` into `FieldSpec{Expr, Kind, Synthetic}`
|
|
32
|
+
and emits server-filter/sort capabilities directly, without runtime JSON
|
|
33
|
+
parsing.
|
|
34
|
+
|
|
35
|
+
The generated Go file imports
|
|
36
|
+
`github.com/Pythia-Software/query-table/backends/go` by default. Override that
|
|
37
|
+
path with `--go-import` when using a fork or a vendored module.
|
|
38
|
+
|
|
39
|
+
The package also exports `generateTypeScript` and `generateGo` for build-tool
|
|
40
|
+
integrations.
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
// src/generate.ts
|
|
2
|
+
import { loadSchema } from "@pythia-software/query-table-core";
|
|
3
|
+
var DEFAULT_GO_IMPORT = "github.com/Pythia-Software/query-table/backends/go";
|
|
4
|
+
var GO_KINDS = {
|
|
5
|
+
text: "FieldText",
|
|
6
|
+
number: "FieldNumber",
|
|
7
|
+
datetime: "FieldDatetime",
|
|
8
|
+
bool: "FieldBool",
|
|
9
|
+
enum: "FieldEnum",
|
|
10
|
+
textarray: "FieldTextArray"
|
|
11
|
+
};
|
|
12
|
+
var GO_KEYWORDS = /* @__PURE__ */ new Set([
|
|
13
|
+
"break",
|
|
14
|
+
"default",
|
|
15
|
+
"func",
|
|
16
|
+
"interface",
|
|
17
|
+
"select",
|
|
18
|
+
"case",
|
|
19
|
+
"defer",
|
|
20
|
+
"go",
|
|
21
|
+
"map",
|
|
22
|
+
"struct",
|
|
23
|
+
"chan",
|
|
24
|
+
"else",
|
|
25
|
+
"goto",
|
|
26
|
+
"package",
|
|
27
|
+
"switch",
|
|
28
|
+
"const",
|
|
29
|
+
"fallthrough",
|
|
30
|
+
"if",
|
|
31
|
+
"range",
|
|
32
|
+
"type",
|
|
33
|
+
"continue",
|
|
34
|
+
"for",
|
|
35
|
+
"import",
|
|
36
|
+
"return",
|
|
37
|
+
"var"
|
|
38
|
+
]);
|
|
39
|
+
function asObject(value, label) {
|
|
40
|
+
if (value == null || typeof value !== "object" || Array.isArray(value)) {
|
|
41
|
+
throw new Error(`${label} must be an object`);
|
|
42
|
+
}
|
|
43
|
+
return value;
|
|
44
|
+
}
|
|
45
|
+
function requiredString(value, label) {
|
|
46
|
+
if (typeof value !== "string" || !value) throw new Error(`${label} must be a non-empty string`);
|
|
47
|
+
return value;
|
|
48
|
+
}
|
|
49
|
+
function goString(value) {
|
|
50
|
+
return JSON.stringify(value).replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
|
|
51
|
+
}
|
|
52
|
+
function exportedName(name) {
|
|
53
|
+
const parts = name.split(/[^A-Za-z0-9]+/).filter(Boolean);
|
|
54
|
+
const base = parts.map((part) => part[0].toUpperCase() + part.slice(1)).join("") || "Generated";
|
|
55
|
+
return `${/^\d/.test(base) ? `Schema${base}` : base}Schema`;
|
|
56
|
+
}
|
|
57
|
+
function validateGoPackage(packageName) {
|
|
58
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(packageName) || GO_KEYWORDS.has(packageName)) {
|
|
59
|
+
throw new Error(`invalid Go package name ${JSON.stringify(packageName)}`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function orderTerms(value, label, fieldNames) {
|
|
63
|
+
if (value == null) return [];
|
|
64
|
+
if (!Array.isArray(value)) throw new Error(`${label} must be an array`);
|
|
65
|
+
return value.map((item, index) => {
|
|
66
|
+
const raw = asObject(item, `${label}[${index}]`);
|
|
67
|
+
const field = requiredString(raw.field, `${label}[${index}].field`);
|
|
68
|
+
if (!fieldNames.has(field)) throw new Error(`${label}[${index}] references unknown field ${JSON.stringify(field)}`);
|
|
69
|
+
if (raw.dir !== "asc" && raw.dir !== "desc") throw new Error(`${label}[${index}].dir must be "asc" or "desc"`);
|
|
70
|
+
const term = { field, dir: raw.dir };
|
|
71
|
+
if (raw.nulls != null) {
|
|
72
|
+
if (raw.nulls !== "first" && raw.nulls !== "last") {
|
|
73
|
+
throw new Error(`${label}[${index}].nulls must be "first" or "last"`);
|
|
74
|
+
}
|
|
75
|
+
term.nulls = raw.nulls;
|
|
76
|
+
}
|
|
77
|
+
return term;
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
function renderOrderTerms(terms) {
|
|
81
|
+
if (terms.length === 0) return "nil";
|
|
82
|
+
const members = terms.map((term) => {
|
|
83
|
+
const nulls = term.nulls ? `, Nulls: ${goString(term.nulls)}` : "";
|
|
84
|
+
return `{Field: ${goString(term.field)}, Dir: ${goString(term.dir)}${nulls}}`;
|
|
85
|
+
});
|
|
86
|
+
return `[]querytable.OrderBy{${members.join(", ")}}`;
|
|
87
|
+
}
|
|
88
|
+
function projectGoFields(doc) {
|
|
89
|
+
if (!Array.isArray(doc.fields)) throw new Error("schema.fields must be an array");
|
|
90
|
+
const rawFields = doc.fields.map((field, index) => asObject(field, `schema.fields[${index}]`));
|
|
91
|
+
const exprByName = /* @__PURE__ */ new Map();
|
|
92
|
+
for (const [index, field] of rawFields.entries()) {
|
|
93
|
+
const name = requiredString(field.name, `schema.fields[${index}].name`);
|
|
94
|
+
if (field.bindings == null) continue;
|
|
95
|
+
const bindings = asObject(field.bindings, `schema.fields[${index}].bindings`);
|
|
96
|
+
if (bindings.postgres == null) continue;
|
|
97
|
+
const postgres = asObject(bindings.postgres, `schema.fields[${index}].bindings.postgres`);
|
|
98
|
+
exprByName.set(name, requiredString(postgres.expr, `schema.fields[${index}].bindings.postgres.expr`));
|
|
99
|
+
}
|
|
100
|
+
const fields = [];
|
|
101
|
+
for (const [index, field] of rawFields.entries()) {
|
|
102
|
+
const name = requiredString(field.name, `schema.fields[${index}].name`);
|
|
103
|
+
const expr = exprByName.get(name);
|
|
104
|
+
if (!expr) continue;
|
|
105
|
+
const bindings = asObject(field.bindings, `schema.fields[${index}].bindings`);
|
|
106
|
+
const postgres = asObject(bindings.postgres, `schema.fields[${index}].bindings.postgres`);
|
|
107
|
+
const kindName = typeof postgres.kind === "string" ? postgres.kind : requiredString(field.type, `schema.fields[${index}].type`);
|
|
108
|
+
const kind = GO_KINDS[kindName];
|
|
109
|
+
if (!kind) throw new Error(`field ${JSON.stringify(name)} has unknown postgres kind ${JSON.stringify(kindName)}`);
|
|
110
|
+
let serverFilter = true;
|
|
111
|
+
if (field.filter != null) {
|
|
112
|
+
const filter = asObject(field.filter, `schema.fields[${index}].filter`);
|
|
113
|
+
if (typeof filter.enabled === "boolean") serverFilter = filter.enabled;
|
|
114
|
+
if (typeof filter.pushdown === "boolean") serverFilter = serverFilter && filter.pushdown;
|
|
115
|
+
}
|
|
116
|
+
let sortable = true;
|
|
117
|
+
let sortExpr = expr;
|
|
118
|
+
if (field.sort != null) {
|
|
119
|
+
const sort = asObject(field.sort, `schema.fields[${index}].sort`);
|
|
120
|
+
if (typeof sort.enabled === "boolean") sortable = sort.enabled;
|
|
121
|
+
if (typeof sort.field === "string") {
|
|
122
|
+
const referencedExpr = exprByName.get(sort.field);
|
|
123
|
+
if (!referencedExpr) {
|
|
124
|
+
throw new Error(`field ${JSON.stringify(name)} sort target ${JSON.stringify(sort.field)} has no postgres binding`);
|
|
125
|
+
}
|
|
126
|
+
sortExpr = referencedExpr;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
fields.push({
|
|
130
|
+
name,
|
|
131
|
+
kind,
|
|
132
|
+
expr,
|
|
133
|
+
synthetic: postgres.synthetic === true,
|
|
134
|
+
serverFilter,
|
|
135
|
+
sortable,
|
|
136
|
+
sortExpr
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
return fields;
|
|
140
|
+
}
|
|
141
|
+
function generateTypeScript(input) {
|
|
142
|
+
const schema = loadSchema(input);
|
|
143
|
+
const constant = exportedName(schema.name).replace(/Schema$/, "").toUpperCase() + "_SCHEMA";
|
|
144
|
+
return [
|
|
145
|
+
"// Code generated by query-table-codegen. DO NOT EDIT.",
|
|
146
|
+
'import type { FieldSchema } from "@pythia-software/query-table-core";',
|
|
147
|
+
"",
|
|
148
|
+
`export const ${constant}: FieldSchema = ${JSON.stringify(schema, null, 2)};`,
|
|
149
|
+
`export const ${constant.replace(/_SCHEMA$/, "_FIELDS")} = ${constant}.fields;`,
|
|
150
|
+
""
|
|
151
|
+
].join("\n");
|
|
152
|
+
}
|
|
153
|
+
function generateGo(input, options) {
|
|
154
|
+
validateGoPackage(options.packageName);
|
|
155
|
+
loadSchema(input);
|
|
156
|
+
const doc = asObject(input, "schema");
|
|
157
|
+
const name = requiredString(doc.name, "schema.name");
|
|
158
|
+
const idField = requiredString(doc.idField, "schema.idField");
|
|
159
|
+
const fields = projectGoFields(doc);
|
|
160
|
+
if (!fields.some((field) => field.name === idField)) {
|
|
161
|
+
throw new Error(`id field ${JSON.stringify(idField)} has no postgres binding`);
|
|
162
|
+
}
|
|
163
|
+
const fieldNames = new Set(doc.fields.map((field) => requiredString(field.name, "schema field name")));
|
|
164
|
+
const defaultSort = orderTerms(doc.defaultSort, "schema.defaultSort", fieldNames);
|
|
165
|
+
const tiebreakSort = orderTerms(doc.tiebreakSort, "schema.tiebreakSort", fieldNames);
|
|
166
|
+
const importPath = options.importPath ?? DEFAULT_GO_IMPORT;
|
|
167
|
+
if (!importPath || /[\s\x00-\x1f\x7f"]/u.test(importPath)) throw new Error("invalid Go import path");
|
|
168
|
+
const longestFieldKey = Math.max(0, ...fields.map((field) => goString(field.name).length));
|
|
169
|
+
const fieldLines = fields.map(
|
|
170
|
+
(field) => {
|
|
171
|
+
const key = goString(field.name);
|
|
172
|
+
const spacing = " ".repeat(longestFieldKey - key.length + 1);
|
|
173
|
+
return ` ${key}:${spacing}{Name: ${goString(field.name)}, Kind: querytable.${field.kind}, Expr: ${goString(field.expr)}, Synthetic: ${field.synthetic}, ServerFilter: ${field.serverFilter}, Sortable: ${field.sortable}, SortExpr: ${goString(field.sortExpr)}},`;
|
|
174
|
+
}
|
|
175
|
+
);
|
|
176
|
+
return [
|
|
177
|
+
"// Code generated by query-table-codegen. DO NOT EDIT.",
|
|
178
|
+
`package ${options.packageName}`,
|
|
179
|
+
"",
|
|
180
|
+
`import querytable ${goString(importPath)}`,
|
|
181
|
+
"",
|
|
182
|
+
`func ${exportedName(name)}() querytable.Schema {`,
|
|
183
|
+
" return querytable.Schema{",
|
|
184
|
+
` Name: ${goString(name)},`,
|
|
185
|
+
` IDField: ${goString(idField)},`,
|
|
186
|
+
" Fields: map[string]querytable.FieldSpec{",
|
|
187
|
+
...fieldLines,
|
|
188
|
+
" },",
|
|
189
|
+
` DefaultSort: ${renderOrderTerms(defaultSort)},`,
|
|
190
|
+
` TiebreakSort: ${renderOrderTerms(tiebreakSort)},`,
|
|
191
|
+
" }",
|
|
192
|
+
"}",
|
|
193
|
+
""
|
|
194
|
+
].join("\n");
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export {
|
|
198
|
+
generateTypeScript,
|
|
199
|
+
generateGo
|
|
200
|
+
};
|
|
201
|
+
//# sourceMappingURL=chunk-RJYRZ372.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/generate.ts"],"sourcesContent":["import { loadSchema } from \"@pythia-software/query-table-core\";\n\nexport interface GoGenerationOptions {\n packageName: string;\n importPath?: string;\n}\n\nconst DEFAULT_GO_IMPORT = \"github.com/Pythia-Software/query-table/backends/go\";\n\nconst GO_KINDS: Record<string, string> = {\n text: \"FieldText\",\n number: \"FieldNumber\",\n datetime: \"FieldDatetime\",\n bool: \"FieldBool\",\n enum: \"FieldEnum\",\n textarray: \"FieldTextArray\",\n};\n\nconst GO_KEYWORDS = new Set([\n \"break\",\n \"default\",\n \"func\",\n \"interface\",\n \"select\",\n \"case\",\n \"defer\",\n \"go\",\n \"map\",\n \"struct\",\n \"chan\",\n \"else\",\n \"goto\",\n \"package\",\n \"switch\",\n \"const\",\n \"fallthrough\",\n \"if\",\n \"range\",\n \"type\",\n \"continue\",\n \"for\",\n \"import\",\n \"return\",\n \"var\",\n]);\n\ntype JsonObject = Record<string, unknown>;\n\ninterface GoField {\n name: string;\n kind: string;\n expr: string;\n synthetic: boolean;\n serverFilter: boolean;\n sortable: boolean;\n sortExpr: string;\n}\n\ninterface OrderTerm {\n field: string;\n dir: \"asc\" | \"desc\";\n nulls?: \"first\" | \"last\";\n}\n\nfunction asObject(value: unknown, label: string): JsonObject {\n if (value == null || typeof value !== \"object\" || Array.isArray(value)) {\n throw new Error(`${label} must be an object`);\n }\n return value as JsonObject;\n}\n\nfunction requiredString(value: unknown, label: string): string {\n if (typeof value !== \"string\" || !value) throw new Error(`${label} must be a non-empty string`);\n return value;\n}\n\nfunction goString(value: string): string {\n return JSON.stringify(value).replace(/\\u2028/g, \"\\\\u2028\").replace(/\\u2029/g, \"\\\\u2029\");\n}\n\nfunction exportedName(name: string): string {\n const parts = name.split(/[^A-Za-z0-9]+/).filter(Boolean);\n const base = parts.map((part) => part[0]!.toUpperCase() + part.slice(1)).join(\"\") || \"Generated\";\n return `${/^\\d/.test(base) ? `Schema${base}` : base}Schema`;\n}\n\nfunction validateGoPackage(packageName: string): void {\n if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(packageName) || GO_KEYWORDS.has(packageName)) {\n throw new Error(`invalid Go package name ${JSON.stringify(packageName)}`);\n }\n}\n\nfunction orderTerms(value: unknown, label: string, fieldNames: Set<string>): OrderTerm[] {\n if (value == null) return [];\n if (!Array.isArray(value)) throw new Error(`${label} must be an array`);\n return value.map((item, index) => {\n const raw = asObject(item, `${label}[${index}]`);\n const field = requiredString(raw.field, `${label}[${index}].field`);\n if (!fieldNames.has(field)) throw new Error(`${label}[${index}] references unknown field ${JSON.stringify(field)}`);\n if (raw.dir !== \"asc\" && raw.dir !== \"desc\") throw new Error(`${label}[${index}].dir must be \"asc\" or \"desc\"`);\n const term: OrderTerm = { field, dir: raw.dir };\n if (raw.nulls != null) {\n if (raw.nulls !== \"first\" && raw.nulls !== \"last\") {\n throw new Error(`${label}[${index}].nulls must be \"first\" or \"last\"`);\n }\n term.nulls = raw.nulls;\n }\n return term;\n });\n}\n\nfunction renderOrderTerms(terms: OrderTerm[]): string {\n if (terms.length === 0) return \"nil\";\n const members = terms.map((term) => {\n const nulls = term.nulls ? `, Nulls: ${goString(term.nulls)}` : \"\";\n return `{Field: ${goString(term.field)}, Dir: ${goString(term.dir)}${nulls}}`;\n });\n return `[]querytable.OrderBy{${members.join(\", \")}}`;\n}\n\nfunction projectGoFields(doc: JsonObject): GoField[] {\n if (!Array.isArray(doc.fields)) throw new Error(\"schema.fields must be an array\");\n const rawFields = doc.fields.map((field, index) => asObject(field, `schema.fields[${index}]`));\n const exprByName = new Map<string, string>();\n\n for (const [index, field] of rawFields.entries()) {\n const name = requiredString(field.name, `schema.fields[${index}].name`);\n if (field.bindings == null) continue;\n const bindings = asObject(field.bindings, `schema.fields[${index}].bindings`);\n if (bindings.postgres == null) continue;\n const postgres = asObject(bindings.postgres, `schema.fields[${index}].bindings.postgres`);\n exprByName.set(name, requiredString(postgres.expr, `schema.fields[${index}].bindings.postgres.expr`));\n }\n\n const fields: GoField[] = [];\n for (const [index, field] of rawFields.entries()) {\n const name = requiredString(field.name, `schema.fields[${index}].name`);\n const expr = exprByName.get(name);\n if (!expr) continue;\n const bindings = asObject(field.bindings, `schema.fields[${index}].bindings`);\n const postgres = asObject(bindings.postgres, `schema.fields[${index}].bindings.postgres`);\n const kindName = typeof postgres.kind === \"string\" ? postgres.kind : requiredString(field.type, `schema.fields[${index}].type`);\n const kind = GO_KINDS[kindName];\n if (!kind) throw new Error(`field ${JSON.stringify(name)} has unknown postgres kind ${JSON.stringify(kindName)}`);\n\n let serverFilter = true;\n if (field.filter != null) {\n const filter = asObject(field.filter, `schema.fields[${index}].filter`);\n if (typeof filter.enabled === \"boolean\") serverFilter = filter.enabled;\n if (typeof filter.pushdown === \"boolean\") serverFilter = serverFilter && filter.pushdown;\n }\n\n let sortable = true;\n let sortExpr = expr;\n if (field.sort != null) {\n const sort = asObject(field.sort, `schema.fields[${index}].sort`);\n if (typeof sort.enabled === \"boolean\") sortable = sort.enabled;\n if (typeof sort.field === \"string\") {\n const referencedExpr = exprByName.get(sort.field);\n if (!referencedExpr) {\n throw new Error(`field ${JSON.stringify(name)} sort target ${JSON.stringify(sort.field)} has no postgres binding`);\n }\n sortExpr = referencedExpr;\n }\n }\n\n fields.push({\n name,\n kind,\n expr,\n synthetic: postgres.synthetic === true,\n serverFilter,\n sortable,\n sortExpr,\n });\n }\n return fields;\n}\n\n/** Generate the frontend projection. SQL bindings are deliberately omitted. */\nexport function generateTypeScript(input: unknown): string {\n const schema = loadSchema(input);\n const constant = exportedName(schema.name).replace(/Schema$/, \"\").toUpperCase() + \"_SCHEMA\";\n return [\n \"// Code generated by query-table-codegen. DO NOT EDIT.\",\n 'import type { FieldSchema } from \"@pythia-software/query-table-core\";',\n \"\",\n `export const ${constant}: FieldSchema = ${JSON.stringify(schema, null, 2)};`,\n `export const ${constant.replace(/_SCHEMA$/, \"_FIELDS\")} = ${constant}.fields;`,\n \"\",\n ].join(\"\\n\");\n}\n\n/** Generate a direct Go Schema literal with no runtime JSON parsing. */\nexport function generateGo(input: unknown, options: GoGenerationOptions): string {\n validateGoPackage(options.packageName);\n loadSchema(input);\n const doc = asObject(input, \"schema\");\n const name = requiredString(doc.name, \"schema.name\");\n const idField = requiredString(doc.idField, \"schema.idField\");\n const fields = projectGoFields(doc);\n if (!fields.some((field) => field.name === idField)) {\n throw new Error(`id field ${JSON.stringify(idField)} has no postgres binding`);\n }\n const fieldNames = new Set((doc.fields as JsonObject[]).map((field) => requiredString(field.name, \"schema field name\")));\n const defaultSort = orderTerms(doc.defaultSort, \"schema.defaultSort\", fieldNames);\n const tiebreakSort = orderTerms(doc.tiebreakSort, \"schema.tiebreakSort\", fieldNames);\n const importPath = options.importPath ?? DEFAULT_GO_IMPORT;\n if (!importPath || /[\\s\\x00-\\x1f\\x7f\"]/u.test(importPath)) throw new Error(\"invalid Go import path\");\n\n const longestFieldKey = Math.max(0, ...fields.map((field) => goString(field.name).length));\n const fieldLines = fields.map(\n (field) => {\n const key = goString(field.name);\n const spacing = \" \".repeat(longestFieldKey - key.length + 1);\n return `\\t\\t\\t${key}:${spacing}{Name: ${goString(field.name)}, Kind: querytable.${field.kind}, Expr: ${goString(field.expr)}, Synthetic: ${field.synthetic}, ServerFilter: ${field.serverFilter}, Sortable: ${field.sortable}, SortExpr: ${goString(field.sortExpr)}},`;\n },\n );\n\n return [\n \"// Code generated by query-table-codegen. DO NOT EDIT.\",\n `package ${options.packageName}`,\n \"\",\n `import querytable ${goString(importPath)}`,\n \"\",\n `func ${exportedName(name)}() querytable.Schema {`,\n \"\\treturn querytable.Schema{\",\n `\\t\\tName: ${goString(name)},`,\n `\\t\\tIDField: ${goString(idField)},`,\n \"\\t\\tFields: map[string]querytable.FieldSpec{\",\n ...fieldLines,\n \"\\t\\t},\",\n `\\t\\tDefaultSort: ${renderOrderTerms(defaultSort)},`,\n `\\t\\tTiebreakSort: ${renderOrderTerms(tiebreakSort)},`,\n \"\\t}\",\n \"}\",\n \"\",\n ].join(\"\\n\");\n}\n"],"mappings":";AAAA,SAAS,kBAAkB;AAO3B,IAAM,oBAAoB;AAE1B,IAAM,WAAmC;AAAA,EACvC,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,MAAM;AAAA,EACN,MAAM;AAAA,EACN,WAAW;AACb;AAEA,IAAM,cAAc,oBAAI,IAAI;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAoBD,SAAS,SAAS,OAAgB,OAA2B;AAC3D,MAAI,SAAS,QAAQ,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,GAAG;AACtE,UAAM,IAAI,MAAM,GAAG,KAAK,oBAAoB;AAAA,EAC9C;AACA,SAAO;AACT;AAEA,SAAS,eAAe,OAAgB,OAAuB;AAC7D,MAAI,OAAO,UAAU,YAAY,CAAC,MAAO,OAAM,IAAI,MAAM,GAAG,KAAK,6BAA6B;AAC9F,SAAO;AACT;AAEA,SAAS,SAAS,OAAuB;AACvC,SAAO,KAAK,UAAU,KAAK,EAAE,QAAQ,WAAW,SAAS,EAAE,QAAQ,WAAW,SAAS;AACzF;AAEA,SAAS,aAAa,MAAsB;AAC1C,QAAM,QAAQ,KAAK,MAAM,eAAe,EAAE,OAAO,OAAO;AACxD,QAAM,OAAO,MAAM,IAAI,CAAC,SAAS,KAAK,CAAC,EAAG,YAAY,IAAI,KAAK,MAAM,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK;AACrF,SAAO,GAAG,MAAM,KAAK,IAAI,IAAI,SAAS,IAAI,KAAK,IAAI;AACrD;AAEA,SAAS,kBAAkB,aAA2B;AACpD,MAAI,CAAC,2BAA2B,KAAK,WAAW,KAAK,YAAY,IAAI,WAAW,GAAG;AACjF,UAAM,IAAI,MAAM,2BAA2B,KAAK,UAAU,WAAW,CAAC,EAAE;AAAA,EAC1E;AACF;AAEA,SAAS,WAAW,OAAgB,OAAe,YAAsC;AACvF,MAAI,SAAS,KAAM,QAAO,CAAC;AAC3B,MAAI,CAAC,MAAM,QAAQ,KAAK,EAAG,OAAM,IAAI,MAAM,GAAG,KAAK,mBAAmB;AACtE,SAAO,MAAM,IAAI,CAAC,MAAM,UAAU;AAChC,UAAM,MAAM,SAAS,MAAM,GAAG,KAAK,IAAI,KAAK,GAAG;AAC/C,UAAM,QAAQ,eAAe,IAAI,OAAO,GAAG,KAAK,IAAI,KAAK,SAAS;AAClE,QAAI,CAAC,WAAW,IAAI,KAAK,EAAG,OAAM,IAAI,MAAM,GAAG,KAAK,IAAI,KAAK,8BAA8B,KAAK,UAAU,KAAK,CAAC,EAAE;AAClH,QAAI,IAAI,QAAQ,SAAS,IAAI,QAAQ,OAAQ,OAAM,IAAI,MAAM,GAAG,KAAK,IAAI,KAAK,+BAA+B;AAC7G,UAAM,OAAkB,EAAE,OAAO,KAAK,IAAI,IAAI;AAC9C,QAAI,IAAI,SAAS,MAAM;AACrB,UAAI,IAAI,UAAU,WAAW,IAAI,UAAU,QAAQ;AACjD,cAAM,IAAI,MAAM,GAAG,KAAK,IAAI,KAAK,mCAAmC;AAAA,MACtE;AACA,WAAK,QAAQ,IAAI;AAAA,IACnB;AACA,WAAO;AAAA,EACT,CAAC;AACH;AAEA,SAAS,iBAAiB,OAA4B;AACpD,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,QAAM,UAAU,MAAM,IAAI,CAAC,SAAS;AAClC,UAAM,QAAQ,KAAK,QAAQ,YAAY,SAAS,KAAK,KAAK,CAAC,KAAK;AAChE,WAAO,WAAW,SAAS,KAAK,KAAK,CAAC,UAAU,SAAS,KAAK,GAAG,CAAC,GAAG,KAAK;AAAA,EAC5E,CAAC;AACD,SAAO,wBAAwB,QAAQ,KAAK,IAAI,CAAC;AACnD;AAEA,SAAS,gBAAgB,KAA4B;AACnD,MAAI,CAAC,MAAM,QAAQ,IAAI,MAAM,EAAG,OAAM,IAAI,MAAM,gCAAgC;AAChF,QAAM,YAAY,IAAI,OAAO,IAAI,CAAC,OAAO,UAAU,SAAS,OAAO,iBAAiB,KAAK,GAAG,CAAC;AAC7F,QAAM,aAAa,oBAAI,IAAoB;AAE3C,aAAW,CAAC,OAAO,KAAK,KAAK,UAAU,QAAQ,GAAG;AAChD,UAAM,OAAO,eAAe,MAAM,MAAM,iBAAiB,KAAK,QAAQ;AACtE,QAAI,MAAM,YAAY,KAAM;AAC5B,UAAM,WAAW,SAAS,MAAM,UAAU,iBAAiB,KAAK,YAAY;AAC5E,QAAI,SAAS,YAAY,KAAM;AAC/B,UAAM,WAAW,SAAS,SAAS,UAAU,iBAAiB,KAAK,qBAAqB;AACxF,eAAW,IAAI,MAAM,eAAe,SAAS,MAAM,iBAAiB,KAAK,0BAA0B,CAAC;AAAA,EACtG;AAEA,QAAM,SAAoB,CAAC;AAC3B,aAAW,CAAC,OAAO,KAAK,KAAK,UAAU,QAAQ,GAAG;AAChD,UAAM,OAAO,eAAe,MAAM,MAAM,iBAAiB,KAAK,QAAQ;AACtE,UAAM,OAAO,WAAW,IAAI,IAAI;AAChC,QAAI,CAAC,KAAM;AACX,UAAM,WAAW,SAAS,MAAM,UAAU,iBAAiB,KAAK,YAAY;AAC5E,UAAM,WAAW,SAAS,SAAS,UAAU,iBAAiB,KAAK,qBAAqB;AACxF,UAAM,WAAW,OAAO,SAAS,SAAS,WAAW,SAAS,OAAO,eAAe,MAAM,MAAM,iBAAiB,KAAK,QAAQ;AAC9H,UAAM,OAAO,SAAS,QAAQ;AAC9B,QAAI,CAAC,KAAM,OAAM,IAAI,MAAM,SAAS,KAAK,UAAU,IAAI,CAAC,8BAA8B,KAAK,UAAU,QAAQ,CAAC,EAAE;AAEhH,QAAI,eAAe;AACnB,QAAI,MAAM,UAAU,MAAM;AACxB,YAAM,SAAS,SAAS,MAAM,QAAQ,iBAAiB,KAAK,UAAU;AACtE,UAAI,OAAO,OAAO,YAAY,UAAW,gBAAe,OAAO;AAC/D,UAAI,OAAO,OAAO,aAAa,UAAW,gBAAe,gBAAgB,OAAO;AAAA,IAClF;AAEA,QAAI,WAAW;AACf,QAAI,WAAW;AACf,QAAI,MAAM,QAAQ,MAAM;AACtB,YAAM,OAAO,SAAS,MAAM,MAAM,iBAAiB,KAAK,QAAQ;AAChE,UAAI,OAAO,KAAK,YAAY,UAAW,YAAW,KAAK;AACvD,UAAI,OAAO,KAAK,UAAU,UAAU;AAClC,cAAM,iBAAiB,WAAW,IAAI,KAAK,KAAK;AAChD,YAAI,CAAC,gBAAgB;AACnB,gBAAM,IAAI,MAAM,SAAS,KAAK,UAAU,IAAI,CAAC,gBAAgB,KAAK,UAAU,KAAK,KAAK,CAAC,0BAA0B;AAAA,QACnH;AACA,mBAAW;AAAA,MACb;AAAA,IACF;AAEA,WAAO,KAAK;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAW,SAAS,cAAc;AAAA,MAClC;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAGO,SAAS,mBAAmB,OAAwB;AACzD,QAAM,SAAS,WAAW,KAAK;AAC/B,QAAM,WAAW,aAAa,OAAO,IAAI,EAAE,QAAQ,WAAW,EAAE,EAAE,YAAY,IAAI;AAClF,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,gBAAgB,QAAQ,mBAAmB,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAAA,IAC1E,gBAAgB,SAAS,QAAQ,YAAY,SAAS,CAAC,MAAM,QAAQ;AAAA,IACrE;AAAA,EACF,EAAE,KAAK,IAAI;AACb;AAGO,SAAS,WAAW,OAAgB,SAAsC;AAC/E,oBAAkB,QAAQ,WAAW;AACrC,aAAW,KAAK;AAChB,QAAM,MAAM,SAAS,OAAO,QAAQ;AACpC,QAAM,OAAO,eAAe,IAAI,MAAM,aAAa;AACnD,QAAM,UAAU,eAAe,IAAI,SAAS,gBAAgB;AAC5D,QAAM,SAAS,gBAAgB,GAAG;AAClC,MAAI,CAAC,OAAO,KAAK,CAAC,UAAU,MAAM,SAAS,OAAO,GAAG;AACnD,UAAM,IAAI,MAAM,YAAY,KAAK,UAAU,OAAO,CAAC,0BAA0B;AAAA,EAC/E;AACA,QAAM,aAAa,IAAI,IAAK,IAAI,OAAwB,IAAI,CAAC,UAAU,eAAe,MAAM,MAAM,mBAAmB,CAAC,CAAC;AACvH,QAAM,cAAc,WAAW,IAAI,aAAa,sBAAsB,UAAU;AAChF,QAAM,eAAe,WAAW,IAAI,cAAc,uBAAuB,UAAU;AACnF,QAAM,aAAa,QAAQ,cAAc;AACzC,MAAI,CAAC,cAAc,sBAAsB,KAAK,UAAU,EAAG,OAAM,IAAI,MAAM,wBAAwB;AAEnG,QAAM,kBAAkB,KAAK,IAAI,GAAG,GAAG,OAAO,IAAI,CAAC,UAAU,SAAS,MAAM,IAAI,EAAE,MAAM,CAAC;AACzF,QAAM,aAAa,OAAO;AAAA,IACxB,CAAC,UAAU;AACT,YAAM,MAAM,SAAS,MAAM,IAAI;AAC/B,YAAM,UAAU,IAAI,OAAO,kBAAkB,IAAI,SAAS,CAAC;AAC3D,aAAO,MAAS,GAAG,IAAI,OAAO,UAAU,SAAS,MAAM,IAAI,CAAC,sBAAsB,MAAM,IAAI,WAAW,SAAS,MAAM,IAAI,CAAC,gBAAgB,MAAM,SAAS,mBAAmB,MAAM,YAAY,eAAe,MAAM,QAAQ,eAAe,SAAS,MAAM,QAAQ,CAAC;AAAA,IACrQ;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA,WAAW,QAAQ,WAAW;AAAA,IAC9B;AAAA,IACA,qBAAqB,SAAS,UAAU,CAAC;AAAA,IACzC;AAAA,IACA,QAAQ,aAAa,IAAI,CAAC;AAAA,IAC1B;AAAA,IACA,cAAgB,SAAS,IAAI,CAAC;AAAA,IAC9B,cAAgB,SAAS,OAAO,CAAC;AAAA,IACjC;AAAA,IACA,GAAG;AAAA,IACH;AAAA,IACA,mBAAqB,iBAAiB,WAAW,CAAC;AAAA,IAClD,mBAAqB,iBAAiB,YAAY,CAAC;AAAA,IACnD;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,KAAK,IAAI;AACb;","names":[]}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
generateGo,
|
|
4
|
+
generateTypeScript
|
|
5
|
+
} from "./chunk-RJYRZ372.js";
|
|
6
|
+
|
|
7
|
+
// src/cli.ts
|
|
8
|
+
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
9
|
+
import { dirname, resolve } from "path";
|
|
10
|
+
var packageManifest = JSON.parse(await readFile(new URL("../package.json", import.meta.url), "utf8"));
|
|
11
|
+
if (typeof packageManifest.version !== "string" || !packageManifest.version) {
|
|
12
|
+
throw new Error("package.json does not contain a valid version");
|
|
13
|
+
}
|
|
14
|
+
var VERSION = packageManifest.version;
|
|
15
|
+
var HELP = `query-table-codegen ${VERSION}
|
|
16
|
+
|
|
17
|
+
Usage:
|
|
18
|
+
query-table-codegen <schema.json> --ts <output.ts>
|
|
19
|
+
query-table-codegen <schema.json> --go <output.go> --go-package <name> [--go-import <path>]
|
|
20
|
+
|
|
21
|
+
Both --ts and --go may be supplied in one invocation.
|
|
22
|
+
`;
|
|
23
|
+
function optionValue(args, index, flag) {
|
|
24
|
+
const value = args[index + 1];
|
|
25
|
+
if (!value || value.startsWith("--")) throw new Error(`${flag} requires a value`);
|
|
26
|
+
return value;
|
|
27
|
+
}
|
|
28
|
+
function parseArgs(args) {
|
|
29
|
+
if (args.includes("--help") || args.includes("-h")) return "help";
|
|
30
|
+
if (args.includes("--version") || args.includes("-v")) return "version";
|
|
31
|
+
const input = args[0];
|
|
32
|
+
if (!input || input.startsWith("--")) throw new Error("a schema JSON path is required");
|
|
33
|
+
const options = { input };
|
|
34
|
+
for (let index = 1; index < args.length; index += 1) {
|
|
35
|
+
const flag = args[index];
|
|
36
|
+
switch (flag) {
|
|
37
|
+
case "--ts":
|
|
38
|
+
options.ts = optionValue(args, index, flag);
|
|
39
|
+
index += 1;
|
|
40
|
+
break;
|
|
41
|
+
case "--go":
|
|
42
|
+
options.go = optionValue(args, index, flag);
|
|
43
|
+
index += 1;
|
|
44
|
+
break;
|
|
45
|
+
case "--go-package":
|
|
46
|
+
options.goPackage = optionValue(args, index, flag);
|
|
47
|
+
index += 1;
|
|
48
|
+
break;
|
|
49
|
+
case "--go-import":
|
|
50
|
+
options.goImport = optionValue(args, index, flag);
|
|
51
|
+
index += 1;
|
|
52
|
+
break;
|
|
53
|
+
default:
|
|
54
|
+
throw new Error(`unknown option ${flag}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (!options.ts && !options.go) throw new Error("at least one of --ts or --go is required");
|
|
58
|
+
if (options.go && !options.goPackage) throw new Error("--go-package is required with --go");
|
|
59
|
+
if (!options.go && (options.goPackage || options.goImport)) throw new Error("--go-package/--go-import require --go");
|
|
60
|
+
return options;
|
|
61
|
+
}
|
|
62
|
+
async function writeGenerated(path, contents) {
|
|
63
|
+
const output = resolve(path);
|
|
64
|
+
await mkdir(dirname(output), { recursive: true });
|
|
65
|
+
await writeFile(output, contents, "utf8");
|
|
66
|
+
}
|
|
67
|
+
async function main() {
|
|
68
|
+
const options = parseArgs(process.argv.slice(2));
|
|
69
|
+
if (options === "help") {
|
|
70
|
+
process.stdout.write(HELP);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (options === "version") {
|
|
74
|
+
process.stdout.write(`${VERSION}
|
|
75
|
+
`);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const source = await readFile(resolve(options.input), "utf8");
|
|
79
|
+
let document;
|
|
80
|
+
try {
|
|
81
|
+
document = JSON.parse(source);
|
|
82
|
+
} catch (error) {
|
|
83
|
+
throw new Error(`invalid JSON in ${options.input}: ${error.message}`);
|
|
84
|
+
}
|
|
85
|
+
const inputPath = resolve(options.input);
|
|
86
|
+
const tsPath = options.ts ? resolve(options.ts) : void 0;
|
|
87
|
+
const goPath = options.go ? resolve(options.go) : void 0;
|
|
88
|
+
if (tsPath === inputPath || goPath === inputPath) throw new Error("an output path must not overwrite the input schema");
|
|
89
|
+
if (tsPath && goPath && tsPath === goPath) throw new Error("--ts and --go must use different output paths");
|
|
90
|
+
const tsOutput = options.ts ? generateTypeScript(document) : void 0;
|
|
91
|
+
const goOptions = options.go ? options.goImport ? { packageName: options.goPackage, importPath: options.goImport } : { packageName: options.goPackage } : void 0;
|
|
92
|
+
const goOutput = goOptions ? generateGo(document, goOptions) : void 0;
|
|
93
|
+
await Promise.all([
|
|
94
|
+
...options.ts && tsOutput ? [writeGenerated(options.ts, tsOutput)] : [],
|
|
95
|
+
...options.go && goOutput ? [writeGenerated(options.go, goOutput)] : []
|
|
96
|
+
]);
|
|
97
|
+
}
|
|
98
|
+
void main().catch((error) => {
|
|
99
|
+
process.stderr.write(`query-table-codegen: ${error.message}
|
|
100
|
+
`);
|
|
101
|
+
process.exitCode = 1;
|
|
102
|
+
});
|
|
103
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname, resolve } from \"node:path\";\nimport { generateGo, generateTypeScript } from \"./generate\";\n\nconst packageManifest = JSON.parse(await readFile(new URL(\"../package.json\", import.meta.url), \"utf8\")) as {\n version?: unknown;\n};\nif (typeof packageManifest.version !== \"string\" || !packageManifest.version) {\n throw new Error(\"package.json does not contain a valid version\");\n}\nconst VERSION = packageManifest.version;\n\ninterface CliOptions {\n input: string;\n ts?: string;\n go?: string;\n goPackage?: string;\n goImport?: string;\n}\n\nconst HELP = `query-table-codegen ${VERSION}\n\nUsage:\n query-table-codegen <schema.json> --ts <output.ts>\n query-table-codegen <schema.json> --go <output.go> --go-package <name> [--go-import <path>]\n\nBoth --ts and --go may be supplied in one invocation.\n`;\n\nfunction optionValue(args: string[], index: number, flag: string): string {\n const value = args[index + 1];\n if (!value || value.startsWith(\"--\")) throw new Error(`${flag} requires a value`);\n return value;\n}\n\nfunction parseArgs(args: string[]): CliOptions | \"help\" | \"version\" {\n if (args.includes(\"--help\") || args.includes(\"-h\")) return \"help\";\n if (args.includes(\"--version\") || args.includes(\"-v\")) return \"version\";\n const input = args[0];\n if (!input || input.startsWith(\"--\")) throw new Error(\"a schema JSON path is required\");\n const options: CliOptions = { input };\n\n for (let index = 1; index < args.length; index += 1) {\n const flag = args[index]!;\n switch (flag) {\n case \"--ts\":\n options.ts = optionValue(args, index, flag);\n index += 1;\n break;\n case \"--go\":\n options.go = optionValue(args, index, flag);\n index += 1;\n break;\n case \"--go-package\":\n options.goPackage = optionValue(args, index, flag);\n index += 1;\n break;\n case \"--go-import\":\n options.goImport = optionValue(args, index, flag);\n index += 1;\n break;\n default:\n throw new Error(`unknown option ${flag}`);\n }\n }\n\n if (!options.ts && !options.go) throw new Error(\"at least one of --ts or --go is required\");\n if (options.go && !options.goPackage) throw new Error(\"--go-package is required with --go\");\n if (!options.go && (options.goPackage || options.goImport)) throw new Error(\"--go-package/--go-import require --go\");\n return options;\n}\n\nasync function writeGenerated(path: string, contents: string): Promise<void> {\n const output = resolve(path);\n await mkdir(dirname(output), { recursive: true });\n await writeFile(output, contents, \"utf8\");\n}\n\nasync function main(): Promise<void> {\n const options = parseArgs(process.argv.slice(2));\n if (options === \"help\") {\n process.stdout.write(HELP);\n return;\n }\n if (options === \"version\") {\n process.stdout.write(`${VERSION}\\n`);\n return;\n }\n\n const source = await readFile(resolve(options.input), \"utf8\");\n let document: unknown;\n try {\n document = JSON.parse(source);\n } catch (error) {\n throw new Error(`invalid JSON in ${options.input}: ${(error as Error).message}`);\n }\n\n const inputPath = resolve(options.input);\n const tsPath = options.ts ? resolve(options.ts) : undefined;\n const goPath = options.go ? resolve(options.go) : undefined;\n if (tsPath === inputPath || goPath === inputPath) throw new Error(\"an output path must not overwrite the input schema\");\n if (tsPath && goPath && tsPath === goPath) throw new Error(\"--ts and --go must use different output paths\");\n\n const tsOutput = options.ts ? generateTypeScript(document) : undefined;\n const goOptions = options.go\n ? options.goImport\n ? { packageName: options.goPackage!, importPath: options.goImport }\n : { packageName: options.goPackage! }\n : undefined;\n const goOutput = goOptions ? generateGo(document, goOptions) : undefined;\n\n await Promise.all([\n ...(options.ts && tsOutput ? [writeGenerated(options.ts, tsOutput)] : []),\n ...(options.go && goOutput ? [writeGenerated(options.go, goOutput)] : []),\n ]);\n}\n\nvoid main().catch((error: unknown) => {\n process.stderr.write(`query-table-codegen: ${(error as Error).message}\\n`);\n process.exitCode = 1;\n});\n"],"mappings":"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","names":[]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
interface GoGenerationOptions {
|
|
2
|
+
packageName: string;
|
|
3
|
+
importPath?: string;
|
|
4
|
+
}
|
|
5
|
+
/** Generate the frontend projection. SQL bindings are deliberately omitted. */
|
|
6
|
+
declare function generateTypeScript(input: unknown): string;
|
|
7
|
+
/** Generate a direct Go Schema literal with no runtime JSON parsing. */
|
|
8
|
+
declare function generateGo(input: unknown, options: GoGenerationOptions): string;
|
|
9
|
+
|
|
10
|
+
export { type GoGenerationOptions, generateGo, generateTypeScript };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pythia-software/query-table-codegen",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Generate the TS catalog + Go schema from a query-table JSON schema document.",
|
|
5
|
+
"keywords": ["codegen", "data-table", "json-schema", "typescript", "golang"],
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "Pythia Software",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/Pythia-Software/query-table.git",
|
|
11
|
+
"directory": "tools/schema-codegen"
|
|
12
|
+
},
|
|
13
|
+
"homepage": "https://github.com/Pythia-Software/query-table/tree/main/tools/schema-codegen#readme",
|
|
14
|
+
"bugs": { "url": "https://github.com/Pythia-Software/query-table/issues" },
|
|
15
|
+
"engines": { "node": ">=22" },
|
|
16
|
+
"type": "module",
|
|
17
|
+
"sideEffects": false,
|
|
18
|
+
"main": "./dist/index.js",
|
|
19
|
+
"module": "./dist/index.js",
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"types": "./dist/index.d.ts",
|
|
24
|
+
"import": "./dist/index.js",
|
|
25
|
+
"default": "./dist/index.js"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"bin": { "query-table-codegen": "./dist/cli.js" },
|
|
29
|
+
"files": ["dist", "README.md", "LICENSE"],
|
|
30
|
+
"publishConfig": { "access": "public" },
|
|
31
|
+
"scripts": {
|
|
32
|
+
"build": "tsup src/index.ts src/cli.ts --format esm --dts --sourcemap --clean --target node22 --external @pythia-software/query-table-core",
|
|
33
|
+
"prepack": "npm run build",
|
|
34
|
+
"typecheck": "tsc --noEmit"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@pythia-software/query-table-core": "0.1.0"
|
|
38
|
+
}
|
|
39
|
+
}
|