@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/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_core3.resolveAffix)({
264
+ const sharedAffix = (0, import_validation_core4.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_core3.resolveConfiguredImport)(
274
+ const spec = (0, import_validation_core4.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_core3.schemaName)(mode, table.tsName, sharedAffix);
281
+ const exported = (0, import_validation_core4.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_core3.importSpecifier)(
288
+ `import { ${[builder, "router"].sort().join(", ")} } from '${(0, import_validation_core4.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_core3.importSpecifier)(`./${BASE_MODULE}.ts`, opts.importExtension);
337
+ const baseSpec = (0, import_validation_core4.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_core3.importSpecifier)(
355
+ rel: (0, import_validation_core4.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_core3.importSpecifier)(`${dir}/${singularize(table.tsName)}Service.ts`, opts.importExtension);
396
+ return (0, import_validation_core4.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_core3, TRPC_MAJOR, q, LIB_IMPORTS, LIB_USAGE, LIBS, cap, singularize, isIdent, BASE_MODULE, TRPCGenerator, index_default;
416
+ var import_validation_core4, 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_core3 = require("@drzl/validation-core");
420
+ import_validation_core4 = 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_core3.formatCode)(
513
+ const formatted = await (0, import_validation_core4.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_core5.COLUMN_FORMATS[c.format]) out.pattern = import_validation_core5.COLUMN_FORMATS[c.format];
609
+ else if (c.format && import_validation_core6.COLUMN_FORMATS[c.format]) out.pattern = import_validation_core6.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_core5.isIntegerColumn)(c) ? "integer" : "number" };
616
+ const out = { type: (0, import_validation_core6.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_core5.parseCheck)(k.expression, k.name));
753
+ const parsed = (table.checks ?? []).map((k) => (0, import_validation_core6.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_core5.insertColumns)(table), "insert"),
768
- update: build2((0, import_validation_core5.updateColumns)(table), "update"),
769
- select: build2((0, import_validation_core5.selectColumns)(table), "select")
767
+ insert: build2((0, import_validation_core6.insertColumns)(table), "insert"),
768
+ update: build2((0, import_validation_core6.updateColumns)(table), "update"),
769
+ select: build2((0, import_validation_core6.selectColumns)(table), "select")
770
770
  };
771
771
  }
772
772
  function componentsDocument(tables, opts = {}) {
@@ -793,6 +793,7 @@ function foreignKeysOf(table) {
793
793
  return table.columns.filter((c) => c.references).map((c) => ({
794
794
  columns: [c.name],
795
795
  foreignTable: c.references.table,
796
+ ...c.references.schema ? { foreignSchema: c.references.schema } : {},
796
797
  foreignColumns: [c.references.column]
797
798
  }));
798
799
  }
@@ -818,11 +819,11 @@ function build(tables, opts) {
818
819
  const clash = operationIds.get(id);
819
820
  if (clash !== void 0) {
820
821
  throw new Error(
821
- `@drzl/generator-json-schema: the operationId "${id}" would be emitted for both "${clash}" and "${table.name}". An operationId is the method name a client generator derives, and the specification requires it to be unique across the document.`
822
+ `@drzl/generator-json-schema: the operationId "${id}" would be emitted for both "${clash}" and "${(0, import_analyzer.qualifiedTableName)(table)}". An operationId is the method name a client generator derives, and the specification requires it to be unique across the document.`
822
823
  );
823
824
  }
824
- operationIds.set(id, table.name);
825
- return { operationId: id, tags: [table.name], ...rest };
825
+ operationIds.set(id, (0, import_analyzer.qualifiedTableName)(table));
826
+ return { operationId: id, tags: [(0, import_analyzer.qualifiedTableName)(table)], ...rest };
826
827
  };
827
828
  const built = tables.map((table) => ({
828
829
  table,
@@ -840,7 +841,10 @@ function build(tables, opts) {
840
841
  if (table.readOnly) {
841
842
  notes.push("It refuses every write, so only reads are described.");
842
843
  }
843
- tags.push({ name: table.name, description: [`Table "${table.name}".`, ...notes].join(" ") });
844
+ tags.push({
845
+ name: (0, import_analyzer.qualifiedTableName)(table),
846
+ description: [`Table "${(0, import_analyzer.qualifiedTableName)(table)}".`, ...notes].join(" ")
847
+ });
844
848
  const T = pascal(table.tsName);
845
849
  const select = ref(componentName(table, "select"));
846
850
  const validationFailed = {
@@ -856,7 +860,7 @@ function build(tables, opts) {
856
860
  ...jsonBody(ref(ERROR_SCHEMA))
857
861
  });
858
862
  const collection = `/${segment}`;
859
- claim(collection, table.tsName, table.name);
863
+ claim(collection, table.tsName, (0, import_analyzer.qualifiedTableName)(table));
860
864
  const item = {
861
865
  get: operation(`list${T}`, table, {
862
866
  summary: `List every ${table.name} row.`,
@@ -885,7 +889,7 @@ function build(tables, opts) {
885
889
  paths[collection] = item;
886
890
  if (!key) continue;
887
891
  const itemPath = `${collection}/${key.map((c) => `{${c.name}}`).join("/")}`;
888
- claim(itemPath, table.tsName, table.name);
892
+ claim(itemPath, table.tsName, (0, import_analyzer.qualifiedTableName)(table));
889
893
  const parameters = key.map((c) => ({
890
894
  name: c.name,
891
895
  in: "path",
@@ -944,14 +948,18 @@ function build(tables, opts) {
944
948
  for (const child of built) {
945
949
  if (child.table === table) continue;
946
950
  const matching = foreignKeysOf(child.table).filter(
947
- (fk) => fk.foreignTable === table.name && fk.foreignColumns.length === key.length && fk.foreignColumns.every((c, i) => c === key[i].name)
951
+ (fk) => (
952
+ // Qualified on both sides. On the bare name a key pointing at `reporting.users` also
953
+ // answered for `public.users`, so the child was hung under a parent in another schema.
954
+ (0, import_analyzer.qualifiedForeignTable)(fk) === (0, import_analyzer.qualifiedTableName)(table) && fk.foreignColumns.length === key.length && fk.foreignColumns.every((c, i) => c === key[i].name)
955
+ )
948
956
  );
949
957
  if (matching.length !== 1) continue;
950
958
  const subPath = `${itemPath}/${child.segment}`;
951
959
  claim(
952
960
  subPath,
953
961
  `${table.tsName} -> ${child.table.tsName}`,
954
- `${table.name} -> ${child.table.name}`
962
+ `${(0, import_analyzer.qualifiedTableName)(table)} -> ${(0, import_analyzer.qualifiedTableName)(child.table)}`
955
963
  );
956
964
  paths[subPath] = {
957
965
  parameters,
@@ -995,9 +1003,9 @@ function openApiDocument(tables, opts = {}) {
995
1003
  function renderTableModule(table, affix, target, applyDefaults) {
996
1004
  const T = table.tsName;
997
1005
  const schemas = tableSchemas(table, { target, applyDefaults });
998
- const decl = (mode) => `export const ${(0, import_validation_core4.schemaName)(mode, T, affix)} = ${JSON.stringify(schemas[mode], null, 2)} as const;
1006
+ const decl = (mode) => `export const ${(0, import_validation_core5.schemaName)(mode, T, affix)} = ${JSON.stringify(schemas[mode], null, 2)} as const;
999
1007
 
1000
- export type ${(0, import_validation_core4.typeName)(mode, T, affix)} = typeof ${(0, import_validation_core4.schemaName)(mode, T, affix)};`;
1008
+ export type ${(0, import_validation_core5.typeName)(mode, T, affix)} = typeof ${(0, import_validation_core5.schemaName)(mode, T, affix)};`;
1001
1009
  return [decl("insert"), decl("update"), decl("select")].join("\n\n") + "\n";
1002
1010
  }
1003
1011
  function resolveDocument(opt) {
@@ -1013,12 +1021,13 @@ function buildHeader2(h) {
1013
1021
 
1014
1022
  `;
1015
1023
  }
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;
1024
+ var import_validation_core5, import_analyzer, import_validation_core6, DRAFT, UUID_FORMAT, base64, ERROR_SCHEMA, componentName, ref, pascal, modesFor, resourceSegment, jsonBody, errorSchema, DEFAULT_FILE_SUFFIX, JsonSchemaGenerator, index_default2;
1017
1025
  var init_dist2 = __esm({
1018
1026
  "../generator-json-schema/dist/index.js"() {
1019
1027
  "use strict";
1020
- import_validation_core4 = require("@drzl/validation-core");
1021
1028
  import_validation_core5 = require("@drzl/validation-core");
1029
+ import_analyzer = require("@drzl/analyzer");
1030
+ import_validation_core6 = require("@drzl/validation-core");
1022
1031
  DRAFT = "https://json-schema.org/draft/2020-12/schema";
1023
1032
  UUID_FORMAT = "uuid";
1024
1033
  base64 = (target) => target === "openapi-3.0" ? { type: "string", format: "byte" } : { type: "string", contentEncoding: "base64" };
@@ -1031,7 +1040,7 @@ var init_dist2 = __esm({
1031
1040
  ...table.readOnly || !key ? [] : ["update"],
1032
1041
  "select"
1033
1042
  ];
1034
- resourceSegment = (table) => encodeURIComponent(table.name);
1043
+ resourceSegment = (table) => table.schema ? `${encodeURIComponent(table.schema)}/${encodeURIComponent(table.name)}` : encodeURIComponent(table.name);
1035
1044
  jsonBody = (schema) => ({ content: { "application/json": { schema } } });
1036
1045
  errorSchema = () => ({
1037
1046
  title: "error",
@@ -1056,14 +1065,14 @@ var init_dist2 = __esm({
1056
1065
  const out = path6.resolve(process.cwd(), opts.outDir);
1057
1066
  const files = [];
1058
1067
  await fs3.mkdir(out, { recursive: true });
1059
- const affix = (0, import_validation_core4.resolveAffix)(opts);
1068
+ const affix = (0, import_validation_core5.resolveAffix)(opts);
1060
1069
  const fileSuffix = opts.fileSuffix ?? DEFAULT_FILE_SUFFIX;
1061
1070
  const target = opts.target ?? "draft-2020-12";
1062
1071
  const document = resolveDocument(opts.document);
1063
1072
  for (const table of this.analysis.tables) {
1064
- const filePath = path6.join(out, (0, import_validation_core4.moduleFileName)(table.tsName, fileSuffix));
1073
+ const filePath = path6.join(out, (0, import_validation_core5.moduleFileName)(table.tsName, fileSuffix));
1065
1074
  const code = renderTableModule(table, affix, target, !!opts.applyDefaults);
1066
- const formatted = await (0, import_validation_core4.formatCode)(
1075
+ const formatted = await (0, import_validation_core5.formatCode)(
1067
1076
  buildHeader2(opts.outputHeader) + code,
1068
1077
  filePath,
1069
1078
  opts.format
@@ -1081,7 +1090,7 @@ var init_dist2 = __esm({
1081
1090
  `;
1082
1091
  await fs3.writeFile(
1083
1092
  componentsPath,
1084
- await (0, import_validation_core4.formatCode)(buildHeader2(opts.outputHeader) + code, componentsPath, opts.format),
1093
+ await (0, import_validation_core5.formatCode)(buildHeader2(opts.outputHeader) + code, componentsPath, opts.format),
1085
1094
  "utf8"
1086
1095
  );
1087
1096
  files.push(componentsPath);
@@ -1102,7 +1111,7 @@ var init_dist2 = __esm({
1102
1111
  `;
1103
1112
  await fs3.writeFile(
1104
1113
  tsPath,
1105
- await (0, import_validation_core4.formatCode)(buildHeader2(opts.outputHeader) + code, tsPath, opts.format),
1114
+ await (0, import_validation_core5.formatCode)(buildHeader2(opts.outputHeader) + code, tsPath, opts.format),
1106
1115
  "utf8"
1107
1116
  );
1108
1117
  files.push(tsPath);
@@ -1116,9 +1125,9 @@ var init_dist2 = __esm({
1116
1125
  const ext = opts.importExtension === "none" ? "" : ".js";
1117
1126
  const indexPath = path6.join(out, "index.ts");
1118
1127
  const index = this.analysis.tables.map(
1119
- (t) => `export * from '${(0, import_validation_core4.moduleSpecifier)(t.tsName, fileSuffix, opts.importExtension)}';`
1128
+ (t) => `export * from '${(0, import_validation_core5.moduleSpecifier)(t.tsName, fileSuffix, opts.importExtension)}';`
1120
1129
  ).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_core4.formatCode)(
1130
+ const indexFormatted = await (0, import_validation_core5.formatCode)(
1122
1131
  buildHeader2(opts.outputHeader) + index,
1123
1132
  indexPath,
1124
1133
  opts.format
@@ -1130,7 +1139,7 @@ var init_dist2 = __esm({
1130
1139
  renderTable(table, opts) {
1131
1140
  return renderTableModule(
1132
1141
  table,
1133
- (0, import_validation_core4.resolveAffix)(opts),
1142
+ (0, import_validation_core5.resolveAffix)(opts),
1134
1143
  opts?.target ?? "draft-2020-12",
1135
1144
  !!opts?.applyDefaults
1136
1145
  );
@@ -1140,8 +1149,574 @@ var init_dist2 = __esm({
1140
1149
  }
1141
1150
  });
1142
1151
 
1152
+ // ../generator-effect/dist/index.js
1153
+ var dist_exports3 = {};
1154
+ __export(dist_exports3, {
1155
+ EffectGenerator: () => EffectGenerator,
1156
+ default: () => index_default3
1157
+ });
1158
+ function filter(expr, description) {
1159
+ return `${NS}.filter((v) => ${expr}, { description: ${JSON.stringify(description)} })`;
1160
+ }
1161
+ function piped(base, steps) {
1162
+ return steps.length ? `${base}.pipe(${steps.join(", ")})` : base;
1163
+ }
1164
+ function isUnknownExpr(expr) {
1165
+ return expr === UNKNOWN_EXPR;
1166
+ }
1167
+ function withNarrowedType(expr, ref2) {
1168
+ return `${expr} as unknown as ${NS}.Schema<${ref2}>`;
1169
+ }
1170
+ function numericBounds(c, checks) {
1171
+ let lo = c.min !== void 0 ? { fn: "greaterThanOrEqualTo", value: c.min } : void 0;
1172
+ let hi = c.max !== void 0 ? { fn: "lessThanOrEqualTo", value: c.max } : void 0;
1173
+ for (const k of checks.filter((x) => x.column === c.name && x.kind === "number")) {
1174
+ if (k.operator === ">=") lo = { fn: "greaterThanOrEqualTo", value: k.value };
1175
+ else if (k.operator === ">") lo = { fn: "greaterThan", value: k.value };
1176
+ else if (k.operator === "<=") hi = { fn: "lessThanOrEqualTo", value: k.value };
1177
+ else if (k.operator === "<") hi = { fn: "lessThan", value: k.value };
1178
+ }
1179
+ return [lo, hi].filter(Boolean).map((x) => `${NS}.${x.fn}(${x.value})`);
1180
+ }
1181
+ function foldedIntoBounds(c, checks) {
1182
+ if (c.arrayDimensions || c.shape) return /* @__PURE__ */ new Set();
1183
+ if (c.tsType !== "number" && c.tsType !== "bigint") return /* @__PURE__ */ new Set();
1184
+ return new Set(
1185
+ checks.filter(
1186
+ (k) => k.column === c.name && k.kind === "number" && k.operator !== "=" && k.operator !== "<>"
1187
+ )
1188
+ );
1189
+ }
1190
+ function nonFiniteBranches(c) {
1191
+ const { nan, infinity } = (0, import_validation_core7.nonFiniteAccepted)(c);
1192
+ return [
1193
+ ...nan ? [`${NS}.Number.pipe(${filter("Number.isNaN(v)", "NaN, which this column stores")})`] : [],
1194
+ ...infinity ? [`${NS}.Literal(Infinity, -Infinity)`] : []
1195
+ ];
1196
+ }
1197
+ function withNonFinite(c, base) {
1198
+ const branches = nonFiniteBranches(c);
1199
+ return branches.length ? `${NS}.Union(${base}, ${branches.join(", ")})` : base;
1200
+ }
1201
+ function dateExpr(mode, coerceDates) {
1202
+ const plain = `${NS}.ValidDateFromSelf`;
1203
+ if (coerceDates === "none") return plain;
1204
+ const fromString = piped(`${NS}.String`, [
1205
+ `${NS}.pattern(new RegExp(${JSON.stringify(import_validation_core7.COERCIBLE_DATE_STRING)}))`,
1206
+ filter((0, import_validation_core7.parsesToADate)("new Date(v)"), "a date the runtime can parse")
1207
+ ]);
1208
+ const fromNumber = piped(`${NS}.Number`, [
1209
+ filter((0, import_validation_core7.parsesToADate)("new Date(v)"), "a date the runtime can parse")
1210
+ ]);
1211
+ const union = `${NS}.Union(${plain}, ${fromString}, ${fromNumber})`;
1212
+ if (coerceDates === "all") return union;
1213
+ return mode === "select" ? plain : union;
1214
+ }
1215
+ function capSteps(c, mode) {
1216
+ const steps = [];
1217
+ if (c.shape?.kind === "byteString") {
1218
+ const n = c.shape.length;
1219
+ if (!n) return steps;
1220
+ return mode === "select" ? [filter(`${import_validation_core7.CODEPOINT_LENGTH} <= ${n}`, `at most ${n} characters`)] : [filter(`new TextEncoder().encode(v).length <= ${n}`, `at most ${n} bytes`)];
1221
+ }
1222
+ if (c.maxLength) {
1223
+ steps.push(
1224
+ filter(`${import_validation_core7.CODEPOINT_LENGTH} <= ${c.maxLength}`, `at most ${c.maxLength} characters`)
1225
+ );
1226
+ }
1227
+ if (c.maxBytes) {
1228
+ steps.push(
1229
+ filter(`new TextEncoder().encode(v).length <= ${c.maxBytes}`, `at most ${c.maxBytes} bytes`)
1230
+ );
1231
+ }
1232
+ return steps;
1233
+ }
1234
+ function lengthSteps(c, lengths) {
1235
+ if (c.arrayDimensions || c.shape) return [];
1236
+ return lengths.filter((k) => k.column === c.name).map(
1237
+ (k) => filter(
1238
+ `${import_validation_core7.CODEPOINT_LENGTH} ${OPS[k.operator]} ${k.value}`,
1239
+ `${k.name ? `${k.name}: ` : ""}length(${c.name}) ${k.operator} ${k.value}`
1240
+ )
1241
+ );
1242
+ }
1243
+ function cardinalitySteps(c, cardinalities) {
1244
+ if (!c.arrayDimensions) return [];
1245
+ return cardinalities.filter((k) => k.column === c.name).map(
1246
+ (k) => filter(
1247
+ `v.length ${OPS[k.operator]} ${k.value}`,
1248
+ `${k.name ? `${k.name}: ` : ""}cardinality(${c.name}) ${k.operator} ${k.value}`
1249
+ )
1250
+ );
1251
+ }
1252
+ function checkSteps(c, checks) {
1253
+ if (c.arrayDimensions || c.shape) return [];
1254
+ const folded = foldedIntoBounds(c, checks);
1255
+ return checks.filter((k) => k.column === c.name && !folded.has(k)).map((k) => {
1256
+ const literal = k.kind === "string" ? JSON.stringify(k.value) : k.value;
1257
+ return filter(
1258
+ `v ${OPS[k.operator]} ${literal}`,
1259
+ `${k.name ? `${k.name}: ` : ""}${c.name} ${k.operator} ${k.value}`
1260
+ );
1261
+ });
1262
+ }
1263
+ function hasNoRuntimeType(c) {
1264
+ return c.tsType === "any" || c.shape?.kind === "custom" || c.shape?.kind === "json";
1265
+ }
1266
+ function shapeExpr(c, mode, replaced = false) {
1267
+ const s = c.shape;
1268
+ if (!s) return void 0;
1269
+ switch (s.kind) {
1270
+ case "json":
1271
+ return replaced ? UNKNOWN_EXPR : JSON_CONST;
1272
+ case "custom":
1273
+ return UNKNOWN_EXPR;
1274
+ case "buffer":
1275
+ return `${NS}.Uint8ArrayFromSelf`;
1276
+ case "tuple":
1277
+ return `${NS}.Tuple(${Array.from({ length: s.length }, () => `${NS}.Number`).join(", ")})`;
1278
+ case "numberObject":
1279
+ return `${NS}.Struct({ ${s.fields.map((f) => `${f}: ${NS}.Number`).join(", ")} })`;
1280
+ case "numberVector":
1281
+ return piped(
1282
+ `${NS}.Array(${NS}.Number)`,
1283
+ s.length ? [filter(`v.length === ${s.length}`, `exactly ${s.length} elements`)] : []
1284
+ );
1285
+ case "bitstring":
1286
+ return piped(`${NS}.String`, [
1287
+ `${NS}.pattern(/^[01]*$/)`,
1288
+ ...s.length ? [
1289
+ s.exact ? filter(`v.length === ${s.length}`, `exactly ${s.length} binary digits`) : filter(`v.length <= ${s.length}`, `at most ${s.length} binary digits`)
1290
+ ] : []
1291
+ ]);
1292
+ case "byteString":
1293
+ return piped(`${NS}.String`, capSteps(c, mode));
1294
+ }
1295
+ }
1296
+ function exprForColumn(c, mode, coerceDates, checks, sets, lengths, replaced) {
1297
+ const shaped = shapeExpr(c, mode, replaced);
1298
+ if (shaped) return shaped;
1299
+ const set = sets.find((x) => x.column === c.name);
1300
+ if (set) {
1301
+ const values = set.values.map((v) => set.kind === "string" ? JSON.stringify(v) : v);
1302
+ return `${NS}.Literal(${values.join(", ")})`;
1303
+ }
1304
+ if (c.arrayDimensions) checks = [];
1305
+ if (c.enumValues && c.enumValues.length) {
1306
+ return `${NS}.Literal(${c.enumValues.map((v) => JSON.stringify(v)).join(", ")})`;
1307
+ }
1308
+ const eq = checks.find((k) => k.column === c.name && k.operator === "=");
1309
+ if (eq && !c.shape) {
1310
+ return `${NS}.Literal(${eq.kind === "string" ? JSON.stringify(eq.value) : eq.value})`;
1311
+ }
1312
+ const rest = [...checkSteps(c, checks), ...lengthSteps(c, lengths)];
1313
+ switch (c.tsType) {
1314
+ case "string": {
1315
+ const base = c.format === "uuid" ? `${NS}.UUID` : `${NS}.String`;
1316
+ const pattern = c.format && c.format !== "uuid" && import_validation_core7.COLUMN_FORMATS[c.format] ? [`${NS}.pattern(new RegExp(${JSON.stringify(import_validation_core7.COLUMN_FORMATS[c.format])}))`] : [];
1317
+ return piped(base, [...pattern, ...capSteps(c, mode), ...rest]);
1318
+ }
1319
+ case "number": {
1320
+ const base = (0, import_validation_core7.isIntegerColumn)(c) ? `${NS}.Int` : `${NS}.Finite`;
1321
+ return withNonFinite(c, piped(base, [...numericBounds(c, checks), ...rest]));
1322
+ }
1323
+ case "bigint":
1324
+ return piped(`${NS}.BigIntFromSelf`, [
1325
+ ...c.min !== void 0 ? [`${NS}.greaterThanOrEqualToBigInt(${c.min}n)`] : [],
1326
+ ...c.max !== void 0 ? [`${NS}.lessThanOrEqualToBigInt(${c.max}n)`] : [],
1327
+ ...rest
1328
+ ]);
1329
+ case "boolean":
1330
+ return `${NS}.Boolean`;
1331
+ case "Date":
1332
+ return dateExpr(mode, coerceDates);
1333
+ case "Uint8Array":
1334
+ return `${NS}.Uint8ArrayFromSelf`;
1335
+ case "any":
1336
+ return UNKNOWN_EXPR;
1337
+ default:
1338
+ return UNKNOWN_EXPR;
1339
+ }
1340
+ }
1341
+ function renderField(c, mode, coerceDates, checks, sets, lengths, cardinalities, applyDefault, narrowRef, brand) {
1342
+ let expr = exprForColumn(
1343
+ c,
1344
+ mode,
1345
+ coerceDates,
1346
+ checks,
1347
+ sets,
1348
+ lengths,
1349
+ !!narrowRef && hasNoRuntimeType(c)
1350
+ );
1351
+ const dims = c.arrayDimensions ?? 0;
1352
+ for (let i = 0; i < dims; i++) {
1353
+ expr = `${NS}.Array(${expr})`;
1354
+ if (i === dims - 1) expr = piped(expr, cardinalitySteps(c, cardinalities));
1355
+ }
1356
+ if (brand) expr = piped(expr, [`${NS}.brand(${JSON.stringify(brand)})`]);
1357
+ if (c.nullable && !isUnknownExpr(expr)) expr = `${NS}.NullOr(${expr})`;
1358
+ if (narrowRef && !brand) expr = withNarrowedType(expr, narrowRef);
1359
+ if (mode === "select") return expr;
1360
+ const wantsDefault = mode === "insert" && applyDefault && c.defaultValue !== void 0;
1361
+ if (wantsDefault) {
1362
+ return `${NS}.optionalWith(${expr}, { default: () => ${JSON.stringify(c.defaultValue)} })`;
1363
+ }
1364
+ if (mode === "update" || c.nullable || c.hasDefault) return `${NS}.optional(${expr})`;
1365
+ return expr;
1366
+ }
1367
+ function wantsRef(c, allColumns) {
1368
+ return allColumns || hasNoRuntimeType(c);
1369
+ }
1370
+ function renderObjectShape(cols, mode, coerceDates, checks, sets, lengths, cardinalities, typedJson, applyDefaults, brands) {
1371
+ return cols.map((c) => {
1372
+ const ref2 = typedJson && wantsRef(c, !!typedJson.allColumns) ? `(typeof ${typedJson.table}.$infer${typedJson.mode === "insert" ? "Insert" : "Select"})[${JSON.stringify(c.name)}]` : void 0;
1373
+ const field2 = renderField(
1374
+ c,
1375
+ mode,
1376
+ coerceDates,
1377
+ checks,
1378
+ sets,
1379
+ lengths,
1380
+ cardinalities,
1381
+ applyDefaults,
1382
+ ref2,
1383
+ brands?.plan.brandOf(brands.tsName, c.name)
1384
+ );
1385
+ return ` ${JSON.stringify(c.name)}: ${field2},`;
1386
+ }).join("\n");
1387
+ }
1388
+ function rowSteps(rows, cols) {
1389
+ const present = new Set(cols.map((c) => c.name));
1390
+ return rows.filter((r) => present.has(r.left) && present.has(r.right)).map((r) => {
1391
+ const l = `o[${JSON.stringify(r.left)}]`;
1392
+ const rt = `o[${JSON.stringify(r.right)}]`;
1393
+ const msg = `${r.name ? `${r.name}: ` : ""}${r.left} ${r.operator} ${r.right}`;
1394
+ return `${NS}.filter((o) => ${l} == null || ${rt} == null || ${l} ${OPS[r.operator]} ${rt}, { description: ${JSON.stringify(msg)} })`;
1395
+ });
1396
+ }
1397
+ function indentBlock(code, by = " ") {
1398
+ return code.split("\n").map((line) => line ? by + line : line).join("\n");
1399
+ }
1400
+ function parsedChecksFor(table) {
1401
+ const parsed = (table.checks ?? []).map((k) => (0, import_validation_core7.parseCheck)(k.expression, k.name));
1402
+ return {
1403
+ checks: parsed.flatMap((p) => p.ok ? p.checks : []),
1404
+ sets: parsed.flatMap((p) => p.ok ? p.sets ?? [] : []),
1405
+ rows: parsed.flatMap((p) => p.ok ? p.rows ?? [] : []),
1406
+ lengths: parsed.flatMap((p) => p.ok ? p.lengths ?? [] : []),
1407
+ cardinalities: parsed.flatMap((p) => p.ok ? p.cardinalities ?? [] : [])
1408
+ };
1409
+ }
1410
+ function nestedNodeCols(node, mode) {
1411
+ const all = mode === "insert" ? (0, import_validation_core7.insertColumns)(node.table) : (0, import_validation_core7.selectColumns)(node.table);
1412
+ return (0, import_validation_core7.nestedNodeColumns)(all, node);
1413
+ }
1414
+ function nestedNodes(node, into = []) {
1415
+ into.push(node);
1416
+ for (const arm of node.arms) nestedNodes(arm.child, into);
1417
+ return into;
1418
+ }
1419
+ function renderNestedObject(node, mode, coerceDates, typedJson, applyDefaults, brands) {
1420
+ const cols = nestedNodeCols(node, mode);
1421
+ const { checks, sets, rows, lengths, cardinalities } = parsedChecksFor(node.table);
1422
+ const tj = typedJson ? { table: node.table.tsName, mode, allColumns: typedJson.allColumns } : void 0;
1423
+ const fields = renderObjectShape(
1424
+ cols,
1425
+ mode,
1426
+ coerceDates,
1427
+ checks,
1428
+ sets,
1429
+ lengths,
1430
+ cardinalities,
1431
+ tj,
1432
+ applyDefaults,
1433
+ brands ? { plan: brands, tsName: node.table.tsName } : void 0
1434
+ );
1435
+ const arms = node.arms.map((arm) => {
1436
+ const notes = (0, import_validation_core7.nestedArmNotes)(arm).map((n) => ` // ${n}
1437
+ `).join("");
1438
+ const child = renderNestedObject(
1439
+ arm.child,
1440
+ mode,
1441
+ coerceDates,
1442
+ typedJson,
1443
+ applyDefaults,
1444
+ brands
1445
+ );
1446
+ const inner = arm.single ? `${NS}.NullOr(
1447
+ ${indentBlock(indentBlock(child))}
1448
+ )` : `${NS}.Array(
1449
+ ${indentBlock(indentBlock(child))}
1450
+ )`;
1451
+ return `${notes} ${JSON.stringify(arm.key)}: ${NS}.optional(${inner}),`;
1452
+ });
1453
+ const body = [fields, ...arms].filter(Boolean).join("\n");
1454
+ return piped(`${NS}.Struct({
1455
+ ${body}
1456
+ })`, rowSteps(rows, cols));
1457
+ }
1458
+ function renderNestedSchemas(table, affix, coerceDates, typedJson, applyDefaults, plans, brands) {
1459
+ const out = [];
1460
+ for (const mode of ["insert", "select"]) {
1461
+ const plan = plans[mode];
1462
+ if (!plan) continue;
1463
+ const name = (0, import_validation_core7.nestedSchemaName)(mode, table.tsName, affix);
1464
+ const tname = (0, import_validation_core7.nestedTypeName)(mode, table.tsName, affix);
1465
+ const expr = renderNestedObject(plan, mode, coerceDates, typedJson, applyDefaults, brands);
1466
+ out.push(
1467
+ `export const ${name} = ${expr};
1468
+
1469
+ export type ${tname} = ${NS}.Schema.Type<typeof ${name}>;
1470
+
1471
+ export const ${STANDARD_PREFIX}${name} = ${NS}.standardSchemaV1(${name});`
1472
+ );
1473
+ }
1474
+ return out.length ? `
1475
+ ${out.join("\n\n")}
1476
+ ` : "";
1477
+ }
1478
+ function nestedPlansFor(table, analysis, depth) {
1479
+ const out = {};
1480
+ for (const mode of ["insert", "select"]) {
1481
+ if (mode === "insert" && table.readOnly) continue;
1482
+ const plan = (0, import_validation_core7.buildNestedPlan)(table, analysis.tables, analysis.relations ?? [], mode, depth);
1483
+ if (plan) out[mode] = plan;
1484
+ }
1485
+ return out;
1486
+ }
1487
+ function renderTableSchemas(table, affix, coerceDates, typedJson, applyDefaults = false, wantsDuplicateFinder = false, nested = {}, brands) {
1488
+ const T = table.tsName;
1489
+ const insertCols = (0, import_validation_core7.insertColumns)(table);
1490
+ const updateCols = (0, import_validation_core7.updateColumns)(table);
1491
+ const selectCols = (0, import_validation_core7.selectColumns)(table);
1492
+ const { checks, sets, rows, lengths, cardinalities } = parsedChecksFor(table);
1493
+ const tj = typedJson ? { table: T, allColumns: typedJson.allColumns } : void 0;
1494
+ const modes = [
1495
+ ["insert", insertCols],
1496
+ ["update", updateCols],
1497
+ ["select", selectCols]
1498
+ ];
1499
+ const blocks = modes.map(([mode, cols]) => {
1500
+ const name = (0, import_validation_core7.schemaName)(mode, T, affix);
1501
+ const tname = (0, import_validation_core7.typeName)(mode, T, affix);
1502
+ const body = renderObjectShape(
1503
+ cols,
1504
+ mode,
1505
+ coerceDates,
1506
+ checks,
1507
+ sets,
1508
+ lengths,
1509
+ cardinalities,
1510
+ // The update schema references the insert-side inferred types: both describe a value going
1511
+ // in, and `$inferSelect` would name the post-default type for a column a write may omit.
1512
+ tj ? { ...tj, mode: mode === "select" ? "select" : "insert" } : void 0,
1513
+ applyDefaults,
1514
+ brands ? { plan: brands, tsName: T } : void 0
1515
+ );
1516
+ const expr = piped(`${NS}.Struct({
1517
+ ${body}
1518
+ })`, rowSteps(rows, cols));
1519
+ return `export const ${name} = ${expr};
1520
+
1521
+ export type ${tname} = ${NS}.Schema.Type<typeof ${name}>;
1522
+
1523
+ export const ${STANDARD_PREFIX}${name} = ${NS}.standardSchemaV1(${name});`;
1524
+ });
1525
+ const nestedByTable = ["insert", "select"].flatMap((m) => {
1526
+ const plan = nested[m];
1527
+ return plan ? nestedNodes(plan).map((n) => [n.table.tsName, nestedNodeCols(n, m)]) : [];
1528
+ });
1529
+ const nestedCols = nestedByTable.flatMap(([, cs]) => cs);
1530
+ const referenced = /* @__PURE__ */ new Set();
1531
+ if (typedJson) {
1532
+ const all = !!typedJson.allColumns;
1533
+ if ([...insertCols, ...updateCols, ...selectCols].some((c) => wantsRef(c, all))) {
1534
+ referenced.add(T);
1535
+ }
1536
+ for (const [name, cs] of nestedByTable) {
1537
+ if (cs.some((c) => wantsRef(c, all))) referenced.add(name);
1538
+ }
1539
+ }
1540
+ const schemaImport = referenced.size ? `import type { ${[...referenced].join(", ")} } from '${typedJson.schemaSpecifier}';
1541
+ ` : "";
1542
+ const needsJson = !typedJson && [...insertCols, ...updateCols, ...selectCols, ...nestedCols].some(
1543
+ (c) => c.shape?.kind === "json"
1544
+ );
1545
+ const finder = wantsDuplicateFinder ? (0, import_validation_core7.renderDuplicateFinder)(table, `findDuplicate${T}`, (0, import_validation_core7.typeName)("insert", T, affix)) : void 0;
1546
+ const duplicates = finder ? `
1547
+ ${finder}
1548
+ ` : "";
1549
+ const nestedCode = renderNestedSchemas(
1550
+ table,
1551
+ affix,
1552
+ coerceDates,
1553
+ typedJson,
1554
+ applyDefaults,
1555
+ nested,
1556
+ brands
1557
+ );
1558
+ const selectName = (0, import_validation_core7.schemaName)("select", T, affix);
1559
+ const brandAliases = (brands?.aliasesFor(T) ?? []).map(
1560
+ (a) => `/** The nominal type of ${T}.${a.column}. */
1561
+ export type ${a.alias} = ${NS}.Schema.Type<typeof ${selectName}>[${JSON.stringify(a.column)}];`
1562
+ ).join("\n\n");
1563
+ const brandCode = brandAliases ? `
1564
+ ${brandAliases}
1565
+ ` : "";
1566
+ return `import * as ${NS} from 'effect/Schema';
1567
+ ${schemaImport}${needsJson ? `
1568
+ ${JSON_PREAMBLE}` : ""}
1569
+ ${blocks.join("\n\n")}
1570
+ ${brandCode}${nestedCode}${duplicates}`;
1571
+ }
1572
+ function buildHeader3(h) {
1573
+ if (h && h.enabled === false) return "";
1574
+ const text = h?.text?.trim();
1575
+ const lines = text ? text.split(/\r?\n/).map((l) => `// ${l}`) : [
1576
+ "// Generated by DRZL (@drzl/*)",
1577
+ "// Generated output is granted to you under your project's license.",
1578
+ "// You may use, copy, modify, and distribute without attribution."
1579
+ ];
1580
+ return lines.join("\n") + "\n\n";
1581
+ }
1582
+ var import_validation_core7, DEFAULT_FILE_SUFFIX2, STANDARD_PREFIX, NS, OPS, JSON_CONST, JSON_PREAMBLE, UNKNOWN_EXPR, EffectGenerator, index_default3;
1583
+ var init_dist3 = __esm({
1584
+ "../generator-effect/dist/index.js"() {
1585
+ "use strict";
1586
+ import_validation_core7 = require("@drzl/validation-core");
1587
+ DEFAULT_FILE_SUFFIX2 = ".effect.ts";
1588
+ STANDARD_PREFIX = "Standard";
1589
+ NS = "Schema";
1590
+ OPS = {
1591
+ ">=": ">=",
1592
+ ">": ">",
1593
+ "<=": "<=",
1594
+ "<": "<",
1595
+ "=": "===",
1596
+ "<>": "!=="
1597
+ };
1598
+ JSON_CONST = "DrzlJsonValue";
1599
+ JSON_PREAMBLE = `type ${JSON_CONST}Type =
1600
+ | string
1601
+ | number
1602
+ | boolean
1603
+ | null
1604
+ | readonly ${JSON_CONST}Type[]
1605
+ | { readonly [key: string]: ${JSON_CONST}Type };
1606
+
1607
+ const ${JSON_CONST}: ${NS}.Schema<${JSON_CONST}Type, unknown> = ${NS}.suspend(() =>
1608
+ ${NS}.Union(
1609
+ ${NS}.String,
1610
+ ${NS}.Finite,
1611
+ ${NS}.Boolean,
1612
+ ${NS}.Null,
1613
+ ${NS}.Array(${JSON_CONST}),
1614
+ // The plain-object test comes before the record, not after it. \`Schema.Record\` rebuilds its
1615
+ // output, so a check placed after it inspects that new object and reports every input as
1616
+ // plain. A Date sailed through: it has no own enumerable keys, so the record accepted it and
1617
+ // rebuilt it as \`{}\`.
1618
+ ${NS}.Unknown.pipe(
1619
+ ${NS}.filter(
1620
+ (o) => {
1621
+ if (typeof o !== 'object' || o === null || Array.isArray(o)) return false;
1622
+ const p = Object.getPrototypeOf(o);
1623
+ return p === Object.prototype || p === null;
1624
+ },
1625
+ { description: 'a plain object' }
1626
+ ),
1627
+ ${NS}.compose(${NS}.Record({ key: ${NS}.String, value: ${JSON_CONST} }), { strict: false })
1628
+ )
1629
+ )
1630
+ );
1631
+ `;
1632
+ UNKNOWN_EXPR = `${NS}.Unknown`;
1633
+ EffectGenerator = class {
1634
+ constructor(analysis) {
1635
+ this.analysis = analysis;
1636
+ this.library = "effect";
1637
+ }
1638
+ async generate(opts) {
1639
+ const fs3 = await import("fs/promises");
1640
+ const path6 = await import("path");
1641
+ const out = path6.resolve(process.cwd(), opts.outDir);
1642
+ const files = [];
1643
+ await fs3.mkdir(out, { recursive: true });
1644
+ const affix = (0, import_validation_core7.resolveAffix)(opts);
1645
+ const coerceDates = opts.coerceDates ?? "input";
1646
+ const fileSuffix = opts.fileSuffix ?? DEFAULT_FILE_SUFFIX2;
1647
+ const wantsTypes = opts.typedJson || opts.typedColumns;
1648
+ const typedJson = wantsTypes && opts.schemaPath ? {
1649
+ schemaSpecifier: (0, import_validation_core7.resolveConfiguredImport)(
1650
+ opts.schemaPath,
1651
+ out,
1652
+ process.cwd(),
1653
+ opts.importExtension
1654
+ ),
1655
+ allColumns: !!opts.typedColumns
1656
+ } : void 0;
1657
+ if (wantsTypes && !opts.schemaPath) {
1658
+ console.warn(
1659
+ "[drzl] typedJson was requested but the schema path is unknown, so json columns keep their wide type."
1660
+ );
1661
+ }
1662
+ const nestedDepth = opts.nestedSchemas ? (0, import_validation_core7.resolveNestedDepth)(opts.nestedDepth, (m) => console.warn(m)) : 0;
1663
+ const brands = (0, import_validation_core7.buildBrandPlan)(this.analysis.tables, opts.branded);
1664
+ for (const note of brands?.notes ?? []) console.warn(`[drzl] ${note}`);
1665
+ for (const table of this.analysis.tables) {
1666
+ const filePath = path6.join(out, (0, import_validation_core7.moduleFileName)(table.tsName, fileSuffix));
1667
+ const code = renderTableSchemas(
1668
+ table,
1669
+ affix,
1670
+ coerceDates,
1671
+ typedJson,
1672
+ !!opts.applyDefaults,
1673
+ !!opts?.duplicateFinder,
1674
+ opts.nestedSchemas ? nestedPlansFor(table, this.analysis, nestedDepth) : {},
1675
+ brands
1676
+ );
1677
+ const formatted = await (0, import_validation_core7.formatCode)(
1678
+ buildHeader3(opts.outputHeader) + code,
1679
+ filePath,
1680
+ opts.format
1681
+ );
1682
+ await fs3.writeFile(filePath, formatted, "utf8");
1683
+ files.push(filePath);
1684
+ }
1685
+ const indexPath = path6.join(out, "index.ts");
1686
+ const indexFormatted = await (0, import_validation_core7.formatCode)(
1687
+ buildHeader3(opts.outputHeader) + this.defaultIndex(this.analysis, opts),
1688
+ indexPath,
1689
+ opts.format
1690
+ );
1691
+ await fs3.writeFile(indexPath, indexFormatted, "utf8");
1692
+ files.push(indexPath);
1693
+ return files;
1694
+ }
1695
+ renderTable(table, opts) {
1696
+ return renderTableSchemas(
1697
+ table,
1698
+ (0, import_validation_core7.resolveAffix)(opts),
1699
+ opts?.coerceDates ?? "input",
1700
+ void 0,
1701
+ !!opts?.applyDefaults,
1702
+ !!opts?.duplicateFinder,
1703
+ {},
1704
+ (0, import_validation_core7.buildBrandPlan)(this.analysis.tables, opts?.branded)
1705
+ );
1706
+ }
1707
+ defaultIndex(analysis, opts) {
1708
+ const fileSuffix = opts.fileSuffix ?? DEFAULT_FILE_SUFFIX2;
1709
+ return analysis.tables.map(
1710
+ (t) => `export * from '${(0, import_validation_core7.moduleSpecifier)(t.tsName, fileSuffix, opts.importExtension)}';`
1711
+ ).join("\n") + "\n";
1712
+ }
1713
+ };
1714
+ index_default3 = EffectGenerator;
1715
+ }
1716
+ });
1717
+
1143
1718
  // src/cli.ts
1144
- var import_analyzer = require("@drzl/analyzer");
1719
+ var import_analyzer2 = require("@drzl/analyzer");
1145
1720
  var import_generator_orpc = require("@drzl/generator-orpc");
1146
1721
  var import_chalk3 = __toESM(require("chalk"), 1);
1147
1722
  var import_chokidar = __toESM(require("chokidar"), 1);
@@ -1165,6 +1740,11 @@ function validationOptions(g, cfg, outDir, caps = {}) {
1165
1740
  duplicateFinder: g.duplicateFinder,
1166
1741
  nestedSchemas: g.nestedSchemas,
1167
1742
  nestedDepth: g.nestedDepth,
1743
+ // Every validation generator can express a brand, including TypeBox, which has no brand
1744
+ // helper and gets one from `TUnsafe` instead. So this needs no capability flag: an option
1745
+ // that reached only four of the five would be the class of defect this file exists to
1746
+ // remove.
1747
+ branded: g.branded,
1168
1748
  // Only where the generator can act on them, so an unsupported option is absent rather than
1169
1749
  // present and ignored.
1170
1750
  ...caps.schemaTypes ? {
@@ -1172,7 +1752,9 @@ function validationOptions(g, cfg, outDir, caps = {}) {
1172
1752
  schemaPath: cfg.schema,
1173
1753
  typedJson: g.typedJson,
1174
1754
  typedColumns: g.typedColumns
1175
- } : {}
1755
+ } : {},
1756
+ ...caps.standardSchema ? { standardSchema: g.standardSchema } : {},
1757
+ ...caps.meta ? { meta: g.meta } : {}
1176
1758
  };
1177
1759
  }
1178
1760
 
@@ -1196,6 +1778,47 @@ var fs = __toESM(require("fs"), 1);
1196
1778
  var import_node_module = require("module");
1197
1779
  var path = __toESM(require("path"), 1);
1198
1780
  var import_zod = require("zod");
1781
+
1782
+ // src/patterns.ts
1783
+ function patternToRegExp(pattern) {
1784
+ return new RegExp(
1785
+ "^" + pattern.split("*").map((part) => part.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join(".*") + "$"
1786
+ );
1787
+ }
1788
+ function matchesAny(patterns, name) {
1789
+ return patterns.some((p) => patternToRegExp(p).test(name));
1790
+ }
1791
+ var DEFAULT_SCHEMA_ALIAS = "public";
1792
+ function tableAliases(table) {
1793
+ return [table.name, `${table.schema ?? DEFAULT_SCHEMA_ALIAS}.${table.name}`];
1794
+ }
1795
+ function matchesTable(patterns, table) {
1796
+ return tableAliases(table).some((alias) => matchesAny(patterns, alias));
1797
+ }
1798
+ function displayTableName(table) {
1799
+ return table.schema ? `${table.schema}.${table.name}` : table.name;
1800
+ }
1801
+ function addressableName(table) {
1802
+ return `${table.schema ?? DEFAULT_SCHEMA_ALIAS}.${table.name}`;
1803
+ }
1804
+ function hasNamedSchemas(tables) {
1805
+ return tables.some((t) => t.schema);
1806
+ }
1807
+ function ambiguousPatternWarnings(patterns, tables, option) {
1808
+ const out = [];
1809
+ for (const pattern of patterns) {
1810
+ if (pattern.includes(".")) continue;
1811
+ const matched = tables.filter((t) => matchesTable([pattern], t));
1812
+ const schemas = new Set(matched.map((t) => t.schema ?? DEFAULT_SCHEMA_ALIAS));
1813
+ if (schemas.size < 2) continue;
1814
+ out.push(
1815
+ `drzl config: ${option} pattern ${JSON.stringify(pattern)} matches tables in more than one schema, and every one of them is affected: ${matched.map(addressableName).sort().join(", ")}. Write the schema to mean one of them, for example ${JSON.stringify(addressableName(matched[0]))}.`
1816
+ );
1817
+ }
1818
+ return out;
1819
+ }
1820
+
1821
+ // src/config.ts
1199
1822
  var NamingSchema = import_zod.z.object({
1200
1823
  routerSuffix: import_zod.z.string().default("Router"),
1201
1824
  procedureCase: import_zod.z.enum(["camel", "kebab", "snake"]).default("camel")
@@ -1229,7 +1852,17 @@ var AffixSchema = import_zod.z.object({
1229
1852
  }).strict();
1230
1853
  var ImportExtensionSchema = import_zod.z.enum(import_validation_core.IMPORT_EXTENSIONS);
1231
1854
  var GeneratorSchema = import_zod.z.object({
1232
- kind: import_zod.z.enum(["orpc", "trpc", "service", "zod", "valibot", "arktype", "typebox", "json-schema"]),
1855
+ kind: import_zod.z.enum([
1856
+ "orpc",
1857
+ "trpc",
1858
+ "service",
1859
+ "zod",
1860
+ "valibot",
1861
+ "arktype",
1862
+ "typebox",
1863
+ "effect",
1864
+ "json-schema"
1865
+ ]),
1233
1866
  /**
1234
1867
  * Overrides the top-level `importExtension` for this generator alone, for a project whose
1235
1868
  * generated directories are compiled by different tsconfigs.
@@ -1265,6 +1898,36 @@ var GeneratorSchema = import_zod.z.object({
1265
1898
  * fact about the table rather than the row. This checks the half that needs no database.
1266
1899
  */
1267
1900
  duplicateFinder: import_zod.z.boolean().optional(),
1901
+ /**
1902
+ * zod only. Attach the facts the analyzer knows and a zod schema cannot state, as `.meta()` on
1903
+ * every field and every table schema: the declared SQL type, the primary key, the unique
1904
+ * constraints, whether the database generates or defaults the value, and the CHECK constraints,
1905
+ * including the ones DRZL declined to enforce.
1906
+ *
1907
+ * `z.toJSONSchema` copies these through, so they are also how an OpenAPI document built from the
1908
+ * emitted schemas gets the declared width back: DRZL enforces one as a `.refine()`, and
1909
+ * `toJSONSchema` drops every refinement in silence.
1910
+ *
1911
+ * `true` is the shorthand for `{ enabled: true }`. `{ description: true }` additionally writes a
1912
+ * `description`, which is what an OpenAPI viewer renders to a human.
1913
+ */
1914
+ meta: import_zod.z.union([
1915
+ import_zod.z.boolean(),
1916
+ import_zod.z.object({ enabled: import_zod.z.boolean().optional(), description: import_zod.z.boolean().optional() }).strict()
1917
+ ]).optional(),
1918
+ /**
1919
+ * TypeBox only. Give every emitted schema a `~standard` key, so it can be handed to a tRPC or
1920
+ * oRPC route.
1921
+ *
1922
+ * TypeBox is the one validator DRZL emits that carries none of its own: measured on 0.34.52, a
1923
+ * bare `Type.Object()` has no `~standard` and the package exports nothing matching
1924
+ * `/standard/i`. zod, valibot and arktype all put one on every schema they build, so the option
1925
+ * does nothing for them and is not passed through.
1926
+ *
1927
+ * The property is non-enumerable, so the schema stays a TypeBox schema in every respect that was
1928
+ * already observable, including the JSON Schema `JSON.stringify` produces.
1929
+ */
1930
+ standardSchema: import_zod.z.boolean().optional(),
1268
1931
  /**
1269
1932
  * Emit `NestedInsert<Table>` and `NestedSelect<Table>` beside the flat schemas: the table plus
1270
1933
  * one key per relation, so `{ ...user, posts: [...] }` can be validated whole.
@@ -1281,6 +1944,29 @@ var GeneratorSchema = import_zod.z.object({
1281
1944
  * it is also what terminates a cycle: `users -> posts -> users` stops here.
1282
1945
  */
1283
1946
  nestedDepth: import_zod.z.number().int().optional(),
1947
+ /**
1948
+ * Give every primary key, and every foreign key pointing at one, a nominal type, so a
1949
+ * `users.id` cannot be passed where a `posts.id` is wanted.
1950
+ *
1951
+ * Type level only, in all five validators. Measured on zod 4.4.3, `.brand()` returns the same
1952
+ * schema object it was called on and the parsed value of `1` is `1`, so nothing about what a
1953
+ * schema accepts changes and no bytes are added to the bundle. TypeBox has no brand of its own
1954
+ * and gets a `TUnsafe` cast, which leaves the schema object identical.
1955
+ *
1956
+ * Off by default: it changes the inferred type of every consumer of the select schemas, which
1957
+ * is the point, but it is a change to existing call sites rather than an addition.
1958
+ *
1959
+ * `true` is the shorthand for `{ enabled: true }`. `{ foreignKeys: false }` brands only the
1960
+ * keys themselves, and `{ aliases: false }` stops the `export type UsersId = ...` lines.
1961
+ */
1962
+ branded: import_zod.z.union([
1963
+ import_zod.z.boolean(),
1964
+ import_zod.z.object({
1965
+ enabled: import_zod.z.boolean().optional(),
1966
+ foreignKeys: import_zod.z.boolean().optional(),
1967
+ aliases: import_zod.z.boolean().optional()
1968
+ }).strict()
1969
+ ]).optional(),
1284
1970
  naming: NamingSchema.optional(),
1285
1971
  outputHeader: import_zod.z.object({
1286
1972
  enabled: import_zod.z.boolean().default(true).optional(),
@@ -1375,6 +2061,10 @@ var GeneratorSchema = import_zod.z.object({
1375
2061
  // template options
1376
2062
  templateOptions: import_zod.z.record(import_zod.z.string(), import_zod.z.any()).optional()
1377
2063
  });
2064
+ var ColumnRulesSchema = import_zod.z.object({
2065
+ omit: import_zod.z.array(import_zod.z.string()).optional(),
2066
+ pick: import_zod.z.array(import_zod.z.string()).optional()
2067
+ }).strict();
1378
2068
  var AnalyzerSchema = import_zod.z.object({
1379
2069
  includeRelations: import_zod.z.boolean().default(true),
1380
2070
  validateConstraints: import_zod.z.boolean().default(true),
@@ -1401,6 +2091,37 @@ var ConfigSchema = import_zod.z.object({
1401
2091
  */
1402
2092
  include: import_zod.z.array(import_zod.z.string()).optional(),
1403
2093
  exclude: import_zod.z.array(import_zod.z.string()).optional(),
2094
+ /**
2095
+ * Which columns of those tables to generate for, keyed by table.
2096
+ *
2097
+ * `include`/`exclude` is all or nothing per table, and the column that should not be in a
2098
+ * generated schema is usually sitting in a table you do want: `passwordHash` on `users`, an
2099
+ * internal note beside the public fields, a `tenantId` the server sets from the session and a
2100
+ * request body must not carry. Editing the emitted file is not an answer, because the next
2101
+ * `drzl generate` overwrites it.
2102
+ *
2103
+ * ```ts
2104
+ * columns: {
2105
+ * users: { omit: ['passwordHash'] },
2106
+ * 'app_*': { omit: ['deleted_at'] },
2107
+ * }
2108
+ * ```
2109
+ *
2110
+ * The key is a table pattern in the same language `include`/`exclude` uses: the database table
2111
+ * name, anchored, with `*` as the only metacharacter. Column patterns are the same language
2112
+ * again. Every matching entry applies, in the order written; within one entry `pick` narrows
2113
+ * first and `omit` then removes, so `omit` wins, exactly as `exclude` wins over `include`.
2114
+ *
2115
+ * Applies to every mode and every generator at once, because it narrows the analysis rather
2116
+ * than any one generator's output. A column cannot be kept in `select` and dropped from
2117
+ * `insert`: see the docs for why that form was not taken on.
2118
+ *
2119
+ * A pattern that matches nothing is an error rather than a no-op, because a typo in `omit`
2120
+ * that silently does nothing leaves the column exactly where it was while reading like a fix.
2121
+ * Dropping a primary key column is an error too; dropping a NOT NULL column with no default is
2122
+ * a warning.
2123
+ */
2124
+ columns: import_zod.z.record(import_zod.z.string(), ColumnRulesSchema).optional(),
1404
2125
  /**
1405
2126
  * How every relative specifier drzl invents spells its extension, for every generator.
1406
2127
  * A generator may override it. Defaults to `js`, which is the only form that resolves
@@ -1563,6 +2284,7 @@ function computeGeneratorOutputDirs(cfg, cwd = process.cwd()) {
1563
2284
  if (g.kind === "valibot") dirs.add(abs(g.path ?? "src/validators/valibot"));
1564
2285
  if (g.kind === "arktype") dirs.add(abs(g.path ?? "src/validators/arktype"));
1565
2286
  if (g.kind === "typebox") dirs.add(abs(g.path ?? "src/validators/typebox"));
2287
+ if (g.kind === "effect") dirs.add(abs(g.path ?? "src/validators/effect"));
1566
2288
  if (g.kind === "json-schema") dirs.add(abs(g.path ?? "src/validators/json-schema"));
1567
2289
  }
1568
2290
  return [...dirs];
@@ -1593,15 +2315,17 @@ function resolveTemplateDirsSync(cfg, cwd = process.cwd()) {
1593
2315
  return Array.from(new Set(results));
1594
2316
  }
1595
2317
  function filterTables(tables, opts) {
1596
- const toRegExp = (pattern) => new RegExp(
1597
- "^" + pattern.split("*").map((part) => part.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join(".*") + "$"
1598
- );
1599
- const matches = (patterns, name) => patterns.some((p) => toRegExp(p).test(name));
1600
2318
  let out = tables;
1601
- if (opts.include?.length) out = out.filter((t) => matches(opts.include, t.name));
1602
- if (opts.exclude?.length) out = out.filter((t) => !matches(opts.exclude, t.name));
2319
+ if (opts.include?.length) out = out.filter((t) => matchesTable(opts.include, t));
2320
+ if (opts.exclude?.length) out = out.filter((t) => !matchesTable(opts.exclude, t));
1603
2321
  return out;
1604
2322
  }
2323
+ function tableFilterWarnings(tables, opts) {
2324
+ return [
2325
+ ...ambiguousPatternWarnings(opts.include ?? [], tables, "include"),
2326
+ ...ambiguousPatternWarnings(opts.exclude ?? [], tables, "exclude")
2327
+ ];
2328
+ }
1605
2329
  function computeWatchTargets(cfg, cwd = process.cwd()) {
1606
2330
  const abs = (p) => path.resolve(cwd, p);
1607
2331
  const schemaAbs = abs(cfg.schema);
@@ -1635,6 +2359,9 @@ function trpcOptions(g, cfg, servicesDir) {
1635
2359
  };
1636
2360
  }
1637
2361
 
2362
+ // src/column-filter.ts
2363
+ var import_validation_core3 = require("@drzl/validation-core");
2364
+
1638
2365
  // src/doctor.ts
1639
2366
  var import_validation_core2 = require("@drzl/validation-core");
1640
2367
  var import_chalk = __toESM(require("chalk"), 1);
@@ -1893,6 +2620,99 @@ function renderDoctorReport(report) {
1893
2620
  return out.join("\n");
1894
2621
  }
1895
2622
 
2623
+ // src/column-filter.ts
2624
+ function checkedColumns(expression, name) {
2625
+ const parsed = (0, import_validation_core3.parseCheck)(expression, name);
2626
+ if (!parsed.ok) return [];
2627
+ return [...new Set(namedColumns(parsed).map((n) => n.column))];
2628
+ }
2629
+ function filterColumns(tables, spec) {
2630
+ const entries = Object.entries(spec ?? {});
2631
+ if (!entries.length) return { tables, warnings: [] };
2632
+ const errors = [];
2633
+ const warnings = [];
2634
+ const nameForConfig = hasNamedSchemas(tables) ? addressableName : displayTableName;
2635
+ for (const [tablePattern, rules] of entries) {
2636
+ const matched = tables.filter((t) => matchesTable([tablePattern], t));
2637
+ if (!matched.length) {
2638
+ errors.push(
2639
+ `columns[${JSON.stringify(tablePattern)}] matches no table. The schema declares: ${tables.map(nameForConfig).join(", ") || "(no tables)"}.`
2640
+ );
2641
+ continue;
2642
+ }
2643
+ const available = [...new Set(matched.flatMap((t) => t.columns.map((c) => c.name)))];
2644
+ for (const which of ["pick", "omit"]) {
2645
+ for (const pattern of rules[which] ?? []) {
2646
+ if (available.some((name) => matchesAny([pattern], name))) continue;
2647
+ errors.push(
2648
+ `columns[${JSON.stringify(tablePattern)}].${which} names ${JSON.stringify(pattern)}, which matches no column of ${matched.map(nameForConfig).join(", ")}. Available: ${available.join(", ")}.`
2649
+ );
2650
+ }
2651
+ }
2652
+ }
2653
+ warnings.push(
2654
+ ...ambiguousPatternWarnings(
2655
+ entries.map(([p]) => p),
2656
+ tables,
2657
+ "columns"
2658
+ )
2659
+ );
2660
+ const out = tables.map((table) => {
2661
+ const mine = entries.filter(([pattern]) => matchesTable([pattern], table));
2662
+ if (!mine.length) return table;
2663
+ let keep = table.columns;
2664
+ for (const [, rules] of mine) {
2665
+ if (rules.pick?.length) keep = keep.filter((c) => matchesAny(rules.pick, c.name));
2666
+ if (rules.omit?.length) keep = keep.filter((c) => !matchesAny(rules.omit, c.name));
2667
+ }
2668
+ if (keep.length === table.columns.length) return table;
2669
+ const kept = new Set(keep.map((c) => c.name));
2670
+ const dropped = table.columns.filter((c) => !kept.has(c.name));
2671
+ if (!keep.length) {
2672
+ errors.push(
2673
+ `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.`
2674
+ );
2675
+ return table;
2676
+ }
2677
+ const lostKey = (table.primaryKey?.columns ?? []).filter((n) => !kept.has(n));
2678
+ if (lostKey.length) {
2679
+ errors.push(
2680
+ `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.`
2681
+ );
2682
+ return table;
2683
+ }
2684
+ for (const c of dropped) {
2685
+ if (c.nullable || c.hasDefault || c.isGenerated || table.readOnly) continue;
2686
+ warnings.push(
2687
+ `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.`
2688
+ );
2689
+ }
2690
+ for (const k of table.checks ?? []) {
2691
+ const lost = checkedColumns(k.expression, k.name).filter(
2692
+ (n) => !kept.has(n) && table.columns.some((c) => c.name === n)
2693
+ );
2694
+ if (!lost.length) continue;
2695
+ warnings.push(
2696
+ `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.`
2697
+ );
2698
+ }
2699
+ return {
2700
+ ...table,
2701
+ columns: keep,
2702
+ unique: (table.unique ?? []).filter((k) => k.columns.every((n) => kept.has(n))),
2703
+ indexes: (table.indexes ?? []).filter((i) => i.columns.every((n) => kept.has(n))),
2704
+ ...table.foreignKeys ? { foreignKeys: table.foreignKeys.filter((f) => f.columns.every((n) => kept.has(n))) } : {}
2705
+ };
2706
+ });
2707
+ if (errors.length) {
2708
+ throw new Error(
2709
+ `drzl config: the "columns" option cannot be honoured.
2710
+ ` + errors.map((e) => ` - ${e}`).join("\n")
2711
+ );
2712
+ }
2713
+ return { tables: out, warnings };
2714
+ }
2715
+
1896
2716
  // src/drift.ts
1897
2717
  var import_node_fs = require("fs");
1898
2718
  var import_node_path = __toESM(require("path"), 1);
@@ -1979,7 +2799,7 @@ var shownThisProcess = false;
1979
2799
  var tips = [
1980
2800
  "Pair DRZL watch mode with drizzle-kit to keep schema & API synced.",
1981
2801
  "Templatize your ORPC routers to roll out new endpoints safely.",
1982
- "Need typed validators? Enable the zod, valibot, arktype, or typebox generators.",
2802
+ "Need typed validators? Enable the zod, valibot, arktype, typebox, or effect generators.",
1983
2803
  "Need JSON Schema or OpenAPI? The json-schema generator emits both, with no runtime dependency.",
1984
2804
  "Use output headers to track generated files and trim noisy diffs."
1985
2805
  ];
@@ -2093,7 +2913,7 @@ Need a template, adapter, or generator DRZL doesn't ship yet?
2093
2913
  );
2094
2914
  program.command("analyze").argument("<schema>", "path to drizzle schema (TS)").option("--relations", "include relations", true).option("--validate", "validate constraints", true).option("--out <file>", "write analysis JSON to file").option("--json", "print JSON to stdout (overrides --out)", false).action(async (schema, opts) => {
2095
2915
  try {
2096
- const analyzer = new import_analyzer.SchemaAnalyzer(schema);
2916
+ const analyzer = new import_analyzer2.SchemaAnalyzer(schema);
2097
2917
  const spinner = !opts.json ? (0, import_ora.default)("Analyzing schema...").start() : null;
2098
2918
  const start = Date.now();
2099
2919
  const res = await analyzer.analyze({
@@ -2141,7 +2961,7 @@ program.command("doctor").description("Report what DRZL cannot type or enforce i
2141
2961
  process.exit(1);
2142
2962
  return;
2143
2963
  }
2144
- const analyzer = new import_analyzer.SchemaAnalyzer(target);
2964
+ const analyzer = new import_analyzer2.SchemaAnalyzer(target);
2145
2965
  const analysis = await analyzer.analyze({
2146
2966
  includeRelations: true,
2147
2967
  validateConstraints: true
@@ -2180,7 +3000,7 @@ program.command("generate").description("Run configured generators (drzl.config.
2180
3000
  process.exit(2);
2181
3001
  return;
2182
3002
  }
2183
- const analyzer = new import_analyzer.SchemaAnalyzer(cfg.schema);
3003
+ const analyzer = new import_analyzer2.SchemaAnalyzer(cfg.schema);
2184
3004
  const spinner = (0, import_ora.default)("Analyzing...").start();
2185
3005
  const t0 = Date.now();
2186
3006
  const analysis = await analyzer.analyze({
@@ -2188,8 +3008,11 @@ program.command("generate").description("Run configured generators (drzl.config.
2188
3008
  validateConstraints: cfg.analyzer.validateConstraints,
2189
3009
  includeHeuristicRelations: cfg.analyzer.includeHeuristicRelations
2190
3010
  });
2191
- analysis.tables = filterTables(analysis.tables, cfg);
2192
3011
  spinner.succeed(`Analysis complete in ${Date.now() - t0}ms`);
3012
+ const narrowed = filterColumns(analysis.tables, cfg.columns);
3013
+ const filterWarnings = tableFilterWarnings(narrowed.tables, cfg);
3014
+ analysis.tables = filterTables(narrowed.tables, cfg);
3015
+ for (const w of [...narrowed.warnings, ...filterWarnings]) console.warn(import_chalk3.default.yellow(w));
2193
3016
  reportWideColumns(analysis.issues);
2194
3017
  const driftDirs = computeGeneratorOutputDirs(cfg);
2195
3018
  const driftBefore = opts.check ? await snapshotAll(driftDirs) : null;
@@ -2280,7 +3103,7 @@ program.command("generate").description("Run configured generators (drzl.config.
2280
3103
  const gen = new ZodGenerator(analysis);
2281
3104
  const target = g.path ?? "src/validators/zod";
2282
3105
  const files = await gen.generate(
2283
- validationOptions(g, cfg, target, { schemaTypes: true })
3106
+ validationOptions(g, cfg, target, { schemaTypes: true, meta: true })
2284
3107
  );
2285
3108
  progress.stop();
2286
3109
  (0, import_ora.default)().succeed(import_chalk3.default.green(`Generated (zod): ${files.length} files`));
@@ -2354,7 +3177,10 @@ program.command("generate").description("Run configured generators (drzl.config.
2354
3177
  const gen = new TypeBoxGenerator(analysis);
2355
3178
  const target = g.path ?? "src/validators/typebox";
2356
3179
  const files = await gen.generate(
2357
- validationOptions(g, cfg, target, { schemaTypes: true })
3180
+ validationOptions(g, cfg, target, {
3181
+ schemaTypes: true,
3182
+ standardSchema: true
3183
+ })
2358
3184
  );
2359
3185
  progress.stop();
2360
3186
  (0, import_ora.default)().succeed(import_chalk3.default.green(`Generated (typebox): ${files.length} files`));
@@ -2364,6 +3190,25 @@ program.command("generate").description("Run configured generators (drzl.config.
2364
3190
  reportGeneratorFailure(g.kind, e);
2365
3191
  process.exit(1);
2366
3192
  }
3193
+ } else if (g.kind === "effect") {
3194
+ try {
3195
+ const { EffectGenerator: EffectGenerator2 } = await loadGenerator(
3196
+ "@drzl/generator-effect",
3197
+ () => Promise.resolve().then(() => (init_dist3(), dist_exports3))
3198
+ );
3199
+ const gen = new EffectGenerator2(analysis);
3200
+ const target = g.path ?? "src/validators/effect";
3201
+ const files = await gen.generate(
3202
+ validationOptions(g, cfg, target, { schemaTypes: true })
3203
+ );
3204
+ progress.stop();
3205
+ (0, import_ora.default)().succeed(import_chalk3.default.green(`Generated (effect): ${files.length} files`));
3206
+ files.forEach((f) => console.log(" -", import_chalk3.default.cyan(f)));
3207
+ } catch (e) {
3208
+ progress.stop();
3209
+ reportGeneratorFailure(g.kind, e);
3210
+ process.exit(1);
3211
+ }
2367
3212
  }
2368
3213
  }
2369
3214
  if (driftBefore) {
@@ -2403,7 +3248,7 @@ Generated output is out of date (${drift.length} file(s)):`));
2403
3248
  });
2404
3249
  program.command("generate:orpc").argument("<schema>", "path to drizzle schema (TS)").option("-o, --outDir <dir>", "output directory", "src/api").option("--template <name>", "template name", "standard").option("--includeRelations", "include relation endpoints").action(async (schema, opts) => {
2405
3250
  try {
2406
- const analyzer = new import_analyzer.SchemaAnalyzer(schema);
3251
+ const analyzer = new import_analyzer2.SchemaAnalyzer(schema);
2407
3252
  const analysis = await analyzer.analyze({
2408
3253
  includeRelations: !!opts.includeRelations,
2409
3254
  validateConstraints: true
@@ -2423,7 +3268,7 @@ program.command("generate:orpc").argument("<schema>", "path to drizzle schema (T
2423
3268
  });
2424
3269
  program.command("generate:trpc").argument("<schema>", "path to drizzle schema (TS)").option("-o, --outDir <dir>", "output directory", "src/api").option("--template <name>", "standard | service", "standard").option("--includeRelations", "include relation endpoints").option("--servicesDir <dir>", "where the service generator writes", "src/services").action(async (schema, opts) => {
2425
3270
  try {
2426
- const analyzer = new import_analyzer.SchemaAnalyzer(schema);
3271
+ const analyzer = new import_analyzer2.SchemaAnalyzer(schema);
2427
3272
  const analysis = await analyzer.analyze({
2428
3273
  includeRelations: !!opts.includeRelations,
2429
3274
  validateConstraints: true
@@ -2525,13 +3370,17 @@ program.command("watch").description("Watch schema and regenerate on changes").o
2525
3370
  })
2526
3371
  );
2527
3372
  }
2528
- const analyzer = new import_analyzer.SchemaAnalyzer(cfg.schema);
3373
+ const analyzer = new import_analyzer2.SchemaAnalyzer(cfg.schema);
2529
3374
  const analysis = await analyzer.analyze({
2530
3375
  includeRelations: cfg.analyzer.includeRelations,
2531
3376
  validateConstraints: cfg.analyzer.validateConstraints,
2532
3377
  includeHeuristicRelations: cfg.analyzer.includeHeuristicRelations
2533
3378
  });
2534
- analysis.tables = filterTables(analysis.tables, cfg);
3379
+ const narrowed = filterColumns(analysis.tables, cfg.columns);
3380
+ const filterWarnings = tableFilterWarnings(narrowed.tables, cfg);
3381
+ analysis.tables = filterTables(narrowed.tables, cfg);
3382
+ if (!opts.json)
3383
+ for (const w of [...narrowed.warnings, ...filterWarnings]) console.warn(import_chalk3.default.yellow(w));
2535
3384
  if (!opts.json) reportWideColumns(analysis.issues);
2536
3385
  if (opts.pipeline === "analyze") {
2537
3386
  if (opts.json) {
@@ -2630,7 +3479,7 @@ program.command("watch").description("Watch schema and regenerate on changes").o
2630
3479
  const gen = new ZodGenerator(analysis);
2631
3480
  const target = g.path ?? "src/validators/zod";
2632
3481
  const files = await gen.generate(
2633
- validationOptions(g, cfg, target, { schemaTypes: true })
3482
+ validationOptions(g, cfg, target, { schemaTypes: true, meta: true })
2634
3483
  );
2635
3484
  opts.json ? console.log(JSON.stringify({ event: "generate_complete", kind: g.kind, files })) : console.log(
2636
3485
  import_chalk3.default.green(`Generated (zod): ${files.length} files`),
@@ -2690,7 +3539,10 @@ program.command("watch").description("Watch schema and regenerate on changes").o
2690
3539
  const gen = new TypeBoxGenerator(analysis);
2691
3540
  const target = g.path ?? "src/validators/typebox";
2692
3541
  const files = await gen.generate(
2693
- validationOptions(g, cfg, target, { schemaTypes: true })
3542
+ validationOptions(g, cfg, target, {
3543
+ schemaTypes: true,
3544
+ standardSchema: true
3545
+ })
2694
3546
  );
2695
3547
  opts.json ? console.log(JSON.stringify({ event: "generate_complete", kind: g.kind, files })) : console.log(
2696
3548
  import_chalk3.default.green(`Generated (typebox): ${files.length} files`),
@@ -2701,6 +3553,26 @@ program.command("watch").description("Watch schema and regenerate on changes").o
2701
3553
  reportGeneratorFailure(g.kind, e);
2702
3554
  return;
2703
3555
  }
3556
+ } else if (g.kind === "effect") {
3557
+ try {
3558
+ const { EffectGenerator: EffectGenerator2 } = await loadGenerator(
3559
+ "@drzl/generator-effect",
3560
+ () => Promise.resolve().then(() => (init_dist3(), dist_exports3))
3561
+ );
3562
+ const gen = new EffectGenerator2(analysis);
3563
+ const target = g.path ?? "src/validators/effect";
3564
+ const files = await gen.generate(
3565
+ validationOptions(g, cfg, target, { schemaTypes: true })
3566
+ );
3567
+ opts.json ? console.log(JSON.stringify({ event: "generate_complete", kind: g.kind, files })) : console.log(
3568
+ import_chalk3.default.green(`Generated (effect): ${files.length} files`),
3569
+ files.map((f) => import_chalk3.default.cyan(f)).join(", ")
3570
+ );
3571
+ newFiles.push(...files);
3572
+ } catch (e) {
3573
+ reportGeneratorFailure(g.kind, e);
3574
+ return;
3575
+ }
2704
3576
  } else if (g.kind === "json-schema") {
2705
3577
  try {
2706
3578
  const { JsonSchemaGenerator: JsonSchemaGenerator2 } = await loadGenerator(