@drzl/cli 4.19.0 → 4.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-V2IXXAC2.js → chunk-XNNKHBGV.js} +146 -7
- package/dist/chunk-XNNKHBGV.js.map +1 -0
- package/dist/cli.cjs +385 -104
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +135 -12
- package/dist/cli.js.map +1 -1
- package/dist/config.cjs +135 -6
- package/dist/config.cjs.map +1 -1
- package/dist/config.d.cts +57 -5
- package/dist/config.d.ts +57 -5
- package/dist/config.js +5 -1
- package/dist/{dist-TRJLPIWT.js → dist-K6N4F3XW.js} +19 -10
- package/dist/dist-K6N4F3XW.js.map +1 -0
- package/dist/{dist-CZDVFYFW.js → dist-WHAW3AMQ.js} +42 -16
- package/dist/dist-WHAW3AMQ.js.map +1 -0
- package/package.json +10 -10
- package/dist/chunk-V2IXXAC2.js.map +0 -1
- package/dist/dist-CZDVFYFW.js.map +0 -1
- package/dist/dist-TRJLPIWT.js.map +0 -1
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
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
|
-
|
|
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,
|
|
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 &&
|
|
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,
|
|
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,
|
|
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,
|
|
768
|
-
update: build2((0,
|
|
769
|
-
select: build2((0,
|
|
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
|
|
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,
|
|
825
|
-
return { operationId: id, tags: [table
|
|
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({
|
|
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,
|
|
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,
|
|
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) =>
|
|
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
|
|
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,
|
|
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,
|
|
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
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
1142
|
+
(0, import_validation_core5.resolveAffix)(opts),
|
|
1134
1143
|
opts?.target ?? "draft-2020-12",
|
|
1135
1144
|
!!opts?.applyDefaults
|
|
1136
1145
|
);
|
|
@@ -1179,7 +1188,7 @@ function foldedIntoBounds(c, checks) {
|
|
|
1179
1188
|
);
|
|
1180
1189
|
}
|
|
1181
1190
|
function nonFiniteBranches(c) {
|
|
1182
|
-
const { nan, infinity } = (0,
|
|
1191
|
+
const { nan, infinity } = (0, import_validation_core7.nonFiniteAccepted)(c);
|
|
1183
1192
|
return [
|
|
1184
1193
|
...nan ? [`${NS}.Number.pipe(${filter("Number.isNaN(v)", "NaN, which this column stores")})`] : [],
|
|
1185
1194
|
...infinity ? [`${NS}.Literal(Infinity, -Infinity)`] : []
|
|
@@ -1193,11 +1202,11 @@ function dateExpr(mode, coerceDates) {
|
|
|
1193
1202
|
const plain = `${NS}.ValidDateFromSelf`;
|
|
1194
1203
|
if (coerceDates === "none") return plain;
|
|
1195
1204
|
const fromString = piped(`${NS}.String`, [
|
|
1196
|
-
`${NS}.pattern(new RegExp(${JSON.stringify(
|
|
1197
|
-
filter((0,
|
|
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")
|
|
1198
1207
|
]);
|
|
1199
1208
|
const fromNumber = piped(`${NS}.Number`, [
|
|
1200
|
-
filter((0,
|
|
1209
|
+
filter((0, import_validation_core7.parsesToADate)("new Date(v)"), "a date the runtime can parse")
|
|
1201
1210
|
]);
|
|
1202
1211
|
const union = `${NS}.Union(${plain}, ${fromString}, ${fromNumber})`;
|
|
1203
1212
|
if (coerceDates === "all") return union;
|
|
@@ -1208,11 +1217,11 @@ function capSteps(c, mode) {
|
|
|
1208
1217
|
if (c.shape?.kind === "byteString") {
|
|
1209
1218
|
const n = c.shape.length;
|
|
1210
1219
|
if (!n) return steps;
|
|
1211
|
-
return mode === "select" ? [filter(`${
|
|
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`)];
|
|
1212
1221
|
}
|
|
1213
1222
|
if (c.maxLength) {
|
|
1214
1223
|
steps.push(
|
|
1215
|
-
filter(`${
|
|
1224
|
+
filter(`${import_validation_core7.CODEPOINT_LENGTH} <= ${c.maxLength}`, `at most ${c.maxLength} characters`)
|
|
1216
1225
|
);
|
|
1217
1226
|
}
|
|
1218
1227
|
if (c.maxBytes) {
|
|
@@ -1226,7 +1235,7 @@ function lengthSteps(c, lengths) {
|
|
|
1226
1235
|
if (c.arrayDimensions || c.shape) return [];
|
|
1227
1236
|
return lengths.filter((k) => k.column === c.name).map(
|
|
1228
1237
|
(k) => filter(
|
|
1229
|
-
`${
|
|
1238
|
+
`${import_validation_core7.CODEPOINT_LENGTH} ${OPS[k.operator]} ${k.value}`,
|
|
1230
1239
|
`${k.name ? `${k.name}: ` : ""}length(${c.name}) ${k.operator} ${k.value}`
|
|
1231
1240
|
)
|
|
1232
1241
|
);
|
|
@@ -1304,11 +1313,11 @@ function exprForColumn(c, mode, coerceDates, checks, sets, lengths, replaced) {
|
|
|
1304
1313
|
switch (c.tsType) {
|
|
1305
1314
|
case "string": {
|
|
1306
1315
|
const base = c.format === "uuid" ? `${NS}.UUID` : `${NS}.String`;
|
|
1307
|
-
const pattern = c.format && c.format !== "uuid" &&
|
|
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])}))`] : [];
|
|
1308
1317
|
return piped(base, [...pattern, ...capSteps(c, mode), ...rest]);
|
|
1309
1318
|
}
|
|
1310
1319
|
case "number": {
|
|
1311
|
-
const base = (0,
|
|
1320
|
+
const base = (0, import_validation_core7.isIntegerColumn)(c) ? `${NS}.Int` : `${NS}.Finite`;
|
|
1312
1321
|
return withNonFinite(c, piped(base, [...numericBounds(c, checks), ...rest]));
|
|
1313
1322
|
}
|
|
1314
1323
|
case "bigint":
|
|
@@ -1329,7 +1338,7 @@ function exprForColumn(c, mode, coerceDates, checks, sets, lengths, replaced) {
|
|
|
1329
1338
|
return UNKNOWN_EXPR;
|
|
1330
1339
|
}
|
|
1331
1340
|
}
|
|
1332
|
-
function renderField(c, mode, coerceDates, checks, sets, lengths, cardinalities, applyDefault, narrowRef) {
|
|
1341
|
+
function renderField(c, mode, coerceDates, checks, sets, lengths, cardinalities, applyDefault, narrowRef, brand) {
|
|
1333
1342
|
let expr = exprForColumn(
|
|
1334
1343
|
c,
|
|
1335
1344
|
mode,
|
|
@@ -1344,8 +1353,9 @@ function renderField(c, mode, coerceDates, checks, sets, lengths, cardinalities,
|
|
|
1344
1353
|
expr = `${NS}.Array(${expr})`;
|
|
1345
1354
|
if (i === dims - 1) expr = piped(expr, cardinalitySteps(c, cardinalities));
|
|
1346
1355
|
}
|
|
1356
|
+
if (brand) expr = piped(expr, [`${NS}.brand(${JSON.stringify(brand)})`]);
|
|
1347
1357
|
if (c.nullable && !isUnknownExpr(expr)) expr = `${NS}.NullOr(${expr})`;
|
|
1348
|
-
if (narrowRef) expr = withNarrowedType(expr, narrowRef);
|
|
1358
|
+
if (narrowRef && !brand) expr = withNarrowedType(expr, narrowRef);
|
|
1349
1359
|
if (mode === "select") return expr;
|
|
1350
1360
|
const wantsDefault = mode === "insert" && applyDefault && c.defaultValue !== void 0;
|
|
1351
1361
|
if (wantsDefault) {
|
|
@@ -1357,7 +1367,7 @@ function renderField(c, mode, coerceDates, checks, sets, lengths, cardinalities,
|
|
|
1357
1367
|
function wantsRef(c, allColumns) {
|
|
1358
1368
|
return allColumns || hasNoRuntimeType(c);
|
|
1359
1369
|
}
|
|
1360
|
-
function renderObjectShape(cols, mode, coerceDates, checks, sets, lengths, cardinalities, typedJson, applyDefaults) {
|
|
1370
|
+
function renderObjectShape(cols, mode, coerceDates, checks, sets, lengths, cardinalities, typedJson, applyDefaults, brands) {
|
|
1361
1371
|
return cols.map((c) => {
|
|
1362
1372
|
const ref2 = typedJson && wantsRef(c, !!typedJson.allColumns) ? `(typeof ${typedJson.table}.$infer${typedJson.mode === "insert" ? "Insert" : "Select"})[${JSON.stringify(c.name)}]` : void 0;
|
|
1363
1373
|
const field2 = renderField(
|
|
@@ -1369,7 +1379,8 @@ function renderObjectShape(cols, mode, coerceDates, checks, sets, lengths, cardi
|
|
|
1369
1379
|
lengths,
|
|
1370
1380
|
cardinalities,
|
|
1371
1381
|
applyDefaults,
|
|
1372
|
-
ref2
|
|
1382
|
+
ref2,
|
|
1383
|
+
brands?.plan.brandOf(brands.tsName, c.name)
|
|
1373
1384
|
);
|
|
1374
1385
|
return ` ${JSON.stringify(c.name)}: ${field2},`;
|
|
1375
1386
|
}).join("\n");
|
|
@@ -1387,7 +1398,7 @@ function indentBlock(code, by = " ") {
|
|
|
1387
1398
|
return code.split("\n").map((line) => line ? by + line : line).join("\n");
|
|
1388
1399
|
}
|
|
1389
1400
|
function parsedChecksFor(table) {
|
|
1390
|
-
const parsed = (table.checks ?? []).map((k) => (0,
|
|
1401
|
+
const parsed = (table.checks ?? []).map((k) => (0, import_validation_core7.parseCheck)(k.expression, k.name));
|
|
1391
1402
|
return {
|
|
1392
1403
|
checks: parsed.flatMap((p) => p.ok ? p.checks : []),
|
|
1393
1404
|
sets: parsed.flatMap((p) => p.ok ? p.sets ?? [] : []),
|
|
@@ -1397,15 +1408,15 @@ function parsedChecksFor(table) {
|
|
|
1397
1408
|
};
|
|
1398
1409
|
}
|
|
1399
1410
|
function nestedNodeCols(node, mode) {
|
|
1400
|
-
const all = mode === "insert" ? (0,
|
|
1401
|
-
return (0,
|
|
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);
|
|
1402
1413
|
}
|
|
1403
1414
|
function nestedNodes(node, into = []) {
|
|
1404
1415
|
into.push(node);
|
|
1405
1416
|
for (const arm of node.arms) nestedNodes(arm.child, into);
|
|
1406
1417
|
return into;
|
|
1407
1418
|
}
|
|
1408
|
-
function renderNestedObject(node, mode, coerceDates, typedJson, applyDefaults) {
|
|
1419
|
+
function renderNestedObject(node, mode, coerceDates, typedJson, applyDefaults, brands) {
|
|
1409
1420
|
const cols = nestedNodeCols(node, mode);
|
|
1410
1421
|
const { checks, sets, rows, lengths, cardinalities } = parsedChecksFor(node.table);
|
|
1411
1422
|
const tj = typedJson ? { table: node.table.tsName, mode, allColumns: typedJson.allColumns } : void 0;
|
|
@@ -1418,12 +1429,20 @@ function renderNestedObject(node, mode, coerceDates, typedJson, applyDefaults) {
|
|
|
1418
1429
|
lengths,
|
|
1419
1430
|
cardinalities,
|
|
1420
1431
|
tj,
|
|
1421
|
-
applyDefaults
|
|
1432
|
+
applyDefaults,
|
|
1433
|
+
brands ? { plan: brands, tsName: node.table.tsName } : void 0
|
|
1422
1434
|
);
|
|
1423
1435
|
const arms = node.arms.map((arm) => {
|
|
1424
|
-
const notes = (0,
|
|
1436
|
+
const notes = (0, import_validation_core7.nestedArmNotes)(arm).map((n) => ` // ${n}
|
|
1425
1437
|
`).join("");
|
|
1426
|
-
const child = renderNestedObject(
|
|
1438
|
+
const child = renderNestedObject(
|
|
1439
|
+
arm.child,
|
|
1440
|
+
mode,
|
|
1441
|
+
coerceDates,
|
|
1442
|
+
typedJson,
|
|
1443
|
+
applyDefaults,
|
|
1444
|
+
brands
|
|
1445
|
+
);
|
|
1427
1446
|
const inner = arm.single ? `${NS}.NullOr(
|
|
1428
1447
|
${indentBlock(indentBlock(child))}
|
|
1429
1448
|
)` : `${NS}.Array(
|
|
@@ -1436,14 +1455,14 @@ ${indentBlock(indentBlock(child))}
|
|
|
1436
1455
|
${body}
|
|
1437
1456
|
})`, rowSteps(rows, cols));
|
|
1438
1457
|
}
|
|
1439
|
-
function renderNestedSchemas(table, affix, coerceDates, typedJson, applyDefaults, plans) {
|
|
1458
|
+
function renderNestedSchemas(table, affix, coerceDates, typedJson, applyDefaults, plans, brands) {
|
|
1440
1459
|
const out = [];
|
|
1441
1460
|
for (const mode of ["insert", "select"]) {
|
|
1442
1461
|
const plan = plans[mode];
|
|
1443
1462
|
if (!plan) continue;
|
|
1444
|
-
const name = (0,
|
|
1445
|
-
const tname = (0,
|
|
1446
|
-
const expr = renderNestedObject(plan, mode, coerceDates, typedJson, applyDefaults);
|
|
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);
|
|
1447
1466
|
out.push(
|
|
1448
1467
|
`export const ${name} = ${expr};
|
|
1449
1468
|
|
|
@@ -1460,16 +1479,16 @@ function nestedPlansFor(table, analysis, depth) {
|
|
|
1460
1479
|
const out = {};
|
|
1461
1480
|
for (const mode of ["insert", "select"]) {
|
|
1462
1481
|
if (mode === "insert" && table.readOnly) continue;
|
|
1463
|
-
const plan = (0,
|
|
1482
|
+
const plan = (0, import_validation_core7.buildNestedPlan)(table, analysis.tables, analysis.relations ?? [], mode, depth);
|
|
1464
1483
|
if (plan) out[mode] = plan;
|
|
1465
1484
|
}
|
|
1466
1485
|
return out;
|
|
1467
1486
|
}
|
|
1468
|
-
function renderTableSchemas(table, affix, coerceDates, typedJson, applyDefaults = false, wantsDuplicateFinder = false, nested = {}) {
|
|
1487
|
+
function renderTableSchemas(table, affix, coerceDates, typedJson, applyDefaults = false, wantsDuplicateFinder = false, nested = {}, brands) {
|
|
1469
1488
|
const T = table.tsName;
|
|
1470
|
-
const insertCols = (0,
|
|
1471
|
-
const updateCols = (0,
|
|
1472
|
-
const selectCols = (0,
|
|
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);
|
|
1473
1492
|
const { checks, sets, rows, lengths, cardinalities } = parsedChecksFor(table);
|
|
1474
1493
|
const tj = typedJson ? { table: T, allColumns: typedJson.allColumns } : void 0;
|
|
1475
1494
|
const modes = [
|
|
@@ -1478,8 +1497,8 @@ function renderTableSchemas(table, affix, coerceDates, typedJson, applyDefaults
|
|
|
1478
1497
|
["select", selectCols]
|
|
1479
1498
|
];
|
|
1480
1499
|
const blocks = modes.map(([mode, cols]) => {
|
|
1481
|
-
const name = (0,
|
|
1482
|
-
const tname = (0,
|
|
1500
|
+
const name = (0, import_validation_core7.schemaName)(mode, T, affix);
|
|
1501
|
+
const tname = (0, import_validation_core7.typeName)(mode, T, affix);
|
|
1483
1502
|
const body = renderObjectShape(
|
|
1484
1503
|
cols,
|
|
1485
1504
|
mode,
|
|
@@ -1491,7 +1510,8 @@ function renderTableSchemas(table, affix, coerceDates, typedJson, applyDefaults
|
|
|
1491
1510
|
// The update schema references the insert-side inferred types: both describe a value going
|
|
1492
1511
|
// in, and `$inferSelect` would name the post-default type for a column a write may omit.
|
|
1493
1512
|
tj ? { ...tj, mode: mode === "select" ? "select" : "insert" } : void 0,
|
|
1494
|
-
applyDefaults
|
|
1513
|
+
applyDefaults,
|
|
1514
|
+
brands ? { plan: brands, tsName: T } : void 0
|
|
1495
1515
|
);
|
|
1496
1516
|
const expr = piped(`${NS}.Struct({
|
|
1497
1517
|
${body}
|
|
@@ -1522,7 +1542,7 @@ export const ${STANDARD_PREFIX}${name} = ${NS}.standardSchemaV1(${name});`;
|
|
|
1522
1542
|
const needsJson = !typedJson && [...insertCols, ...updateCols, ...selectCols, ...nestedCols].some(
|
|
1523
1543
|
(c) => c.shape?.kind === "json"
|
|
1524
1544
|
);
|
|
1525
|
-
const finder = wantsDuplicateFinder ? (0,
|
|
1545
|
+
const finder = wantsDuplicateFinder ? (0, import_validation_core7.renderDuplicateFinder)(table, `findDuplicate${T}`, (0, import_validation_core7.typeName)("insert", T, affix)) : void 0;
|
|
1526
1546
|
const duplicates = finder ? `
|
|
1527
1547
|
${finder}
|
|
1528
1548
|
` : "";
|
|
@@ -1532,13 +1552,22 @@ ${finder}
|
|
|
1532
1552
|
coerceDates,
|
|
1533
1553
|
typedJson,
|
|
1534
1554
|
applyDefaults,
|
|
1535
|
-
nested
|
|
1555
|
+
nested,
|
|
1556
|
+
brands
|
|
1536
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
|
+
` : "";
|
|
1537
1566
|
return `import * as ${NS} from 'effect/Schema';
|
|
1538
1567
|
${schemaImport}${needsJson ? `
|
|
1539
1568
|
${JSON_PREAMBLE}` : ""}
|
|
1540
1569
|
${blocks.join("\n\n")}
|
|
1541
|
-
${nestedCode}${duplicates}`;
|
|
1570
|
+
${brandCode}${nestedCode}${duplicates}`;
|
|
1542
1571
|
}
|
|
1543
1572
|
function buildHeader3(h) {
|
|
1544
1573
|
if (h && h.enabled === false) return "";
|
|
@@ -1550,11 +1579,11 @@ function buildHeader3(h) {
|
|
|
1550
1579
|
];
|
|
1551
1580
|
return lines.join("\n") + "\n\n";
|
|
1552
1581
|
}
|
|
1553
|
-
var
|
|
1582
|
+
var import_validation_core7, DEFAULT_FILE_SUFFIX2, STANDARD_PREFIX, NS, OPS, JSON_CONST, JSON_PREAMBLE, UNKNOWN_EXPR, EffectGenerator, index_default3;
|
|
1554
1583
|
var init_dist3 = __esm({
|
|
1555
1584
|
"../generator-effect/dist/index.js"() {
|
|
1556
1585
|
"use strict";
|
|
1557
|
-
|
|
1586
|
+
import_validation_core7 = require("@drzl/validation-core");
|
|
1558
1587
|
DEFAULT_FILE_SUFFIX2 = ".effect.ts";
|
|
1559
1588
|
STANDARD_PREFIX = "Standard";
|
|
1560
1589
|
NS = "Schema";
|
|
@@ -1612,12 +1641,12 @@ const ${JSON_CONST}: ${NS}.Schema<${JSON_CONST}Type, unknown> = ${NS}.suspend(()
|
|
|
1612
1641
|
const out = path6.resolve(process.cwd(), opts.outDir);
|
|
1613
1642
|
const files = [];
|
|
1614
1643
|
await fs3.mkdir(out, { recursive: true });
|
|
1615
|
-
const affix = (0,
|
|
1644
|
+
const affix = (0, import_validation_core7.resolveAffix)(opts);
|
|
1616
1645
|
const coerceDates = opts.coerceDates ?? "input";
|
|
1617
1646
|
const fileSuffix = opts.fileSuffix ?? DEFAULT_FILE_SUFFIX2;
|
|
1618
1647
|
const wantsTypes = opts.typedJson || opts.typedColumns;
|
|
1619
1648
|
const typedJson = wantsTypes && opts.schemaPath ? {
|
|
1620
|
-
schemaSpecifier: (0,
|
|
1649
|
+
schemaSpecifier: (0, import_validation_core7.resolveConfiguredImport)(
|
|
1621
1650
|
opts.schemaPath,
|
|
1622
1651
|
out,
|
|
1623
1652
|
process.cwd(),
|
|
@@ -1630,9 +1659,11 @@ const ${JSON_CONST}: ${NS}.Schema<${JSON_CONST}Type, unknown> = ${NS}.suspend(()
|
|
|
1630
1659
|
"[drzl] typedJson was requested but the schema path is unknown, so json columns keep their wide type."
|
|
1631
1660
|
);
|
|
1632
1661
|
}
|
|
1633
|
-
const nestedDepth = opts.nestedSchemas ? (0,
|
|
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}`);
|
|
1634
1665
|
for (const table of this.analysis.tables) {
|
|
1635
|
-
const filePath = path6.join(out, (0,
|
|
1666
|
+
const filePath = path6.join(out, (0, import_validation_core7.moduleFileName)(table.tsName, fileSuffix));
|
|
1636
1667
|
const code = renderTableSchemas(
|
|
1637
1668
|
table,
|
|
1638
1669
|
affix,
|
|
@@ -1640,9 +1671,10 @@ const ${JSON_CONST}: ${NS}.Schema<${JSON_CONST}Type, unknown> = ${NS}.suspend(()
|
|
|
1640
1671
|
typedJson,
|
|
1641
1672
|
!!opts.applyDefaults,
|
|
1642
1673
|
!!opts?.duplicateFinder,
|
|
1643
|
-
opts.nestedSchemas ? nestedPlansFor(table, this.analysis, nestedDepth) : {}
|
|
1674
|
+
opts.nestedSchemas ? nestedPlansFor(table, this.analysis, nestedDepth) : {},
|
|
1675
|
+
brands
|
|
1644
1676
|
);
|
|
1645
|
-
const formatted = await (0,
|
|
1677
|
+
const formatted = await (0, import_validation_core7.formatCode)(
|
|
1646
1678
|
buildHeader3(opts.outputHeader) + code,
|
|
1647
1679
|
filePath,
|
|
1648
1680
|
opts.format
|
|
@@ -1651,7 +1683,7 @@ const ${JSON_CONST}: ${NS}.Schema<${JSON_CONST}Type, unknown> = ${NS}.suspend(()
|
|
|
1651
1683
|
files.push(filePath);
|
|
1652
1684
|
}
|
|
1653
1685
|
const indexPath = path6.join(out, "index.ts");
|
|
1654
|
-
const indexFormatted = await (0,
|
|
1686
|
+
const indexFormatted = await (0, import_validation_core7.formatCode)(
|
|
1655
1687
|
buildHeader3(opts.outputHeader) + this.defaultIndex(this.analysis, opts),
|
|
1656
1688
|
indexPath,
|
|
1657
1689
|
opts.format
|
|
@@ -1663,17 +1695,19 @@ const ${JSON_CONST}: ${NS}.Schema<${JSON_CONST}Type, unknown> = ${NS}.suspend(()
|
|
|
1663
1695
|
renderTable(table, opts) {
|
|
1664
1696
|
return renderTableSchemas(
|
|
1665
1697
|
table,
|
|
1666
|
-
(0,
|
|
1698
|
+
(0, import_validation_core7.resolveAffix)(opts),
|
|
1667
1699
|
opts?.coerceDates ?? "input",
|
|
1668
1700
|
void 0,
|
|
1669
1701
|
!!opts?.applyDefaults,
|
|
1670
|
-
!!opts?.duplicateFinder
|
|
1702
|
+
!!opts?.duplicateFinder,
|
|
1703
|
+
{},
|
|
1704
|
+
(0, import_validation_core7.buildBrandPlan)(this.analysis.tables, opts?.branded)
|
|
1671
1705
|
);
|
|
1672
1706
|
}
|
|
1673
1707
|
defaultIndex(analysis, opts) {
|
|
1674
1708
|
const fileSuffix = opts.fileSuffix ?? DEFAULT_FILE_SUFFIX2;
|
|
1675
1709
|
return analysis.tables.map(
|
|
1676
|
-
(t) => `export * from '${(0,
|
|
1710
|
+
(t) => `export * from '${(0, import_validation_core7.moduleSpecifier)(t.tsName, fileSuffix, opts.importExtension)}';`
|
|
1677
1711
|
).join("\n") + "\n";
|
|
1678
1712
|
}
|
|
1679
1713
|
};
|
|
@@ -1682,7 +1716,7 @@ const ${JSON_CONST}: ${NS}.Schema<${JSON_CONST}Type, unknown> = ${NS}.suspend(()
|
|
|
1682
1716
|
});
|
|
1683
1717
|
|
|
1684
1718
|
// src/cli.ts
|
|
1685
|
-
var
|
|
1719
|
+
var import_analyzer2 = require("@drzl/analyzer");
|
|
1686
1720
|
var import_generator_orpc = require("@drzl/generator-orpc");
|
|
1687
1721
|
var import_chalk3 = __toESM(require("chalk"), 1);
|
|
1688
1722
|
var import_chokidar = __toESM(require("chokidar"), 1);
|
|
@@ -1706,6 +1740,11 @@ function validationOptions(g, cfg, outDir, caps = {}) {
|
|
|
1706
1740
|
duplicateFinder: g.duplicateFinder,
|
|
1707
1741
|
nestedSchemas: g.nestedSchemas,
|
|
1708
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,
|
|
1709
1748
|
// Only where the generator can act on them, so an unsupported option is absent rather than
|
|
1710
1749
|
// present and ignored.
|
|
1711
1750
|
...caps.schemaTypes ? {
|
|
@@ -1713,7 +1752,9 @@ function validationOptions(g, cfg, outDir, caps = {}) {
|
|
|
1713
1752
|
schemaPath: cfg.schema,
|
|
1714
1753
|
typedJson: g.typedJson,
|
|
1715
1754
|
typedColumns: g.typedColumns
|
|
1716
|
-
} : {}
|
|
1755
|
+
} : {},
|
|
1756
|
+
...caps.standardSchema ? { standardSchema: g.standardSchema } : {},
|
|
1757
|
+
...caps.meta ? { meta: g.meta } : {}
|
|
1717
1758
|
};
|
|
1718
1759
|
}
|
|
1719
1760
|
|
|
@@ -1737,6 +1778,47 @@ var fs = __toESM(require("fs"), 1);
|
|
|
1737
1778
|
var import_node_module = require("module");
|
|
1738
1779
|
var path = __toESM(require("path"), 1);
|
|
1739
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
|
|
1740
1822
|
var NamingSchema = import_zod.z.object({
|
|
1741
1823
|
routerSuffix: import_zod.z.string().default("Router"),
|
|
1742
1824
|
procedureCase: import_zod.z.enum(["camel", "kebab", "snake"]).default("camel")
|
|
@@ -1816,6 +1898,36 @@ var GeneratorSchema = import_zod.z.object({
|
|
|
1816
1898
|
* fact about the table rather than the row. This checks the half that needs no database.
|
|
1817
1899
|
*/
|
|
1818
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(),
|
|
1819
1931
|
/**
|
|
1820
1932
|
* Emit `NestedInsert<Table>` and `NestedSelect<Table>` beside the flat schemas: the table plus
|
|
1821
1933
|
* one key per relation, so `{ ...user, posts: [...] }` can be validated whole.
|
|
@@ -1832,6 +1944,29 @@ var GeneratorSchema = import_zod.z.object({
|
|
|
1832
1944
|
* it is also what terminates a cycle: `users -> posts -> users` stops here.
|
|
1833
1945
|
*/
|
|
1834
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(),
|
|
1835
1970
|
naming: NamingSchema.optional(),
|
|
1836
1971
|
outputHeader: import_zod.z.object({
|
|
1837
1972
|
enabled: import_zod.z.boolean().default(true).optional(),
|
|
@@ -1926,6 +2061,10 @@ var GeneratorSchema = import_zod.z.object({
|
|
|
1926
2061
|
// template options
|
|
1927
2062
|
templateOptions: import_zod.z.record(import_zod.z.string(), import_zod.z.any()).optional()
|
|
1928
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();
|
|
1929
2068
|
var AnalyzerSchema = import_zod.z.object({
|
|
1930
2069
|
includeRelations: import_zod.z.boolean().default(true),
|
|
1931
2070
|
validateConstraints: import_zod.z.boolean().default(true),
|
|
@@ -1952,6 +2091,37 @@ var ConfigSchema = import_zod.z.object({
|
|
|
1952
2091
|
*/
|
|
1953
2092
|
include: import_zod.z.array(import_zod.z.string()).optional(),
|
|
1954
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(),
|
|
1955
2125
|
/**
|
|
1956
2126
|
* How every relative specifier drzl invents spells its extension, for every generator.
|
|
1957
2127
|
* A generator may override it. Defaults to `js`, which is the only form that resolves
|
|
@@ -2145,15 +2315,17 @@ function resolveTemplateDirsSync(cfg, cwd = process.cwd()) {
|
|
|
2145
2315
|
return Array.from(new Set(results));
|
|
2146
2316
|
}
|
|
2147
2317
|
function filterTables(tables, opts) {
|
|
2148
|
-
const toRegExp = (pattern) => new RegExp(
|
|
2149
|
-
"^" + pattern.split("*").map((part) => part.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join(".*") + "$"
|
|
2150
|
-
);
|
|
2151
|
-
const matches = (patterns, name) => patterns.some((p) => toRegExp(p).test(name));
|
|
2152
2318
|
let out = tables;
|
|
2153
|
-
if (opts.include?.length) out = out.filter((t) =>
|
|
2154
|
-
if (opts.exclude?.length) out = out.filter((t) => !
|
|
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));
|
|
2155
2321
|
return out;
|
|
2156
2322
|
}
|
|
2323
|
+
function tableFilterWarnings(tables, opts) {
|
|
2324
|
+
return [
|
|
2325
|
+
...ambiguousPatternWarnings(opts.include ?? [], tables, "include"),
|
|
2326
|
+
...ambiguousPatternWarnings(opts.exclude ?? [], tables, "exclude")
|
|
2327
|
+
];
|
|
2328
|
+
}
|
|
2157
2329
|
function computeWatchTargets(cfg, cwd = process.cwd()) {
|
|
2158
2330
|
const abs = (p) => path.resolve(cwd, p);
|
|
2159
2331
|
const schemaAbs = abs(cfg.schema);
|
|
@@ -2187,6 +2359,9 @@ function trpcOptions(g, cfg, servicesDir) {
|
|
|
2187
2359
|
};
|
|
2188
2360
|
}
|
|
2189
2361
|
|
|
2362
|
+
// src/column-filter.ts
|
|
2363
|
+
var import_validation_core3 = require("@drzl/validation-core");
|
|
2364
|
+
|
|
2190
2365
|
// src/doctor.ts
|
|
2191
2366
|
var import_validation_core2 = require("@drzl/validation-core");
|
|
2192
2367
|
var import_chalk = __toESM(require("chalk"), 1);
|
|
@@ -2445,6 +2620,99 @@ function renderDoctorReport(report) {
|
|
|
2445
2620
|
return out.join("\n");
|
|
2446
2621
|
}
|
|
2447
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
|
+
|
|
2448
2716
|
// src/drift.ts
|
|
2449
2717
|
var import_node_fs = require("fs");
|
|
2450
2718
|
var import_node_path = __toESM(require("path"), 1);
|
|
@@ -2645,7 +2913,7 @@ Need a template, adapter, or generator DRZL doesn't ship yet?
|
|
|
2645
2913
|
);
|
|
2646
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) => {
|
|
2647
2915
|
try {
|
|
2648
|
-
const analyzer = new
|
|
2916
|
+
const analyzer = new import_analyzer2.SchemaAnalyzer(schema);
|
|
2649
2917
|
const spinner = !opts.json ? (0, import_ora.default)("Analyzing schema...").start() : null;
|
|
2650
2918
|
const start = Date.now();
|
|
2651
2919
|
const res = await analyzer.analyze({
|
|
@@ -2693,7 +2961,7 @@ program.command("doctor").description("Report what DRZL cannot type or enforce i
|
|
|
2693
2961
|
process.exit(1);
|
|
2694
2962
|
return;
|
|
2695
2963
|
}
|
|
2696
|
-
const analyzer = new
|
|
2964
|
+
const analyzer = new import_analyzer2.SchemaAnalyzer(target);
|
|
2697
2965
|
const analysis = await analyzer.analyze({
|
|
2698
2966
|
includeRelations: true,
|
|
2699
2967
|
validateConstraints: true
|
|
@@ -2732,7 +3000,7 @@ program.command("generate").description("Run configured generators (drzl.config.
|
|
|
2732
3000
|
process.exit(2);
|
|
2733
3001
|
return;
|
|
2734
3002
|
}
|
|
2735
|
-
const analyzer = new
|
|
3003
|
+
const analyzer = new import_analyzer2.SchemaAnalyzer(cfg.schema);
|
|
2736
3004
|
const spinner = (0, import_ora.default)("Analyzing...").start();
|
|
2737
3005
|
const t0 = Date.now();
|
|
2738
3006
|
const analysis = await analyzer.analyze({
|
|
@@ -2740,8 +3008,11 @@ program.command("generate").description("Run configured generators (drzl.config.
|
|
|
2740
3008
|
validateConstraints: cfg.analyzer.validateConstraints,
|
|
2741
3009
|
includeHeuristicRelations: cfg.analyzer.includeHeuristicRelations
|
|
2742
3010
|
});
|
|
2743
|
-
analysis.tables = filterTables(analysis.tables, cfg);
|
|
2744
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));
|
|
2745
3016
|
reportWideColumns(analysis.issues);
|
|
2746
3017
|
const driftDirs = computeGeneratorOutputDirs(cfg);
|
|
2747
3018
|
const driftBefore = opts.check ? await snapshotAll(driftDirs) : null;
|
|
@@ -2832,7 +3103,7 @@ program.command("generate").description("Run configured generators (drzl.config.
|
|
|
2832
3103
|
const gen = new ZodGenerator(analysis);
|
|
2833
3104
|
const target = g.path ?? "src/validators/zod";
|
|
2834
3105
|
const files = await gen.generate(
|
|
2835
|
-
validationOptions(g, cfg, target, { schemaTypes: true })
|
|
3106
|
+
validationOptions(g, cfg, target, { schemaTypes: true, meta: true })
|
|
2836
3107
|
);
|
|
2837
3108
|
progress.stop();
|
|
2838
3109
|
(0, import_ora.default)().succeed(import_chalk3.default.green(`Generated (zod): ${files.length} files`));
|
|
@@ -2906,7 +3177,10 @@ program.command("generate").description("Run configured generators (drzl.config.
|
|
|
2906
3177
|
const gen = new TypeBoxGenerator(analysis);
|
|
2907
3178
|
const target = g.path ?? "src/validators/typebox";
|
|
2908
3179
|
const files = await gen.generate(
|
|
2909
|
-
validationOptions(g, cfg, target, {
|
|
3180
|
+
validationOptions(g, cfg, target, {
|
|
3181
|
+
schemaTypes: true,
|
|
3182
|
+
standardSchema: true
|
|
3183
|
+
})
|
|
2910
3184
|
);
|
|
2911
3185
|
progress.stop();
|
|
2912
3186
|
(0, import_ora.default)().succeed(import_chalk3.default.green(`Generated (typebox): ${files.length} files`));
|
|
@@ -2974,7 +3248,7 @@ Generated output is out of date (${drift.length} file(s)):`));
|
|
|
2974
3248
|
});
|
|
2975
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) => {
|
|
2976
3250
|
try {
|
|
2977
|
-
const analyzer = new
|
|
3251
|
+
const analyzer = new import_analyzer2.SchemaAnalyzer(schema);
|
|
2978
3252
|
const analysis = await analyzer.analyze({
|
|
2979
3253
|
includeRelations: !!opts.includeRelations,
|
|
2980
3254
|
validateConstraints: true
|
|
@@ -2994,7 +3268,7 @@ program.command("generate:orpc").argument("<schema>", "path to drizzle schema (T
|
|
|
2994
3268
|
});
|
|
2995
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) => {
|
|
2996
3270
|
try {
|
|
2997
|
-
const analyzer = new
|
|
3271
|
+
const analyzer = new import_analyzer2.SchemaAnalyzer(schema);
|
|
2998
3272
|
const analysis = await analyzer.analyze({
|
|
2999
3273
|
includeRelations: !!opts.includeRelations,
|
|
3000
3274
|
validateConstraints: true
|
|
@@ -3096,13 +3370,17 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
3096
3370
|
})
|
|
3097
3371
|
);
|
|
3098
3372
|
}
|
|
3099
|
-
const analyzer = new
|
|
3373
|
+
const analyzer = new import_analyzer2.SchemaAnalyzer(cfg.schema);
|
|
3100
3374
|
const analysis = await analyzer.analyze({
|
|
3101
3375
|
includeRelations: cfg.analyzer.includeRelations,
|
|
3102
3376
|
validateConstraints: cfg.analyzer.validateConstraints,
|
|
3103
3377
|
includeHeuristicRelations: cfg.analyzer.includeHeuristicRelations
|
|
3104
3378
|
});
|
|
3105
|
-
|
|
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));
|
|
3106
3384
|
if (!opts.json) reportWideColumns(analysis.issues);
|
|
3107
3385
|
if (opts.pipeline === "analyze") {
|
|
3108
3386
|
if (opts.json) {
|
|
@@ -3201,7 +3479,7 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
3201
3479
|
const gen = new ZodGenerator(analysis);
|
|
3202
3480
|
const target = g.path ?? "src/validators/zod";
|
|
3203
3481
|
const files = await gen.generate(
|
|
3204
|
-
validationOptions(g, cfg, target, { schemaTypes: true })
|
|
3482
|
+
validationOptions(g, cfg, target, { schemaTypes: true, meta: true })
|
|
3205
3483
|
);
|
|
3206
3484
|
opts.json ? console.log(JSON.stringify({ event: "generate_complete", kind: g.kind, files })) : console.log(
|
|
3207
3485
|
import_chalk3.default.green(`Generated (zod): ${files.length} files`),
|
|
@@ -3261,7 +3539,10 @@ program.command("watch").description("Watch schema and regenerate on changes").o
|
|
|
3261
3539
|
const gen = new TypeBoxGenerator(analysis);
|
|
3262
3540
|
const target = g.path ?? "src/validators/typebox";
|
|
3263
3541
|
const files = await gen.generate(
|
|
3264
|
-
validationOptions(g, cfg, target, {
|
|
3542
|
+
validationOptions(g, cfg, target, {
|
|
3543
|
+
schemaTypes: true,
|
|
3544
|
+
standardSchema: true
|
|
3545
|
+
})
|
|
3265
3546
|
);
|
|
3266
3547
|
opts.json ? console.log(JSON.stringify({ event: "generate_complete", kind: g.kind, files })) : console.log(
|
|
3267
3548
|
import_chalk3.default.green(`Generated (typebox): ${files.length} files`),
|