@zenstackhq/plugin-policy 3.3.0-beta.3 → 3.3.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
@@ -4,13 +4,13 @@ var __name = (target, value) => __defProp(target, "name", { value, configurable:
4
4
  // src/functions.ts
5
5
  import { invariant as invariant4 } from "@zenstackhq/common-helpers";
6
6
  import { CRUD, QueryUtils as QueryUtils3 } from "@zenstackhq/orm";
7
- import { ExpressionWrapper as ExpressionWrapper2, ValueNode as ValueNode4 } from "kysely";
7
+ import { ExpressionWrapper as ExpressionWrapper3, ValueNode as ValueNode4 } from "kysely";
8
8
 
9
9
  // src/policy-handler.ts
10
10
  import { invariant as invariant3 } from "@zenstackhq/common-helpers";
11
11
  import { getCrudDialect as getCrudDialect2, QueryUtils as QueryUtils2, RejectedByPolicyReason, SchemaUtils as SchemaUtils2 } from "@zenstackhq/orm";
12
12
  import { ExpressionUtils as ExpressionUtils4 } from "@zenstackhq/orm/schema";
13
- import { AliasNode as AliasNode3, BinaryOperationNode as BinaryOperationNode3, ColumnNode as ColumnNode3, DeleteQueryNode, expressionBuilder as expressionBuilder2, ExpressionWrapper, FromNode as FromNode2, IdentifierNode as IdentifierNode2, InsertQueryNode, OperationNodeTransformer, OperatorNode as OperatorNode3, ParensNode as ParensNode2, PrimitiveValueListNode, ReferenceNode as ReferenceNode3, ReturningNode, SelectAllNode, SelectionNode as SelectionNode2, SelectQueryNode as SelectQueryNode2, sql, TableNode as TableNode3, UpdateQueryNode, ValueNode as ValueNode3, ValuesNode, WhereNode as WhereNode2 } from "kysely";
13
+ import { AliasNode as AliasNode3, BinaryOperationNode as BinaryOperationNode3, ColumnNode as ColumnNode3, DeleteQueryNode, expressionBuilder as expressionBuilder2, ExpressionWrapper as ExpressionWrapper2, FromNode as FromNode2, IdentifierNode as IdentifierNode2, InsertQueryNode, OperationNodeTransformer, OperatorNode as OperatorNode3, ParensNode as ParensNode2, PrimitiveValueListNode, ReferenceNode as ReferenceNode3, ReturningNode, SelectAllNode, SelectionNode as SelectionNode2, SelectQueryNode as SelectQueryNode2, sql, TableNode as TableNode3, UpdateQueryNode, ValueNode as ValueNode3, ValuesNode, WhereNode as WhereNode2 } from "kysely";
14
14
  import { match as match3 } from "ts-pattern";
15
15
 
16
16
  // src/column-collector.ts
@@ -36,7 +36,7 @@ var ColumnCollector = class extends KyselyUtils.DefaultOperationNodeVisitor {
36
36
  import { invariant as invariant2 } from "@zenstackhq/common-helpers";
37
37
  import { getCrudDialect, QueryUtils, SchemaUtils } from "@zenstackhq/orm";
38
38
  import { ExpressionUtils as ExpressionUtils3 } from "@zenstackhq/orm/schema";
39
- import { AliasNode as AliasNode2, BinaryOperationNode as BinaryOperationNode2, ColumnNode as ColumnNode2, expressionBuilder, FromNode, FunctionNode as FunctionNode2, IdentifierNode, OperatorNode as OperatorNode2, ReferenceNode as ReferenceNode2, SelectionNode, SelectQueryNode, TableNode as TableNode2, ValueListNode, ValueNode as ValueNode2, WhereNode } from "kysely";
39
+ import { AliasNode as AliasNode2, BinaryOperationNode as BinaryOperationNode2, ColumnNode as ColumnNode2, expressionBuilder, ExpressionWrapper, FromNode, FunctionNode as FunctionNode2, IdentifierNode, OperatorNode as OperatorNode2, ReferenceNode as ReferenceNode2, SelectionNode, SelectQueryNode, TableNode as TableNode2, ValueListNode, ValueNode as ValueNode2, WhereNode } from "kysely";
40
40
  import { match as match2 } from "ts-pattern";
41
41
 
42
42
  // src/expression-evaluator.ts
@@ -287,6 +287,7 @@ var ExpressionTransformer = class {
287
287
  }
288
288
  client;
289
289
  dialect;
290
+ eb = expressionBuilder();
290
291
  constructor(client) {
291
292
  this.client = client;
292
293
  this.dialect = getCrudDialect(this.schema, this.clientOptions);
@@ -311,13 +312,15 @@ var ExpressionTransformer = class {
311
312
  if (!handler) {
312
313
  throw new Error(`Unsupported expression kind: ${expression.kind}`);
313
314
  }
314
- return handler.value.call(this, expression, context);
315
+ const result = handler.value.call(this, expression, context);
316
+ invariant2("kind" in result, `expression handler must return an OperationNode: transforming ${expression.kind}`);
317
+ return result;
315
318
  }
316
319
  _literal(expr2) {
317
320
  return this.transformValue(expr2.value, typeof expr2.value === "string" ? "String" : typeof expr2.value === "boolean" ? "Boolean" : "Int");
318
321
  }
319
322
  _array(expr2, context) {
320
- return ValueListNode.create(expr2.items.map((item) => this.transform(item, context)));
323
+ return this.dialect.buildArrayValue(expr2.items.map((item) => new ExpressionWrapper(this.transform(item, context))), expr2.type).toOperationNode();
321
324
  }
322
325
  _field(expr2, context) {
323
326
  if (context.contextValue) {
@@ -604,9 +607,11 @@ var ExpressionTransformer = class {
604
607
  return trueNode(this.dialect);
605
608
  } else if (value === false) {
606
609
  return falseNode(this.dialect);
610
+ } else if (Array.isArray(value)) {
611
+ return this.dialect.buildArrayValue(value.map((v) => new ExpressionWrapper(this.transformValue(v, type))), type).toOperationNode();
607
612
  } else {
608
613
  const transformed = this.dialect.transformInput(value, type, false) ?? null;
609
- if (!Array.isArray(transformed)) {
614
+ if (typeof transformed !== "string") {
610
615
  return ValueNode2.createImmediate(transformed);
611
616
  } else {
612
617
  return ValueNode2.create(transformed);
@@ -630,8 +635,7 @@ var ExpressionTransformer = class {
630
635
  if (!func) {
631
636
  throw createUnsupportedError(`Function not implemented: ${expr2.function}`);
632
637
  }
633
- const eb = expressionBuilder();
634
- return func(eb, (expr2.args ?? []).map((arg) => this.transformCallArg(eb, arg, context)), {
638
+ return func(this.eb, (expr2.args ?? []).map((arg) => this.transformCallArg(arg, context)), {
635
639
  client: this.client,
636
640
  dialect: this.dialect,
637
641
  model: context.modelOrType,
@@ -651,21 +655,12 @@ var ExpressionTransformer = class {
651
655
  }
652
656
  return func;
653
657
  }
654
- transformCallArg(eb, arg, context) {
655
- if (ExpressionUtils3.isLiteral(arg)) {
656
- return eb.val(arg.value);
657
- }
658
+ transformCallArg(arg, context) {
658
659
  if (ExpressionUtils3.isField(arg)) {
659
- return eb.ref(arg.field);
660
- }
661
- if (ExpressionUtils3.isCall(arg)) {
662
- return this.transformCall(arg, context);
663
- }
664
- if (this.isAuthMember(arg)) {
665
- const valNode = this.valueMemberAccess(this.auth, arg, this.authType);
666
- return valNode ? eb.val(valNode.value) : eb.val(null);
660
+ return this.eb.ref(arg.field);
661
+ } else {
662
+ return new ExpressionWrapper(this.transform(arg, context));
667
663
  }
668
- throw createUnsupportedError(`Unsupported argument expression: ${arg.kind}`);
669
664
  }
670
665
  _member(expr2, context) {
671
666
  if (ExpressionUtils3.isBinding(expr2.receiver)) {
@@ -1068,7 +1063,7 @@ var PolicyHandler = class extends OperationNodeTransformer {
1068
1063
  modelLevelFilter,
1069
1064
  node.where?.where ?? trueNode(this.dialect)
1070
1065
  ]);
1071
- const preUpdateCheckQuery = this.eb.selectFrom(mutationModel).select((eb) => eb.fn.coalesce(eb.fn.sum(this.dialect.castInt(new ExpressionWrapper(logicalNot(this.dialect, fieldLevelFilter)))), eb.lit(0)).as("$filteredCount")).where(() => new ExpressionWrapper(updateFilter));
1066
+ const preUpdateCheckQuery = this.eb.selectFrom(mutationModel).select((eb) => eb.fn.coalesce(eb.fn.sum(this.dialect.castInt(new ExpressionWrapper2(logicalNot(this.dialect, fieldLevelFilter)))), eb.lit(0)).as("$filteredCount")).where(() => new ExpressionWrapper2(updateFilter));
1072
1067
  const preUpdateResult = await proceed(preUpdateCheckQuery.toOperationNode());
1073
1068
  if (preUpdateResult.rows[0].$filteredCount > 0) {
1074
1069
  throw createRejectedByPolicyError(mutationModel, RejectedByPolicyReason.NO_ACCESS, "some rows cannot be updated due to field policies");
@@ -1119,10 +1114,10 @@ var PolicyHandler = class extends OperationNodeTransformer {
1119
1114
  eb(eb.fn("COUNT", [
1120
1115
  eb.lit(1)
1121
1116
  ]), "=", Number(updateResult.numAffectedRows ?? 0)).as("$condition")
1122
- ]).where(() => new ExpressionWrapper(conjunction(this.dialect, [
1117
+ ]).where(() => new ExpressionWrapper2(conjunction(this.dialect, [
1123
1118
  idConditions,
1124
1119
  postUpdateFilter
1125
- ]))).$if(needsBeforeUpdateJoin, (qb) => qb.leftJoin(() => new ExpressionWrapper(beforeUpdateTable).as("$before"), (join) => {
1120
+ ]))).$if(needsBeforeUpdateJoin, (qb) => qb.leftJoin(() => new ExpressionWrapper2(beforeUpdateTable).as("$before"), (join) => {
1126
1121
  const idFields = QueryUtils2.requireIdFields(this.client.$schema, model);
1127
1122
  return idFields.reduce((acc, f) => acc.onRef(`${model}.${f}`, "=", `$before.${f}`), join);
1128
1123
  }));
@@ -1232,7 +1227,7 @@ var PolicyHandler = class extends OperationNodeTransformer {
1232
1227
  if (!columnName) {
1233
1228
  return update;
1234
1229
  }
1235
- const wrappedValue = sql`IF(${new ExpressionWrapper(filter)}, ${new ExpressionWrapper(update.value)}, ${sql.ref(columnName)})`.toOperationNode();
1230
+ const wrappedValue = sql`IF(${new ExpressionWrapper2(filter)}, ${new ExpressionWrapper2(update.value)}, ${sql.ref(columnName)})`.toOperationNode();
1236
1231
  return {
1237
1232
  ...update,
1238
1233
  value: wrappedValue
@@ -1409,7 +1404,7 @@ var PolicyHandler = class extends OperationNodeTransformer {
1409
1404
  };
1410
1405
  }
1411
1406
  const eb = expressionBuilder2();
1412
- const selection = eb.case().when(new ExpressionWrapper(filter)).then(eb.ref(field)).else(null).end().as(field).toOperationNode();
1407
+ const selection = eb.case().when(new ExpressionWrapper2(filter)).then(eb.ref(field)).else(null).end().as(field).toOperationNode();
1413
1408
  return {
1414
1409
  hasPolicies: true,
1415
1410
  selection: SelectionNode2.create(selection)
@@ -1489,9 +1484,9 @@ var PolicyHandler = class extends OperationNodeTransformer {
1489
1484
  invariant3(bValue !== null && bValue !== void 0, "B value cannot be null or undefined");
1490
1485
  const eb = expressionBuilder2();
1491
1486
  const filterA = this.buildPolicyFilter(m2m.firstModel, void 0, "update");
1492
- const queryA = eb.selectFrom(m2m.firstModel).where(eb(eb.ref(`${m2m.firstModel}.${m2m.firstIdField}`), "=", aValue)).select(() => new ExpressionWrapper(filterA).as("$t"));
1487
+ const queryA = eb.selectFrom(m2m.firstModel).where(eb(eb.ref(`${m2m.firstModel}.${m2m.firstIdField}`), "=", aValue)).select(() => new ExpressionWrapper2(filterA).as("$t"));
1493
1488
  const filterB = this.buildPolicyFilter(m2m.secondModel, void 0, "update");
1494
- const queryB = eb.selectFrom(m2m.secondModel).where(eb(eb.ref(`${m2m.secondModel}.${m2m.secondIdField}`), "=", bValue)).select(() => new ExpressionWrapper(filterB).as("$t"));
1489
+ const queryB = eb.selectFrom(m2m.secondModel).where(eb(eb.ref(`${m2m.secondModel}.${m2m.secondIdField}`), "=", bValue)).select(() => new ExpressionWrapper2(filterB).as("$t"));
1495
1490
  const queryNode = {
1496
1491
  kind: "SelectQueryNode",
1497
1492
  selections: [
@@ -1515,7 +1510,7 @@ var PolicyHandler = class extends OperationNodeTransformer {
1515
1510
  for (const def of allFields) {
1516
1511
  const index = fields.indexOf(def.name);
1517
1512
  if (index >= 0) {
1518
- allValues.push(new ExpressionWrapper(values[index]));
1513
+ allValues.push(new ExpressionWrapper2(values[index]));
1519
1514
  } else {
1520
1515
  allValues.push(this.eb.lit(null));
1521
1516
  }
@@ -1524,7 +1519,7 @@ var PolicyHandler = class extends OperationNodeTransformer {
1524
1519
  allValues
1525
1520
  ]);
1526
1521
  const filter = this.buildPolicyFilter(model, void 0, "create");
1527
- const preCreateCheck = this.eb.selectFrom(valuesTable.as(model)).select(this.eb(this.eb.fn.count(this.eb.lit(1)), ">", 0).as("$condition")).where(() => new ExpressionWrapper(filter));
1522
+ const preCreateCheck = this.eb.selectFrom(valuesTable.as(model)).select(this.eb(this.eb.fn.count(this.eb.lit(1)), ">", 0).as("$condition")).where(() => new ExpressionWrapper2(filter));
1528
1523
  const result = await proceed(preCreateCheck.toOperationNode());
1529
1524
  if (!result.rows[0]?.$condition) {
1530
1525
  throw createRejectedByPolicyError(model, RejectedByPolicyReason.NO_ACCESS);
@@ -1560,8 +1555,9 @@ var PolicyHandler = class extends OperationNodeTransformer {
1560
1555
  value = this.dialect.transformInput(item, fieldDef.type, !!fieldDef.array);
1561
1556
  }
1562
1557
  if (Array.isArray(value)) {
1558
+ const fieldDef = QueryUtils2.requireField(this.client.$schema, model, fields[i]);
1563
1559
  result.push({
1564
- node: this.dialect.buildArrayLiteralSQL(value).toOperationNode(),
1560
+ node: this.dialect.buildArrayValue(value, fieldDef.type).toOperationNode(),
1565
1561
  raw: value
1566
1562
  });
1567
1563
  } else {
@@ -1810,8 +1806,8 @@ var PolicyHandler = class extends OperationNodeTransformer {
1810
1806
  }
1811
1807
  const checkForOperation = operation === "read" ? "read" : "update";
1812
1808
  const joinTable = alias ?? tableName;
1813
- const aQuery = this.eb.selectFrom(m2m.firstModel).whereRef(`${m2m.firstModel}.${m2m.firstIdField}`, "=", `${joinTable}.A`).select(() => new ExpressionWrapper(this.buildPolicyFilter(m2m.firstModel, void 0, checkForOperation)).as("$conditionA"));
1814
- const bQuery = this.eb.selectFrom(m2m.secondModel).whereRef(`${m2m.secondModel}.${m2m.secondIdField}`, "=", `${joinTable}.B`).select(() => new ExpressionWrapper(this.buildPolicyFilter(m2m.secondModel, void 0, checkForOperation)).as("$conditionB"));
1809
+ const aQuery = this.eb.selectFrom(m2m.firstModel).whereRef(`${m2m.firstModel}.${m2m.firstIdField}`, "=", `${joinTable}.A`).select(() => new ExpressionWrapper2(this.buildPolicyFilter(m2m.firstModel, void 0, checkForOperation)).as("$conditionA"));
1810
+ const bQuery = this.eb.selectFrom(m2m.secondModel).whereRef(`${m2m.secondModel}.${m2m.secondIdField}`, "=", `${joinTable}.B`).select(() => new ExpressionWrapper2(this.buildPolicyFilter(m2m.secondModel, void 0, checkForOperation)).as("$conditionB"));
1815
1811
  return this.eb.and([
1816
1812
  aQuery,
1817
1813
  bQuery
@@ -1911,7 +1907,7 @@ var check = /* @__PURE__ */ __name((eb, args, { client, model, modelAlias, opera
1911
1907
  const policyHandler = new PolicyHandler(client);
1912
1908
  const op = arg2Node ? arg2Node.value : operation;
1913
1909
  const policyCondition = policyHandler.buildPolicyFilter(relationModel, void 0, op);
1914
- const result = eb.selectFrom(eb.selectFrom(relationModel).where(joinCondition).select(new ExpressionWrapper2(policyCondition).as("$condition")).as("$sub")).selectAll();
1910
+ const result = eb.selectFrom(eb.selectFrom(relationModel).where(joinCondition).select(new ExpressionWrapper3(policyCondition).as("$condition")).as("$sub")).selectAll();
1915
1911
  return result;
1916
1912
  }, "check");
1917
1913