@drzl/cli 4.19.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/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-V2IXXAC2.js";
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";
@@ -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-TRJLPIWT.js")
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, { schemaTypes: true })
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`));
@@ -809,7 +925,7 @@ program.command("generate").description("Run configured generators (drzl.config.
809
925
  try {
810
926
  const { EffectGenerator } = await loadGenerator(
811
927
  "@drzl/generator-effect",
812
- () => import("./dist-CZDVFYFW.js")
928
+ () => import("./dist-WHAW3AMQ.js")
813
929
  );
814
930
  const gen = new EffectGenerator(analysis);
815
931
  const target = g.path ?? "src/validators/effect";
@@ -991,7 +1107,11 @@ program.command("watch").description("Watch schema and regenerate on changes").o
991
1107
  validateConstraints: cfg.analyzer.validateConstraints,
992
1108
  includeHeuristicRelations: cfg.analyzer.includeHeuristicRelations
993
1109
  });
994
- analysis.tables = filterTables(analysis.tables, cfg);
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));
995
1115
  if (!opts.json) reportWideColumns(analysis.issues);
996
1116
  if (opts.pipeline === "analyze") {
997
1117
  if (opts.json) {
@@ -1090,7 +1210,7 @@ program.command("watch").description("Watch schema and regenerate on changes").o
1090
1210
  const gen = new ZodGenerator(analysis);
1091
1211
  const target = g.path ?? "src/validators/zod";
1092
1212
  const files = await gen.generate(
1093
- validationOptions(g, cfg, target, { schemaTypes: true })
1213
+ validationOptions(g, cfg, target, { schemaTypes: true, meta: true })
1094
1214
  );
1095
1215
  opts.json ? console.log(JSON.stringify({ event: "generate_complete", kind: g.kind, files })) : console.log(
1096
1216
  chalk3.green(`Generated (zod): ${files.length} files`),
@@ -1150,7 +1270,10 @@ program.command("watch").description("Watch schema and regenerate on changes").o
1150
1270
  const gen = new TypeBoxGenerator(analysis);
1151
1271
  const target = g.path ?? "src/validators/typebox";
1152
1272
  const files = await gen.generate(
1153
- validationOptions(g, cfg, target, { schemaTypes: true })
1273
+ validationOptions(g, cfg, target, {
1274
+ schemaTypes: true,
1275
+ standardSchema: true
1276
+ })
1154
1277
  );
1155
1278
  opts.json ? console.log(JSON.stringify({ event: "generate_complete", kind: g.kind, files })) : console.log(
1156
1279
  chalk3.green(`Generated (typebox): ${files.length} files`),
@@ -1165,7 +1288,7 @@ program.command("watch").description("Watch schema and regenerate on changes").o
1165
1288
  try {
1166
1289
  const { EffectGenerator } = await loadGenerator(
1167
1290
  "@drzl/generator-effect",
1168
- () => import("./dist-CZDVFYFW.js")
1291
+ () => import("./dist-WHAW3AMQ.js")
1169
1292
  );
1170
1293
  const gen = new EffectGenerator(analysis);
1171
1294
  const target = g.path ?? "src/validators/effect";
@@ -1185,7 +1308,7 @@ program.command("watch").description("Watch schema and regenerate on changes").o
1185
1308
  try {
1186
1309
  const { JsonSchemaGenerator } = await loadGenerator(
1187
1310
  "@drzl/generator-json-schema",
1188
- () => import("./dist-TRJLPIWT.js")
1311
+ () => import("./dist-K6N4F3XW.js")
1189
1312
  );
1190
1313
  const gen = new JsonSchemaGenerator(analysis);
1191
1314
  const target = g.path ?? "src/validators/json-schema";