kysely-gen 0.11.0 → 0.12.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.
Files changed (3) hide show
  1. package/README.md +16 -0
  2. package/dist/cli.js +121 -8
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -52,6 +52,7 @@ npx kysely-gen --dialect mysql --url mysql://user:pass@localhost:3306/db
52
52
  | `--include-pattern <glob>` | Only include matching tables |
53
53
  | `--exclude-pattern <glob>` | Exclude matching tables |
54
54
  | `--zod` | Generate Zod schemas instead of TypeScript interfaces |
55
+ | `--no-boolean-coerce` | Output `0 \| 1` instead of coercing to `boolean` for CHECK constraints |
55
56
 
56
57
  ## Zod Schema Generation
57
58
 
@@ -159,6 +160,21 @@ export interface DB {
159
160
  - All MSSQL types: `uniqueidentifier`, `datetime2`, `datetimeoffset`, `money`, `xml`, `varbinary`, etc.
160
161
  - Both URL (`mssql://`) and ADO.NET connection string formats
161
162
 
163
+ ### Boolean Coercion (SQLite/MSSQL)
164
+
165
+ SQLite and MSSQL store booleans as integers (0/1). When a `CHECK(col IN (0, 1))` constraint is detected, kysely-gen generates:
166
+
167
+ **TypeScript:** `boolean`
168
+ **Zod:** `z.union([z.literal(0), z.literal(1)]).transform(v => v === 1)`
169
+
170
+ The Zod transform coerces the raw 0/1 from the database to `true`/`false`, ensuring runtime validation passes.
171
+
172
+ Use `--no-boolean-coerce` to output raw `0 | 1` instead:
173
+
174
+ ```sh
175
+ kysely-gen --zod --no-boolean-coerce
176
+ ```
177
+
162
178
  ## Type Mappings
163
179
 
164
180
  Types are generated to match the default behavior of each database driver. If you customize your driver's type parsers, the generated types may not match.
package/dist/cli.js CHANGED
@@ -109669,7 +109669,17 @@ function transformColumn(column, enums, enumResolver, mapType, options, unknownT
109669
109669
  }
109670
109670
  } else if (column.checkConstraint) {
109671
109671
  if (column.checkConstraint.type === "boolean") {
109672
- type = { kind: "primitive", value: "boolean" };
109672
+ if (options?.noBooleanCoerce) {
109673
+ type = {
109674
+ kind: "union",
109675
+ types: [
109676
+ { kind: "literal", value: 0 },
109677
+ { kind: "literal", value: 1 }
109678
+ ]
109679
+ };
109680
+ } else {
109681
+ type = { kind: "primitive", value: "boolean" };
109682
+ }
109673
109683
  if (column.isNullable) {
109674
109684
  type = {
109675
109685
  kind: "union",
@@ -109848,6 +109858,46 @@ function isBooleanPattern(values) {
109848
109858
  const sorted = [...values].sort((a, b) => a - b);
109849
109859
  return sorted[0] === 0 && sorted[1] === 1;
109850
109860
  }
109861
+ var MSSQL_IN_REGEX = /\[\w+\]\s+IN\s*\(([^)]+)\)/i;
109862
+ function parseMssqlCheckConstraint(definition) {
109863
+ if (!definition || definition.trim() === "")
109864
+ return null;
109865
+ const inMatch = definition.match(MSSQL_IN_REGEX);
109866
+ if (inMatch) {
109867
+ const valuesPart = inMatch[1];
109868
+ if (!valuesPart || valuesPart.trim() === "")
109869
+ return null;
109870
+ const numericValues = parseNumericArray(valuesPart);
109871
+ if (numericValues !== null) {
109872
+ if (isBooleanPattern(numericValues)) {
109873
+ return { type: "boolean" };
109874
+ }
109875
+ return { type: "number", values: numericValues };
109876
+ }
109877
+ const stringValues = parseStringArray(valuesPart);
109878
+ if (stringValues !== null && stringValues.length > 0) {
109879
+ return { type: "string", values: stringValues };
109880
+ }
109881
+ }
109882
+ if (definition.includes(" OR ")) {
109883
+ const values = parseMssqlOrChain(definition);
109884
+ if (values !== null && values.length > 0) {
109885
+ return { type: "string", values };
109886
+ }
109887
+ }
109888
+ return null;
109889
+ }
109890
+ function parseMssqlOrChain(definition) {
109891
+ const values = [];
109892
+ let match;
109893
+ const regex2 = /\[\w+\]='((?:[^']|'')*)'/g;
109894
+ while ((match = regex2.exec(definition)) !== null) {
109895
+ let value = match[1];
109896
+ value = value.replace(/''/g, "'");
109897
+ values.push(value);
109898
+ }
109899
+ return values.length > 0 ? values : null;
109900
+ }
109851
109901
 
109852
109902
  // src/dialects/postgres/introspect.ts
109853
109903
  async function introspectPostgres(db, options) {
@@ -110857,11 +110907,22 @@ class SqliteDialect2 {
110857
110907
 
110858
110908
  // src/dialects/mssql/introspect.ts
110859
110909
  async function introspectMssql(db, options) {
110860
- const [baseTables, views] = await Promise.all([
110910
+ const [baseTables, views, checkConstraints] = await Promise.all([
110861
110911
  introspectTables4(db, options.schemas),
110862
- introspectViews4(db, options.schemas)
110912
+ introspectViews4(db, options.schemas),
110913
+ introspectCheckConstraints3(db, options.schemas)
110863
110914
  ]);
110864
- const tables = [...baseTables, ...views];
110915
+ const tables = [...baseTables, ...views].map((table) => ({
110916
+ ...table,
110917
+ columns: table.columns.map((column) => {
110918
+ const key = `${table.schema}.${table.name}.${column.name}`;
110919
+ const checkConstraint = checkConstraints.get(key);
110920
+ return {
110921
+ ...column,
110922
+ ...checkConstraint && { checkConstraint }
110923
+ };
110924
+ })
110925
+ }));
110865
110926
  return {
110866
110927
  tables,
110867
110928
  enums: []
@@ -110937,6 +110998,35 @@ function buildTableMetadata2(rows, isView) {
110937
110998
  }
110938
110999
  return Array.from(tableMap.values());
110939
111000
  }
111001
+ async function introspectCheckConstraints3(db, schemas) {
111002
+ const rawConstraints = await sql`
111003
+ SELECT
111004
+ s.name AS schema_name,
111005
+ t.name AS table_name,
111006
+ c.name AS column_name,
111007
+ cc.definition AS check_definition
111008
+ FROM sys.check_constraints cc
111009
+ JOIN sys.tables t ON cc.parent_object_id = t.object_id
111010
+ JOIN sys.schemas s ON t.schema_id = s.schema_id
111011
+ LEFT JOIN sys.columns c ON cc.parent_column_id = c.column_id AND c.object_id = t.object_id
111012
+ WHERE cc.is_disabled = 0
111013
+ AND cc.parent_column_id > 0
111014
+ AND s.name IN (${sql.join(schemas.map((s) => sql`${s}`))})
111015
+ `.execute(db);
111016
+ const constraintMap = new Map;
111017
+ for (const row of rawConstraints.rows) {
111018
+ if (!row.column_name)
111019
+ continue;
111020
+ const key = `${row.schema_name}.${row.table_name}.${row.column_name}`;
111021
+ if (constraintMap.has(key))
111022
+ continue;
111023
+ const parsed = parseMssqlCheckConstraint(row.check_definition);
111024
+ if (parsed) {
111025
+ constraintMap.set(key, parsed);
111026
+ }
111027
+ }
111028
+ return constraintMap;
111029
+ }
110940
111030
 
110941
111031
  // src/dialects/mssql/type-mapper.ts
110942
111032
  function mapMssqlType(dataType, options) {
@@ -111554,7 +111644,22 @@ function transformColumnToZod(column, enums, enumResolver, mode, options) {
111554
111644
  }
111555
111645
  } else if (column.checkConstraint) {
111556
111646
  if (column.checkConstraint.type === "boolean") {
111557
- schema = { kind: "zod-primitive", method: "boolean" };
111647
+ const unionSchema = {
111648
+ kind: "zod-union",
111649
+ schemas: [
111650
+ { kind: "zod-literal", value: 0 },
111651
+ { kind: "zod-literal", value: 1 }
111652
+ ]
111653
+ };
111654
+ if (options?.noBooleanCoerce) {
111655
+ schema = unionSchema;
111656
+ } else {
111657
+ schema = {
111658
+ kind: "zod-transform",
111659
+ schema: unionSchema,
111660
+ transformFn: "v => v === 1"
111661
+ };
111662
+ }
111558
111663
  } else if (column.checkConstraint.type === "string") {
111559
111664
  schema = { kind: "zod-enum", values: column.checkConstraint.values };
111560
111665
  } else {
@@ -111713,6 +111818,8 @@ function serializeZodSchema(node) {
111713
111818
  return serializeZodReference(node);
111714
111819
  case "zod-custom":
111715
111820
  return serializeZodCustom(node);
111821
+ case "zod-transform":
111822
+ return serializeZodTransform(node);
111716
111823
  }
111717
111824
  }
111718
111825
  function serializeZodPrimitive(node) {
@@ -111762,6 +111869,9 @@ function serializeZodReference(node) {
111762
111869
  function serializeZodCustom(node) {
111763
111870
  return `z.custom<${node.typeReference}>()`;
111764
111871
  }
111872
+ function serializeZodTransform(node) {
111873
+ return `${serializeZodSchema(node.schema)}.transform(${node.transformFn})`;
111874
+ }
111765
111875
  function serializeZodDeclaration(node) {
111766
111876
  switch (node.kind) {
111767
111877
  case "zod-import":
@@ -111788,7 +111898,7 @@ function serializeZod(program2) {
111788
111898
 
111789
111899
  // src/cli.ts
111790
111900
  var program2 = new Command;
111791
- program2.name("kysely-gen").description("Generate Kysely types from your database").version("0.1.0").option("-o, --out <path>", "Output file path", "./db.d.ts").option("-s, --schema <name>", "Schema to introspect (can be specified multiple times)", collect, []).option("--url <connection-string>", "Database connection string (overrides DATABASE_URL env)").option("-d, --dialect <name>", "Database dialect (postgres, mysql, sqlite). Auto-detected from URL if not specified").option("--camel-case", "Convert column and table names to camelCase (use with Kysely CamelCasePlugin)").option("--include-pattern <pattern>", "Only include tables matching glob pattern (schema.table format)", collect, []).option("--exclude-pattern <pattern>", "Exclude tables matching glob pattern (schema.table format)", collect, []).option("--print", "Output to stdout instead of writing to file").option("--verify", "Verify types match existing file (exit 1 if different)").option("--zod", "Generate Zod schemas with inferred types instead of TypeScript interfaces").action(async (options) => {
111901
+ program2.name("kysely-gen").description("Generate Kysely types from your database").version("0.1.0").option("-o, --out <path>", "Output file path", "./db.d.ts").option("-s, --schema <name>", "Schema to introspect (can be specified multiple times)", collect, []).option("--url <connection-string>", "Database connection string (overrides DATABASE_URL env)").option("-d, --dialect <name>", "Database dialect (postgres, mysql, sqlite). Auto-detected from URL if not specified").option("--camel-case", "Convert column and table names to camelCase (use with Kysely CamelCasePlugin)").option("--include-pattern <pattern>", "Only include tables matching glob pattern (schema.table format)", collect, []).option("--exclude-pattern <pattern>", "Exclude tables matching glob pattern (schema.table format)", collect, []).option("--print", "Output to stdout instead of writing to file").option("--verify", "Verify types match existing file (exit 1 if different)").option("--zod", "Generate Zod schemas with inferred types instead of TypeScript interfaces").option("--no-boolean-coerce", "Output 0 | 1 instead of coercing to boolean for CHECK(col IN (0, 1))").action(async (options) => {
111792
111902
  try {
111793
111903
  await generate(options);
111794
111904
  } catch (error2) {
@@ -111877,10 +111987,12 @@ async function generate(options) {
111877
111987
  spinner.succeed(`Found ${source_default.bold(tableCount)} tables and ${source_default.bold(enumCount)} enums`);
111878
111988
  let code;
111879
111989
  let warnings = [];
111990
+ const noBooleanCoerce = options.booleanCoerce === false;
111880
111991
  if (options.zod) {
111881
111992
  spinner.start("Generating Zod schemas...");
111882
111993
  const zodProgram = transformDatabaseToZod(metadata, {
111883
- camelCase: options.camelCase
111994
+ camelCase: options.camelCase,
111995
+ noBooleanCoerce
111884
111996
  });
111885
111997
  code = serializeZod(zodProgram);
111886
111998
  } else {
@@ -111889,7 +112001,8 @@ async function generate(options) {
111889
112001
  dialectName,
111890
112002
  camelCase: options.camelCase,
111891
112003
  includePattern: options.includePattern.length > 0 ? options.includePattern : undefined,
111892
- excludePattern: options.excludePattern.length > 0 ? options.excludePattern : undefined
112004
+ excludePattern: options.excludePattern.length > 0 ? options.excludePattern : undefined,
112005
+ noBooleanCoerce
111893
112006
  });
111894
112007
  code = serialize(astProgram);
111895
112008
  warnings = tsWarnings;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kysely-gen",
3
- "version": "0.11.0",
3
+ "version": "0.12.0",
4
4
  "description": "Database type generator for Kysely - Supports PostgreSQL, MySQL, SQLite, and MSSQL",
5
5
  "type": "module",
6
6
  "license": "MIT",