arkormx 1.2.0 → 1.2.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/dist/cli.mjs CHANGED
@@ -549,7 +549,7 @@ var TableBuilder = class {
549
549
  */
550
550
  foreign(column) {
551
551
  const columnName = this.resolveColumn(column).name;
552
- return this.foreignKey(columnName + (column ? "" : "Id"));
552
+ return this.foreignKey(column ?? columnName);
553
553
  }
554
554
  /**
555
555
  * Returns a deep copy of the defined columns for the table.
@@ -967,24 +967,23 @@ const deriveRelationFieldName = (columnName) => {
967
967
  if (trimmed.endsWith("_id") && trimmed.length > 3) return trimmed.slice(0, -3).replace(/_([a-zA-Z0-9])/g, (_, letter) => letter.toUpperCase());
968
968
  return `${trimmed.charAt(0).toLowerCase()}${trimmed.slice(1)}`;
969
969
  };
970
- const pascalWords = (value) => {
971
- return value.match(/[A-Z][a-z0-9]*/g) ?? [value];
972
- };
973
970
  /**
974
- * Derive a relation name for the inverse side of a relation based on the
971
+ * Derive a relation name for both sides of a relation based on the
975
972
  * source and target model names, using an explicit alias if provided or a
976
- * convention of combining the target model name with the last segment of
977
- * the source model name.
973
+ * convention of combining the full source model name with the target model name.
978
974
  *
979
975
  * @param sourceModelName The name of the source model in the relation.
980
976
  * @param targetModelName The name of the target model in the relation.
981
- * @param explicitAlias An optional explicit alias for the inverse relation.
982
- * @returns The derived or explicit inverse relation alias.
977
+ * @param explicitAlias An optional explicit alias for the relation.
978
+ * @returns The derived or explicit relation alias.
983
979
  */
984
- const deriveInverseRelationAlias = (sourceModelName, targetModelName, explicitAlias) => {
980
+ const deriveRelationAlias = (sourceModelName, targetModelName, explicitAlias) => {
985
981
  if (explicitAlias && explicitAlias.trim().length > 0) return explicitAlias.trim();
986
- const sourceWords = pascalWords(sourceModelName);
987
- return `${sourceWords[sourceWords.length - 1] ?? sourceModelName}${targetModelName}`;
982
+ return [sourceModelName, targetModelName].sort((left, right) => left.localeCompare(right)).join("");
983
+ };
984
+ const deriveSingularFieldName = (modelName) => {
985
+ if (!modelName) return "item";
986
+ return `${modelName.charAt(0).toLowerCase()}${modelName.slice(1)}`;
988
987
  };
989
988
  const deriveCollectionFieldName = (modelName) => {
990
989
  if (!modelName) return "items";
@@ -992,6 +991,12 @@ const deriveCollectionFieldName = (modelName) => {
992
991
  if (camel.endsWith("s")) return `${camel}es`;
993
992
  return `${camel}s`;
994
993
  };
994
+ const resolveForeignKeyColumn = (columns, foreignKey) => {
995
+ return columns.find((column) => column.name === foreignKey.column);
996
+ };
997
+ const isOneToOneForeignKey = (column) => {
998
+ return Boolean(column?.unique || column?.primary);
999
+ };
995
1000
  /**
996
1001
  * Format a SchemaForeignKeyAction value as a Prisma onDelete action string.
997
1002
  *
@@ -1012,15 +1017,16 @@ const formatRelationAction = (action) => {
1012
1017
  * @param foreignKey The foreign key definition to convert to a relation line.
1013
1018
  * @returns The corresponding Prisma schema line for the relation field.
1014
1019
  */
1015
- const buildRelationLine = (foreignKey) => {
1020
+ const buildRelationLine = (sourceModelName, foreignKey, columns = []) => {
1016
1021
  if (!foreignKey.referencesTable.trim()) throw new ArkormException(`Foreign key [${foreignKey.column}] must define a referenced table.`);
1017
1022
  if (!foreignKey.referencesColumn.trim()) throw new ArkormException(`Foreign key [${foreignKey.column}] must define a referenced column.`);
1023
+ const sourceColumn = resolveForeignKeyColumn(columns, foreignKey);
1018
1024
  const fieldName = foreignKey.fieldAlias?.trim() || deriveRelationFieldName(foreignKey.column);
1019
1025
  const targetModel = toModelName(foreignKey.referencesTable);
1020
- const relationName = foreignKey.relationAlias?.trim();
1021
- const relationPrefix = relationName ? `@relation("${relationName.replace(/"/g, "\\\"")}", ` : "@relation(";
1026
+ const relationName = deriveRelationAlias(sourceModelName, targetModel, foreignKey.relationAlias?.trim());
1027
+ const optional = sourceColumn?.nullable ? "?" : "";
1022
1028
  const onDelete = foreignKey.onDelete ? `, onDelete: ${formatRelationAction(foreignKey.onDelete)}` : "";
1023
- return ` ${fieldName} ${targetModel} ${relationPrefix}fields: [${foreignKey.column}], references: [${foreignKey.referencesColumn}]${onDelete})`;
1029
+ return ` ${fieldName} ${targetModel}${optional} @relation("${relationName.replace(/"/g, "\\\"")}", fields: [${foreignKey.column}], references: [${foreignKey.referencesColumn}]${onDelete})`;
1024
1030
  };
1025
1031
  /**
1026
1032
  * Build a Prisma relation field line for the inverse side of a relation, based
@@ -1032,8 +1038,11 @@ const buildRelationLine = (foreignKey) => {
1032
1038
  * @param foreignKey The foreign key definition for the relation.
1033
1039
  * @returns The Prisma schema line for the inverse relation field.
1034
1040
  */
1035
- const buildInverseRelationLine = (sourceModelName, targetModelName, foreignKey) => {
1036
- return ` ${deriveCollectionFieldName(sourceModelName)} ${sourceModelName}[] @relation("${deriveInverseRelationAlias(sourceModelName, targetModelName, foreignKey.inverseRelationAlias).replace(/"/g, "\\\"")}")`;
1041
+ const buildInverseRelationLine = (sourceModelName, targetModelName, foreignKey, columns = []) => {
1042
+ const sourceColumn = resolveForeignKeyColumn(columns, foreignKey);
1043
+ const fieldName = isOneToOneForeignKey(sourceColumn) ? deriveSingularFieldName(sourceModelName) : deriveCollectionFieldName(sourceModelName);
1044
+ const relationName = deriveRelationAlias(sourceModelName, targetModelName, foreignKey.relationAlias?.trim());
1045
+ return ` ${fieldName} ${isOneToOneForeignKey(sourceColumn) ? `${sourceModelName}?` : `${sourceModelName}[]`} @relation("${relationName.replace(/"/g, "\\\"")}")`;
1037
1046
  };
1038
1047
  /**
1039
1048
  * Inject a line into the body of a Prisma model block if it does not already
@@ -1061,14 +1070,15 @@ const injectLineIntoModelBody = (bodyLines, line, exists) => {
1061
1070
  * @param foreignKeys An array of foreign key definitions to process.
1062
1071
  * @returns The updated Prisma schema string with inverse relations applied.
1063
1072
  */
1064
- const applyInverseRelations = (schema, sourceModelName, foreignKeys) => {
1073
+ const applyInverseRelations = (schema, sourceModelName, foreignKeys, columns = []) => {
1065
1074
  let nextSchema = schema;
1066
1075
  for (const foreignKey of foreignKeys) {
1067
1076
  const targetModel = findModelBlock(nextSchema, foreignKey.referencesTable);
1068
1077
  if (!targetModel) continue;
1069
- const inverseLine = buildInverseRelationLine(sourceModelName, targetModel.modelName, foreignKey);
1078
+ const sourceColumn = resolveForeignKeyColumn(columns, foreignKey);
1079
+ const inverseLine = buildInverseRelationLine(sourceModelName, targetModel.modelName, foreignKey, columns);
1070
1080
  const targetBodyLines = targetModel.block.split("\n");
1071
- const fieldName = deriveCollectionFieldName(sourceModelName);
1081
+ const fieldName = isOneToOneForeignKey(sourceColumn) ? deriveSingularFieldName(sourceModelName) : deriveCollectionFieldName(sourceModelName);
1072
1082
  const fieldRegex = new RegExp(`^\\s*${escapeRegex(fieldName)}\\s+`);
1073
1083
  injectLineIntoModelBody(targetBodyLines, inverseLine, (line) => fieldRegex.test(line));
1074
1084
  const updatedTarget = targetBodyLines.join("\n");
@@ -1087,7 +1097,7 @@ const buildModelBlock = (operation) => {
1087
1097
  const modelName = toModelName(operation.table);
1088
1098
  const mapped = operation.table !== modelName.toLowerCase();
1089
1099
  const fields = operation.columns.map(buildFieldLine);
1090
- const relations = (operation.foreignKeys ?? []).map(buildRelationLine);
1100
+ const relations = (operation.foreignKeys ?? []).map((foreignKey) => buildRelationLine(modelName, foreignKey, operation.columns));
1091
1101
  const metadata = [...(operation.indexes ?? []).map(buildIndexLine), ...mapped ? [` @@map("${str(operation.table).snake()}")`] : []];
1092
1102
  return `model ${modelName} {\n${(metadata.length > 0 ? [
1093
1103
  ...fields,
@@ -1145,7 +1155,7 @@ const applyCreateTableOperation = (schema, operation) => {
1145
1155
  if (findModelBlock(schema, operation.table)) throw new ArkormException(`Prisma model for table [${operation.table}] already exists.`);
1146
1156
  const schemaWithEnums = ensureEnumBlocks(schema, operation.columns);
1147
1157
  const block = buildModelBlock(operation);
1148
- return applyInverseRelations(`${schemaWithEnums.trimEnd()}\n\n${block}\n`, toModelName(operation.table), operation.foreignKeys ?? []);
1158
+ return applyInverseRelations(`${schemaWithEnums.trimEnd()}\n\n${block}\n`, toModelName(operation.table), operation.foreignKeys ?? [], operation.columns);
1149
1159
  };
1150
1160
  /**
1151
1161
  * Apply an alter table operation to a Prisma schema string, modifying the model
@@ -1186,12 +1196,12 @@ const applyAlterTableOperation = (schema, operation) => {
1186
1196
  bodyLines.splice(insertIndex, 0, indexLine);
1187
1197
  });
1188
1198
  for (const foreignKey of operation.addForeignKeys ?? []) {
1189
- const relationLine = buildRelationLine(foreignKey);
1199
+ const relationLine = buildRelationLine(model.modelName, foreignKey, operation.addColumns);
1190
1200
  const relationRegex = new RegExp(`^\\s*${escapeRegex(foreignKey.fieldAlias?.trim() || deriveRelationFieldName(foreignKey.column))}\\s+`);
1191
1201
  injectLineIntoModelBody(bodyLines, relationLine, (line) => relationRegex.test(line));
1192
1202
  }
1193
1203
  block = bodyLines.join("\n");
1194
- return applyInverseRelations(`${schemaWithEnums.slice(0, refreshedModel.start)}${block}${schemaWithEnums.slice(refreshedModel.end)}`, model.modelName, operation.addForeignKeys ?? []);
1204
+ return applyInverseRelations(`${schemaWithEnums.slice(0, refreshedModel.start)}${block}${schemaWithEnums.slice(refreshedModel.end)}`, model.modelName, operation.addForeignKeys ?? [], operation.addColumns);
1195
1205
  };
1196
1206
  /**
1197
1207
  * Apply a drop table operation to a Prisma schema string, removing the model block
package/dist/index.cjs CHANGED
@@ -658,7 +658,7 @@ var TableBuilder = class {
658
658
  */
659
659
  foreign(column) {
660
660
  const columnName = this.resolveColumn(column).name;
661
- return this.foreignKey(columnName + (column ? "" : "Id"));
661
+ return this.foreignKey(column ?? columnName);
662
662
  }
663
663
  /**
664
664
  * Returns a deep copy of the defined columns for the table.
@@ -1076,24 +1076,24 @@ const deriveRelationFieldName = (columnName) => {
1076
1076
  if (trimmed.endsWith("_id") && trimmed.length > 3) return trimmed.slice(0, -3).replace(/_([a-zA-Z0-9])/g, (_, letter) => letter.toUpperCase());
1077
1077
  return `${trimmed.charAt(0).toLowerCase()}${trimmed.slice(1)}`;
1078
1078
  };
1079
- const pascalWords = (value) => {
1080
- return value.match(/[A-Z][a-z0-9]*/g) ?? [value];
1081
- };
1082
1079
  /**
1083
- * Derive a relation name for the inverse side of a relation based on the
1080
+ * Derive a relation name for both sides of a relation based on the
1084
1081
  * source and target model names, using an explicit alias if provided or a
1085
- * convention of combining the target model name with the last segment of
1086
- * the source model name.
1082
+ * convention of combining the full source model name with the target model name.
1087
1083
  *
1088
1084
  * @param sourceModelName The name of the source model in the relation.
1089
1085
  * @param targetModelName The name of the target model in the relation.
1090
- * @param explicitAlias An optional explicit alias for the inverse relation.
1091
- * @returns The derived or explicit inverse relation alias.
1086
+ * @param explicitAlias An optional explicit alias for the relation.
1087
+ * @returns The derived or explicit relation alias.
1092
1088
  */
1093
- const deriveInverseRelationAlias = (sourceModelName, targetModelName, explicitAlias) => {
1089
+ const deriveRelationAlias = (sourceModelName, targetModelName, explicitAlias) => {
1094
1090
  if (explicitAlias && explicitAlias.trim().length > 0) return explicitAlias.trim();
1095
- const sourceWords = pascalWords(sourceModelName);
1096
- return `${sourceWords[sourceWords.length - 1] ?? sourceModelName}${targetModelName}`;
1091
+ return [sourceModelName, targetModelName].sort((left, right) => left.localeCompare(right)).join("");
1092
+ };
1093
+ const deriveInverseRelationAlias = deriveRelationAlias;
1094
+ const deriveSingularFieldName = (modelName) => {
1095
+ if (!modelName) return "item";
1096
+ return `${modelName.charAt(0).toLowerCase()}${modelName.slice(1)}`;
1097
1097
  };
1098
1098
  const deriveCollectionFieldName = (modelName) => {
1099
1099
  if (!modelName) return "items";
@@ -1101,6 +1101,12 @@ const deriveCollectionFieldName = (modelName) => {
1101
1101
  if (camel.endsWith("s")) return `${camel}es`;
1102
1102
  return `${camel}s`;
1103
1103
  };
1104
+ const resolveForeignKeyColumn = (columns, foreignKey) => {
1105
+ return columns.find((column) => column.name === foreignKey.column);
1106
+ };
1107
+ const isOneToOneForeignKey = (column) => {
1108
+ return Boolean(column?.unique || column?.primary);
1109
+ };
1104
1110
  /**
1105
1111
  * Format a SchemaForeignKeyAction value as a Prisma onDelete action string.
1106
1112
  *
@@ -1121,15 +1127,16 @@ const formatRelationAction = (action) => {
1121
1127
  * @param foreignKey The foreign key definition to convert to a relation line.
1122
1128
  * @returns The corresponding Prisma schema line for the relation field.
1123
1129
  */
1124
- const buildRelationLine = (foreignKey) => {
1130
+ const buildRelationLine = (sourceModelName, foreignKey, columns = []) => {
1125
1131
  if (!foreignKey.referencesTable.trim()) throw new ArkormException(`Foreign key [${foreignKey.column}] must define a referenced table.`);
1126
1132
  if (!foreignKey.referencesColumn.trim()) throw new ArkormException(`Foreign key [${foreignKey.column}] must define a referenced column.`);
1133
+ const sourceColumn = resolveForeignKeyColumn(columns, foreignKey);
1127
1134
  const fieldName = foreignKey.fieldAlias?.trim() || deriveRelationFieldName(foreignKey.column);
1128
1135
  const targetModel = toModelName(foreignKey.referencesTable);
1129
- const relationName = foreignKey.relationAlias?.trim();
1130
- const relationPrefix = relationName ? `@relation("${relationName.replace(/"/g, "\\\"")}", ` : "@relation(";
1136
+ const relationName = deriveRelationAlias(sourceModelName, targetModel, foreignKey.relationAlias?.trim());
1137
+ const optional = sourceColumn?.nullable ? "?" : "";
1131
1138
  const onDelete = foreignKey.onDelete ? `, onDelete: ${formatRelationAction(foreignKey.onDelete)}` : "";
1132
- return ` ${fieldName} ${targetModel} ${relationPrefix}fields: [${foreignKey.column}], references: [${foreignKey.referencesColumn}]${onDelete})`;
1139
+ return ` ${fieldName} ${targetModel}${optional} @relation("${relationName.replace(/"/g, "\\\"")}", fields: [${foreignKey.column}], references: [${foreignKey.referencesColumn}]${onDelete})`;
1133
1140
  };
1134
1141
  /**
1135
1142
  * Build a Prisma relation field line for the inverse side of a relation, based
@@ -1141,8 +1148,11 @@ const buildRelationLine = (foreignKey) => {
1141
1148
  * @param foreignKey The foreign key definition for the relation.
1142
1149
  * @returns The Prisma schema line for the inverse relation field.
1143
1150
  */
1144
- const buildInverseRelationLine = (sourceModelName, targetModelName, foreignKey) => {
1145
- return ` ${deriveCollectionFieldName(sourceModelName)} ${sourceModelName}[] @relation("${deriveInverseRelationAlias(sourceModelName, targetModelName, foreignKey.inverseRelationAlias).replace(/"/g, "\\\"")}")`;
1151
+ const buildInverseRelationLine = (sourceModelName, targetModelName, foreignKey, columns = []) => {
1152
+ const sourceColumn = resolveForeignKeyColumn(columns, foreignKey);
1153
+ const fieldName = isOneToOneForeignKey(sourceColumn) ? deriveSingularFieldName(sourceModelName) : deriveCollectionFieldName(sourceModelName);
1154
+ const relationName = deriveRelationAlias(sourceModelName, targetModelName, foreignKey.relationAlias?.trim());
1155
+ return ` ${fieldName} ${isOneToOneForeignKey(sourceColumn) ? `${sourceModelName}?` : `${sourceModelName}[]`} @relation("${relationName.replace(/"/g, "\\\"")}")`;
1146
1156
  };
1147
1157
  /**
1148
1158
  * Inject a line into the body of a Prisma model block if it does not already
@@ -1170,14 +1180,15 @@ const injectLineIntoModelBody = (bodyLines, line, exists) => {
1170
1180
  * @param foreignKeys An array of foreign key definitions to process.
1171
1181
  * @returns The updated Prisma schema string with inverse relations applied.
1172
1182
  */
1173
- const applyInverseRelations = (schema, sourceModelName, foreignKeys) => {
1183
+ const applyInverseRelations = (schema, sourceModelName, foreignKeys, columns = []) => {
1174
1184
  let nextSchema = schema;
1175
1185
  for (const foreignKey of foreignKeys) {
1176
1186
  const targetModel = findModelBlock(nextSchema, foreignKey.referencesTable);
1177
1187
  if (!targetModel) continue;
1178
- const inverseLine = buildInverseRelationLine(sourceModelName, targetModel.modelName, foreignKey);
1188
+ const sourceColumn = resolveForeignKeyColumn(columns, foreignKey);
1189
+ const inverseLine = buildInverseRelationLine(sourceModelName, targetModel.modelName, foreignKey, columns);
1179
1190
  const targetBodyLines = targetModel.block.split("\n");
1180
- const fieldName = deriveCollectionFieldName(sourceModelName);
1191
+ const fieldName = isOneToOneForeignKey(sourceColumn) ? deriveSingularFieldName(sourceModelName) : deriveCollectionFieldName(sourceModelName);
1181
1192
  const fieldRegex = new RegExp(`^\\s*${escapeRegex(fieldName)}\\s+`);
1182
1193
  injectLineIntoModelBody(targetBodyLines, inverseLine, (line) => fieldRegex.test(line));
1183
1194
  const updatedTarget = targetBodyLines.join("\n");
@@ -1196,7 +1207,7 @@ const buildModelBlock = (operation) => {
1196
1207
  const modelName = toModelName(operation.table);
1197
1208
  const mapped = operation.table !== modelName.toLowerCase();
1198
1209
  const fields = operation.columns.map(buildFieldLine);
1199
- const relations = (operation.foreignKeys ?? []).map(buildRelationLine);
1210
+ const relations = (operation.foreignKeys ?? []).map((foreignKey) => buildRelationLine(modelName, foreignKey, operation.columns));
1200
1211
  const metadata = [...(operation.indexes ?? []).map(buildIndexLine), ...mapped ? [` @@map("${(0, _h3ravel_support.str)(operation.table).snake()}")`] : []];
1201
1212
  return `model ${modelName} {\n${(metadata.length > 0 ? [
1202
1213
  ...fields,
@@ -1254,7 +1265,7 @@ const applyCreateTableOperation = (schema, operation) => {
1254
1265
  if (findModelBlock(schema, operation.table)) throw new ArkormException(`Prisma model for table [${operation.table}] already exists.`);
1255
1266
  const schemaWithEnums = ensureEnumBlocks(schema, operation.columns);
1256
1267
  const block = buildModelBlock(operation);
1257
- return applyInverseRelations(`${schemaWithEnums.trimEnd()}\n\n${block}\n`, toModelName(operation.table), operation.foreignKeys ?? []);
1268
+ return applyInverseRelations(`${schemaWithEnums.trimEnd()}\n\n${block}\n`, toModelName(operation.table), operation.foreignKeys ?? [], operation.columns);
1258
1269
  };
1259
1270
  /**
1260
1271
  * Apply an alter table operation to a Prisma schema string, modifying the model
@@ -1295,12 +1306,12 @@ const applyAlterTableOperation = (schema, operation) => {
1295
1306
  bodyLines.splice(insertIndex, 0, indexLine);
1296
1307
  });
1297
1308
  for (const foreignKey of operation.addForeignKeys ?? []) {
1298
- const relationLine = buildRelationLine(foreignKey);
1309
+ const relationLine = buildRelationLine(model.modelName, foreignKey, operation.addColumns);
1299
1310
  const relationRegex = new RegExp(`^\\s*${escapeRegex(foreignKey.fieldAlias?.trim() || deriveRelationFieldName(foreignKey.column))}\\s+`);
1300
1311
  injectLineIntoModelBody(bodyLines, relationLine, (line) => relationRegex.test(line));
1301
1312
  }
1302
1313
  block = bodyLines.join("\n");
1303
- return applyInverseRelations(`${schemaWithEnums.slice(0, refreshedModel.start)}${block}${schemaWithEnums.slice(refreshedModel.end)}`, model.modelName, operation.addForeignKeys ?? []);
1314
+ return applyInverseRelations(`${schemaWithEnums.slice(0, refreshedModel.start)}${block}${schemaWithEnums.slice(refreshedModel.end)}`, model.modelName, operation.addForeignKeys ?? [], operation.addColumns);
1304
1315
  };
1305
1316
  /**
1306
1317
  * Apply a drop table operation to a Prisma schema string, removing the model block
@@ -6468,7 +6479,9 @@ exports.defineConfig = defineConfig;
6468
6479
  exports.defineFactory = defineFactory;
6469
6480
  exports.deriveCollectionFieldName = deriveCollectionFieldName;
6470
6481
  exports.deriveInverseRelationAlias = deriveInverseRelationAlias;
6482
+ exports.deriveRelationAlias = deriveRelationAlias;
6471
6483
  exports.deriveRelationFieldName = deriveRelationFieldName;
6484
+ exports.deriveSingularFieldName = deriveSingularFieldName;
6472
6485
  exports.ensureArkormConfigLoading = ensureArkormConfigLoading;
6473
6486
  exports.escapeRegex = escapeRegex;
6474
6487
  exports.findAppliedMigration = findAppliedMigration;
package/dist/index.d.cts CHANGED
@@ -3281,17 +3281,18 @@ declare const buildIndexLine: (index: SchemaIndex) => string;
3281
3281
  */
3282
3282
  declare const deriveRelationFieldName: (columnName: string) => string;
3283
3283
  /**
3284
- * Derive a relation name for the inverse side of a relation based on the
3284
+ * Derive a relation name for both sides of a relation based on the
3285
3285
  * source and target model names, using an explicit alias if provided or a
3286
- * convention of combining the target model name with the last segment of
3287
- * the source model name.
3286
+ * convention of combining the full source model name with the target model name.
3288
3287
  *
3289
3288
  * @param sourceModelName The name of the source model in the relation.
3290
3289
  * @param targetModelName The name of the target model in the relation.
3291
- * @param explicitAlias An optional explicit alias for the inverse relation.
3292
- * @returns The derived or explicit inverse relation alias.
3290
+ * @param explicitAlias An optional explicit alias for the relation.
3291
+ * @returns The derived or explicit relation alias.
3293
3292
  */
3293
+ declare const deriveRelationAlias: (sourceModelName: string, targetModelName: string, explicitAlias?: string) => string;
3294
3294
  declare const deriveInverseRelationAlias: (sourceModelName: string, targetModelName: string, explicitAlias?: string) => string;
3295
+ declare const deriveSingularFieldName: (modelName: string) => string;
3295
3296
  declare const deriveCollectionFieldName: (modelName: string) => string;
3296
3297
  /**
3297
3298
  * Format a SchemaForeignKeyAction value as a Prisma onDelete action string.
@@ -3307,7 +3308,7 @@ declare const formatRelationAction: (action: SchemaForeignKeyAction) => string;
3307
3308
  * @param foreignKey The foreign key definition to convert to a relation line.
3308
3309
  * @returns The corresponding Prisma schema line for the relation field.
3309
3310
  */
3310
- declare const buildRelationLine: (foreignKey: SchemaForeignKey) => string;
3311
+ declare const buildRelationLine: (sourceModelName: string, foreignKey: SchemaForeignKey, columns?: SchemaColumn[]) => string;
3311
3312
  /**
3312
3313
  * Build a Prisma relation field line for the inverse side of a relation, based
3313
3314
  * on the source and target model names and the foreign key definition, using
@@ -3318,7 +3319,7 @@ declare const buildRelationLine: (foreignKey: SchemaForeignKey) => string;
3318
3319
  * @param foreignKey The foreign key definition for the relation.
3319
3320
  * @returns The Prisma schema line for the inverse relation field.
3320
3321
  */
3321
- declare const buildInverseRelationLine: (sourceModelName: string, targetModelName: string, foreignKey: SchemaForeignKey) => string;
3322
+ declare const buildInverseRelationLine: (sourceModelName: string, targetModelName: string, foreignKey: SchemaForeignKey, columns?: SchemaColumn[]) => string;
3322
3323
  /**
3323
3324
  * Build a Prisma model block string based on a SchemaTableCreateOperation, including
3324
3325
  * all fields and any necessary mapping.
@@ -3598,4 +3599,4 @@ declare class URLDriver {
3598
3599
  url(page: number): string;
3599
3600
  }
3600
3601
  //#endregion
3601
- export { ArkormCollection, ArkormErrorContext, ArkormException, Attribute, AttributeOptions, CliApp, EnumBuilder, ForeignKeyBuilder, InitCommand, InlineFactory, LengthAwarePaginator, MIGRATION_BRAND, MakeFactoryCommand, MakeMigrationCommand, MakeModelCommand, MakeSeederCommand, MigrateCommand, MigrateRollbackCommand, Migration, MigrationHistoryCommand, MissingDelegateException, Model, ModelFactory, ModelNotFoundException, ModelsSyncCommand, PRISMA_ENUM_MEMBER_REGEX, PRISMA_ENUM_REGEX, PRISMA_MODEL_REGEX, Paginator, PrismaDelegateMap, QueryBuilder, QueryConstraintException, RelationResolutionException, SEEDER_BRAND, SchemaBuilder, ScopeNotDefinedException, SeedCommand, Seeder, SeederCallArgument, SeederConstructor, SeederInput, TableBuilder, URLDriver, UniqueConstraintResolutionException, UnsupportedAdapterFeatureException, applyAlterTableOperation, applyCreateTableOperation, applyDropTableOperation, applyMigrationRollbackToPrismaSchema, applyMigrationToPrismaSchema, applyOperationsToPrismaSchema, buildEnumBlock, buildFieldLine, buildIndexLine, buildInverseRelationLine, buildMigrationIdentity, buildMigrationRunId, buildMigrationSource, buildModelBlock, buildRelationLine, computeMigrationChecksum, configureArkormRuntime, createMigrationTimestamp, createPrismaAdapter, createPrismaDelegateMap, defineConfig, defineFactory, deriveCollectionFieldName, deriveInverseRelationAlias, deriveRelationFieldName, ensureArkormConfigLoading, escapeRegex, findAppliedMigration, findEnumBlock, findModelBlock, formatDefaultValue, formatEnumDefaultValue, formatRelationAction, generateMigrationFile, getActiveTransactionClient, getDefaultStubsPath, getLastMigrationRun, getLatestAppliedMigrations, getMigrationPlan, getRuntimePaginationCurrentPageResolver, getRuntimePaginationURLDriverFactory, getRuntimePrismaClient, getUserConfig, inferDelegateName, isDelegateLike, isMigrationApplied, isTransactionCapableClient, loadArkormConfig, markMigrationApplied, markMigrationRun, pad, readAppliedMigrationsState, removeAppliedMigration, resetArkormRuntimeForTests, resolveCast, resolveEnumName, resolveMigrationClassName, resolveMigrationStateFilePath, resolvePrismaType, runArkormTransaction, runMigrationWithPrisma, runPrismaCommand, toMigrationFileSlug, toModelName, writeAppliedMigrationsState };
3602
+ export { ArkormCollection, ArkormErrorContext, ArkormException, Attribute, AttributeOptions, CliApp, EnumBuilder, ForeignKeyBuilder, InitCommand, InlineFactory, LengthAwarePaginator, MIGRATION_BRAND, MakeFactoryCommand, MakeMigrationCommand, MakeModelCommand, MakeSeederCommand, MigrateCommand, MigrateRollbackCommand, Migration, MigrationHistoryCommand, MissingDelegateException, Model, ModelFactory, ModelNotFoundException, ModelsSyncCommand, PRISMA_ENUM_MEMBER_REGEX, PRISMA_ENUM_REGEX, PRISMA_MODEL_REGEX, Paginator, PrismaDelegateMap, QueryBuilder, QueryConstraintException, RelationResolutionException, SEEDER_BRAND, SchemaBuilder, ScopeNotDefinedException, SeedCommand, Seeder, SeederCallArgument, SeederConstructor, SeederInput, TableBuilder, URLDriver, UniqueConstraintResolutionException, UnsupportedAdapterFeatureException, applyAlterTableOperation, applyCreateTableOperation, applyDropTableOperation, applyMigrationRollbackToPrismaSchema, applyMigrationToPrismaSchema, applyOperationsToPrismaSchema, buildEnumBlock, buildFieldLine, buildIndexLine, buildInverseRelationLine, buildMigrationIdentity, buildMigrationRunId, buildMigrationSource, buildModelBlock, buildRelationLine, computeMigrationChecksum, configureArkormRuntime, createMigrationTimestamp, createPrismaAdapter, createPrismaDelegateMap, defineConfig, defineFactory, deriveCollectionFieldName, deriveInverseRelationAlias, deriveRelationAlias, deriveRelationFieldName, deriveSingularFieldName, ensureArkormConfigLoading, escapeRegex, findAppliedMigration, findEnumBlock, findModelBlock, formatDefaultValue, formatEnumDefaultValue, formatRelationAction, generateMigrationFile, getActiveTransactionClient, getDefaultStubsPath, getLastMigrationRun, getLatestAppliedMigrations, getMigrationPlan, getRuntimePaginationCurrentPageResolver, getRuntimePaginationURLDriverFactory, getRuntimePrismaClient, getUserConfig, inferDelegateName, isDelegateLike, isMigrationApplied, isTransactionCapableClient, loadArkormConfig, markMigrationApplied, markMigrationRun, pad, readAppliedMigrationsState, removeAppliedMigration, resetArkormRuntimeForTests, resolveCast, resolveEnumName, resolveMigrationClassName, resolveMigrationStateFilePath, resolvePrismaType, runArkormTransaction, runMigrationWithPrisma, runPrismaCommand, toMigrationFileSlug, toModelName, writeAppliedMigrationsState };
package/dist/index.d.mts CHANGED
@@ -3281,17 +3281,18 @@ declare const buildIndexLine: (index: SchemaIndex) => string;
3281
3281
  */
3282
3282
  declare const deriveRelationFieldName: (columnName: string) => string;
3283
3283
  /**
3284
- * Derive a relation name for the inverse side of a relation based on the
3284
+ * Derive a relation name for both sides of a relation based on the
3285
3285
  * source and target model names, using an explicit alias if provided or a
3286
- * convention of combining the target model name with the last segment of
3287
- * the source model name.
3286
+ * convention of combining the full source model name with the target model name.
3288
3287
  *
3289
3288
  * @param sourceModelName The name of the source model in the relation.
3290
3289
  * @param targetModelName The name of the target model in the relation.
3291
- * @param explicitAlias An optional explicit alias for the inverse relation.
3292
- * @returns The derived or explicit inverse relation alias.
3290
+ * @param explicitAlias An optional explicit alias for the relation.
3291
+ * @returns The derived or explicit relation alias.
3293
3292
  */
3293
+ declare const deriveRelationAlias: (sourceModelName: string, targetModelName: string, explicitAlias?: string) => string;
3294
3294
  declare const deriveInverseRelationAlias: (sourceModelName: string, targetModelName: string, explicitAlias?: string) => string;
3295
+ declare const deriveSingularFieldName: (modelName: string) => string;
3295
3296
  declare const deriveCollectionFieldName: (modelName: string) => string;
3296
3297
  /**
3297
3298
  * Format a SchemaForeignKeyAction value as a Prisma onDelete action string.
@@ -3307,7 +3308,7 @@ declare const formatRelationAction: (action: SchemaForeignKeyAction) => string;
3307
3308
  * @param foreignKey The foreign key definition to convert to a relation line.
3308
3309
  * @returns The corresponding Prisma schema line for the relation field.
3309
3310
  */
3310
- declare const buildRelationLine: (foreignKey: SchemaForeignKey) => string;
3311
+ declare const buildRelationLine: (sourceModelName: string, foreignKey: SchemaForeignKey, columns?: SchemaColumn[]) => string;
3311
3312
  /**
3312
3313
  * Build a Prisma relation field line for the inverse side of a relation, based
3313
3314
  * on the source and target model names and the foreign key definition, using
@@ -3318,7 +3319,7 @@ declare const buildRelationLine: (foreignKey: SchemaForeignKey) => string;
3318
3319
  * @param foreignKey The foreign key definition for the relation.
3319
3320
  * @returns The Prisma schema line for the inverse relation field.
3320
3321
  */
3321
- declare const buildInverseRelationLine: (sourceModelName: string, targetModelName: string, foreignKey: SchemaForeignKey) => string;
3322
+ declare const buildInverseRelationLine: (sourceModelName: string, targetModelName: string, foreignKey: SchemaForeignKey, columns?: SchemaColumn[]) => string;
3322
3323
  /**
3323
3324
  * Build a Prisma model block string based on a SchemaTableCreateOperation, including
3324
3325
  * all fields and any necessary mapping.
@@ -3598,4 +3599,4 @@ declare class URLDriver {
3598
3599
  url(page: number): string;
3599
3600
  }
3600
3601
  //#endregion
3601
- export { ArkormCollection, ArkormErrorContext, ArkormException, Attribute, AttributeOptions, CliApp, EnumBuilder, ForeignKeyBuilder, InitCommand, InlineFactory, LengthAwarePaginator, MIGRATION_BRAND, MakeFactoryCommand, MakeMigrationCommand, MakeModelCommand, MakeSeederCommand, MigrateCommand, MigrateRollbackCommand, Migration, MigrationHistoryCommand, MissingDelegateException, Model, ModelFactory, ModelNotFoundException, ModelsSyncCommand, PRISMA_ENUM_MEMBER_REGEX, PRISMA_ENUM_REGEX, PRISMA_MODEL_REGEX, Paginator, PrismaDelegateMap, QueryBuilder, QueryConstraintException, RelationResolutionException, SEEDER_BRAND, SchemaBuilder, ScopeNotDefinedException, SeedCommand, Seeder, SeederCallArgument, SeederConstructor, SeederInput, TableBuilder, URLDriver, UniqueConstraintResolutionException, UnsupportedAdapterFeatureException, applyAlterTableOperation, applyCreateTableOperation, applyDropTableOperation, applyMigrationRollbackToPrismaSchema, applyMigrationToPrismaSchema, applyOperationsToPrismaSchema, buildEnumBlock, buildFieldLine, buildIndexLine, buildInverseRelationLine, buildMigrationIdentity, buildMigrationRunId, buildMigrationSource, buildModelBlock, buildRelationLine, computeMigrationChecksum, configureArkormRuntime, createMigrationTimestamp, createPrismaAdapter, createPrismaDelegateMap, defineConfig, defineFactory, deriveCollectionFieldName, deriveInverseRelationAlias, deriveRelationFieldName, ensureArkormConfigLoading, escapeRegex, findAppliedMigration, findEnumBlock, findModelBlock, formatDefaultValue, formatEnumDefaultValue, formatRelationAction, generateMigrationFile, getActiveTransactionClient, getDefaultStubsPath, getLastMigrationRun, getLatestAppliedMigrations, getMigrationPlan, getRuntimePaginationCurrentPageResolver, getRuntimePaginationURLDriverFactory, getRuntimePrismaClient, getUserConfig, inferDelegateName, isDelegateLike, isMigrationApplied, isTransactionCapableClient, loadArkormConfig, markMigrationApplied, markMigrationRun, pad, readAppliedMigrationsState, removeAppliedMigration, resetArkormRuntimeForTests, resolveCast, resolveEnumName, resolveMigrationClassName, resolveMigrationStateFilePath, resolvePrismaType, runArkormTransaction, runMigrationWithPrisma, runPrismaCommand, toMigrationFileSlug, toModelName, writeAppliedMigrationsState };
3602
+ export { ArkormCollection, ArkormErrorContext, ArkormException, Attribute, AttributeOptions, CliApp, EnumBuilder, ForeignKeyBuilder, InitCommand, InlineFactory, LengthAwarePaginator, MIGRATION_BRAND, MakeFactoryCommand, MakeMigrationCommand, MakeModelCommand, MakeSeederCommand, MigrateCommand, MigrateRollbackCommand, Migration, MigrationHistoryCommand, MissingDelegateException, Model, ModelFactory, ModelNotFoundException, ModelsSyncCommand, PRISMA_ENUM_MEMBER_REGEX, PRISMA_ENUM_REGEX, PRISMA_MODEL_REGEX, Paginator, PrismaDelegateMap, QueryBuilder, QueryConstraintException, RelationResolutionException, SEEDER_BRAND, SchemaBuilder, ScopeNotDefinedException, SeedCommand, Seeder, SeederCallArgument, SeederConstructor, SeederInput, TableBuilder, URLDriver, UniqueConstraintResolutionException, UnsupportedAdapterFeatureException, applyAlterTableOperation, applyCreateTableOperation, applyDropTableOperation, applyMigrationRollbackToPrismaSchema, applyMigrationToPrismaSchema, applyOperationsToPrismaSchema, buildEnumBlock, buildFieldLine, buildIndexLine, buildInverseRelationLine, buildMigrationIdentity, buildMigrationRunId, buildMigrationSource, buildModelBlock, buildRelationLine, computeMigrationChecksum, configureArkormRuntime, createMigrationTimestamp, createPrismaAdapter, createPrismaDelegateMap, defineConfig, defineFactory, deriveCollectionFieldName, deriveInverseRelationAlias, deriveRelationAlias, deriveRelationFieldName, deriveSingularFieldName, ensureArkormConfigLoading, escapeRegex, findAppliedMigration, findEnumBlock, findModelBlock, formatDefaultValue, formatEnumDefaultValue, formatRelationAction, generateMigrationFile, getActiveTransactionClient, getDefaultStubsPath, getLastMigrationRun, getLatestAppliedMigrations, getMigrationPlan, getRuntimePaginationCurrentPageResolver, getRuntimePaginationURLDriverFactory, getRuntimePrismaClient, getUserConfig, inferDelegateName, isDelegateLike, isMigrationApplied, isTransactionCapableClient, loadArkormConfig, markMigrationApplied, markMigrationRun, pad, readAppliedMigrationsState, removeAppliedMigration, resetArkormRuntimeForTests, resolveCast, resolveEnumName, resolveMigrationClassName, resolveMigrationStateFilePath, resolvePrismaType, runArkormTransaction, runMigrationWithPrisma, runPrismaCommand, toMigrationFileSlug, toModelName, writeAppliedMigrationsState };
package/dist/index.mjs CHANGED
@@ -629,7 +629,7 @@ var TableBuilder = class {
629
629
  */
630
630
  foreign(column) {
631
631
  const columnName = this.resolveColumn(column).name;
632
- return this.foreignKey(columnName + (column ? "" : "Id"));
632
+ return this.foreignKey(column ?? columnName);
633
633
  }
634
634
  /**
635
635
  * Returns a deep copy of the defined columns for the table.
@@ -1047,24 +1047,24 @@ const deriveRelationFieldName = (columnName) => {
1047
1047
  if (trimmed.endsWith("_id") && trimmed.length > 3) return trimmed.slice(0, -3).replace(/_([a-zA-Z0-9])/g, (_, letter) => letter.toUpperCase());
1048
1048
  return `${trimmed.charAt(0).toLowerCase()}${trimmed.slice(1)}`;
1049
1049
  };
1050
- const pascalWords = (value) => {
1051
- return value.match(/[A-Z][a-z0-9]*/g) ?? [value];
1052
- };
1053
1050
  /**
1054
- * Derive a relation name for the inverse side of a relation based on the
1051
+ * Derive a relation name for both sides of a relation based on the
1055
1052
  * source and target model names, using an explicit alias if provided or a
1056
- * convention of combining the target model name with the last segment of
1057
- * the source model name.
1053
+ * convention of combining the full source model name with the target model name.
1058
1054
  *
1059
1055
  * @param sourceModelName The name of the source model in the relation.
1060
1056
  * @param targetModelName The name of the target model in the relation.
1061
- * @param explicitAlias An optional explicit alias for the inverse relation.
1062
- * @returns The derived or explicit inverse relation alias.
1057
+ * @param explicitAlias An optional explicit alias for the relation.
1058
+ * @returns The derived or explicit relation alias.
1063
1059
  */
1064
- const deriveInverseRelationAlias = (sourceModelName, targetModelName, explicitAlias) => {
1060
+ const deriveRelationAlias = (sourceModelName, targetModelName, explicitAlias) => {
1065
1061
  if (explicitAlias && explicitAlias.trim().length > 0) return explicitAlias.trim();
1066
- const sourceWords = pascalWords(sourceModelName);
1067
- return `${sourceWords[sourceWords.length - 1] ?? sourceModelName}${targetModelName}`;
1062
+ return [sourceModelName, targetModelName].sort((left, right) => left.localeCompare(right)).join("");
1063
+ };
1064
+ const deriveInverseRelationAlias = deriveRelationAlias;
1065
+ const deriveSingularFieldName = (modelName) => {
1066
+ if (!modelName) return "item";
1067
+ return `${modelName.charAt(0).toLowerCase()}${modelName.slice(1)}`;
1068
1068
  };
1069
1069
  const deriveCollectionFieldName = (modelName) => {
1070
1070
  if (!modelName) return "items";
@@ -1072,6 +1072,12 @@ const deriveCollectionFieldName = (modelName) => {
1072
1072
  if (camel.endsWith("s")) return `${camel}es`;
1073
1073
  return `${camel}s`;
1074
1074
  };
1075
+ const resolveForeignKeyColumn = (columns, foreignKey) => {
1076
+ return columns.find((column) => column.name === foreignKey.column);
1077
+ };
1078
+ const isOneToOneForeignKey = (column) => {
1079
+ return Boolean(column?.unique || column?.primary);
1080
+ };
1075
1081
  /**
1076
1082
  * Format a SchemaForeignKeyAction value as a Prisma onDelete action string.
1077
1083
  *
@@ -1092,15 +1098,16 @@ const formatRelationAction = (action) => {
1092
1098
  * @param foreignKey The foreign key definition to convert to a relation line.
1093
1099
  * @returns The corresponding Prisma schema line for the relation field.
1094
1100
  */
1095
- const buildRelationLine = (foreignKey) => {
1101
+ const buildRelationLine = (sourceModelName, foreignKey, columns = []) => {
1096
1102
  if (!foreignKey.referencesTable.trim()) throw new ArkormException(`Foreign key [${foreignKey.column}] must define a referenced table.`);
1097
1103
  if (!foreignKey.referencesColumn.trim()) throw new ArkormException(`Foreign key [${foreignKey.column}] must define a referenced column.`);
1104
+ const sourceColumn = resolveForeignKeyColumn(columns, foreignKey);
1098
1105
  const fieldName = foreignKey.fieldAlias?.trim() || deriveRelationFieldName(foreignKey.column);
1099
1106
  const targetModel = toModelName(foreignKey.referencesTable);
1100
- const relationName = foreignKey.relationAlias?.trim();
1101
- const relationPrefix = relationName ? `@relation("${relationName.replace(/"/g, "\\\"")}", ` : "@relation(";
1107
+ const relationName = deriveRelationAlias(sourceModelName, targetModel, foreignKey.relationAlias?.trim());
1108
+ const optional = sourceColumn?.nullable ? "?" : "";
1102
1109
  const onDelete = foreignKey.onDelete ? `, onDelete: ${formatRelationAction(foreignKey.onDelete)}` : "";
1103
- return ` ${fieldName} ${targetModel} ${relationPrefix}fields: [${foreignKey.column}], references: [${foreignKey.referencesColumn}]${onDelete})`;
1110
+ return ` ${fieldName} ${targetModel}${optional} @relation("${relationName.replace(/"/g, "\\\"")}", fields: [${foreignKey.column}], references: [${foreignKey.referencesColumn}]${onDelete})`;
1104
1111
  };
1105
1112
  /**
1106
1113
  * Build a Prisma relation field line for the inverse side of a relation, based
@@ -1112,8 +1119,11 @@ const buildRelationLine = (foreignKey) => {
1112
1119
  * @param foreignKey The foreign key definition for the relation.
1113
1120
  * @returns The Prisma schema line for the inverse relation field.
1114
1121
  */
1115
- const buildInverseRelationLine = (sourceModelName, targetModelName, foreignKey) => {
1116
- return ` ${deriveCollectionFieldName(sourceModelName)} ${sourceModelName}[] @relation("${deriveInverseRelationAlias(sourceModelName, targetModelName, foreignKey.inverseRelationAlias).replace(/"/g, "\\\"")}")`;
1122
+ const buildInverseRelationLine = (sourceModelName, targetModelName, foreignKey, columns = []) => {
1123
+ const sourceColumn = resolveForeignKeyColumn(columns, foreignKey);
1124
+ const fieldName = isOneToOneForeignKey(sourceColumn) ? deriveSingularFieldName(sourceModelName) : deriveCollectionFieldName(sourceModelName);
1125
+ const relationName = deriveRelationAlias(sourceModelName, targetModelName, foreignKey.relationAlias?.trim());
1126
+ return ` ${fieldName} ${isOneToOneForeignKey(sourceColumn) ? `${sourceModelName}?` : `${sourceModelName}[]`} @relation("${relationName.replace(/"/g, "\\\"")}")`;
1117
1127
  };
1118
1128
  /**
1119
1129
  * Inject a line into the body of a Prisma model block if it does not already
@@ -1141,14 +1151,15 @@ const injectLineIntoModelBody = (bodyLines, line, exists) => {
1141
1151
  * @param foreignKeys An array of foreign key definitions to process.
1142
1152
  * @returns The updated Prisma schema string with inverse relations applied.
1143
1153
  */
1144
- const applyInverseRelations = (schema, sourceModelName, foreignKeys) => {
1154
+ const applyInverseRelations = (schema, sourceModelName, foreignKeys, columns = []) => {
1145
1155
  let nextSchema = schema;
1146
1156
  for (const foreignKey of foreignKeys) {
1147
1157
  const targetModel = findModelBlock(nextSchema, foreignKey.referencesTable);
1148
1158
  if (!targetModel) continue;
1149
- const inverseLine = buildInverseRelationLine(sourceModelName, targetModel.modelName, foreignKey);
1159
+ const sourceColumn = resolveForeignKeyColumn(columns, foreignKey);
1160
+ const inverseLine = buildInverseRelationLine(sourceModelName, targetModel.modelName, foreignKey, columns);
1150
1161
  const targetBodyLines = targetModel.block.split("\n");
1151
- const fieldName = deriveCollectionFieldName(sourceModelName);
1162
+ const fieldName = isOneToOneForeignKey(sourceColumn) ? deriveSingularFieldName(sourceModelName) : deriveCollectionFieldName(sourceModelName);
1152
1163
  const fieldRegex = new RegExp(`^\\s*${escapeRegex(fieldName)}\\s+`);
1153
1164
  injectLineIntoModelBody(targetBodyLines, inverseLine, (line) => fieldRegex.test(line));
1154
1165
  const updatedTarget = targetBodyLines.join("\n");
@@ -1167,7 +1178,7 @@ const buildModelBlock = (operation) => {
1167
1178
  const modelName = toModelName(operation.table);
1168
1179
  const mapped = operation.table !== modelName.toLowerCase();
1169
1180
  const fields = operation.columns.map(buildFieldLine);
1170
- const relations = (operation.foreignKeys ?? []).map(buildRelationLine);
1181
+ const relations = (operation.foreignKeys ?? []).map((foreignKey) => buildRelationLine(modelName, foreignKey, operation.columns));
1171
1182
  const metadata = [...(operation.indexes ?? []).map(buildIndexLine), ...mapped ? [` @@map("${str(operation.table).snake()}")`] : []];
1172
1183
  return `model ${modelName} {\n${(metadata.length > 0 ? [
1173
1184
  ...fields,
@@ -1225,7 +1236,7 @@ const applyCreateTableOperation = (schema, operation) => {
1225
1236
  if (findModelBlock(schema, operation.table)) throw new ArkormException(`Prisma model for table [${operation.table}] already exists.`);
1226
1237
  const schemaWithEnums = ensureEnumBlocks(schema, operation.columns);
1227
1238
  const block = buildModelBlock(operation);
1228
- return applyInverseRelations(`${schemaWithEnums.trimEnd()}\n\n${block}\n`, toModelName(operation.table), operation.foreignKeys ?? []);
1239
+ return applyInverseRelations(`${schemaWithEnums.trimEnd()}\n\n${block}\n`, toModelName(operation.table), operation.foreignKeys ?? [], operation.columns);
1229
1240
  };
1230
1241
  /**
1231
1242
  * Apply an alter table operation to a Prisma schema string, modifying the model
@@ -1266,12 +1277,12 @@ const applyAlterTableOperation = (schema, operation) => {
1266
1277
  bodyLines.splice(insertIndex, 0, indexLine);
1267
1278
  });
1268
1279
  for (const foreignKey of operation.addForeignKeys ?? []) {
1269
- const relationLine = buildRelationLine(foreignKey);
1280
+ const relationLine = buildRelationLine(model.modelName, foreignKey, operation.addColumns);
1270
1281
  const relationRegex = new RegExp(`^\\s*${escapeRegex(foreignKey.fieldAlias?.trim() || deriveRelationFieldName(foreignKey.column))}\\s+`);
1271
1282
  injectLineIntoModelBody(bodyLines, relationLine, (line) => relationRegex.test(line));
1272
1283
  }
1273
1284
  block = bodyLines.join("\n");
1274
- return applyInverseRelations(`${schemaWithEnums.slice(0, refreshedModel.start)}${block}${schemaWithEnums.slice(refreshedModel.end)}`, model.modelName, operation.addForeignKeys ?? []);
1285
+ return applyInverseRelations(`${schemaWithEnums.slice(0, refreshedModel.start)}${block}${schemaWithEnums.slice(refreshedModel.end)}`, model.modelName, operation.addForeignKeys ?? [], operation.addColumns);
1275
1286
  };
1276
1287
  /**
1277
1288
  * Apply a drop table operation to a Prisma schema string, removing the model block
@@ -6376,4 +6387,4 @@ var Model = class Model {
6376
6387
  };
6377
6388
 
6378
6389
  //#endregion
6379
- export { ArkormCollection, ArkormException, Attribute, CliApp, EnumBuilder, ForeignKeyBuilder, InitCommand, InlineFactory, LengthAwarePaginator, MIGRATION_BRAND, MakeFactoryCommand, MakeMigrationCommand, MakeModelCommand, MakeSeederCommand, MigrateCommand, MigrateRollbackCommand, Migration, MigrationHistoryCommand, MissingDelegateException, Model, ModelFactory, ModelNotFoundException, ModelsSyncCommand, PRISMA_ENUM_MEMBER_REGEX, PRISMA_ENUM_REGEX, PRISMA_MODEL_REGEX, Paginator, QueryBuilder, QueryConstraintException, RelationResolutionException, SEEDER_BRAND, SchemaBuilder, ScopeNotDefinedException, SeedCommand, Seeder, TableBuilder, URLDriver, UniqueConstraintResolutionException, UnsupportedAdapterFeatureException, applyAlterTableOperation, applyCreateTableOperation, applyDropTableOperation, applyMigrationRollbackToPrismaSchema, applyMigrationToPrismaSchema, applyOperationsToPrismaSchema, buildEnumBlock, buildFieldLine, buildIndexLine, buildInverseRelationLine, buildMigrationIdentity, buildMigrationRunId, buildMigrationSource, buildModelBlock, buildRelationLine, computeMigrationChecksum, configureArkormRuntime, createMigrationTimestamp, createPrismaAdapter, createPrismaDelegateMap, defineConfig, defineFactory, deriveCollectionFieldName, deriveInverseRelationAlias, deriveRelationFieldName, ensureArkormConfigLoading, escapeRegex, findAppliedMigration, findEnumBlock, findModelBlock, formatDefaultValue, formatEnumDefaultValue, formatRelationAction, generateMigrationFile, getActiveTransactionClient, getDefaultStubsPath, getLastMigrationRun, getLatestAppliedMigrations, getMigrationPlan, getRuntimePaginationCurrentPageResolver, getRuntimePaginationURLDriverFactory, getRuntimePrismaClient, getUserConfig, inferDelegateName, isDelegateLike, isMigrationApplied, isTransactionCapableClient, loadArkormConfig, markMigrationApplied, markMigrationRun, pad, readAppliedMigrationsState, removeAppliedMigration, resetArkormRuntimeForTests, resolveCast, resolveEnumName, resolveMigrationClassName, resolveMigrationStateFilePath, resolvePrismaType, runArkormTransaction, runMigrationWithPrisma, runPrismaCommand, toMigrationFileSlug, toModelName, writeAppliedMigrationsState };
6390
+ export { ArkormCollection, ArkormException, Attribute, CliApp, EnumBuilder, ForeignKeyBuilder, InitCommand, InlineFactory, LengthAwarePaginator, MIGRATION_BRAND, MakeFactoryCommand, MakeMigrationCommand, MakeModelCommand, MakeSeederCommand, MigrateCommand, MigrateRollbackCommand, Migration, MigrationHistoryCommand, MissingDelegateException, Model, ModelFactory, ModelNotFoundException, ModelsSyncCommand, PRISMA_ENUM_MEMBER_REGEX, PRISMA_ENUM_REGEX, PRISMA_MODEL_REGEX, Paginator, QueryBuilder, QueryConstraintException, RelationResolutionException, SEEDER_BRAND, SchemaBuilder, ScopeNotDefinedException, SeedCommand, Seeder, TableBuilder, URLDriver, UniqueConstraintResolutionException, UnsupportedAdapterFeatureException, applyAlterTableOperation, applyCreateTableOperation, applyDropTableOperation, applyMigrationRollbackToPrismaSchema, applyMigrationToPrismaSchema, applyOperationsToPrismaSchema, buildEnumBlock, buildFieldLine, buildIndexLine, buildInverseRelationLine, buildMigrationIdentity, buildMigrationRunId, buildMigrationSource, buildModelBlock, buildRelationLine, computeMigrationChecksum, configureArkormRuntime, createMigrationTimestamp, createPrismaAdapter, createPrismaDelegateMap, defineConfig, defineFactory, deriveCollectionFieldName, deriveInverseRelationAlias, deriveRelationAlias, deriveRelationFieldName, deriveSingularFieldName, ensureArkormConfigLoading, escapeRegex, findAppliedMigration, findEnumBlock, findModelBlock, formatDefaultValue, formatEnumDefaultValue, formatRelationAction, generateMigrationFile, getActiveTransactionClient, getDefaultStubsPath, getLastMigrationRun, getLatestAppliedMigrations, getMigrationPlan, getRuntimePaginationCurrentPageResolver, getRuntimePaginationURLDriverFactory, getRuntimePrismaClient, getUserConfig, inferDelegateName, isDelegateLike, isMigrationApplied, isTransactionCapableClient, loadArkormConfig, markMigrationApplied, markMigrationRun, pad, readAppliedMigrationsState, removeAppliedMigration, resetArkormRuntimeForTests, resolveCast, resolveEnumName, resolveMigrationClassName, resolveMigrationStateFilePath, resolvePrismaType, runArkormTransaction, runMigrationWithPrisma, runPrismaCommand, toMigrationFileSlug, toModelName, writeAppliedMigrationsState };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arkormx",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
4
4
  "description": "Modern TypeScript-first ORM for Node.js.",
5
5
  "keywords": [
6
6
  "orm",