@sapui5/sap.fe.base 1.142.0 → 1.142.2

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.
@@ -1,10 +1,17 @@
1
1
  import type {
2
2
  ActionParameter,
3
+ AddAnnotationExpression,
4
+ AddConditionalExpression,
3
5
  AndAnnotationExpression,
4
6
  AndConditionalExpression,
5
7
  ApplyAnnotationExpression,
6
8
  Boolean,
7
9
  ConditionalCheckOrValue,
10
+ ConvertedMetadata,
11
+ DivAnnotationExpression,
12
+ DivByAnnotationExpression,
13
+ DivByConditionalExpression,
14
+ DivConditionalExpression,
8
15
  EntitySet,
9
16
  EntityType,
10
17
  EqAnnotationExpression,
@@ -13,14 +20,24 @@ import type {
13
20
  GeConditionalExpression,
14
21
  GtAnnotationExpression,
15
22
  GtConditionalExpression,
23
+ HasAnnotationExpression,
24
+ HasConditionalExpression,
16
25
  IfAnnotationExpression,
17
26
  IfAnnotationExpressionValue,
27
+ InAnnotationExpression,
28
+ InConditionalExpression,
18
29
  LeAnnotationExpression,
19
30
  LeConditionalExpression,
20
31
  LtAnnotationExpression,
21
32
  LtConditionalExpression,
33
+ ModAnnotationExpression,
34
+ ModConditionalExpression,
35
+ MulAnnotationExpression,
36
+ MulConditionalExpression,
22
37
  NeAnnotationExpression,
23
38
  NeConditionalExpression,
39
+ NegAnnotationExpression,
40
+ NegConditionalExpression,
24
41
  NotAnnotationExpression,
25
42
  NotConditionalExpression,
26
43
  OrAnnotationExpression,
@@ -29,6 +46,8 @@ import type {
29
46
  PathConditionExpression,
30
47
  Property,
31
48
  PropertyAnnotationValue,
49
+ SubAnnotationExpression,
50
+ SubConditionalExpression,
32
51
  ConstantExpression as VocabConstantExpression
33
52
  } from "@sap-ux/vocabularies-types";
34
53
  import { CommonAnnotationTerms } from "@sap-ux/vocabularies-types/vocabularies/Common";
@@ -157,7 +176,7 @@ export type EmbeddedUI5ExpressionBindingExpression<T> = BaseExpression<T> & {
157
176
  value: string;
158
177
  };
159
178
 
160
- export type ComparisonOperator = "===" | "!==" | ">=" | ">" | "<=" | "<";
179
+ export type ComparisonOperator = "===" | "!==" | ">=" | ">" | "<=" | "<" | "Has" | "In";
161
180
  export type ComparisonExpression = BaseExpression<boolean> & {
162
181
  _type: "Comparison";
163
182
  operator: ComparisonOperator;
@@ -165,6 +184,13 @@ export type ComparisonExpression = BaseExpression<boolean> & {
165
184
  operand2: BindingToolkitExpression<unknown>;
166
185
  };
167
186
 
187
+ export type ArithmeticOperator = "+" | "-" | "*" | "/" | "%" | "Neg" | "Floor";
188
+ export type ArithmeticExpression = BaseExpression<number> & {
189
+ _type: "Arithmetic";
190
+ operator: ArithmeticOperator;
191
+ operand1: BindingToolkitExpression<unknown>;
192
+ operand2: BindingToolkitExpression<unknown>;
193
+ };
168
194
  export type IfElseExpression<T> = BaseExpression<T> & {
169
195
  _type: "IfElse";
170
196
  condition: BindingToolkitExpression<boolean>;
@@ -174,6 +200,9 @@ export type IfElseExpression<T> = BaseExpression<T> & {
174
200
  export function isProperty(serviceObject: unknown): serviceObject is Property {
175
201
  return (serviceObject as Property)?._type === "Property";
176
202
  }
203
+ function isActionParameter(serviceObject: unknown): serviceObject is ActionParameter {
204
+ return (serviceObject as ActionParameter)?._type === "ActionParameter";
205
+ }
177
206
 
178
207
  /**
179
208
  * An expression that evaluates to type T.
@@ -195,7 +224,8 @@ export type BindingToolkitExpression<T> =
195
224
  | FormatterExpression<T>
196
225
  | ComplexTypeExpression<T>
197
226
  | ReferenceExpression
198
- | FunctionExpression<T>;
227
+ | FunctionExpression<T>
228
+ | ArithmeticExpression;
199
229
 
200
230
  type TypeMappingDefinition = {
201
231
  type: string;
@@ -293,7 +323,12 @@ export function _checkExpressionsAreEqual<T>(a: BindingToolkitExpression<T>, b:
293
323
  case "EmbeddedBinding":
294
324
  case "EmbeddedExpressionBinding":
295
325
  return a.value === (b as ConstantExpression<T>).value;
296
-
326
+ case "Arithmetic":
327
+ return (
328
+ a.operator === (b as ArithmeticExpression).operator &&
329
+ _checkExpressionsAreEqual(a.operand1, (b as ArithmeticExpression).operand1) &&
330
+ _checkExpressionsAreEqual(a.operand2, (b as ArithmeticExpression).operand2)
331
+ );
297
332
  case "Not":
298
333
  return _checkExpressionsAreEqual(a.operand, (b as NotExpression).operand);
299
334
  case "Truthy":
@@ -499,6 +534,138 @@ export function or(...operands: ExpressionOrPrimitive<boolean>[]): BindingToolki
499
534
  }
500
535
  }
501
536
 
537
+ export function add<T extends PrimitiveType>(
538
+ leftOperand: ExpressionOrPrimitive<T>,
539
+ rightOperand: ExpressionOrPrimitive<T>
540
+ ): BindingToolkitExpression<number> {
541
+ return arithmetic("+", leftOperand, rightOperand);
542
+ }
543
+
544
+ export function subtract<T extends PrimitiveType>(
545
+ leftOperand: ExpressionOrPrimitive<T>,
546
+ rightOperand: ExpressionOrPrimitive<T>
547
+ ): BindingToolkitExpression<number> {
548
+ return arithmetic("-", leftOperand, rightOperand);
549
+ }
550
+
551
+ export function multiply<T extends PrimitiveType>(
552
+ leftOperand: ExpressionOrPrimitive<T>,
553
+ rightOperand: ExpressionOrPrimitive<T>
554
+ ): BindingToolkitExpression<number> {
555
+ return arithmetic("*", leftOperand, rightOperand);
556
+ }
557
+
558
+ export function divide<T extends PrimitiveType>(
559
+ leftOperand: ExpressionOrPrimitive<T>,
560
+ rightOperand: ExpressionOrPrimitive<T>
561
+ ): BindingToolkitExpression<number> {
562
+ return arithmetic("/", leftOperand, rightOperand);
563
+ }
564
+
565
+ export function modulo<T extends PrimitiveType>(
566
+ leftOperand: ExpressionOrPrimitive<T>,
567
+ rightOperand: ExpressionOrPrimitive<T>
568
+ ): BindingToolkitExpression<number> {
569
+ return arithmetic("%", leftOperand, rightOperand);
570
+ }
571
+
572
+ export function negate<T extends PrimitiveType>(operand: ExpressionOrPrimitive<T>): BindingToolkitExpression<number> {
573
+ return arithmetic("Neg", operand, null);
574
+ }
575
+
576
+ export function floor<T extends PrimitiveType>(operand: ExpressionOrPrimitive<T>): BindingToolkitExpression<number> {
577
+ return arithmetic("Floor", operand, null);
578
+ }
579
+
580
+ /**
581
+ * Arithmetic expression.
582
+ * @param operator
583
+ * @param leftOperand
584
+ * @param rightOperand
585
+ * @returns Expression evaluating to number
586
+ */
587
+ export function arithmetic<T extends PrimitiveType>(
588
+ operator: ArithmeticOperator,
589
+ leftOperand: ExpressionOrPrimitive<T>,
590
+ rightOperand: ExpressionOrPrimitive<T>
591
+ ): BindingToolkitExpression<number> {
592
+ const leftExpression = wrapPrimitive(leftOperand);
593
+ const rightExpression = wrapPrimitive(rightOperand);
594
+ if (hasUnresolvableExpression(leftExpression, rightExpression)) {
595
+ return unresolvableExpression;
596
+ }
597
+ if (isConstant(leftExpression) && isConstant(rightExpression)) {
598
+ switch (operator) {
599
+ case "+":
600
+ if (leftExpression.value === null || leftExpression.value === undefined) {
601
+ return constant(rightExpression.value as number);
602
+ } else if (rightExpression.value === null || rightExpression.value === undefined) {
603
+ return constant(leftExpression.value as number);
604
+ }
605
+ const sum = (leftExpression.value as number) + (rightExpression.value as number);
606
+ return constant(sum);
607
+ case "-":
608
+ if (leftExpression.value === null || leftExpression.value === undefined) {
609
+ return constant(rightExpression.value as number);
610
+ } else if (rightExpression.value === null || rightExpression.value === undefined) {
611
+ return constant(leftExpression.value as number);
612
+ }
613
+ const diff = (leftExpression.value as number) - (rightExpression.value as number);
614
+ return constant(diff);
615
+ case "/":
616
+ if (
617
+ leftExpression.value === null ||
618
+ leftExpression.value === undefined ||
619
+ rightExpression.value === null ||
620
+ rightExpression.value === undefined
621
+ ) {
622
+ return constant(0);
623
+ }
624
+ const div = (leftExpression.value as number) / (rightExpression.value as number);
625
+ return constant(div);
626
+ case "Neg":
627
+ if (leftExpression.value === null || leftExpression.value === undefined) {
628
+ return constant(0);
629
+ }
630
+ return constant(-leftExpression.value);
631
+ case "%":
632
+ if (
633
+ leftExpression.value === null ||
634
+ leftExpression.value === undefined ||
635
+ rightExpression.value === null ||
636
+ rightExpression.value === undefined
637
+ ) {
638
+ return constant(0);
639
+ }
640
+ const mod = (leftExpression.value as number) % (rightExpression.value as number);
641
+ return constant(mod);
642
+ case "Floor":
643
+ if (leftExpression.value === null || leftExpression.value === undefined) {
644
+ return constant(0);
645
+ }
646
+ return constant(Math.floor(leftExpression.value as number));
647
+ case "*":
648
+ if (
649
+ leftExpression.value === null ||
650
+ leftExpression.value === undefined ||
651
+ rightExpression.value === null ||
652
+ rightExpression.value === undefined
653
+ ) {
654
+ return constant(0);
655
+ }
656
+ const mult = (leftExpression.value as number) * (rightExpression.value as number);
657
+ return constant(mult);
658
+ }
659
+ } else {
660
+ return {
661
+ _type: "Arithmetic",
662
+ operator: operator,
663
+ operand1: leftExpression,
664
+ operand2: rightExpression
665
+ };
666
+ }
667
+ }
668
+
502
669
  /**
503
670
  * Logical `not` operator.
504
671
  * @param operand The expression to reverse
@@ -539,6 +706,11 @@ export function not(operand: ExpressionOrPrimitive<boolean>): BindingToolkitExpr
539
706
  return { ...operand, operator: "<=" };
540
707
  case ">=":
541
708
  return { ...operand, operator: "<" };
709
+ default:
710
+ return {
711
+ _type: "Not",
712
+ operand: operand
713
+ };
542
714
  }
543
715
  } else if (operand._type === "Not") {
544
716
  return operand.operand;
@@ -849,6 +1021,15 @@ type ComplexAnnotationExpression<P> =
849
1021
  | GeAnnotationExpression<P>
850
1022
  | LeAnnotationExpression<P>
851
1023
  | LtAnnotationExpression<P>
1024
+ | HasAnnotationExpression<P>
1025
+ | InAnnotationExpression<P>
1026
+ | AddAnnotationExpression<P>
1027
+ | SubAnnotationExpression<P>
1028
+ | MulAnnotationExpression<P>
1029
+ | DivAnnotationExpression<P>
1030
+ | ModAnnotationExpression<P>
1031
+ | DivByAnnotationExpression<P>
1032
+ | NegAnnotationExpression<P>
852
1033
  | VocabConstantExpression<P>;
853
1034
 
854
1035
  function isPrimitiveObject(objectType: object): boolean {
@@ -871,6 +1052,11 @@ function isComplexAnnotationExpression<T>(annotationValue: PropertyAnnotationVal
871
1052
  return typeof annotationValue === "object" && !isPrimitiveObject(annotationValue as object);
872
1053
  }
873
1054
 
1055
+ let CONVERTER_ROOT_SYMBOL: symbol | undefined;
1056
+ export function setConverterRootSymbol(converterRootSymbol: symbol): void {
1057
+ CONVERTER_ROOT_SYMBOL = converterRootSymbol;
1058
+ }
1059
+
874
1060
  /**
875
1061
  * Generate the corresponding annotationValue for a given annotation annotationValue.
876
1062
  * @template T The target type
@@ -886,6 +1072,11 @@ export function getExpressionFromAnnotation<T extends PrimitiveType>(
886
1072
  defaultValue?: ExpressionOrPrimitive<T>,
887
1073
  pathVisitor?: Function
888
1074
  ): BindingToolkitExpression<PrimitiveTypeCast<T>> {
1075
+ let converterRoot: ConvertedMetadata | undefined;
1076
+ if (CONVERTER_ROOT_SYMBOL) {
1077
+ converterRoot = (annotationValue?.[CONVERTER_ROOT_SYMBOL as never] as unknown as Function)?.() as ConvertedMetadata | undefined;
1078
+ }
1079
+
889
1080
  if (annotationValue === undefined) {
890
1081
  return wrapPrimitive(defaultValue as PrimitiveTypeCast<T>);
891
1082
  }
@@ -898,55 +1089,106 @@ export function getExpressionFromAnnotation<T extends PrimitiveType>(
898
1089
  case "Path":
899
1090
  return pathInModel(annotationValue.path, undefined, visitedNavigationPaths, pathVisitor);
900
1091
  case "If":
901
- return annotationIfExpression(annotationValue.$If, visitedNavigationPaths, pathVisitor) as BindingToolkitExpression<
902
- PrimitiveTypeCast<T>
903
- >;
1092
+ return annotationIfExpression(
1093
+ annotationValue.$If,
1094
+ visitedNavigationPaths,
1095
+ pathVisitor,
1096
+ converterRoot
1097
+ ) as BindingToolkitExpression<PrimitiveTypeCast<T>>;
904
1098
  case "Not":
905
- return not(parseAnnotationCondition(annotationValue.$Not, visitedNavigationPaths, pathVisitor)) as BindingToolkitExpression<
906
- PrimitiveTypeCast<T>
907
- >;
1099
+ return not(
1100
+ parseAnnotationCondition(annotationValue.$Not, visitedNavigationPaths, pathVisitor, converterRoot)
1101
+ ) as BindingToolkitExpression<PrimitiveTypeCast<T>>;
908
1102
  case "Eq":
909
1103
  return equal(
910
- parseAnnotationCondition(annotationValue.$Eq[0], visitedNavigationPaths, pathVisitor),
911
- parseAnnotationCondition(annotationValue.$Eq[1], visitedNavigationPaths, pathVisitor)
1104
+ parseAnnotationCondition(annotationValue.$Eq[0], visitedNavigationPaths, pathVisitor, converterRoot),
1105
+ parseAnnotationCondition(annotationValue.$Eq[1], visitedNavigationPaths, pathVisitor, converterRoot)
912
1106
  ) as BindingToolkitExpression<PrimitiveTypeCast<T>>;
913
1107
  case "Ne":
914
1108
  return notEqual(
915
- parseAnnotationCondition(annotationValue.$Ne[0], visitedNavigationPaths, pathVisitor),
916
- parseAnnotationCondition(annotationValue.$Ne[1], visitedNavigationPaths, pathVisitor)
1109
+ parseAnnotationCondition(annotationValue.$Ne[0], visitedNavigationPaths, pathVisitor, converterRoot),
1110
+ parseAnnotationCondition(annotationValue.$Ne[1], visitedNavigationPaths, pathVisitor, converterRoot)
917
1111
  ) as BindingToolkitExpression<PrimitiveTypeCast<T>>;
918
1112
  case "Gt":
919
1113
  return greaterThan(
920
- parseAnnotationCondition(annotationValue.$Gt[0], visitedNavigationPaths, pathVisitor),
921
- parseAnnotationCondition(annotationValue.$Gt[1], visitedNavigationPaths, pathVisitor)
1114
+ parseAnnotationCondition(annotationValue.$Gt[0], visitedNavigationPaths, pathVisitor, converterRoot),
1115
+ parseAnnotationCondition(annotationValue.$Gt[1], visitedNavigationPaths, pathVisitor, converterRoot)
922
1116
  ) as BindingToolkitExpression<PrimitiveTypeCast<T>>;
923
1117
  case "Ge":
924
1118
  return greaterOrEqual(
925
- parseAnnotationCondition(annotationValue.$Ge[0], visitedNavigationPaths, pathVisitor),
926
- parseAnnotationCondition(annotationValue.$Ge[1], visitedNavigationPaths, pathVisitor)
1119
+ parseAnnotationCondition(annotationValue.$Ge[0], visitedNavigationPaths, pathVisitor, converterRoot),
1120
+ parseAnnotationCondition(annotationValue.$Ge[1], visitedNavigationPaths, pathVisitor, converterRoot)
927
1121
  ) as BindingToolkitExpression<PrimitiveTypeCast<T>>;
928
1122
  case "Lt":
929
1123
  return lessThan(
930
- parseAnnotationCondition(annotationValue.$Lt[0], visitedNavigationPaths, pathVisitor),
931
- parseAnnotationCondition(annotationValue.$Lt[1], visitedNavigationPaths, pathVisitor)
1124
+ parseAnnotationCondition(annotationValue.$Lt[0], visitedNavigationPaths, pathVisitor, converterRoot),
1125
+ parseAnnotationCondition(annotationValue.$Lt[1], visitedNavigationPaths, pathVisitor, converterRoot)
932
1126
  ) as BindingToolkitExpression<PrimitiveTypeCast<T>>;
933
1127
  case "Le":
934
1128
  return lessOrEqual(
935
- parseAnnotationCondition(annotationValue.$Le[0], visitedNavigationPaths, pathVisitor),
936
- parseAnnotationCondition(annotationValue.$Le[1], visitedNavigationPaths, pathVisitor)
1129
+ parseAnnotationCondition(annotationValue.$Le[0], visitedNavigationPaths, pathVisitor, converterRoot),
1130
+ parseAnnotationCondition(annotationValue.$Le[1], visitedNavigationPaths, pathVisitor, converterRoot)
937
1131
  ) as BindingToolkitExpression<PrimitiveTypeCast<T>>;
938
1132
  case "Or":
939
1133
  return or(
940
1134
  ...annotationValue.$Or.map(function (orCondition) {
941
- return parseAnnotationCondition<boolean>(orCondition, visitedNavigationPaths, pathVisitor);
1135
+ return parseAnnotationCondition<boolean>(orCondition, visitedNavigationPaths, pathVisitor, converterRoot);
942
1136
  })
943
1137
  ) as BindingToolkitExpression<PrimitiveTypeCast<T>>;
944
1138
  case "And":
945
1139
  return and(
946
1140
  ...annotationValue.$And.map(function (andCondition) {
947
- return parseAnnotationCondition<boolean>(andCondition, visitedNavigationPaths, pathVisitor);
1141
+ return parseAnnotationCondition<boolean>(andCondition, visitedNavigationPaths, pathVisitor, converterRoot);
948
1142
  })
949
1143
  ) as BindingToolkitExpression<PrimitiveTypeCast<T>>;
1144
+ case "In":
1145
+ return comparison(
1146
+ "In",
1147
+ parseAnnotationCondition(annotationValue.$In[0], visitedNavigationPaths, pathVisitor, converterRoot),
1148
+ parseAnnotationCondition(annotationValue.$In[1], visitedNavigationPaths, pathVisitor, converterRoot)
1149
+ ) as BindingToolkitExpression<PrimitiveTypeCast<T>>;
1150
+ case "Has":
1151
+ return comparison(
1152
+ "Has",
1153
+ parseAnnotationCondition(annotationValue.$Has[0], visitedNavigationPaths, pathVisitor, converterRoot),
1154
+ parseAnnotationCondition(annotationValue.$Has[1], visitedNavigationPaths, pathVisitor, converterRoot)
1155
+ ) as BindingToolkitExpression<PrimitiveTypeCast<T>>;
1156
+ case "Add":
1157
+ return add(
1158
+ parseAnnotationCondition(annotationValue.$Add[0], visitedNavigationPaths, pathVisitor, converterRoot),
1159
+ parseAnnotationCondition(annotationValue.$Add[1], visitedNavigationPaths, pathVisitor, converterRoot)
1160
+ ) as BindingToolkitExpression<PrimitiveTypeCast<T>>;
1161
+ case "Sub":
1162
+ return subtract(
1163
+ parseAnnotationCondition(annotationValue.$Sub[0], visitedNavigationPaths, pathVisitor, converterRoot),
1164
+ parseAnnotationCondition(annotationValue.$Sub[1], visitedNavigationPaths, pathVisitor, converterRoot)
1165
+ ) as BindingToolkitExpression<PrimitiveTypeCast<T>>;
1166
+ case "Mul":
1167
+ return multiply(
1168
+ parseAnnotationCondition(annotationValue.$Mul[0], visitedNavigationPaths, pathVisitor, converterRoot),
1169
+ parseAnnotationCondition(annotationValue.$Mul[1], visitedNavigationPaths, pathVisitor, converterRoot)
1170
+ ) as BindingToolkitExpression<PrimitiveTypeCast<T>>;
1171
+ case "Div":
1172
+ return floor(
1173
+ divide(
1174
+ parseAnnotationCondition(annotationValue.$Div[0], visitedNavigationPaths, pathVisitor, converterRoot),
1175
+ parseAnnotationCondition(annotationValue.$Div[1], visitedNavigationPaths, pathVisitor, converterRoot)
1176
+ )
1177
+ ) as BindingToolkitExpression<PrimitiveTypeCast<T>>;
1178
+ case "DivBy":
1179
+ return divide(
1180
+ parseAnnotationCondition(annotationValue.$DivBy[0], visitedNavigationPaths, pathVisitor, converterRoot),
1181
+ parseAnnotationCondition(annotationValue.$DivBy[1], visitedNavigationPaths, pathVisitor, converterRoot)
1182
+ ) as BindingToolkitExpression<PrimitiveTypeCast<T>>;
1183
+ case "Mod":
1184
+ return modulo(
1185
+ parseAnnotationCondition(annotationValue.$Mod[0], visitedNavigationPaths, pathVisitor, converterRoot),
1186
+ parseAnnotationCondition(annotationValue.$Mod[1], visitedNavigationPaths, pathVisitor, converterRoot)
1187
+ ) as BindingToolkitExpression<PrimitiveTypeCast<T>>;
1188
+ case "Neg":
1189
+ return negate(
1190
+ parseAnnotationCondition(annotationValue.$Neg[0], visitedNavigationPaths, pathVisitor, converterRoot)
1191
+ ) as BindingToolkitExpression<PrimitiveTypeCast<T>>;
950
1192
  case "Apply":
951
1193
  return annotationApplyExpression(
952
1194
  annotationValue as ApplyAnnotationExpression<string>,
@@ -966,60 +1208,122 @@ export function getExpressionFromAnnotation<T extends PrimitiveType>(
966
1208
  * @param annotationValue The condition or value from the annotation
967
1209
  * @param visitedNavigationPaths The path from the root entity set
968
1210
  * @param pathVisitor A function to modify the resulting path
1211
+ * @param converterRoot The root converted metadata
969
1212
  * @returns An equivalent expression
970
1213
  */
971
1214
  function parseAnnotationCondition<T extends PrimitiveType>(
972
1215
  annotationValue: ConditionalCheckOrValue,
973
1216
  visitedNavigationPaths: string[] = [],
974
- pathVisitor?: Function
1217
+ pathVisitor?: Function,
1218
+ converterRoot?: ConvertedMetadata
975
1219
  ): BindingToolkitExpression<T> {
976
1220
  if (annotationValue === null || typeof annotationValue !== "object") {
977
1221
  return constant(annotationValue as T);
978
1222
  } else if (annotationValue.hasOwnProperty("$Or")) {
979
1223
  return or(
980
1224
  ...((annotationValue as OrConditionalExpression).$Or.map(function (orCondition) {
981
- return parseAnnotationCondition(orCondition, visitedNavigationPaths, pathVisitor);
1225
+ return parseAnnotationCondition(orCondition, visitedNavigationPaths, pathVisitor, converterRoot);
982
1226
  }) as unknown as BindingToolkitExpression<boolean>[])
983
1227
  ) as BindingToolkitExpression<T>;
984
1228
  } else if (annotationValue.hasOwnProperty("$And")) {
985
1229
  return and(
986
1230
  ...((annotationValue as AndConditionalExpression).$And.map(function (andCondition) {
987
- return parseAnnotationCondition(andCondition, visitedNavigationPaths, pathVisitor);
1231
+ return parseAnnotationCondition(andCondition, visitedNavigationPaths, pathVisitor, converterRoot);
988
1232
  }) as unknown as BindingToolkitExpression<boolean>[])
989
1233
  ) as BindingToolkitExpression<T>;
990
1234
  } else if (annotationValue.hasOwnProperty("$Not")) {
991
1235
  return not(
992
- parseAnnotationCondition((annotationValue as NotConditionalExpression).$Not, visitedNavigationPaths, pathVisitor)
1236
+ parseAnnotationCondition((annotationValue as NotConditionalExpression).$Not, visitedNavigationPaths, pathVisitor, converterRoot)
993
1237
  ) as BindingToolkitExpression<T>;
994
1238
  } else if (annotationValue.hasOwnProperty("$Eq")) {
995
1239
  return equal(
996
- parseAnnotationCondition((annotationValue as EqConditionalExpression).$Eq[0], visitedNavigationPaths, pathVisitor),
997
- parseAnnotationCondition((annotationValue as EqConditionalExpression).$Eq[1], visitedNavigationPaths, pathVisitor)
1240
+ parseAnnotationCondition(
1241
+ (annotationValue as EqConditionalExpression).$Eq[0],
1242
+ visitedNavigationPaths,
1243
+ pathVisitor,
1244
+ converterRoot
1245
+ ),
1246
+ parseAnnotationCondition(
1247
+ (annotationValue as EqConditionalExpression).$Eq[1],
1248
+ visitedNavigationPaths,
1249
+ pathVisitor,
1250
+ converterRoot
1251
+ )
998
1252
  ) as BindingToolkitExpression<T>;
999
1253
  } else if (annotationValue.hasOwnProperty("$Ne")) {
1000
1254
  return notEqual(
1001
- parseAnnotationCondition((annotationValue as NeConditionalExpression).$Ne[0], visitedNavigationPaths, pathVisitor),
1002
- parseAnnotationCondition((annotationValue as NeConditionalExpression).$Ne[1], visitedNavigationPaths, pathVisitor)
1255
+ parseAnnotationCondition(
1256
+ (annotationValue as NeConditionalExpression).$Ne[0],
1257
+ visitedNavigationPaths,
1258
+ pathVisitor,
1259
+ converterRoot
1260
+ ),
1261
+ parseAnnotationCondition(
1262
+ (annotationValue as NeConditionalExpression).$Ne[1],
1263
+ visitedNavigationPaths,
1264
+ pathVisitor,
1265
+ converterRoot
1266
+ )
1003
1267
  ) as BindingToolkitExpression<T>;
1004
1268
  } else if (annotationValue.hasOwnProperty("$Gt")) {
1005
1269
  return greaterThan(
1006
- parseAnnotationCondition((annotationValue as GtConditionalExpression).$Gt[0], visitedNavigationPaths, pathVisitor),
1007
- parseAnnotationCondition((annotationValue as GtConditionalExpression).$Gt[1], visitedNavigationPaths, pathVisitor)
1270
+ parseAnnotationCondition(
1271
+ (annotationValue as GtConditionalExpression).$Gt[0],
1272
+ visitedNavigationPaths,
1273
+ pathVisitor,
1274
+ converterRoot
1275
+ ),
1276
+ parseAnnotationCondition(
1277
+ (annotationValue as GtConditionalExpression).$Gt[1],
1278
+ visitedNavigationPaths,
1279
+ pathVisitor,
1280
+ converterRoot
1281
+ )
1008
1282
  ) as BindingToolkitExpression<T>;
1009
1283
  } else if (annotationValue.hasOwnProperty("$Ge")) {
1010
1284
  return greaterOrEqual(
1011
- parseAnnotationCondition((annotationValue as GeConditionalExpression).$Ge[0], visitedNavigationPaths, pathVisitor),
1012
- parseAnnotationCondition((annotationValue as GeConditionalExpression).$Ge[1], visitedNavigationPaths, pathVisitor)
1285
+ parseAnnotationCondition(
1286
+ (annotationValue as GeConditionalExpression).$Ge[0],
1287
+ visitedNavigationPaths,
1288
+ pathVisitor,
1289
+ converterRoot
1290
+ ),
1291
+ parseAnnotationCondition(
1292
+ (annotationValue as GeConditionalExpression).$Ge[1],
1293
+ visitedNavigationPaths,
1294
+ pathVisitor,
1295
+ converterRoot
1296
+ )
1013
1297
  ) as BindingToolkitExpression<T>;
1014
1298
  } else if (annotationValue.hasOwnProperty("$Lt")) {
1015
1299
  return lessThan(
1016
- parseAnnotationCondition((annotationValue as LtConditionalExpression).$Lt[0], visitedNavigationPaths, pathVisitor),
1017
- parseAnnotationCondition((annotationValue as LtConditionalExpression).$Lt[1], visitedNavigationPaths, pathVisitor)
1300
+ parseAnnotationCondition(
1301
+ (annotationValue as LtConditionalExpression).$Lt[0],
1302
+ visitedNavigationPaths,
1303
+ pathVisitor,
1304
+ converterRoot
1305
+ ),
1306
+ parseAnnotationCondition(
1307
+ (annotationValue as LtConditionalExpression).$Lt[1],
1308
+ visitedNavigationPaths,
1309
+ pathVisitor,
1310
+ converterRoot
1311
+ )
1018
1312
  ) as BindingToolkitExpression<T>;
1019
1313
  } else if (annotationValue.hasOwnProperty("$Le")) {
1020
1314
  return lessOrEqual(
1021
- parseAnnotationCondition((annotationValue as LeConditionalExpression).$Le[0], visitedNavigationPaths, pathVisitor),
1022
- parseAnnotationCondition((annotationValue as LeConditionalExpression).$Le[1], visitedNavigationPaths, pathVisitor)
1315
+ parseAnnotationCondition(
1316
+ (annotationValue as LeConditionalExpression).$Le[0],
1317
+ visitedNavigationPaths,
1318
+ pathVisitor,
1319
+ converterRoot
1320
+ ),
1321
+ parseAnnotationCondition(
1322
+ (annotationValue as LeConditionalExpression).$Le[1],
1323
+ visitedNavigationPaths,
1324
+ pathVisitor,
1325
+ converterRoot
1326
+ )
1023
1327
  ) as BindingToolkitExpression<T>;
1024
1328
  } else if (annotationValue.hasOwnProperty("$Path")) {
1025
1329
  return pathInModel((annotationValue as PathConditionExpression<T>).$Path, undefined, visitedNavigationPaths, pathVisitor);
@@ -1045,9 +1349,137 @@ function parseAnnotationCondition<T extends PrimitiveType>(
1045
1349
  pathVisitor
1046
1350
  ) as BindingToolkitExpression<T>;
1047
1351
  } else if (annotationValue.hasOwnProperty("$EnumMember")) {
1048
- return constant(resolveEnumValue((annotationValue as { $EnumMember?: string }).$EnumMember) as T);
1352
+ return constant(resolveEnumValue((annotationValue as { $EnumMember?: string }).$EnumMember, converterRoot) as T);
1353
+ } else if (annotationValue.hasOwnProperty("$In")) {
1354
+ return comparison(
1355
+ "In",
1356
+ parseAnnotationCondition(
1357
+ (annotationValue as InConditionalExpression).$In[0],
1358
+ visitedNavigationPaths,
1359
+ pathVisitor,
1360
+ converterRoot
1361
+ ),
1362
+ parseAnnotationCondition(
1363
+ (annotationValue as InConditionalExpression).$In[1],
1364
+ visitedNavigationPaths,
1365
+ pathVisitor,
1366
+ converterRoot
1367
+ )
1368
+ ) as BindingToolkitExpression<T>;
1369
+ } else if (annotationValue.hasOwnProperty("$Has")) {
1370
+ return comparison(
1371
+ "Has",
1372
+ parseAnnotationCondition(
1373
+ (annotationValue as HasConditionalExpression).$Has[0],
1374
+ visitedNavigationPaths,
1375
+ pathVisitor,
1376
+ converterRoot
1377
+ ),
1378
+ parseAnnotationCondition(
1379
+ (annotationValue as HasConditionalExpression).$Has[1],
1380
+ visitedNavigationPaths,
1381
+ pathVisitor,
1382
+ converterRoot
1383
+ )
1384
+ ) as BindingToolkitExpression<T>;
1385
+ } else if (annotationValue.hasOwnProperty("$Add")) {
1386
+ return add(
1387
+ parseAnnotationCondition(
1388
+ (annotationValue as AddConditionalExpression).$Add[0],
1389
+ visitedNavigationPaths,
1390
+ pathVisitor,
1391
+ converterRoot
1392
+ ),
1393
+ parseAnnotationCondition(
1394
+ (annotationValue as AddConditionalExpression).$Add[1],
1395
+ visitedNavigationPaths,
1396
+ pathVisitor,
1397
+ converterRoot
1398
+ )
1399
+ ) as BindingToolkitExpression<T>;
1400
+ } else if (annotationValue.hasOwnProperty("$Sub")) {
1401
+ return subtract(
1402
+ parseAnnotationCondition(
1403
+ (annotationValue as SubConditionalExpression).$Sub[0],
1404
+ visitedNavigationPaths,
1405
+ pathVisitor,
1406
+ converterRoot
1407
+ ),
1408
+ parseAnnotationCondition(
1409
+ (annotationValue as SubConditionalExpression).$Sub[1],
1410
+ visitedNavigationPaths,
1411
+ pathVisitor,
1412
+ converterRoot
1413
+ )
1414
+ ) as BindingToolkitExpression<T>;
1415
+ } else if (annotationValue.hasOwnProperty("$Mul")) {
1416
+ return multiply(
1417
+ parseAnnotationCondition(
1418
+ (annotationValue as MulConditionalExpression).$Mul[0],
1419
+ visitedNavigationPaths,
1420
+ pathVisitor,
1421
+ converterRoot
1422
+ ),
1423
+ parseAnnotationCondition(
1424
+ (annotationValue as MulConditionalExpression).$Mul[1],
1425
+ visitedNavigationPaths,
1426
+ pathVisitor,
1427
+ converterRoot
1428
+ )
1429
+ ) as BindingToolkitExpression<T>;
1430
+ } else if (annotationValue.hasOwnProperty("$Div")) {
1431
+ return floor(
1432
+ divide(
1433
+ parseAnnotationCondition(
1434
+ (annotationValue as DivConditionalExpression).$Div[0],
1435
+ visitedNavigationPaths,
1436
+ pathVisitor,
1437
+ converterRoot
1438
+ ),
1439
+ parseAnnotationCondition(
1440
+ (annotationValue as DivConditionalExpression).$Div[1],
1441
+ visitedNavigationPaths,
1442
+ pathVisitor,
1443
+ converterRoot
1444
+ )
1445
+ )
1446
+ ) as BindingToolkitExpression<T>;
1447
+ } else if (annotationValue.hasOwnProperty("$DivBy")) {
1448
+ return divide(
1449
+ parseAnnotationCondition(
1450
+ (annotationValue as DivByConditionalExpression).$DivBy[0],
1451
+ visitedNavigationPaths,
1452
+ pathVisitor,
1453
+ converterRoot
1454
+ ),
1455
+ parseAnnotationCondition(
1456
+ (annotationValue as DivByConditionalExpression).$DivBy[1],
1457
+ visitedNavigationPaths,
1458
+ pathVisitor,
1459
+ converterRoot
1460
+ )
1461
+ ) as BindingToolkitExpression<T>;
1462
+ } else if (annotationValue.hasOwnProperty("$Mod")) {
1463
+ return modulo(
1464
+ parseAnnotationCondition(
1465
+ (annotationValue as ModConditionalExpression).$Mod[0],
1466
+ visitedNavigationPaths,
1467
+ pathVisitor,
1468
+ converterRoot
1469
+ ),
1470
+ parseAnnotationCondition(
1471
+ (annotationValue as ModConditionalExpression).$Mod[1],
1472
+ visitedNavigationPaths,
1473
+ pathVisitor,
1474
+ converterRoot
1475
+ )
1476
+ ) as BindingToolkitExpression<T>;
1477
+ } else if (annotationValue.hasOwnProperty("$Neg")) {
1478
+ return negate(
1479
+ parseAnnotationCondition((annotationValue as NegConditionalExpression).$Neg, visitedNavigationPaths, pathVisitor, converterRoot)
1480
+ ) as BindingToolkitExpression<T>;
1049
1481
  }
1050
- return constant(false as T);
1482
+ return constant(annotationValue as T);
1051
1483
  }
1052
1484
 
1053
1485
  /**
@@ -1056,17 +1488,19 @@ function parseAnnotationCondition<T extends PrimitiveType>(
1056
1488
  * @param annotationValue An If expression returning the type T
1057
1489
  * @param visitedNavigationPaths The path from the root entity set
1058
1490
  * @param pathVisitor A function to modify the resulting path
1491
+ * @param converterRoot The root converted metadata
1059
1492
  * @returns The equivalent ifElse expression
1060
1493
  */
1061
1494
  export function annotationIfExpression<T extends PrimitiveType>(
1062
1495
  annotationValue: IfAnnotationExpressionValue<T>,
1063
1496
  visitedNavigationPaths: string[] = [],
1064
- pathVisitor?: Function
1497
+ pathVisitor?: Function,
1498
+ converterRoot?: ConvertedMetadata
1065
1499
  ): BindingToolkitExpression<T> {
1066
1500
  return ifElse(
1067
- parseAnnotationCondition(annotationValue[0], visitedNavigationPaths, pathVisitor),
1068
- parseAnnotationCondition(annotationValue[1] as ConditionalCheckOrValue, visitedNavigationPaths, pathVisitor),
1069
- parseAnnotationCondition(annotationValue[2] as ConditionalCheckOrValue, visitedNavigationPaths, pathVisitor)
1501
+ parseAnnotationCondition(annotationValue[0], visitedNavigationPaths, pathVisitor, converterRoot),
1502
+ parseAnnotationCondition(annotationValue[1] as ConditionalCheckOrValue, visitedNavigationPaths, pathVisitor, converterRoot),
1503
+ parseAnnotationCondition(annotationValue[2] as ConditionalCheckOrValue, visitedNavigationPaths, pathVisitor, converterRoot)
1070
1504
  );
1071
1505
  }
1072
1506
  // This type is not recursively transformed from the metamodel content, as such we have some ugly things there
@@ -1205,6 +1639,43 @@ function comparison<T extends PrimitiveType>(
1205
1639
  return constant(false);
1206
1640
  }
1207
1641
  return constant(leftExpression.value >= rightExpression.value);
1642
+
1643
+ case "Has": {
1644
+ if (
1645
+ leftExpression.value === null ||
1646
+ leftExpression.value === undefined ||
1647
+ rightExpression.value === null ||
1648
+ rightExpression.value === undefined
1649
+ ) {
1650
+ return constant(false);
1651
+ }
1652
+ const leftValueInt = leftExpression.value as number;
1653
+ const rightValueInt = rightExpression.value as number;
1654
+ return constant(!!(leftValueInt & rightValueInt));
1655
+ }
1656
+ case "In": {
1657
+ if (
1658
+ leftExpression.value === null ||
1659
+ leftExpression.value === undefined ||
1660
+ rightExpression.value === null ||
1661
+ rightExpression.value === undefined
1662
+ ) {
1663
+ return constant(false);
1664
+ }
1665
+ // Check all the children of the right expression are constants
1666
+ const allChildrenConstants = (rightExpression.value as T[]).every((item) => isConstant(item));
1667
+ if (allChildrenConstants) {
1668
+ const rightValues = (rightExpression.value as ConstantExpression<T>[]).map((item) => item.value);
1669
+ return constant(rightValues.includes(leftExpression.value));
1670
+ } else {
1671
+ return {
1672
+ _type: "Comparison",
1673
+ operator: operator,
1674
+ operand1: leftExpression,
1675
+ operand2: rightExpression
1676
+ };
1677
+ }
1678
+ }
1208
1679
  }
1209
1680
  } else {
1210
1681
  return {
@@ -1359,6 +1830,20 @@ export function lessThan<T extends DefinedPrimitiveType>(
1359
1830
  return comparison("<", leftOperand, rightOperand);
1360
1831
  }
1361
1832
 
1833
+ /**
1834
+ * Comparison: "Include" (In).
1835
+ * @template T The target type
1836
+ * @param leftOperand The operand on the left side
1837
+ * @param rightOperand The operand on the right side of the comparison
1838
+ * @returns An expression representing the comparison
1839
+ */
1840
+ export function includes<T extends DefinedPrimitiveType>(
1841
+ leftOperand: ExpressionOrPrimitive<T>,
1842
+ rightOperand: ExpressionOrPrimitive<T[]>
1843
+ ): BindingToolkitExpression<boolean> {
1844
+ return comparison("In", leftOperand, rightOperand as ExpressionOrPrimitive<T>);
1845
+ }
1846
+
1362
1847
  /**
1363
1848
  * If-then-else expression.
1364
1849
  *
@@ -1584,7 +2069,7 @@ export function formatResult<T, U extends Fn<T>>(
1584
2069
  }
1585
2070
 
1586
2071
  type BindingConstraints = {
1587
- scale?: number;
2072
+ scale?: number | string;
1588
2073
  precision?: number;
1589
2074
  maxLength?: number;
1590
2075
  nullable?: boolean;
@@ -1664,16 +2149,18 @@ export function formatWithTypeInformation<T>(
1664
2149
  parseAsString: false
1665
2150
  });
1666
2151
  }
1667
- if (outExpression.type === "sap.ui.model.odata.type.String" && isProperty(property)) {
2152
+ if (outExpression.type === "sap.ui.model.odata.type.String" && (isProperty(property) || isActionParameter(property))) {
1668
2153
  if (outExpression.constraints?.isDigitSequence || (outExpression.constraints?.nullable !== false && property.nullable === true)) {
1669
2154
  outExpression.formatOptions.parseKeepsEmptyString = false;
1670
2155
  } else {
1671
2156
  outExpression.formatOptions.parseKeepsEmptyString = true;
1672
2157
  }
1673
- const fiscalType = getFiscalType(property);
1674
- if (fiscalType) {
1675
- outExpression.formatOptions.fiscalType = fiscalType;
1676
- outExpression.type = "sap.fe.core.type.FiscalDate";
2158
+ if (isProperty(property)) {
2159
+ const fiscalType = getFiscalType(property);
2160
+ if (fiscalType) {
2161
+ outExpression.formatOptions.fiscalType = fiscalType;
2162
+ outExpression.type = "sap.fe.core.type.FiscalDate";
2163
+ }
1677
2164
  }
1678
2165
  }
1679
2166
  const typesSupportingParseEmptyValueToZero = [
@@ -1899,6 +2386,51 @@ export function isEmpty(expression: BindingToolkitExpression<string>): BindingTo
1899
2386
  });
1900
2387
  return and(...aBindings);
1901
2388
  }
2389
+ /**
2390
+ * Shortcut function to determine if a binding can be statically resolvable.
2391
+ * @param exp The expression to evaluate
2392
+ * @returns A Boolean evaluating if there is no relative path dependent on the current context
2393
+ */
2394
+ export function isExpressionStaticallyResolvable<T extends PrimitiveType>(exp: BindingToolkitExpression<T>): boolean {
2395
+ const referencedPaths: string[] = [];
2396
+ transformRecursively(
2397
+ exp,
2398
+ "PathInModel",
2399
+ (pathExp) => {
2400
+ if (pathExp.modelName === undefined && typeof pathExp.path === "string") {
2401
+ referencedPaths.push(pathExp.path);
2402
+ }
2403
+ return pathExp;
2404
+ },
2405
+ true
2406
+ );
2407
+ if (referencedPaths.length === 0) {
2408
+ return true;
2409
+ }
2410
+ return referencedPaths.every((p) => p.startsWith("/"));
2411
+ }
2412
+
2413
+ export function getStaticallyResolvableExpression<T extends PrimitiveType>(
2414
+ exp: BindingToolkitExpression<T>,
2415
+ defaultValue: BindingToolkitExpression<T>
2416
+ ): BindingToolkitExpression<T> {
2417
+ if (exp._type === "Set") {
2418
+ const newOperands = exp.operands.map((operand: BindingToolkitExpression<boolean>) => {
2419
+ if (isExpressionStaticallyResolvable(operand) || isConstant(operand)) {
2420
+ return operand;
2421
+ } else {
2422
+ return defaultValue;
2423
+ }
2424
+ });
2425
+
2426
+ if (exp.operator === "||") {
2427
+ return or(...(newOperands as unknown as BindingToolkitExpression<boolean>[])) as unknown as BindingToolkitExpression<T>;
2428
+ } else {
2429
+ return and(...(newOperands as unknown as BindingToolkitExpression<boolean>[])) as unknown as BindingToolkitExpression<T>;
2430
+ }
2431
+ }
2432
+ return isExpressionStaticallyResolvable(exp) ? exp : defaultValue;
2433
+ }
1902
2434
 
1903
2435
  export function concat(...inExpressions: ExpressionOrPrimitive<string>[]): BindingToolkitExpression<string> {
1904
2436
  const expressions = inExpressions.map(wrapPrimitive);
@@ -2048,6 +2580,21 @@ export function transformRecursively<T extends PrimitiveType | unknown, ET exten
2048
2580
  expression = comparison(expression.operator, operand1, operand2) as BindingToolkitExpression<T>;
2049
2581
  }
2050
2582
  break;
2583
+ case "Arithmetic":
2584
+ const operand1 = transformRecursively(
2585
+ expression.operand1 as BindingToolkitExpression<T>,
2586
+ expressionType,
2587
+ transformFunction,
2588
+ includeAllExpression
2589
+ );
2590
+ const operand2 = transformRecursively(
2591
+ expression.operand2 as BindingToolkitExpression<T>,
2592
+ expressionType,
2593
+ transformFunction,
2594
+ includeAllExpression
2595
+ );
2596
+ expression = arithmetic(expression.operator, operand1, operand2) as BindingToolkitExpression<T>;
2597
+ break;
2051
2598
  case "Constant":
2052
2599
  const constantValue: Record<string, BindingToolkitExpression<unknown>> = expression.value as Record<
2053
2600
  string,
@@ -2364,6 +2911,9 @@ export function compileExpression<T extends PrimitiveType>(
2364
2911
  case "Not":
2365
2912
  const notExpression = `!${compileExpression(expr.operand, true)}`;
2366
2913
  return wrapBindingExpression(notExpression, embeddedInBinding);
2914
+ case "Arithmetic":
2915
+ const arithmeticExpression = compileArithmeticExpression(expr);
2916
+ return wrapBindingExpression(arithmeticExpression, embeddedInBinding);
2367
2917
 
2368
2918
  case "Truthy":
2369
2919
  const truthyExpression = `!!${compileExpression(expr.operand, true)}`;
@@ -2382,6 +2932,25 @@ export function compileExpression<T extends PrimitiveType>(
2382
2932
  }
2383
2933
  }
2384
2934
 
2935
+ function compileArithmeticExpression(expression: ArithmeticExpression): string {
2936
+ function compileOperand(operand: BindingToolkitExpression<unknown>): CompiledBindingToolkitExpression {
2937
+ const compiledOperand = compileExpression(operand, true) ?? "undefined";
2938
+ return wrapBindingExpression(compiledOperand, true, needParenthesis(operand));
2939
+ }
2940
+ switch (expression.operator) {
2941
+ case "Neg":
2942
+ return `-(${compileOperand(expression.operand1)})`;
2943
+ case "Floor":
2944
+ return `Math.floor(${compileOperand(expression.operand1)})`;
2945
+ case "%":
2946
+ case "/":
2947
+ case "*":
2948
+ case "+":
2949
+ case "-":
2950
+ return `${compileOperand(expression.operand1)} ${expression.operator} ${compileOperand(expression.operand2)}`;
2951
+ }
2952
+ }
2953
+
2385
2954
  /**
2386
2955
  * Compile a comparison expression.
2387
2956
  * @param expression The comparison expression.
@@ -2392,8 +2961,25 @@ function compileComparisonExpression(expression: ComparisonExpression): string {
2392
2961
  const compiledOperand = compileExpression(operand, true) ?? "undefined";
2393
2962
  return wrapBindingExpression(compiledOperand, true, needParenthesis(operand));
2394
2963
  }
2395
-
2396
- return `${compileOperand(expression.operand1)} ${expression.operator} ${compileOperand(expression.operand2)}`;
2964
+ switch (expression.operator) {
2965
+ case "In": {
2966
+ const path = compileOperand(expression.operand1);
2967
+ const collection = compileOperand(expression.operand2) ?? [];
2968
+ return `${collection}.includes(${path})`;
2969
+ }
2970
+ case "Has": {
2971
+ const path = compileOperand(expression.operand1);
2972
+ const flags = compileOperand(expression.operand2) ?? [];
2973
+ return `(${path} & ${flags}) > 0`;
2974
+ }
2975
+ case "===":
2976
+ case "!==":
2977
+ case ">=":
2978
+ case ">":
2979
+ case "<=":
2980
+ case "<":
2981
+ return `${compileOperand(expression.operand1)} ${expression.operator} ${compileOperand(expression.operand2)}`;
2982
+ }
2397
2983
  }
2398
2984
 
2399
2985
  /**