@drzl/cli 4.18.0 → 4.21.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-XNNKHBGV.js} +158 -8
- package/dist/chunk-XNNKHBGV.js.map +1 -0
- package/dist/cli.cjs +931 -59
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +173 -11
- package/dist/cli.js.map +1 -1
- package/dist/config.cjs +147 -7
- package/dist/config.cjs.map +1 -1
- package/dist/config.d.cts +57 -3
- package/dist/config.d.ts +57 -3
- package/dist/config.js +5 -1
- package/dist/{dist-TRJLPIWT.js → dist-K6N4F3XW.js} +19 -10
- package/dist/dist-K6N4F3XW.js.map +1 -0
- package/dist/dist-WHAW3AMQ.js +585 -0
- package/dist/dist-WHAW3AMQ.js.map +1 -0
- package/package.json +10 -9
- package/dist/chunk-I4KMDCQR.js.map +0 -1
- package/dist/dist-TRJLPIWT.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
addressableName,
|
|
4
|
+
ambiguousPatternWarnings,
|
|
3
5
|
computeGeneratorOutputDirs,
|
|
4
6
|
computeWatchTargets,
|
|
7
|
+
displayTableName,
|
|
5
8
|
filterTables,
|
|
9
|
+
hasNamedSchemas,
|
|
6
10
|
loadConfig,
|
|
11
|
+
matchesAny,
|
|
12
|
+
matchesTable,
|
|
13
|
+
tableFilterWarnings,
|
|
7
14
|
trpcOutDir
|
|
8
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-XNNKHBGV.js";
|
|
9
16
|
|
|
10
17
|
// src/cli.ts
|
|
11
18
|
import { SchemaAnalyzer } from "@drzl/analyzer";
|
|
@@ -32,6 +39,11 @@ function validationOptions(g, cfg, outDir, caps = {}) {
|
|
|
32
39
|
duplicateFinder: g.duplicateFinder,
|
|
33
40
|
nestedSchemas: g.nestedSchemas,
|
|
34
41
|
nestedDepth: g.nestedDepth,
|
|
42
|
+
// Every validation generator can express a brand, including TypeBox, which has no brand
|
|
43
|
+
// helper and gets one from `TUnsafe` instead. So this needs no capability flag: an option
|
|
44
|
+
// that reached only four of the five would be the class of defect this file exists to
|
|
45
|
+
// remove.
|
|
46
|
+
branded: g.branded,
|
|
35
47
|
// Only where the generator can act on them, so an unsupported option is absent rather than
|
|
36
48
|
// present and ignored.
|
|
37
49
|
...caps.schemaTypes ? {
|
|
@@ -39,7 +51,9 @@ function validationOptions(g, cfg, outDir, caps = {}) {
|
|
|
39
51
|
schemaPath: cfg.schema,
|
|
40
52
|
typedJson: g.typedJson,
|
|
41
53
|
typedColumns: g.typedColumns
|
|
42
|
-
} : {}
|
|
54
|
+
} : {},
|
|
55
|
+
...caps.standardSchema ? { standardSchema: g.standardSchema } : {},
|
|
56
|
+
...caps.meta ? { meta: g.meta } : {}
|
|
43
57
|
};
|
|
44
58
|
}
|
|
45
59
|
|
|
@@ -76,6 +90,9 @@ function trpcOptions(g, cfg, servicesDir) {
|
|
|
76
90
|
};
|
|
77
91
|
}
|
|
78
92
|
|
|
93
|
+
// src/column-filter.ts
|
|
94
|
+
import { parseCheck as parseCheck2 } from "@drzl/validation-core";
|
|
95
|
+
|
|
79
96
|
// src/doctor.ts
|
|
80
97
|
import { parseCheck } from "@drzl/validation-core";
|
|
81
98
|
import chalk from "chalk";
|
|
@@ -334,6 +351,99 @@ function renderDoctorReport(report) {
|
|
|
334
351
|
return out.join("\n");
|
|
335
352
|
}
|
|
336
353
|
|
|
354
|
+
// src/column-filter.ts
|
|
355
|
+
function checkedColumns(expression, name) {
|
|
356
|
+
const parsed = parseCheck2(expression, name);
|
|
357
|
+
if (!parsed.ok) return [];
|
|
358
|
+
return [...new Set(namedColumns(parsed).map((n) => n.column))];
|
|
359
|
+
}
|
|
360
|
+
function filterColumns(tables, spec) {
|
|
361
|
+
const entries = Object.entries(spec ?? {});
|
|
362
|
+
if (!entries.length) return { tables, warnings: [] };
|
|
363
|
+
const errors = [];
|
|
364
|
+
const warnings = [];
|
|
365
|
+
const nameForConfig = hasNamedSchemas(tables) ? addressableName : displayTableName;
|
|
366
|
+
for (const [tablePattern, rules] of entries) {
|
|
367
|
+
const matched = tables.filter((t) => matchesTable([tablePattern], t));
|
|
368
|
+
if (!matched.length) {
|
|
369
|
+
errors.push(
|
|
370
|
+
`columns[${JSON.stringify(tablePattern)}] matches no table. The schema declares: ${tables.map(nameForConfig).join(", ") || "(no tables)"}.`
|
|
371
|
+
);
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
const available = [...new Set(matched.flatMap((t) => t.columns.map((c) => c.name)))];
|
|
375
|
+
for (const which of ["pick", "omit"]) {
|
|
376
|
+
for (const pattern of rules[which] ?? []) {
|
|
377
|
+
if (available.some((name) => matchesAny([pattern], name))) continue;
|
|
378
|
+
errors.push(
|
|
379
|
+
`columns[${JSON.stringify(tablePattern)}].${which} names ${JSON.stringify(pattern)}, which matches no column of ${matched.map(nameForConfig).join(", ")}. Available: ${available.join(", ")}.`
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
warnings.push(
|
|
385
|
+
...ambiguousPatternWarnings(
|
|
386
|
+
entries.map(([p]) => p),
|
|
387
|
+
tables,
|
|
388
|
+
"columns"
|
|
389
|
+
)
|
|
390
|
+
);
|
|
391
|
+
const out = tables.map((table) => {
|
|
392
|
+
const mine = entries.filter(([pattern]) => matchesTable([pattern], table));
|
|
393
|
+
if (!mine.length) return table;
|
|
394
|
+
let keep = table.columns;
|
|
395
|
+
for (const [, rules] of mine) {
|
|
396
|
+
if (rules.pick?.length) keep = keep.filter((c) => matchesAny(rules.pick, c.name));
|
|
397
|
+
if (rules.omit?.length) keep = keep.filter((c) => !matchesAny(rules.omit, c.name));
|
|
398
|
+
}
|
|
399
|
+
if (keep.length === table.columns.length) return table;
|
|
400
|
+
const kept = new Set(keep.map((c) => c.name));
|
|
401
|
+
const dropped = table.columns.filter((c) => !kept.has(c.name));
|
|
402
|
+
if (!keep.length) {
|
|
403
|
+
errors.push(
|
|
404
|
+
`columns leaves table "${displayTableName(table)}" with no columns at all. An empty schema describes no row, so this is never a narrower API. Exclude the table instead, with the top-level "exclude" option.`
|
|
405
|
+
);
|
|
406
|
+
return table;
|
|
407
|
+
}
|
|
408
|
+
const lostKey = (table.primaryKey?.columns ?? []).filter((n) => !kept.has(n));
|
|
409
|
+
if (lostKey.length) {
|
|
410
|
+
errors.push(
|
|
411
|
+
`columns drops ${lostKey.map((n) => JSON.stringify(n)).join(", ")} from table "${displayTableName(table)}", which is part of its primary key (${table.primaryKey?.columns.join(", ")}). The generated getById, update and delete address rows by that key, so the emitted schemas would describe a row nothing can address. Keep the key, or leave the whole table out with the top-level "exclude" option.`
|
|
412
|
+
);
|
|
413
|
+
return table;
|
|
414
|
+
}
|
|
415
|
+
for (const c of dropped) {
|
|
416
|
+
if (c.nullable || c.hasDefault || c.isGenerated || table.readOnly) continue;
|
|
417
|
+
warnings.push(
|
|
418
|
+
`drzl config: the "columns" option drops "${c.name}" from table "${displayTableName(table)}", and the database requires it: NOT NULL with no default. The emitted insert schema therefore describes a payload that is not a complete row, so whatever calls db.insert has to supply "${c.name}" itself.`
|
|
419
|
+
);
|
|
420
|
+
}
|
|
421
|
+
for (const k of table.checks ?? []) {
|
|
422
|
+
const lost = checkedColumns(k.expression, k.name).filter(
|
|
423
|
+
(n) => !kept.has(n) && table.columns.some((c) => c.name === n)
|
|
424
|
+
);
|
|
425
|
+
if (!lost.length) continue;
|
|
426
|
+
warnings.push(
|
|
427
|
+
`drzl config: CHECK ${k.name ? `"${k.name}"` : "(unnamed)"} on table "${displayTableName(table)}" names ${lost.map((n) => JSON.stringify(n)).join(", ")}, which the "columns" option drops, so nothing DRZL emits enforces it. Your database still does.`
|
|
428
|
+
);
|
|
429
|
+
}
|
|
430
|
+
return {
|
|
431
|
+
...table,
|
|
432
|
+
columns: keep,
|
|
433
|
+
unique: (table.unique ?? []).filter((k) => k.columns.every((n) => kept.has(n))),
|
|
434
|
+
indexes: (table.indexes ?? []).filter((i) => i.columns.every((n) => kept.has(n))),
|
|
435
|
+
...table.foreignKeys ? { foreignKeys: table.foreignKeys.filter((f) => f.columns.every((n) => kept.has(n))) } : {}
|
|
436
|
+
};
|
|
437
|
+
});
|
|
438
|
+
if (errors.length) {
|
|
439
|
+
throw new Error(
|
|
440
|
+
`drzl config: the "columns" option cannot be honoured.
|
|
441
|
+
` + errors.map((e) => ` - ${e}`).join("\n")
|
|
442
|
+
);
|
|
443
|
+
}
|
|
444
|
+
return { tables: out, warnings };
|
|
445
|
+
}
|
|
446
|
+
|
|
337
447
|
// src/drift.ts
|
|
338
448
|
import { promises as fs } from "fs";
|
|
339
449
|
import path from "path";
|
|
@@ -420,7 +530,7 @@ var shownThisProcess = false;
|
|
|
420
530
|
var tips = [
|
|
421
531
|
"Pair DRZL watch mode with drizzle-kit to keep schema & API synced.",
|
|
422
532
|
"Templatize your ORPC routers to roll out new endpoints safely.",
|
|
423
|
-
"Need typed validators? Enable the zod, valibot, arktype, or
|
|
533
|
+
"Need typed validators? Enable the zod, valibot, arktype, typebox, or effect generators.",
|
|
424
534
|
"Need JSON Schema or OpenAPI? The json-schema generator emits both, with no runtime dependency.",
|
|
425
535
|
"Use output headers to track generated files and trim noisy diffs."
|
|
426
536
|
];
|
|
@@ -629,8 +739,11 @@ program.command("generate").description("Run configured generators (drzl.config.
|
|
|
629
739
|
validateConstraints: cfg.analyzer.validateConstraints,
|
|
630
740
|
includeHeuristicRelations: cfg.analyzer.includeHeuristicRelations
|
|
631
741
|
});
|
|
632
|
-
analysis.tables = filterTables(analysis.tables, cfg);
|
|
633
742
|
spinner.succeed(`Analysis complete in ${Date.now() - t0}ms`);
|
|
743
|
+
const narrowed = filterColumns(analysis.tables, cfg.columns);
|
|
744
|
+
const filterWarnings = tableFilterWarnings(narrowed.tables, cfg);
|
|
745
|
+
analysis.tables = filterTables(narrowed.tables, cfg);
|
|
746
|
+
for (const w of [...narrowed.warnings, ...filterWarnings]) console.warn(chalk3.yellow(w));
|
|
634
747
|
reportWideColumns(analysis.issues);
|
|
635
748
|
const driftDirs = computeGeneratorOutputDirs(cfg);
|
|
636
749
|
const driftBefore = opts.check ? await snapshotAll(driftDirs) : null;
|
|
@@ -721,7 +834,7 @@ program.command("generate").description("Run configured generators (drzl.config.
|
|
|
721
834
|
const gen = new ZodGenerator(analysis);
|
|
722
835
|
const target = g.path ?? "src/validators/zod";
|
|
723
836
|
const files = await gen.generate(
|
|
724
|
-
validationOptions(g, cfg, target, { schemaTypes: true })
|
|
837
|
+
validationOptions(g, cfg, target, { schemaTypes: true, meta: true })
|
|
725
838
|
);
|
|
726
839
|
progress.stop();
|
|
727
840
|
ora().succeed(chalk3.green(`Generated (zod): ${files.length} files`));
|
|
@@ -773,7 +886,7 @@ program.command("generate").description("Run configured generators (drzl.config.
|
|
|
773
886
|
try {
|
|
774
887
|
const { JsonSchemaGenerator } = await loadGenerator(
|
|
775
888
|
"@drzl/generator-json-schema",
|
|
776
|
-
() => import("./dist-
|
|
889
|
+
() => import("./dist-K6N4F3XW.js")
|
|
777
890
|
);
|
|
778
891
|
const gen = new JsonSchemaGenerator(analysis);
|
|
779
892
|
const target = g.path ?? "src/validators/json-schema";
|
|
@@ -795,7 +908,10 @@ program.command("generate").description("Run configured generators (drzl.config.
|
|
|
795
908
|
const gen = new TypeBoxGenerator(analysis);
|
|
796
909
|
const target = g.path ?? "src/validators/typebox";
|
|
797
910
|
const files = await gen.generate(
|
|
798
|
-
validationOptions(g, cfg, target, {
|
|
911
|
+
validationOptions(g, cfg, target, {
|
|
912
|
+
schemaTypes: true,
|
|
913
|
+
standardSchema: true
|
|
914
|
+
})
|
|
799
915
|
);
|
|
800
916
|
progress.stop();
|
|
801
917
|
ora().succeed(chalk3.green(`Generated (typebox): ${files.length} files`));
|
|
@@ -805,6 +921,25 @@ program.command("generate").description("Run configured generators (drzl.config.
|
|
|
805
921
|
reportGeneratorFailure(g.kind, e);
|
|
806
922
|
process.exit(1);
|
|
807
923
|
}
|
|
924
|
+
} else if (g.kind === "effect") {
|
|
925
|
+
try {
|
|
926
|
+
const { EffectGenerator } = await loadGenerator(
|
|
927
|
+
"@drzl/generator-effect",
|
|
928
|
+
() => import("./dist-WHAW3AMQ.js")
|
|
929
|
+
);
|
|
930
|
+
const gen = new EffectGenerator(analysis);
|
|
931
|
+
const target = g.path ?? "src/validators/effect";
|
|
932
|
+
const files = await gen.generate(
|
|
933
|
+
validationOptions(g, cfg, target, { schemaTypes: true })
|
|
934
|
+
);
|
|
935
|
+
progress.stop();
|
|
936
|
+
ora().succeed(chalk3.green(`Generated (effect): ${files.length} files`));
|
|
937
|
+
files.forEach((f) => console.log(" -", chalk3.cyan(f)));
|
|
938
|
+
} catch (e) {
|
|
939
|
+
progress.stop();
|
|
940
|
+
reportGeneratorFailure(g.kind, e);
|
|
941
|
+
process.exit(1);
|
|
942
|
+
}
|
|
808
943
|
}
|
|
809
944
|
}
|
|
810
945
|
if (driftBefore) {
|
|
@@ -972,7 +1107,11 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
972
1107
|
validateConstraints: cfg.analyzer.validateConstraints,
|
|
973
1108
|
includeHeuristicRelations: cfg.analyzer.includeHeuristicRelations
|
|
974
1109
|
});
|
|
975
|
-
|
|
1110
|
+
const narrowed = filterColumns(analysis.tables, cfg.columns);
|
|
1111
|
+
const filterWarnings = tableFilterWarnings(narrowed.tables, cfg);
|
|
1112
|
+
analysis.tables = filterTables(narrowed.tables, cfg);
|
|
1113
|
+
if (!opts.json)
|
|
1114
|
+
for (const w of [...narrowed.warnings, ...filterWarnings]) console.warn(chalk3.yellow(w));
|
|
976
1115
|
if (!opts.json) reportWideColumns(analysis.issues);
|
|
977
1116
|
if (opts.pipeline === "analyze") {
|
|
978
1117
|
if (opts.json) {
|
|
@@ -1071,7 +1210,7 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
1071
1210
|
const gen = new ZodGenerator(analysis);
|
|
1072
1211
|
const target = g.path ?? "src/validators/zod";
|
|
1073
1212
|
const files = await gen.generate(
|
|
1074
|
-
validationOptions(g, cfg, target, { schemaTypes: true })
|
|
1213
|
+
validationOptions(g, cfg, target, { schemaTypes: true, meta: true })
|
|
1075
1214
|
);
|
|
1076
1215
|
opts.json ? console.log(JSON.stringify({ event: "generate_complete", kind: g.kind, files })) : console.log(
|
|
1077
1216
|
chalk3.green(`Generated (zod): ${files.length} files`),
|
|
@@ -1131,7 +1270,10 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
1131
1270
|
const gen = new TypeBoxGenerator(analysis);
|
|
1132
1271
|
const target = g.path ?? "src/validators/typebox";
|
|
1133
1272
|
const files = await gen.generate(
|
|
1134
|
-
validationOptions(g, cfg, target, {
|
|
1273
|
+
validationOptions(g, cfg, target, {
|
|
1274
|
+
schemaTypes: true,
|
|
1275
|
+
standardSchema: true
|
|
1276
|
+
})
|
|
1135
1277
|
);
|
|
1136
1278
|
opts.json ? console.log(JSON.stringify({ event: "generate_complete", kind: g.kind, files })) : console.log(
|
|
1137
1279
|
chalk3.green(`Generated (typebox): ${files.length} files`),
|
|
@@ -1142,11 +1284,31 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
1142
1284
|
reportGeneratorFailure(g.kind, e);
|
|
1143
1285
|
return;
|
|
1144
1286
|
}
|
|
1287
|
+
} else if (g.kind === "effect") {
|
|
1288
|
+
try {
|
|
1289
|
+
const { EffectGenerator } = await loadGenerator(
|
|
1290
|
+
"@drzl/generator-effect",
|
|
1291
|
+
() => import("./dist-WHAW3AMQ.js")
|
|
1292
|
+
);
|
|
1293
|
+
const gen = new EffectGenerator(analysis);
|
|
1294
|
+
const target = g.path ?? "src/validators/effect";
|
|
1295
|
+
const files = await gen.generate(
|
|
1296
|
+
validationOptions(g, cfg, target, { schemaTypes: true })
|
|
1297
|
+
);
|
|
1298
|
+
opts.json ? console.log(JSON.stringify({ event: "generate_complete", kind: g.kind, files })) : console.log(
|
|
1299
|
+
chalk3.green(`Generated (effect): ${files.length} files`),
|
|
1300
|
+
files.map((f) => chalk3.cyan(f)).join(", ")
|
|
1301
|
+
);
|
|
1302
|
+
newFiles.push(...files);
|
|
1303
|
+
} catch (e) {
|
|
1304
|
+
reportGeneratorFailure(g.kind, e);
|
|
1305
|
+
return;
|
|
1306
|
+
}
|
|
1145
1307
|
} else if (g.kind === "json-schema") {
|
|
1146
1308
|
try {
|
|
1147
1309
|
const { JsonSchemaGenerator } = await loadGenerator(
|
|
1148
1310
|
"@drzl/generator-json-schema",
|
|
1149
|
-
() => import("./dist-
|
|
1311
|
+
() => import("./dist-K6N4F3XW.js")
|
|
1150
1312
|
);
|
|
1151
1313
|
const gen = new JsonSchemaGenerator(analysis);
|
|
1152
1314
|
const target = g.path ?? "src/validators/json-schema";
|