@dudousxd/nestjs-codegen 0.22.1 → 0.24.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/CHANGELOG.md CHANGED
@@ -1,5 +1,37 @@
1
1
  # @dudousxd/nestjs-codegen
2
2
 
3
+ ## 0.24.0
4
+
5
+ ### Minor Changes
6
+
7
+ - c8d7eec: A mapped column's two declared types can disagree, so both are now emitted.
8
+
9
+ **If you upgraded to 0.23.0, you were affected** wherever an entity declares a mapped column — `@Property({ columnType: 'date', type: DateType }) x?: Opt<string>`, or a DECIMAL read back as a string. Those fields kept being emitted, but with the wrong kind, so every union a client derives from it rejected them: `.lt('serviceEndDate', …)` stopped compiling on a column that had been orderable all along.
10
+
11
+ The cause was the brand unwrapping itself. `classifyFieldType` consults the column decorator ONLY when the TS type resolves to `unknown` — so before 0.23.0 an `Opt<string>` fell through to `columnType: 'date'` and classified `date`, correctly and by accident. Making the TS side resolve meant the decorator was never reached.
12
+
13
+ The fix is not to pick a winner, because both are true and each is needed for a different question. `kind` now carries what the COLUMN is — the semantics an operator set derives from — and a new optional `valueKind` carries what the VALUE is, when they differ. A DATE column read back as `'YYYY-MM-DD'` emits `kind: 'date'` and `valueKind: 'string'`.
14
+
15
+ Collapsing them loses one or the other. Answer `string` and the field stops accepting the ordering and range operators the column supports. Answer `date` and the emitted type promises a `Date` the value never holds — which a type-preserving wire format like superjson then contradicts at runtime, since it faithfully transports the string that is actually there. That second failure predates 0.23.0 and is fixed here too: these columns used to emit as `Date` while carrying a string.
16
+
17
+ `valueKind` is absent when the two agree, which is the overwhelming majority of columns, so nothing changes for them.
18
+
19
+ Also teaches the decorator reader `columnType` (MikroORM's raw DDL slot) alongside `type`, and a mapped-type class (`type: DateType`) alongside a keyword string. It read neither, which is why the conflict was invisible from that side.
20
+
21
+ ## 0.23.0
22
+
23
+ ### Minor Changes
24
+
25
+ - 4b13eef: Classify through transparent type brands, so a MikroORM entity's columns stop coming back `unknown`.
26
+
27
+ `Opt<T>` is how MikroORM marks a column optional on insert, and it is how nearly every nullable column in a MikroORM entity is written. It is a compile-time marker with no runtime shape of its own — an `Opt<string>` column holds a string — but the classifier saw a type reference it did not recognise and answered `unknown`. On a real entity that meant 31 of 35 fields unclassified, including plain `Opt<string>` names; the four that worked were the two written as bare primitives and the two written `Date & Opt<Date>`, where the intersection happens to expose a type the classifier already knew.
28
+
29
+ That is not a cosmetic gap. `unknown` is indistinguishable from "the classifier could not tell", so every consumer that reads a kind — operator sets, a number/date gate, a UI picking a filter control from the column's type — has to fall back to permissive for an entity that had in fact declared its types perfectly well.
30
+
31
+ `Opt<T>` and `Hidden<T>` are now unwrapped to their argument, recursively, so `Opt<Hidden<number>>` is a number.
32
+
33
+ Deliberately a short allowlist rather than "unwrap any single-argument reference". `Ref<T>`, `Rel<T>`, `Collection<T>` and `IdentifiedReference<T>` are **not** transparent: their runtime value is a reference or a collection, not a `T`. Unwrapping those would classify a relation as whatever its target's shape happens to be — silently, and only visibly far downstream.
34
+
3
35
  ## 0.22.1
4
36
 
5
37
  ### Patch Changes
package/dist/cli/main.cjs CHANGED
@@ -1369,6 +1369,7 @@ function classifyTypeKeyword(raw) {
1369
1369
  function markNullable(r, nullable) {
1370
1370
  return nullable ? { ...r, nullable: true } : r;
1371
1371
  }
1372
+ var TRANSPARENT_TYPE_BRANDS = /* @__PURE__ */ new Set(["Opt", "Hidden"]);
1372
1373
  function classifyTypeNode(typeNode, sourceFile, project, opts) {
1373
1374
  if (import_ts_morph4.Node.isUnionTypeNode(typeNode)) {
1374
1375
  let nullable = false;
@@ -1427,6 +1428,11 @@ function classifyTypeNode(typeNode, sourceFile, project, opts) {
1427
1428
  const refName = typeNode.getTypeName().getText();
1428
1429
  if (refName === "Date") return { kind: "date" };
1429
1430
  if (refName === "Record" || refName === "Object") return { kind: "json" };
1431
+ const unwrapped = TRANSPARENT_TYPE_BRANDS.has(refName) ? typeNode.getTypeArguments()[0] : void 0;
1432
+ if (unwrapped) {
1433
+ const inner = classifyTypeNode(unwrapped, sourceFile, project, opts);
1434
+ return inner;
1435
+ }
1430
1436
  const typeRef = opts?.resolveRef?.(refName) ?? null;
1431
1437
  const en = resolveEnumValues(refName, sourceFile, project);
1432
1438
  if (en) {
@@ -1497,12 +1503,19 @@ function classifyFromColumnDecorator(prop, sourceFile, project) {
1497
1503
  return { kind: "string" };
1498
1504
  }
1499
1505
  }
1500
- const typeProp = arg.getProperty("type");
1501
- if (typeProp && import_ts_morph4.Node.isPropertyAssignment(typeProp)) {
1506
+ for (const key of ["columnType", "type"]) {
1507
+ const typeProp = arg.getProperty(key);
1508
+ if (!typeProp || !import_ts_morph4.Node.isPropertyAssignment(typeProp)) continue;
1502
1509
  const init = typeProp.getInitializer();
1503
- if (init && import_ts_morph4.Node.isStringLiteral(init)) {
1510
+ if (!init) continue;
1511
+ if (import_ts_morph4.Node.isStringLiteral(init)) {
1504
1512
  const kind = classifyTypeKeyword(init.getLiteralValue());
1505
1513
  if (kind) return { kind };
1514
+ continue;
1515
+ }
1516
+ if (import_ts_morph4.Node.isIdentifier(init)) {
1517
+ const kind = classifyTypeKeyword(init.getText());
1518
+ if (kind) return { kind };
1506
1519
  }
1507
1520
  }
1508
1521
  }
@@ -1514,12 +1527,18 @@ function classifyFromColumnDecorator(prop, sourceFile, project) {
1514
1527
  function classifyFieldType(prop, sourceFile, project) {
1515
1528
  let nullable = prop.hasQuestionToken();
1516
1529
  const typeNode = prop.getTypeNode();
1530
+ const fromDecorator = classifyFromColumnDecorator(prop, sourceFile, project);
1517
1531
  if (typeNode) {
1518
1532
  const r = classifyTypeNode(typeNode, sourceFile, project);
1519
1533
  if (r.nullable) nullable = true;
1520
- if (r.kind !== "unknown") return markNullable(r, nullable);
1534
+ if (r.kind !== "unknown") {
1535
+ const conflicts = fromDecorator !== null && fromDecorator.kind !== "unknown" && fromDecorator.kind !== r.kind;
1536
+ if (conflicts) {
1537
+ return markNullable({ ...fromDecorator, valueKind: r.kind }, nullable);
1538
+ }
1539
+ return markNullable(r, nullable);
1540
+ }
1521
1541
  }
1522
- const fromDecorator = classifyFromColumnDecorator(prop, sourceFile, project);
1523
1542
  if (fromDecorator) {
1524
1543
  return markNullable(fromDecorator, nullable || fromDecorator.nullable === true);
1525
1544
  }
@@ -1527,6 +1546,7 @@ function classifyFieldType(prop, sourceFile, project) {
1527
1546
  }
1528
1547
  function toFilterFieldType(name, r) {
1529
1548
  const ft = { name, kind: r.kind };
1549
+ if (r.valueKind && r.valueKind !== r.kind) ft.valueKind = r.valueKind;
1530
1550
  if (r.enumValues && r.enumValues.length > 0) ft.enumValues = r.enumValues;
1531
1551
  if (r.nullable) ft.nullable = true;
1532
1552
  if (r.numericEnum) ft.numericEnum = true;
@@ -3332,7 +3352,7 @@ function kindToTs(kind, enumValues, numericEnum) {
3332
3352
  }
3333
3353
  function emitFieldTypesLiteral(fts) {
3334
3354
  const entries = fts.map((f) => {
3335
- let t = f.typeRef ? f.typeRef.name : kindToTs(f.kind, f.enumValues, f.numericEnum);
3355
+ let t = f.typeRef ? f.typeRef.name : kindToTs(f.valueKind ?? f.kind, f.enumValues, f.numericEnum);
3336
3356
  if (f.nullable) t = `${t} | null`;
3337
3357
  return `${JSON.stringify(f.name)}: ${t}`;
3338
3358
  });
@@ -5253,7 +5273,7 @@ async function watch(config, onChange, options = {}) {
5253
5273
  }
5254
5274
 
5255
5275
  // src/index.ts
5256
- var VERSION = "0.22.1";
5276
+ var VERSION = "0.24.0";
5257
5277
 
5258
5278
  // src/cli/codegen.ts
5259
5279
  async function runCodegen(opts = {}) {