@drzl/analyzer 1.14.0 → 1.15.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/README.md +12 -1
- package/dist/index.cjs +92 -11
- package/dist/index.d.cts +36 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.js +92 -11
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -47,6 +47,17 @@ The CLI consumes this analysis to generate validation, services, and routers.
|
|
|
47
47
|
- relations (incl. inferred), enums
|
|
48
48
|
- issues (warnings/errors) for constraints and shape
|
|
49
49
|
|
|
50
|
+
Per column, beyond the type: `nullable`, `hasDefault`, `defaultValue` (literal defaults only),
|
|
51
|
+
`isGenerated`, `enumValues`, `maxLength` (characters), `maxBytes` (MySQL's TEXT family is a byte
|
|
52
|
+
budget, which is a different measurement on the same kind of column), `min`/`max`,
|
|
53
|
+
`arrayDimensions`, `format`, and `shape` for values that are not scalars (json, buffer, tuple,
|
|
54
|
+
vector, bitstring, customType).
|
|
55
|
+
|
|
56
|
+
`DRZL_ANL_UNKNOWN_COLUMN` is reported for any column whose validator would accept anything, which
|
|
57
|
+
is the shape a missing type mapping takes: nothing throws, and every row passes.
|
|
58
|
+
|
|
50
59
|
## Notes
|
|
51
60
|
|
|
52
|
-
- Best‑effort introspection aligned with Drizzle symbols across versions.
|
|
61
|
+
- Best‑effort introspection aligned with Drizzle symbols across versions. Both live majors are
|
|
62
|
+
covered and diffed against each other in CI: 0.4x and v1 model arrays and enums differently, and
|
|
63
|
+
reading only one silently typed every `.array()` column as `unknown` on the other.
|
package/dist/index.cjs
CHANGED
|
@@ -57,11 +57,16 @@ var MYSQL_TEXT_CAPS = {
|
|
|
57
57
|
mediumblob: 16777215,
|
|
58
58
|
longblob: 4294967295
|
|
59
59
|
};
|
|
60
|
-
var
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
60
|
+
var FLOAT32_MAX = "340282346638528859811704183484516925440";
|
|
61
|
+
var PG_FLOAT4_INPUT_MAX = "340282356779733661637539395458142568448";
|
|
62
|
+
var PG_FLOAT4_RANGE = [`-${PG_FLOAT4_INPUT_MAX}`, PG_FLOAT4_INPUT_MAX];
|
|
63
|
+
var MYSQL_FLOAT_RANGE = [`-${FLOAT32_MAX}`, FLOAT32_MAX];
|
|
64
|
+
var JS_SAFE_INTEGER_BOUNDS = ["-9007199254740991", "9007199254740991"];
|
|
65
|
+
var TUPLE_CLASS_SHAPES = {
|
|
66
|
+
// `line()` is the trap here: its `drizzle:entityKind` is `PgLine` while its constructor is
|
|
67
|
+
// `PgLineTuple`, and this path matches on the constructor.
|
|
68
|
+
PgPointTuple: { kind: "tuple", length: 2 },
|
|
69
|
+
PgLineTuple: { kind: "tuple", length: 3 }
|
|
65
70
|
};
|
|
66
71
|
function describeV1Column(column) {
|
|
67
72
|
const codec = column?.codec;
|
|
@@ -111,7 +116,8 @@ function describeV1Column(column) {
|
|
|
111
116
|
break;
|
|
112
117
|
case "float":
|
|
113
118
|
case "double": {
|
|
114
|
-
|
|
119
|
+
if (semantic === "float")
|
|
120
|
+
[out.min, out.max] = codec === "float4" ? PG_FLOAT4_RANGE : MYSQL_FLOAT_RANGE;
|
|
115
121
|
out.integer = false;
|
|
116
122
|
out.tsType = "number";
|
|
117
123
|
out.dbType = semantic === "float" ? "REAL" : "DOUBLE";
|
|
@@ -200,7 +206,7 @@ function describeV1Column(column) {
|
|
|
200
206
|
out.tsType = "number";
|
|
201
207
|
out.dbType = "NUMERIC";
|
|
202
208
|
out.integer = false;
|
|
203
|
-
[out.min, out.max] =
|
|
209
|
+
[out.min, out.max] = JS_SAFE_INTEGER_BOUNDS;
|
|
204
210
|
} else if (js === "string") {
|
|
205
211
|
out.tsType = "string";
|
|
206
212
|
out.dbType = codec === "varchar" ? "VARCHAR" : codec === "char" ? "CHAR" : "TEXT";
|
|
@@ -507,6 +513,11 @@ var _SchemaAnalyzer = class _SchemaAnalyzer {
|
|
|
507
513
|
[out.min, out.max] = range;
|
|
508
514
|
out.integer = true;
|
|
509
515
|
}
|
|
516
|
+
if (Object.prototype.hasOwnProperty.call(_SchemaAnalyzer.INEXACT_RANGES, ctor)) {
|
|
517
|
+
const inexact = _SchemaAnalyzer.INEXACT_RANGES[ctor];
|
|
518
|
+
if (inexact) [out.min, out.max] = inexact;
|
|
519
|
+
out.integer = false;
|
|
520
|
+
}
|
|
510
521
|
if (/^(Pg)?UUID$/i.test(ctor) || /Uuid$/i.test(ctor)) out.format = "uuid";
|
|
511
522
|
return out;
|
|
512
523
|
}
|
|
@@ -578,9 +589,25 @@ var _SchemaAnalyzer = class _SchemaAnalyzer {
|
|
|
578
589
|
return { tsType: "Date", dbType: "TIMESTAMP" };
|
|
579
590
|
case "PgNumeric":
|
|
580
591
|
return { tsType: "string", dbType: "NUMERIC" };
|
|
581
|
-
case "PgFloat":
|
|
582
592
|
case "PgDoublePrecision":
|
|
583
593
|
return { tsType: "number", dbType: "DOUBLE" };
|
|
594
|
+
// `real()` builds a `PgReal`, which matched no arm and fell through to the coarse
|
|
595
|
+
// `/Numeric|Float|Double|Real/i` below, so a real column was labelled NUMERIC while v1
|
|
596
|
+
// called it REAL. The arm above used to name `PgFloat` alongside `PgDoublePrecision`, and
|
|
597
|
+
// no such class exists in pg-core on either major: `float` is MySQL's spelling and Gel's
|
|
598
|
+
// is `GelReal`, both of which are matched elsewhere. Enumerated from the module's own
|
|
599
|
+
// exports on 0.45.2 and on 1.0.0-rc.4, which name only PgReal and PgDoublePrecision.
|
|
600
|
+
case "PgReal":
|
|
601
|
+
return { tsType: "number", dbType: "REAL" };
|
|
602
|
+
// 0.4x names a point and a line by their mode. `point()` is a `PgPointTuple` and `line()` a
|
|
603
|
+
// `PgLineTuple`, whose entity kind is `PgLine` while its constructor is not, and both used
|
|
604
|
+
// to fall through to `/Point|Line/i` and come back `string`. The driver hands back [x, y]
|
|
605
|
+
// and [a, b, c], so a select schema built on 0.4x refused every row, and an insert schema
|
|
606
|
+
// took the one string form `mapToDriverValue` turns into something Postgres rejects.
|
|
607
|
+
case "PgPointTuple":
|
|
608
|
+
return { tsType: "[number, number]", dbType: "POINT" };
|
|
609
|
+
case "PgLineTuple":
|
|
610
|
+
return { tsType: "[number, number, number]", dbType: "LINE" };
|
|
584
611
|
case "PgJson":
|
|
585
612
|
case "PgJsonb":
|
|
586
613
|
return { tsType: "any", dbType: ctor === "PgJsonb" ? "JSONB" : "JSON" };
|
|
@@ -703,8 +730,8 @@ var _SchemaAnalyzer = class _SchemaAnalyzer {
|
|
|
703
730
|
const sqlKind = String(outerCol?.constructor?.[/* @__PURE__ */ Symbol.for("drizzle:entityKind")] ?? "");
|
|
704
731
|
const sqlType = sqlKind.startsWith("MySql") && typeof col?.getSQLType === "function" ? String(col.getSQLType()).toLowerCase() : void 0;
|
|
705
732
|
const byteCap = sqlType ? MYSQL_TEXT_CAPS[sqlType] : void 0;
|
|
706
|
-
const
|
|
707
|
-
const shape = (v1?.shape ??
|
|
733
|
+
const fallbackShape = dbType === "JSON" || dbType === "JSONB" || col?.config?.mode === "json" ? { kind: "json" } : TUPLE_CLASS_SHAPES[String(col?.constructor?.name ?? "")];
|
|
734
|
+
const shape = (v1?.shape ?? fallbackShape)?.kind;
|
|
708
735
|
const finalTs = v1?.tsType ?? tsType;
|
|
709
736
|
const wide = (finalTs === "unknown" || finalTs === "any") && (!shape || shape === "custom");
|
|
710
737
|
if (wide) {
|
|
@@ -733,7 +760,7 @@ var _SchemaAnalyzer = class _SchemaAnalyzer {
|
|
|
733
760
|
// that spread has nothing to say and this is the only source.
|
|
734
761
|
...arrayDims ? { arrayDimensions: arrayDims } : {},
|
|
735
762
|
// Only where v1 did not already describe the value, so a shaped column keeps its shape.
|
|
736
|
-
...
|
|
763
|
+
...fallbackShape && !v1?.shape ? { shape: fallbackShape } : {},
|
|
737
764
|
...byteCap && v1?.maxBytes === void 0 ? { maxBytes: byteCap } : {}
|
|
738
765
|
});
|
|
739
766
|
}
|
|
@@ -1011,6 +1038,60 @@ _SchemaAnalyzer.INT_RANGES = {
|
|
|
1011
1038
|
MySqlBigInt64: ["-9223372036854775808", "9223372036854775807"],
|
|
1012
1039
|
SingleStoreBigInt64: ["-9223372036854775808", "9223372036854775807"]
|
|
1013
1040
|
};
|
|
1041
|
+
/**
|
|
1042
|
+
* The numeric column classes that are not exact, and the magnitude each one can really hold.
|
|
1043
|
+
*
|
|
1044
|
+
* Only drizzle v1 states this outright, as a `float` or `double` semantic on `dataType`. On
|
|
1045
|
+
* 0.4x the same columns reach the analyzer by class name, `INT_RANGES` was the only range table
|
|
1046
|
+
* on that path, and so nothing said anything about them at all: not the range, and not that
|
|
1047
|
+
* they are inexact. The parity gate measured seven of the ten classes below and reported DRZL
|
|
1048
|
+
* differing from the first-party validator for the same major on all seven. The three
|
|
1049
|
+
* SingleStore classes are in no fixture either pass carries and are covered by unit tests alone.
|
|
1050
|
+
*
|
|
1051
|
+
* Differing is what the gate reports, not what it forbids. Most of its waivers have DRZL
|
|
1052
|
+
* accepting something official refuses and the run counts them; an earlier version of this
|
|
1053
|
+
* sentence said the gate exists to forbid being looser, which the same commit's own success
|
|
1054
|
+
* banner denies.
|
|
1055
|
+
*
|
|
1056
|
+
* `null` is a value in this table and is not the same as a class it does not name. It says the
|
|
1057
|
+
* column is inexact and that no finite magnitude bound is truthful for it, which is the case for
|
|
1058
|
+
* every 8 byte float: float8 is the JavaScript number's own format, so Postgres accepts every
|
|
1059
|
+
* finite JS number into one, measured to `Number.MAX_VALUE`. Read with an own-property test for
|
|
1060
|
+
* that reason, and because a plain object answers to `constructor` and `toString`.
|
|
1061
|
+
*
|
|
1062
|
+
* `integer: false` travels with every entry, bound or not, and what it decides depends on which.
|
|
1063
|
+
* `isIntegerColumn` falls back to "declares both bounds" when the flag is absent, so on a bounded
|
|
1064
|
+
* entry the flag is the only thing stopping the emitted schema calling `.int()` and refusing 1.5.
|
|
1065
|
+
* On an unbounded one it decides nothing, measured both ways against the real function in
|
|
1066
|
+
* `@drzl/validation-core`'s integer-column.spec.ts. It is stated there because it is true of the
|
|
1067
|
+
* column, not because it guards anything. That spec is where the measurement moved when the
|
|
1068
|
+
* analyzer's copy of it turned out to be a closed loop; this sentence went on naming
|
|
1069
|
+
* floats-and-tuples-0.4x.spec.ts, which asserts the flag is present and nothing about what it
|
|
1070
|
+
* decides.
|
|
1071
|
+
*
|
|
1072
|
+
* The widths are the type's, not the name's: MySQL and SingleStore `real` is a synonym for
|
|
1073
|
+
* `double` unless REAL_AS_FLOAT is set, and SQLite `real` is an 8 byte IEEE float. Both are
|
|
1074
|
+
* `number double` on drizzle v1, which is where these pairings come from.
|
|
1075
|
+
*/
|
|
1076
|
+
_SchemaAnalyzer.INEXACT_RANGES = {
|
|
1077
|
+
// 4 byte floats, the one width a database refuses a magnitude for, and the two that have one
|
|
1078
|
+
// refuse at different values. SingleStore is MySQL wire-compatible and unmeasured here, so it
|
|
1079
|
+
// takes MySQL's rather than the wider of the two.
|
|
1080
|
+
PgReal: PG_FLOAT4_RANGE,
|
|
1081
|
+
MySqlFloat: MYSQL_FLOAT_RANGE,
|
|
1082
|
+
SingleStoreFloat: MYSQL_FLOAT_RANGE,
|
|
1083
|
+
// 8 byte floats, which hold every finite JS number
|
|
1084
|
+
PgDoublePrecision: null,
|
|
1085
|
+
MySqlDouble: null,
|
|
1086
|
+
MySqlReal: null,
|
|
1087
|
+
SQLiteReal: null,
|
|
1088
|
+
SingleStoreDouble: null,
|
|
1089
|
+
SingleStoreReal: null,
|
|
1090
|
+
// `numeric({ mode: 'number' })`, which v1 reaches through the bare-number arm of
|
|
1091
|
+
// `describeV1Column`. This one is about what a JS number can carry rather than about the
|
|
1092
|
+
// column, which Postgres caps far lower: it refuses 2147483648 into a `numeric(10,2)`.
|
|
1093
|
+
PgNumericNumber: JS_SAFE_INTEGER_BOUNDS
|
|
1094
|
+
};
|
|
1014
1095
|
var SchemaAnalyzer = _SchemaAnalyzer;
|
|
1015
1096
|
var index_default = SchemaAnalyzer;
|
|
1016
1097
|
// Annotate the CommonJS export names for ESM import in node:
|
package/dist/index.d.cts
CHANGED
|
@@ -354,6 +354,42 @@ declare class SchemaAnalyzer {
|
|
|
354
354
|
* promise a precision that cannot survive the round trip.
|
|
355
355
|
*/
|
|
356
356
|
private static readonly INT_RANGES;
|
|
357
|
+
/**
|
|
358
|
+
* The numeric column classes that are not exact, and the magnitude each one can really hold.
|
|
359
|
+
*
|
|
360
|
+
* Only drizzle v1 states this outright, as a `float` or `double` semantic on `dataType`. On
|
|
361
|
+
* 0.4x the same columns reach the analyzer by class name, `INT_RANGES` was the only range table
|
|
362
|
+
* on that path, and so nothing said anything about them at all: not the range, and not that
|
|
363
|
+
* they are inexact. The parity gate measured seven of the ten classes below and reported DRZL
|
|
364
|
+
* differing from the first-party validator for the same major on all seven. The three
|
|
365
|
+
* SingleStore classes are in no fixture either pass carries and are covered by unit tests alone.
|
|
366
|
+
*
|
|
367
|
+
* Differing is what the gate reports, not what it forbids. Most of its waivers have DRZL
|
|
368
|
+
* accepting something official refuses and the run counts them; an earlier version of this
|
|
369
|
+
* sentence said the gate exists to forbid being looser, which the same commit's own success
|
|
370
|
+
* banner denies.
|
|
371
|
+
*
|
|
372
|
+
* `null` is a value in this table and is not the same as a class it does not name. It says the
|
|
373
|
+
* column is inexact and that no finite magnitude bound is truthful for it, which is the case for
|
|
374
|
+
* every 8 byte float: float8 is the JavaScript number's own format, so Postgres accepts every
|
|
375
|
+
* finite JS number into one, measured to `Number.MAX_VALUE`. Read with an own-property test for
|
|
376
|
+
* that reason, and because a plain object answers to `constructor` and `toString`.
|
|
377
|
+
*
|
|
378
|
+
* `integer: false` travels with every entry, bound or not, and what it decides depends on which.
|
|
379
|
+
* `isIntegerColumn` falls back to "declares both bounds" when the flag is absent, so on a bounded
|
|
380
|
+
* entry the flag is the only thing stopping the emitted schema calling `.int()` and refusing 1.5.
|
|
381
|
+
* On an unbounded one it decides nothing, measured both ways against the real function in
|
|
382
|
+
* `@drzl/validation-core`'s integer-column.spec.ts. It is stated there because it is true of the
|
|
383
|
+
* column, not because it guards anything. That spec is where the measurement moved when the
|
|
384
|
+
* analyzer's copy of it turned out to be a closed loop; this sentence went on naming
|
|
385
|
+
* floats-and-tuples-0.4x.spec.ts, which asserts the flag is present and nothing about what it
|
|
386
|
+
* decides.
|
|
387
|
+
*
|
|
388
|
+
* The widths are the type's, not the name's: MySQL and SingleStore `real` is a synonym for
|
|
389
|
+
* `double` unless REAL_AS_FLOAT is set, and SQLite `real` is an 8 byte IEEE float. Both are
|
|
390
|
+
* `number double` on drizzle v1, which is where these pairings come from.
|
|
391
|
+
*/
|
|
392
|
+
private static readonly INEXACT_RANGES;
|
|
357
393
|
/**
|
|
358
394
|
* Constraints the column definition already carries, which the analysis used to throw away.
|
|
359
395
|
*
|
package/dist/index.d.ts
CHANGED
|
@@ -354,6 +354,42 @@ declare class SchemaAnalyzer {
|
|
|
354
354
|
* promise a precision that cannot survive the round trip.
|
|
355
355
|
*/
|
|
356
356
|
private static readonly INT_RANGES;
|
|
357
|
+
/**
|
|
358
|
+
* The numeric column classes that are not exact, and the magnitude each one can really hold.
|
|
359
|
+
*
|
|
360
|
+
* Only drizzle v1 states this outright, as a `float` or `double` semantic on `dataType`. On
|
|
361
|
+
* 0.4x the same columns reach the analyzer by class name, `INT_RANGES` was the only range table
|
|
362
|
+
* on that path, and so nothing said anything about them at all: not the range, and not that
|
|
363
|
+
* they are inexact. The parity gate measured seven of the ten classes below and reported DRZL
|
|
364
|
+
* differing from the first-party validator for the same major on all seven. The three
|
|
365
|
+
* SingleStore classes are in no fixture either pass carries and are covered by unit tests alone.
|
|
366
|
+
*
|
|
367
|
+
* Differing is what the gate reports, not what it forbids. Most of its waivers have DRZL
|
|
368
|
+
* accepting something official refuses and the run counts them; an earlier version of this
|
|
369
|
+
* sentence said the gate exists to forbid being looser, which the same commit's own success
|
|
370
|
+
* banner denies.
|
|
371
|
+
*
|
|
372
|
+
* `null` is a value in this table and is not the same as a class it does not name. It says the
|
|
373
|
+
* column is inexact and that no finite magnitude bound is truthful for it, which is the case for
|
|
374
|
+
* every 8 byte float: float8 is the JavaScript number's own format, so Postgres accepts every
|
|
375
|
+
* finite JS number into one, measured to `Number.MAX_VALUE`. Read with an own-property test for
|
|
376
|
+
* that reason, and because a plain object answers to `constructor` and `toString`.
|
|
377
|
+
*
|
|
378
|
+
* `integer: false` travels with every entry, bound or not, and what it decides depends on which.
|
|
379
|
+
* `isIntegerColumn` falls back to "declares both bounds" when the flag is absent, so on a bounded
|
|
380
|
+
* entry the flag is the only thing stopping the emitted schema calling `.int()` and refusing 1.5.
|
|
381
|
+
* On an unbounded one it decides nothing, measured both ways against the real function in
|
|
382
|
+
* `@drzl/validation-core`'s integer-column.spec.ts. It is stated there because it is true of the
|
|
383
|
+
* column, not because it guards anything. That spec is where the measurement moved when the
|
|
384
|
+
* analyzer's copy of it turned out to be a closed loop; this sentence went on naming
|
|
385
|
+
* floats-and-tuples-0.4x.spec.ts, which asserts the flag is present and nothing about what it
|
|
386
|
+
* decides.
|
|
387
|
+
*
|
|
388
|
+
* The widths are the type's, not the name's: MySQL and SingleStore `real` is a synonym for
|
|
389
|
+
* `double` unless REAL_AS_FLOAT is set, and SQLite `real` is an 8 byte IEEE float. Both are
|
|
390
|
+
* `number double` on drizzle v1, which is where these pairings come from.
|
|
391
|
+
*/
|
|
392
|
+
private static readonly INEXACT_RANGES;
|
|
357
393
|
/**
|
|
358
394
|
* Constraints the column definition already carries, which the analysis used to throw away.
|
|
359
395
|
*
|
package/dist/index.js
CHANGED
|
@@ -17,11 +17,16 @@ var MYSQL_TEXT_CAPS = {
|
|
|
17
17
|
mediumblob: 16777215,
|
|
18
18
|
longblob: 4294967295
|
|
19
19
|
};
|
|
20
|
-
var
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
var FLOAT32_MAX = "340282346638528859811704183484516925440";
|
|
21
|
+
var PG_FLOAT4_INPUT_MAX = "340282356779733661637539395458142568448";
|
|
22
|
+
var PG_FLOAT4_RANGE = [`-${PG_FLOAT4_INPUT_MAX}`, PG_FLOAT4_INPUT_MAX];
|
|
23
|
+
var MYSQL_FLOAT_RANGE = [`-${FLOAT32_MAX}`, FLOAT32_MAX];
|
|
24
|
+
var JS_SAFE_INTEGER_BOUNDS = ["-9007199254740991", "9007199254740991"];
|
|
25
|
+
var TUPLE_CLASS_SHAPES = {
|
|
26
|
+
// `line()` is the trap here: its `drizzle:entityKind` is `PgLine` while its constructor is
|
|
27
|
+
// `PgLineTuple`, and this path matches on the constructor.
|
|
28
|
+
PgPointTuple: { kind: "tuple", length: 2 },
|
|
29
|
+
PgLineTuple: { kind: "tuple", length: 3 }
|
|
25
30
|
};
|
|
26
31
|
function describeV1Column(column) {
|
|
27
32
|
const codec = column?.codec;
|
|
@@ -71,7 +76,8 @@ function describeV1Column(column) {
|
|
|
71
76
|
break;
|
|
72
77
|
case "float":
|
|
73
78
|
case "double": {
|
|
74
|
-
|
|
79
|
+
if (semantic === "float")
|
|
80
|
+
[out.min, out.max] = codec === "float4" ? PG_FLOAT4_RANGE : MYSQL_FLOAT_RANGE;
|
|
75
81
|
out.integer = false;
|
|
76
82
|
out.tsType = "number";
|
|
77
83
|
out.dbType = semantic === "float" ? "REAL" : "DOUBLE";
|
|
@@ -160,7 +166,7 @@ function describeV1Column(column) {
|
|
|
160
166
|
out.tsType = "number";
|
|
161
167
|
out.dbType = "NUMERIC";
|
|
162
168
|
out.integer = false;
|
|
163
|
-
[out.min, out.max] =
|
|
169
|
+
[out.min, out.max] = JS_SAFE_INTEGER_BOUNDS;
|
|
164
170
|
} else if (js === "string") {
|
|
165
171
|
out.tsType = "string";
|
|
166
172
|
out.dbType = codec === "varchar" ? "VARCHAR" : codec === "char" ? "CHAR" : "TEXT";
|
|
@@ -467,6 +473,11 @@ var _SchemaAnalyzer = class _SchemaAnalyzer {
|
|
|
467
473
|
[out.min, out.max] = range;
|
|
468
474
|
out.integer = true;
|
|
469
475
|
}
|
|
476
|
+
if (Object.prototype.hasOwnProperty.call(_SchemaAnalyzer.INEXACT_RANGES, ctor)) {
|
|
477
|
+
const inexact = _SchemaAnalyzer.INEXACT_RANGES[ctor];
|
|
478
|
+
if (inexact) [out.min, out.max] = inexact;
|
|
479
|
+
out.integer = false;
|
|
480
|
+
}
|
|
470
481
|
if (/^(Pg)?UUID$/i.test(ctor) || /Uuid$/i.test(ctor)) out.format = "uuid";
|
|
471
482
|
return out;
|
|
472
483
|
}
|
|
@@ -538,9 +549,25 @@ var _SchemaAnalyzer = class _SchemaAnalyzer {
|
|
|
538
549
|
return { tsType: "Date", dbType: "TIMESTAMP" };
|
|
539
550
|
case "PgNumeric":
|
|
540
551
|
return { tsType: "string", dbType: "NUMERIC" };
|
|
541
|
-
case "PgFloat":
|
|
542
552
|
case "PgDoublePrecision":
|
|
543
553
|
return { tsType: "number", dbType: "DOUBLE" };
|
|
554
|
+
// `real()` builds a `PgReal`, which matched no arm and fell through to the coarse
|
|
555
|
+
// `/Numeric|Float|Double|Real/i` below, so a real column was labelled NUMERIC while v1
|
|
556
|
+
// called it REAL. The arm above used to name `PgFloat` alongside `PgDoublePrecision`, and
|
|
557
|
+
// no such class exists in pg-core on either major: `float` is MySQL's spelling and Gel's
|
|
558
|
+
// is `GelReal`, both of which are matched elsewhere. Enumerated from the module's own
|
|
559
|
+
// exports on 0.45.2 and on 1.0.0-rc.4, which name only PgReal and PgDoublePrecision.
|
|
560
|
+
case "PgReal":
|
|
561
|
+
return { tsType: "number", dbType: "REAL" };
|
|
562
|
+
// 0.4x names a point and a line by their mode. `point()` is a `PgPointTuple` and `line()` a
|
|
563
|
+
// `PgLineTuple`, whose entity kind is `PgLine` while its constructor is not, and both used
|
|
564
|
+
// to fall through to `/Point|Line/i` and come back `string`. The driver hands back [x, y]
|
|
565
|
+
// and [a, b, c], so a select schema built on 0.4x refused every row, and an insert schema
|
|
566
|
+
// took the one string form `mapToDriverValue` turns into something Postgres rejects.
|
|
567
|
+
case "PgPointTuple":
|
|
568
|
+
return { tsType: "[number, number]", dbType: "POINT" };
|
|
569
|
+
case "PgLineTuple":
|
|
570
|
+
return { tsType: "[number, number, number]", dbType: "LINE" };
|
|
544
571
|
case "PgJson":
|
|
545
572
|
case "PgJsonb":
|
|
546
573
|
return { tsType: "any", dbType: ctor === "PgJsonb" ? "JSONB" : "JSON" };
|
|
@@ -663,8 +690,8 @@ var _SchemaAnalyzer = class _SchemaAnalyzer {
|
|
|
663
690
|
const sqlKind = String(outerCol?.constructor?.[/* @__PURE__ */ Symbol.for("drizzle:entityKind")] ?? "");
|
|
664
691
|
const sqlType = sqlKind.startsWith("MySql") && typeof col?.getSQLType === "function" ? String(col.getSQLType()).toLowerCase() : void 0;
|
|
665
692
|
const byteCap = sqlType ? MYSQL_TEXT_CAPS[sqlType] : void 0;
|
|
666
|
-
const
|
|
667
|
-
const shape = (v1?.shape ??
|
|
693
|
+
const fallbackShape = dbType === "JSON" || dbType === "JSONB" || col?.config?.mode === "json" ? { kind: "json" } : TUPLE_CLASS_SHAPES[String(col?.constructor?.name ?? "")];
|
|
694
|
+
const shape = (v1?.shape ?? fallbackShape)?.kind;
|
|
668
695
|
const finalTs = v1?.tsType ?? tsType;
|
|
669
696
|
const wide = (finalTs === "unknown" || finalTs === "any") && (!shape || shape === "custom");
|
|
670
697
|
if (wide) {
|
|
@@ -693,7 +720,7 @@ var _SchemaAnalyzer = class _SchemaAnalyzer {
|
|
|
693
720
|
// that spread has nothing to say and this is the only source.
|
|
694
721
|
...arrayDims ? { arrayDimensions: arrayDims } : {},
|
|
695
722
|
// Only where v1 did not already describe the value, so a shaped column keeps its shape.
|
|
696
|
-
...
|
|
723
|
+
...fallbackShape && !v1?.shape ? { shape: fallbackShape } : {},
|
|
697
724
|
...byteCap && v1?.maxBytes === void 0 ? { maxBytes: byteCap } : {}
|
|
698
725
|
});
|
|
699
726
|
}
|
|
@@ -971,6 +998,60 @@ _SchemaAnalyzer.INT_RANGES = {
|
|
|
971
998
|
MySqlBigInt64: ["-9223372036854775808", "9223372036854775807"],
|
|
972
999
|
SingleStoreBigInt64: ["-9223372036854775808", "9223372036854775807"]
|
|
973
1000
|
};
|
|
1001
|
+
/**
|
|
1002
|
+
* The numeric column classes that are not exact, and the magnitude each one can really hold.
|
|
1003
|
+
*
|
|
1004
|
+
* Only drizzle v1 states this outright, as a `float` or `double` semantic on `dataType`. On
|
|
1005
|
+
* 0.4x the same columns reach the analyzer by class name, `INT_RANGES` was the only range table
|
|
1006
|
+
* on that path, and so nothing said anything about them at all: not the range, and not that
|
|
1007
|
+
* they are inexact. The parity gate measured seven of the ten classes below and reported DRZL
|
|
1008
|
+
* differing from the first-party validator for the same major on all seven. The three
|
|
1009
|
+
* SingleStore classes are in no fixture either pass carries and are covered by unit tests alone.
|
|
1010
|
+
*
|
|
1011
|
+
* Differing is what the gate reports, not what it forbids. Most of its waivers have DRZL
|
|
1012
|
+
* accepting something official refuses and the run counts them; an earlier version of this
|
|
1013
|
+
* sentence said the gate exists to forbid being looser, which the same commit's own success
|
|
1014
|
+
* banner denies.
|
|
1015
|
+
*
|
|
1016
|
+
* `null` is a value in this table and is not the same as a class it does not name. It says the
|
|
1017
|
+
* column is inexact and that no finite magnitude bound is truthful for it, which is the case for
|
|
1018
|
+
* every 8 byte float: float8 is the JavaScript number's own format, so Postgres accepts every
|
|
1019
|
+
* finite JS number into one, measured to `Number.MAX_VALUE`. Read with an own-property test for
|
|
1020
|
+
* that reason, and because a plain object answers to `constructor` and `toString`.
|
|
1021
|
+
*
|
|
1022
|
+
* `integer: false` travels with every entry, bound or not, and what it decides depends on which.
|
|
1023
|
+
* `isIntegerColumn` falls back to "declares both bounds" when the flag is absent, so on a bounded
|
|
1024
|
+
* entry the flag is the only thing stopping the emitted schema calling `.int()` and refusing 1.5.
|
|
1025
|
+
* On an unbounded one it decides nothing, measured both ways against the real function in
|
|
1026
|
+
* `@drzl/validation-core`'s integer-column.spec.ts. It is stated there because it is true of the
|
|
1027
|
+
* column, not because it guards anything. That spec is where the measurement moved when the
|
|
1028
|
+
* analyzer's copy of it turned out to be a closed loop; this sentence went on naming
|
|
1029
|
+
* floats-and-tuples-0.4x.spec.ts, which asserts the flag is present and nothing about what it
|
|
1030
|
+
* decides.
|
|
1031
|
+
*
|
|
1032
|
+
* The widths are the type's, not the name's: MySQL and SingleStore `real` is a synonym for
|
|
1033
|
+
* `double` unless REAL_AS_FLOAT is set, and SQLite `real` is an 8 byte IEEE float. Both are
|
|
1034
|
+
* `number double` on drizzle v1, which is where these pairings come from.
|
|
1035
|
+
*/
|
|
1036
|
+
_SchemaAnalyzer.INEXACT_RANGES = {
|
|
1037
|
+
// 4 byte floats, the one width a database refuses a magnitude for, and the two that have one
|
|
1038
|
+
// refuse at different values. SingleStore is MySQL wire-compatible and unmeasured here, so it
|
|
1039
|
+
// takes MySQL's rather than the wider of the two.
|
|
1040
|
+
PgReal: PG_FLOAT4_RANGE,
|
|
1041
|
+
MySqlFloat: MYSQL_FLOAT_RANGE,
|
|
1042
|
+
SingleStoreFloat: MYSQL_FLOAT_RANGE,
|
|
1043
|
+
// 8 byte floats, which hold every finite JS number
|
|
1044
|
+
PgDoublePrecision: null,
|
|
1045
|
+
MySqlDouble: null,
|
|
1046
|
+
MySqlReal: null,
|
|
1047
|
+
SQLiteReal: null,
|
|
1048
|
+
SingleStoreDouble: null,
|
|
1049
|
+
SingleStoreReal: null,
|
|
1050
|
+
// `numeric({ mode: 'number' })`, which v1 reaches through the bare-number arm of
|
|
1051
|
+
// `describeV1Column`. This one is about what a JS number can carry rather than about the
|
|
1052
|
+
// column, which Postgres caps far lower: it refuses 2147483648 into a `numeric(10,2)`.
|
|
1053
|
+
PgNumericNumber: JS_SAFE_INTEGER_BOUNDS
|
|
1054
|
+
};
|
|
974
1055
|
var SchemaAnalyzer = _SchemaAnalyzer;
|
|
975
1056
|
var index_default = SchemaAnalyzer;
|
|
976
1057
|
export {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@drzl/analyzer",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.15.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"url": "https://github.com/sponsors/omar-dulaimi"
|
|
44
44
|
},
|
|
45
45
|
"scripts": {
|
|
46
|
-
"build": "tsup src/index.ts --dts --format esm,cjs",
|
|
46
|
+
"build": "tsup src/index.ts --dts --format esm,cjs --clean",
|
|
47
47
|
"lint": "eslint . --ext .ts",
|
|
48
48
|
"test": "vitest run --testTimeout=20000"
|
|
49
49
|
}
|