postgresdk 0.18.1 → 0.18.2
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/dist/cli.js +0 -0
- package/package.json +1 -1
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/core/operations.d.ts +0 -68
- package/dist/emit-api-contract.d.ts +0 -60
- package/dist/emit-client.d.ts.map +0 -1
- package/dist/emit-client.js +0 -114
- package/dist/emit-client.js.map +0 -1
- package/dist/emit-include-builder.d.ts.map +0 -1
- package/dist/emit-include-builder.js +0 -30
- package/dist/emit-include-builder.js.map +0 -1
- package/dist/emit-include-loader.d.ts.map +0 -1
- package/dist/emit-include-loader.js +0 -299
- package/dist/emit-include-loader.js.map +0 -1
- package/dist/emit-include-spec.d.ts.map +0 -1
- package/dist/emit-include-spec.js +0 -26
- package/dist/emit-include-spec.js.map +0 -1
- package/dist/emit-logger.d.ts.map +0 -1
- package/dist/emit-logger.js +0 -35
- package/dist/emit-logger.js.map +0 -1
- package/dist/emit-router.d.ts +0 -5
- package/dist/emit-routes.d.ts.map +0 -1
- package/dist/emit-routes.js +0 -208
- package/dist/emit-routes.js.map +0 -1
- package/dist/emit-server-index.d.ts +0 -5
- package/dist/emit-types.d.ts.map +0 -1
- package/dist/emit-types.js +0 -51
- package/dist/emit-types.js.map +0 -1
- package/dist/emit-zod.d.ts.map +0 -1
- package/dist/emit-zod.js +0 -43
- package/dist/emit-zod.js.map +0 -1
- package/dist/gen.config.d.ts +0 -10
- package/dist/gen.config.js +0 -10
- package/dist/gen.config.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/introspect.d.ts.map +0 -1
- package/dist/introspect.js +0 -132
- package/dist/introspect.js.map +0 -1
- package/dist/rel-classify.d.ts.map +0 -1
- package/dist/rel-classify.js +0 -52
- package/dist/rel-classify.js.map +0 -1
- package/dist/src/cli.d.ts +0 -2
- package/dist/src/cli.js +0 -39
- package/dist/src/cli.js.map +0 -1
- package/dist/src/emit-client.d.ts +0 -3
- package/dist/src/emit-client.js +0 -114
- package/dist/src/emit-client.js.map +0 -1
- package/dist/src/emit-include-builder.d.ts +0 -2
- package/dist/src/emit-include-builder.js +0 -30
- package/dist/src/emit-include-builder.js.map +0 -1
- package/dist/src/emit-include-loader.d.ts +0 -9
- package/dist/src/emit-include-loader.js +0 -299
- package/dist/src/emit-include-loader.js.map +0 -1
- package/dist/src/emit-include-spec.d.ts +0 -2
- package/dist/src/emit-include-spec.js +0 -26
- package/dist/src/emit-include-spec.js.map +0 -1
- package/dist/src/emit-logger.d.ts +0 -1
- package/dist/src/emit-logger.js +0 -35
- package/dist/src/emit-logger.js.map +0 -1
- package/dist/src/emit-routes.d.ts +0 -20
- package/dist/src/emit-routes.js +0 -208
- package/dist/src/emit-routes.js.map +0 -1
- package/dist/src/emit-types.d.ts +0 -5
- package/dist/src/emit-types.js +0 -51
- package/dist/src/emit-types.js.map +0 -1
- package/dist/src/emit-zod.d.ts +0 -5
- package/dist/src/emit-zod.js +0 -43
- package/dist/src/emit-zod.js.map +0 -1
- package/dist/src/index.d.ts +0 -1
- package/dist/src/index.js +0 -83
- package/dist/src/index.js.map +0 -1
- package/dist/src/introspect.d.ts +0 -26
- package/dist/src/introspect.js +0 -132
- package/dist/src/introspect.js.map +0 -1
- package/dist/src/rel-classify.d.ts +0 -10
- package/dist/src/rel-classify.js +0 -52
- package/dist/src/rel-classify.js.map +0 -1
- package/dist/src/utils.d.ts +0 -6
- package/dist/src/utils.js +0 -17
- package/dist/src/utils.js.map +0 -1
- package/dist/utils.d.ts.map +0 -1
- package/dist/utils.js +0 -17
- package/dist/utils.js.map +0 -1
package/dist/src/emit-routes.js
DELETED
|
@@ -1,208 +0,0 @@
|
|
|
1
|
-
import { pascal } from "./utils";
|
|
2
|
-
/**
|
|
3
|
-
* Emits a Hono router for one table, using generated Zod schemas.
|
|
4
|
-
*
|
|
5
|
-
* Expects:
|
|
6
|
-
* - Generated file at ../zod/<table>.ts exporting Insert<Type>Schema & Update<Type>Schema
|
|
7
|
-
* - deps: { pg: Pool | Client } with .query(text, params)
|
|
8
|
-
*
|
|
9
|
-
* Endpoints:
|
|
10
|
-
* POST /v1/<table> create (Insert<Type>Schema)
|
|
11
|
-
* GET /v1/<table>/:...pk get by pk
|
|
12
|
-
* POST /v1/<table>/list list (limit/offset; includes)
|
|
13
|
-
* PATCH /v1/<table>/:...pk update (Update<Type>Schema)
|
|
14
|
-
* DELETE /v1/<table>/:...pk delete (or soft-delete)
|
|
15
|
-
*/
|
|
16
|
-
export function emitRoutes(table, _graph, opts) {
|
|
17
|
-
const fileTableName = table.name; // SQL table name for file/route
|
|
18
|
-
const Type = pascal(table.name); // PascalCase for type/schemas
|
|
19
|
-
// Normalize pk to an array and fallback to ["id"] if empty
|
|
20
|
-
const rawPk = table.pk;
|
|
21
|
-
const pkCols = Array.isArray(rawPk) ? rawPk : rawPk ? [rawPk] : [];
|
|
22
|
-
const safePkCols = pkCols.length ? pkCols : ["id"];
|
|
23
|
-
const hasCompositePk = safePkCols.length > 1;
|
|
24
|
-
const pkPath = hasCompositePk ? safePkCols.map((c) => `:${c}`).join("/") : `:${safePkCols[0]}`;
|
|
25
|
-
const softDel = opts.softDeleteColumn && table.columns.some((c) => c.name === opts.softDeleteColumn) ? opts.softDeleteColumn : null;
|
|
26
|
-
// IMPORTANT: interpolate column names at generator time (no escaping/backslashes here)
|
|
27
|
-
const wherePkSql = hasCompositePk
|
|
28
|
-
? safePkCols.map((c, i) => `"${c}" = $${i + 1}`).join(" AND ")
|
|
29
|
-
: `"${safePkCols[0]}" = $1`;
|
|
30
|
-
const getPkParams = hasCompositePk
|
|
31
|
-
? `const pkValues = [${safePkCols.map((c) => `c.req.param("${c}")`).join(", ")}];`
|
|
32
|
-
: `const pkValues = [c.req.param("${safePkCols[0]}")];`;
|
|
33
|
-
// Build SET clause indices for UPDATE (PK params first, then update values)
|
|
34
|
-
// These strings are emitted into generated code, so we DO escape ${...} intentionally here.
|
|
35
|
-
const updateSetSql = hasCompositePk
|
|
36
|
-
? `Object.keys(updateData).map((k, i) => \`"\${k}" = $\${i + ${safePkCols.length} + 1}\`).join(", ")`
|
|
37
|
-
: `Object.keys(updateData).map((k, i) => \`"\${k}" = $\${i + 2}\`).join(", ")`;
|
|
38
|
-
// Prevent updating PK columns
|
|
39
|
-
const pkFilter = safePkCols.length
|
|
40
|
-
? `const updateData = Object.fromEntries(Object.entries(parsed.data).filter(([k]) => !new Set(${JSON.stringify(safePkCols)}).has(k)));`
|
|
41
|
-
: `const updateData = parsed.data;`;
|
|
42
|
-
return `/* Generated. Do not edit. */
|
|
43
|
-
import { Hono } from "hono";
|
|
44
|
-
import { z } from "zod";
|
|
45
|
-
import { Insert${Type}Schema, Update${Type}Schema } from "../zod/${fileTableName}";
|
|
46
|
-
import { loadIncludes } from "../include-loader";
|
|
47
|
-
|
|
48
|
-
const DEBUG = process.env.SDK_DEBUG === "1" || process.env.SDK_DEBUG === "true";
|
|
49
|
-
const log = {
|
|
50
|
-
debug: (...args: any[]) => { if (DEBUG) console.debug("[sdk]", ...args); },
|
|
51
|
-
error: (...args: any[]) => console.error("[sdk]", ...args),
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
const listSchema = z.object({
|
|
55
|
-
include: z.any().optional(), // TODO: typed include spec in later pass
|
|
56
|
-
limit: z.number().int().positive().max(100).optional(),
|
|
57
|
-
offset: z.number().int().min(0).optional(),
|
|
58
|
-
orderBy: z.any().optional() // TODO: typed orderBy in a later pass
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
export function register${Type}Routes(app: Hono, deps: { pg: { query: (text: string, params?: any[]) => Promise<{ rows: any[] }> } }) {
|
|
62
|
-
const base = "/v1/${fileTableName}";
|
|
63
|
-
|
|
64
|
-
// CREATE
|
|
65
|
-
app.post(base, async (c) => {
|
|
66
|
-
try {
|
|
67
|
-
const body = await c.req.json().catch(() => ({}));
|
|
68
|
-
log.debug("POST ${fileTableName} body:", body);
|
|
69
|
-
const parsed = Insert${Type}Schema.safeParse(body);
|
|
70
|
-
if (!parsed.success) {
|
|
71
|
-
const issues = parsed.error.flatten();
|
|
72
|
-
log.debug("POST ${fileTableName} invalid:", issues);
|
|
73
|
-
return c.json({ error: "Invalid body", issues }, 400);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const data = parsed.data;
|
|
77
|
-
const cols = Object.keys(data);
|
|
78
|
-
const vals = Object.values(data);
|
|
79
|
-
if (!cols.length) return c.json({ error: "No fields provided" }, 400);
|
|
80
|
-
|
|
81
|
-
const placeholders = cols.map((_, i) => '$' + (i + 1)).join(", ");
|
|
82
|
-
const text = \`INSERT INTO "${fileTableName}" (\${cols.map(c => '"' + c + '"').join(", ")})
|
|
83
|
-
VALUES (\${placeholders})
|
|
84
|
-
RETURNING *\`;
|
|
85
|
-
log.debug("SQL:", text, "vals:", vals);
|
|
86
|
-
const { rows } = await deps.pg.query(text, vals);
|
|
87
|
-
return c.json(rows[0] ?? null, rows[0] ? 201 : 500);
|
|
88
|
-
} catch (e: any) {
|
|
89
|
-
log.error("POST ${fileTableName} error:", e?.stack ?? e);
|
|
90
|
-
return c.json({ error: e?.message ?? "Internal error", ...(DEBUG ? { stack: e?.stack } : {}) }, 500);
|
|
91
|
-
}
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
// GET BY PK
|
|
95
|
-
app.get(\`\${base}/${pkPath}\`, async (c) => {
|
|
96
|
-
try {
|
|
97
|
-
${getPkParams}
|
|
98
|
-
const text = \`SELECT * FROM "${fileTableName}" WHERE ${wherePkSql} LIMIT 1\`;
|
|
99
|
-
log.debug("GET ${fileTableName} by PK:", pkValues, "SQL:", text);
|
|
100
|
-
const { rows } = await deps.pg.query(text, pkValues);
|
|
101
|
-
if (!rows[0]) return c.json(null, 404);
|
|
102
|
-
return c.json(rows[0]);
|
|
103
|
-
} catch (e: any) {
|
|
104
|
-
log.error("GET ${fileTableName} error:", e?.stack ?? e);
|
|
105
|
-
return c.json({ error: e?.message ?? "Internal error", ...(DEBUG ? { stack: e?.stack } : {}) }, 500);
|
|
106
|
-
}
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
// LIST
|
|
110
|
-
app.post(\`\${base}/list\`, async (c) => {
|
|
111
|
-
try {
|
|
112
|
-
const body = listSchema.safeParse(await c.req.json().catch(() => ({})));
|
|
113
|
-
if (!body.success) {
|
|
114
|
-
const issues = body.error.flatten();
|
|
115
|
-
log.debug("LIST ${fileTableName} invalid:", issues);
|
|
116
|
-
return c.json({ error: "Invalid body", issues }, 400);
|
|
117
|
-
}
|
|
118
|
-
const { include, limit = 50, offset = 0 } = body.data;
|
|
119
|
-
|
|
120
|
-
const where = ${softDel ? `\`WHERE "${softDel}" IS NULL\`` : `""`};
|
|
121
|
-
const text = \`SELECT * FROM "${fileTableName}" \${where} LIMIT $1 OFFSET $2\`;
|
|
122
|
-
log.debug("LIST ${fileTableName} SQL:", text, "params:", [limit, offset]);
|
|
123
|
-
const { rows } = await deps.pg.query(text, [limit, offset]);
|
|
124
|
-
|
|
125
|
-
if (!include) {
|
|
126
|
-
log.debug("LIST ${fileTableName} rows:", rows.length);
|
|
127
|
-
return c.json(rows);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// Attempt include stitching with explicit error handling
|
|
131
|
-
log.debug("LIST ${fileTableName} include spec:", include);
|
|
132
|
-
try {
|
|
133
|
-
const stitched = await loadIncludes("${fileTableName}", rows, include, deps.pg, ${opts.includeDepthLimit});
|
|
134
|
-
log.debug("LIST ${fileTableName} stitched count:", Array.isArray(stitched) ? stitched.length : "n/a");
|
|
135
|
-
return c.json(stitched);
|
|
136
|
-
} catch (e: any) {
|
|
137
|
-
const strict = process.env.SDK_STRICT_INCLUDE === "1" || process.env.SDK_STRICT_INCLUDE === "true";
|
|
138
|
-
const msg = e?.message ?? String(e);
|
|
139
|
-
const stack = e?.stack;
|
|
140
|
-
log.error("LIST ${fileTableName} include stitch FAILED:", msg, stack);
|
|
141
|
-
|
|
142
|
-
if (strict) {
|
|
143
|
-
return c.json({ error: "include-stitch-failed", message: msg, ...(DEBUG ? { stack: e?.stack } : {}) }, 500);
|
|
144
|
-
}
|
|
145
|
-
// Non-strict fallback: return base rows plus error metadata
|
|
146
|
-
return c.json({ data: rows, includeError: { message: msg, ...(DEBUG ? { stack: e?.stack } : {}) } }, 200);
|
|
147
|
-
}
|
|
148
|
-
} catch (e: any) {
|
|
149
|
-
log.error("LIST ${fileTableName} error:", e?.stack ?? e);
|
|
150
|
-
return c.json({ error: e?.message ?? "Internal error", ...(DEBUG ? { stack: e?.stack } : {}) }, 500);
|
|
151
|
-
}
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
// UPDATE
|
|
155
|
-
app.patch(\`\${base}/${pkPath}\`, async (c) => {
|
|
156
|
-
try {
|
|
157
|
-
${getPkParams}
|
|
158
|
-
const body = await c.req.json().catch(() => ({}));
|
|
159
|
-
log.debug("PATCH ${fileTableName} pk:", pkValues, "patch:", body);
|
|
160
|
-
const parsed = Update${Type}Schema.safeParse(body);
|
|
161
|
-
if (!parsed.success) {
|
|
162
|
-
const issues = parsed.error.flatten();
|
|
163
|
-
log.debug("PATCH ${fileTableName} invalid:", issues);
|
|
164
|
-
return c.json({ error: "Invalid body", issues: issues }, 400);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
${pkFilter}
|
|
168
|
-
if (!Object.keys(updateData).length) return c.json({ error: "No updatable fields provided" }, 400);
|
|
169
|
-
|
|
170
|
-
const setSql = ${updateSetSql};
|
|
171
|
-
const text = \`UPDATE "${fileTableName}" SET \${setSql} WHERE ${wherePkSql} RETURNING *\`;
|
|
172
|
-
const params = ${hasCompositePk ? `[...pkValues, ...Object.values(updateData)]` : `[pkValues[0], ...Object.values(updateData)]`};
|
|
173
|
-
log.debug("PATCH ${fileTableName} SQL:", text, "params:", params);
|
|
174
|
-
const { rows } = await deps.pg.query(text, params);
|
|
175
|
-
if (!rows[0]) return c.json(null, 404);
|
|
176
|
-
return c.json(rows[0]);
|
|
177
|
-
} catch (e: any) {
|
|
178
|
-
log.error("PATCH ${fileTableName} error:", e?.stack ?? e);
|
|
179
|
-
return c.json({ error: e?.message ?? "Internal error", ...(DEBUG ? { stack: e?.stack } : {}) }, 500);
|
|
180
|
-
}
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
// DELETE (soft or hard)
|
|
184
|
-
app.delete(\`\${base}/${pkPath}\`, async (c) => {
|
|
185
|
-
try {
|
|
186
|
-
${getPkParams}
|
|
187
|
-
${softDel
|
|
188
|
-
? `
|
|
189
|
-
const text = \`UPDATE "${fileTableName}" SET "${softDel}" = NOW() WHERE ${wherePkSql} RETURNING *\`;
|
|
190
|
-
log.debug("DELETE (soft) ${fileTableName} SQL:", text, "pk:", pkValues);
|
|
191
|
-
const { rows } = await deps.pg.query(text, pkValues);
|
|
192
|
-
if (!rows[0]) return c.json(null, 404);
|
|
193
|
-
return c.json(rows[0]);`
|
|
194
|
-
: `
|
|
195
|
-
const text = \`DELETE FROM "${fileTableName}" WHERE ${wherePkSql} RETURNING *\`;
|
|
196
|
-
log.debug("DELETE ${fileTableName} SQL:", text, "pk:", pkValues);
|
|
197
|
-
const { rows } = await deps.pg.query(text, pkValues);
|
|
198
|
-
if (!rows[0]) return c.json(null, 404);
|
|
199
|
-
return c.json(rows[0]);`}
|
|
200
|
-
} catch (e: any) {
|
|
201
|
-
log.error("DELETE ${fileTableName} error:", e?.stack ?? e);
|
|
202
|
-
return c.json({ error: e?.message ?? "Internal error", ...(DEBUG ? { stack: e?.stack } : {}) }, 500);
|
|
203
|
-
}
|
|
204
|
-
});
|
|
205
|
-
}
|
|
206
|
-
`;
|
|
207
|
-
}
|
|
208
|
-
//# sourceMappingURL=emit-routes.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"emit-routes.js","sourceRoot":"","sources":["../../src/emit-routes.ts"],"names":[],"mappings":"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"}
|
package/dist/src/emit-types.d.ts
DELETED
package/dist/src/emit-types.js
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
function tsTypeFor(pgType, opts) {
|
|
2
|
-
const t = pgType.toLowerCase();
|
|
3
|
-
if (t.startsWith("_"))
|
|
4
|
-
return `${tsTypeFor(t.slice(1), opts)}[]`;
|
|
5
|
-
if (t === "uuid")
|
|
6
|
-
return "string";
|
|
7
|
-
if (t === "bool" || t === "boolean")
|
|
8
|
-
return "boolean";
|
|
9
|
-
if (t === "int2" || t === "int4" || t === "int8" || t === "float4" || t === "float8" || t === "numeric") {
|
|
10
|
-
return opts.numericMode === "number" ? "number" : "string";
|
|
11
|
-
}
|
|
12
|
-
if (t === "date" || t.startsWith("timestamp"))
|
|
13
|
-
return opts.dateType === "date" ? "Date" : "string";
|
|
14
|
-
if (t === "json" || t === "jsonb")
|
|
15
|
-
return "unknown";
|
|
16
|
-
return "string";
|
|
17
|
-
}
|
|
18
|
-
const pascal = (s) => s
|
|
19
|
-
.split(/[_\s-]+/)
|
|
20
|
-
.map((w) => (w?.[0] ? w[0].toUpperCase() + w.slice(1) : ""))
|
|
21
|
-
.join("");
|
|
22
|
-
export function emitTypes(table, opts) {
|
|
23
|
-
const Type = pascal(table.name);
|
|
24
|
-
const insertFields = table.columns
|
|
25
|
-
.map((col) => {
|
|
26
|
-
const base = tsTypeFor(col.pgType, opts);
|
|
27
|
-
const optional = col.hasDefault || col.nullable ? "?" : "";
|
|
28
|
-
const valueType = col.nullable ? `${base} | null` : base;
|
|
29
|
-
return ` ${col.name}${optional}: ${valueType};`;
|
|
30
|
-
})
|
|
31
|
-
.join("\n");
|
|
32
|
-
const selectFields = table.columns
|
|
33
|
-
.map((col) => {
|
|
34
|
-
const base = tsTypeFor(col.pgType, opts);
|
|
35
|
-
const valueType = col.nullable ? `${base} | null` : base;
|
|
36
|
-
return ` ${col.name}: ${valueType};`;
|
|
37
|
-
})
|
|
38
|
-
.join("\n");
|
|
39
|
-
return `/* Generated. Do not edit. */
|
|
40
|
-
export type Insert${Type} = {
|
|
41
|
-
${insertFields}
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
export type Update${Type} = Partial<Insert${Type}>;
|
|
45
|
-
|
|
46
|
-
export type Select${Type} = {
|
|
47
|
-
${selectFields}
|
|
48
|
-
};
|
|
49
|
-
`;
|
|
50
|
-
}
|
|
51
|
-
//# sourceMappingURL=emit-types.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"emit-types.js","sourceRoot":"","sources":["../../src/emit-types.ts"],"names":[],"mappings":"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"}
|
package/dist/src/emit-zod.d.ts
DELETED
package/dist/src/emit-zod.js
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { pascal } from "./utils";
|
|
2
|
-
export function emitZod(table, opts) {
|
|
3
|
-
const Type = pascal(table.name);
|
|
4
|
-
const zFor = (pg) => {
|
|
5
|
-
if (pg === "uuid")
|
|
6
|
-
return `z.string().uuid()`;
|
|
7
|
-
if (pg === "bool" || pg === "boolean")
|
|
8
|
-
return `z.boolean()`;
|
|
9
|
-
if (pg === "int2" || pg === "int4" || pg === "int8")
|
|
10
|
-
return opts.numericMode === "number" ? `z.number()` : `z.string()`;
|
|
11
|
-
if (pg === "numeric" || pg === "float4" || pg === "float8")
|
|
12
|
-
return opts.numericMode === "number" ? `z.number()` : `z.string()`;
|
|
13
|
-
if (pg === "jsonb" || pg === "json")
|
|
14
|
-
return `z.unknown()`;
|
|
15
|
-
if (pg === "date" || pg.startsWith("timestamp"))
|
|
16
|
-
return opts.dateType === "date" ? `z.date()` : `z.string()`;
|
|
17
|
-
if (pg.startsWith("_"))
|
|
18
|
-
return `z.array(${zFor(pg.slice(1))})`;
|
|
19
|
-
return `z.string()`; // text/varchar/unknown
|
|
20
|
-
};
|
|
21
|
-
const fields = table.columns
|
|
22
|
-
.map((c) => {
|
|
23
|
-
let z = zFor(c.pgType);
|
|
24
|
-
if (c.nullable)
|
|
25
|
-
z += `.nullable()`;
|
|
26
|
-
if (c.hasDefault)
|
|
27
|
-
z += `.optional()`;
|
|
28
|
-
return ` ${c.name}: ${z}`;
|
|
29
|
-
})
|
|
30
|
-
.join(",\n");
|
|
31
|
-
return `import { z } from "zod";
|
|
32
|
-
|
|
33
|
-
export const Insert${Type}Schema = z.object({
|
|
34
|
-
${fields}
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
export const Update${Type}Schema = Insert${Type}Schema.partial();
|
|
38
|
-
|
|
39
|
-
export type Insert${Type} = z.infer<typeof Insert${Type}Schema>;
|
|
40
|
-
export type Update${Type} = z.infer<typeof Update${Type}Schema>;
|
|
41
|
-
`;
|
|
42
|
-
}
|
|
43
|
-
//# sourceMappingURL=emit-zod.js.map
|
package/dist/src/emit-zod.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"emit-zod.js","sourceRoot":"","sources":["../../src/emit-zod.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEjC,MAAM,UAAU,OAAO,CAAC,KAAY,EAAE,IAAuE;IAC3G,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAEhC,MAAM,IAAI,GAAG,CAAC,EAAU,EAAU,EAAE;QAClC,IAAI,EAAE,KAAK,MAAM;YAAE,OAAO,mBAAmB,CAAC;QAC9C,IAAI,EAAE,KAAK,MAAM,IAAI,EAAE,KAAK,SAAS;YAAE,OAAO,aAAa,CAAC;QAC5D,IAAI,EAAE,KAAK,MAAM,IAAI,EAAE,KAAK,MAAM,IAAI,EAAE,KAAK,MAAM;YACjD,OAAO,IAAI,CAAC,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC;QACrE,IAAI,EAAE,KAAK,SAAS,IAAI,EAAE,KAAK,QAAQ,IAAI,EAAE,KAAK,QAAQ;YACxD,OAAO,IAAI,CAAC,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC;QACrE,IAAI,EAAE,KAAK,OAAO,IAAI,EAAE,KAAK,MAAM;YAAE,OAAO,aAAa,CAAC;QAC1D,IAAI,EAAE,KAAK,MAAM,IAAI,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC;YAAE,OAAO,IAAI,CAAC,QAAQ,KAAK,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,YAAY,CAAC;QAC7G,IAAI,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,OAAO,WAAW,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;QAC/D,OAAO,YAAY,CAAC,CAAC,uBAAuB;IAC9C,CAAC,CAAC;IAEF,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO;SACzB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACT,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QACvB,IAAI,CAAC,CAAC,QAAQ;YAAE,CAAC,IAAI,aAAa,CAAC;QACnC,IAAI,CAAC,CAAC,UAAU;YAAE,CAAC,IAAI,aAAa,CAAC;QACrC,OAAO,KAAK,CAAC,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;IAC7B,CAAC,CAAC;SACD,IAAI,CAAC,KAAK,CAAC,CAAC;IAEf,OAAO;;qBAEY,IAAI;EACvB,MAAM;;;qBAGa,IAAI,kBAAkB,IAAI;;oBAE3B,IAAI,2BAA2B,IAAI;oBACnC,IAAI,2BAA2B,IAAI;CACtD,CAAC;AACF,CAAC"}
|
package/dist/src/index.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/src/index.js
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import cfg from "../gen.config";
|
|
2
|
-
import { join } from "path";
|
|
3
|
-
import { introspect } from "./introspect";
|
|
4
|
-
import { buildGraph } from "./rel-classify";
|
|
5
|
-
import { emitIncludeSpec } from "./emit-include-spec";
|
|
6
|
-
import { emitIncludeBuilder } from "./emit-include-builder";
|
|
7
|
-
import { emitZod } from "./emit-zod";
|
|
8
|
-
import { emitRoutes } from "./emit-routes";
|
|
9
|
-
import { emitClient, emitClientIndex } from "./emit-client";
|
|
10
|
-
import { emitIncludeLoader } from "./emit-include-loader";
|
|
11
|
-
import { emitTypes } from "./emit-types";
|
|
12
|
-
import { emitLogger } from "./emit-logger";
|
|
13
|
-
import { ensureDirs, writeFiles } from "./utils";
|
|
14
|
-
(async () => {
|
|
15
|
-
const model = await introspect(cfg.connectionString, cfg.schema);
|
|
16
|
-
const graph = buildGraph(model);
|
|
17
|
-
const serverDir = cfg.outServer;
|
|
18
|
-
const clientDir = cfg.outClient;
|
|
19
|
-
const normDateType = cfg.dateType === "string" ? "string" : "date";
|
|
20
|
-
await ensureDirs([
|
|
21
|
-
serverDir,
|
|
22
|
-
join(serverDir, "types"),
|
|
23
|
-
join(serverDir, "zod"),
|
|
24
|
-
join(serverDir, "routes"),
|
|
25
|
-
clientDir,
|
|
26
|
-
join(clientDir, "types"),
|
|
27
|
-
]);
|
|
28
|
-
const files = [];
|
|
29
|
-
// include-spec (shared)
|
|
30
|
-
const includeSpec = emitIncludeSpec(graph);
|
|
31
|
-
files.push({ path: join(serverDir, "include-spec.ts"), content: includeSpec });
|
|
32
|
-
files.push({ path: join(clientDir, "include-spec.ts"), content: includeSpec });
|
|
33
|
-
// include-builder (server)
|
|
34
|
-
files.push({
|
|
35
|
-
path: join(serverDir, "include-builder.ts"),
|
|
36
|
-
content: emitIncludeBuilder(graph, cfg.includeDepthLimit),
|
|
37
|
-
});
|
|
38
|
-
// include-loader (server)
|
|
39
|
-
files.push({
|
|
40
|
-
path: join(serverDir, "include-loader.ts"),
|
|
41
|
-
content: emitIncludeLoader(graph, model, cfg.includeDepthLimit),
|
|
42
|
-
});
|
|
43
|
-
// logger (server)
|
|
44
|
-
files.push({ path: join(serverDir, "logger.ts"), content: emitLogger() });
|
|
45
|
-
// per-table outputs
|
|
46
|
-
for (const table of Object.values(model.tables)) {
|
|
47
|
-
// types (server + client)
|
|
48
|
-
const typesSrc = emitTypes(table, { dateType: normDateType, numericMode: "string" });
|
|
49
|
-
files.push({ path: join(serverDir, "types", `${table.name}.ts`), content: typesSrc });
|
|
50
|
-
files.push({ path: join(clientDir, "types", `${table.name}.ts`), content: typesSrc });
|
|
51
|
-
// zod
|
|
52
|
-
files.push({
|
|
53
|
-
path: join(serverDir, "zod", `${table.name}.ts`),
|
|
54
|
-
content: emitZod(table, { dateType: normDateType, numericMode: "string" }),
|
|
55
|
-
});
|
|
56
|
-
// routes
|
|
57
|
-
files.push({
|
|
58
|
-
path: join(serverDir, "routes", `${table.name}.ts`),
|
|
59
|
-
content: emitRoutes(table, graph, {
|
|
60
|
-
softDeleteColumn: cfg.softDeleteColumn ?? null,
|
|
61
|
-
includeDepthLimit: cfg.includeDepthLimit ?? 3,
|
|
62
|
-
}),
|
|
63
|
-
});
|
|
64
|
-
// client
|
|
65
|
-
files.push({
|
|
66
|
-
path: join(clientDir, `${table.name}.ts`),
|
|
67
|
-
content: emitClient(table),
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
// client index (SDK)
|
|
71
|
-
files.push({
|
|
72
|
-
path: join(clientDir, "index.ts"),
|
|
73
|
-
content: emitClientIndex(Object.values(model.tables)),
|
|
74
|
-
});
|
|
75
|
-
await writeFiles(files);
|
|
76
|
-
console.log(`✓ Generated ${files.length} files`);
|
|
77
|
-
console.log(` server: ${serverDir}`);
|
|
78
|
-
console.log(` client: ${clientDir}`);
|
|
79
|
-
})().catch((e) => {
|
|
80
|
-
console.error("❌ Generation failed", e);
|
|
81
|
-
process.exit(1);
|
|
82
|
-
});
|
|
83
|
-
//# sourceMappingURL=index.js.map
|
package/dist/src/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/src/introspect.d.ts
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
export type Column = {
|
|
2
|
-
name: string;
|
|
3
|
-
pgType: string;
|
|
4
|
-
nullable: boolean;
|
|
5
|
-
hasDefault: boolean;
|
|
6
|
-
};
|
|
7
|
-
export type ForeignKey = {
|
|
8
|
-
from: string[];
|
|
9
|
-
toTable: string;
|
|
10
|
-
to: string[];
|
|
11
|
-
onDelete: "cascade" | "restrict" | "set null" | "no action";
|
|
12
|
-
onUpdate: "cascade" | "restrict" | "set null" | "no action";
|
|
13
|
-
};
|
|
14
|
-
export type Table = {
|
|
15
|
-
name: string;
|
|
16
|
-
columns: Column[];
|
|
17
|
-
pk: string[];
|
|
18
|
-
uniques: string[][];
|
|
19
|
-
fks: ForeignKey[];
|
|
20
|
-
};
|
|
21
|
-
export type Model = {
|
|
22
|
-
schema: string;
|
|
23
|
-
tables: Record<string, Table>;
|
|
24
|
-
enums: Record<string, string[]>;
|
|
25
|
-
};
|
|
26
|
-
export declare function introspect(connectionString: string, schema: string): Promise<Model>;
|
package/dist/src/introspect.js
DELETED
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
import { Client } from "pg";
|
|
2
|
-
function ensureTable(tables, name) {
|
|
3
|
-
if (!tables[name])
|
|
4
|
-
tables[name] = { name, columns: [], pk: [], uniques: [], fks: [] };
|
|
5
|
-
return tables[name];
|
|
6
|
-
}
|
|
7
|
-
function decodeAction(ch) {
|
|
8
|
-
switch (ch) {
|
|
9
|
-
case "c":
|
|
10
|
-
return "cascade";
|
|
11
|
-
case "r":
|
|
12
|
-
return "restrict";
|
|
13
|
-
case "n":
|
|
14
|
-
return "set null";
|
|
15
|
-
default:
|
|
16
|
-
return "no action";
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
export async function introspect(connectionString, schema) {
|
|
20
|
-
const pg = new Client({ connectionString });
|
|
21
|
-
await pg.connect();
|
|
22
|
-
const tables = {};
|
|
23
|
-
const enums = {};
|
|
24
|
-
try {
|
|
25
|
-
const tablesRows = await pg.query(`
|
|
26
|
-
SELECT c.oid, c.relname AS table
|
|
27
|
-
FROM pg_class c
|
|
28
|
-
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
29
|
-
WHERE c.relkind = 'r' AND n.nspname = $1
|
|
30
|
-
ORDER BY c.relname
|
|
31
|
-
`, [schema]);
|
|
32
|
-
for (const r of tablesRows.rows)
|
|
33
|
-
ensureTable(tables, r.table);
|
|
34
|
-
const colsRows = await pg.query(`
|
|
35
|
-
SELECT table_name, column_name, is_nullable, udt_name, data_type, column_default
|
|
36
|
-
FROM information_schema.columns
|
|
37
|
-
WHERE table_schema = $1
|
|
38
|
-
ORDER BY table_name, ordinal_position
|
|
39
|
-
`, [schema]);
|
|
40
|
-
for (const r of colsRows.rows) {
|
|
41
|
-
const t = ensureTable(tables, r.table_name);
|
|
42
|
-
t.columns.push({
|
|
43
|
-
name: r.column_name,
|
|
44
|
-
pgType: (r.udt_name ?? r.data_type).toLowerCase(),
|
|
45
|
-
nullable: r.is_nullable === "YES",
|
|
46
|
-
hasDefault: r.column_default != null,
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
const pkRows = await pg.query(`
|
|
50
|
-
SELECT
|
|
51
|
-
tc.table_name,
|
|
52
|
-
COALESCE(json_agg(kcu.column_name ORDER BY kcu.ordinal_position), '[]'::json) AS cols
|
|
53
|
-
FROM information_schema.table_constraints tc
|
|
54
|
-
JOIN information_schema.key_column_usage kcu
|
|
55
|
-
ON tc.constraint_name = kcu.constraint_name
|
|
56
|
-
AND tc.table_schema = kcu.table_schema
|
|
57
|
-
WHERE tc.constraint_type = 'PRIMARY KEY'
|
|
58
|
-
AND tc.table_schema = $1
|
|
59
|
-
GROUP BY tc.table_name
|
|
60
|
-
`, [schema]);
|
|
61
|
-
for (const r of pkRows.rows) {
|
|
62
|
-
const t = ensureTable(tables, r.table_name);
|
|
63
|
-
t.pk = (r.cols ?? []).slice();
|
|
64
|
-
}
|
|
65
|
-
const uniqRows = await pg.query(`
|
|
66
|
-
SELECT
|
|
67
|
-
tc.table_name,
|
|
68
|
-
COALESCE(json_agg(kcu.column_name ORDER BY kcu.ordinal_position), '[]'::json) AS cols
|
|
69
|
-
FROM information_schema.table_constraints tc
|
|
70
|
-
JOIN information_schema.key_column_usage kcu
|
|
71
|
-
ON tc.constraint_name = kcu.constraint_name
|
|
72
|
-
AND tc.table_schema = kcu.table_schema
|
|
73
|
-
WHERE tc.constraint_type = 'UNIQUE'
|
|
74
|
-
AND tc.table_schema = $1
|
|
75
|
-
GROUP BY tc.table_name, tc.constraint_name
|
|
76
|
-
`, [schema]);
|
|
77
|
-
for (const r of uniqRows.rows) {
|
|
78
|
-
const t = ensureTable(tables, r.table_name);
|
|
79
|
-
if (r.cols && r.cols.length)
|
|
80
|
-
t.uniques.push(r.cols);
|
|
81
|
-
}
|
|
82
|
-
const fkRows = await pg.query(`
|
|
83
|
-
SELECT
|
|
84
|
-
con.oid AS con_oid,
|
|
85
|
-
src.relname AS src_table,
|
|
86
|
-
tgt.relname AS tgt_table,
|
|
87
|
-
con.confdeltype,
|
|
88
|
-
con.confupdtype,
|
|
89
|
-
COALESCE(json_agg(src_att.attname ORDER BY ord.n), '[]'::json) AS src_cols,
|
|
90
|
-
COALESCE(json_agg(tgt_att.attname ORDER BY ord.n), '[]'::json) AS tgt_cols
|
|
91
|
-
FROM pg_constraint con
|
|
92
|
-
JOIN pg_class src ON src.oid = con.conrelid
|
|
93
|
-
JOIN pg_class tgt ON tgt.oid = con.confrelid
|
|
94
|
-
JOIN LATERAL generate_subscripts(con.conkey, 1) ord(n) ON true
|
|
95
|
-
JOIN pg_attribute src_att ON src_att.attrelid = src.oid AND src_att.attnum = con.conkey[ord.n]
|
|
96
|
-
JOIN pg_attribute tgt_att ON tgt_att.attrelid = tgt.oid AND tgt_att.attnum = con.confkey[ord.n]
|
|
97
|
-
JOIN pg_namespace ns ON ns.oid = src.relnamespace
|
|
98
|
-
WHERE con.contype = 'f'
|
|
99
|
-
AND ns.nspname = $1
|
|
100
|
-
GROUP BY con.oid, src.relname, tgt.relname, con.confdeltype, con.confupdtype
|
|
101
|
-
ORDER BY src.relname, con.oid
|
|
102
|
-
`, [schema]);
|
|
103
|
-
for (const r of fkRows.rows) {
|
|
104
|
-
const t = ensureTable(tables, r.src_table);
|
|
105
|
-
t.fks.push({
|
|
106
|
-
from: (r.src_cols ?? []).slice(),
|
|
107
|
-
toTable: r.tgt_table,
|
|
108
|
-
to: (r.tgt_cols ?? []).slice(),
|
|
109
|
-
onDelete: decodeAction(r.confdeltype),
|
|
110
|
-
onUpdate: decodeAction(r.confupdtype),
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
const enumRows = await pg.query(`
|
|
114
|
-
SELECT t.typname AS enum_name, e.enumlabel
|
|
115
|
-
FROM pg_type t
|
|
116
|
-
JOIN pg_enum e ON e.enumtypid = t.oid
|
|
117
|
-
JOIN pg_namespace n ON n.oid = t.typnamespace
|
|
118
|
-
WHERE n.nspname = $1
|
|
119
|
-
ORDER BY t.typname, e.enumsortorder
|
|
120
|
-
`, [schema]);
|
|
121
|
-
for (const r of enumRows.rows) {
|
|
122
|
-
if (!enums[r.enum_name])
|
|
123
|
-
enums[r.enum_name] = [];
|
|
124
|
-
enums[r.enum_name].push(r.enumlabel);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
finally {
|
|
128
|
-
await pg.end();
|
|
129
|
-
}
|
|
130
|
-
return { schema, tables, enums };
|
|
131
|
-
}
|
|
132
|
-
//# sourceMappingURL=introspect.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"introspect.js","sourceRoot":"","sources":["../../src/introspect.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type { Model } from "./introspect";
|
|
2
|
-
export type Edge = {
|
|
3
|
-
from: string;
|
|
4
|
-
key: string;
|
|
5
|
-
kind: "one" | "many";
|
|
6
|
-
target: string;
|
|
7
|
-
via?: string;
|
|
8
|
-
};
|
|
9
|
-
export type Graph = Record<string, Record<string, Edge>>;
|
|
10
|
-
export declare function buildGraph(model: Model): Graph;
|
package/dist/src/rel-classify.js
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
const singular = (s) => (s.endsWith("s") ? s.slice(0, -1) : s);
|
|
2
|
-
const plural = (s) => (s.endsWith("s") ? s : s + "s");
|
|
3
|
-
export function buildGraph(model) {
|
|
4
|
-
const graph = {};
|
|
5
|
-
const tables = Object.values(model.tables);
|
|
6
|
-
// init nodes
|
|
7
|
-
for (const t of tables)
|
|
8
|
-
graph[t.name] = graph[t.name] ?? {};
|
|
9
|
-
// 1) 1:N & 1:1 from FKs
|
|
10
|
-
for (const child of tables) {
|
|
11
|
-
for (const fk of child.fks) {
|
|
12
|
-
const parent = tables.find((t) => t.name === fk.toTable);
|
|
13
|
-
if (!parent)
|
|
14
|
-
continue;
|
|
15
|
-
// cache nodes so TS knows they're not undefined
|
|
16
|
-
const childNode = (graph[child.name] ??= {});
|
|
17
|
-
const parentNode = (graph[parent.name] ??= {});
|
|
18
|
-
const upKey = singular(parent.name);
|
|
19
|
-
const downKey = plural(child.name);
|
|
20
|
-
if (!(upKey in childNode)) {
|
|
21
|
-
childNode[upKey] = { from: child.name, key: upKey, kind: "one", target: parent.name };
|
|
22
|
-
}
|
|
23
|
-
if (!(downKey in parentNode)) {
|
|
24
|
-
parentNode[downKey] = { from: parent.name, key: downKey, kind: "many", target: child.name };
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
// 2) M:N via junction (two FKs)
|
|
29
|
-
for (const j of tables) {
|
|
30
|
-
if ((j.fks?.length ?? 0) !== 2)
|
|
31
|
-
continue;
|
|
32
|
-
const [fkA, fkB] = j.fks;
|
|
33
|
-
if (!fkA || !fkB)
|
|
34
|
-
continue;
|
|
35
|
-
const A = fkA.toTable;
|
|
36
|
-
const B = fkB.toTable;
|
|
37
|
-
if (!A || !B || A === B)
|
|
38
|
-
continue;
|
|
39
|
-
const aNode = (graph[A] ??= {});
|
|
40
|
-
const bNode = (graph[B] ??= {});
|
|
41
|
-
const aKey = plural(B);
|
|
42
|
-
const bKey = plural(A);
|
|
43
|
-
if (!(aKey in aNode)) {
|
|
44
|
-
aNode[aKey] = { from: A, key: aKey, kind: "many", target: B, via: j.name };
|
|
45
|
-
}
|
|
46
|
-
if (!(bKey in bNode)) {
|
|
47
|
-
bNode[bKey] = { from: B, key: bKey, kind: "many", target: A, via: j.name };
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
return graph;
|
|
51
|
-
}
|
|
52
|
-
//# sourceMappingURL=rel-classify.js.map
|