@zenstackhq/language 3.0.0-beta.3 → 3.0.0-beta.5

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.
package/dist/index.js CHANGED
@@ -5354,7 +5354,7 @@ function getFunctionExpressionContext(funcDecl) {
5354
5354
  }
5355
5355
  __name(getFunctionExpressionContext, "getFunctionExpressionContext");
5356
5356
  function isCheckInvocation(node) {
5357
- return isInvocationExpr(node) && node.function.ref?.name === "check" && isFromStdlib(node.function.ref);
5357
+ return isInvocationExpr(node) && node.function.ref?.name === "check";
5358
5358
  }
5359
5359
  __name(isCheckInvocation, "isCheckInvocation");
5360
5360
  function resolveTransitiveImports(documents, model) {
@@ -5649,6 +5649,7 @@ var AttributeApplicationValidator = class {
5649
5649
  });
5650
5650
  }
5651
5651
  }
5652
+ // TODO: design a way to let plugin register validation
5652
5653
  _checkModelLevelPolicy(attr, accept) {
5653
5654
  const kind = getStringLiteral(attr.args[0]?.value);
5654
5655
  if (!kind) {
@@ -5664,8 +5665,49 @@ var AttributeApplicationValidator = class {
5664
5665
  "delete",
5665
5666
  "all"
5666
5667
  ], attr, accept);
5667
- this.rejectEncryptedFields(attr, accept);
5668
+ if ((kind === "create" || kind === "all") && attr.args[1]?.value) {
5669
+ this.rejectNonOwnedRelationInExpression(attr.args[1].value, accept);
5670
+ }
5671
+ }
5672
+ rejectNonOwnedRelationInExpression(expr, accept) {
5673
+ const contextModel = AstUtils2.getContainerOfType(expr, isDataModel);
5674
+ if (!contextModel) {
5675
+ return;
5676
+ }
5677
+ if (AstUtils2.streamAst(expr).some((node) => {
5678
+ if (!isDataFieldReference(node)) {
5679
+ return false;
5680
+ }
5681
+ if (node.target.ref?.$container !== contextModel) {
5682
+ return false;
5683
+ }
5684
+ const field = node.target.ref;
5685
+ if (!isRelationshipField(field)) {
5686
+ return false;
5687
+ }
5688
+ if (isAuthOrAuthMemberAccess(node)) {
5689
+ return false;
5690
+ }
5691
+ const startNode = isCollectionPredicate(node.$container) && node.$container.left === node ? node.$container : node;
5692
+ const collectionPredicate = AstUtils2.getContainerOfType(startNode.$container, isCollectionPredicate);
5693
+ if (collectionPredicate && isAuthOrAuthMemberAccess(collectionPredicate.left)) {
5694
+ return false;
5695
+ }
5696
+ const relationAttr = field.attributes.find((attr) => attr.decl.ref?.name === "@relation");
5697
+ if (!relationAttr) {
5698
+ return true;
5699
+ }
5700
+ if (!relationAttr.args.some((arg) => arg.name === "fields")) {
5701
+ return true;
5702
+ }
5703
+ return false;
5704
+ })) {
5705
+ accept("error", `non-owned relation fields are not allowed in "create" rules`, {
5706
+ node: expr
5707
+ });
5708
+ }
5668
5709
  }
5710
+ // TODO: design a way to let plugin register validation
5669
5711
  _checkFieldLevelPolicy(attr, accept) {
5670
5712
  const kind = getStringLiteral(attr.args[0]?.value);
5671
5713
  if (!kind) {
@@ -5693,7 +5735,6 @@ var AttributeApplicationValidator = class {
5693
5735
  });
5694
5736
  }
5695
5737
  }
5696
- this.rejectEncryptedFields(attr, accept);
5697
5738
  }
5698
5739
  _checkValidate(attr, accept) {
5699
5740
  const condition = attr.args[0]?.value;
@@ -5743,15 +5784,6 @@ var AttributeApplicationValidator = class {
5743
5784
  });
5744
5785
  }
5745
5786
  }
5746
- rejectEncryptedFields(attr, accept) {
5747
- AstUtils2.streamAllContents(attr).forEach((node) => {
5748
- if (isDataFieldReference(node) && hasAttribute(node.target.ref, "@encrypted")) {
5749
- accept("error", `Encrypted fields cannot be used in policy rules`, {
5750
- node
5751
- });
5752
- }
5753
- });
5754
- }
5755
5787
  validatePolicyKinds(kind, candidates, attr, accept) {
5756
5788
  const items = kind.split(",").map((x) => x.trim());
5757
5789
  items.forEach((item) => {
@@ -6509,23 +6541,25 @@ var ExpressionValidator = class {
6509
6541
  "Any"
6510
6542
  ];
6511
6543
  }
6512
- if (typeof expr.left.$resolvedType?.decl !== "string" || !supportedShapes.includes(expr.left.$resolvedType.decl)) {
6544
+ const leftResolvedDecl = expr.left.$resolvedType?.decl;
6545
+ const rightResolvedDecl = expr.right.$resolvedType?.decl;
6546
+ if (leftResolvedDecl && (typeof leftResolvedDecl !== "string" || !supportedShapes.includes(leftResolvedDecl))) {
6513
6547
  accept("error", `invalid operand type for "${expr.operator}" operator`, {
6514
6548
  node: expr.left
6515
6549
  });
6516
6550
  return;
6517
6551
  }
6518
- if (typeof expr.right.$resolvedType?.decl !== "string" || !supportedShapes.includes(expr.right.$resolvedType.decl)) {
6552
+ if (rightResolvedDecl && (typeof rightResolvedDecl !== "string" || !supportedShapes.includes(rightResolvedDecl))) {
6519
6553
  accept("error", `invalid operand type for "${expr.operator}" operator`, {
6520
6554
  node: expr.right
6521
6555
  });
6522
6556
  return;
6523
6557
  }
6524
- if (expr.left.$resolvedType.decl === "DateTime" && expr.right.$resolvedType.decl !== "DateTime") {
6558
+ if (leftResolvedDecl === "DateTime" && rightResolvedDecl && rightResolvedDecl !== "DateTime") {
6525
6559
  accept("error", "incompatible operand types", {
6526
6560
  node: expr
6527
6561
  });
6528
- } else if (expr.right.$resolvedType.decl === "DateTime" && expr.left.$resolvedType.decl !== "DateTime") {
6562
+ } else if (rightResolvedDecl === "DateTime" && leftResolvedDecl && leftResolvedDecl !== "DateTime") {
6529
6563
  accept("error", "incompatible operand types", {
6530
6564
  node: expr
6531
6565
  });
@@ -6565,11 +6599,11 @@ var ExpressionValidator = class {
6565
6599
  });
6566
6600
  }
6567
6601
  if (isDataFieldReference(expr.left) && (isThisExpr(expr.right) || isDataFieldReference(expr.right))) {
6568
- accept("error", "comparison between model-typed fields are not supported", {
6602
+ accept("error", "comparison between models is not supported", {
6569
6603
  node: expr
6570
6604
  });
6571
6605
  } else if (isDataFieldReference(expr.right) && (isThisExpr(expr.left) || isDataFieldReference(expr.left))) {
6572
- accept("error", "comparison between model-typed fields are not supported", {
6606
+ accept("error", "comparison between models is not supported", {
6573
6607
  node: expr
6574
6608
  });
6575
6609
  }
@@ -6761,6 +6795,7 @@ var FunctionInvocationValidator = class {
6761
6795
  }
6762
6796
  return true;
6763
6797
  }
6798
+ // TODO: move this to policy plugin
6764
6799
  _checkCheck(expr, accept) {
6765
6800
  let valid = true;
6766
6801
  const fieldArg = expr.args[0].value;