@@ -14,6 +14,7 @@ import {
1414    isDataModelField , 
1515    isLiteralExpr , 
1616    isMemberAccessExpr , 
17+     isNullExpr , 
1718    isReferenceExpr , 
1819    isThisExpr , 
1920    isUnaryExpr , 
@@ -125,13 +126,19 @@ export class ConstraintTransformer {
125126    } 
126127
127128    private  transformMemberAccess ( expr : MemberAccessExpr )  { 
129+         // "this.x" is transformed into a named variable 
128130        if  ( isThisExpr ( expr . operand ) )  { 
129-             // "this.x" is transformed into a named variable 
130131            return  this . variable ( expr . member . $refText ,  'boolean' ) ; 
131132        } 
132133
133-         // other member access expressions are not supported and thus 
134-         // transformed into a free variable 
134+         // top-level auth access 
135+         const  authAccess  =  this . getAuthAccess ( expr ) ; 
136+         if  ( authAccess )  { 
137+             return  this . value ( `${ authAccess }  ,  'boolean' ) ; 
138+         } 
139+ 
140+         // other top-level member access expressions are not supported 
141+         // and thus transformed into a free variable 
135142        return  this . nextVar ( ) ; 
136143    } 
137144
@@ -153,14 +160,19 @@ export class ConstraintTransformer {
153160    } 
154161
155162    private  transformComparison ( expr : BinaryExpr )  { 
156-         const  leftOperand  =  this . getComparisonOperand ( expr . left ) ; 
157-         const  rightOperand  =  this . getComparisonOperand ( expr . right ) ; 
163+         if  ( this . isAuthEqualNull ( expr ) )  { 
164+             // `auth() == null` => `user === null` 
165+             return  this . value ( `${ this . options . authAccessor }  ,  'boolean' ) ; 
166+         } 
158167
159-         if  ( leftOperand   ===   undefined   ||   rightOperand   ===   undefined )  { 
160-             // if either operand is not supported, transform into a free variable  
161-             return  this . nextVar ( ) ; 
168+         if  ( this . isAuthNotEqualNull ( expr ) )  { 
169+             // `auth() != null` => `user !== null`  
170+             return  this . value ( ` ${ this . options . authAccessor }  !== null` ,   'boolean' ) ; 
162171        } 
163172
173+         const  leftOperand  =  this . getComparisonOperand ( expr . left ) ; 
174+         const  rightOperand  =  this . getComparisonOperand ( expr . right ) ; 
175+ 
164176        const  op  =  match ( expr . operator ) 
165177            . with ( '==' ,  ( )  =>  'eq' ) 
166178            . with ( '!=' ,  ( )  =>  'eq' ) 
@@ -175,12 +187,52 @@ export class ConstraintTransformer {
175187        let  result  =  `{ kind: '${ op } ${ leftOperand } ${ rightOperand }  ; 
176188        if  ( expr . operator  ===  '!=' )  { 
177189            // transform "!=" into "not eq" 
178-             result  =  `{ kind: 'not', children: [${ result }  ; 
190+             result  =  this . not ( result ) ; 
191+         } 
192+ 
193+         // `auth()` access can be undefined, when that happens, we assume a false condition 
194+         // for the comparison, unless we're directly comparing `auth() != null` 
195+ 
196+         const  leftAuthAccess  =  this . getAuthAccess ( expr . left ) ; 
197+         const  rightAuthAccess  =  this . getAuthAccess ( expr . right ) ; 
198+ 
199+         if  ( leftAuthAccess )  { 
200+             // `auth().f op x` => `auth().f !== undefined && auth().f op x` 
201+             return  this . and ( this . value ( `${ this . normalizeToNull ( leftAuthAccess ) }  ,  'boolean' ) ,  result ) ; 
202+         }  else  if  ( rightAuthAccess )  { 
203+             // `x op auth().f` => `auth().f !== undefined && x op auth().f` 
204+             return  this . and ( this . value ( `${ this . normalizeToNull ( rightAuthAccess ) }  ,  'boolean' ) ,  result ) ; 
205+         } 
206+ 
207+         if  ( leftOperand  ===  undefined  ||  rightOperand  ===  undefined )  { 
208+             // if either operand is not supported, transform into a free variable 
209+             return  this . nextVar ( ) ; 
179210        } 
180211
181212        return  result ; 
182213    } 
183214
215+     // normalize `auth()` access undefined value to null 
216+     private  normalizeToNull ( expr : string )  { 
217+         return  `(${ expr }  ; 
218+     } 
219+ 
220+     private  isAuthEqualNull ( expr : BinaryExpr )  { 
221+         return  ( 
222+             expr . operator  ===  '=='  && 
223+             ( ( isAuthInvocation ( expr . left )  &&  isNullExpr ( expr . right ) )  || 
224+                 ( isAuthInvocation ( expr . right )  &&  isNullExpr ( expr . left ) ) ) 
225+         ) ; 
226+     } 
227+ 
228+     private  isAuthNotEqualNull ( expr : BinaryExpr )  { 
229+         return  ( 
230+             expr . operator  ===  '!='  && 
231+             ( ( isAuthInvocation ( expr . left )  &&  isNullExpr ( expr . right ) )  || 
232+                 ( isAuthInvocation ( expr . right )  &&  isNullExpr ( expr . left ) ) ) 
233+         ) ; 
234+     } 
235+ 
184236    private  getComparisonOperand ( expr : Expression )  { 
185237        if  ( isLiteralExpr ( expr ) )  { 
186238            return  this . transformLiteral ( expr ) ; 
@@ -199,16 +251,9 @@ export class ConstraintTransformer {
199251
200252        const  authAccess  =  this . getAuthAccess ( expr ) ; 
201253        if  ( authAccess )  { 
202-             // `auth().` access is transformed into a runtime boolean value if it 
203-             // doesn't evaluate to undefined (due to ?. chaining), otherwise into 
204-             // a named variable 
205-             const  fieldAccess  =  `${ this . options . authAccessor } ${ authAccess }  ; 
206254            const  mappedType  =  this . mapType ( expr ) ; 
207255            if  ( mappedType )  { 
208-                 return  `${ fieldAccess } ${ this . expressionVariable ( expr ,  mappedType ) } ${ this . value (  
209-                     fieldAccess ,  
210-                     mappedType  
211-                 ) }  `; 
256+                 return  `${ this . value ( authAccess ,  mappedType ) }  ; 
212257            }  else  { 
213258                return  undefined ; 
214259            } 
@@ -241,7 +286,7 @@ export class ConstraintTransformer {
241286        } 
242287
243288        if  ( isAuthInvocation ( expr . operand ) )  { 
244-             return  expr . member . $refText ; 
289+             return  ` ${ this . options . authAccessor } ?. ${ expr . member . $refText } ` ; 
245290        }  else  { 
246291            const  operand  =  this . getAuthAccess ( expr . operand ) ; 
247292            return  operand  ? `${ operand } ${ expr . member . $refText }   : undefined ; 
0 commit comments