@@ -247,7 +247,7 @@ const READ_VERBS = 'find|query|select|count|aggregate';
247247function readCallPattern ( table ) {
248248 return new RegExp (
249249 '[\\w$.]*(?:' + READ_VERBS + ')[\\w$]*\\s*\\(\\s*' + // callee + open paren
250- '[^)\'"`]{0,80}' + // earlier plain arguments, if any
250+ '[^( )\'"`]{0,80}' + // earlier plain arguments, if any
251251 '[\'"`]' + table + '[\'"`]' , // the table, quoted
252252 'i' ,
253253 ) ;
@@ -613,6 +613,35 @@ function selfTest() {
613613 expect ( `querying only ${ t } is not a resolver` , queriesAllGrantTables ( resolverFixtureBody ( [ t ] ) ) , false ) ;
614614 }
615615
616+ // --- The read-call spellings the criterion must recognise (POSITIVE polarity). ---
617+ // These go red if READ_VERBS is narrowed or the argument pattern regresses — the
618+ // recall side of the criterion, which no fixture above can reach because
619+ // `resolverFixtureBody` only ever emits `ql.find`. The canonical resolver uses the
620+ // `tryFind` HELPER spelling, so losing it would break the positive control itself.
621+ const t0 = GRANT_TABLES [ 0 ] ;
622+ for ( const [ label , src ] of [
623+ [ 'member call' , `ql.find('${ t0 } ', { where: {} })` ] ,
624+ [ 'helper call with a leading argument' , `await tryFind(ql, '${ t0 } ', { user_id }, 200)` ] ,
625+ [ 'double quotes' , `ql.find("${ t0 } ")` ] ,
626+ [ 'template literal' , 'dataEngine.findOne(`' + t0 + '`)' ] ,
627+ [ 'argument on the next line' , `this.ql.find(\n '${ t0 } ',\n { limit: 1 })` ] ,
628+ ] ) {
629+ expect ( `a read call is recognised — ${ label } ` , queriesGrantTable ( src , t0 ) , true ) ;
630+ }
631+ // ...and the shapes that merely CONTAIN the name are not read calls (negative
632+ // polarity, listed for the record). The nested-call case is the sharp one: the table
633+ // must be an argument of the READ call, not of something nested inside it.
634+ for ( const [ label , src ] of [
635+ [ 'a comment' , `// we read ${ t0 } here` ] ,
636+ [ 'a constant list' , `const NAMES = ['${ t0 } '];` ] ,
637+ [ 'an unquoted object key' , `${ t0 } : { allowRead: true },` ] ,
638+ [ 'a Set literal' , `new Set(['${ t0 } '])` ] ,
639+ [ 'page metadata' , `objectName: '${ t0 } ',` ] ,
640+ [ 'a nested call inside a read call' , `find(wrap('${ t0 } '))` ] ,
641+ ] ) {
642+ expect ( `not a read call — ${ label } ` , queriesGrantTable ( src , t0 ) , false ) ;
643+ }
644+
616645 // (1) — a second file reading rows from every grant table is a duplicate resolver.
617646 write ( 'packages/rest/src/my-own-resolver.ts' , resolverFixtureBody ( ) ) ;
618647 const dupErrors = audit ( dir ) ;
0 commit comments