@drzl/cli 4.17.0 → 4.18.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.cjs CHANGED
@@ -261,7 +261,7 @@ ${entries}
261
261
  const decided = [...declared, body].join("\n\n");
262
262
  const imports = [];
263
263
  if (useShared) {
264
- const sharedAffix = (0, import_validation_core2.resolveAffix)({
264
+ const sharedAffix = (0, import_validation_core3.resolveAffix)({
265
265
  affix: opts.validation?.affix,
266
266
  schemaSuffix: opts.validation?.schemaSuffix
267
267
  });
@@ -271,21 +271,21 @@ ${entries}
271
271
  ["select", selectName]
272
272
  ].filter(([, local]) => decided.includes(local));
273
273
  if (wanted.length) {
274
- const spec = (0, import_validation_core2.resolveConfiguredImport)(
274
+ const spec = (0, import_validation_core3.resolveConfiguredImport)(
275
275
  opts.validation.importPath,
276
276
  ctx.out,
277
277
  process.cwd(),
278
278
  opts.importExtension
279
279
  );
280
280
  const names = wanted.map(([mode, local]) => {
281
- const exported = (0, import_validation_core2.schemaName)(mode, table.tsName, sharedAffix);
281
+ const exported = (0, import_validation_core3.schemaName)(mode, table.tsName, sharedAffix);
282
282
  return exported === local ? local : `${exported} as ${local}`;
283
283
  }).join(", ");
284
284
  imports.push(`import { ${names} } from '${spec}';`);
285
285
  }
286
286
  }
287
287
  imports.push(
288
- `import { ${[builder, "router"].sort().join(", ")} } from '${(0, import_validation_core2.importSpecifier)(
288
+ `import { ${[builder, "router"].sort().join(", ")} } from '${(0, import_validation_core3.importSpecifier)(
289
289
  `./${BASE_MODULE}.ts`,
290
290
  opts.importExtension
291
291
  )}';`
@@ -334,7 +334,7 @@ function relationProcedures(table, lib, selectSchemaName, taken, service) {
334
334
  return out;
335
335
  }
336
336
  function renderBarrel(routers, ctx, path6, opts) {
337
- const baseSpec = (0, import_validation_core2.importSpecifier)(`./${BASE_MODULE}.ts`, opts.importExtension);
337
+ const baseSpec = (0, import_validation_core3.importSpecifier)(`./${BASE_MODULE}.ts`, opts.importExtension);
338
338
  const reExports = `export { createCallerFactory, publicProcedure, router } from '${baseSpec}';
339
339
  ` + (opts.databaseInjection?.enabled === true ? `export { dbProcedure } from '${baseSpec}';
340
340
  ` : "") + `export type { Context } from '${baseSpec}';
@@ -352,7 +352,7 @@ export type AppRouter = typeof appRouter;
352
352
  ${reExports}`;
353
353
  }
354
354
  const entries = routers.map(({ filePath, exportName, table }) => ({
355
- rel: (0, import_validation_core2.importSpecifier)(
355
+ rel: (0, import_validation_core3.importSpecifier)(
356
356
  "./" + path6.relative(ctx.out, filePath).replace(/\\/g, "/"),
357
357
  opts.importExtension
358
358
  ),
@@ -393,7 +393,7 @@ function routerExportName(table, naming) {
393
393
  function serviceImportSpecifier(table, ctx, opts) {
394
394
  const rel = relativePosix(ctx.out, ctx.services);
395
395
  const dir = !rel ? "." : rel.startsWith(".") ? rel : `./${rel}`;
396
- return (0, import_validation_core2.importSpecifier)(`${dir}/${singularize(table.tsName)}Service.ts`, opts.importExtension);
396
+ return (0, import_validation_core3.importSpecifier)(`${dir}/${singularize(table.tsName)}Service.ts`, opts.importExtension);
397
397
  }
398
398
  function relativePosix(from, to) {
399
399
  const norm = (p) => p.replace(/\\/g, "/").replace(/\/+$/, "");
@@ -413,11 +413,11 @@ function buildHeader(h) {
413
413
  ];
414
414
  return lines.join("\n") + "\n\n";
415
415
  }
416
- var import_validation_core2, TRPC_MAJOR, q, LIB_IMPORTS, LIB_USAGE, LIBS, cap, singularize, isIdent, BASE_MODULE, TRPCGenerator, index_default;
416
+ var import_validation_core3, TRPC_MAJOR, q, LIB_IMPORTS, LIB_USAGE, LIBS, cap, singularize, isIdent, BASE_MODULE, TRPCGenerator, index_default;
417
417
  var init_dist = __esm({
418
418
  "../generator-trpc/dist/index.js"() {
419
419
  "use strict";
420
- import_validation_core2 = require("@drzl/validation-core");
420
+ import_validation_core3 = require("@drzl/validation-core");
421
421
  TRPC_MAJOR = 11;
422
422
  q = (v) => JSON.stringify(v);
423
423
  LIB_IMPORTS = {
@@ -510,7 +510,7 @@ ${body}
510
510
  await fs3.mkdir(out, { recursive: true });
511
511
  const files = [];
512
512
  const write = async (filePath, content) => {
513
- const formatted = await (0, import_validation_core2.formatCode)(
513
+ const formatted = await (0, import_validation_core3.formatCode)(
514
514
  buildHeader(opts.outputHeader) + content,
515
515
  filePath,
516
516
  opts.format
@@ -606,14 +606,14 @@ function baseSchema(c, mode, target, checks, sets, lengths) {
606
606
  case "string": {
607
607
  const out = { type: "string" };
608
608
  if (c.format === "uuid") out.format = UUID_FORMAT;
609
- else if (c.format && import_validation_core4.COLUMN_FORMATS[c.format]) out.pattern = import_validation_core4.COLUMN_FORMATS[c.format];
609
+ else if (c.format && import_validation_core5.COLUMN_FORMATS[c.format]) out.pattern = import_validation_core5.COLUMN_FORMATS[c.format];
610
610
  if (c.maxLength !== void 0) out.maxLength = c.maxLength;
611
611
  applyByteCap(out, c);
612
612
  applyLengths(out, c, lengths);
613
613
  return out;
614
614
  }
615
615
  case "number": {
616
- const out = { type: (0, import_validation_core4.isIntegerColumn)(c) ? "integer" : "number" };
616
+ const out = { type: (0, import_validation_core5.isIntegerColumn)(c) ? "integer" : "number" };
617
617
  if (!c.arrayDimensions) applyNumericBounds(out, c, checks, target);
618
618
  return out;
619
619
  }
@@ -750,7 +750,7 @@ function tableSchema(table, cols, mode, target, applyDefaults, parsed) {
750
750
  };
751
751
  }
752
752
  function collect(table) {
753
- const parsed = (table.checks ?? []).map((k) => (0, import_validation_core4.parseCheck)(k.expression, k.name));
753
+ const parsed = (table.checks ?? []).map((k) => (0, import_validation_core5.parseCheck)(k.expression, k.name));
754
754
  return {
755
755
  checks: parsed.flatMap((p) => p.ok ? p.checks : []),
756
756
  sets: parsed.flatMap((p) => p.ok ? p.sets ?? [] : []),
@@ -764,9 +764,9 @@ function tableSchemas(table, opts = {}) {
764
764
  const parsed = collect(table);
765
765
  const build2 = (cols, mode) => tableSchema(table, cols, mode, target, !!opts.applyDefaults, parsed);
766
766
  return {
767
- insert: build2((0, import_validation_core4.insertColumns)(table), "insert"),
768
- update: build2((0, import_validation_core4.updateColumns)(table), "update"),
769
- select: build2((0, import_validation_core4.selectColumns)(table), "select")
767
+ insert: build2((0, import_validation_core5.insertColumns)(table), "insert"),
768
+ update: build2((0, import_validation_core5.updateColumns)(table), "update"),
769
+ select: build2((0, import_validation_core5.selectColumns)(table), "select")
770
770
  };
771
771
  }
772
772
  function componentsDocument(tables, opts = {}) {
@@ -995,9 +995,9 @@ function openApiDocument(tables, opts = {}) {
995
995
  function renderTableModule(table, affix, target, applyDefaults) {
996
996
  const T = table.tsName;
997
997
  const schemas = tableSchemas(table, { target, applyDefaults });
998
- const decl = (mode) => `export const ${(0, import_validation_core3.schemaName)(mode, T, affix)} = ${JSON.stringify(schemas[mode], null, 2)} as const;
998
+ const decl = (mode) => `export const ${(0, import_validation_core4.schemaName)(mode, T, affix)} = ${JSON.stringify(schemas[mode], null, 2)} as const;
999
999
 
1000
- export type ${(0, import_validation_core3.typeName)(mode, T, affix)} = typeof ${(0, import_validation_core3.schemaName)(mode, T, affix)};`;
1000
+ export type ${(0, import_validation_core4.typeName)(mode, T, affix)} = typeof ${(0, import_validation_core4.schemaName)(mode, T, affix)};`;
1001
1001
  return [decl("insert"), decl("update"), decl("select")].join("\n\n") + "\n";
1002
1002
  }
1003
1003
  function resolveDocument(opt) {
@@ -1013,12 +1013,12 @@ function buildHeader2(h) {
1013
1013
 
1014
1014
  `;
1015
1015
  }
1016
- var import_validation_core3, import_validation_core4, DRAFT, UUID_FORMAT, base64, ERROR_SCHEMA, componentName, ref, pascal, modesFor, resourceSegment, jsonBody, errorSchema, DEFAULT_FILE_SUFFIX, JsonSchemaGenerator, index_default2;
1016
+ var import_validation_core4, import_validation_core5, DRAFT, UUID_FORMAT, base64, ERROR_SCHEMA, componentName, ref, pascal, modesFor, resourceSegment, jsonBody, errorSchema, DEFAULT_FILE_SUFFIX, JsonSchemaGenerator, index_default2;
1017
1017
  var init_dist2 = __esm({
1018
1018
  "../generator-json-schema/dist/index.js"() {
1019
1019
  "use strict";
1020
- import_validation_core3 = require("@drzl/validation-core");
1021
1020
  import_validation_core4 = require("@drzl/validation-core");
1021
+ import_validation_core5 = require("@drzl/validation-core");
1022
1022
  DRAFT = "https://json-schema.org/draft/2020-12/schema";
1023
1023
  UUID_FORMAT = "uuid";
1024
1024
  base64 = (target) => target === "openapi-3.0" ? { type: "string", format: "byte" } : { type: "string", contentEncoding: "base64" };
@@ -1056,14 +1056,14 @@ var init_dist2 = __esm({
1056
1056
  const out = path6.resolve(process.cwd(), opts.outDir);
1057
1057
  const files = [];
1058
1058
  await fs3.mkdir(out, { recursive: true });
1059
- const affix = (0, import_validation_core3.resolveAffix)(opts);
1059
+ const affix = (0, import_validation_core4.resolveAffix)(opts);
1060
1060
  const fileSuffix = opts.fileSuffix ?? DEFAULT_FILE_SUFFIX;
1061
1061
  const target = opts.target ?? "draft-2020-12";
1062
1062
  const document = resolveDocument(opts.document);
1063
1063
  for (const table of this.analysis.tables) {
1064
- const filePath = path6.join(out, (0, import_validation_core3.moduleFileName)(table.tsName, fileSuffix));
1064
+ const filePath = path6.join(out, (0, import_validation_core4.moduleFileName)(table.tsName, fileSuffix));
1065
1065
  const code = renderTableModule(table, affix, target, !!opts.applyDefaults);
1066
- const formatted = await (0, import_validation_core3.formatCode)(
1066
+ const formatted = await (0, import_validation_core4.formatCode)(
1067
1067
  buildHeader2(opts.outputHeader) + code,
1068
1068
  filePath,
1069
1069
  opts.format
@@ -1081,7 +1081,7 @@ var init_dist2 = __esm({
1081
1081
  `;
1082
1082
  await fs3.writeFile(
1083
1083
  componentsPath,
1084
- await (0, import_validation_core3.formatCode)(buildHeader2(opts.outputHeader) + code, componentsPath, opts.format),
1084
+ await (0, import_validation_core4.formatCode)(buildHeader2(opts.outputHeader) + code, componentsPath, opts.format),
1085
1085
  "utf8"
1086
1086
  );
1087
1087
  files.push(componentsPath);
@@ -1102,7 +1102,7 @@ var init_dist2 = __esm({
1102
1102
  `;
1103
1103
  await fs3.writeFile(
1104
1104
  tsPath,
1105
- await (0, import_validation_core3.formatCode)(buildHeader2(opts.outputHeader) + code, tsPath, opts.format),
1105
+ await (0, import_validation_core4.formatCode)(buildHeader2(opts.outputHeader) + code, tsPath, opts.format),
1106
1106
  "utf8"
1107
1107
  );
1108
1108
  files.push(tsPath);
@@ -1116,9 +1116,9 @@ var init_dist2 = __esm({
1116
1116
  const ext = opts.importExtension === "none" ? "" : ".js";
1117
1117
  const indexPath = path6.join(out, "index.ts");
1118
1118
  const index = this.analysis.tables.map(
1119
- (t) => `export * from '${(0, import_validation_core3.moduleSpecifier)(t.tsName, fileSuffix, opts.importExtension)}';`
1119
+ (t) => `export * from '${(0, import_validation_core4.moduleSpecifier)(t.tsName, fileSuffix, opts.importExtension)}';`
1120
1120
  ).concat(opts.components ? [`export * from './components${ext}';`] : []).concat(document && document.format !== "json" ? [`export * from './openapi${ext}';`] : []).join("\n") + "\n";
1121
- const indexFormatted = await (0, import_validation_core3.formatCode)(
1121
+ const indexFormatted = await (0, import_validation_core4.formatCode)(
1122
1122
  buildHeader2(opts.outputHeader) + index,
1123
1123
  indexPath,
1124
1124
  opts.format
@@ -1130,7 +1130,7 @@ var init_dist2 = __esm({
1130
1130
  renderTable(table, opts) {
1131
1131
  return renderTableModule(
1132
1132
  table,
1133
- (0, import_validation_core3.resolveAffix)(opts),
1133
+ (0, import_validation_core4.resolveAffix)(opts),
1134
1134
  opts?.target ?? "draft-2020-12",
1135
1135
  !!opts?.applyDefaults
1136
1136
  );
@@ -1143,7 +1143,7 @@ var init_dist2 = __esm({
1143
1143
  // src/cli.ts
1144
1144
  var import_analyzer = require("@drzl/analyzer");
1145
1145
  var import_generator_orpc = require("@drzl/generator-orpc");
1146
- var import_chalk2 = __toESM(require("chalk"), 1);
1146
+ var import_chalk3 = __toESM(require("chalk"), 1);
1147
1147
  var import_chokidar = __toESM(require("chokidar"), 1);
1148
1148
  var import_cli_progress = __toESM(require("cli-progress"), 1);
1149
1149
  var import_commander = require("commander");
@@ -1635,6 +1635,264 @@ function trpcOptions(g, cfg, servicesDir) {
1635
1635
  };
1636
1636
  }
1637
1637
 
1638
+ // src/doctor.ts
1639
+ var import_validation_core2 = require("@drzl/validation-core");
1640
+ var import_chalk = __toESM(require("chalk"), 1);
1641
+ var HANDLED_CODES = /* @__PURE__ */ new Set(["DRZL_ANL_UNKNOWN_COLUMN"]);
1642
+ function splitPath(path6) {
1643
+ if (!path6) return {};
1644
+ const dot = path6.lastIndexOf(".");
1645
+ if (dot <= 0) return { table: path6 };
1646
+ return { table: path6.slice(0, dot), column: path6.slice(dot + 1) };
1647
+ }
1648
+ function namedColumns(parsed) {
1649
+ const out = [];
1650
+ for (const c of parsed.checks) out.push({ column: c.column, scalar: true });
1651
+ for (const s of parsed.sets ?? []) out.push({ column: s.column, scalar: true });
1652
+ for (const l of parsed.lengths ?? []) out.push({ column: l.column, scalar: false });
1653
+ for (const c of parsed.cardinalities ?? []) out.push({ column: c.column, scalar: false });
1654
+ for (const r of parsed.rows ?? []) {
1655
+ out.push({ column: r.left, scalar: false });
1656
+ out.push({ column: r.right, scalar: false });
1657
+ }
1658
+ return out;
1659
+ }
1660
+ function describeShape(c) {
1661
+ if (c.arrayDimensions) return "an array";
1662
+ switch (c.shape?.kind) {
1663
+ case "json":
1664
+ return "a JSON";
1665
+ case "buffer":
1666
+ return "a binary";
1667
+ case "tuple":
1668
+ case "numberObject":
1669
+ return "a structured";
1670
+ case "numberVector":
1671
+ return "a vector";
1672
+ case "bitstring":
1673
+ return "a bit-string";
1674
+ case "byteString":
1675
+ return "a byte-string";
1676
+ case "custom":
1677
+ return "a customType";
1678
+ default:
1679
+ return "a structured";
1680
+ }
1681
+ }
1682
+ function checkFindings(table) {
1683
+ const out = [];
1684
+ const byName = new Map(table.columns.map((c) => [c.name, c]));
1685
+ for (const k of table.checks ?? []) {
1686
+ const label = k.name ? `"${k.name}"` : "an unnamed constraint";
1687
+ const raw = k.expression ?? "";
1688
+ const expr = raw.trim() ? raw : "(empty)";
1689
+ const parsed = (0, import_validation_core2.parseCheck)(raw, k.name);
1690
+ if (!parsed.ok) {
1691
+ out.push({
1692
+ kind: "check-declined",
1693
+ level: "warn",
1694
+ table: table.tsName,
1695
+ constraint: k.name,
1696
+ message: `CHECK ${label} on "${table.tsName}" is not translated: ${parsed.reason}. Expression: ${expr}`,
1697
+ 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."
1698
+ });
1699
+ continue;
1700
+ }
1701
+ const seen = /* @__PURE__ */ new Set();
1702
+ for (const { column, scalar } of namedColumns(parsed)) {
1703
+ if (seen.has(column)) continue;
1704
+ seen.add(column);
1705
+ const col = byName.get(column);
1706
+ if (!col) {
1707
+ out.push({
1708
+ kind: "check-unknown-column",
1709
+ level: "warn",
1710
+ table: table.tsName,
1711
+ column,
1712
+ constraint: k.name,
1713
+ message: `CHECK ${label} on "${table.tsName}" names "${column}", which is not a column of that table, so nothing enforces it. Expression: ${expr}`,
1714
+ hint: "A constraint is attached to the field it names. Check the spelling, or move a constraint spanning two tables out of the schema."
1715
+ });
1716
+ continue;
1717
+ }
1718
+ if (scalar && (col.arrayDimensions || col.shape)) {
1719
+ out.push({
1720
+ kind: "check-not-scalar",
1721
+ level: "warn",
1722
+ table: table.tsName,
1723
+ column,
1724
+ constraint: k.name,
1725
+ 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}`,
1726
+ 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."
1727
+ });
1728
+ }
1729
+ }
1730
+ }
1731
+ return out;
1732
+ }
1733
+ function primaryKeyFindings(table) {
1734
+ if (table.readOnly) return [];
1735
+ const pk = table.primaryKey?.columns ?? [];
1736
+ if (!pk.length) {
1737
+ const hasId = table.columns.some((c) => c.name === "id");
1738
+ return [
1739
+ {
1740
+ kind: "no-primary-key",
1741
+ level: "warn",
1742
+ table: table.tsName,
1743
+ 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.`,
1744
+ hint: "Declare a primary key, or leave this table out with the config table filter."
1745
+ }
1746
+ ];
1747
+ }
1748
+ if (pk.length > 1) {
1749
+ return [
1750
+ {
1751
+ kind: "partial-primary-key",
1752
+ level: "warn",
1753
+ table: table.tsName,
1754
+ 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.`,
1755
+ hint: "Treat the generated service as a starting point for this table and widen the key by hand."
1756
+ }
1757
+ ];
1758
+ }
1759
+ return [];
1760
+ }
1761
+ function buildDoctorReport(analysis, schemaPath) {
1762
+ const findings = [];
1763
+ const errors = analysis.issues.filter((i) => i.level === "error");
1764
+ for (const i of errors) {
1765
+ findings.push({
1766
+ kind: "analyzer",
1767
+ level: "error",
1768
+ ...splitPath(i.path),
1769
+ message: i.message,
1770
+ hint: i.hint
1771
+ });
1772
+ }
1773
+ for (const i of analysis.issues) {
1774
+ if (i.code !== "DRZL_ANL_UNKNOWN_COLUMN") continue;
1775
+ findings.push({
1776
+ kind: "unknown-column",
1777
+ level: "warn",
1778
+ ...splitPath(i.path),
1779
+ message: i.message,
1780
+ hint: i.hint
1781
+ });
1782
+ }
1783
+ for (const t of analysis.tables) findings.push(...checkFindings(t));
1784
+ for (const t of analysis.tables) findings.push(...primaryKeyFindings(t));
1785
+ for (const i of analysis.issues) {
1786
+ if (i.level === "error" || HANDLED_CODES.has(i.code)) continue;
1787
+ findings.push({
1788
+ kind: "analyzer",
1789
+ level: "warn",
1790
+ ...splitPath(i.path),
1791
+ message: i.message,
1792
+ hint: i.hint
1793
+ });
1794
+ }
1795
+ const columns = analysis.tables.reduce((n, t) => n + t.columns.length, 0);
1796
+ const checks = analysis.tables.reduce((n, t) => n + (t.checks?.length ?? 0), 0);
1797
+ return {
1798
+ schema: schemaPath,
1799
+ dialect: analysis.dialect,
1800
+ ok: findings.length === 0,
1801
+ counts: { tables: analysis.tables.length, columns, checks, findings: findings.length },
1802
+ findings
1803
+ };
1804
+ }
1805
+ var SECTIONS = [
1806
+ {
1807
+ kinds: ["unknown-column"],
1808
+ title: "Columns DRZL cannot type",
1809
+ why: "These get a validator that accepts any value."
1810
+ },
1811
+ {
1812
+ kinds: ["check-declined", "check-unknown-column", "check-not-scalar"],
1813
+ title: "CHECK constraints DRZL does not enforce",
1814
+ why: "Your database still enforces these. Nothing DRZL generates does."
1815
+ },
1816
+ {
1817
+ kinds: ["no-primary-key", "partial-primary-key"],
1818
+ title: "Primary keys the generators cannot use",
1819
+ why: "The generated getById, update and delete are keyed on one column."
1820
+ },
1821
+ {
1822
+ kinds: ["analyzer"],
1823
+ title: "Other findings",
1824
+ why: "Reported by the analyzer while reading the schema."
1825
+ }
1826
+ ];
1827
+ function wrap(text, indent, first = indent, width = 96) {
1828
+ const lines = [];
1829
+ let line = "";
1830
+ for (const word of text.split(/\s+/)) {
1831
+ if (line && `${line} ${word}`.length + indent.length > width) {
1832
+ lines.push(line);
1833
+ line = word;
1834
+ } else {
1835
+ line = line ? `${line} ${word}` : word;
1836
+ }
1837
+ }
1838
+ if (line) lines.push(line);
1839
+ return lines.map((l, i) => (i === 0 ? first : indent) + l).join("\n");
1840
+ }
1841
+ function renderDoctorReport(report) {
1842
+ const out = [];
1843
+ const plural = (n, one) => `${n} ${one}${n === 1 ? "" : "s"}`;
1844
+ out.push(import_chalk.default.bold(`DRZL doctor ${report.schema}`));
1845
+ out.push(
1846
+ import_chalk.default.dim(
1847
+ `${report.dialect}, ${plural(report.counts.tables, "table")}, ${plural(report.counts.columns, "column")}, ${plural(report.counts.checks, "CHECK constraint")}`
1848
+ )
1849
+ );
1850
+ out.push("");
1851
+ if (report.ok) {
1852
+ out.push(import_chalk.default.green("Nothing to report."));
1853
+ out.push(import_chalk.default.dim(" Every column has a type DRZL can describe."));
1854
+ out.push(import_chalk.default.dim(" Every CHECK constraint is translated into the generated validators."));
1855
+ out.push(import_chalk.default.dim(" Every table has a primary key the generators can use."));
1856
+ return out.join("\n");
1857
+ }
1858
+ const fatal = report.findings.filter((f) => f.level === "error");
1859
+ if (fatal.length) {
1860
+ out.push(import_chalk.default.red("DRZL could not read this schema"));
1861
+ out.push(import_chalk.default.dim(" Nothing else could be checked."));
1862
+ out.push("");
1863
+ for (const f of fatal) {
1864
+ out.push(wrap(f.message, " ", ` ${import_chalk.default.dim("-")} `));
1865
+ if (f.hint) out.push(import_chalk.default.dim(wrap(f.hint, " ")));
1866
+ }
1867
+ out.push("");
1868
+ }
1869
+ for (const section of SECTIONS) {
1870
+ const mine = report.findings.filter(
1871
+ (f) => f.level !== "error" && section.kinds.includes(f.kind)
1872
+ );
1873
+ if (!mine.length) continue;
1874
+ out.push(import_chalk.default.yellow(`${section.title} (${mine.length})`));
1875
+ out.push(import_chalk.default.dim(` ${section.why}`));
1876
+ out.push("");
1877
+ const groups = /* @__PURE__ */ new Map();
1878
+ for (const f of mine) {
1879
+ const key = f.hint ?? "";
1880
+ groups.set(key, [...groups.get(key) ?? [], f]);
1881
+ }
1882
+ for (const [hint, items] of groups) {
1883
+ for (const f of items) out.push(wrap(f.message, " ", ` ${import_chalk.default.dim("-")} `));
1884
+ if (hint) out.push(import_chalk.default.dim(wrap(hint, " ")));
1885
+ out.push("");
1886
+ }
1887
+ }
1888
+ out.push(
1889
+ import_chalk.default.bold(`${plural(report.counts.findings, "finding")} in ${report.schema}.`) + import_chalk.default.dim(
1890
+ 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."
1891
+ )
1892
+ );
1893
+ return out.join("\n");
1894
+ }
1895
+
1638
1896
  // src/drift.ts
1639
1897
  var import_node_fs = require("fs");
1640
1898
  var import_node_path = __toESM(require("path"), 1);
@@ -1711,7 +1969,7 @@ async function loadGenerator(specifier, load) {
1711
1969
  }
1712
1970
 
1713
1971
  // src/sponsor.ts
1714
- var import_chalk = __toESM(require("chalk"), 1);
1972
+ var import_chalk2 = __toESM(require("chalk"), 1);
1715
1973
  var import_node_fs2 = require("fs");
1716
1974
  var import_node_path2 = __toESM(require("path"), 1);
1717
1975
  var CACHE_DIR = import_node_path2.default.join(process.cwd(), "node_modules", ".cache", "@drzl");
@@ -1725,9 +1983,9 @@ var tips = [
1725
1983
  "Need JSON Schema or OpenAPI? The json-schema generator emits both, with no runtime dependency.",
1726
1984
  "Use output headers to track generated files and trim noisy diffs."
1727
1985
  ];
1728
- var green = (msg) => import_chalk.default.hex("#6ee7b7")(msg);
1729
- var cyan = (msg) => import_chalk.default.cyan(msg);
1730
- var gray = (msg) => import_chalk.default.gray(msg);
1986
+ var green = (msg) => import_chalk2.default.hex("#6ee7b7")(msg);
1987
+ var cyan = (msg) => import_chalk2.default.cyan(msg);
1988
+ var gray = (msg) => import_chalk2.default.gray(msg);
1731
1989
  function maybeShowSponsorMessage({
1732
1990
  reason = "generate",
1733
1991
  minIntervalMs = DEFAULT_INTERVAL_MS,
@@ -1816,13 +2074,13 @@ var CLI_VERSION = readCliVersion();
1816
2074
  function reportGeneratorFailure(kind, e) {
1817
2075
  if (e instanceof GeneratorNotInstalledError) {
1818
2076
  console.error(
1819
- import_chalk2.default.red(`The ${kind} generator is not installed.`),
1820
- import_chalk2.default.yellow(`
2077
+ import_chalk3.default.red(`The ${kind} generator is not installed.`),
2078
+ import_chalk3.default.yellow(`
1821
2079
  Install with: npm install ${e.specifier}`)
1822
2080
  );
1823
2081
  return;
1824
2082
  }
1825
- console.error(import_chalk2.default.red(`The ${kind} generator failed:`), e?.message ?? e);
2083
+ console.error(import_chalk3.default.red(`The ${kind} generator failed:`), e?.message ?? e);
1826
2084
  }
1827
2085
  var program = new import_commander.Command();
1828
2086
  program.name("drzl").description("DRZL - Drizzle Developer Toolkit").version(CLI_VERSION);
@@ -1849,9 +2107,9 @@ program.command("analyze").argument("<schema>", "path to drizzle schema (TS)").o
1849
2107
  } else if (opts.out) {
1850
2108
  const fs3 = await import("fs/promises");
1851
2109
  await fs3.writeFile(opts.out, json, "utf8");
1852
- spinner?.succeed(import_chalk2.default.green(`Analysis written to ${opts.out} in ${ms}ms`));
2110
+ spinner?.succeed(import_chalk3.default.green(`Analysis written to ${opts.out} in ${ms}ms`));
1853
2111
  } else {
1854
- spinner?.succeed(import_chalk2.default.green(`Analyzed in ${ms}ms`));
2112
+ spinner?.succeed(import_chalk3.default.green(`Analyzed in ${ms}ms`));
1855
2113
  console.log(json);
1856
2114
  }
1857
2115
  process.exit(res.issues.some((i) => i.level === "error") ? 2 : 0);
@@ -1861,7 +2119,48 @@ program.command("analyze").argument("<schema>", "path to drizzle schema (TS)").o
1861
2119
  console.log(JSON.stringify({ event: "error", code: "DRZL_CLI_ANALYZE", message: msg }));
1862
2120
  else
1863
2121
  console.error(
1864
- import_chalk2.default.red("Analyze failed (DRZL_CLI_ANALYZE):"),
2122
+ import_chalk3.default.red("Analyze failed (DRZL_CLI_ANALYZE):"),
2123
+ msg,
2124
+ "\nTip: run with --json for structured output."
2125
+ );
2126
+ process.exit(1);
2127
+ }
2128
+ });
2129
+ 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) => {
2130
+ try {
2131
+ let target = schema;
2132
+ if (!target) {
2133
+ const cfg = await loadConfig(opts.config);
2134
+ target = cfg?.schema;
2135
+ }
2136
+ if (!target) {
2137
+ const msg = "No schema given. Pass a path, or run from a directory with a drzl.config.";
2138
+ if (opts.json)
2139
+ console.log(JSON.stringify({ event: "error", code: "DRZL_CLI_DOCTOR", message: msg }));
2140
+ else console.error(import_chalk3.default.red("Doctor failed (DRZL_CLI_DOCTOR):"), msg);
2141
+ process.exit(1);
2142
+ return;
2143
+ }
2144
+ const analyzer = new import_analyzer.SchemaAnalyzer(target);
2145
+ const analysis = await analyzer.analyze({
2146
+ includeRelations: true,
2147
+ validateConstraints: true
2148
+ });
2149
+ const report = buildDoctorReport(analysis, target);
2150
+ if (opts.json) console.log(JSON.stringify(report, null, 2));
2151
+ else console.log(renderDoctorReport(report));
2152
+ if (report.findings.some((f) => f.level === "error")) {
2153
+ process.exit(1);
2154
+ return;
2155
+ }
2156
+ process.exit(opts.strict && report.findings.length ? 2 : 0);
2157
+ } catch (e) {
2158
+ const msg = e?.message ?? String(e);
2159
+ if (opts.json)
2160
+ console.log(JSON.stringify({ event: "error", code: "DRZL_CLI_DOCTOR", message: msg }));
2161
+ else
2162
+ console.error(
2163
+ import_chalk3.default.red("Doctor failed (DRZL_CLI_DOCTOR):"),
1865
2164
  msg,
1866
2165
  "\nTip: run with --json for structured output."
1867
2166
  );
@@ -1876,7 +2175,7 @@ program.command("generate").description("Run configured generators (drzl.config.
1876
2175
  const cfg = await loadConfig(opts.config);
1877
2176
  if (!cfg) {
1878
2177
  console.error(
1879
- import_chalk2.default.red("No config found (DRZL_CFG_001). Create drzl.config.ts or pass --config.")
2178
+ import_chalk3.default.red("No config found (DRZL_CFG_001). Create drzl.config.ts or pass --config.")
1880
2179
  );
1881
2180
  process.exit(2);
1882
2181
  return;
@@ -1921,8 +2220,8 @@ program.command("generate").description("Run configured generators (drzl.config.
1921
2220
  onProgress: ({ index }) => progress.update(index)
1922
2221
  });
1923
2222
  progress.stop();
1924
- (0, import_ora.default)().succeed(import_chalk2.default.green(`Generated (${g.kind}): ${files.length} files`));
1925
- files.forEach((f) => console.log(" -", import_chalk2.default.cyan(f)));
2223
+ (0, import_ora.default)().succeed(import_chalk3.default.green(`Generated (${g.kind}): ${files.length} files`));
2224
+ files.forEach((f) => console.log(" -", import_chalk3.default.cyan(f)));
1926
2225
  } else if (g.kind === "trpc") {
1927
2226
  try {
1928
2227
  const { TRPCGenerator: TRPCGenerator2 } = await loadGenerator(
@@ -1935,8 +2234,8 @@ program.command("generate").description("Run configured generators (drzl.config.
1935
2234
  onProgress: ({ index }) => progress.update(index)
1936
2235
  });
1937
2236
  progress.stop();
1938
- (0, import_ora.default)().succeed(import_chalk2.default.green(`Generated (trpc): ${files.length} files`));
1939
- files.forEach((f) => console.log(" -", import_chalk2.default.cyan(f)));
2237
+ (0, import_ora.default)().succeed(import_chalk3.default.green(`Generated (trpc): ${files.length} files`));
2238
+ files.forEach((f) => console.log(" -", import_chalk3.default.cyan(f)));
1940
2239
  } catch (e) {
1941
2240
  progress.stop();
1942
2241
  reportGeneratorFailure(g.kind, e);
@@ -1965,8 +2264,8 @@ program.command("generate").description("Run configured generators (drzl.config.
1965
2264
  databaseInjection: g.databaseInjection
1966
2265
  });
1967
2266
  progress.stop();
1968
- (0, import_ora.default)().succeed(import_chalk2.default.green(`Generated (service): ${files.length} files`));
1969
- files.forEach((f) => console.log(" -", import_chalk2.default.cyan(f)));
2267
+ (0, import_ora.default)().succeed(import_chalk3.default.green(`Generated (service): ${files.length} files`));
2268
+ files.forEach((f) => console.log(" -", import_chalk3.default.cyan(f)));
1970
2269
  } catch (e) {
1971
2270
  progress.stop();
1972
2271
  reportGeneratorFailure(g.kind, e);
@@ -1984,8 +2283,8 @@ program.command("generate").description("Run configured generators (drzl.config.
1984
2283
  validationOptions(g, cfg, target, { schemaTypes: true })
1985
2284
  );
1986
2285
  progress.stop();
1987
- (0, import_ora.default)().succeed(import_chalk2.default.green(`Generated (zod): ${files.length} files`));
1988
- files.forEach((f) => console.log(" -", import_chalk2.default.cyan(f)));
2286
+ (0, import_ora.default)().succeed(import_chalk3.default.green(`Generated (zod): ${files.length} files`));
2287
+ files.forEach((f) => console.log(" -", import_chalk3.default.cyan(f)));
1989
2288
  } catch (e) {
1990
2289
  progress.stop();
1991
2290
  reportGeneratorFailure(g.kind, e);
@@ -2003,8 +2302,8 @@ program.command("generate").description("Run configured generators (drzl.config.
2003
2302
  validationOptions(g, cfg, target, { schemaTypes: true })
2004
2303
  );
2005
2304
  progress.stop();
2006
- (0, import_ora.default)().succeed(import_chalk2.default.green(`Generated (valibot): ${files.length} files`));
2007
- files.forEach((f) => console.log(" -", import_chalk2.default.cyan(f)));
2305
+ (0, import_ora.default)().succeed(import_chalk3.default.green(`Generated (valibot): ${files.length} files`));
2306
+ files.forEach((f) => console.log(" -", import_chalk3.default.cyan(f)));
2008
2307
  } catch (e) {
2009
2308
  progress.stop();
2010
2309
  reportGeneratorFailure(g.kind, e);
@@ -2022,8 +2321,8 @@ program.command("generate").description("Run configured generators (drzl.config.
2022
2321
  validationOptions(g, cfg, target, { schemaTypes: false })
2023
2322
  );
2024
2323
  progress.stop();
2025
- (0, import_ora.default)().succeed(import_chalk2.default.green(`Generated (arktype): ${files.length} files`));
2026
- files.forEach((f) => console.log(" -", import_chalk2.default.cyan(f)));
2324
+ (0, import_ora.default)().succeed(import_chalk3.default.green(`Generated (arktype): ${files.length} files`));
2325
+ files.forEach((f) => console.log(" -", import_chalk3.default.cyan(f)));
2027
2326
  } catch (e) {
2028
2327
  progress.stop();
2029
2328
  reportGeneratorFailure(g.kind, e);
@@ -2039,8 +2338,8 @@ program.command("generate").description("Run configured generators (drzl.config.
2039
2338
  const target = g.path ?? "src/validators/json-schema";
2040
2339
  const files = await gen.generate(jsonSchemaOptions(g, cfg, target));
2041
2340
  progress.stop();
2042
- (0, import_ora.default)().succeed(import_chalk2.default.green(`Generated (json-schema): ${files.length} files`));
2043
- files.forEach((f) => console.log(" -", import_chalk2.default.cyan(f)));
2341
+ (0, import_ora.default)().succeed(import_chalk3.default.green(`Generated (json-schema): ${files.length} files`));
2342
+ files.forEach((f) => console.log(" -", import_chalk3.default.cyan(f)));
2044
2343
  } catch (e) {
2045
2344
  progress.stop();
2046
2345
  reportGeneratorFailure(g.kind, e);
@@ -2058,8 +2357,8 @@ program.command("generate").description("Run configured generators (drzl.config.
2058
2357
  validationOptions(g, cfg, target, { schemaTypes: true })
2059
2358
  );
2060
2359
  progress.stop();
2061
- (0, import_ora.default)().succeed(import_chalk2.default.green(`Generated (typebox): ${files.length} files`));
2062
- files.forEach((f) => console.log(" -", import_chalk2.default.cyan(f)));
2360
+ (0, import_ora.default)().succeed(import_chalk3.default.green(`Generated (typebox): ${files.length} files`));
2361
+ files.forEach((f) => console.log(" -", import_chalk3.default.cyan(f)));
2063
2362
  } catch (e) {
2064
2363
  progress.stop();
2065
2364
  reportGeneratorFailure(g.kind, e);
@@ -2072,22 +2371,22 @@ program.command("generate").description("Run configured generators (drzl.config.
2072
2371
  const drift = diffSnapshots(driftBefore, after);
2073
2372
  await restoreSnapshot(driftBefore, after);
2074
2373
  if (drift.length) {
2075
- console.error(import_chalk2.default.red(`
2374
+ console.error(import_chalk3.default.red(`
2076
2375
  Generated output is out of date (${drift.length} file(s)):`));
2077
2376
  for (const d of drift) {
2078
2377
  const mark = d.status === "added" ? "+" : d.status === "removed" ? "-" : "~";
2079
2378
  console.error(
2080
- ` ${mark} ${import_chalk2.default.yellow(d.status.padEnd(8))} ${path5.relative(process.cwd(), d.file)}`
2379
+ ` ${mark} ${import_chalk3.default.yellow(d.status.padEnd(8))} ${path5.relative(process.cwd(), d.file)}`
2081
2380
  );
2082
2381
  }
2083
2382
  console.error(
2084
- import_chalk2.default.dim(
2383
+ import_chalk3.default.dim(
2085
2384
  "\nRun `drzl generate` and commit the result. Nothing was written by this check."
2086
2385
  )
2087
2386
  );
2088
2387
  process.exit(1);
2089
2388
  }
2090
- console.log(import_chalk2.default.green("Generated output is up to date."));
2389
+ console.log(import_chalk3.default.green("Generated output is up to date."));
2091
2390
  return;
2092
2391
  }
2093
2392
  if (cfg.generators.length) {
@@ -2095,7 +2394,7 @@ Generated output is out of date (${drift.length} file(s)):`));
2095
2394
  }
2096
2395
  } catch (e) {
2097
2396
  console.error(
2098
- import_chalk2.default.red("Generate failed (DRZL_GEN_001):"),
2397
+ import_chalk3.default.red("Generate failed (DRZL_GEN_001):"),
2099
2398
  e?.message ?? e,
2100
2399
  "\nTip: check your drzl.config.ts and template path."
2101
2400
  );
@@ -2115,10 +2414,10 @@ program.command("generate:orpc").argument("<schema>", "path to drizzle schema (T
2115
2414
  template: opts.template,
2116
2415
  includeRelations: !!opts.includeRelations
2117
2416
  });
2118
- console.log(import_chalk2.default.green(`Generated:`), files.map((f) => import_chalk2.default.cyan(f)).join(", "));
2417
+ console.log(import_chalk3.default.green(`Generated:`), files.map((f) => import_chalk3.default.cyan(f)).join(", "));
2119
2418
  maybeShowSponsorMessage({ reason: "generate:orpc" });
2120
2419
  } catch (e) {
2121
- console.error(import_chalk2.default.red("Generate orpc failed:"), e?.message ?? e);
2420
+ console.error(import_chalk3.default.red("Generate orpc failed:"), e?.message ?? e);
2122
2421
  process.exit(1);
2123
2422
  }
2124
2423
  });
@@ -2142,7 +2441,7 @@ program.command("generate:trpc").argument("<schema>", "path to drizzle schema (T
2142
2441
  // cannot become the branch that forgets it.
2143
2442
  servicesDir: opts.servicesDir
2144
2443
  });
2145
- console.log(import_chalk2.default.green(`Generated:`), files.map((f) => import_chalk2.default.cyan(f)).join(", "));
2444
+ console.log(import_chalk3.default.green(`Generated:`), files.map((f) => import_chalk3.default.cyan(f)).join(", "));
2146
2445
  maybeShowSponsorMessage({ reason: "generate:trpc" });
2147
2446
  } catch (e) {
2148
2447
  reportGeneratorFailure("trpc", e);
@@ -2152,7 +2451,7 @@ program.command("generate:trpc").argument("<schema>", "path to drizzle schema (T
2152
2451
  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) => {
2153
2452
  let cfg = await loadConfig(opts.config);
2154
2453
  if (!cfg) {
2155
- console.error(import_chalk2.default.red("No config found. Create drzl.config.ts or pass --config."));
2454
+ console.error(import_chalk3.default.red("No config found. Create drzl.config.ts or pass --config."));
2156
2455
  process.exit(2);
2157
2456
  return;
2158
2457
  }
@@ -2244,7 +2543,7 @@ program.command("watch").description("Watch schema and regenerate on changes").o
2244
2543
  })
2245
2544
  );
2246
2545
  } else {
2247
- console.log(import_chalk2.default.green("Analyze complete."));
2546
+ console.log(import_chalk3.default.green("Analyze complete."));
2248
2547
  }
2249
2548
  return;
2250
2549
  }
@@ -2274,8 +2573,8 @@ program.command("watch").description("Watch schema and regenerate on changes").o
2274
2573
  servicesDir
2275
2574
  });
2276
2575
  opts.json ? console.log(JSON.stringify({ event: "generate_complete", kind: g.kind, files })) : console.log(
2277
- import_chalk2.default.green(`Generated (${g.kind}):`),
2278
- files.map((f) => import_chalk2.default.cyan(f)).join(", ")
2576
+ import_chalk3.default.green(`Generated (${g.kind}):`),
2577
+ files.map((f) => import_chalk3.default.cyan(f)).join(", ")
2279
2578
  );
2280
2579
  newFiles.push(...files);
2281
2580
  } else if (g.kind === "trpc") {
@@ -2287,8 +2586,8 @@ program.command("watch").description("Watch schema and regenerate on changes").o
2287
2586
  const gen = new TRPCGenerator2(analysis);
2288
2587
  const { files } = await gen.generate(trpcOptions(g, cfg, servicesDir));
2289
2588
  opts.json ? console.log(JSON.stringify({ event: "generate_complete", kind: g.kind, files })) : console.log(
2290
- import_chalk2.default.green(`Generated (trpc): ${files.length} files`),
2291
- files.map((f) => import_chalk2.default.cyan(f)).join(", ")
2589
+ import_chalk3.default.green(`Generated (trpc): ${files.length} files`),
2590
+ files.map((f) => import_chalk3.default.cyan(f)).join(", ")
2292
2591
  );
2293
2592
  newFiles.push(...files);
2294
2593
  } catch (e) {
@@ -2314,8 +2613,8 @@ program.command("watch").description("Watch schema and regenerate on changes").o
2314
2613
  databaseInjection: g.databaseInjection
2315
2614
  });
2316
2615
  opts.json ? console.log(JSON.stringify({ event: "generate_complete", kind: g.kind, files })) : console.log(
2317
- import_chalk2.default.green(`Generated (service): ${files.length} files`),
2318
- files.map((f) => import_chalk2.default.cyan(f)).join(", ")
2616
+ import_chalk3.default.green(`Generated (service): ${files.length} files`),
2617
+ files.map((f) => import_chalk3.default.cyan(f)).join(", ")
2319
2618
  );
2320
2619
  newFiles.push(...files);
2321
2620
  } catch (e) {
@@ -2334,8 +2633,8 @@ program.command("watch").description("Watch schema and regenerate on changes").o
2334
2633
  validationOptions(g, cfg, target, { schemaTypes: true })
2335
2634
  );
2336
2635
  opts.json ? console.log(JSON.stringify({ event: "generate_complete", kind: g.kind, files })) : console.log(
2337
- import_chalk2.default.green(`Generated (zod): ${files.length} files`),
2338
- files.map((f) => import_chalk2.default.cyan(f)).join(", ")
2636
+ import_chalk3.default.green(`Generated (zod): ${files.length} files`),
2637
+ files.map((f) => import_chalk3.default.cyan(f)).join(", ")
2339
2638
  );
2340
2639
  newFiles.push(...files);
2341
2640
  } catch (e) {
@@ -2354,8 +2653,8 @@ program.command("watch").description("Watch schema and regenerate on changes").o
2354
2653
  validationOptions(g, cfg, target, { schemaTypes: true })
2355
2654
  );
2356
2655
  opts.json ? console.log(JSON.stringify({ event: "generate_complete", kind: g.kind, files })) : console.log(
2357
- import_chalk2.default.green(`Generated (valibot): ${files.length} files`),
2358
- files.map((f) => import_chalk2.default.cyan(f)).join(", ")
2656
+ import_chalk3.default.green(`Generated (valibot): ${files.length} files`),
2657
+ files.map((f) => import_chalk3.default.cyan(f)).join(", ")
2359
2658
  );
2360
2659
  newFiles.push(...files);
2361
2660
  } catch (e) {
@@ -2374,8 +2673,8 @@ program.command("watch").description("Watch schema and regenerate on changes").o
2374
2673
  validationOptions(g, cfg, target, { schemaTypes: false })
2375
2674
  );
2376
2675
  opts.json ? console.log(JSON.stringify({ event: "generate_complete", kind: g.kind, files })) : console.log(
2377
- import_chalk2.default.green(`Generated (arktype): ${files.length} files`),
2378
- files.map((f) => import_chalk2.default.cyan(f)).join(", ")
2676
+ import_chalk3.default.green(`Generated (arktype): ${files.length} files`),
2677
+ files.map((f) => import_chalk3.default.cyan(f)).join(", ")
2379
2678
  );
2380
2679
  newFiles.push(...files);
2381
2680
  } catch (e) {
@@ -2394,8 +2693,8 @@ program.command("watch").description("Watch schema and regenerate on changes").o
2394
2693
  validationOptions(g, cfg, target, { schemaTypes: true })
2395
2694
  );
2396
2695
  opts.json ? console.log(JSON.stringify({ event: "generate_complete", kind: g.kind, files })) : console.log(
2397
- import_chalk2.default.green(`Generated (typebox): ${files.length} files`),
2398
- files.map((f) => import_chalk2.default.cyan(f)).join(", ")
2696
+ import_chalk3.default.green(`Generated (typebox): ${files.length} files`),
2697
+ files.map((f) => import_chalk3.default.cyan(f)).join(", ")
2399
2698
  );
2400
2699
  newFiles.push(...files);
2401
2700
  } catch (e) {
@@ -2412,8 +2711,8 @@ program.command("watch").description("Watch schema and regenerate on changes").o
2412
2711
  const target = g.path ?? "src/validators/json-schema";
2413
2712
  const files = await gen.generate(jsonSchemaOptions(g, cfg, target));
2414
2713
  opts.json ? console.log(JSON.stringify({ event: "generate_complete", kind: g.kind, files })) : console.log(
2415
- import_chalk2.default.green(`Generated (json-schema): ${files.length} files`),
2416
- files.map((f) => import_chalk2.default.cyan(f)).join(", ")
2714
+ import_chalk3.default.green(`Generated (json-schema): ${files.length} files`),
2715
+ files.map((f) => import_chalk3.default.cyan(f)).join(", ")
2417
2716
  );
2418
2717
  newFiles.push(...files);
2419
2718
  } catch (e) {
@@ -2425,8 +2724,8 @@ program.command("watch").description("Watch schema and regenerate on changes").o
2425
2724
  const added = newFiles.filter((f) => !lastFiles.includes(f));
2426
2725
  const removed = lastFiles.filter((f) => !newFiles.includes(f));
2427
2726
  opts.json ? console.log(JSON.stringify({ event: "diff", added, removed })) : (() => {
2428
- if (added.length) console.log(import_chalk2.default.blue(`Added: ${added.join(", ")}`));
2429
- if (removed.length) console.log(import_chalk2.default.yellow(`Removed: ${removed.join(", ")}`));
2727
+ if (added.length) console.log(import_chalk3.default.blue(`Added: ${added.join(", ")}`));
2728
+ if (removed.length) console.log(import_chalk3.default.yellow(`Removed: ${removed.join(", ")}`));
2430
2729
  })();
2431
2730
  if (newFiles.length && !opts.json) {
2432
2731
  const reason = opts.pipeline && opts.pipeline !== "all" ? `watch:${opts.pipeline}` : "watch";
@@ -2434,7 +2733,7 @@ program.command("watch").description("Watch schema and regenerate on changes").o
2434
2733
  }
2435
2734
  lastFiles = newFiles;
2436
2735
  } catch (e) {
2437
- opts.json ? console.log(JSON.stringify({ event: "error", message: String(e?.message ?? e) })) : console.error(import_chalk2.default.red("Watch pipeline failed:"), e?.message ?? e);
2736
+ opts.json ? console.log(JSON.stringify({ event: "error", message: String(e?.message ?? e) })) : console.error(import_chalk3.default.red("Watch pipeline failed:"), e?.message ?? e);
2438
2737
  }
2439
2738
  };
2440
2739
  const debounced = Number(opts.debounce) || 200;
@@ -2459,12 +2758,12 @@ program.command("watch").description("Watch schema and regenerate on changes").o
2459
2758
  );
2460
2759
  } else {
2461
2760
  console.log(
2462
- import_chalk2.default.gray(
2761
+ import_chalk3.default.gray(
2463
2762
  "Watching:\n " + Array.from(currentTargets).map((p) => path5.relative(process.cwd(), p)).join("\n ")
2464
2763
  )
2465
2764
  );
2466
2765
  }
2467
- watcher.on("add", (p) => trigger(p)).on("change", (p) => trigger(p)).on("unlink", (p) => trigger(p)).on("error", (err) => console.error(import_chalk2.default.red("Watcher error:"), err));
2766
+ watcher.on("add", (p) => trigger(p)).on("change", (p) => trigger(p)).on("unlink", (p) => trigger(p)).on("error", (err) => console.error(import_chalk3.default.red("Watcher error:"), err));
2468
2767
  await run();
2469
2768
  });
2470
2769
  program.command("init").description("Scaffold a drzl.config.ts").option("-y, --yes", "accept defaults").action(async (_opts) => {
@@ -2484,9 +2783,9 @@ program.command("init").description("Scaffold a drzl.config.ts").option("-y, --y
2484
2783
  `;
2485
2784
  try {
2486
2785
  await fs3.writeFile(target, template, { flag: "wx" });
2487
- console.log(import_chalk2.default.green(`Created ${target}`));
2786
+ console.log(import_chalk3.default.green(`Created ${target}`));
2488
2787
  } catch (e) {
2489
- console.error(import_chalk2.default.red("Init failed:"), e?.message ?? e);
2788
+ console.error(import_chalk3.default.red("Init failed:"), e?.message ?? e);
2490
2789
  process.exit(1);
2491
2790
  }
2492
2791
  });
@@ -2494,13 +2793,14 @@ function reportWideColumns(issues) {
2494
2793
  const wide = issues.filter((i) => i.code === "DRZL_ANL_UNKNOWN_COLUMN");
2495
2794
  if (!wide.length) return;
2496
2795
  console.warn(
2497
- import_chalk2.default.yellow(`
2796
+ import_chalk3.default.yellow(`
2498
2797
  ${wide.length} column${wide.length === 1 ? "" : "s"} could not be typed:`)
2499
2798
  );
2500
- for (const i of wide.slice(0, 10)) console.warn(import_chalk2.default.gray(` - ${i.message}`));
2501
- if (wide.length > 10) console.warn(import_chalk2.default.gray(` ... and ${wide.length - 10} more`));
2799
+ for (const i of wide.slice(0, 10)) console.warn(import_chalk3.default.gray(` - ${i.message}`));
2800
+ if (wide.length > 10) console.warn(import_chalk3.default.gray(` ... and ${wide.length - 10} more`));
2502
2801
  const hints = [...new Set(wide.map((i) => i.hint).filter(Boolean))];
2503
- for (const h of hints) console.warn(import_chalk2.default.gray(` ${h}`));
2802
+ for (const h of hints) console.warn(import_chalk3.default.gray(` ${h}`));
2803
+ console.warn(import_chalk3.default.gray(" Run `drzl doctor` for the full report."));
2504
2804
  }
2505
2805
  program.parseAsync(process.argv);
2506
2806
  //# sourceMappingURL=cli.cjs.map