arkormx 2.4.6 → 2.4.7

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.
@@ -1046,18 +1046,21 @@ var TableBuilder = class {
1046
1046
  this.dropColumnNames = [];
1047
1047
  this.indexes = [];
1048
1048
  this.foreignKeys = [];
1049
+ this.compositeUniqueConstraints = [];
1049
1050
  }
1050
- /**
1051
- * Defines a primary key column in the table.
1052
- *
1053
- * @param columnNameOrOptions
1054
- * @param options
1055
- * @returns
1056
- */
1057
1051
  primary(columnNameOrOptions, options) {
1052
+ if (Array.isArray(columnNameOrOptions)) {
1053
+ const columns = this.normalizeCompositeColumns(columnNameOrOptions, "primary key");
1054
+ if (this.compositePrimaryKey) throw new Error("A composite primary key has already been defined for this table.");
1055
+ this.compositePrimaryKey = {
1056
+ columns,
1057
+ ...typeof options === "string" && options.trim() ? { name: options.trim() } : {}
1058
+ };
1059
+ return this;
1060
+ }
1058
1061
  const config = typeof columnNameOrOptions === "string" ? {
1059
1062
  columnName: columnNameOrOptions,
1060
- ...options ?? {}
1063
+ ...typeof options === "object" ? options : {}
1061
1064
  } : columnNameOrOptions ?? {};
1062
1065
  const column = this.resolveColumn(config.columnName);
1063
1066
  column.primary = true;
@@ -1157,15 +1160,19 @@ var TableBuilder = class {
1157
1160
  float(name, options = {}) {
1158
1161
  return this.column(name, "float", options);
1159
1162
  }
1160
- /**
1161
- * Marks a column as unique in the table.
1162
- *
1163
- * @param name Optional explicit column name.
1164
- * When omitted, applies to the latest defined column.
1165
- * @returns The current TableBuilder instance for chaining.
1166
- */
1167
- unique(name) {
1168
- const column = this.resolveColumn(name);
1163
+ unique(columnsOrName, name) {
1164
+ if (Array.isArray(columnsOrName)) {
1165
+ const columns = this.normalizeCompositeColumns(columnsOrName, "unique constraint");
1166
+ const normalizedName = name?.trim();
1167
+ if (this.compositeUniqueConstraints.find((constraint) => constraint.columns.length === columns.length && constraint.columns.every((column, index) => column === columns[index]))) throw new Error(`A unique constraint for columns [${columns.join(", ")}] has already been defined for this table.`);
1168
+ if (normalizedName && this.compositeUniqueConstraints.some((constraint) => constraint.name === normalizedName)) throw new Error(`A unique constraint named [${normalizedName}] has already been defined for this table.`);
1169
+ this.compositeUniqueConstraints.push({
1170
+ columns,
1171
+ ...normalizedName ? { name: normalizedName } : {}
1172
+ });
1173
+ return this;
1174
+ }
1175
+ const column = this.resolveColumn(columnsOrName);
1169
1176
  column.unique = true;
1170
1177
  return this;
1171
1178
  }
@@ -1407,6 +1414,24 @@ var TableBuilder = class {
1407
1414
  return this.foreignKeys.map((foreignKey) => ({ ...foreignKey }));
1408
1415
  }
1409
1416
  /**
1417
+ * Returns a copy of the table-level composite primary key.
1418
+ */
1419
+ getPrimaryKey() {
1420
+ return this.compositePrimaryKey ? {
1421
+ ...this.compositePrimaryKey,
1422
+ columns: [...this.compositePrimaryKey.columns]
1423
+ } : void 0;
1424
+ }
1425
+ /**
1426
+ * Returns copies of table-level composite unique constraints.
1427
+ */
1428
+ getUniqueConstraints() {
1429
+ return this.compositeUniqueConstraints.map((constraint) => ({
1430
+ ...constraint,
1431
+ columns: [...constraint.columns]
1432
+ }));
1433
+ }
1434
+ /**
1410
1435
  * Defines a column in the table with the given name.
1411
1436
  *
1412
1437
  * @param name The name of the column.
@@ -1448,6 +1473,13 @@ var TableBuilder = class {
1448
1473
  if (!column) throw new Error(`Column [${targetName}] was not found in the table definition.`);
1449
1474
  return column;
1450
1475
  }
1476
+ normalizeCompositeColumns(columns, label) {
1477
+ const normalized = columns.map((column) => column.trim());
1478
+ if (normalized.length < 2) throw new Error(`A composite ${label} must contain at least two columns.`);
1479
+ if (normalized.some((column) => !column)) throw new Error(`Composite ${label} columns must be non-empty strings.`);
1480
+ if (new Set(normalized).size !== normalized.length) throw new Error(`Composite ${label} columns must be unique.`);
1481
+ return normalized;
1482
+ }
1451
1483
  };
1452
1484
 
1453
1485
  //#endregion
@@ -1473,12 +1505,17 @@ var SchemaBuilder = class {
1473
1505
  createTable(table, callback) {
1474
1506
  const builder = new TableBuilder();
1475
1507
  callback(builder);
1508
+ const primaryKey = builder.getPrimaryKey();
1509
+ this.validateCompositePrimaryKey(table, primaryKey, builder.getColumns(), true);
1510
+ this.validateCompositeUniqueConstraints(table, builder.getUniqueConstraints(), builder.getColumns(), true);
1476
1511
  this.operations.push({
1477
1512
  type: "createTable",
1478
1513
  table,
1479
1514
  columns: builder.getColumns(),
1480
1515
  indexes: builder.getIndexes(),
1481
- foreignKeys: builder.getForeignKeys()
1516
+ foreignKeys: builder.getForeignKeys(),
1517
+ primaryKey,
1518
+ uniqueConstraints: builder.getUniqueConstraints()
1482
1519
  });
1483
1520
  return this;
1484
1521
  }
@@ -1492,13 +1529,18 @@ var SchemaBuilder = class {
1492
1529
  alterTable(table, callback) {
1493
1530
  const builder = new TableBuilder();
1494
1531
  callback(builder);
1532
+ const primaryKey = builder.getPrimaryKey();
1533
+ this.validateCompositePrimaryKey(table, primaryKey, builder.getColumns(), false);
1534
+ this.validateCompositeUniqueConstraints(table, builder.getUniqueConstraints(), builder.getColumns(), false);
1495
1535
  this.operations.push({
1496
1536
  type: "alterTable",
1497
1537
  table,
1498
1538
  addColumns: builder.getColumns(),
1499
1539
  dropColumns: builder.getDropColumns(),
1500
1540
  addIndexes: builder.getIndexes(),
1501
- addForeignKeys: builder.getForeignKeys()
1541
+ addForeignKeys: builder.getForeignKeys(),
1542
+ addPrimaryKey: primaryKey,
1543
+ addUniqueConstraints: builder.getUniqueConstraints()
1502
1544
  });
1503
1545
  return this;
1504
1546
  }
@@ -1532,7 +1574,15 @@ var SchemaBuilder = class {
1532
1574
  ...index,
1533
1575
  columns: [...index.columns]
1534
1576
  })),
1535
- foreignKeys: operation.foreignKeys.map((foreignKey) => ({ ...foreignKey }))
1577
+ foreignKeys: operation.foreignKeys.map((foreignKey) => ({ ...foreignKey })),
1578
+ primaryKey: operation.primaryKey ? {
1579
+ ...operation.primaryKey,
1580
+ columns: [...operation.primaryKey.columns]
1581
+ } : void 0,
1582
+ uniqueConstraints: operation.uniqueConstraints?.map((constraint) => ({
1583
+ ...constraint,
1584
+ columns: [...constraint.columns]
1585
+ }))
1536
1586
  };
1537
1587
  if (operation.type === "alterTable") return {
1538
1588
  ...operation,
@@ -1545,11 +1595,37 @@ var SchemaBuilder = class {
1545
1595
  ...index,
1546
1596
  columns: [...index.columns]
1547
1597
  })),
1548
- addForeignKeys: operation.addForeignKeys.map((foreignKey) => ({ ...foreignKey }))
1598
+ addForeignKeys: operation.addForeignKeys.map((foreignKey) => ({ ...foreignKey })),
1599
+ addPrimaryKey: operation.addPrimaryKey ? {
1600
+ ...operation.addPrimaryKey,
1601
+ columns: [...operation.addPrimaryKey.columns]
1602
+ } : void 0,
1603
+ addUniqueConstraints: operation.addUniqueConstraints?.map((constraint) => ({
1604
+ ...constraint,
1605
+ columns: [...constraint.columns]
1606
+ }))
1549
1607
  };
1550
1608
  return { ...operation };
1551
1609
  });
1552
1610
  }
1611
+ validateCompositePrimaryKey(table, primaryKey, columns, requireColumns) {
1612
+ if (!primaryKey) return;
1613
+ if (columns.some((column) => column.primary)) throw new Error(`Table [${table}] cannot combine column primary keys with a composite primary key.`);
1614
+ if (!requireColumns) return;
1615
+ primaryKey.columns.forEach((columnName) => {
1616
+ const column = columns.find((candidate) => candidate.name === columnName);
1617
+ if (!column) throw new Error(`Composite primary key column [${columnName}] was not found on table [${table}].`);
1618
+ if (column.nullable) throw new Error(`Composite primary key column [${columnName}] on table [${table}] cannot be nullable.`);
1619
+ });
1620
+ }
1621
+ validateCompositeUniqueConstraints(table, constraints, columns, requireColumns) {
1622
+ if (!requireColumns) return;
1623
+ constraints.forEach((constraint) => {
1624
+ constraint.columns.forEach((columnName) => {
1625
+ if (!columns.some((column) => column.name === columnName)) throw new Error(`Composite unique constraint column [${columnName}] was not found on table [${table}].`);
1626
+ });
1627
+ });
1628
+ }
1553
1629
  };
1554
1630
 
1555
1631
  //#endregion
@@ -1773,6 +1849,24 @@ const buildIndexLine = (index) => {
1773
1849
  return ` @@index([${index.columns.join(", ")}]${typeof index.name === "string" && index.name.trim().length > 0 ? `, name: "${index.name.replace(/"/g, "\\\"")}"` : ""})`;
1774
1850
  };
1775
1851
  /**
1852
+ * Build a Prisma model-level composite primary key definition.
1853
+ *
1854
+ * @param primaryKey
1855
+ * @returns
1856
+ */
1857
+ const buildPrimaryKeyLine = (primaryKey) => {
1858
+ return ` @@id([${primaryKey.columns.join(", ")}]${typeof primaryKey.name === "string" && primaryKey.name.trim().length > 0 ? `, name: "${primaryKey.name.replace(/"/g, "\\\"")}"` : ""})`;
1859
+ };
1860
+ /**
1861
+ * Build a Prisma model-level composite unique constraint definition.
1862
+ *
1863
+ * @param constraint
1864
+ * @returns
1865
+ */
1866
+ const buildUniqueConstraintLine = (constraint) => {
1867
+ return ` @@unique([${constraint.columns.join(", ")}]${typeof constraint.name === "string" && constraint.name.trim().length > 0 ? `, name: "${constraint.name.replace(/"/g, "\\\"")}"` : ""})`;
1868
+ };
1869
+ /**
1776
1870
  * Derive a relation field name from a foreign key column name by applying
1777
1871
  * common conventions, such as removing "Id" suffixes and converting to camelCase.
1778
1872
  *
@@ -1921,7 +2015,14 @@ const buildModelBlock = (operation) => {
1921
2015
  const mapped = operation.table !== modelName.toLowerCase();
1922
2016
  const fields = operation.columns.map(buildFieldLine);
1923
2017
  const relations = (operation.foreignKeys ?? []).map((foreignKey) => buildRelationLine(modelName, foreignKey, operation.columns));
1924
- const metadata = [...(operation.indexes ?? []).map(buildIndexLine), ...mapped ? [` @@map("${(0, _h3ravel_support.str)(operation.table).snake()}")`] : []];
2018
+ const indexes = (operation.indexes ?? []).map(buildIndexLine);
2019
+ const uniqueConstraints = (operation.uniqueConstraints ?? []).map(buildUniqueConstraintLine);
2020
+ const metadata = [
2021
+ ...operation.primaryKey ? [buildPrimaryKeyLine(operation.primaryKey)] : [],
2022
+ ...uniqueConstraints,
2023
+ ...indexes,
2024
+ ...mapped ? [` @@map("${(0, _h3ravel_support.str)(operation.table).snake()}")`] : []
2025
+ ];
1925
2026
  return `model ${modelName} {\n${(metadata.length > 0 ? [
1926
2027
  ...fields,
1927
2028
  ...relations,
@@ -2018,6 +2119,18 @@ const applyAlterTableOperation = (schema, operation) => {
2018
2119
  const insertIndex = Math.max(1, bodyLines.length - 1);
2019
2120
  bodyLines.splice(insertIndex, 0, indexLine);
2020
2121
  });
2122
+ if (operation.addPrimaryKey) {
2123
+ const primaryKeyLine = buildPrimaryKeyLine(operation.addPrimaryKey);
2124
+ if (bodyLines.some((line) => line.includes(" @id") || line.trim().startsWith("@@id("))) throw new ArkormException(`Prisma model for table [${operation.table}] already defines a primary key.`);
2125
+ const insertIndex = Math.max(1, bodyLines.length - 1);
2126
+ bodyLines.splice(insertIndex, 0, primaryKeyLine);
2127
+ }
2128
+ for (const constraint of operation.addUniqueConstraints ?? []) {
2129
+ const constraintLine = buildUniqueConstraintLine(constraint);
2130
+ if (bodyLines.some((line) => line.trim() === constraintLine.trim())) continue;
2131
+ const insertIndex = Math.max(1, bodyLines.length - 1);
2132
+ bodyLines.splice(insertIndex, 0, constraintLine);
2133
+ }
2021
2134
  for (const foreignKey of operation.addForeignKeys ?? []) {
2022
2135
  const relationLine = buildRelationLine(model.modelName, foreignKey, operation.addColumns);
2023
2136
  const relationRegex = new RegExp(`^\\s*${escapeRegex(foreignKey.fieldAlias?.trim() || deriveRelationFieldName(foreignKey.column))}\\s+`);
@@ -5182,12 +5295,24 @@ Object.defineProperty(exports, 'buildModelBlock', {
5182
5295
  return buildModelBlock;
5183
5296
  }
5184
5297
  });
5298
+ Object.defineProperty(exports, 'buildPrimaryKeyLine', {
5299
+ enumerable: true,
5300
+ get: function () {
5301
+ return buildPrimaryKeyLine;
5302
+ }
5303
+ });
5185
5304
  Object.defineProperty(exports, 'buildRelationLine', {
5186
5305
  enumerable: true,
5187
5306
  get: function () {
5188
5307
  return buildRelationLine;
5189
5308
  }
5190
5309
  });
5310
+ Object.defineProperty(exports, 'buildUniqueConstraintLine', {
5311
+ enumerable: true,
5312
+ get: function () {
5313
+ return buildUniqueConstraintLine;
5314
+ }
5315
+ });
5191
5316
  Object.defineProperty(exports, 'computeMigrationChecksum', {
5192
5317
  enumerable: true,
5193
5318
  get: function () {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arkormx",
3
- "version": "2.4.6",
3
+ "version": "2.4.7",
4
4
  "description": "Modern TypeScript-first ORM for Node.js.",
5
5
  "keywords": [
6
6
  "orm",