@@ -1338,18 +1338,78 @@ export class RestApiHandler<Schema extends SchemaDef = SchemaDef> implements Api
13381338 return Object . values ( modelDef . fields ) . filter ( ( f ) => modelDef . idFields . includes ( f . name ) ) ;
13391339 }
13401340
1341- // map external ID name to unique constraint field
13421341 const externalIdName = this . externalIdMapping [ modelLower ] ;
1343- for ( const [ name , info ] of Object . entries ( modelDef . uniqueFields ) ) {
1344- if ( name === externalIdName ) {
1345- if ( typeof info . type === 'string' ) {
1346- // single unique field
1347- return [ this . requireField ( model , info . type ) ] ;
1348- } else {
1349- // compound unique fields
1350- return Object . keys ( info ) . map ( ( f ) => this . requireField ( model , f ) ) ;
1342+
1343+ // 1. Model-level unique indexes with @@unique(map: ...)
1344+ if ( Array . isArray ( modelDef . attributes ) ) {
1345+ const uniqueAttr = modelDef . attributes . find (
1346+ ( attr : any ) =>
1347+ attr . name === '@@unique' &&
1348+ Array . isArray ( attr . args ) &&
1349+ attr . args . some ( ( a : any ) => a . name === 'map' && a . value ?. value === externalIdName ) ,
1350+ ) ;
1351+ if ( uniqueAttr ) {
1352+ const fieldsArg = uniqueAttr . args . find ( ( a : any ) => a . name === 'fields' ) ;
1353+ if ( fieldsArg && Array . isArray ( fieldsArg . value ?. items ) && fieldsArg . value . items . length >= 1 ) {
1354+ const fieldNames = fieldsArg . value . items . map ( ( item : any ) => {
1355+ let fieldName : string | undefined = undefined ;
1356+ if ( typeof item . field === 'string' && modelDef . fields [ item . field ] ) {
1357+ fieldName = item . field ;
1358+ } else if (
1359+ typeof item . value === 'object' &&
1360+ typeof item . value . name === 'string' &&
1361+ modelDef . fields [ item . value . name ]
1362+ ) {
1363+ fieldName = item . value . name ;
1364+ }
1365+ if ( ! fieldName ) {
1366+ throw new Error ( `Invalid unique field mapping in model ${ model } : ${ JSON . stringify ( item ) } ` ) ;
1367+ }
1368+ return fieldName ;
1369+ } ) ;
1370+ return fieldNames . map ( ( fieldName : string ) => this . requireField ( model , fieldName ) ) ;
1371+ }
1372+ }
1373+ }
1374+
1375+ // 2. uniqueFields by key name
1376+ const uniqueFieldEntry = Object . entries ( modelDef . uniqueFields ) . find ( ( [ name ] ) => name === externalIdName ) ;
1377+ if ( uniqueFieldEntry ) {
1378+ const [ name , info ] = uniqueFieldEntry ;
1379+ if ( typeof info . type === 'string' ) {
1380+ return [ this . requireField ( model , name ) ] ;
1381+ }
1382+ const fieldNames = Object . keys ( info ) . filter ( ( f ) => {
1383+ if ( ! ( f in modelDef . fields ) ) {
1384+ return false ;
13511385 }
1386+ const fieldInfo = ( info as Record < string , any > ) [ f ] ;
1387+ return typeof fieldInfo === 'object' && fieldInfo !== null && typeof fieldInfo . type === 'string' ;
1388+ } ) ;
1389+ if ( fieldNames . length === 0 ) {
1390+ throw new Error (
1391+ `No valid field names found for compound unique key ${ externalIdName } in model ${ model } ` ,
1392+ ) ;
13521393 }
1394+ return fieldNames . map ( ( f ) => this . requireField ( model , f ) ) ;
1395+ }
1396+
1397+ // 3. Field-level unique attribute with @unique(map: ...)
1398+ const field = Object . values ( modelDef . fields ) . find ( ( fieldDef ) => {
1399+ if ( ! Array . isArray ( fieldDef . attributes ) ) {
1400+ return false ;
1401+ }
1402+ // @unique (map: ...)
1403+ return fieldDef . attributes . some (
1404+ ( attr : any ) =>
1405+ attr . name === '@unique' &&
1406+ Array . isArray ( attr . args ) &&
1407+ attr . args . some ( ( a : any ) => a . name === 'map' && a . value ?. value === externalIdName ) ,
1408+ ) ;
1409+ } ) ;
1410+
1411+ if ( field ) {
1412+ return [ field ] ;
13531413 }
13541414
13551415 throw new Error ( `Model ${ model } does not have unique key ${ externalIdName } ` ) ;
@@ -2060,9 +2120,7 @@ export class RestApiHandler<Schema extends SchemaDef = SchemaDef> implements Api
20602120 }
20612121 } else {
20622122 if ( op === 'between' ) {
2063- const parts = value
2064- . split ( ',' )
2065- . map ( ( v ) => this . coerce ( fieldDef , v ) ) ;
2123+ const parts = value . split ( ',' ) . map ( ( v ) => this . coerce ( fieldDef , v ) ) ;
20662124 if ( parts . length !== 2 ) {
20672125 throw new InvalidValueError ( `"between" expects exactly 2 comma-separated values` ) ;
20682126 }
0 commit comments