@drzl/cli 4.21.0 → 4.23.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/dist/{chunk-XNNKHBGV.js → chunk-54E2IO7N.js} +491 -65
- package/dist/chunk-54E2IO7N.js.map +1 -0
- package/dist/{dist-K6N4F3XW.js → chunk-KKPDOZOD.js} +221 -39
- package/dist/chunk-KKPDOZOD.js.map +1 -0
- package/dist/cli.cjs +6606 -1471
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +2854 -698
- package/dist/cli.js.map +1 -1
- package/dist/config.cjs +497 -64
- package/dist/config.cjs.map +1 -1
- package/dist/config.d.cts +262 -6
- package/dist/config.d.ts +262 -6
- package/dist/config.js +25 -1
- package/dist/dist-KQMPKOFK.js +350 -0
- package/dist/dist-KQMPKOFK.js.map +1 -0
- package/dist/{dist-WHAW3AMQ.js → dist-KX62ETKK.js} +71 -22
- package/dist/dist-KX62ETKK.js.map +1 -0
- package/dist/dist-P24ILO5N.js +431 -0
- package/dist/dist-P24ILO5N.js.map +1 -0
- package/dist/dist-QYH7DRFY.js +27 -0
- package/dist/dist-QYH7DRFY.js.map +1 -0
- package/dist/dist-SGI2I53L.js +434 -0
- package/dist/dist-SGI2I53L.js.map +1 -0
- package/dist/dist-T5376MW7.js +489 -0
- package/dist/dist-T5376MW7.js.map +1 -0
- package/dist/dist-UVP6B4XJ.js +646 -0
- package/dist/dist-UVP6B4XJ.js.map +1 -0
- package/dist/{dist-XBGVORL3.js → dist-ZIHNXQ7U.js} +16 -6
- package/dist/dist-ZIHNXQ7U.js.map +1 -0
- package/dist/drzl.config.schema.json +717 -0
- package/package.json +19 -13
- package/dist/chunk-XNNKHBGV.js.map +0 -1
- package/dist/dist-K6N4F3XW.js.map +0 -1
- package/dist/dist-WHAW3AMQ.js.map +0 -1
- package/dist/dist-XBGVORL3.js.map +0 -1
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
import {
|
|
2
|
+
tableSchemas
|
|
3
|
+
} from "./chunk-KKPDOZOD.js";
|
|
4
|
+
|
|
5
|
+
// ../generator-fastify/dist/index.js
|
|
6
|
+
import { fileWriter } from "@drzl/validation-core";
|
|
7
|
+
import { formatCode, importSpecifier, selectColumns } from "@drzl/validation-core";
|
|
8
|
+
var APP_MODULE = "index";
|
|
9
|
+
var lit = (v) => /['\\]/.test(v) ? JSON.stringify(v) : `'${v}'`;
|
|
10
|
+
var NUMERIC_SEGMENT_PATTERN = "^-?\\d+(\\.\\d+)?$";
|
|
11
|
+
var BIGINT_SEGMENT_PATTERN = "^-?\\d+$";
|
|
12
|
+
var NOT_FOUND_SCHEMA = "{ type: 'object', properties: { message: { type: 'string' } }, required: ['message'], additionalProperties: false }";
|
|
13
|
+
var cap = (s) => s.charAt(0).toUpperCase() + s.slice(1);
|
|
14
|
+
var isIdent = (s) => /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(s);
|
|
15
|
+
function keyColumns(table) {
|
|
16
|
+
const names = table.primaryKey?.columns ?? [];
|
|
17
|
+
if (!names.length) return null;
|
|
18
|
+
const cols = names.map((n) => table.columns.find((c) => c.name === n));
|
|
19
|
+
if (cols.some((c) => !c)) return null;
|
|
20
|
+
return cols;
|
|
21
|
+
}
|
|
22
|
+
function segmentSchema(column) {
|
|
23
|
+
if (column.enumValues && column.enumValues.length) return { enum: [...column.enumValues] };
|
|
24
|
+
switch (column.tsType) {
|
|
25
|
+
case "number":
|
|
26
|
+
return { type: "string", pattern: NUMERIC_SEGMENT_PATTERN };
|
|
27
|
+
case "bigint":
|
|
28
|
+
return { type: "string", pattern: BIGINT_SEGMENT_PATTERN };
|
|
29
|
+
case "Date":
|
|
30
|
+
return { type: "string", format: "date-time" };
|
|
31
|
+
default:
|
|
32
|
+
return { type: "string" };
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function paramsSchema(cols) {
|
|
36
|
+
return {
|
|
37
|
+
type: "object",
|
|
38
|
+
properties: Object.fromEntries(cols.map((c) => [c.name, segmentSchema(c)])),
|
|
39
|
+
required: cols.map((c) => c.name),
|
|
40
|
+
additionalProperties: false
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function adaptForFastify(schema) {
|
|
44
|
+
const { $schema: _dialect, $id: _id, ...rest } = walk(schema);
|
|
45
|
+
return rest;
|
|
46
|
+
}
|
|
47
|
+
function walk(value) {
|
|
48
|
+
if (Array.isArray(value)) return value.map(walk);
|
|
49
|
+
if (value && typeof value === "object") {
|
|
50
|
+
const out = {};
|
|
51
|
+
for (const [k, v] of Object.entries(value)) {
|
|
52
|
+
if (k === "prefixItems") continue;
|
|
53
|
+
out[k] = walk(v);
|
|
54
|
+
}
|
|
55
|
+
const prefix = value.prefixItems;
|
|
56
|
+
if (Array.isArray(prefix) && prefix.length) out.items = walk(prefix[0]);
|
|
57
|
+
return out;
|
|
58
|
+
}
|
|
59
|
+
return value;
|
|
60
|
+
}
|
|
61
|
+
function rowFieldType(column) {
|
|
62
|
+
if (column.enumValues && column.enumValues.length) {
|
|
63
|
+
return column.enumValues.map((v) => `'${v.replace(/'/g, "\\'")}'`).join(" | ");
|
|
64
|
+
}
|
|
65
|
+
const s = column.shape;
|
|
66
|
+
if (s) {
|
|
67
|
+
switch (s.kind) {
|
|
68
|
+
case "tuple":
|
|
69
|
+
return `[${Array.from({ length: s.length }, () => "number").join(", ")}]`;
|
|
70
|
+
case "numberObject":
|
|
71
|
+
return `{ ${s.fields.map((f) => `${f}: number`).join("; ")} }`;
|
|
72
|
+
case "numberVector":
|
|
73
|
+
return "number[]";
|
|
74
|
+
default:
|
|
75
|
+
return "unknown";
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
switch (column.tsType) {
|
|
79
|
+
case "number":
|
|
80
|
+
return "number";
|
|
81
|
+
case "string":
|
|
82
|
+
return "string";
|
|
83
|
+
case "boolean":
|
|
84
|
+
return "boolean";
|
|
85
|
+
case "Date":
|
|
86
|
+
return "Date";
|
|
87
|
+
case "bigint":
|
|
88
|
+
return "bigint";
|
|
89
|
+
default:
|
|
90
|
+
return "unknown";
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function objectKey(name) {
|
|
94
|
+
return isIdent(name) ? name : JSON.stringify(name);
|
|
95
|
+
}
|
|
96
|
+
function toCase(s, c) {
|
|
97
|
+
if (!c) return s;
|
|
98
|
+
const parts = s.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_-]/g, " ").split(/\s+/);
|
|
99
|
+
if (c === "camel") {
|
|
100
|
+
return parts.map(
|
|
101
|
+
(p, i) => i === 0 ? p.toLowerCase() : p.charAt(0).toUpperCase() + p.slice(1).toLowerCase()
|
|
102
|
+
).join("");
|
|
103
|
+
}
|
|
104
|
+
if (c === "kebab") return parts.map((p) => p.toLowerCase()).join("-");
|
|
105
|
+
if (c === "snake") return parts.map((p) => p.toLowerCase()).join("_");
|
|
106
|
+
return s;
|
|
107
|
+
}
|
|
108
|
+
function routesExportName(table, naming) {
|
|
109
|
+
const base = `${table.tsName}${naming?.routerSuffix ?? "Routes"}`;
|
|
110
|
+
const c = naming?.procedureCase;
|
|
111
|
+
return toCase(base, c === "kebab" ? "camel" : c);
|
|
112
|
+
}
|
|
113
|
+
function mountPath(table, naming) {
|
|
114
|
+
return `/${toCase(table.tsName, naming?.procedureCase)}`;
|
|
115
|
+
}
|
|
116
|
+
var FastifyGenerator = class {
|
|
117
|
+
constructor(analysis) {
|
|
118
|
+
this.analysis = analysis;
|
|
119
|
+
}
|
|
120
|
+
async generate(opts) {
|
|
121
|
+
const fs = fileWriter(opts.fileSink);
|
|
122
|
+
const path = await import("path");
|
|
123
|
+
const out = path.resolve(process.cwd(), opts.outputDir);
|
|
124
|
+
const ctx = { out };
|
|
125
|
+
await fs.mkdir(out, { recursive: true });
|
|
126
|
+
const files = [];
|
|
127
|
+
const write = async (filePath, content) => {
|
|
128
|
+
const formatted = await formatCode(
|
|
129
|
+
buildHeader(opts.outputHeader) + content,
|
|
130
|
+
filePath,
|
|
131
|
+
opts.format
|
|
132
|
+
);
|
|
133
|
+
await fs.writeFile(filePath, formatted, "utf8");
|
|
134
|
+
files.push(filePath);
|
|
135
|
+
};
|
|
136
|
+
const barrelPath = path.join(out, `${APP_MODULE}.ts`);
|
|
137
|
+
const modules = [];
|
|
138
|
+
const total = this.analysis.tables.length;
|
|
139
|
+
let index = 0;
|
|
140
|
+
for (const table of this.analysis.tables) {
|
|
141
|
+
const base = `${table.tsName}${opts.naming?.routerSuffix ?? ""}`;
|
|
142
|
+
const filePath = path.join(out, `${toCase(base, opts.naming?.procedureCase)}.ts`);
|
|
143
|
+
if (filePath === barrelPath) {
|
|
144
|
+
throw new Error(
|
|
145
|
+
`@drzl/generator-fastify: the routes for table "${table.name}" would be written to ${filePath}, which is the barrel this generator also writes. Set naming.routerSuffix to move it out of the way.`
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
await write(filePath, renderRoutes(table, opts));
|
|
149
|
+
modules.push({ table, filePath, exportName: routesExportName(table, opts.naming) });
|
|
150
|
+
index++;
|
|
151
|
+
opts.onProgress?.({ index, total, table: table.name, filePath });
|
|
152
|
+
}
|
|
153
|
+
await write(barrelPath, renderBarrel(modules, ctx, path, opts));
|
|
154
|
+
return { files };
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
var index_default = FastifyGenerator;
|
|
158
|
+
function buildHeader(h) {
|
|
159
|
+
if (h && h.enabled === false) return "";
|
|
160
|
+
const text = h?.text?.trim();
|
|
161
|
+
const lines = text ? text.split(/\r?\n/).map((l) => `// ${l}`) : [
|
|
162
|
+
"// Generated by DRZL (@drzl/*)",
|
|
163
|
+
"// Generated output is granted to you under your project's license.",
|
|
164
|
+
"// You may use, copy, modify, and distribute without attribution."
|
|
165
|
+
];
|
|
166
|
+
return lines.join("\n") + "\n\n";
|
|
167
|
+
}
|
|
168
|
+
function renderRoutes(table, opts) {
|
|
169
|
+
const insertName = `Insert${table.tsName}Schema`;
|
|
170
|
+
const updateName = `Update${table.tsName}Schema`;
|
|
171
|
+
const selectName = `Select${table.tsName}Schema`;
|
|
172
|
+
const paramsName = `${cap(table.tsName)}ParamsSchema`;
|
|
173
|
+
const rowType = `Select${table.tsName}Row`;
|
|
174
|
+
const writable = !table.readOnly;
|
|
175
|
+
const key = keyColumns(table);
|
|
176
|
+
const routes = [];
|
|
177
|
+
const notImplemented = (what) => `throw new Error('Not implemented: ${what} ${table.tsName}.');`;
|
|
178
|
+
const paramHint = `// Fastify has already validated req.params against ${paramsName}; numeric key segments stay strings here.`;
|
|
179
|
+
routes.push({
|
|
180
|
+
name: "list",
|
|
181
|
+
method: "get",
|
|
182
|
+
path: "/",
|
|
183
|
+
schema: [`response: { 200: { type: 'array', items: ${selectName} } }`],
|
|
184
|
+
replyType: `${rowType}[]`,
|
|
185
|
+
// The stub states its contract twice over: the annotated local is what a reader sees, and the
|
|
186
|
+
// Reply generic is what Fastify's own types hold the handler to. The response schema is what
|
|
187
|
+
// the serializer runs; nothing infers a client from any of them, which the docs say plainly.
|
|
188
|
+
body: [`const rows: ${rowType}[] = [];`, "return rows;"]
|
|
189
|
+
});
|
|
190
|
+
if (key) {
|
|
191
|
+
const keyPath = "/" + key.map((c) => `:${c.name}`).join("/");
|
|
192
|
+
routes.push({
|
|
193
|
+
name: "byId",
|
|
194
|
+
method: "get",
|
|
195
|
+
path: keyPath,
|
|
196
|
+
schema: [
|
|
197
|
+
`params: ${paramsName}`,
|
|
198
|
+
`response: { 200: ${selectName}, 404: ${NOT_FOUND_SCHEMA} }`
|
|
199
|
+
],
|
|
200
|
+
replyType: `${rowType} | { message: string }`,
|
|
201
|
+
body: [
|
|
202
|
+
paramHint,
|
|
203
|
+
`const row: ${rowType} | null = null;`,
|
|
204
|
+
"if (row !== null) return row;",
|
|
205
|
+
`return reply.code(404).send({ message: ${lit(`${table.tsName} row not found`)} });`
|
|
206
|
+
]
|
|
207
|
+
});
|
|
208
|
+
if (writable) {
|
|
209
|
+
routes.push({
|
|
210
|
+
name: "update",
|
|
211
|
+
method: "patch",
|
|
212
|
+
path: keyPath,
|
|
213
|
+
schema: [
|
|
214
|
+
`params: ${paramsName}`,
|
|
215
|
+
`body: ${updateName}`,
|
|
216
|
+
`response: { 200: ${selectName} }`
|
|
217
|
+
],
|
|
218
|
+
replyType: rowType,
|
|
219
|
+
body: [notImplemented("update")]
|
|
220
|
+
});
|
|
221
|
+
routes.push({
|
|
222
|
+
name: "delete",
|
|
223
|
+
method: "delete",
|
|
224
|
+
path: keyPath,
|
|
225
|
+
schema: [`params: ${paramsName}`, `response: { 200: { type: 'boolean' } }`],
|
|
226
|
+
replyType: "boolean",
|
|
227
|
+
body: [paramHint, "return true;"]
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
if (writable) {
|
|
232
|
+
routes.push({
|
|
233
|
+
name: "create",
|
|
234
|
+
method: "post",
|
|
235
|
+
path: "/",
|
|
236
|
+
schema: [`body: ${insertName}`, `response: { 200: ${selectName} }`],
|
|
237
|
+
replyType: rowType,
|
|
238
|
+
body: [notImplemented("create")]
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
if (opts.includeRelations) {
|
|
242
|
+
routes.push(...relationRoutes(table, rowType, selectName, opts));
|
|
243
|
+
}
|
|
244
|
+
const order = ["list", "byId", "create", "update", "delete"];
|
|
245
|
+
const rank = (n) => order.indexOf(n) === -1 ? order.length : order.indexOf(n);
|
|
246
|
+
routes.sort((a, b) => rank(a.name) - rank(b.name));
|
|
247
|
+
const exportName = routesExportName(table, opts.naming);
|
|
248
|
+
const statements = routes.map((r) => {
|
|
249
|
+
const reads = (what) => r.body.some((line) => !line.startsWith("//") && what.test(line));
|
|
250
|
+
const params = reads(/\breply\./) ? reads(/\breq\./) ? "(req, reply)" : "(_req, reply)" : reads(/\breq\./) ? "(req)" : "()";
|
|
251
|
+
return [
|
|
252
|
+
` app.${r.method}<{ Reply: ${r.replyType} }>(${lit(r.path)}, {`,
|
|
253
|
+
` schema: { ${r.schema.join(", ")} },`,
|
|
254
|
+
` }, async ${params} => {`,
|
|
255
|
+
...r.body.map((line) => ` ${line}`),
|
|
256
|
+
` });`
|
|
257
|
+
].join("\n");
|
|
258
|
+
}).join("\n\n");
|
|
259
|
+
const plugin = `export const ${exportName}: FastifyPluginAsync = async (app) => {
|
|
260
|
+
${statements}
|
|
261
|
+
};
|
|
262
|
+
`;
|
|
263
|
+
const schemas = tableSchemas(table);
|
|
264
|
+
const declared = [];
|
|
265
|
+
const emit = (name, schema) => `export const ${name} = ${JSON.stringify(adaptForFastify(schema), null, 2)} as const;`;
|
|
266
|
+
if (writable) {
|
|
267
|
+
declared.push(emit(insertName, schemas.insert));
|
|
268
|
+
declared.push(emit(updateName, schemas.update));
|
|
269
|
+
}
|
|
270
|
+
declared.push(emit(selectName, schemas.select));
|
|
271
|
+
if (key) {
|
|
272
|
+
declared.push(emit(paramsName, paramsSchema(key)));
|
|
273
|
+
}
|
|
274
|
+
const rowFields = selectColumns(table).map((c) => ` ${objectKey(c.name)}: ${rowFieldType(c)}${c.nullable ? " | null" : ""};`).join("\n");
|
|
275
|
+
declared.push(`export interface ${rowType} {
|
|
276
|
+
${rowFields}
|
|
277
|
+
}`);
|
|
278
|
+
const wide = selectColumns(table).filter((c) => rowFieldType(c) === "unknown").map((c) => c.name);
|
|
279
|
+
const wideNote = wide.length ? `// No precise type for ${wide.length === 1 ? "this column" : "these columns"}: ${wide.join(", ")}.
|
|
280
|
+
// DRZL could not derive one from the schema, so these routes carry it as unknown and its
|
|
281
|
+
// schema constrains only what the builder could state.
|
|
282
|
+
` : "";
|
|
283
|
+
return `// Generated by @drzl/generator-fastify
|
|
284
|
+
// Routes for table: ${table.name}
|
|
285
|
+
${wideNote}import type { FastifyPluginAsync } from 'fastify';
|
|
286
|
+
|
|
287
|
+
${declared.join("\n\n")}
|
|
288
|
+
|
|
289
|
+
${plugin}`;
|
|
290
|
+
}
|
|
291
|
+
function relationRoutes(table, rowType, selectName, opts) {
|
|
292
|
+
const out = [];
|
|
293
|
+
const taken = /* @__PURE__ */ new Set();
|
|
294
|
+
for (const fk of table.foreignKeys ?? []) {
|
|
295
|
+
if (fk.columns.length !== 1) continue;
|
|
296
|
+
const colName = fk.columns[0];
|
|
297
|
+
const column = table.columns.find((c) => c.name === colName);
|
|
298
|
+
if (!column) continue;
|
|
299
|
+
const segment = toCase(`by-${colName}`, opts.naming?.procedureCase ?? "kebab");
|
|
300
|
+
if (taken.has(segment)) continue;
|
|
301
|
+
taken.add(segment);
|
|
302
|
+
const params = JSON.stringify(paramsSchema([column]));
|
|
303
|
+
out.push({
|
|
304
|
+
name: `listBy${cap(colName)}`,
|
|
305
|
+
method: "get",
|
|
306
|
+
path: `/${segment}/:${colName}`,
|
|
307
|
+
schema: [`params: ${params}`, `response: { 200: { type: 'array', items: ${selectName} } }`],
|
|
308
|
+
replyType: `${rowType}[]`,
|
|
309
|
+
body: [`const rows: ${rowType}[] = [];`, "return rows;"]
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
return out;
|
|
313
|
+
}
|
|
314
|
+
function renderBarrel(modules, ctx, path, opts) {
|
|
315
|
+
if (!modules.length) {
|
|
316
|
+
return `// Generated by @drzl/generator-fastify
|
|
317
|
+
// No tables detected in analysis. Add tables to your schema and regenerate.
|
|
318
|
+
import type { FastifyPluginAsync } from 'fastify';
|
|
319
|
+
|
|
320
|
+
export const routes: FastifyPluginAsync = async () => {};
|
|
321
|
+
`;
|
|
322
|
+
}
|
|
323
|
+
const entries = modules.map(({ filePath, exportName, table }) => ({
|
|
324
|
+
rel: importSpecifier(
|
|
325
|
+
"./" + path.relative(ctx.out, filePath).replace(/\\/g, "/"),
|
|
326
|
+
opts.importExtension
|
|
327
|
+
),
|
|
328
|
+
exportName,
|
|
329
|
+
mount: mountPath(table, opts.naming)
|
|
330
|
+
}));
|
|
331
|
+
const imports = entries.map((e) => `import { ${e.exportName} } from '${e.rel}';`).join("\n");
|
|
332
|
+
const registrations = entries.map((e) => ` app.register(${e.exportName}, { prefix: ${lit(e.mount)} });`).join("\n");
|
|
333
|
+
const reExports = entries.map((e) => `export * from '${e.rel}';`).join("\n");
|
|
334
|
+
return `// Generated by @drzl/generator-fastify
|
|
335
|
+
import type { FastifyPluginAsync } from 'fastify';
|
|
336
|
+
${imports}
|
|
337
|
+
|
|
338
|
+
export const routes: FastifyPluginAsync = async (app) => {
|
|
339
|
+
${registrations}
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
${reExports}
|
|
343
|
+
`;
|
|
344
|
+
}
|
|
345
|
+
export {
|
|
346
|
+
APP_MODULE,
|
|
347
|
+
FastifyGenerator,
|
|
348
|
+
index_default as default
|
|
349
|
+
};
|
|
350
|
+
//# sourceMappingURL=dist-KQMPKOFK.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../generator-fastify/dist/index.js"],"sourcesContent":["// src/index.ts\nimport { fileWriter } from \"@drzl/validation-core\";\nimport { tableSchemas } from \"@drzl/generator-json-schema\";\nimport { formatCode, importSpecifier, selectColumns } from \"@drzl/validation-core\";\nvar APP_MODULE = \"index\";\nvar lit = (v) => /['\\\\]/.test(v) ? JSON.stringify(v) : `'${v}'`;\nvar NUMERIC_SEGMENT_PATTERN = \"^-?\\\\d+(\\\\.\\\\d+)?$\";\nvar BIGINT_SEGMENT_PATTERN = \"^-?\\\\d+$\";\nvar NOT_FOUND_SCHEMA = \"{ type: 'object', properties: { message: { type: 'string' } }, required: ['message'], additionalProperties: false }\";\nvar cap = (s) => s.charAt(0).toUpperCase() + s.slice(1);\nvar isIdent = (s) => /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(s);\nfunction keyColumns(table) {\n const names = table.primaryKey?.columns ?? [];\n if (!names.length) return null;\n const cols = names.map((n) => table.columns.find((c) => c.name === n));\n if (cols.some((c) => !c)) return null;\n return cols;\n}\nfunction segmentSchema(column) {\n if (column.enumValues && column.enumValues.length) return { enum: [...column.enumValues] };\n switch (column.tsType) {\n case \"number\":\n return { type: \"string\", pattern: NUMERIC_SEGMENT_PATTERN };\n case \"bigint\":\n return { type: \"string\", pattern: BIGINT_SEGMENT_PATTERN };\n case \"Date\":\n return { type: \"string\", format: \"date-time\" };\n default:\n return { type: \"string\" };\n }\n}\nfunction paramsSchema(cols) {\n return {\n type: \"object\",\n properties: Object.fromEntries(cols.map((c) => [c.name, segmentSchema(c)])),\n required: cols.map((c) => c.name),\n additionalProperties: false\n };\n}\nfunction adaptForFastify(schema) {\n const { $schema: _dialect, $id: _id, ...rest } = walk(schema);\n return rest;\n}\nfunction walk(value) {\n if (Array.isArray(value)) return value.map(walk);\n if (value && typeof value === \"object\") {\n const out = {};\n for (const [k, v] of Object.entries(value)) {\n if (k === \"prefixItems\") continue;\n out[k] = walk(v);\n }\n const prefix = value.prefixItems;\n if (Array.isArray(prefix) && prefix.length) out.items = walk(prefix[0]);\n return out;\n }\n return value;\n}\nfunction rowFieldType(column) {\n if (column.enumValues && column.enumValues.length) {\n return column.enumValues.map((v) => `'${v.replace(/'/g, \"\\\\'\")}'`).join(\" | \");\n }\n const s = column.shape;\n if (s) {\n switch (s.kind) {\n case \"tuple\":\n return `[${Array.from({ length: s.length }, () => \"number\").join(\", \")}]`;\n case \"numberObject\":\n return `{ ${s.fields.map((f) => `${f}: number`).join(\"; \")} }`;\n case \"numberVector\":\n return \"number[]\";\n default:\n return \"unknown\";\n }\n }\n switch (column.tsType) {\n case \"number\":\n return \"number\";\n case \"string\":\n return \"string\";\n case \"boolean\":\n return \"boolean\";\n case \"Date\":\n return \"Date\";\n case \"bigint\":\n return \"bigint\";\n default:\n return \"unknown\";\n }\n}\nfunction objectKey(name) {\n return isIdent(name) ? name : JSON.stringify(name);\n}\nfunction toCase(s, c) {\n if (!c) return s;\n const parts = s.replace(/([a-z0-9])([A-Z])/g, \"$1 $2\").replace(/[_-]/g, \" \").split(/\\s+/);\n if (c === \"camel\") {\n return parts.map(\n (p, i) => i === 0 ? p.toLowerCase() : p.charAt(0).toUpperCase() + p.slice(1).toLowerCase()\n ).join(\"\");\n }\n if (c === \"kebab\") return parts.map((p) => p.toLowerCase()).join(\"-\");\n if (c === \"snake\") return parts.map((p) => p.toLowerCase()).join(\"_\");\n return s;\n}\nfunction routesExportName(table, naming) {\n const base = `${table.tsName}${naming?.routerSuffix ?? \"Routes\"}`;\n const c = naming?.procedureCase;\n return toCase(base, c === \"kebab\" ? \"camel\" : c);\n}\nfunction mountPath(table, naming) {\n return `/${toCase(table.tsName, naming?.procedureCase)}`;\n}\nvar FastifyGenerator = class {\n constructor(analysis) {\n this.analysis = analysis;\n }\n async generate(opts) {\n const fs = fileWriter(opts.fileSink);\n const path = await import(\"path\");\n const out = path.resolve(process.cwd(), opts.outputDir);\n const ctx = { out };\n await fs.mkdir(out, { recursive: true });\n const files = [];\n const write = async (filePath, content) => {\n const formatted = await formatCode(\n buildHeader(opts.outputHeader) + content,\n filePath,\n opts.format\n );\n await fs.writeFile(filePath, formatted, \"utf8\");\n files.push(filePath);\n };\n const barrelPath = path.join(out, `${APP_MODULE}.ts`);\n const modules = [];\n const total = this.analysis.tables.length;\n let index = 0;\n for (const table of this.analysis.tables) {\n const base = `${table.tsName}${opts.naming?.routerSuffix ?? \"\"}`;\n const filePath = path.join(out, `${toCase(base, opts.naming?.procedureCase)}.ts`);\n if (filePath === barrelPath) {\n throw new Error(\n `@drzl/generator-fastify: the routes for table \"${table.name}\" would be written to ${filePath}, which is the barrel this generator also writes. Set naming.routerSuffix to move it out of the way.`\n );\n }\n await write(filePath, renderRoutes(table, opts));\n modules.push({ table, filePath, exportName: routesExportName(table, opts.naming) });\n index++;\n opts.onProgress?.({ index, total, table: table.name, filePath });\n }\n await write(barrelPath, renderBarrel(modules, ctx, path, opts));\n return { files };\n }\n};\nvar index_default = FastifyGenerator;\nfunction buildHeader(h) {\n if (h && h.enabled === false) return \"\";\n const text = h?.text?.trim();\n const lines = text ? text.split(/\\r?\\n/).map((l) => `// ${l}`) : [\n \"// Generated by DRZL (@drzl/*)\",\n \"// Generated output is granted to you under your project's license.\",\n \"// You may use, copy, modify, and distribute without attribution.\"\n ];\n return lines.join(\"\\n\") + \"\\n\\n\";\n}\nfunction renderRoutes(table, opts) {\n const insertName = `Insert${table.tsName}Schema`;\n const updateName = `Update${table.tsName}Schema`;\n const selectName = `Select${table.tsName}Schema`;\n const paramsName = `${cap(table.tsName)}ParamsSchema`;\n const rowType = `Select${table.tsName}Row`;\n const writable = !table.readOnly;\n const key = keyColumns(table);\n const routes = [];\n const notImplemented = (what) => `throw new Error('Not implemented: ${what} ${table.tsName}.');`;\n const paramHint = `// Fastify has already validated req.params against ${paramsName}; numeric key segments stay strings here.`;\n routes.push({\n name: \"list\",\n method: \"get\",\n path: \"/\",\n schema: [`response: { 200: { type: 'array', items: ${selectName} } }`],\n replyType: `${rowType}[]`,\n // The stub states its contract twice over: the annotated local is what a reader sees, and the\n // Reply generic is what Fastify's own types hold the handler to. The response schema is what\n // the serializer runs; nothing infers a client from any of them, which the docs say plainly.\n body: [`const rows: ${rowType}[] = [];`, \"return rows;\"]\n });\n if (key) {\n const keyPath = \"/\" + key.map((c) => `:${c.name}`).join(\"/\");\n routes.push({\n name: \"byId\",\n method: \"get\",\n path: keyPath,\n schema: [\n `params: ${paramsName}`,\n `response: { 200: ${selectName}, 404: ${NOT_FOUND_SCHEMA} }`\n ],\n replyType: `${rowType} | { message: string }`,\n body: [\n paramHint,\n `const row: ${rowType} | null = null;`,\n \"if (row !== null) return row;\",\n `return reply.code(404).send({ message: ${lit(`${table.tsName} row not found`)} });`\n ]\n });\n if (writable) {\n routes.push({\n name: \"update\",\n method: \"patch\",\n path: keyPath,\n schema: [\n `params: ${paramsName}`,\n `body: ${updateName}`,\n `response: { 200: ${selectName} }`\n ],\n replyType: rowType,\n body: [notImplemented(\"update\")]\n });\n routes.push({\n name: \"delete\",\n method: \"delete\",\n path: keyPath,\n schema: [`params: ${paramsName}`, `response: { 200: { type: 'boolean' } }`],\n replyType: \"boolean\",\n body: [paramHint, \"return true;\"]\n });\n }\n }\n if (writable) {\n routes.push({\n name: \"create\",\n method: \"post\",\n path: \"/\",\n schema: [`body: ${insertName}`, `response: { 200: ${selectName} }`],\n replyType: rowType,\n body: [notImplemented(\"create\")]\n });\n }\n if (opts.includeRelations) {\n routes.push(...relationRoutes(table, rowType, selectName, opts));\n }\n const order = [\"list\", \"byId\", \"create\", \"update\", \"delete\"];\n const rank = (n) => order.indexOf(n) === -1 ? order.length : order.indexOf(n);\n routes.sort((a, b) => rank(a.name) - rank(b.name));\n const exportName = routesExportName(table, opts.naming);\n const statements = routes.map((r) => {\n const reads = (what) => r.body.some((line) => !line.startsWith(\"//\") && what.test(line));\n const params = reads(/\\breply\\./) ? reads(/\\breq\\./) ? \"(req, reply)\" : \"(_req, reply)\" : reads(/\\breq\\./) ? \"(req)\" : \"()\";\n return [\n ` app.${r.method}<{ Reply: ${r.replyType} }>(${lit(r.path)}, {`,\n ` schema: { ${r.schema.join(\", \")} },`,\n ` }, async ${params} => {`,\n ...r.body.map((line) => ` ${line}`),\n ` });`\n ].join(\"\\n\");\n }).join(\"\\n\\n\");\n const plugin = `export const ${exportName}: FastifyPluginAsync = async (app) => {\n${statements}\n};\n`;\n const schemas = tableSchemas(table);\n const declared = [];\n const emit = (name, schema) => `export const ${name} = ${JSON.stringify(adaptForFastify(schema), null, 2)} as const;`;\n if (writable) {\n declared.push(emit(insertName, schemas.insert));\n declared.push(emit(updateName, schemas.update));\n }\n declared.push(emit(selectName, schemas.select));\n if (key) {\n declared.push(emit(paramsName, paramsSchema(key)));\n }\n const rowFields = selectColumns(table).map((c) => ` ${objectKey(c.name)}: ${rowFieldType(c)}${c.nullable ? \" | null\" : \"\"};`).join(\"\\n\");\n declared.push(`export interface ${rowType} {\n${rowFields}\n}`);\n const wide = selectColumns(table).filter((c) => rowFieldType(c) === \"unknown\").map((c) => c.name);\n const wideNote = wide.length ? `// No precise type for ${wide.length === 1 ? \"this column\" : \"these columns\"}: ${wide.join(\", \")}.\n// DRZL could not derive one from the schema, so these routes carry it as unknown and its\n// schema constrains only what the builder could state.\n` : \"\";\n return `// Generated by @drzl/generator-fastify\n// Routes for table: ${table.name}\n${wideNote}import type { FastifyPluginAsync } from 'fastify';\n\n${declared.join(\"\\n\\n\")}\n\n${plugin}`;\n}\nfunction relationRoutes(table, rowType, selectName, opts) {\n const out = [];\n const taken = /* @__PURE__ */ new Set();\n for (const fk of table.foreignKeys ?? []) {\n if (fk.columns.length !== 1) continue;\n const colName = fk.columns[0];\n const column = table.columns.find((c) => c.name === colName);\n if (!column) continue;\n const segment = toCase(`by-${colName}`, opts.naming?.procedureCase ?? \"kebab\");\n if (taken.has(segment)) continue;\n taken.add(segment);\n const params = JSON.stringify(paramsSchema([column]));\n out.push({\n name: `listBy${cap(colName)}`,\n method: \"get\",\n path: `/${segment}/:${colName}`,\n schema: [`params: ${params}`, `response: { 200: { type: 'array', items: ${selectName} } }`],\n replyType: `${rowType}[]`,\n body: [`const rows: ${rowType}[] = [];`, \"return rows;\"]\n });\n }\n return out;\n}\nfunction renderBarrel(modules, ctx, path, opts) {\n if (!modules.length) {\n return `// Generated by @drzl/generator-fastify\n// No tables detected in analysis. Add tables to your schema and regenerate.\nimport type { FastifyPluginAsync } from 'fastify';\n\nexport const routes: FastifyPluginAsync = async () => {};\n`;\n }\n const entries = modules.map(({ filePath, exportName, table }) => ({\n rel: importSpecifier(\n \"./\" + path.relative(ctx.out, filePath).replace(/\\\\/g, \"/\"),\n opts.importExtension\n ),\n exportName,\n mount: mountPath(table, opts.naming)\n }));\n const imports = entries.map((e) => `import { ${e.exportName} } from '${e.rel}';`).join(\"\\n\");\n const registrations = entries.map((e) => ` app.register(${e.exportName}, { prefix: ${lit(e.mount)} });`).join(\"\\n\");\n const reExports = entries.map((e) => `export * from '${e.rel}';`).join(\"\\n\");\n return `// Generated by @drzl/generator-fastify\nimport type { FastifyPluginAsync } from 'fastify';\n${imports}\n\nexport const routes: FastifyPluginAsync = async (app) => {\n${registrations}\n};\n\n${reExports}\n`;\n}\nexport {\n APP_MODULE,\n FastifyGenerator,\n index_default as default\n};\n"],"mappings":"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","names":[]}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// ../generator-effect/dist/index.js
|
|
2
|
+
import { fileWriter } from "@drzl/validation-core";
|
|
2
3
|
import {
|
|
3
4
|
buildBrandPlan,
|
|
4
5
|
buildNestedPlan,
|
|
@@ -8,11 +9,22 @@ import {
|
|
|
8
9
|
nestedSchemaName,
|
|
9
10
|
nestedTypeName,
|
|
10
11
|
resolveNestedDepth,
|
|
12
|
+
applyWirePolicy,
|
|
13
|
+
canonicalMembers,
|
|
14
|
+
canonicalNumericText,
|
|
15
|
+
comparisonWire,
|
|
16
|
+
describeSet,
|
|
17
|
+
needsNumericCanon,
|
|
11
18
|
CODEPOINT_LENGTH,
|
|
12
19
|
COERCIBLE_DATE_STRING,
|
|
13
20
|
COLUMN_FORMATS,
|
|
21
|
+
NUMERIC_CANON_NAME,
|
|
22
|
+
NUMERIC_CANON_SOURCE,
|
|
14
23
|
insertColumns,
|
|
15
24
|
isIntegerColumn,
|
|
25
|
+
lengthCheckLabel,
|
|
26
|
+
lengthMeasure,
|
|
27
|
+
measureExpression,
|
|
16
28
|
moduleFileName,
|
|
17
29
|
moduleSpecifier,
|
|
18
30
|
nonFiniteAccepted,
|
|
@@ -24,7 +36,8 @@ import {
|
|
|
24
36
|
schemaName,
|
|
25
37
|
selectColumns,
|
|
26
38
|
typeName,
|
|
27
|
-
updateColumns
|
|
39
|
+
updateColumns,
|
|
40
|
+
wireNumberLiteral
|
|
28
41
|
} from "@drzl/validation-core";
|
|
29
42
|
var DEFAULT_FILE_SUFFIX = ".effect.ts";
|
|
30
43
|
var STANDARD_PREFIX = "Standard";
|
|
@@ -149,13 +162,16 @@ function capSteps(c, mode) {
|
|
|
149
162
|
return steps;
|
|
150
163
|
}
|
|
151
164
|
function lengthSteps(c, lengths) {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
(
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
165
|
+
return lengths.filter((k) => k.column === c.name).flatMap((k) => {
|
|
166
|
+
const measure = lengthMeasure(c, k);
|
|
167
|
+
if (!measure) return [];
|
|
168
|
+
return [
|
|
169
|
+
filter(
|
|
170
|
+
`${measureExpression(measure, "v")} ${OPS[k.operator]} ${k.value}`,
|
|
171
|
+
lengthCheckLabel(k)
|
|
172
|
+
)
|
|
173
|
+
];
|
|
174
|
+
});
|
|
159
175
|
}
|
|
160
176
|
function cardinalitySteps(c, cardinalities) {
|
|
161
177
|
if (!c.arrayDimensions) return [];
|
|
@@ -169,12 +185,19 @@ function cardinalitySteps(c, cardinalities) {
|
|
|
169
185
|
function checkSteps(c, checks) {
|
|
170
186
|
if (c.arrayDimensions || c.shape) return [];
|
|
171
187
|
const folded = foldedIntoBounds(c, checks);
|
|
188
|
+
const numericWire = comparisonWire(c) === "numeric-string";
|
|
172
189
|
return checks.filter((k) => k.column === c.name && !folded.has(k)).map((k) => {
|
|
173
|
-
const
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
190
|
+
const label = `${k.name ? `${k.name}: ` : ""}${c.name} ${k.operator} ${k.value}`;
|
|
191
|
+
if (numericWire) {
|
|
192
|
+
if (k.operator === "=" || k.operator === "<>") {
|
|
193
|
+
const canon = JSON.stringify(canonicalNumericText(k.value));
|
|
194
|
+
const op = k.operator === "=" ? "===" : "!==";
|
|
195
|
+
return filter(`${NUMERIC_CANON_NAME}(v) ${op} ${canon}`, label);
|
|
196
|
+
}
|
|
197
|
+
return filter(`Number(v) ${OPS[k.operator]} ${k.value}`, label);
|
|
198
|
+
}
|
|
199
|
+
const literal = k.kind === "string" ? JSON.stringify(k.value) : wireNumberLiteral(c, k.value);
|
|
200
|
+
return filter(`v ${OPS[k.operator]} ${literal}`, label);
|
|
178
201
|
});
|
|
179
202
|
}
|
|
180
203
|
function hasNoRuntimeType(c) {
|
|
@@ -212,10 +235,19 @@ function shapeExpr(c, mode, replaced = false) {
|
|
|
212
235
|
}
|
|
213
236
|
function exprForColumn(c, mode, coerceDates, checks, sets, lengths, replaced) {
|
|
214
237
|
const shaped = shapeExpr(c, mode, replaced);
|
|
215
|
-
if (shaped) return shaped;
|
|
238
|
+
if (shaped) return piped(shaped, lengthSteps(c, lengths));
|
|
216
239
|
const set = sets.find((x) => x.column === c.name);
|
|
217
240
|
if (set) {
|
|
218
|
-
|
|
241
|
+
if (comparisonWire(c) === "numeric-string") {
|
|
242
|
+
const members = canonicalMembers(set.values);
|
|
243
|
+
const test = members.map((m) => `canon === ${JSON.stringify(m)}`).join(" || ");
|
|
244
|
+
return piped(`${NS}.String`, [
|
|
245
|
+
filter(`((canon) => ${test})(${NUMERIC_CANON_NAME}(v))`, describeSet(set))
|
|
246
|
+
]);
|
|
247
|
+
}
|
|
248
|
+
const values = set.values.map(
|
|
249
|
+
(v) => set.kind === "string" ? JSON.stringify(v) : wireNumberLiteral(c, v)
|
|
250
|
+
);
|
|
219
251
|
return `${NS}.Literal(${values.join(", ")})`;
|
|
220
252
|
}
|
|
221
253
|
if (c.arrayDimensions) checks = [];
|
|
@@ -223,8 +255,8 @@ function exprForColumn(c, mode, coerceDates, checks, sets, lengths, replaced) {
|
|
|
223
255
|
return `${NS}.Literal(${c.enumValues.map((v) => JSON.stringify(v)).join(", ")})`;
|
|
224
256
|
}
|
|
225
257
|
const eq = checks.find((k) => k.column === c.name && k.operator === "=");
|
|
226
|
-
if (eq && !c.shape) {
|
|
227
|
-
return `${NS}.Literal(${eq.kind === "string" ? JSON.stringify(eq.value) : eq.value})`;
|
|
258
|
+
if (eq && !c.shape && comparisonWire(c) !== "numeric-string") {
|
|
259
|
+
return `${NS}.Literal(${eq.kind === "string" ? JSON.stringify(eq.value) : wireNumberLiteral(c, eq.value)})`;
|
|
228
260
|
}
|
|
229
261
|
const rest = [...checkSteps(c, checks), ...lengthSteps(c, lengths)];
|
|
230
262
|
switch (c.tsType) {
|
|
@@ -316,9 +348,14 @@ function indentBlock(code, by = " ") {
|
|
|
316
348
|
}
|
|
317
349
|
function parsedChecksFor(table) {
|
|
318
350
|
const parsed = (table.checks ?? []).map((k) => parseCheck(k.expression, k.name));
|
|
351
|
+
const { checks, sets } = applyWirePolicy(
|
|
352
|
+
table.columns,
|
|
353
|
+
parsed.flatMap((p) => p.ok ? p.checks : []),
|
|
354
|
+
parsed.flatMap((p) => p.ok ? p.sets ?? [] : [])
|
|
355
|
+
);
|
|
319
356
|
return {
|
|
320
|
-
checks
|
|
321
|
-
sets
|
|
357
|
+
checks,
|
|
358
|
+
sets,
|
|
322
359
|
rows: parsed.flatMap((p) => p.ok ? p.rows ?? [] : []),
|
|
323
360
|
lengths: parsed.flatMap((p) => p.ok ? p.lengths ?? [] : []),
|
|
324
361
|
cardinalities: parsed.flatMap((p) => p.ok ? p.cardinalities ?? [] : [])
|
|
@@ -480,9 +517,21 @@ export type ${a.alias} = ${NS}.Schema.Type<typeof ${selectName}>[${JSON.stringif
|
|
|
480
517
|
const brandCode = brandAliases ? `
|
|
481
518
|
${brandAliases}
|
|
482
519
|
` : "";
|
|
520
|
+
const involved = [
|
|
521
|
+
table,
|
|
522
|
+
...["insert", "select"].flatMap((m) => {
|
|
523
|
+
const plan = nested[m];
|
|
524
|
+
return plan ? nestedNodes(plan).map((n) => n.table) : [];
|
|
525
|
+
})
|
|
526
|
+
];
|
|
527
|
+
const canonPreamble = involved.some((t) => {
|
|
528
|
+
const own = parsedChecksFor(t);
|
|
529
|
+
return needsNumericCanon(t.columns, own.checks, own.sets);
|
|
530
|
+
}) ? `
|
|
531
|
+
${NUMERIC_CANON_SOURCE}` : "";
|
|
483
532
|
return `import * as ${NS} from 'effect/Schema';
|
|
484
533
|
${schemaImport}${needsJson ? `
|
|
485
|
-
${JSON_PREAMBLE}` : ""}
|
|
534
|
+
${JSON_PREAMBLE}` : ""}${canonPreamble}
|
|
486
535
|
${blocks.join("\n\n")}
|
|
487
536
|
${brandCode}${nestedCode}${duplicates}`;
|
|
488
537
|
}
|
|
@@ -492,7 +541,7 @@ var EffectGenerator = class {
|
|
|
492
541
|
this.library = "effect";
|
|
493
542
|
}
|
|
494
543
|
async generate(opts) {
|
|
495
|
-
const fs =
|
|
544
|
+
const fs = fileWriter(opts.fileSink);
|
|
496
545
|
const path = await import("path");
|
|
497
546
|
const out = path.resolve(process.cwd(), opts.outDir);
|
|
498
547
|
const files = [];
|
|
@@ -582,4 +631,4 @@ export {
|
|
|
582
631
|
EffectGenerator,
|
|
583
632
|
index_default as default
|
|
584
633
|
};
|
|
585
|
-
//# sourceMappingURL=dist-
|
|
634
|
+
//# sourceMappingURL=dist-KX62ETKK.js.map
|