@drzl/cli 4.17.0 → 4.19.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-I4KMDCQR.js → chunk-V2IXXAC2.js} +13 -2
- package/dist/chunk-V2IXXAC2.js.map +1 -0
- package/dist/cli.cjs +986 -95
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +406 -67
- package/dist/cli.js.map +1 -1
- package/dist/config.cjs +12 -1
- package/dist/config.cjs.map +1 -1
- package/dist/config.d.cts +4 -2
- package/dist/config.d.ts +4 -2
- package/dist/config.js +1 -1
- package/dist/dist-CZDVFYFW.js +559 -0
- package/dist/dist-CZDVFYFW.js.map +1 -0
- package/package.json +7 -6
- package/dist/chunk-I4KMDCQR.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -5,12 +5,12 @@ import {
|
|
|
5
5
|
filterTables,
|
|
6
6
|
loadConfig,
|
|
7
7
|
trpcOutDir
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-V2IXXAC2.js";
|
|
9
9
|
|
|
10
10
|
// src/cli.ts
|
|
11
11
|
import { SchemaAnalyzer } from "@drzl/analyzer";
|
|
12
12
|
import { ORPCGenerator } from "@drzl/generator-orpc";
|
|
13
|
-
import
|
|
13
|
+
import chalk3 from "chalk";
|
|
14
14
|
import chokidar from "chokidar";
|
|
15
15
|
import cliProgress from "cli-progress";
|
|
16
16
|
import { Command } from "commander";
|
|
@@ -76,6 +76,264 @@ function trpcOptions(g, cfg, servicesDir) {
|
|
|
76
76
|
};
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
// src/doctor.ts
|
|
80
|
+
import { parseCheck } from "@drzl/validation-core";
|
|
81
|
+
import chalk from "chalk";
|
|
82
|
+
var HANDLED_CODES = /* @__PURE__ */ new Set(["DRZL_ANL_UNKNOWN_COLUMN"]);
|
|
83
|
+
function splitPath(path5) {
|
|
84
|
+
if (!path5) return {};
|
|
85
|
+
const dot = path5.lastIndexOf(".");
|
|
86
|
+
if (dot <= 0) return { table: path5 };
|
|
87
|
+
return { table: path5.slice(0, dot), column: path5.slice(dot + 1) };
|
|
88
|
+
}
|
|
89
|
+
function namedColumns(parsed) {
|
|
90
|
+
const out = [];
|
|
91
|
+
for (const c of parsed.checks) out.push({ column: c.column, scalar: true });
|
|
92
|
+
for (const s of parsed.sets ?? []) out.push({ column: s.column, scalar: true });
|
|
93
|
+
for (const l of parsed.lengths ?? []) out.push({ column: l.column, scalar: false });
|
|
94
|
+
for (const c of parsed.cardinalities ?? []) out.push({ column: c.column, scalar: false });
|
|
95
|
+
for (const r of parsed.rows ?? []) {
|
|
96
|
+
out.push({ column: r.left, scalar: false });
|
|
97
|
+
out.push({ column: r.right, scalar: false });
|
|
98
|
+
}
|
|
99
|
+
return out;
|
|
100
|
+
}
|
|
101
|
+
function describeShape(c) {
|
|
102
|
+
if (c.arrayDimensions) return "an array";
|
|
103
|
+
switch (c.shape?.kind) {
|
|
104
|
+
case "json":
|
|
105
|
+
return "a JSON";
|
|
106
|
+
case "buffer":
|
|
107
|
+
return "a binary";
|
|
108
|
+
case "tuple":
|
|
109
|
+
case "numberObject":
|
|
110
|
+
return "a structured";
|
|
111
|
+
case "numberVector":
|
|
112
|
+
return "a vector";
|
|
113
|
+
case "bitstring":
|
|
114
|
+
return "a bit-string";
|
|
115
|
+
case "byteString":
|
|
116
|
+
return "a byte-string";
|
|
117
|
+
case "custom":
|
|
118
|
+
return "a customType";
|
|
119
|
+
default:
|
|
120
|
+
return "a structured";
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
function checkFindings(table) {
|
|
124
|
+
const out = [];
|
|
125
|
+
const byName = new Map(table.columns.map((c) => [c.name, c]));
|
|
126
|
+
for (const k of table.checks ?? []) {
|
|
127
|
+
const label = k.name ? `"${k.name}"` : "an unnamed constraint";
|
|
128
|
+
const raw = k.expression ?? "";
|
|
129
|
+
const expr = raw.trim() ? raw : "(empty)";
|
|
130
|
+
const parsed = parseCheck(raw, k.name);
|
|
131
|
+
if (!parsed.ok) {
|
|
132
|
+
out.push({
|
|
133
|
+
kind: "check-declined",
|
|
134
|
+
level: "warn",
|
|
135
|
+
table: table.tsName,
|
|
136
|
+
constraint: k.name,
|
|
137
|
+
message: `CHECK ${label} on "${table.tsName}" is not translated: ${parsed.reason}. Expression: ${expr}`,
|
|
138
|
+
hint: "Only constraints whose meaning is unambiguous are translated, because a validator enforcing a guess rejects rows the database accepts. Your database still enforces this one; nothing DRZL emits does."
|
|
139
|
+
});
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
const seen = /* @__PURE__ */ new Set();
|
|
143
|
+
for (const { column, scalar } of namedColumns(parsed)) {
|
|
144
|
+
if (seen.has(column)) continue;
|
|
145
|
+
seen.add(column);
|
|
146
|
+
const col = byName.get(column);
|
|
147
|
+
if (!col) {
|
|
148
|
+
out.push({
|
|
149
|
+
kind: "check-unknown-column",
|
|
150
|
+
level: "warn",
|
|
151
|
+
table: table.tsName,
|
|
152
|
+
column,
|
|
153
|
+
constraint: k.name,
|
|
154
|
+
message: `CHECK ${label} on "${table.tsName}" names "${column}", which is not a column of that table, so nothing enforces it. Expression: ${expr}`,
|
|
155
|
+
hint: "A constraint is attached to the field it names. Check the spelling, or move a constraint spanning two tables out of the schema."
|
|
156
|
+
});
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
if (scalar && (col.arrayDimensions || col.shape)) {
|
|
160
|
+
out.push({
|
|
161
|
+
kind: "check-not-scalar",
|
|
162
|
+
level: "warn",
|
|
163
|
+
table: table.tsName,
|
|
164
|
+
column,
|
|
165
|
+
constraint: k.name,
|
|
166
|
+
message: `CHECK ${label} on "${table.tsName}" compares ${describeShape(col)} column "${column}" against a scalar literal, which does not describe it, so it is not translated. Expression: ${expr}`,
|
|
167
|
+
hint: "On an array column only cardinality(col) is read, since it is the one comparison that is about the array rather than about an element."
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return out;
|
|
173
|
+
}
|
|
174
|
+
function primaryKeyFindings(table) {
|
|
175
|
+
if (table.readOnly) return [];
|
|
176
|
+
const pk = table.primaryKey?.columns ?? [];
|
|
177
|
+
if (!pk.length) {
|
|
178
|
+
const hasId = table.columns.some((c) => c.name === "id");
|
|
179
|
+
return [
|
|
180
|
+
{
|
|
181
|
+
kind: "no-primary-key",
|
|
182
|
+
level: "warn",
|
|
183
|
+
table: table.tsName,
|
|
184
|
+
message: hasId ? `Table "${table.tsName}" declares no primary key. The service and router generators fall back to a column named "id", which this table happens to have, so they work by coincidence.` : `Table "${table.tsName}" declares no primary key. The service and router generators fall back to a column named "id", which this table does not have, so the generated service will not compile.`,
|
|
185
|
+
hint: "Declare a primary key, or leave this table out with the config table filter."
|
|
186
|
+
}
|
|
187
|
+
];
|
|
188
|
+
}
|
|
189
|
+
if (pk.length > 1) {
|
|
190
|
+
return [
|
|
191
|
+
{
|
|
192
|
+
kind: "partial-primary-key",
|
|
193
|
+
level: "warn",
|
|
194
|
+
table: table.tsName,
|
|
195
|
+
message: `Table "${table.tsName}" has a composite primary key (${pk.join(", ")}). The service and router generators key getById, update and delete on "${pk[0]}" alone, so those operations match on part of the key.`,
|
|
196
|
+
hint: "Treat the generated service as a starting point for this table and widen the key by hand."
|
|
197
|
+
}
|
|
198
|
+
];
|
|
199
|
+
}
|
|
200
|
+
return [];
|
|
201
|
+
}
|
|
202
|
+
function buildDoctorReport(analysis, schemaPath) {
|
|
203
|
+
const findings = [];
|
|
204
|
+
const errors = analysis.issues.filter((i) => i.level === "error");
|
|
205
|
+
for (const i of errors) {
|
|
206
|
+
findings.push({
|
|
207
|
+
kind: "analyzer",
|
|
208
|
+
level: "error",
|
|
209
|
+
...splitPath(i.path),
|
|
210
|
+
message: i.message,
|
|
211
|
+
hint: i.hint
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
for (const i of analysis.issues) {
|
|
215
|
+
if (i.code !== "DRZL_ANL_UNKNOWN_COLUMN") continue;
|
|
216
|
+
findings.push({
|
|
217
|
+
kind: "unknown-column",
|
|
218
|
+
level: "warn",
|
|
219
|
+
...splitPath(i.path),
|
|
220
|
+
message: i.message,
|
|
221
|
+
hint: i.hint
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
for (const t of analysis.tables) findings.push(...checkFindings(t));
|
|
225
|
+
for (const t of analysis.tables) findings.push(...primaryKeyFindings(t));
|
|
226
|
+
for (const i of analysis.issues) {
|
|
227
|
+
if (i.level === "error" || HANDLED_CODES.has(i.code)) continue;
|
|
228
|
+
findings.push({
|
|
229
|
+
kind: "analyzer",
|
|
230
|
+
level: "warn",
|
|
231
|
+
...splitPath(i.path),
|
|
232
|
+
message: i.message,
|
|
233
|
+
hint: i.hint
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
const columns = analysis.tables.reduce((n, t) => n + t.columns.length, 0);
|
|
237
|
+
const checks = analysis.tables.reduce((n, t) => n + (t.checks?.length ?? 0), 0);
|
|
238
|
+
return {
|
|
239
|
+
schema: schemaPath,
|
|
240
|
+
dialect: analysis.dialect,
|
|
241
|
+
ok: findings.length === 0,
|
|
242
|
+
counts: { tables: analysis.tables.length, columns, checks, findings: findings.length },
|
|
243
|
+
findings
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
var SECTIONS = [
|
|
247
|
+
{
|
|
248
|
+
kinds: ["unknown-column"],
|
|
249
|
+
title: "Columns DRZL cannot type",
|
|
250
|
+
why: "These get a validator that accepts any value."
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
kinds: ["check-declined", "check-unknown-column", "check-not-scalar"],
|
|
254
|
+
title: "CHECK constraints DRZL does not enforce",
|
|
255
|
+
why: "Your database still enforces these. Nothing DRZL generates does."
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
kinds: ["no-primary-key", "partial-primary-key"],
|
|
259
|
+
title: "Primary keys the generators cannot use",
|
|
260
|
+
why: "The generated getById, update and delete are keyed on one column."
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
kinds: ["analyzer"],
|
|
264
|
+
title: "Other findings",
|
|
265
|
+
why: "Reported by the analyzer while reading the schema."
|
|
266
|
+
}
|
|
267
|
+
];
|
|
268
|
+
function wrap(text, indent, first = indent, width = 96) {
|
|
269
|
+
const lines = [];
|
|
270
|
+
let line = "";
|
|
271
|
+
for (const word of text.split(/\s+/)) {
|
|
272
|
+
if (line && `${line} ${word}`.length + indent.length > width) {
|
|
273
|
+
lines.push(line);
|
|
274
|
+
line = word;
|
|
275
|
+
} else {
|
|
276
|
+
line = line ? `${line} ${word}` : word;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
if (line) lines.push(line);
|
|
280
|
+
return lines.map((l, i) => (i === 0 ? first : indent) + l).join("\n");
|
|
281
|
+
}
|
|
282
|
+
function renderDoctorReport(report) {
|
|
283
|
+
const out = [];
|
|
284
|
+
const plural = (n, one) => `${n} ${one}${n === 1 ? "" : "s"}`;
|
|
285
|
+
out.push(chalk.bold(`DRZL doctor ${report.schema}`));
|
|
286
|
+
out.push(
|
|
287
|
+
chalk.dim(
|
|
288
|
+
`${report.dialect}, ${plural(report.counts.tables, "table")}, ${plural(report.counts.columns, "column")}, ${plural(report.counts.checks, "CHECK constraint")}`
|
|
289
|
+
)
|
|
290
|
+
);
|
|
291
|
+
out.push("");
|
|
292
|
+
if (report.ok) {
|
|
293
|
+
out.push(chalk.green("Nothing to report."));
|
|
294
|
+
out.push(chalk.dim(" Every column has a type DRZL can describe."));
|
|
295
|
+
out.push(chalk.dim(" Every CHECK constraint is translated into the generated validators."));
|
|
296
|
+
out.push(chalk.dim(" Every table has a primary key the generators can use."));
|
|
297
|
+
return out.join("\n");
|
|
298
|
+
}
|
|
299
|
+
const fatal = report.findings.filter((f) => f.level === "error");
|
|
300
|
+
if (fatal.length) {
|
|
301
|
+
out.push(chalk.red("DRZL could not read this schema"));
|
|
302
|
+
out.push(chalk.dim(" Nothing else could be checked."));
|
|
303
|
+
out.push("");
|
|
304
|
+
for (const f of fatal) {
|
|
305
|
+
out.push(wrap(f.message, " ", ` ${chalk.dim("-")} `));
|
|
306
|
+
if (f.hint) out.push(chalk.dim(wrap(f.hint, " ")));
|
|
307
|
+
}
|
|
308
|
+
out.push("");
|
|
309
|
+
}
|
|
310
|
+
for (const section of SECTIONS) {
|
|
311
|
+
const mine = report.findings.filter(
|
|
312
|
+
(f) => f.level !== "error" && section.kinds.includes(f.kind)
|
|
313
|
+
);
|
|
314
|
+
if (!mine.length) continue;
|
|
315
|
+
out.push(chalk.yellow(`${section.title} (${mine.length})`));
|
|
316
|
+
out.push(chalk.dim(` ${section.why}`));
|
|
317
|
+
out.push("");
|
|
318
|
+
const groups = /* @__PURE__ */ new Map();
|
|
319
|
+
for (const f of mine) {
|
|
320
|
+
const key = f.hint ?? "";
|
|
321
|
+
groups.set(key, [...groups.get(key) ?? [], f]);
|
|
322
|
+
}
|
|
323
|
+
for (const [hint, items] of groups) {
|
|
324
|
+
for (const f of items) out.push(wrap(f.message, " ", ` ${chalk.dim("-")} `));
|
|
325
|
+
if (hint) out.push(chalk.dim(wrap(hint, " ")));
|
|
326
|
+
out.push("");
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
out.push(
|
|
330
|
+
chalk.bold(`${plural(report.counts.findings, "finding")} in ${report.schema}.`) + chalk.dim(
|
|
331
|
+
fatal.length ? " Fix the error above and run this again." : " None of these stop DRZL generating; they are what it will not check for you."
|
|
332
|
+
)
|
|
333
|
+
);
|
|
334
|
+
return out.join("\n");
|
|
335
|
+
}
|
|
336
|
+
|
|
79
337
|
// src/drift.ts
|
|
80
338
|
import { promises as fs } from "fs";
|
|
81
339
|
import path from "path";
|
|
@@ -152,7 +410,7 @@ async function loadGenerator(specifier, load) {
|
|
|
152
410
|
}
|
|
153
411
|
|
|
154
412
|
// src/sponsor.ts
|
|
155
|
-
import
|
|
413
|
+
import chalk2 from "chalk";
|
|
156
414
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
157
415
|
import path2 from "path";
|
|
158
416
|
var CACHE_DIR = path2.join(process.cwd(), "node_modules", ".cache", "@drzl");
|
|
@@ -162,13 +420,13 @@ var shownThisProcess = false;
|
|
|
162
420
|
var tips = [
|
|
163
421
|
"Pair DRZL watch mode with drizzle-kit to keep schema & API synced.",
|
|
164
422
|
"Templatize your ORPC routers to roll out new endpoints safely.",
|
|
165
|
-
"Need typed validators? Enable the zod, valibot, arktype, or
|
|
423
|
+
"Need typed validators? Enable the zod, valibot, arktype, typebox, or effect generators.",
|
|
166
424
|
"Need JSON Schema or OpenAPI? The json-schema generator emits both, with no runtime dependency.",
|
|
167
425
|
"Use output headers to track generated files and trim noisy diffs."
|
|
168
426
|
];
|
|
169
|
-
var green = (msg) =>
|
|
170
|
-
var cyan = (msg) =>
|
|
171
|
-
var gray = (msg) =>
|
|
427
|
+
var green = (msg) => chalk2.hex("#6ee7b7")(msg);
|
|
428
|
+
var cyan = (msg) => chalk2.cyan(msg);
|
|
429
|
+
var gray = (msg) => chalk2.gray(msg);
|
|
172
430
|
function maybeShowSponsorMessage({
|
|
173
431
|
reason = "generate",
|
|
174
432
|
minIntervalMs = DEFAULT_INTERVAL_MS,
|
|
@@ -257,13 +515,13 @@ var CLI_VERSION = readCliVersion();
|
|
|
257
515
|
function reportGeneratorFailure(kind, e) {
|
|
258
516
|
if (e instanceof GeneratorNotInstalledError) {
|
|
259
517
|
console.error(
|
|
260
|
-
|
|
261
|
-
|
|
518
|
+
chalk3.red(`The ${kind} generator is not installed.`),
|
|
519
|
+
chalk3.yellow(`
|
|
262
520
|
Install with: npm install ${e.specifier}`)
|
|
263
521
|
);
|
|
264
522
|
return;
|
|
265
523
|
}
|
|
266
|
-
console.error(
|
|
524
|
+
console.error(chalk3.red(`The ${kind} generator failed:`), e?.message ?? e);
|
|
267
525
|
}
|
|
268
526
|
var program = new Command();
|
|
269
527
|
program.name("drzl").description("DRZL - Drizzle Developer Toolkit").version(CLI_VERSION);
|
|
@@ -290,9 +548,9 @@ program.command("analyze").argument("<schema>", "path to drizzle schema (TS)").o
|
|
|
290
548
|
} else if (opts.out) {
|
|
291
549
|
const fs2 = await import("fs/promises");
|
|
292
550
|
await fs2.writeFile(opts.out, json, "utf8");
|
|
293
|
-
spinner?.succeed(
|
|
551
|
+
spinner?.succeed(chalk3.green(`Analysis written to ${opts.out} in ${ms}ms`));
|
|
294
552
|
} else {
|
|
295
|
-
spinner?.succeed(
|
|
553
|
+
spinner?.succeed(chalk3.green(`Analyzed in ${ms}ms`));
|
|
296
554
|
console.log(json);
|
|
297
555
|
}
|
|
298
556
|
process.exit(res.issues.some((i) => i.level === "error") ? 2 : 0);
|
|
@@ -302,7 +560,48 @@ program.command("analyze").argument("<schema>", "path to drizzle schema (TS)").o
|
|
|
302
560
|
console.log(JSON.stringify({ event: "error", code: "DRZL_CLI_ANALYZE", message: msg }));
|
|
303
561
|
else
|
|
304
562
|
console.error(
|
|
305
|
-
|
|
563
|
+
chalk3.red("Analyze failed (DRZL_CLI_ANALYZE):"),
|
|
564
|
+
msg,
|
|
565
|
+
"\nTip: run with --json for structured output."
|
|
566
|
+
);
|
|
567
|
+
process.exit(1);
|
|
568
|
+
}
|
|
569
|
+
});
|
|
570
|
+
program.command("doctor").description("Report what DRZL cannot type or enforce in your schema, and why").argument("[schema]", "path to drizzle schema (TS); defaults to the schema in drzl.config").option("-c, --config <path>", "path to drzl.config, read when no schema argument is given").option("--json", "print the report as JSON instead of prose", false).option("--strict", "exit 2 when anything is reported", false).action(async (schema, opts) => {
|
|
571
|
+
try {
|
|
572
|
+
let target = schema;
|
|
573
|
+
if (!target) {
|
|
574
|
+
const cfg = await loadConfig(opts.config);
|
|
575
|
+
target = cfg?.schema;
|
|
576
|
+
}
|
|
577
|
+
if (!target) {
|
|
578
|
+
const msg = "No schema given. Pass a path, or run from a directory with a drzl.config.";
|
|
579
|
+
if (opts.json)
|
|
580
|
+
console.log(JSON.stringify({ event: "error", code: "DRZL_CLI_DOCTOR", message: msg }));
|
|
581
|
+
else console.error(chalk3.red("Doctor failed (DRZL_CLI_DOCTOR):"), msg);
|
|
582
|
+
process.exit(1);
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
const analyzer = new SchemaAnalyzer(target);
|
|
586
|
+
const analysis = await analyzer.analyze({
|
|
587
|
+
includeRelations: true,
|
|
588
|
+
validateConstraints: true
|
|
589
|
+
});
|
|
590
|
+
const report = buildDoctorReport(analysis, target);
|
|
591
|
+
if (opts.json) console.log(JSON.stringify(report, null, 2));
|
|
592
|
+
else console.log(renderDoctorReport(report));
|
|
593
|
+
if (report.findings.some((f) => f.level === "error")) {
|
|
594
|
+
process.exit(1);
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
process.exit(opts.strict && report.findings.length ? 2 : 0);
|
|
598
|
+
} catch (e) {
|
|
599
|
+
const msg = e?.message ?? String(e);
|
|
600
|
+
if (opts.json)
|
|
601
|
+
console.log(JSON.stringify({ event: "error", code: "DRZL_CLI_DOCTOR", message: msg }));
|
|
602
|
+
else
|
|
603
|
+
console.error(
|
|
604
|
+
chalk3.red("Doctor failed (DRZL_CLI_DOCTOR):"),
|
|
306
605
|
msg,
|
|
307
606
|
"\nTip: run with --json for structured output."
|
|
308
607
|
);
|
|
@@ -317,7 +616,7 @@ program.command("generate").description("Run configured generators (drzl.config.
|
|
|
317
616
|
const cfg = await loadConfig(opts.config);
|
|
318
617
|
if (!cfg) {
|
|
319
618
|
console.error(
|
|
320
|
-
|
|
619
|
+
chalk3.red("No config found (DRZL_CFG_001). Create drzl.config.ts or pass --config.")
|
|
321
620
|
);
|
|
322
621
|
process.exit(2);
|
|
323
622
|
return;
|
|
@@ -362,8 +661,8 @@ program.command("generate").description("Run configured generators (drzl.config.
|
|
|
362
661
|
onProgress: ({ index }) => progress.update(index)
|
|
363
662
|
});
|
|
364
663
|
progress.stop();
|
|
365
|
-
ora().succeed(
|
|
366
|
-
files.forEach((f) => console.log(" -",
|
|
664
|
+
ora().succeed(chalk3.green(`Generated (${g.kind}): ${files.length} files`));
|
|
665
|
+
files.forEach((f) => console.log(" -", chalk3.cyan(f)));
|
|
367
666
|
} else if (g.kind === "trpc") {
|
|
368
667
|
try {
|
|
369
668
|
const { TRPCGenerator } = await loadGenerator(
|
|
@@ -376,8 +675,8 @@ program.command("generate").description("Run configured generators (drzl.config.
|
|
|
376
675
|
onProgress: ({ index }) => progress.update(index)
|
|
377
676
|
});
|
|
378
677
|
progress.stop();
|
|
379
|
-
ora().succeed(
|
|
380
|
-
files.forEach((f) => console.log(" -",
|
|
678
|
+
ora().succeed(chalk3.green(`Generated (trpc): ${files.length} files`));
|
|
679
|
+
files.forEach((f) => console.log(" -", chalk3.cyan(f)));
|
|
381
680
|
} catch (e) {
|
|
382
681
|
progress.stop();
|
|
383
682
|
reportGeneratorFailure(g.kind, e);
|
|
@@ -406,8 +705,8 @@ program.command("generate").description("Run configured generators (drzl.config.
|
|
|
406
705
|
databaseInjection: g.databaseInjection
|
|
407
706
|
});
|
|
408
707
|
progress.stop();
|
|
409
|
-
ora().succeed(
|
|
410
|
-
files.forEach((f) => console.log(" -",
|
|
708
|
+
ora().succeed(chalk3.green(`Generated (service): ${files.length} files`));
|
|
709
|
+
files.forEach((f) => console.log(" -", chalk3.cyan(f)));
|
|
411
710
|
} catch (e) {
|
|
412
711
|
progress.stop();
|
|
413
712
|
reportGeneratorFailure(g.kind, e);
|
|
@@ -425,8 +724,8 @@ program.command("generate").description("Run configured generators (drzl.config.
|
|
|
425
724
|
validationOptions(g, cfg, target, { schemaTypes: true })
|
|
426
725
|
);
|
|
427
726
|
progress.stop();
|
|
428
|
-
ora().succeed(
|
|
429
|
-
files.forEach((f) => console.log(" -",
|
|
727
|
+
ora().succeed(chalk3.green(`Generated (zod): ${files.length} files`));
|
|
728
|
+
files.forEach((f) => console.log(" -", chalk3.cyan(f)));
|
|
430
729
|
} catch (e) {
|
|
431
730
|
progress.stop();
|
|
432
731
|
reportGeneratorFailure(g.kind, e);
|
|
@@ -444,8 +743,8 @@ program.command("generate").description("Run configured generators (drzl.config.
|
|
|
444
743
|
validationOptions(g, cfg, target, { schemaTypes: true })
|
|
445
744
|
);
|
|
446
745
|
progress.stop();
|
|
447
|
-
ora().succeed(
|
|
448
|
-
files.forEach((f) => console.log(" -",
|
|
746
|
+
ora().succeed(chalk3.green(`Generated (valibot): ${files.length} files`));
|
|
747
|
+
files.forEach((f) => console.log(" -", chalk3.cyan(f)));
|
|
449
748
|
} catch (e) {
|
|
450
749
|
progress.stop();
|
|
451
750
|
reportGeneratorFailure(g.kind, e);
|
|
@@ -463,8 +762,8 @@ program.command("generate").description("Run configured generators (drzl.config.
|
|
|
463
762
|
validationOptions(g, cfg, target, { schemaTypes: false })
|
|
464
763
|
);
|
|
465
764
|
progress.stop();
|
|
466
|
-
ora().succeed(
|
|
467
|
-
files.forEach((f) => console.log(" -",
|
|
765
|
+
ora().succeed(chalk3.green(`Generated (arktype): ${files.length} files`));
|
|
766
|
+
files.forEach((f) => console.log(" -", chalk3.cyan(f)));
|
|
468
767
|
} catch (e) {
|
|
469
768
|
progress.stop();
|
|
470
769
|
reportGeneratorFailure(g.kind, e);
|
|
@@ -480,8 +779,8 @@ program.command("generate").description("Run configured generators (drzl.config.
|
|
|
480
779
|
const target = g.path ?? "src/validators/json-schema";
|
|
481
780
|
const files = await gen.generate(jsonSchemaOptions(g, cfg, target));
|
|
482
781
|
progress.stop();
|
|
483
|
-
ora().succeed(
|
|
484
|
-
files.forEach((f) => console.log(" -",
|
|
782
|
+
ora().succeed(chalk3.green(`Generated (json-schema): ${files.length} files`));
|
|
783
|
+
files.forEach((f) => console.log(" -", chalk3.cyan(f)));
|
|
485
784
|
} catch (e) {
|
|
486
785
|
progress.stop();
|
|
487
786
|
reportGeneratorFailure(g.kind, e);
|
|
@@ -499,8 +798,27 @@ program.command("generate").description("Run configured generators (drzl.config.
|
|
|
499
798
|
validationOptions(g, cfg, target, { schemaTypes: true })
|
|
500
799
|
);
|
|
501
800
|
progress.stop();
|
|
502
|
-
ora().succeed(
|
|
503
|
-
files.forEach((f) => console.log(" -",
|
|
801
|
+
ora().succeed(chalk3.green(`Generated (typebox): ${files.length} files`));
|
|
802
|
+
files.forEach((f) => console.log(" -", chalk3.cyan(f)));
|
|
803
|
+
} catch (e) {
|
|
804
|
+
progress.stop();
|
|
805
|
+
reportGeneratorFailure(g.kind, e);
|
|
806
|
+
process.exit(1);
|
|
807
|
+
}
|
|
808
|
+
} else if (g.kind === "effect") {
|
|
809
|
+
try {
|
|
810
|
+
const { EffectGenerator } = await loadGenerator(
|
|
811
|
+
"@drzl/generator-effect",
|
|
812
|
+
() => import("./dist-CZDVFYFW.js")
|
|
813
|
+
);
|
|
814
|
+
const gen = new EffectGenerator(analysis);
|
|
815
|
+
const target = g.path ?? "src/validators/effect";
|
|
816
|
+
const files = await gen.generate(
|
|
817
|
+
validationOptions(g, cfg, target, { schemaTypes: true })
|
|
818
|
+
);
|
|
819
|
+
progress.stop();
|
|
820
|
+
ora().succeed(chalk3.green(`Generated (effect): ${files.length} files`));
|
|
821
|
+
files.forEach((f) => console.log(" -", chalk3.cyan(f)));
|
|
504
822
|
} catch (e) {
|
|
505
823
|
progress.stop();
|
|
506
824
|
reportGeneratorFailure(g.kind, e);
|
|
@@ -513,22 +831,22 @@ program.command("generate").description("Run configured generators (drzl.config.
|
|
|
513
831
|
const drift = diffSnapshots(driftBefore, after);
|
|
514
832
|
await restoreSnapshot(driftBefore, after);
|
|
515
833
|
if (drift.length) {
|
|
516
|
-
console.error(
|
|
834
|
+
console.error(chalk3.red(`
|
|
517
835
|
Generated output is out of date (${drift.length} file(s)):`));
|
|
518
836
|
for (const d of drift) {
|
|
519
837
|
const mark = d.status === "added" ? "+" : d.status === "removed" ? "-" : "~";
|
|
520
838
|
console.error(
|
|
521
|
-
` ${mark} ${
|
|
839
|
+
` ${mark} ${chalk3.yellow(d.status.padEnd(8))} ${path4.relative(process.cwd(), d.file)}`
|
|
522
840
|
);
|
|
523
841
|
}
|
|
524
842
|
console.error(
|
|
525
|
-
|
|
843
|
+
chalk3.dim(
|
|
526
844
|
"\nRun `drzl generate` and commit the result. Nothing was written by this check."
|
|
527
845
|
)
|
|
528
846
|
);
|
|
529
847
|
process.exit(1);
|
|
530
848
|
}
|
|
531
|
-
console.log(
|
|
849
|
+
console.log(chalk3.green("Generated output is up to date."));
|
|
532
850
|
return;
|
|
533
851
|
}
|
|
534
852
|
if (cfg.generators.length) {
|
|
@@ -536,7 +854,7 @@ Generated output is out of date (${drift.length} file(s)):`));
|
|
|
536
854
|
}
|
|
537
855
|
} catch (e) {
|
|
538
856
|
console.error(
|
|
539
|
-
|
|
857
|
+
chalk3.red("Generate failed (DRZL_GEN_001):"),
|
|
540
858
|
e?.message ?? e,
|
|
541
859
|
"\nTip: check your drzl.config.ts and template path."
|
|
542
860
|
);
|
|
@@ -556,10 +874,10 @@ program.command("generate:orpc").argument("<schema>", "path to drizzle schema (T
|
|
|
556
874
|
template: opts.template,
|
|
557
875
|
includeRelations: !!opts.includeRelations
|
|
558
876
|
});
|
|
559
|
-
console.log(
|
|
877
|
+
console.log(chalk3.green(`Generated:`), files.map((f) => chalk3.cyan(f)).join(", "));
|
|
560
878
|
maybeShowSponsorMessage({ reason: "generate:orpc" });
|
|
561
879
|
} catch (e) {
|
|
562
|
-
console.error(
|
|
880
|
+
console.error(chalk3.red("Generate orpc failed:"), e?.message ?? e);
|
|
563
881
|
process.exit(1);
|
|
564
882
|
}
|
|
565
883
|
});
|
|
@@ -583,7 +901,7 @@ program.command("generate:trpc").argument("<schema>", "path to drizzle schema (T
|
|
|
583
901
|
// cannot become the branch that forgets it.
|
|
584
902
|
servicesDir: opts.servicesDir
|
|
585
903
|
});
|
|
586
|
-
console.log(
|
|
904
|
+
console.log(chalk3.green(`Generated:`), files.map((f) => chalk3.cyan(f)).join(", "));
|
|
587
905
|
maybeShowSponsorMessage({ reason: "generate:trpc" });
|
|
588
906
|
} catch (e) {
|
|
589
907
|
reportGeneratorFailure("trpc", e);
|
|
@@ -593,7 +911,7 @@ program.command("generate:trpc").argument("<schema>", "path to drizzle schema (T
|
|
|
593
911
|
program.command("watch").description("Watch schema and regenerate on changes").option("-c, --config <path>", "path to drzl.config").option("--pipeline <name>", "all | analyze | generate-orpc | generate-trpc", "all").option("--debounce <ms>", "debounce ms", "200").option("--json", "emit JSON logs", false).option("--poll", "force polling (helps WSL/Docker/remote FS)", false).action(async (opts) => {
|
|
594
912
|
let cfg = await loadConfig(opts.config);
|
|
595
913
|
if (!cfg) {
|
|
596
|
-
console.error(
|
|
914
|
+
console.error(chalk3.red("No config found. Create drzl.config.ts or pass --config."));
|
|
597
915
|
process.exit(2);
|
|
598
916
|
return;
|
|
599
917
|
}
|
|
@@ -685,7 +1003,7 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
685
1003
|
})
|
|
686
1004
|
);
|
|
687
1005
|
} else {
|
|
688
|
-
console.log(
|
|
1006
|
+
console.log(chalk3.green("Analyze complete."));
|
|
689
1007
|
}
|
|
690
1008
|
return;
|
|
691
1009
|
}
|
|
@@ -715,8 +1033,8 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
715
1033
|
servicesDir
|
|
716
1034
|
});
|
|
717
1035
|
opts.json ? console.log(JSON.stringify({ event: "generate_complete", kind: g.kind, files })) : console.log(
|
|
718
|
-
|
|
719
|
-
files.map((f) =>
|
|
1036
|
+
chalk3.green(`Generated (${g.kind}):`),
|
|
1037
|
+
files.map((f) => chalk3.cyan(f)).join(", ")
|
|
720
1038
|
);
|
|
721
1039
|
newFiles.push(...files);
|
|
722
1040
|
} else if (g.kind === "trpc") {
|
|
@@ -728,8 +1046,8 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
728
1046
|
const gen = new TRPCGenerator(analysis);
|
|
729
1047
|
const { files } = await gen.generate(trpcOptions(g, cfg, servicesDir));
|
|
730
1048
|
opts.json ? console.log(JSON.stringify({ event: "generate_complete", kind: g.kind, files })) : console.log(
|
|
731
|
-
|
|
732
|
-
files.map((f) =>
|
|
1049
|
+
chalk3.green(`Generated (trpc): ${files.length} files`),
|
|
1050
|
+
files.map((f) => chalk3.cyan(f)).join(", ")
|
|
733
1051
|
);
|
|
734
1052
|
newFiles.push(...files);
|
|
735
1053
|
} catch (e) {
|
|
@@ -755,8 +1073,8 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
755
1073
|
databaseInjection: g.databaseInjection
|
|
756
1074
|
});
|
|
757
1075
|
opts.json ? console.log(JSON.stringify({ event: "generate_complete", kind: g.kind, files })) : console.log(
|
|
758
|
-
|
|
759
|
-
files.map((f) =>
|
|
1076
|
+
chalk3.green(`Generated (service): ${files.length} files`),
|
|
1077
|
+
files.map((f) => chalk3.cyan(f)).join(", ")
|
|
760
1078
|
);
|
|
761
1079
|
newFiles.push(...files);
|
|
762
1080
|
} catch (e) {
|
|
@@ -775,8 +1093,8 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
775
1093
|
validationOptions(g, cfg, target, { schemaTypes: true })
|
|
776
1094
|
);
|
|
777
1095
|
opts.json ? console.log(JSON.stringify({ event: "generate_complete", kind: g.kind, files })) : console.log(
|
|
778
|
-
|
|
779
|
-
files.map((f) =>
|
|
1096
|
+
chalk3.green(`Generated (zod): ${files.length} files`),
|
|
1097
|
+
files.map((f) => chalk3.cyan(f)).join(", ")
|
|
780
1098
|
);
|
|
781
1099
|
newFiles.push(...files);
|
|
782
1100
|
} catch (e) {
|
|
@@ -795,8 +1113,8 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
795
1113
|
validationOptions(g, cfg, target, { schemaTypes: true })
|
|
796
1114
|
);
|
|
797
1115
|
opts.json ? console.log(JSON.stringify({ event: "generate_complete", kind: g.kind, files })) : console.log(
|
|
798
|
-
|
|
799
|
-
files.map((f) =>
|
|
1116
|
+
chalk3.green(`Generated (valibot): ${files.length} files`),
|
|
1117
|
+
files.map((f) => chalk3.cyan(f)).join(", ")
|
|
800
1118
|
);
|
|
801
1119
|
newFiles.push(...files);
|
|
802
1120
|
} catch (e) {
|
|
@@ -815,8 +1133,8 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
815
1133
|
validationOptions(g, cfg, target, { schemaTypes: false })
|
|
816
1134
|
);
|
|
817
1135
|
opts.json ? console.log(JSON.stringify({ event: "generate_complete", kind: g.kind, files })) : console.log(
|
|
818
|
-
|
|
819
|
-
files.map((f) =>
|
|
1136
|
+
chalk3.green(`Generated (arktype): ${files.length} files`),
|
|
1137
|
+
files.map((f) => chalk3.cyan(f)).join(", ")
|
|
820
1138
|
);
|
|
821
1139
|
newFiles.push(...files);
|
|
822
1140
|
} catch (e) {
|
|
@@ -835,8 +1153,28 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
835
1153
|
validationOptions(g, cfg, target, { schemaTypes: true })
|
|
836
1154
|
);
|
|
837
1155
|
opts.json ? console.log(JSON.stringify({ event: "generate_complete", kind: g.kind, files })) : console.log(
|
|
838
|
-
|
|
839
|
-
files.map((f) =>
|
|
1156
|
+
chalk3.green(`Generated (typebox): ${files.length} files`),
|
|
1157
|
+
files.map((f) => chalk3.cyan(f)).join(", ")
|
|
1158
|
+
);
|
|
1159
|
+
newFiles.push(...files);
|
|
1160
|
+
} catch (e) {
|
|
1161
|
+
reportGeneratorFailure(g.kind, e);
|
|
1162
|
+
return;
|
|
1163
|
+
}
|
|
1164
|
+
} else if (g.kind === "effect") {
|
|
1165
|
+
try {
|
|
1166
|
+
const { EffectGenerator } = await loadGenerator(
|
|
1167
|
+
"@drzl/generator-effect",
|
|
1168
|
+
() => import("./dist-CZDVFYFW.js")
|
|
1169
|
+
);
|
|
1170
|
+
const gen = new EffectGenerator(analysis);
|
|
1171
|
+
const target = g.path ?? "src/validators/effect";
|
|
1172
|
+
const files = await gen.generate(
|
|
1173
|
+
validationOptions(g, cfg, target, { schemaTypes: true })
|
|
1174
|
+
);
|
|
1175
|
+
opts.json ? console.log(JSON.stringify({ event: "generate_complete", kind: g.kind, files })) : console.log(
|
|
1176
|
+
chalk3.green(`Generated (effect): ${files.length} files`),
|
|
1177
|
+
files.map((f) => chalk3.cyan(f)).join(", ")
|
|
840
1178
|
);
|
|
841
1179
|
newFiles.push(...files);
|
|
842
1180
|
} catch (e) {
|
|
@@ -853,8 +1191,8 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
853
1191
|
const target = g.path ?? "src/validators/json-schema";
|
|
854
1192
|
const files = await gen.generate(jsonSchemaOptions(g, cfg, target));
|
|
855
1193
|
opts.json ? console.log(JSON.stringify({ event: "generate_complete", kind: g.kind, files })) : console.log(
|
|
856
|
-
|
|
857
|
-
files.map((f) =>
|
|
1194
|
+
chalk3.green(`Generated (json-schema): ${files.length} files`),
|
|
1195
|
+
files.map((f) => chalk3.cyan(f)).join(", ")
|
|
858
1196
|
);
|
|
859
1197
|
newFiles.push(...files);
|
|
860
1198
|
} catch (e) {
|
|
@@ -866,8 +1204,8 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
866
1204
|
const added = newFiles.filter((f) => !lastFiles.includes(f));
|
|
867
1205
|
const removed = lastFiles.filter((f) => !newFiles.includes(f));
|
|
868
1206
|
opts.json ? console.log(JSON.stringify({ event: "diff", added, removed })) : (() => {
|
|
869
|
-
if (added.length) console.log(
|
|
870
|
-
if (removed.length) console.log(
|
|
1207
|
+
if (added.length) console.log(chalk3.blue(`Added: ${added.join(", ")}`));
|
|
1208
|
+
if (removed.length) console.log(chalk3.yellow(`Removed: ${removed.join(", ")}`));
|
|
871
1209
|
})();
|
|
872
1210
|
if (newFiles.length && !opts.json) {
|
|
873
1211
|
const reason = opts.pipeline && opts.pipeline !== "all" ? `watch:${opts.pipeline}` : "watch";
|
|
@@ -875,7 +1213,7 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
875
1213
|
}
|
|
876
1214
|
lastFiles = newFiles;
|
|
877
1215
|
} catch (e) {
|
|
878
|
-
opts.json ? console.log(JSON.stringify({ event: "error", message: String(e?.message ?? e) })) : console.error(
|
|
1216
|
+
opts.json ? console.log(JSON.stringify({ event: "error", message: String(e?.message ?? e) })) : console.error(chalk3.red("Watch pipeline failed:"), e?.message ?? e);
|
|
879
1217
|
}
|
|
880
1218
|
};
|
|
881
1219
|
const debounced = Number(opts.debounce) || 200;
|
|
@@ -900,12 +1238,12 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
900
1238
|
);
|
|
901
1239
|
} else {
|
|
902
1240
|
console.log(
|
|
903
|
-
|
|
1241
|
+
chalk3.gray(
|
|
904
1242
|
"Watching:\n " + Array.from(currentTargets).map((p) => path4.relative(process.cwd(), p)).join("\n ")
|
|
905
1243
|
)
|
|
906
1244
|
);
|
|
907
1245
|
}
|
|
908
|
-
watcher.on("add", (p) => trigger(p)).on("change", (p) => trigger(p)).on("unlink", (p) => trigger(p)).on("error", (err) => console.error(
|
|
1246
|
+
watcher.on("add", (p) => trigger(p)).on("change", (p) => trigger(p)).on("unlink", (p) => trigger(p)).on("error", (err) => console.error(chalk3.red("Watcher error:"), err));
|
|
909
1247
|
await run();
|
|
910
1248
|
});
|
|
911
1249
|
program.command("init").description("Scaffold a drzl.config.ts").option("-y, --yes", "accept defaults").action(async (_opts) => {
|
|
@@ -925,9 +1263,9 @@ program.command("init").description("Scaffold a drzl.config.ts").option("-y, --y
|
|
|
925
1263
|
`;
|
|
926
1264
|
try {
|
|
927
1265
|
await fs2.writeFile(target, template, { flag: "wx" });
|
|
928
|
-
console.log(
|
|
1266
|
+
console.log(chalk3.green(`Created ${target}`));
|
|
929
1267
|
} catch (e) {
|
|
930
|
-
console.error(
|
|
1268
|
+
console.error(chalk3.red("Init failed:"), e?.message ?? e);
|
|
931
1269
|
process.exit(1);
|
|
932
1270
|
}
|
|
933
1271
|
});
|
|
@@ -935,13 +1273,14 @@ function reportWideColumns(issues) {
|
|
|
935
1273
|
const wide = issues.filter((i) => i.code === "DRZL_ANL_UNKNOWN_COLUMN");
|
|
936
1274
|
if (!wide.length) return;
|
|
937
1275
|
console.warn(
|
|
938
|
-
|
|
1276
|
+
chalk3.yellow(`
|
|
939
1277
|
${wide.length} column${wide.length === 1 ? "" : "s"} could not be typed:`)
|
|
940
1278
|
);
|
|
941
|
-
for (const i of wide.slice(0, 10)) console.warn(
|
|
942
|
-
if (wide.length > 10) console.warn(
|
|
1279
|
+
for (const i of wide.slice(0, 10)) console.warn(chalk3.gray(` - ${i.message}`));
|
|
1280
|
+
if (wide.length > 10) console.warn(chalk3.gray(` ... and ${wide.length - 10} more`));
|
|
943
1281
|
const hints = [...new Set(wide.map((i) => i.hint).filter(Boolean))];
|
|
944
|
-
for (const h of hints) console.warn(
|
|
1282
|
+
for (const h of hints) console.warn(chalk3.gray(` ${h}`));
|
|
1283
|
+
console.warn(chalk3.gray(" Run `drzl doctor` for the full report."));
|
|
945
1284
|
}
|
|
946
1285
|
program.parseAsync(process.argv);
|
|
947
1286
|
//# sourceMappingURL=cli.js.map
|