@drzl/cli 4.19.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-54E2IO7N.js +1030 -0
- package/dist/chunk-54E2IO7N.js.map +1 -0
- package/dist/{dist-TRJLPIWT.js → chunk-KKPDOZOD.js} +239 -48
- package/dist/chunk-KKPDOZOD.js.map +1 -0
- package/dist/cli.cjs +6850 -1434
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +2958 -679
- package/dist/cli.js.map +1 -1
- package/dist/config.cjs +632 -70
- package/dist/config.cjs.map +1 -1
- package/dist/config.d.cts +314 -6
- package/dist/config.d.ts +314 -6
- package/dist/config.js +29 -1
- package/dist/dist-KQMPKOFK.js +350 -0
- package/dist/dist-KQMPKOFK.js.map +1 -0
- package/dist/{dist-CZDVFYFW.js → dist-KX62ETKK.js} +112 -37
- 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-V2IXXAC2.js +0 -465
- package/dist/chunk-V2IXXAC2.js.map +0 -1
- package/dist/dist-CZDVFYFW.js.map +0 -1
- package/dist/dist-TRJLPIWT.js.map +0 -1
- package/dist/dist-XBGVORL3.js.map +0 -1
|
@@ -0,0 +1,489 @@
|
|
|
1
|
+
// ../generator-express/dist/index.js
|
|
2
|
+
import { fileWriter } from "@drzl/validation-core";
|
|
3
|
+
import {
|
|
4
|
+
formatCode,
|
|
5
|
+
importSpecifier,
|
|
6
|
+
resolveAffix,
|
|
7
|
+
resolveConfiguredImport,
|
|
8
|
+
schemaName
|
|
9
|
+
} from "@drzl/validation-core";
|
|
10
|
+
var APP_MODULE = "index";
|
|
11
|
+
var VALIDATION_MODULE = "validation";
|
|
12
|
+
var q = (v) => JSON.stringify(v);
|
|
13
|
+
var lit = (v) => /['\\]/.test(v) ? JSON.stringify(v) : `'${v}'`;
|
|
14
|
+
var NUMERIC_SEGMENT = String.raw`/^-?\d+(\.\d+)?$/`;
|
|
15
|
+
var LIB_IMPORTS = {
|
|
16
|
+
zod: "import { z } from 'zod';",
|
|
17
|
+
valibot: "import * as v from 'valibot';",
|
|
18
|
+
arktype: "import { type } from 'arktype';"
|
|
19
|
+
};
|
|
20
|
+
var LIB_USAGE = {
|
|
21
|
+
zod: /\bz\./,
|
|
22
|
+
valibot: /\bv\./,
|
|
23
|
+
arktype: /\btype\(|\.infer\b/
|
|
24
|
+
};
|
|
25
|
+
var LIBS = {
|
|
26
|
+
zod: {
|
|
27
|
+
number: "z.number()",
|
|
28
|
+
string: "z.string()",
|
|
29
|
+
boolean: "z.boolean()",
|
|
30
|
+
date: "z.date()",
|
|
31
|
+
unknown: "z.unknown()",
|
|
32
|
+
enum: (vals) => `z.enum([${vals.map(q).join(", ")}] as const)`,
|
|
33
|
+
nullable: (b) => `${b}.nullable()`,
|
|
34
|
+
optional: (b) => `${b}.optional()`,
|
|
35
|
+
object: (body) => `z.object({
|
|
36
|
+
${body}
|
|
37
|
+
})`,
|
|
38
|
+
objectInline: (body) => `z.object({ ${body} })`,
|
|
39
|
+
partialUpdate: (s) => `${s}.partial()`,
|
|
40
|
+
// Not `z.coerce.number()`, and not `z.coerce.date()`: both accept far more than a path
|
|
41
|
+
// segment addressing a row should. See the grid referenced on `LibDialect.coerce`.
|
|
42
|
+
coerce: (t) => t === "number" ? `z.string().regex(${NUMERIC_SEGMENT}).transform(Number)` : t === "Date" ? "z.iso.datetime().transform((s) => new Date(s))" : null,
|
|
43
|
+
infer: (s) => `z.output<typeof ${s}>`
|
|
44
|
+
},
|
|
45
|
+
valibot: {
|
|
46
|
+
number: "v.number()",
|
|
47
|
+
string: "v.string()",
|
|
48
|
+
boolean: "v.boolean()",
|
|
49
|
+
date: "v.date()",
|
|
50
|
+
unknown: "v.unknown()",
|
|
51
|
+
enum: (vals) => `v.picklist([${vals.map(q).join(", ")}] as const)`,
|
|
52
|
+
nullable: (b) => `v.nullable(${b})`,
|
|
53
|
+
optional: (b) => `v.optional(${b})`,
|
|
54
|
+
object: (body) => `v.object({
|
|
55
|
+
${body}
|
|
56
|
+
})`,
|
|
57
|
+
objectInline: (body) => `v.object({ ${body} })`,
|
|
58
|
+
// A valibot pipe step sees the previous step's *output*, so the check has to happen while the
|
|
59
|
+
// value is still the string: after a `v.transform(Number)` there is no string left to look at.
|
|
60
|
+
coerce: (t) => t === "number" ? `v.pipe(v.string(), v.regex(${NUMERIC_SEGMENT}), v.transform(Number))` : t === "Date" ? "v.pipe(v.string(), v.isoTimestamp(), v.transform((s) => new Date(s)))" : null,
|
|
61
|
+
infer: (s) => `v.InferOutput<typeof ${s}>`
|
|
62
|
+
},
|
|
63
|
+
arktype: {
|
|
64
|
+
number: "number",
|
|
65
|
+
string: "string",
|
|
66
|
+
boolean: "boolean",
|
|
67
|
+
date: "Date",
|
|
68
|
+
unknown: "unknown",
|
|
69
|
+
// The surrounding encode adds the quotes, so the union is built with the inner quoting
|
|
70
|
+
// ArkType expects.
|
|
71
|
+
enum: (vals) => vals.map((x) => `'${x.replace(/'/g, "\\'")}'`).join(" | "),
|
|
72
|
+
nullable: (b) => `(${b} | null)`,
|
|
73
|
+
optional: (b) => `${b}?`,
|
|
74
|
+
object: (body) => `type({
|
|
75
|
+
${body}
|
|
76
|
+
})`,
|
|
77
|
+
objectInline: (body) => `type({ ${body} })`,
|
|
78
|
+
fieldIsString: true,
|
|
79
|
+
// ArkType ships these as keywords, and they are morphs: the declared output type is `number`,
|
|
80
|
+
// not `string`. Returned bare, because `fieldIsString` quotes every expression this dialect
|
|
81
|
+
// produces, and a keyword returned pre-quoted arrives as a string *literal* type matching
|
|
82
|
+
// nothing but that sentence. `string.date.iso.parse` and not `string.date.parse`, which
|
|
83
|
+
// accepts `"1"` as the year 2001.
|
|
84
|
+
coerce: (t) => t === "number" ? "string.numeric.parse" : t === "Date" ? "string.date.iso.parse" : null,
|
|
85
|
+
infer: (s) => `typeof ${s}.infer`
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
var cap = (s) => s.charAt(0).toUpperCase() + s.slice(1);
|
|
89
|
+
var isIdent = (s) => /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(s);
|
|
90
|
+
function keyColumns(table) {
|
|
91
|
+
const names = table.primaryKey?.columns ?? [];
|
|
92
|
+
if (!names.length) return null;
|
|
93
|
+
const cols = names.map((n) => table.columns.find((c) => c.name === n));
|
|
94
|
+
if (cols.some((c) => !c)) return null;
|
|
95
|
+
return cols;
|
|
96
|
+
}
|
|
97
|
+
function isWide(column) {
|
|
98
|
+
if (column.enumValues && column.enumValues.length) return false;
|
|
99
|
+
if (column.shape?.kind === "tuple" || column.shape?.kind === "numberObject") return false;
|
|
100
|
+
return !["number", "string", "boolean", "Date"].includes(column.tsType);
|
|
101
|
+
}
|
|
102
|
+
function mapExpr(column, lib, mode) {
|
|
103
|
+
const d = LIBS[lib];
|
|
104
|
+
let base = (() => {
|
|
105
|
+
if (column.enumValues && column.enumValues.length) return d.enum(column.enumValues);
|
|
106
|
+
switch (column.tsType) {
|
|
107
|
+
case "number":
|
|
108
|
+
return d.number;
|
|
109
|
+
case "string":
|
|
110
|
+
return d.string;
|
|
111
|
+
case "boolean":
|
|
112
|
+
return d.boolean;
|
|
113
|
+
case "Date":
|
|
114
|
+
return d.date;
|
|
115
|
+
default:
|
|
116
|
+
return d.unknown;
|
|
117
|
+
}
|
|
118
|
+
})();
|
|
119
|
+
if (column.nullable) base = d.nullable(base);
|
|
120
|
+
if (mode !== "select") {
|
|
121
|
+
const optional = mode === "update" || column.nullable || column.hasDefault;
|
|
122
|
+
if (optional) base = d.optional(base);
|
|
123
|
+
}
|
|
124
|
+
return base;
|
|
125
|
+
}
|
|
126
|
+
function objectKey(name) {
|
|
127
|
+
return isIdent(name) ? name : JSON.stringify(name);
|
|
128
|
+
}
|
|
129
|
+
function field(column, lib, mode) {
|
|
130
|
+
const d = LIBS[lib];
|
|
131
|
+
const expr = mapExpr(column, lib, mode);
|
|
132
|
+
return `${objectKey(column.name)}: ${d.fieldIsString ? JSON.stringify(expr) : expr}`;
|
|
133
|
+
}
|
|
134
|
+
function paramField(column, lib) {
|
|
135
|
+
const d = LIBS[lib];
|
|
136
|
+
const expr = (() => {
|
|
137
|
+
if (column.enumValues && column.enumValues.length) return d.enum(column.enumValues);
|
|
138
|
+
return d.coerce(column.tsType) ?? d.string;
|
|
139
|
+
})();
|
|
140
|
+
return `${objectKey(column.name)}: ${d.fieldIsString ? JSON.stringify(expr) : expr}`;
|
|
141
|
+
}
|
|
142
|
+
function renderSchema(table, lib, mode) {
|
|
143
|
+
const d = LIBS[lib];
|
|
144
|
+
const cols = table.columns.filter((c) => mode === "select" ? true : !c.isGenerated);
|
|
145
|
+
const body = cols.map((c) => ` ${field(c, lib, mode)},`).join("\n");
|
|
146
|
+
const schema = d.object(body);
|
|
147
|
+
return mode === "update" && d.partialUpdate ? d.partialUpdate(schema) : schema;
|
|
148
|
+
}
|
|
149
|
+
function toCase(s, c) {
|
|
150
|
+
if (!c) return s;
|
|
151
|
+
const parts = s.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_-]/g, " ").split(/\s+/);
|
|
152
|
+
if (c === "camel") {
|
|
153
|
+
return parts.map(
|
|
154
|
+
(p, i) => i === 0 ? p.toLowerCase() : p.charAt(0).toUpperCase() + p.slice(1).toLowerCase()
|
|
155
|
+
).join("");
|
|
156
|
+
}
|
|
157
|
+
if (c === "kebab") return parts.map((p) => p.toLowerCase()).join("-");
|
|
158
|
+
if (c === "snake") return parts.map((p) => p.toLowerCase()).join("_");
|
|
159
|
+
return s;
|
|
160
|
+
}
|
|
161
|
+
function routesExportName(table, naming) {
|
|
162
|
+
const base = `${table.tsName}${naming?.routerSuffix ?? "Routes"}`;
|
|
163
|
+
const c = naming?.procedureCase;
|
|
164
|
+
return toCase(base, c === "kebab" ? "camel" : c);
|
|
165
|
+
}
|
|
166
|
+
function mountPath(table, naming) {
|
|
167
|
+
return `/${toCase(table.tsName, naming?.procedureCase)}`;
|
|
168
|
+
}
|
|
169
|
+
var ExpressGenerator = class {
|
|
170
|
+
constructor(analysis) {
|
|
171
|
+
this.analysis = analysis;
|
|
172
|
+
}
|
|
173
|
+
async generate(opts) {
|
|
174
|
+
const fs = fileWriter(opts.fileSink);
|
|
175
|
+
const path = await import("path");
|
|
176
|
+
const out = path.resolve(process.cwd(), opts.outputDir);
|
|
177
|
+
const ctx = { out };
|
|
178
|
+
await fs.mkdir(out, { recursive: true });
|
|
179
|
+
const files = [];
|
|
180
|
+
const write = async (filePath, content) => {
|
|
181
|
+
const formatted = await formatCode(
|
|
182
|
+
buildHeader(opts.outputHeader) + content,
|
|
183
|
+
filePath,
|
|
184
|
+
opts.format
|
|
185
|
+
);
|
|
186
|
+
await fs.writeFile(filePath, formatted, "utf8");
|
|
187
|
+
files.push(filePath);
|
|
188
|
+
};
|
|
189
|
+
const barrelPath = path.join(out, `${APP_MODULE}.ts`);
|
|
190
|
+
const validationPath = path.join(out, `${VALIDATION_MODULE}.ts`);
|
|
191
|
+
const modules = [];
|
|
192
|
+
const total = this.analysis.tables.length;
|
|
193
|
+
let index = 0;
|
|
194
|
+
let anyValidates = false;
|
|
195
|
+
for (const table of this.analysis.tables) {
|
|
196
|
+
const base = `${table.tsName}${opts.naming?.routerSuffix ?? ""}`;
|
|
197
|
+
const filePath = path.join(out, `${toCase(base, opts.naming?.procedureCase)}.ts`);
|
|
198
|
+
if (filePath === barrelPath || filePath === validationPath) {
|
|
199
|
+
const which = filePath === barrelPath ? "the barrel" : "the validation middleware module";
|
|
200
|
+
throw new Error(
|
|
201
|
+
`@drzl/generator-express: the routes for table "${table.name}" would be written to ${filePath}, which is ${which} this generator also writes. Set naming.routerSuffix to move it out of the way.`
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
const source = renderRoutes(table, opts, ctx);
|
|
205
|
+
if (source.includes(`${VALIDATE_FN}('`)) anyValidates = true;
|
|
206
|
+
await write(filePath, source);
|
|
207
|
+
modules.push({ table, filePath, exportName: routesExportName(table, opts.naming) });
|
|
208
|
+
index++;
|
|
209
|
+
opts.onProgress?.({ index, total, table: table.name, filePath });
|
|
210
|
+
}
|
|
211
|
+
if (anyValidates) {
|
|
212
|
+
await write(validationPath, renderValidationModule());
|
|
213
|
+
}
|
|
214
|
+
await write(barrelPath, renderBarrel(modules, ctx, path, opts));
|
|
215
|
+
return { files };
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
var index_default = ExpressGenerator;
|
|
219
|
+
function buildHeader(h) {
|
|
220
|
+
if (h && h.enabled === false) return "";
|
|
221
|
+
const text = h?.text?.trim();
|
|
222
|
+
const lines = text ? text.split(/\r?\n/).map((l) => `// ${l}`) : [
|
|
223
|
+
"// Generated by DRZL (@drzl/*)",
|
|
224
|
+
"// Generated output is granted to you under your project's license.",
|
|
225
|
+
"// You may use, copy, modify, and distribute without attribution."
|
|
226
|
+
];
|
|
227
|
+
return lines.join("\n") + "\n\n";
|
|
228
|
+
}
|
|
229
|
+
var VALIDATE_FN = "validate";
|
|
230
|
+
var VALID_PARAM_HINT = "// validate() has already replaced req.params with the parsed values.";
|
|
231
|
+
function renderValidationModule() {
|
|
232
|
+
return `// Generated by @drzl/generator-express
|
|
233
|
+
// A validation middleware over Standard Schema v1, so the same routes accept zod, valibot or
|
|
234
|
+
// arktype schemas. On failure it answers 400 with { error, slot, issues: [{ message, path }] };
|
|
235
|
+
// on success it replaces req[slot] with the parsed output and calls next().
|
|
236
|
+
import type { RequestHandler } from 'express';
|
|
237
|
+
|
|
238
|
+
interface StandardIssue {
|
|
239
|
+
readonly message: string;
|
|
240
|
+
readonly path?: ReadonlyArray<PropertyKey | { readonly key: PropertyKey }> | undefined;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
type StandardResult<Output> =
|
|
244
|
+
| { readonly value: Output; readonly issues?: undefined }
|
|
245
|
+
| { readonly issues: ReadonlyArray<StandardIssue> };
|
|
246
|
+
|
|
247
|
+
export interface StandardSchema<Output = unknown> {
|
|
248
|
+
readonly '~standard': {
|
|
249
|
+
readonly version: 1;
|
|
250
|
+
readonly vendor: string;
|
|
251
|
+
readonly validate: (
|
|
252
|
+
value: unknown
|
|
253
|
+
) => StandardResult<Output> | Promise<StandardResult<Output>>;
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export function ${VALIDATE_FN}(slot: 'params' | 'body', schema: StandardSchema): RequestHandler {
|
|
258
|
+
return async (req, res, next) => {
|
|
259
|
+
const result = await schema['~standard'].validate(req[slot]);
|
|
260
|
+
if (result.issues) {
|
|
261
|
+
res.status(400).json({
|
|
262
|
+
error: 'Validation failed',
|
|
263
|
+
slot,
|
|
264
|
+
issues: result.issues.map((issue) => ({
|
|
265
|
+
message: issue.message,
|
|
266
|
+
path: (issue.path ?? []).map((p) => (typeof p === 'object' && p !== null ? p.key : p)),
|
|
267
|
+
})),
|
|
268
|
+
});
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
req[slot] = result.value as never;
|
|
272
|
+
next();
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
`;
|
|
276
|
+
}
|
|
277
|
+
function renderRoutes(table, opts, ctx) {
|
|
278
|
+
const lib = opts.validation?.library ?? "zod";
|
|
279
|
+
const d = LIBS[lib];
|
|
280
|
+
const insertName = `Insert${table.tsName}Schema`;
|
|
281
|
+
const updateName = `Update${table.tsName}Schema`;
|
|
282
|
+
const selectName = `Select${table.tsName}Schema`;
|
|
283
|
+
const paramsName = `${cap(table.tsName)}ParamsSchema`;
|
|
284
|
+
const rowType = `Select${table.tsName}Row`;
|
|
285
|
+
const writable = !table.readOnly;
|
|
286
|
+
const key = keyColumns(table);
|
|
287
|
+
const routes = [];
|
|
288
|
+
const notImplemented = (what) => `throw new Error('Not implemented: ${what} ${table.tsName}.');`;
|
|
289
|
+
routes.push({
|
|
290
|
+
name: "list",
|
|
291
|
+
method: "get",
|
|
292
|
+
path: "/",
|
|
293
|
+
middleware: [],
|
|
294
|
+
resType: `${rowType}[]`,
|
|
295
|
+
// The stub states its own contract twice over: the annotated local is what a reader sees,
|
|
296
|
+
// and the annotated res parameter is what Express's own types hold the handler to. Nothing
|
|
297
|
+
// infers a client from either, which the docs say plainly; the types are for the person
|
|
298
|
+
// filling the stub in.
|
|
299
|
+
body: [`const rows: ${rowType}[] = [];`, "res.json(rows);"]
|
|
300
|
+
});
|
|
301
|
+
if (key) {
|
|
302
|
+
const keyPath = "/" + key.map((c) => `:${c.name}`).join("/");
|
|
303
|
+
routes.push({
|
|
304
|
+
name: "byId",
|
|
305
|
+
method: "get",
|
|
306
|
+
path: keyPath,
|
|
307
|
+
middleware: [`${VALIDATE_FN}('params', ${paramsName})`],
|
|
308
|
+
resType: `${rowType} | null`,
|
|
309
|
+
body: [VALID_PARAM_HINT, `const row: ${rowType} | null = null;`, "res.json(row);"]
|
|
310
|
+
});
|
|
311
|
+
if (writable) {
|
|
312
|
+
routes.push({
|
|
313
|
+
name: "update",
|
|
314
|
+
method: "patch",
|
|
315
|
+
path: keyPath,
|
|
316
|
+
middleware: [
|
|
317
|
+
`${VALIDATE_FN}('params', ${paramsName})`,
|
|
318
|
+
"json()",
|
|
319
|
+
`${VALIDATE_FN}('body', ${updateName})`
|
|
320
|
+
],
|
|
321
|
+
resType: rowType,
|
|
322
|
+
body: [notImplemented("update")]
|
|
323
|
+
});
|
|
324
|
+
routes.push({
|
|
325
|
+
name: "delete",
|
|
326
|
+
method: "delete",
|
|
327
|
+
path: keyPath,
|
|
328
|
+
middleware: [`${VALIDATE_FN}('params', ${paramsName})`],
|
|
329
|
+
resType: "boolean",
|
|
330
|
+
body: [VALID_PARAM_HINT, "res.json(true);"]
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
if (writable) {
|
|
335
|
+
routes.push({
|
|
336
|
+
name: "create",
|
|
337
|
+
method: "post",
|
|
338
|
+
path: "/",
|
|
339
|
+
middleware: ["json()", `${VALIDATE_FN}('body', ${insertName})`],
|
|
340
|
+
resType: rowType,
|
|
341
|
+
body: [notImplemented("create")]
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
if (opts.includeRelations) {
|
|
345
|
+
routes.push(...relationRoutes(table, lib, rowType, opts));
|
|
346
|
+
}
|
|
347
|
+
const order = ["list", "byId", "create", "update", "delete"];
|
|
348
|
+
const rank = (n) => order.indexOf(n) === -1 ? order.length : order.indexOf(n);
|
|
349
|
+
routes.sort((a, b) => rank(a.name) - rank(b.name));
|
|
350
|
+
const exportName = routesExportName(table, opts.naming);
|
|
351
|
+
const statements = routes.map((r) => {
|
|
352
|
+
const args = [lit(r.path), ...r.middleware].join(", ");
|
|
353
|
+
const reads = (what) => r.body.some((line) => !line.startsWith("//") && what.test(line));
|
|
354
|
+
const reqParam = reads(/\breq\./) ? "req" : "_req";
|
|
355
|
+
const resParam = reads(/\bres\./) ? "res" : "_res";
|
|
356
|
+
return [
|
|
357
|
+
`${exportName}.${r.method}(${args}, async (${reqParam}, ${resParam}: Response<${r.resType}>) => {`,
|
|
358
|
+
...r.body.map((line) => ` ${line}`),
|
|
359
|
+
`});`
|
|
360
|
+
].join("\n");
|
|
361
|
+
}).join("\n\n");
|
|
362
|
+
const body = `export const ${exportName} = Router();
|
|
363
|
+
|
|
364
|
+
${statements}
|
|
365
|
+
`;
|
|
366
|
+
const useShared = !!opts.validation?.useShared && !!opts.validation?.importPath;
|
|
367
|
+
const declared = [];
|
|
368
|
+
if (!useShared) {
|
|
369
|
+
if (writable) {
|
|
370
|
+
declared.push(`export const ${insertName} = ${renderSchema(table, lib, "insert")};`);
|
|
371
|
+
declared.push(`export const ${updateName} = ${renderSchema(table, lib, "update")};`);
|
|
372
|
+
}
|
|
373
|
+
declared.push(`export const ${selectName} = ${renderSchema(table, lib, "select")};`);
|
|
374
|
+
}
|
|
375
|
+
if (key) {
|
|
376
|
+
declared.push(
|
|
377
|
+
`export const ${paramsName} = ${d.objectInline(
|
|
378
|
+
key.map((c) => paramField(c, lib)).join(", ")
|
|
379
|
+
)};`
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
declared.push(`export type ${rowType} = ${d.infer(selectName)};`);
|
|
383
|
+
const decided = [...declared, body].join("\n\n");
|
|
384
|
+
const imports = [];
|
|
385
|
+
if (useShared) {
|
|
386
|
+
const sharedAffix = resolveAffix({
|
|
387
|
+
affix: opts.validation?.affix,
|
|
388
|
+
schemaSuffix: opts.validation?.schemaSuffix
|
|
389
|
+
});
|
|
390
|
+
const wanted = [
|
|
391
|
+
["insert", insertName],
|
|
392
|
+
["update", updateName],
|
|
393
|
+
["select", selectName]
|
|
394
|
+
].filter(([, local]) => decided.includes(local));
|
|
395
|
+
if (wanted.length) {
|
|
396
|
+
const spec = resolveConfiguredImport(
|
|
397
|
+
opts.validation.importPath,
|
|
398
|
+
ctx.out,
|
|
399
|
+
process.cwd(),
|
|
400
|
+
opts.importExtension
|
|
401
|
+
);
|
|
402
|
+
const names = wanted.map(([mode, local]) => {
|
|
403
|
+
const exported = schemaName(mode, table.tsName, sharedAffix);
|
|
404
|
+
return exported === local ? local : `${exported} as ${local}`;
|
|
405
|
+
}).join(", ");
|
|
406
|
+
imports.push(`import { ${names} } from '${spec}';`);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
const expressNames = ["Router"];
|
|
410
|
+
if (decided.includes("json()")) expressNames.push("json");
|
|
411
|
+
expressNames.push("type Response");
|
|
412
|
+
imports.push(`import { ${expressNames.join(", ")} } from 'express';`);
|
|
413
|
+
if (decided.includes(`${VALIDATE_FN}('`)) {
|
|
414
|
+
const spec = importSpecifier(`./${VALIDATION_MODULE}.ts`, opts.importExtension);
|
|
415
|
+
imports.push(`import { ${VALIDATE_FN} } from '${spec}';`);
|
|
416
|
+
}
|
|
417
|
+
if (LIB_USAGE[lib].test(decided)) imports.unshift(LIB_IMPORTS[lib]);
|
|
418
|
+
const wide = table.columns.filter(isWide).map((c) => c.name);
|
|
419
|
+
const wideNote = wide.length ? `// No validated type for ${wide.length === 1 ? "this column" : "these columns"}: ${wide.join(", ")}.
|
|
420
|
+
// DRZL could not derive one from the schema, so these routes accept any value there.
|
|
421
|
+
` : "";
|
|
422
|
+
return `// Generated by @drzl/generator-express
|
|
423
|
+
// Routes for table: ${table.name}
|
|
424
|
+
${wideNote}${imports.join("\n")}
|
|
425
|
+
|
|
426
|
+
${decided}`;
|
|
427
|
+
}
|
|
428
|
+
function relationRoutes(table, lib, rowType, opts) {
|
|
429
|
+
const d = LIBS[lib];
|
|
430
|
+
const out = [];
|
|
431
|
+
const taken = /* @__PURE__ */ new Set();
|
|
432
|
+
for (const fk of table.foreignKeys ?? []) {
|
|
433
|
+
if (fk.columns.length !== 1) continue;
|
|
434
|
+
const colName = fk.columns[0];
|
|
435
|
+
const column = table.columns.find((c) => c.name === colName);
|
|
436
|
+
if (!column) continue;
|
|
437
|
+
const segment = toCase(`by-${colName}`, opts.naming?.procedureCase ?? "kebab");
|
|
438
|
+
if (taken.has(segment)) continue;
|
|
439
|
+
taken.add(segment);
|
|
440
|
+
const inline = d.objectInline(paramField(column, lib));
|
|
441
|
+
out.push({
|
|
442
|
+
name: `listBy${cap(colName)}`,
|
|
443
|
+
method: "get",
|
|
444
|
+
path: `/${segment}/:${colName}`,
|
|
445
|
+
middleware: [`${VALIDATE_FN}('params', ${inline})`],
|
|
446
|
+
resType: `${rowType}[]`,
|
|
447
|
+
body: [VALID_PARAM_HINT, `const rows: ${rowType}[] = [];`, "res.json(rows);"]
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
return out;
|
|
451
|
+
}
|
|
452
|
+
function renderBarrel(modules, ctx, path, opts) {
|
|
453
|
+
if (!modules.length) {
|
|
454
|
+
return `// Generated by @drzl/generator-express
|
|
455
|
+
// No tables detected in analysis. Add tables to your schema and regenerate.
|
|
456
|
+
import express from 'express';
|
|
457
|
+
|
|
458
|
+
export const app = express();
|
|
459
|
+
`;
|
|
460
|
+
}
|
|
461
|
+
const entries = modules.map(({ filePath, exportName, table }) => ({
|
|
462
|
+
rel: importSpecifier(
|
|
463
|
+
"./" + path.relative(ctx.out, filePath).replace(/\\/g, "/"),
|
|
464
|
+
opts.importExtension
|
|
465
|
+
),
|
|
466
|
+
exportName,
|
|
467
|
+
mount: mountPath(table, opts.naming)
|
|
468
|
+
}));
|
|
469
|
+
const imports = entries.map((e) => `import { ${e.exportName} } from '${e.rel}';`).join("\n");
|
|
470
|
+
const mounts = entries.map((e) => `app.use(${lit(e.mount)}, ${e.exportName});`).join("\n");
|
|
471
|
+
const reExports = entries.map((e) => `export * from '${e.rel}';`).join("\n");
|
|
472
|
+
return `// Generated by @drzl/generator-express
|
|
473
|
+
import express from 'express';
|
|
474
|
+
${imports}
|
|
475
|
+
|
|
476
|
+
export const app = express();
|
|
477
|
+
|
|
478
|
+
${mounts}
|
|
479
|
+
|
|
480
|
+
${reExports}
|
|
481
|
+
`;
|
|
482
|
+
}
|
|
483
|
+
export {
|
|
484
|
+
APP_MODULE,
|
|
485
|
+
ExpressGenerator,
|
|
486
|
+
VALIDATION_MODULE,
|
|
487
|
+
index_default as default
|
|
488
|
+
};
|
|
489
|
+
//# sourceMappingURL=dist-T5376MW7.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../generator-express/dist/index.js"],"sourcesContent":["// src/index.ts\nimport { fileWriter } from \"@drzl/validation-core\";\nimport {\n formatCode,\n importSpecifier,\n resolveAffix,\n resolveConfiguredImport,\n schemaName\n} from \"@drzl/validation-core\";\nvar APP_MODULE = \"index\";\nvar VALIDATION_MODULE = \"validation\";\nvar q = (v) => JSON.stringify(v);\nvar lit = (v) => /['\\\\]/.test(v) ? JSON.stringify(v) : `'${v}'`;\nvar NUMERIC_SEGMENT = String.raw`/^-?\\d+(\\.\\d+)?$/`;\nvar LIB_IMPORTS = {\n zod: \"import { z } from 'zod';\",\n valibot: \"import * as v from 'valibot';\",\n arktype: \"import { type } from 'arktype';\"\n};\nvar LIB_USAGE = {\n zod: /\\bz\\./,\n valibot: /\\bv\\./,\n arktype: /\\btype\\(|\\.infer\\b/\n};\nvar LIBS = {\n zod: {\n number: \"z.number()\",\n string: \"z.string()\",\n boolean: \"z.boolean()\",\n date: \"z.date()\",\n unknown: \"z.unknown()\",\n enum: (vals) => `z.enum([${vals.map(q).join(\", \")}] as const)`,\n nullable: (b) => `${b}.nullable()`,\n optional: (b) => `${b}.optional()`,\n object: (body) => `z.object({\n${body}\n})`,\n objectInline: (body) => `z.object({ ${body} })`,\n partialUpdate: (s) => `${s}.partial()`,\n // Not `z.coerce.number()`, and not `z.coerce.date()`: both accept far more than a path\n // segment addressing a row should. See the grid referenced on `LibDialect.coerce`.\n coerce: (t) => t === \"number\" ? `z.string().regex(${NUMERIC_SEGMENT}).transform(Number)` : t === \"Date\" ? \"z.iso.datetime().transform((s) => new Date(s))\" : null,\n infer: (s) => `z.output<typeof ${s}>`\n },\n valibot: {\n number: \"v.number()\",\n string: \"v.string()\",\n boolean: \"v.boolean()\",\n date: \"v.date()\",\n unknown: \"v.unknown()\",\n enum: (vals) => `v.picklist([${vals.map(q).join(\", \")}] as const)`,\n nullable: (b) => `v.nullable(${b})`,\n optional: (b) => `v.optional(${b})`,\n object: (body) => `v.object({\n${body}\n})`,\n objectInline: (body) => `v.object({ ${body} })`,\n // A valibot pipe step sees the previous step's *output*, so the check has to happen while the\n // value is still the string: after a `v.transform(Number)` there is no string left to look at.\n coerce: (t) => t === \"number\" ? `v.pipe(v.string(), v.regex(${NUMERIC_SEGMENT}), v.transform(Number))` : t === \"Date\" ? \"v.pipe(v.string(), v.isoTimestamp(), v.transform((s) => new Date(s)))\" : null,\n infer: (s) => `v.InferOutput<typeof ${s}>`\n },\n arktype: {\n number: \"number\",\n string: \"string\",\n boolean: \"boolean\",\n date: \"Date\",\n unknown: \"unknown\",\n // The surrounding encode adds the quotes, so the union is built with the inner quoting\n // ArkType expects.\n enum: (vals) => vals.map((x) => `'${x.replace(/'/g, \"\\\\'\")}'`).join(\" | \"),\n nullable: (b) => `(${b} | null)`,\n optional: (b) => `${b}?`,\n object: (body) => `type({\n${body}\n})`,\n objectInline: (body) => `type({ ${body} })`,\n fieldIsString: true,\n // ArkType ships these as keywords, and they are morphs: the declared output type is `number`,\n // not `string`. Returned bare, because `fieldIsString` quotes every expression this dialect\n // produces, and a keyword returned pre-quoted arrives as a string *literal* type matching\n // nothing but that sentence. `string.date.iso.parse` and not `string.date.parse`, which\n // accepts `\"1\"` as the year 2001.\n coerce: (t) => t === \"number\" ? \"string.numeric.parse\" : t === \"Date\" ? \"string.date.iso.parse\" : null,\n infer: (s) => `typeof ${s}.infer`\n }\n};\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 isWide(column) {\n if (column.enumValues && column.enumValues.length) return false;\n if (column.shape?.kind === \"tuple\" || column.shape?.kind === \"numberObject\") return false;\n return ![\"number\", \"string\", \"boolean\", \"Date\"].includes(column.tsType);\n}\nfunction mapExpr(column, lib, mode) {\n const d = LIBS[lib];\n let base = (() => {\n if (column.enumValues && column.enumValues.length) return d.enum(column.enumValues);\n switch (column.tsType) {\n case \"number\":\n return d.number;\n case \"string\":\n return d.string;\n case \"boolean\":\n return d.boolean;\n case \"Date\":\n return d.date;\n default:\n return d.unknown;\n }\n })();\n if (column.nullable) base = d.nullable(base);\n if (mode !== \"select\") {\n const optional = mode === \"update\" || column.nullable || column.hasDefault;\n if (optional) base = d.optional(base);\n }\n return base;\n}\nfunction objectKey(name) {\n return isIdent(name) ? name : JSON.stringify(name);\n}\nfunction field(column, lib, mode) {\n const d = LIBS[lib];\n const expr = mapExpr(column, lib, mode);\n return `${objectKey(column.name)}: ${d.fieldIsString ? JSON.stringify(expr) : expr}`;\n}\nfunction paramField(column, lib) {\n const d = LIBS[lib];\n const expr = (() => {\n if (column.enumValues && column.enumValues.length) return d.enum(column.enumValues);\n return d.coerce(column.tsType) ?? d.string;\n })();\n return `${objectKey(column.name)}: ${d.fieldIsString ? JSON.stringify(expr) : expr}`;\n}\nfunction renderSchema(table, lib, mode) {\n const d = LIBS[lib];\n const cols = table.columns.filter((c) => mode === \"select\" ? true : !c.isGenerated);\n const body = cols.map((c) => ` ${field(c, lib, mode)},`).join(\"\\n\");\n const schema = d.object(body);\n return mode === \"update\" && d.partialUpdate ? d.partialUpdate(schema) : schema;\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 ExpressGenerator = 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 validationPath = path.join(out, `${VALIDATION_MODULE}.ts`);\n const modules = [];\n const total = this.analysis.tables.length;\n let index = 0;\n let anyValidates = false;\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 || filePath === validationPath) {\n const which = filePath === barrelPath ? \"the barrel\" : \"the validation middleware module\";\n throw new Error(\n `@drzl/generator-express: the routes for table \"${table.name}\" would be written to ${filePath}, which is ${which} this generator also writes. Set naming.routerSuffix to move it out of the way.`\n );\n }\n const source = renderRoutes(table, opts, ctx);\n if (source.includes(`${VALIDATE_FN}('`)) anyValidates = true;\n await write(filePath, source);\n modules.push({ table, filePath, exportName: routesExportName(table, opts.naming) });\n index++;\n opts.onProgress?.({ index, total, table: table.name, filePath });\n }\n if (anyValidates) {\n await write(validationPath, renderValidationModule());\n }\n await write(barrelPath, renderBarrel(modules, ctx, path, opts));\n return { files };\n }\n};\nvar index_default = ExpressGenerator;\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}\nvar VALIDATE_FN = \"validate\";\nvar VALID_PARAM_HINT = \"// validate() has already replaced req.params with the parsed values.\";\nfunction renderValidationModule() {\n return `// Generated by @drzl/generator-express\n// A validation middleware over Standard Schema v1, so the same routes accept zod, valibot or\n// arktype schemas. On failure it answers 400 with { error, slot, issues: [{ message, path }] };\n// on success it replaces req[slot] with the parsed output and calls next().\nimport type { RequestHandler } from 'express';\n\ninterface StandardIssue {\n readonly message: string;\n readonly path?: ReadonlyArray<PropertyKey | { readonly key: PropertyKey }> | undefined;\n}\n\ntype StandardResult<Output> =\n | { readonly value: Output; readonly issues?: undefined }\n | { readonly issues: ReadonlyArray<StandardIssue> };\n\nexport interface StandardSchema<Output = unknown> {\n readonly '~standard': {\n readonly version: 1;\n readonly vendor: string;\n readonly validate: (\n value: unknown\n ) => StandardResult<Output> | Promise<StandardResult<Output>>;\n };\n}\n\nexport function ${VALIDATE_FN}(slot: 'params' | 'body', schema: StandardSchema): RequestHandler {\n return async (req, res, next) => {\n const result = await schema['~standard'].validate(req[slot]);\n if (result.issues) {\n res.status(400).json({\n error: 'Validation failed',\n slot,\n issues: result.issues.map((issue) => ({\n message: issue.message,\n path: (issue.path ?? []).map((p) => (typeof p === 'object' && p !== null ? p.key : p)),\n })),\n });\n return;\n }\n req[slot] = result.value as never;\n next();\n };\n}\n`;\n}\nfunction renderRoutes(table, opts, ctx) {\n const lib = opts.validation?.library ?? \"zod\";\n const d = LIBS[lib];\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 routes.push({\n name: \"list\",\n method: \"get\",\n path: \"/\",\n middleware: [],\n resType: `${rowType}[]`,\n // The stub states its own contract twice over: the annotated local is what a reader sees,\n // and the annotated res parameter is what Express's own types hold the handler to. Nothing\n // infers a client from either, which the docs say plainly; the types are for the person\n // filling the stub in.\n body: [`const rows: ${rowType}[] = [];`, \"res.json(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 middleware: [`${VALIDATE_FN}('params', ${paramsName})`],\n resType: `${rowType} | null`,\n body: [VALID_PARAM_HINT, `const row: ${rowType} | null = null;`, \"res.json(row);\"]\n });\n if (writable) {\n routes.push({\n name: \"update\",\n method: \"patch\",\n path: keyPath,\n middleware: [\n `${VALIDATE_FN}('params', ${paramsName})`,\n \"json()\",\n `${VALIDATE_FN}('body', ${updateName})`\n ],\n resType: rowType,\n body: [notImplemented(\"update\")]\n });\n routes.push({\n name: \"delete\",\n method: \"delete\",\n path: keyPath,\n middleware: [`${VALIDATE_FN}('params', ${paramsName})`],\n resType: \"boolean\",\n body: [VALID_PARAM_HINT, \"res.json(true);\"]\n });\n }\n }\n if (writable) {\n routes.push({\n name: \"create\",\n method: \"post\",\n path: \"/\",\n middleware: [\"json()\", `${VALIDATE_FN}('body', ${insertName})`],\n resType: rowType,\n body: [notImplemented(\"create\")]\n });\n }\n if (opts.includeRelations) {\n routes.push(...relationRoutes(table, lib, rowType, 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 args = [lit(r.path), ...r.middleware].join(\", \");\n const reads = (what) => r.body.some((line) => !line.startsWith(\"//\") && what.test(line));\n const reqParam = reads(/\\breq\\./) ? \"req\" : \"_req\";\n const resParam = reads(/\\bres\\./) ? \"res\" : \"_res\";\n return [\n `${exportName}.${r.method}(${args}, async (${reqParam}, ${resParam}: Response<${r.resType}>) => {`,\n ...r.body.map((line) => ` ${line}`),\n `});`\n ].join(\"\\n\");\n }).join(\"\\n\\n\");\n const body = `export const ${exportName} = Router();\n\n${statements}\n`;\n const useShared = !!opts.validation?.useShared && !!opts.validation?.importPath;\n const declared = [];\n if (!useShared) {\n if (writable) {\n declared.push(`export const ${insertName} = ${renderSchema(table, lib, \"insert\")};`);\n declared.push(`export const ${updateName} = ${renderSchema(table, lib, \"update\")};`);\n }\n declared.push(`export const ${selectName} = ${renderSchema(table, lib, \"select\")};`);\n }\n if (key) {\n declared.push(\n `export const ${paramsName} = ${d.objectInline(\n key.map((c) => paramField(c, lib)).join(\", \")\n )};`\n );\n }\n declared.push(`export type ${rowType} = ${d.infer(selectName)};`);\n const decided = [...declared, body].join(\"\\n\\n\");\n const imports = [];\n if (useShared) {\n const sharedAffix = resolveAffix({\n affix: opts.validation?.affix,\n schemaSuffix: opts.validation?.schemaSuffix\n });\n const wanted = [\n [\"insert\", insertName],\n [\"update\", updateName],\n [\"select\", selectName]\n ].filter(([, local]) => decided.includes(local));\n if (wanted.length) {\n const spec = resolveConfiguredImport(\n opts.validation.importPath,\n ctx.out,\n process.cwd(),\n opts.importExtension\n );\n const names = wanted.map(([mode, local]) => {\n const exported = schemaName(mode, table.tsName, sharedAffix);\n return exported === local ? local : `${exported} as ${local}`;\n }).join(\", \");\n imports.push(`import { ${names} } from '${spec}';`);\n }\n }\n const expressNames = [\"Router\"];\n if (decided.includes(\"json()\")) expressNames.push(\"json\");\n expressNames.push(\"type Response\");\n imports.push(`import { ${expressNames.join(\", \")} } from 'express';`);\n if (decided.includes(`${VALIDATE_FN}('`)) {\n const spec = importSpecifier(`./${VALIDATION_MODULE}.ts`, opts.importExtension);\n imports.push(`import { ${VALIDATE_FN} } from '${spec}';`);\n }\n if (LIB_USAGE[lib].test(decided)) imports.unshift(LIB_IMPORTS[lib]);\n const wide = table.columns.filter(isWide).map((c) => c.name);\n const wideNote = wide.length ? `// No validated type for ${wide.length === 1 ? \"this column\" : \"these columns\"}: ${wide.join(\", \")}.\n// DRZL could not derive one from the schema, so these routes accept any value there.\n` : \"\";\n return `// Generated by @drzl/generator-express\n// Routes for table: ${table.name}\n${wideNote}${imports.join(\"\\n\")}\n\n${decided}`;\n}\nfunction relationRoutes(table, lib, rowType, opts) {\n const d = LIBS[lib];\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 inline = d.objectInline(paramField(column, lib));\n out.push({\n name: `listBy${cap(colName)}`,\n method: \"get\",\n path: `/${segment}/:${colName}`,\n middleware: [`${VALIDATE_FN}('params', ${inline})`],\n resType: `${rowType}[]`,\n body: [VALID_PARAM_HINT, `const rows: ${rowType}[] = [];`, \"res.json(rows);\"]\n });\n }\n return out;\n}\nfunction renderBarrel(modules, ctx, path, opts) {\n if (!modules.length) {\n return `// Generated by @drzl/generator-express\n// No tables detected in analysis. Add tables to your schema and regenerate.\nimport express from 'express';\n\nexport const app = express();\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 mounts = entries.map((e) => `app.use(${lit(e.mount)}, ${e.exportName});`).join(\"\\n\");\n const reExports = entries.map((e) => `export * from '${e.rel}';`).join(\"\\n\");\n return `// Generated by @drzl/generator-express\nimport express from 'express';\n${imports}\n\nexport const app = express();\n\n${mounts}\n\n${reExports}\n`;\n}\nexport {\n APP_MODULE,\n ExpressGenerator,\n VALIDATION_MODULE,\n index_default as default\n};\n"],"mappings":"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","names":[]}
|