@drzl/analyzer 1.5.2 → 1.7.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/index.cjs CHANGED
@@ -31,11 +31,147 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
33
  SchemaAnalyzer: () => SchemaAnalyzer,
34
- default: () => index_default
34
+ default: () => index_default,
35
+ describeV1Column: () => describeV1Column
35
36
  });
36
37
  module.exports = __toCommonJS(index_exports);
37
38
  var import_meta = {};
38
- var SchemaAnalyzer = class {
39
+ function renderSqlLiteral(v) {
40
+ if (v === null || v === void 0) return "NULL";
41
+ if (typeof v === "number" || typeof v === "bigint") return String(v);
42
+ if (typeof v === "boolean") return v ? "TRUE" : "FALSE";
43
+ if (v instanceof Date) return `'${v.toISOString()}'`;
44
+ if (Array.isArray(v)) return `(${v.map(renderSqlLiteral).join(", ")})`;
45
+ return `'${String(v).replace(/'/g, "''")}'`;
46
+ }
47
+ var V1_FLOAT_BOUNDS = {
48
+ float: ["-8388608", "8388607"],
49
+ // real / float4, 2^23
50
+ double: ["-140737488355328", "140737488355327"]
51
+ // double precision / float8, 2^47
52
+ };
53
+ function describeV1Column(column) {
54
+ const codec = column?.codec;
55
+ const dataType = column?.dataType;
56
+ if (typeof codec !== "string" || typeof dataType !== "string") return null;
57
+ const [js, semantic = ""] = dataType.split(" ");
58
+ const out = {};
59
+ switch (semantic) {
60
+ case "int16":
61
+ case "int32":
62
+ case "int53":
63
+ case "int64": {
64
+ const range = {
65
+ int16: ["-32768", "32767"],
66
+ int32: ["-2147483648", "2147483647"],
67
+ int53: ["-9007199254740991", "9007199254740991"],
68
+ int64: ["-9223372036854775808", "9223372036854775807"]
69
+ }[semantic];
70
+ [out.min, out.max] = range;
71
+ out.integer = true;
72
+ out.tsType = js === "bigint" ? "bigint" : "number";
73
+ out.dbType = semantic === "int16" ? "SMALLINT" : semantic === "int32" ? "INTEGER" : "BIGINT";
74
+ break;
75
+ }
76
+ case "float":
77
+ case "double": {
78
+ [out.min, out.max] = V1_FLOAT_BOUNDS[semantic];
79
+ out.integer = false;
80
+ out.tsType = "number";
81
+ out.dbType = semantic === "float" ? "REAL" : "DOUBLE";
82
+ break;
83
+ }
84
+ case "uuid":
85
+ out.tsType = "string";
86
+ out.dbType = "UUID";
87
+ out.format = "uuid";
88
+ break;
89
+ case "numeric":
90
+ out.tsType = "string";
91
+ out.dbType = "NUMERIC";
92
+ break;
93
+ case "json":
94
+ out.tsType = "any";
95
+ out.dbType = codec === "jsonb" ? "JSONB" : "JSON";
96
+ out.shape = { kind: "json" };
97
+ break;
98
+ case "buffer":
99
+ out.tsType = "Buffer";
100
+ out.dbType = "BYTEA";
101
+ out.shape = { kind: "buffer" };
102
+ break;
103
+ case "date":
104
+ out.tsType = js === "string" ? "string" : "Date";
105
+ out.dbType = codec.startsWith("timestamp") ? "TIMESTAMP" : "DATE";
106
+ break;
107
+ case "timestamp":
108
+ out.tsType = js === "string" ? "string" : "Date";
109
+ out.dbType = "TIMESTAMP";
110
+ break;
111
+ case "time":
112
+ out.tsType = "string";
113
+ out.dbType = "TIME";
114
+ break;
115
+ case "interval":
116
+ out.tsType = "string";
117
+ out.dbType = "INTERVAL";
118
+ break;
119
+ case "inet":
120
+ case "cidr":
121
+ case "macaddr":
122
+ out.tsType = "string";
123
+ out.dbType = semantic.toUpperCase();
124
+ break;
125
+ case "binary":
126
+ out.tsType = "string";
127
+ out.dbType = "BIT";
128
+ out.shape = { kind: "bitstring", length: declaredLength(column) };
129
+ break;
130
+ case "point":
131
+ case "geometry":
132
+ out.tsType = "[number, number]";
133
+ out.dbType = semantic.toUpperCase();
134
+ out.shape = { kind: "tuple", length: 2 };
135
+ break;
136
+ case "line":
137
+ out.tsType = "[number, number, number]";
138
+ out.dbType = "LINE";
139
+ out.shape = { kind: "tuple", length: 3 };
140
+ break;
141
+ case "vector":
142
+ out.tsType = "number[]";
143
+ out.dbType = "VECTOR";
144
+ out.shape = { kind: "numberVector", length: declaredLength(column) };
145
+ break;
146
+ case "enum":
147
+ out.tsType = "string";
148
+ out.dbType = "TEXT";
149
+ break;
150
+ default:
151
+ if (js === "boolean") {
152
+ out.tsType = "boolean";
153
+ out.dbType = "BOOLEAN";
154
+ } else if (js === "number") {
155
+ out.tsType = "number";
156
+ out.dbType = "NUMERIC";
157
+ out.integer = false;
158
+ [out.min, out.max] = ["-9007199254740991", "9007199254740991"];
159
+ } else if (js === "string") {
160
+ out.tsType = "string";
161
+ out.dbType = codec === "varchar" ? "VARCHAR" : codec === "char" ? "CHAR" : "TEXT";
162
+ } else {
163
+ return null;
164
+ }
165
+ }
166
+ const dims = column?.dimensions;
167
+ if (typeof dims === "number" && dims >= 1) out.arrayDimensions = dims;
168
+ return out;
169
+ }
170
+ function declaredLength(column) {
171
+ const n = column?.length ?? column?.config?.length ?? column?.config?.dimensions;
172
+ return typeof n === "number" && Number.isFinite(n) && n > 0 ? n : void 0;
173
+ }
174
+ var _SchemaAnalyzer = class _SchemaAnalyzer {
39
175
  constructor(schemaPath) {
40
176
  this.schemaPath = schemaPath;
41
177
  }
@@ -172,6 +308,12 @@ var SchemaAnalyzer = class {
172
308
  if (Array.isArray(c?.value)) return c.value.join("");
173
309
  if (typeof c?.name === "string") return toTs(c.name);
174
310
  if (c?.queryChunks) return this.renderSql(c, toTs);
311
+ if (c === null || ["number", "string", "boolean", "bigint"].includes(typeof c)) {
312
+ return renderSqlLiteral(c);
313
+ }
314
+ if (typeof c === "object" && "value" in c) {
315
+ return renderSqlLiteral(c.value);
316
+ }
175
317
  return "?";
176
318
  }).join("").trim();
177
319
  }
@@ -243,6 +385,27 @@ var SchemaAnalyzer = class {
243
385
  }
244
386
  return out;
245
387
  }
388
+ /**
389
+ * Constraints the column definition already carries, which the analysis used to throw away.
390
+ *
391
+ * Everything here is read off Drizzle's own column instance, so it states what the schema
392
+ * states. Nothing is inferred from a name or guessed from a type.
393
+ */
394
+ columnConstraints(column) {
395
+ const ctor = column?.constructor?.name ?? "";
396
+ const out = {};
397
+ const length = column?.length ?? column?.config?.length;
398
+ if (typeof length === "number" && Number.isFinite(length) && length > 0) {
399
+ out.maxLength = length;
400
+ }
401
+ const range = _SchemaAnalyzer.INT_RANGES[ctor];
402
+ if (range) {
403
+ [out.min, out.max] = range;
404
+ out.integer = true;
405
+ }
406
+ if (/^(Pg)?UUID$/i.test(ctor) || /Uuid$/i.test(ctor)) out.format = "uuid";
407
+ return out;
408
+ }
246
409
  mapColumnType(column) {
247
410
  const ctor = column?.constructor?.name ?? "";
248
411
  switch (ctor) {
@@ -264,6 +427,14 @@ var SchemaAnalyzer = class {
264
427
  case "PgInteger":
265
428
  case "PgSmallInt":
266
429
  return { tsType: "number", dbType: "INTEGER" };
430
+ // Drizzle names these by their mode: `PgBigInt53` for `{ mode: 'number' }` and
431
+ // `PgBigInt64` for `{ mode: 'bigint' }`. `PgBigInt` matched neither, so both fell through
432
+ // to the regex arm and came back as `bigint`, which is wrong for the number mode: the
433
+ // value really is a JS number there, and a schema demanding a bigint rejects every row.
434
+ case "PgBigInt53":
435
+ return { tsType: "number", dbType: "BIGINT" };
436
+ case "PgBigInt64":
437
+ return { tsType: "bigint", dbType: "BIGINT" };
267
438
  case "PgBigInt":
268
439
  return {
269
440
  tsType: column?.config?.mode === "number" ? "number" : "bigint",
@@ -277,6 +448,9 @@ var SchemaAnalyzer = class {
277
448
  case "PgVarchar":
278
449
  case "PgChar":
279
450
  return { tsType: "string", dbType: "TEXT" };
451
+ // Drizzle spells it `PgUUID`. `PgUuid` matched nothing, so every uuid column fell through
452
+ // to the regex arm below and came back as plain TEXT, losing the format.
453
+ case "PgUUID":
280
454
  case "PgUuid":
281
455
  return { tsType: "string", dbType: "UUID" };
282
456
  case "PgBoolean":
@@ -301,7 +475,8 @@ var SchemaAnalyzer = class {
301
475
  if (/Text|Varchar|Char|Uuid/i.test(ctor)) return { tsType: "string", dbType: "TEXT" };
302
476
  if (/Inet|Cidr|Macaddr8?|Uuid/i.test(ctor)) return { tsType: "string", dbType: "TEXT" };
303
477
  if (/Point|Line/i.test(ctor)) return { tsType: "string", dbType: "TEXT" };
304
- if (/TimestampString|DateString/i.test(ctor)) return { tsType: "string", dbType: "TIMESTAMP" };
478
+ if (/TimestampString|DateString/i.test(ctor))
479
+ return { tsType: "string", dbType: "TIMESTAMP" };
305
480
  if (/Timestamptz/i.test(ctor)) return { tsType: "Date", dbType: "TIMESTAMPTZ" };
306
481
  if (/Timestamp/i.test(ctor)) return { tsType: "Date", dbType: "TIMESTAMP" };
307
482
  if (/Date/i.test(ctor)) return { tsType: "Date", dbType: "TIMESTAMP" };
@@ -310,8 +485,10 @@ var SchemaAnalyzer = class {
310
485
  if (/\bInt(eger)?\b|Serial/i.test(ctor)) return { tsType: "number", dbType: "INTEGER" };
311
486
  if (/BigInt/i.test(ctor)) return { tsType: "bigint", dbType: "BIGINT" };
312
487
  if (/Bool/i.test(ctor)) return { tsType: "boolean", dbType: "BOOLEAN" };
313
- if (/Jsonb?/i.test(ctor)) return { tsType: "any", dbType: /Jsonb/i.test(ctor) ? "JSONB" : "JSON" };
314
- if (/Numeric|Float|Double|Real/i.test(ctor)) return { tsType: "number", dbType: "NUMERIC" };
488
+ if (/Jsonb?/i.test(ctor))
489
+ return { tsType: "any", dbType: /Jsonb/i.test(ctor) ? "JSONB" : "JSON" };
490
+ if (/Numeric|Float|Double|Real/i.test(ctor))
491
+ return { tsType: "number", dbType: "NUMERIC" };
315
492
  }
316
493
  if (/^MySql/i.test(ctor)) {
317
494
  if (/BigInt64/i.test(ctor)) return { tsType: "bigint", dbType: "BIGINT" };
@@ -363,7 +540,8 @@ var SchemaAnalyzer = class {
363
540
  if (/TimestampTz/i.test(ctor)) return { tsType: "Date", dbType: "TIMESTAMPTZ" };
364
541
  if (/Timestamp/i.test(ctor)) return { tsType: "string", dbType: "TIMESTAMP" };
365
542
  if (/LocalDateString|LocalTime/i.test(ctor)) return { tsType: "string", dbType: "TEXT" };
366
- if (/DateDuration|RelDuration|Duration/i.test(ctor)) return { tsType: "string", dbType: "TEXT" };
543
+ if (/DateDuration|RelDuration|Duration/i.test(ctor))
544
+ return { tsType: "string", dbType: "TEXT" };
367
545
  }
368
546
  return { tsType: "unknown", dbType: "UNKNOWN" };
369
547
  }
@@ -398,6 +576,9 @@ var SchemaAnalyzer = class {
398
576
  arr.push(colName);
399
577
  uniqueGroups.set(uName, arr);
400
578
  }
579
+ const v1 = describeV1Column(col);
580
+ const constraints = this.columnConstraints(col);
581
+ if (v1?.shape) delete constraints.maxLength;
401
582
  columns.push({
402
583
  name: colName,
403
584
  tsType,
@@ -407,7 +588,9 @@ var SchemaAnalyzer = class {
407
588
  isGenerated,
408
589
  defaultExpression: void 0,
409
590
  references,
410
- enumValues: Array.isArray(ev) ? ev : void 0
591
+ enumValues: Array.isArray(ev) ? ev : void 0,
592
+ ...constraints,
593
+ ...v1 ?? {}
411
594
  });
412
595
  }
413
596
  const name = this.getSymbol(tbl, "drizzle:Name") || tsName;
@@ -568,27 +751,32 @@ var SchemaAnalyzer = class {
568
751
  enums.push(candidate);
569
752
  }
570
753
  let dialect = "unknown";
571
- const ctorNames = /* @__PURE__ */ new Set();
754
+ const marks = /* @__PURE__ */ new Set();
572
755
  for (const [_, val] of Object.entries(exportsObj)) {
573
756
  const cols = val?.[/* @__PURE__ */ Symbol.for("drizzle:Columns")];
574
- if (cols) {
575
- for (const c of Object.values(cols)) {
576
- const n = c?.constructor?.name;
577
- if (n) ctorNames.add(n);
578
- }
757
+ if (!cols) continue;
758
+ for (const c of Object.values(cols)) {
759
+ const kind = c?.constructor?.[/* @__PURE__ */ Symbol.for("drizzle:entityKind")];
760
+ if (typeof kind === "string") marks.add(kind);
761
+ const n = c?.constructor?.name;
762
+ if (n) marks.add(n);
579
763
  }
580
764
  }
581
- const names = Array.from(ctorNames).join(",");
765
+ const names = Array.from(marks).join(",");
582
766
  if (/SQLite/i.test(names)) dialect = "sqlite";
583
- else if (/Pg|Postgres/i.test(names)) dialect = "postgres";
584
- else if (/MySql|Mysql/i.test(names)) dialect = "mysql";
585
767
  else if (/SingleStore/i.test(names)) dialect = "singlestore";
768
+ else if (/Cockroach/i.test(names)) dialect = "cockroach";
769
+ else if (/MsSql/i.test(names)) dialect = "mssql";
770
+ else if (/MySql|Mysql/i.test(names)) dialect = "mysql";
771
+ else if (/Pg|Postgres/i.test(names)) dialect = "postgres";
586
772
  else if (/Gel/i.test(names)) dialect = "gel";
587
- if (dialect === "unknown") {
588
- const looksSqlite = tables.some(
589
- (t) => t.columns.some((c) => ["INTEGER", "TEXT", "REAL", "BLOB", "NUMERIC"].includes(c.dbType))
590
- );
591
- if (looksSqlite) dialect = "sqlite";
773
+ if (dialect === "unknown" && tables.length) {
774
+ issues.push({
775
+ code: "DRZL_ANL_DIALECT",
776
+ level: "warn",
777
+ message: `Could not identify the Drizzle dialect for this schema${names ? `; saw column kinds: ${Array.from(marks).slice(0, 6).join(", ")}` : ""}.`,
778
+ hint: "Column types will fall back to their coarse defaults. If this is a dialect DRZL does not know yet, please open an issue."
779
+ });
592
780
  }
593
781
  if (opts.includeRelations) {
594
782
  relations.push(...this.inferManyToMany(tables));
@@ -628,8 +816,49 @@ var SchemaAnalyzer = class {
628
816
  };
629
817
  }
630
818
  };
819
+ /**
820
+ * The range an integer column can actually hold, keyed off the Drizzle column class.
821
+ *
822
+ * Matches what `drizzle-orm/zod` emits, measured at 1.0.0-rc.4. The two bigint modes differ on
823
+ * purpose: in `{ mode: 'number' }` the value arrives as a JS number, so the real ceiling is
824
+ * `Number.MAX_SAFE_INTEGER` rather than the column's, and bounding at the column's would
825
+ * promise a precision that cannot survive the round trip.
826
+ */
827
+ _SchemaAnalyzer.INT_RANGES = {
828
+ // 8 bit
829
+ MySqlTinyInt: ["-128", "127"],
830
+ SQLiteInteger: ["-9223372036854775808", "9223372036854775807"],
831
+ // 16 bit
832
+ PgSmallInt: ["-32768", "32767"],
833
+ // A serial is an ordinary integer column that happens to default from a sequence. The
834
+ // sequence starts at 1, the column does not: `INSERT ... (id) VALUES (-5)` is accepted by
835
+ // Postgres and is how backfills and sentinel rows get written. Lower-bounding these at 1
836
+ // rejected valid rows, and `drizzle-orm/zod` bounds them by the integer width too.
837
+ PgSmallSerial: ["-32768", "32767"],
838
+ MySqlSmallInt: ["-32768", "32767"],
839
+ SingleStoreSmallInt: ["-32768", "32767"],
840
+ // 24 bit
841
+ MySqlMediumInt: ["-8388608", "8388607"],
842
+ // 32 bit
843
+ PgInteger: ["-2147483648", "2147483647"],
844
+ PgSerial: ["-2147483648", "2147483647"],
845
+ MySqlInt: ["-2147483648", "2147483647"],
846
+ SingleStoreInt: ["-2147483648", "2147483647"],
847
+ // 53 bit, the JS safe-integer ceiling rather than the column's
848
+ PgBigInt53: ["-9007199254740991", "9007199254740991"],
849
+ PgBigSerial53: ["-9007199254740991", "9007199254740991"],
850
+ MySqlBigInt53: ["-9007199254740991", "9007199254740991"],
851
+ SingleStoreBigInt53: ["-9007199254740991", "9007199254740991"],
852
+ // 64 bit, representable because the value is a bigint
853
+ PgBigInt64: ["-9223372036854775808", "9223372036854775807"],
854
+ PgBigSerial64: ["-9223372036854775808", "9223372036854775807"],
855
+ MySqlBigInt64: ["-9223372036854775808", "9223372036854775807"],
856
+ SingleStoreBigInt64: ["-9223372036854775808", "9223372036854775807"]
857
+ };
858
+ var SchemaAnalyzer = _SchemaAnalyzer;
631
859
  var index_default = SchemaAnalyzer;
632
860
  // Annotate the CommonJS export names for ESM import in node:
633
861
  0 && (module.exports = {
634
- SchemaAnalyzer
862
+ SchemaAnalyzer,
863
+ describeV1Column
635
864
  });
package/dist/index.d.cts CHANGED
@@ -1,4 +1,8 @@
1
- type Dialect = 'sqlite' | 'postgres' | 'mysql' | 'singlestore' | 'gel' | 'unknown';
1
+ /**
2
+ * `mssql` and `cockroach` arrived with Drizzle v1. `gel` was removed in the same release, but
3
+ * stays here so an analysis of a 0.4x schema still names what it found.
4
+ */
5
+ type Dialect = 'sqlite' | 'postgres' | 'mysql' | 'singlestore' | 'mssql' | 'cockroach' | 'gel' | 'unknown';
2
6
  interface Issue {
3
7
  code: string;
4
8
  level: 'info' | 'warn' | 'error';
@@ -31,7 +35,80 @@ interface Column {
31
35
  onUpdate?: string;
32
36
  };
33
37
  enumValues?: string[];
38
+ /**
39
+ * Declared character limit, from `varchar('x', { length: 255 })` and friends.
40
+ *
41
+ * The column has always known this and the analysis discarded it, so generated schemas
42
+ * accepted a 300 character value that the database then rejected. Absent where the type has
43
+ * no limit, since claiming one would invent a constraint the schema never stated.
44
+ */
45
+ maxLength?: number;
46
+ /**
47
+ * Inclusive bounds for an integer column, as decimal strings.
48
+ *
49
+ * Strings rather than numbers because a 64 bit bound is not representable as a JS number:
50
+ * `9223372036854775807` rounds to `9223372036854775808` the moment it becomes one, so a
51
+ * numeric field here would silently emit a wrong bound. Absent for floats and for `numeric`,
52
+ * which have no integer range.
53
+ */
54
+ min?: string;
55
+ max?: string;
56
+ /**
57
+ * Whether a numeric column holds whole numbers only.
58
+ *
59
+ * Stated rather than inferred. Generators used to read "has both bounds" as "is an integer",
60
+ * which held only while integers were the sole bounded type; bounding `real` and `double
61
+ * precision` promptly made every float schema reject `1.5`. Absent on a pre-1.7 analysis, and
62
+ * generators fall back to the old inference there.
63
+ */
64
+ integer?: boolean;
65
+ /** A string column with a known shape, currently only `uuid`. */
66
+ format?: 'uuid';
67
+ /**
68
+ * Array depth for a column declared with `.array()`, absent when the column is a scalar.
69
+ *
70
+ * Drizzle does not give an array its own column class: `text().array()` is still a `PgText`,
71
+ * distinguished only by `dimensions`. Reading the class alone therefore produced a schema for
72
+ * the *element*, which rejected every row the database returned and accepted a bare string in
73
+ * its place.
74
+ *
75
+ * `.array(3)` sets a size rather than a dimension and Drizzle itself treats the result as a
76
+ * scalar, so it is deliberately not an array here either.
77
+ */
78
+ arrayDimensions?: number;
79
+ /**
80
+ * A structured value that cannot be expressed as a scalar plus constraints.
81
+ *
82
+ * These all used to fall through to `any`/`unknown` or, worse, to `string`. A `point` really
83
+ * arrives as `[number, number]`, so a string schema rejected every row; a `bytea` typed as
84
+ * `unknown` accepted `null` on a NOT NULL column.
85
+ */
86
+ shape?: ColumnShape;
87
+ }
88
+ type ColumnShape =
89
+ /** `bytea`, `blob`: a binary payload, carried as a Buffer/Uint8Array. */
90
+ {
91
+ kind: 'buffer';
92
+ }
93
+ /** `json`, `jsonb`: any value that survives a JSON round trip, checked recursively. */
94
+ | {
95
+ kind: 'json';
34
96
  }
97
+ /** `point`, `line`, `geometry`: a fixed-length tuple of numbers. */
98
+ | {
99
+ kind: 'tuple';
100
+ length: number;
101
+ }
102
+ /** `vector`, `halfvec`: a numeric vector, with a fixed length where one is declared. */
103
+ | {
104
+ kind: 'numberVector';
105
+ length?: number;
106
+ }
107
+ /** `bit`: a string of `0`/`1`, with a fixed length where one is declared. */
108
+ | {
109
+ kind: 'bitstring';
110
+ length?: number;
111
+ };
35
112
  interface Key {
36
113
  name?: string;
37
114
  columns: string[];
@@ -88,6 +165,20 @@ interface AnalyzeOptions {
88
165
  validateConstraints?: boolean;
89
166
  includeHeuristicRelations?: boolean;
90
167
  }
168
+ /**
169
+ * Everything Drizzle v1 states about a column outright, or `null` on an older Drizzle.
170
+ *
171
+ * v1 stamps each column with a `dataType` of the form `"<js type> <semantic>"` (`"number
172
+ * int32"`, `"object buffer"`, `"array point"`) alongside a `codec` naming the SQL side. That
173
+ * is a far better key than the constructor name the analyzer used to match on: the class list
174
+ * ran to dozens of names per dialect, drifted between releases, and a miss fell through to a
175
+ * regex that guessed from the name. `PgBinaryVector`, for one, is a bit string and not a
176
+ * vector at all.
177
+ *
178
+ * Gated on `codec`, which 0.4x columns do not carry, so an older schema keeps the class-name
179
+ * path below untouched.
180
+ */
181
+ declare function describeV1Column(column: any): Partial<Column> | null;
91
182
  declare class SchemaAnalyzer {
92
183
  private readonly schemaPath;
93
184
  constructor(schemaPath: string);
@@ -171,9 +262,25 @@ declare class SchemaAnalyzer {
171
262
  * it a join table would invent a relation the author never declared.
172
263
  */
173
264
  private inferManyToMany;
265
+ /**
266
+ * The range an integer column can actually hold, keyed off the Drizzle column class.
267
+ *
268
+ * Matches what `drizzle-orm/zod` emits, measured at 1.0.0-rc.4. The two bigint modes differ on
269
+ * purpose: in `{ mode: 'number' }` the value arrives as a JS number, so the real ceiling is
270
+ * `Number.MAX_SAFE_INTEGER` rather than the column's, and bounding at the column's would
271
+ * promise a precision that cannot survive the round trip.
272
+ */
273
+ private static readonly INT_RANGES;
274
+ /**
275
+ * Constraints the column definition already carries, which the analysis used to throw away.
276
+ *
277
+ * Everything here is read off Drizzle's own column instance, so it states what the schema
278
+ * states. Nothing is inferred from a name or guessed from a type.
279
+ */
280
+ private columnConstraints;
174
281
  private mapColumnType;
175
282
  private analyzeTable;
176
283
  analyze(opts?: AnalyzeOptions): Promise<Analysis>;
177
284
  }
178
285
 
179
- export { type Analysis, type AnalyzeOptions, type Check, type Column, type ColumnRef, type Dialect, type Enum, type ForeignKey, type Index, type Issue, type Key, type Relation, SchemaAnalyzer, type Table, SchemaAnalyzer as default };
286
+ export { type Analysis, type AnalyzeOptions, type Check, type Column, type ColumnRef, type ColumnShape, type Dialect, type Enum, type ForeignKey, type Index, type Issue, type Key, type Relation, SchemaAnalyzer, type Table, SchemaAnalyzer as default, describeV1Column };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,8 @@
1
- type Dialect = 'sqlite' | 'postgres' | 'mysql' | 'singlestore' | 'gel' | 'unknown';
1
+ /**
2
+ * `mssql` and `cockroach` arrived with Drizzle v1. `gel` was removed in the same release, but
3
+ * stays here so an analysis of a 0.4x schema still names what it found.
4
+ */
5
+ type Dialect = 'sqlite' | 'postgres' | 'mysql' | 'singlestore' | 'mssql' | 'cockroach' | 'gel' | 'unknown';
2
6
  interface Issue {
3
7
  code: string;
4
8
  level: 'info' | 'warn' | 'error';
@@ -31,7 +35,80 @@ interface Column {
31
35
  onUpdate?: string;
32
36
  };
33
37
  enumValues?: string[];
38
+ /**
39
+ * Declared character limit, from `varchar('x', { length: 255 })` and friends.
40
+ *
41
+ * The column has always known this and the analysis discarded it, so generated schemas
42
+ * accepted a 300 character value that the database then rejected. Absent where the type has
43
+ * no limit, since claiming one would invent a constraint the schema never stated.
44
+ */
45
+ maxLength?: number;
46
+ /**
47
+ * Inclusive bounds for an integer column, as decimal strings.
48
+ *
49
+ * Strings rather than numbers because a 64 bit bound is not representable as a JS number:
50
+ * `9223372036854775807` rounds to `9223372036854775808` the moment it becomes one, so a
51
+ * numeric field here would silently emit a wrong bound. Absent for floats and for `numeric`,
52
+ * which have no integer range.
53
+ */
54
+ min?: string;
55
+ max?: string;
56
+ /**
57
+ * Whether a numeric column holds whole numbers only.
58
+ *
59
+ * Stated rather than inferred. Generators used to read "has both bounds" as "is an integer",
60
+ * which held only while integers were the sole bounded type; bounding `real` and `double
61
+ * precision` promptly made every float schema reject `1.5`. Absent on a pre-1.7 analysis, and
62
+ * generators fall back to the old inference there.
63
+ */
64
+ integer?: boolean;
65
+ /** A string column with a known shape, currently only `uuid`. */
66
+ format?: 'uuid';
67
+ /**
68
+ * Array depth for a column declared with `.array()`, absent when the column is a scalar.
69
+ *
70
+ * Drizzle does not give an array its own column class: `text().array()` is still a `PgText`,
71
+ * distinguished only by `dimensions`. Reading the class alone therefore produced a schema for
72
+ * the *element*, which rejected every row the database returned and accepted a bare string in
73
+ * its place.
74
+ *
75
+ * `.array(3)` sets a size rather than a dimension and Drizzle itself treats the result as a
76
+ * scalar, so it is deliberately not an array here either.
77
+ */
78
+ arrayDimensions?: number;
79
+ /**
80
+ * A structured value that cannot be expressed as a scalar plus constraints.
81
+ *
82
+ * These all used to fall through to `any`/`unknown` or, worse, to `string`. A `point` really
83
+ * arrives as `[number, number]`, so a string schema rejected every row; a `bytea` typed as
84
+ * `unknown` accepted `null` on a NOT NULL column.
85
+ */
86
+ shape?: ColumnShape;
87
+ }
88
+ type ColumnShape =
89
+ /** `bytea`, `blob`: a binary payload, carried as a Buffer/Uint8Array. */
90
+ {
91
+ kind: 'buffer';
92
+ }
93
+ /** `json`, `jsonb`: any value that survives a JSON round trip, checked recursively. */
94
+ | {
95
+ kind: 'json';
34
96
  }
97
+ /** `point`, `line`, `geometry`: a fixed-length tuple of numbers. */
98
+ | {
99
+ kind: 'tuple';
100
+ length: number;
101
+ }
102
+ /** `vector`, `halfvec`: a numeric vector, with a fixed length where one is declared. */
103
+ | {
104
+ kind: 'numberVector';
105
+ length?: number;
106
+ }
107
+ /** `bit`: a string of `0`/`1`, with a fixed length where one is declared. */
108
+ | {
109
+ kind: 'bitstring';
110
+ length?: number;
111
+ };
35
112
  interface Key {
36
113
  name?: string;
37
114
  columns: string[];
@@ -88,6 +165,20 @@ interface AnalyzeOptions {
88
165
  validateConstraints?: boolean;
89
166
  includeHeuristicRelations?: boolean;
90
167
  }
168
+ /**
169
+ * Everything Drizzle v1 states about a column outright, or `null` on an older Drizzle.
170
+ *
171
+ * v1 stamps each column with a `dataType` of the form `"<js type> <semantic>"` (`"number
172
+ * int32"`, `"object buffer"`, `"array point"`) alongside a `codec` naming the SQL side. That
173
+ * is a far better key than the constructor name the analyzer used to match on: the class list
174
+ * ran to dozens of names per dialect, drifted between releases, and a miss fell through to a
175
+ * regex that guessed from the name. `PgBinaryVector`, for one, is a bit string and not a
176
+ * vector at all.
177
+ *
178
+ * Gated on `codec`, which 0.4x columns do not carry, so an older schema keeps the class-name
179
+ * path below untouched.
180
+ */
181
+ declare function describeV1Column(column: any): Partial<Column> | null;
91
182
  declare class SchemaAnalyzer {
92
183
  private readonly schemaPath;
93
184
  constructor(schemaPath: string);
@@ -171,9 +262,25 @@ declare class SchemaAnalyzer {
171
262
  * it a join table would invent a relation the author never declared.
172
263
  */
173
264
  private inferManyToMany;
265
+ /**
266
+ * The range an integer column can actually hold, keyed off the Drizzle column class.
267
+ *
268
+ * Matches what `drizzle-orm/zod` emits, measured at 1.0.0-rc.4. The two bigint modes differ on
269
+ * purpose: in `{ mode: 'number' }` the value arrives as a JS number, so the real ceiling is
270
+ * `Number.MAX_SAFE_INTEGER` rather than the column's, and bounding at the column's would
271
+ * promise a precision that cannot survive the round trip.
272
+ */
273
+ private static readonly INT_RANGES;
274
+ /**
275
+ * Constraints the column definition already carries, which the analysis used to throw away.
276
+ *
277
+ * Everything here is read off Drizzle's own column instance, so it states what the schema
278
+ * states. Nothing is inferred from a name or guessed from a type.
279
+ */
280
+ private columnConstraints;
174
281
  private mapColumnType;
175
282
  private analyzeTable;
176
283
  analyze(opts?: AnalyzeOptions): Promise<Analysis>;
177
284
  }
178
285
 
179
- export { type Analysis, type AnalyzeOptions, type Check, type Column, type ColumnRef, type Dialect, type Enum, type ForeignKey, type Index, type Issue, type Key, type Relation, SchemaAnalyzer, type Table, SchemaAnalyzer as default };
286
+ export { type Analysis, type AnalyzeOptions, type Check, type Column, type ColumnRef, type ColumnShape, type Dialect, type Enum, type ForeignKey, type Index, type Issue, type Key, type Relation, SchemaAnalyzer, type Table, SchemaAnalyzer as default, describeV1Column };
package/dist/index.js CHANGED
@@ -1,5 +1,140 @@
1
1
  // src/index.ts
2
- var SchemaAnalyzer = class {
2
+ function renderSqlLiteral(v) {
3
+ if (v === null || v === void 0) return "NULL";
4
+ if (typeof v === "number" || typeof v === "bigint") return String(v);
5
+ if (typeof v === "boolean") return v ? "TRUE" : "FALSE";
6
+ if (v instanceof Date) return `'${v.toISOString()}'`;
7
+ if (Array.isArray(v)) return `(${v.map(renderSqlLiteral).join(", ")})`;
8
+ return `'${String(v).replace(/'/g, "''")}'`;
9
+ }
10
+ var V1_FLOAT_BOUNDS = {
11
+ float: ["-8388608", "8388607"],
12
+ // real / float4, 2^23
13
+ double: ["-140737488355328", "140737488355327"]
14
+ // double precision / float8, 2^47
15
+ };
16
+ function describeV1Column(column) {
17
+ const codec = column?.codec;
18
+ const dataType = column?.dataType;
19
+ if (typeof codec !== "string" || typeof dataType !== "string") return null;
20
+ const [js, semantic = ""] = dataType.split(" ");
21
+ const out = {};
22
+ switch (semantic) {
23
+ case "int16":
24
+ case "int32":
25
+ case "int53":
26
+ case "int64": {
27
+ const range = {
28
+ int16: ["-32768", "32767"],
29
+ int32: ["-2147483648", "2147483647"],
30
+ int53: ["-9007199254740991", "9007199254740991"],
31
+ int64: ["-9223372036854775808", "9223372036854775807"]
32
+ }[semantic];
33
+ [out.min, out.max] = range;
34
+ out.integer = true;
35
+ out.tsType = js === "bigint" ? "bigint" : "number";
36
+ out.dbType = semantic === "int16" ? "SMALLINT" : semantic === "int32" ? "INTEGER" : "BIGINT";
37
+ break;
38
+ }
39
+ case "float":
40
+ case "double": {
41
+ [out.min, out.max] = V1_FLOAT_BOUNDS[semantic];
42
+ out.integer = false;
43
+ out.tsType = "number";
44
+ out.dbType = semantic === "float" ? "REAL" : "DOUBLE";
45
+ break;
46
+ }
47
+ case "uuid":
48
+ out.tsType = "string";
49
+ out.dbType = "UUID";
50
+ out.format = "uuid";
51
+ break;
52
+ case "numeric":
53
+ out.tsType = "string";
54
+ out.dbType = "NUMERIC";
55
+ break;
56
+ case "json":
57
+ out.tsType = "any";
58
+ out.dbType = codec === "jsonb" ? "JSONB" : "JSON";
59
+ out.shape = { kind: "json" };
60
+ break;
61
+ case "buffer":
62
+ out.tsType = "Buffer";
63
+ out.dbType = "BYTEA";
64
+ out.shape = { kind: "buffer" };
65
+ break;
66
+ case "date":
67
+ out.tsType = js === "string" ? "string" : "Date";
68
+ out.dbType = codec.startsWith("timestamp") ? "TIMESTAMP" : "DATE";
69
+ break;
70
+ case "timestamp":
71
+ out.tsType = js === "string" ? "string" : "Date";
72
+ out.dbType = "TIMESTAMP";
73
+ break;
74
+ case "time":
75
+ out.tsType = "string";
76
+ out.dbType = "TIME";
77
+ break;
78
+ case "interval":
79
+ out.tsType = "string";
80
+ out.dbType = "INTERVAL";
81
+ break;
82
+ case "inet":
83
+ case "cidr":
84
+ case "macaddr":
85
+ out.tsType = "string";
86
+ out.dbType = semantic.toUpperCase();
87
+ break;
88
+ case "binary":
89
+ out.tsType = "string";
90
+ out.dbType = "BIT";
91
+ out.shape = { kind: "bitstring", length: declaredLength(column) };
92
+ break;
93
+ case "point":
94
+ case "geometry":
95
+ out.tsType = "[number, number]";
96
+ out.dbType = semantic.toUpperCase();
97
+ out.shape = { kind: "tuple", length: 2 };
98
+ break;
99
+ case "line":
100
+ out.tsType = "[number, number, number]";
101
+ out.dbType = "LINE";
102
+ out.shape = { kind: "tuple", length: 3 };
103
+ break;
104
+ case "vector":
105
+ out.tsType = "number[]";
106
+ out.dbType = "VECTOR";
107
+ out.shape = { kind: "numberVector", length: declaredLength(column) };
108
+ break;
109
+ case "enum":
110
+ out.tsType = "string";
111
+ out.dbType = "TEXT";
112
+ break;
113
+ default:
114
+ if (js === "boolean") {
115
+ out.tsType = "boolean";
116
+ out.dbType = "BOOLEAN";
117
+ } else if (js === "number") {
118
+ out.tsType = "number";
119
+ out.dbType = "NUMERIC";
120
+ out.integer = false;
121
+ [out.min, out.max] = ["-9007199254740991", "9007199254740991"];
122
+ } else if (js === "string") {
123
+ out.tsType = "string";
124
+ out.dbType = codec === "varchar" ? "VARCHAR" : codec === "char" ? "CHAR" : "TEXT";
125
+ } else {
126
+ return null;
127
+ }
128
+ }
129
+ const dims = column?.dimensions;
130
+ if (typeof dims === "number" && dims >= 1) out.arrayDimensions = dims;
131
+ return out;
132
+ }
133
+ function declaredLength(column) {
134
+ const n = column?.length ?? column?.config?.length ?? column?.config?.dimensions;
135
+ return typeof n === "number" && Number.isFinite(n) && n > 0 ? n : void 0;
136
+ }
137
+ var _SchemaAnalyzer = class _SchemaAnalyzer {
3
138
  constructor(schemaPath) {
4
139
  this.schemaPath = schemaPath;
5
140
  }
@@ -136,6 +271,12 @@ var SchemaAnalyzer = class {
136
271
  if (Array.isArray(c?.value)) return c.value.join("");
137
272
  if (typeof c?.name === "string") return toTs(c.name);
138
273
  if (c?.queryChunks) return this.renderSql(c, toTs);
274
+ if (c === null || ["number", "string", "boolean", "bigint"].includes(typeof c)) {
275
+ return renderSqlLiteral(c);
276
+ }
277
+ if (typeof c === "object" && "value" in c) {
278
+ return renderSqlLiteral(c.value);
279
+ }
139
280
  return "?";
140
281
  }).join("").trim();
141
282
  }
@@ -207,6 +348,27 @@ var SchemaAnalyzer = class {
207
348
  }
208
349
  return out;
209
350
  }
351
+ /**
352
+ * Constraints the column definition already carries, which the analysis used to throw away.
353
+ *
354
+ * Everything here is read off Drizzle's own column instance, so it states what the schema
355
+ * states. Nothing is inferred from a name or guessed from a type.
356
+ */
357
+ columnConstraints(column) {
358
+ const ctor = column?.constructor?.name ?? "";
359
+ const out = {};
360
+ const length = column?.length ?? column?.config?.length;
361
+ if (typeof length === "number" && Number.isFinite(length) && length > 0) {
362
+ out.maxLength = length;
363
+ }
364
+ const range = _SchemaAnalyzer.INT_RANGES[ctor];
365
+ if (range) {
366
+ [out.min, out.max] = range;
367
+ out.integer = true;
368
+ }
369
+ if (/^(Pg)?UUID$/i.test(ctor) || /Uuid$/i.test(ctor)) out.format = "uuid";
370
+ return out;
371
+ }
210
372
  mapColumnType(column) {
211
373
  const ctor = column?.constructor?.name ?? "";
212
374
  switch (ctor) {
@@ -228,6 +390,14 @@ var SchemaAnalyzer = class {
228
390
  case "PgInteger":
229
391
  case "PgSmallInt":
230
392
  return { tsType: "number", dbType: "INTEGER" };
393
+ // Drizzle names these by their mode: `PgBigInt53` for `{ mode: 'number' }` and
394
+ // `PgBigInt64` for `{ mode: 'bigint' }`. `PgBigInt` matched neither, so both fell through
395
+ // to the regex arm and came back as `bigint`, which is wrong for the number mode: the
396
+ // value really is a JS number there, and a schema demanding a bigint rejects every row.
397
+ case "PgBigInt53":
398
+ return { tsType: "number", dbType: "BIGINT" };
399
+ case "PgBigInt64":
400
+ return { tsType: "bigint", dbType: "BIGINT" };
231
401
  case "PgBigInt":
232
402
  return {
233
403
  tsType: column?.config?.mode === "number" ? "number" : "bigint",
@@ -241,6 +411,9 @@ var SchemaAnalyzer = class {
241
411
  case "PgVarchar":
242
412
  case "PgChar":
243
413
  return { tsType: "string", dbType: "TEXT" };
414
+ // Drizzle spells it `PgUUID`. `PgUuid` matched nothing, so every uuid column fell through
415
+ // to the regex arm below and came back as plain TEXT, losing the format.
416
+ case "PgUUID":
244
417
  case "PgUuid":
245
418
  return { tsType: "string", dbType: "UUID" };
246
419
  case "PgBoolean":
@@ -265,7 +438,8 @@ var SchemaAnalyzer = class {
265
438
  if (/Text|Varchar|Char|Uuid/i.test(ctor)) return { tsType: "string", dbType: "TEXT" };
266
439
  if (/Inet|Cidr|Macaddr8?|Uuid/i.test(ctor)) return { tsType: "string", dbType: "TEXT" };
267
440
  if (/Point|Line/i.test(ctor)) return { tsType: "string", dbType: "TEXT" };
268
- if (/TimestampString|DateString/i.test(ctor)) return { tsType: "string", dbType: "TIMESTAMP" };
441
+ if (/TimestampString|DateString/i.test(ctor))
442
+ return { tsType: "string", dbType: "TIMESTAMP" };
269
443
  if (/Timestamptz/i.test(ctor)) return { tsType: "Date", dbType: "TIMESTAMPTZ" };
270
444
  if (/Timestamp/i.test(ctor)) return { tsType: "Date", dbType: "TIMESTAMP" };
271
445
  if (/Date/i.test(ctor)) return { tsType: "Date", dbType: "TIMESTAMP" };
@@ -274,8 +448,10 @@ var SchemaAnalyzer = class {
274
448
  if (/\bInt(eger)?\b|Serial/i.test(ctor)) return { tsType: "number", dbType: "INTEGER" };
275
449
  if (/BigInt/i.test(ctor)) return { tsType: "bigint", dbType: "BIGINT" };
276
450
  if (/Bool/i.test(ctor)) return { tsType: "boolean", dbType: "BOOLEAN" };
277
- if (/Jsonb?/i.test(ctor)) return { tsType: "any", dbType: /Jsonb/i.test(ctor) ? "JSONB" : "JSON" };
278
- if (/Numeric|Float|Double|Real/i.test(ctor)) return { tsType: "number", dbType: "NUMERIC" };
451
+ if (/Jsonb?/i.test(ctor))
452
+ return { tsType: "any", dbType: /Jsonb/i.test(ctor) ? "JSONB" : "JSON" };
453
+ if (/Numeric|Float|Double|Real/i.test(ctor))
454
+ return { tsType: "number", dbType: "NUMERIC" };
279
455
  }
280
456
  if (/^MySql/i.test(ctor)) {
281
457
  if (/BigInt64/i.test(ctor)) return { tsType: "bigint", dbType: "BIGINT" };
@@ -327,7 +503,8 @@ var SchemaAnalyzer = class {
327
503
  if (/TimestampTz/i.test(ctor)) return { tsType: "Date", dbType: "TIMESTAMPTZ" };
328
504
  if (/Timestamp/i.test(ctor)) return { tsType: "string", dbType: "TIMESTAMP" };
329
505
  if (/LocalDateString|LocalTime/i.test(ctor)) return { tsType: "string", dbType: "TEXT" };
330
- if (/DateDuration|RelDuration|Duration/i.test(ctor)) return { tsType: "string", dbType: "TEXT" };
506
+ if (/DateDuration|RelDuration|Duration/i.test(ctor))
507
+ return { tsType: "string", dbType: "TEXT" };
331
508
  }
332
509
  return { tsType: "unknown", dbType: "UNKNOWN" };
333
510
  }
@@ -362,6 +539,9 @@ var SchemaAnalyzer = class {
362
539
  arr.push(colName);
363
540
  uniqueGroups.set(uName, arr);
364
541
  }
542
+ const v1 = describeV1Column(col);
543
+ const constraints = this.columnConstraints(col);
544
+ if (v1?.shape) delete constraints.maxLength;
365
545
  columns.push({
366
546
  name: colName,
367
547
  tsType,
@@ -371,7 +551,9 @@ var SchemaAnalyzer = class {
371
551
  isGenerated,
372
552
  defaultExpression: void 0,
373
553
  references,
374
- enumValues: Array.isArray(ev) ? ev : void 0
554
+ enumValues: Array.isArray(ev) ? ev : void 0,
555
+ ...constraints,
556
+ ...v1 ?? {}
375
557
  });
376
558
  }
377
559
  const name = this.getSymbol(tbl, "drizzle:Name") || tsName;
@@ -532,27 +714,32 @@ var SchemaAnalyzer = class {
532
714
  enums.push(candidate);
533
715
  }
534
716
  let dialect = "unknown";
535
- const ctorNames = /* @__PURE__ */ new Set();
717
+ const marks = /* @__PURE__ */ new Set();
536
718
  for (const [_, val] of Object.entries(exportsObj)) {
537
719
  const cols = val?.[/* @__PURE__ */ Symbol.for("drizzle:Columns")];
538
- if (cols) {
539
- for (const c of Object.values(cols)) {
540
- const n = c?.constructor?.name;
541
- if (n) ctorNames.add(n);
542
- }
720
+ if (!cols) continue;
721
+ for (const c of Object.values(cols)) {
722
+ const kind = c?.constructor?.[/* @__PURE__ */ Symbol.for("drizzle:entityKind")];
723
+ if (typeof kind === "string") marks.add(kind);
724
+ const n = c?.constructor?.name;
725
+ if (n) marks.add(n);
543
726
  }
544
727
  }
545
- const names = Array.from(ctorNames).join(",");
728
+ const names = Array.from(marks).join(",");
546
729
  if (/SQLite/i.test(names)) dialect = "sqlite";
547
- else if (/Pg|Postgres/i.test(names)) dialect = "postgres";
548
- else if (/MySql|Mysql/i.test(names)) dialect = "mysql";
549
730
  else if (/SingleStore/i.test(names)) dialect = "singlestore";
731
+ else if (/Cockroach/i.test(names)) dialect = "cockroach";
732
+ else if (/MsSql/i.test(names)) dialect = "mssql";
733
+ else if (/MySql|Mysql/i.test(names)) dialect = "mysql";
734
+ else if (/Pg|Postgres/i.test(names)) dialect = "postgres";
550
735
  else if (/Gel/i.test(names)) dialect = "gel";
551
- if (dialect === "unknown") {
552
- const looksSqlite = tables.some(
553
- (t) => t.columns.some((c) => ["INTEGER", "TEXT", "REAL", "BLOB", "NUMERIC"].includes(c.dbType))
554
- );
555
- if (looksSqlite) dialect = "sqlite";
736
+ if (dialect === "unknown" && tables.length) {
737
+ issues.push({
738
+ code: "DRZL_ANL_DIALECT",
739
+ level: "warn",
740
+ message: `Could not identify the Drizzle dialect for this schema${names ? `; saw column kinds: ${Array.from(marks).slice(0, 6).join(", ")}` : ""}.`,
741
+ hint: "Column types will fall back to their coarse defaults. If this is a dialect DRZL does not know yet, please open an issue."
742
+ });
556
743
  }
557
744
  if (opts.includeRelations) {
558
745
  relations.push(...this.inferManyToMany(tables));
@@ -592,8 +779,49 @@ var SchemaAnalyzer = class {
592
779
  };
593
780
  }
594
781
  };
782
+ /**
783
+ * The range an integer column can actually hold, keyed off the Drizzle column class.
784
+ *
785
+ * Matches what `drizzle-orm/zod` emits, measured at 1.0.0-rc.4. The two bigint modes differ on
786
+ * purpose: in `{ mode: 'number' }` the value arrives as a JS number, so the real ceiling is
787
+ * `Number.MAX_SAFE_INTEGER` rather than the column's, and bounding at the column's would
788
+ * promise a precision that cannot survive the round trip.
789
+ */
790
+ _SchemaAnalyzer.INT_RANGES = {
791
+ // 8 bit
792
+ MySqlTinyInt: ["-128", "127"],
793
+ SQLiteInteger: ["-9223372036854775808", "9223372036854775807"],
794
+ // 16 bit
795
+ PgSmallInt: ["-32768", "32767"],
796
+ // A serial is an ordinary integer column that happens to default from a sequence. The
797
+ // sequence starts at 1, the column does not: `INSERT ... (id) VALUES (-5)` is accepted by
798
+ // Postgres and is how backfills and sentinel rows get written. Lower-bounding these at 1
799
+ // rejected valid rows, and `drizzle-orm/zod` bounds them by the integer width too.
800
+ PgSmallSerial: ["-32768", "32767"],
801
+ MySqlSmallInt: ["-32768", "32767"],
802
+ SingleStoreSmallInt: ["-32768", "32767"],
803
+ // 24 bit
804
+ MySqlMediumInt: ["-8388608", "8388607"],
805
+ // 32 bit
806
+ PgInteger: ["-2147483648", "2147483647"],
807
+ PgSerial: ["-2147483648", "2147483647"],
808
+ MySqlInt: ["-2147483648", "2147483647"],
809
+ SingleStoreInt: ["-2147483648", "2147483647"],
810
+ // 53 bit, the JS safe-integer ceiling rather than the column's
811
+ PgBigInt53: ["-9007199254740991", "9007199254740991"],
812
+ PgBigSerial53: ["-9007199254740991", "9007199254740991"],
813
+ MySqlBigInt53: ["-9007199254740991", "9007199254740991"],
814
+ SingleStoreBigInt53: ["-9007199254740991", "9007199254740991"],
815
+ // 64 bit, representable because the value is a bigint
816
+ PgBigInt64: ["-9223372036854775808", "9223372036854775807"],
817
+ PgBigSerial64: ["-9223372036854775808", "9223372036854775807"],
818
+ MySqlBigInt64: ["-9223372036854775808", "9223372036854775807"],
819
+ SingleStoreBigInt64: ["-9223372036854775808", "9223372036854775807"]
820
+ };
821
+ var SchemaAnalyzer = _SchemaAnalyzer;
595
822
  var index_default = SchemaAnalyzer;
596
823
  export {
597
824
  SchemaAnalyzer,
598
- index_default as default
825
+ index_default as default,
826
+ describeV1Column
599
827
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drzl/analyzer",
3
- "version": "1.5.2",
3
+ "version": "1.7.0",
4
4
  "private": false,
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",