@smartive/graphql-magic 22.1.0 → 22.2.0-next.1

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,6 +1,10 @@
1
- ## 22.1.0 (2025-11-05)
1
+ ## 22.2.0-next.1 (2025-12-02)
2
2
 
3
- * feat: enhance field validation logic in MigrationGenerator (#379) ([ac0067f](https://github.com/smartive/graphql-magic/commit/ac0067f)), closes [#379](https://github.com/smartive/graphql-magic/issues/379)
4
- * chore(deps): update dependency esbuild to v0.25.12 (#377) ([bc672e9](https://github.com/smartive/graphql-magic/commit/bc672e9)), closes [#377](https://github.com/smartive/graphql-magic/issues/377)
5
- * chore(deps): update dependency eslint to v9.39.0 (#376) ([14232f2](https://github.com/smartive/graphql-magic/commit/14232f2)), closes [#376](https://github.com/smartive/graphql-magic/issues/376)
6
- * chore(deps): update dependency eslint to v9.39.1 (#378) ([fbfbd77](https://github.com/smartive/graphql-magic/commit/fbfbd77)), closes [#378](https://github.com/smartive/graphql-magic/issues/378)
3
+ * feat: generated fields and migration check ([d0a275c](https://github.com/smartive/graphql-magic/commit/d0a275c))
4
+ * chore(deps): update dependency @types/lodash to v4.17.21 (#383) ([b0dd062](https://github.com/smartive/graphql-magic/commit/b0dd062)), closes [#383](https://github.com/smartive/graphql-magic/issues/383)
5
+ * chore(deps): update dependency esbuild to v0.26.0 (#380) ([c3065d6](https://github.com/smartive/graphql-magic/commit/c3065d6)), closes [#380](https://github.com/smartive/graphql-magic/issues/380)
6
+ * chore(deps): update dependency esbuild to v0.27.0 (#381) ([62cca2a](https://github.com/smartive/graphql-magic/commit/62cca2a)), closes [#381](https://github.com/smartive/graphql-magic/issues/381)
7
+ * chore(deps): update dependency prettier to v3.7.1 (#385) ([680ed83](https://github.com/smartive/graphql-magic/commit/680ed83)), closes [#385](https://github.com/smartive/graphql-magic/issues/385)
8
+ * chore(deps): update dependency prettier to v3.7.2 (#386) ([19e819d](https://github.com/smartive/graphql-magic/commit/19e819d)), closes [#386](https://github.com/smartive/graphql-magic/issues/386)
9
+ * chore(deps): update dependency prettier to v3.7.3 (#387) ([73f2af9](https://github.com/smartive/graphql-magic/commit/73f2af9)), closes [#387](https://github.com/smartive/graphql-magic/issues/387)
10
+ * chore(deps): update dependency ts-jest to v29.4.6 (#388) ([31776a8](https://github.com/smartive/graphql-magic/commit/31776a8)), closes [#388](https://github.com/smartive/graphql-magic/issues/388)
package/dist/bin/gqm.cjs CHANGED
@@ -750,6 +750,7 @@ var MigrationGenerator = class {
750
750
  columns = {};
751
751
  uuidUsed;
752
752
  nowUsed;
753
+ needsMigration = false;
753
754
  async generate() {
754
755
  const { writer, schema, models } = this;
755
756
  const enums = (await schema.knex("pg_type").where({ typtype: "e" }).select("typname")).map(({ typname }) => typname);
@@ -848,32 +849,23 @@ var MigrationGenerator = class {
848
849
  up,
849
850
  down
850
851
  );
851
- const existingFields = model.fields.filter((field) => {
852
+ const rawExistingFields = model.fields.filter((field) => {
853
+ if (!field.generateAs) {
854
+ return false;
855
+ }
852
856
  const col = this.getColumn(model.name, field.kind === "relation" ? `${field.name}Id` : field.name);
853
857
  if (!col) {
854
858
  return false;
855
859
  }
856
- if (!field.nonNull && !col.is_nullable || field.nonNull && col.is_nullable) {
860
+ if (col.generation_expression !== field.generateAs) {
857
861
  return true;
858
862
  }
859
- if (!field.kind || field.kind === "primitive") {
860
- if (field.type === "Int") {
861
- if (col.data_type !== "integer") {
862
- return true;
863
- }
864
- }
865
- if (field.type === "Float") {
866
- if (field.double) {
867
- if (col.data_type !== "double precision") {
868
- return true;
869
- }
870
- } else if (col.data_type !== "numeric") {
871
- return true;
872
- }
873
- }
874
- }
875
- return false;
863
+ return this.hasChanged(model, field);
876
864
  });
865
+ if (rawExistingFields.length) {
866
+ this.updateFieldsRaw(model, rawExistingFields, up, down);
867
+ }
868
+ const existingFields = model.fields.filter((field) => !field.generateAs && this.hasChanged(model, field));
877
869
  this.updateFields(model, existingFields, up, down);
878
870
  }
879
871
  if (isUpdatableModel(model)) {
@@ -971,6 +963,9 @@ var MigrationGenerator = class {
971
963
  if (this.nowUsed) {
972
964
  writer.writeLine(`const now = date();`).blankLine();
973
965
  }
966
+ if (up.length || down.length) {
967
+ this.needsMigration = true;
968
+ }
974
969
  this.migration("up", up);
975
970
  this.migration("down", down.reverse());
976
971
  return writer.toString();
@@ -1013,6 +1008,9 @@ var MigrationGenerator = class {
1013
1008
  const postAlter = [];
1014
1009
  for (const field of fields2) {
1015
1010
  alter.push(() => this.column(field, { setNonNull: field.defaultValue !== void 0 }));
1011
+ if (field.generateAs) {
1012
+ continue;
1013
+ }
1016
1014
  if (field.nonNull && field.defaultValue === void 0) {
1017
1015
  updates.push(() => this.writer.write(`${field.name}: 'TODO',`).newLine());
1018
1016
  postAlter.push(() => this.column(field, { alter: true, foreign: false }));
@@ -1036,12 +1034,56 @@ var MigrationGenerator = class {
1036
1034
  });
1037
1035
  down.push(() => {
1038
1036
  this.alterTable(model.name, () => {
1039
- for (const { kind, name: name2 } of fields2) {
1037
+ for (const { kind, name: name2 } of fields2.toReversed()) {
1040
1038
  this.dropColumn(kind === "relation" ? `${name2}Id` : name2);
1041
1039
  }
1042
1040
  });
1043
1041
  });
1044
1042
  }
1043
+ updateFieldsRaw(model, fields2, up, down) {
1044
+ if (!fields2.length) {
1045
+ return;
1046
+ }
1047
+ up.push(() => {
1048
+ this.alterTableRaw(model.name, () => {
1049
+ for (const [index, field] of fields2.entries()) {
1050
+ this.columnRaw(field, { alter: true }, index);
1051
+ }
1052
+ });
1053
+ });
1054
+ down.push(() => {
1055
+ this.alterTableRaw(model.name, () => {
1056
+ for (const [index, field] of fields2.entries()) {
1057
+ this.columnRaw(field, { alter: true }, index);
1058
+ }
1059
+ });
1060
+ });
1061
+ if (isUpdatableModel(model)) {
1062
+ const updatableFields = fields2.filter(isUpdatableField);
1063
+ if (!updatableFields.length) {
1064
+ return;
1065
+ }
1066
+ up.push(() => {
1067
+ this.alterTable(`${model.name}Revision`, () => {
1068
+ for (const [index, field] of updatableFields.entries()) {
1069
+ this.columnRaw(field, { alter: true }, index);
1070
+ }
1071
+ });
1072
+ });
1073
+ down.push(() => {
1074
+ this.alterTable(`${model.name}Revision`, () => {
1075
+ for (const [index, field] of updatableFields.entries()) {
1076
+ this.columnRaw(
1077
+ field,
1078
+ { alter: true },
1079
+ index,
1080
+ summonByName(this.columns[model.name], field.kind === "relation" ? `${field.name}Id` : field.name)
1081
+ );
1082
+ }
1083
+ });
1084
+ });
1085
+ }
1086
+ }
1045
1087
  updateFields(model, fields2, up, down) {
1046
1088
  if (!fields2.length) {
1047
1089
  return;
@@ -1162,6 +1204,11 @@ var MigrationGenerator = class {
1162
1204
  createTable(table, block) {
1163
1205
  return this.writer.write(`await knex.schema.createTable('${table}', (table) => `).inlineBlock(block).write(");").newLine().blankLine();
1164
1206
  }
1207
+ alterTableRaw(table, block) {
1208
+ this.writer.write(`await knex.raw('ALTER TABLE "${table}"`);
1209
+ block();
1210
+ this.writer.write(`');`).newLine().blankLine();
1211
+ }
1165
1212
  alterTable(table, block) {
1166
1213
  return this.writer.write(`await knex.schema.alterTable('${table}', (table) => `).inlineBlock(block).write(");").newLine().blankLine();
1167
1214
  }
@@ -1183,25 +1230,107 @@ var MigrationGenerator = class {
1183
1230
  }
1184
1231
  return value2;
1185
1232
  }
1186
- column({ name: name2, primary, list: list2, ...field }, { setUnique = true, setNonNull = true, alter = false, foreign = true, setDefault = true } = {}, toColumn) {
1187
- const col = (what) => {
1188
- if (what) {
1189
- this.writer.write(what);
1190
- }
1233
+ columnRaw({ name: name2, ...field }, { setNonNull = true, alter = false } = {}, index, toColumn) {
1234
+ const nonNull2 = () => {
1191
1235
  if (setNonNull) {
1192
1236
  if (toColumn) {
1193
1237
  if (toColumn.is_nullable) {
1194
- this.writer.write(`.nullable()`);
1195
- } else {
1196
- this.writer.write(".notNullable()");
1238
+ return false;
1197
1239
  }
1198
- } else {
1199
- if (field.nonNull) {
1200
- this.writer.write(`.notNullable()`);
1240
+ return true;
1241
+ }
1242
+ if (field.nonNull) {
1243
+ return true;
1244
+ }
1245
+ return false;
1246
+ }
1247
+ };
1248
+ const kind = field.kind;
1249
+ if (field.generateAs) {
1250
+ let type = "";
1251
+ switch (kind) {
1252
+ case void 0:
1253
+ case "primitive":
1254
+ switch (field.type) {
1255
+ case "Float":
1256
+ type = `decimal(${field.precision ?? "undefined"}, ${field.scale ?? "undefined"})`;
1257
+ break;
1258
+ default:
1259
+ throw new Error(`Generated columns of kind ${kind} and type ${field.type} are not supported yet.`);
1260
+ }
1261
+ break;
1262
+ default:
1263
+ throw new Error(`Generated columns of kind ${kind} are not supported yet.`);
1264
+ }
1265
+ if (index) {
1266
+ this.writer.write(`,`);
1267
+ }
1268
+ if (alter) {
1269
+ this.writer.write(` ALTER COLUMN "${name2}" TYPE ${type}`);
1270
+ if (setNonNull) {
1271
+ if (nonNull2()) {
1272
+ this.writer.write(`, ALTER COLUMN "${name2}" SET NOT NULL`);
1201
1273
  } else {
1202
- this.writer.write(".nullable()");
1274
+ this.writer.write(`, ALTER COLUMN "${name2}" DROP NOT NULL`);
1275
+ }
1276
+ }
1277
+ this.writer.write(`, ALTER COLUMN "${name2}" SET EXPRESSION AS (${field.generateAs})`);
1278
+ } else {
1279
+ this.writer.write(
1280
+ `${alter ? "ALTER" : "ADD"} COLUMN "${name2}" ${type}${nonNull2() ? " not null" : ""} GENERATED ALWAYS AS (${field.generateAs}) STORED`
1281
+ );
1282
+ }
1283
+ return;
1284
+ }
1285
+ throw new Error(`Only generated columns can be created with columnRaw`);
1286
+ }
1287
+ column({ name: name2, primary, list: list2, ...field }, { setUnique = true, setNonNull = true, alter = false, foreign = true, setDefault = true } = {}, toColumn) {
1288
+ const nonNull2 = () => {
1289
+ if (setNonNull) {
1290
+ if (toColumn) {
1291
+ if (toColumn.is_nullable) {
1292
+ return false;
1203
1293
  }
1294
+ return true;
1204
1295
  }
1296
+ if (field.nonNull) {
1297
+ return true;
1298
+ }
1299
+ return false;
1300
+ }
1301
+ };
1302
+ const kind = field.kind;
1303
+ if (field.generateAs) {
1304
+ let type = "";
1305
+ switch (kind) {
1306
+ case void 0:
1307
+ case "primitive":
1308
+ switch (field.type) {
1309
+ case "Float":
1310
+ type = `decimal(${field.precision ?? "undefined"}, ${field.scale ?? "undefined"})`;
1311
+ break;
1312
+ default:
1313
+ throw new Error(`Generated columns of kind ${kind} and type ${field.type} are not supported yet.`);
1314
+ }
1315
+ break;
1316
+ default:
1317
+ throw new Error(`Generated columns of kind ${kind} are not supported yet.`);
1318
+ }
1319
+ this.writer.write(
1320
+ `table.specificType('${name2}', '${type}${nonNull2() ? " not null" : ""} GENERATED ALWAYS AS (${field.generateAs}) STORED')`
1321
+ );
1322
+ if (alter) {
1323
+ this.writer.write(".alter()");
1324
+ }
1325
+ this.writer.write(";").newLine();
1326
+ return;
1327
+ }
1328
+ const col = (what) => {
1329
+ if (what) {
1330
+ this.writer.write(what);
1331
+ }
1332
+ if (setNonNull) {
1333
+ this.writer.write(nonNull2() ? ".notNullable()" : ".nullable()");
1205
1334
  }
1206
1335
  if (setDefault && field.defaultValue !== void 0) {
1207
1336
  this.writer.write(`.defaultTo(${this.value(field.defaultValue)})`);
@@ -1216,7 +1345,6 @@ var MigrationGenerator = class {
1216
1345
  }
1217
1346
  this.writer.write(";").newLine();
1218
1347
  };
1219
- const kind = field.kind;
1220
1348
  switch (kind) {
1221
1349
  case void 0:
1222
1350
  case "primitive":
@@ -1286,6 +1414,39 @@ var MigrationGenerator = class {
1286
1414
  getColumn(tableName, columnName) {
1287
1415
  return this.columns[tableName].find((col) => col.name === columnName);
1288
1416
  }
1417
+ hasChanged(model, field) {
1418
+ const col = this.getColumn(model.name, field.kind === "relation" ? `${field.name}Id` : field.name);
1419
+ if (!col) {
1420
+ return false;
1421
+ }
1422
+ if (field.generateAs) {
1423
+ if (col.generation_expression !== field.generateAs) {
1424
+ throw new Error(
1425
+ `Column ${col.name} has specific type ${col.generation_expression} but expected ${field.generateAs}`
1426
+ );
1427
+ }
1428
+ }
1429
+ if (!field.nonNull && !col.is_nullable || field.nonNull && col.is_nullable) {
1430
+ return true;
1431
+ }
1432
+ if (!field.kind || field.kind === "primitive") {
1433
+ if (field.type === "Int") {
1434
+ if (col.data_type !== "integer") {
1435
+ return true;
1436
+ }
1437
+ }
1438
+ if (field.type === "Float") {
1439
+ if (field.double) {
1440
+ if (col.data_type !== "double precision") {
1441
+ return true;
1442
+ }
1443
+ } else if (col.data_type !== "numeric") {
1444
+ return true;
1445
+ }
1446
+ }
1447
+ }
1448
+ return false;
1449
+ }
1289
1450
  };
1290
1451
  var getMigrationDate = () => {
1291
1452
  const date = /* @__PURE__ */ new Date();
@@ -2497,6 +2658,21 @@ import_commander.program.command("generate-migration [<name>] [<date>]").descrip
2497
2658
  await db.destroy();
2498
2659
  }
2499
2660
  });
2661
+ import_commander.program.command("check-needs-migration").description("Check if a migration is needed").action(async () => {
2662
+ const knexfile = await parseKnexfile();
2663
+ const db = (0, import_knex.default)(knexfile);
2664
+ try {
2665
+ const models = await parseModels();
2666
+ const mg = new MigrationGenerator(db, models);
2667
+ await mg.generate();
2668
+ if (mg.needsMigration) {
2669
+ console.error("Migration is needed.");
2670
+ process.exit(1);
2671
+ }
2672
+ } finally {
2673
+ await db.destroy();
2674
+ }
2675
+ });
2500
2676
  import_commander.program.command("*", { noHelp: true }).description("Invalid command").action(() => {
2501
2677
  console.error("Invalid command: %s\nSee --help for a list of available commands.", import_commander.program.args.join(" "));
2502
2678
  process.exit(1);
@@ -1114,6 +1114,7 @@ var MigrationGenerator = class {
1114
1114
  columns = {};
1115
1115
  uuidUsed;
1116
1116
  nowUsed;
1117
+ needsMigration = false;
1117
1118
  async generate() {
1118
1119
  const { writer, schema, models } = this;
1119
1120
  const enums = (await schema.knex("pg_type").where({ typtype: "e" }).select("typname")).map(({ typname }) => typname);
@@ -1212,32 +1213,23 @@ var MigrationGenerator = class {
1212
1213
  up,
1213
1214
  down
1214
1215
  );
1215
- const existingFields = model.fields.filter((field) => {
1216
+ const rawExistingFields = model.fields.filter((field) => {
1217
+ if (!field.generateAs) {
1218
+ return false;
1219
+ }
1216
1220
  const col = this.getColumn(model.name, field.kind === "relation" ? `${field.name}Id` : field.name);
1217
1221
  if (!col) {
1218
1222
  return false;
1219
1223
  }
1220
- if (!field.nonNull && !col.is_nullable || field.nonNull && col.is_nullable) {
1224
+ if (col.generation_expression !== field.generateAs) {
1221
1225
  return true;
1222
1226
  }
1223
- if (!field.kind || field.kind === "primitive") {
1224
- if (field.type === "Int") {
1225
- if (col.data_type !== "integer") {
1226
- return true;
1227
- }
1228
- }
1229
- if (field.type === "Float") {
1230
- if (field.double) {
1231
- if (col.data_type !== "double precision") {
1232
- return true;
1233
- }
1234
- } else if (col.data_type !== "numeric") {
1235
- return true;
1236
- }
1237
- }
1238
- }
1239
- return false;
1227
+ return this.hasChanged(model, field);
1240
1228
  });
1229
+ if (rawExistingFields.length) {
1230
+ this.updateFieldsRaw(model, rawExistingFields, up, down);
1231
+ }
1232
+ const existingFields = model.fields.filter((field) => !field.generateAs && this.hasChanged(model, field));
1241
1233
  this.updateFields(model, existingFields, up, down);
1242
1234
  }
1243
1235
  if (isUpdatableModel(model)) {
@@ -1335,6 +1327,9 @@ var MigrationGenerator = class {
1335
1327
  if (this.nowUsed) {
1336
1328
  writer.writeLine(`const now = date();`).blankLine();
1337
1329
  }
1330
+ if (up.length || down.length) {
1331
+ this.needsMigration = true;
1332
+ }
1338
1333
  this.migration("up", up);
1339
1334
  this.migration("down", down.reverse());
1340
1335
  return writer.toString();
@@ -1377,6 +1372,9 @@ var MigrationGenerator = class {
1377
1372
  const postAlter = [];
1378
1373
  for (const field of fields2) {
1379
1374
  alter.push(() => this.column(field, { setNonNull: field.defaultValue !== void 0 }));
1375
+ if (field.generateAs) {
1376
+ continue;
1377
+ }
1380
1378
  if (field.nonNull && field.defaultValue === void 0) {
1381
1379
  updates.push(() => this.writer.write(`${field.name}: 'TODO',`).newLine());
1382
1380
  postAlter.push(() => this.column(field, { alter: true, foreign: false }));
@@ -1400,12 +1398,56 @@ var MigrationGenerator = class {
1400
1398
  });
1401
1399
  down.push(() => {
1402
1400
  this.alterTable(model.name, () => {
1403
- for (const { kind, name: name2 } of fields2) {
1401
+ for (const { kind, name: name2 } of fields2.toReversed()) {
1404
1402
  this.dropColumn(kind === "relation" ? `${name2}Id` : name2);
1405
1403
  }
1406
1404
  });
1407
1405
  });
1408
1406
  }
1407
+ updateFieldsRaw(model, fields2, up, down) {
1408
+ if (!fields2.length) {
1409
+ return;
1410
+ }
1411
+ up.push(() => {
1412
+ this.alterTableRaw(model.name, () => {
1413
+ for (const [index, field] of fields2.entries()) {
1414
+ this.columnRaw(field, { alter: true }, index);
1415
+ }
1416
+ });
1417
+ });
1418
+ down.push(() => {
1419
+ this.alterTableRaw(model.name, () => {
1420
+ for (const [index, field] of fields2.entries()) {
1421
+ this.columnRaw(field, { alter: true }, index);
1422
+ }
1423
+ });
1424
+ });
1425
+ if (isUpdatableModel(model)) {
1426
+ const updatableFields = fields2.filter(isUpdatableField);
1427
+ if (!updatableFields.length) {
1428
+ return;
1429
+ }
1430
+ up.push(() => {
1431
+ this.alterTable(`${model.name}Revision`, () => {
1432
+ for (const [index, field] of updatableFields.entries()) {
1433
+ this.columnRaw(field, { alter: true }, index);
1434
+ }
1435
+ });
1436
+ });
1437
+ down.push(() => {
1438
+ this.alterTable(`${model.name}Revision`, () => {
1439
+ for (const [index, field] of updatableFields.entries()) {
1440
+ this.columnRaw(
1441
+ field,
1442
+ { alter: true },
1443
+ index,
1444
+ summonByName(this.columns[model.name], field.kind === "relation" ? `${field.name}Id` : field.name)
1445
+ );
1446
+ }
1447
+ });
1448
+ });
1449
+ }
1450
+ }
1409
1451
  updateFields(model, fields2, up, down) {
1410
1452
  if (!fields2.length) {
1411
1453
  return;
@@ -1526,6 +1568,11 @@ var MigrationGenerator = class {
1526
1568
  createTable(table, block) {
1527
1569
  return this.writer.write(`await knex.schema.createTable('${table}', (table) => `).inlineBlock(block).write(");").newLine().blankLine();
1528
1570
  }
1571
+ alterTableRaw(table, block) {
1572
+ this.writer.write(`await knex.raw('ALTER TABLE "${table}"`);
1573
+ block();
1574
+ this.writer.write(`');`).newLine().blankLine();
1575
+ }
1529
1576
  alterTable(table, block) {
1530
1577
  return this.writer.write(`await knex.schema.alterTable('${table}', (table) => `).inlineBlock(block).write(");").newLine().blankLine();
1531
1578
  }
@@ -1547,25 +1594,107 @@ var MigrationGenerator = class {
1547
1594
  }
1548
1595
  return value2;
1549
1596
  }
1550
- column({ name: name2, primary, list: list2, ...field }, { setUnique = true, setNonNull = true, alter = false, foreign = true, setDefault = true } = {}, toColumn) {
1551
- const col = (what) => {
1552
- if (what) {
1553
- this.writer.write(what);
1554
- }
1597
+ columnRaw({ name: name2, ...field }, { setNonNull = true, alter = false } = {}, index, toColumn) {
1598
+ const nonNull2 = () => {
1555
1599
  if (setNonNull) {
1556
1600
  if (toColumn) {
1557
1601
  if (toColumn.is_nullable) {
1558
- this.writer.write(`.nullable()`);
1559
- } else {
1560
- this.writer.write(".notNullable()");
1602
+ return false;
1561
1603
  }
1562
- } else {
1563
- if (field.nonNull) {
1564
- this.writer.write(`.notNullable()`);
1604
+ return true;
1605
+ }
1606
+ if (field.nonNull) {
1607
+ return true;
1608
+ }
1609
+ return false;
1610
+ }
1611
+ };
1612
+ const kind = field.kind;
1613
+ if (field.generateAs) {
1614
+ let type = "";
1615
+ switch (kind) {
1616
+ case void 0:
1617
+ case "primitive":
1618
+ switch (field.type) {
1619
+ case "Float":
1620
+ type = `decimal(${field.precision ?? "undefined"}, ${field.scale ?? "undefined"})`;
1621
+ break;
1622
+ default:
1623
+ throw new Error(`Generated columns of kind ${kind} and type ${field.type} are not supported yet.`);
1624
+ }
1625
+ break;
1626
+ default:
1627
+ throw new Error(`Generated columns of kind ${kind} are not supported yet.`);
1628
+ }
1629
+ if (index) {
1630
+ this.writer.write(`,`);
1631
+ }
1632
+ if (alter) {
1633
+ this.writer.write(` ALTER COLUMN "${name2}" TYPE ${type}`);
1634
+ if (setNonNull) {
1635
+ if (nonNull2()) {
1636
+ this.writer.write(`, ALTER COLUMN "${name2}" SET NOT NULL`);
1565
1637
  } else {
1566
- this.writer.write(".nullable()");
1638
+ this.writer.write(`, ALTER COLUMN "${name2}" DROP NOT NULL`);
1567
1639
  }
1568
1640
  }
1641
+ this.writer.write(`, ALTER COLUMN "${name2}" SET EXPRESSION AS (${field.generateAs})`);
1642
+ } else {
1643
+ this.writer.write(
1644
+ `${alter ? "ALTER" : "ADD"} COLUMN "${name2}" ${type}${nonNull2() ? " not null" : ""} GENERATED ALWAYS AS (${field.generateAs}) STORED`
1645
+ );
1646
+ }
1647
+ return;
1648
+ }
1649
+ throw new Error(`Only generated columns can be created with columnRaw`);
1650
+ }
1651
+ column({ name: name2, primary, list: list2, ...field }, { setUnique = true, setNonNull = true, alter = false, foreign = true, setDefault = true } = {}, toColumn) {
1652
+ const nonNull2 = () => {
1653
+ if (setNonNull) {
1654
+ if (toColumn) {
1655
+ if (toColumn.is_nullable) {
1656
+ return false;
1657
+ }
1658
+ return true;
1659
+ }
1660
+ if (field.nonNull) {
1661
+ return true;
1662
+ }
1663
+ return false;
1664
+ }
1665
+ };
1666
+ const kind = field.kind;
1667
+ if (field.generateAs) {
1668
+ let type = "";
1669
+ switch (kind) {
1670
+ case void 0:
1671
+ case "primitive":
1672
+ switch (field.type) {
1673
+ case "Float":
1674
+ type = `decimal(${field.precision ?? "undefined"}, ${field.scale ?? "undefined"})`;
1675
+ break;
1676
+ default:
1677
+ throw new Error(`Generated columns of kind ${kind} and type ${field.type} are not supported yet.`);
1678
+ }
1679
+ break;
1680
+ default:
1681
+ throw new Error(`Generated columns of kind ${kind} are not supported yet.`);
1682
+ }
1683
+ this.writer.write(
1684
+ `table.specificType('${name2}', '${type}${nonNull2() ? " not null" : ""} GENERATED ALWAYS AS (${field.generateAs}) STORED')`
1685
+ );
1686
+ if (alter) {
1687
+ this.writer.write(".alter()");
1688
+ }
1689
+ this.writer.write(";").newLine();
1690
+ return;
1691
+ }
1692
+ const col = (what) => {
1693
+ if (what) {
1694
+ this.writer.write(what);
1695
+ }
1696
+ if (setNonNull) {
1697
+ this.writer.write(nonNull2() ? ".notNullable()" : ".nullable()");
1569
1698
  }
1570
1699
  if (setDefault && field.defaultValue !== void 0) {
1571
1700
  this.writer.write(`.defaultTo(${this.value(field.defaultValue)})`);
@@ -1580,7 +1709,6 @@ var MigrationGenerator = class {
1580
1709
  }
1581
1710
  this.writer.write(";").newLine();
1582
1711
  };
1583
- const kind = field.kind;
1584
1712
  switch (kind) {
1585
1713
  case void 0:
1586
1714
  case "primitive":
@@ -1650,6 +1778,39 @@ var MigrationGenerator = class {
1650
1778
  getColumn(tableName, columnName) {
1651
1779
  return this.columns[tableName].find((col) => col.name === columnName);
1652
1780
  }
1781
+ hasChanged(model, field) {
1782
+ const col = this.getColumn(model.name, field.kind === "relation" ? `${field.name}Id` : field.name);
1783
+ if (!col) {
1784
+ return false;
1785
+ }
1786
+ if (field.generateAs) {
1787
+ if (col.generation_expression !== field.generateAs) {
1788
+ throw new Error(
1789
+ `Column ${col.name} has specific type ${col.generation_expression} but expected ${field.generateAs}`
1790
+ );
1791
+ }
1792
+ }
1793
+ if (!field.nonNull && !col.is_nullable || field.nonNull && col.is_nullable) {
1794
+ return true;
1795
+ }
1796
+ if (!field.kind || field.kind === "primitive") {
1797
+ if (field.type === "Int") {
1798
+ if (col.data_type !== "integer") {
1799
+ return true;
1800
+ }
1801
+ }
1802
+ if (field.type === "Float") {
1803
+ if (field.double) {
1804
+ if (col.data_type !== "double precision") {
1805
+ return true;
1806
+ }
1807
+ } else if (col.data_type !== "numeric") {
1808
+ return true;
1809
+ }
1810
+ }
1811
+ }
1812
+ return false;
1813
+ }
1653
1814
  };
1654
1815
  var getMigrationDate = () => {
1655
1816
  const date = /* @__PURE__ */ new Date();
@@ -7,23 +7,28 @@ export declare class MigrationGenerator {
7
7
  private columns;
8
8
  private uuidUsed?;
9
9
  private nowUsed?;
10
+ needsMigration: boolean;
10
11
  constructor(knex: Knex, models: Models);
11
12
  generate(): Promise<string>;
12
13
  private renameFields;
13
14
  private createFields;
15
+ private updateFieldsRaw;
14
16
  private updateFields;
15
17
  private createRevisionTable;
16
18
  private createRevisionFields;
17
19
  private createEnums;
18
20
  private migration;
19
21
  private createTable;
22
+ private alterTableRaw;
20
23
  private alterTable;
21
24
  private dropColumn;
22
25
  private dropTable;
23
26
  private renameTable;
24
27
  private renameColumn;
25
28
  private value;
29
+ private columnRaw;
26
30
  private column;
27
31
  private getColumn;
32
+ private hasChanged;
28
33
  }
29
34
  export declare const getMigrationDate: () => string;