@@ -914,12 +914,104 @@ export type RelationMutation<TContract extends Contract<SqlStorage>, ModelName e
914914 | RelationMutationConnect < TContract , ModelName >
915915 | RelationMutationDisconnect < TContract , ModelName > ;
916916
917- export interface RelationMutator < TContract extends Contract < SqlStorage > , ModelName extends string > {
917+ type RelationThrough <
918+ TContract extends Contract < SqlStorage > ,
919+ ModelName extends string ,
920+ RelName extends string ,
921+ > =
922+ RelationsOf < TContract , ModelName > extends infer Rels extends Record < string , unknown >
923+ ? RelName extends keyof Rels
924+ ? Rels [ RelName ] extends {
925+ readonly through : infer Through extends {
926+ readonly table : string ;
927+ readonly parentColumns : readonly string [ ] ;
928+ readonly childColumns : readonly string [ ] ;
929+ } ;
930+ }
931+ ? Through
932+ : never
933+ : never
934+ : never ;
935+
936+ /**
937+ * Resolves a storage table name to its owning domain model by scanning the
938+ * model map for the model whose `storage.table` matches. Junction tables
939+ * (e.g. `user_roles`) surface their generated model (e.g. `UserRole`) so the
940+ * junction's own field nullability/defaults can be inspected.
941+ */
942+ type ModelNameForTable < TContract extends Contract < SqlStorage > , TableName extends string > = {
943+ [ M in keyof ModelsOf < TContract > & string ] : ModelsOf < TContract > [ M ] extends {
944+ readonly storage : { readonly table : TableName } ;
945+ }
946+ ? M
947+ : never ;
948+ } [ keyof ModelsOf < TContract > & string ] ;
949+
950+ /**
951+ * A junction field is a *payload* field when its backing column is neither a
952+ * parent-side nor a child-side foreign-key column of the join. Those payload
953+ * fields are the ones the relation API can't populate from `create`/`connect`.
954+ */
955+ type JunctionPayloadFieldNames <
956+ TContract extends Contract < SqlStorage > ,
957+ JunctionModel extends string ,
958+ JoinColumns extends string ,
959+ > = {
960+ [ F in CreateFieldNames < TContract , JunctionModel > ] : FieldColumnName <
961+ TContract ,
962+ JunctionModel ,
963+ F
964+ > extends JoinColumns
965+ ? never
966+ : F ;
967+ } [ CreateFieldNames < TContract , JunctionModel > ] ;
968+
969+ /**
970+ * True when the relation's junction carries at least one required payload
971+ * column — a non-join column that is not nullable and has no default. Such a
972+ * relation can't be populated through nested `create`, so its create input is
973+ * disabled at the type level (mirroring the runtime guard in
974+ * `mutation-executor.ts`).
975+ */
976+ type HasRequiredJunctionPayload <
977+ TContract extends Contract < SqlStorage > ,
978+ ModelName extends string ,
979+ RelName extends string ,
980+ > =
981+ RelationThrough < TContract , ModelName , RelName > extends infer Through extends {
982+ readonly table : string ;
983+ readonly parentColumns : readonly string [ ] ;
984+ readonly childColumns : readonly string [ ] ;
985+ }
986+ ? ModelNameForTable < TContract , Through [ 'table' ] > extends infer JunctionModel extends string
987+ ? {
988+ [ F in JunctionPayloadFieldNames <
989+ TContract ,
990+ JunctionModel ,
991+ Through [ 'parentColumns' ] [ number ] | Through [ 'childColumns' ] [ number ]
992+ > ] : IsOptionalCreateField < TContract , JunctionModel , F > extends true ? never : F ;
993+ } [ JunctionPayloadFieldNames <
994+ TContract ,
995+ JunctionModel ,
996+ Through [ 'parentColumns' ] [ number ] | Through [ 'childColumns' ] [ number ]
997+ > ] extends never
998+ ? false
999+ : true
1000+ : false
1001+ : false ;
1002+
1003+ export interface RelationMutator <
1004+ TContract extends Contract < SqlStorage > ,
1005+ ModelName extends string ,
1006+ CreateDisabled extends boolean = false ,
1007+ > {
9181008 create (
919- data : MutationCreateInput < TContract , ModelName > ,
1009+ data : CreateDisabled extends true ? never : MutationCreateInput < TContract , ModelName > ,
9201010 ) : RelationMutationCreate < TContract , ModelName > ;
9211011 create (
922- data : readonly MutationCreateInput < TContract , ModelName > [ ] ,
1012+ data : CreateDisabled extends true
1013+ ? never
1014+ : readonly MutationCreateInput < TContract , ModelName > [ ] ,
9231015 ) : RelationMutationCreate < TContract , ModelName > ;
9241016 connect (
9251017 criterion : RelationConnectCriterion < TContract , ModelName > ,
@@ -938,7 +1030,11 @@ type RelationMutationCallback<
9381030 ModelName extends string ,
9391031 RelName extends RelationNames < TContract , ModelName > ,
9401032> = (
941- mutator : RelationMutator < TContract , RelatedModelName < TContract , ModelName , RelName > & string > ,
1033+ mutator : RelationMutator <
1034+ TContract ,
1035+ RelatedModelName < TContract , ModelName , RelName > & string ,
1036+ HasRequiredJunctionPayload < TContract , ModelName , RelName >
1037+ > ,
9421038) => RelationMutation < TContract , RelatedModelName < TContract , ModelName , RelName > & string > ;
9431039
9441040type RelationMutationFields <
0 commit comments