@zenstackhq/runtime 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.
@@ -2,46 +2,50 @@ var __defProp = Object.defineProperty;
2
2
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
3
 
4
4
  // src/plugins/policy/errors.ts
5
+ var RejectedByPolicyReason = /* @__PURE__ */ function(RejectedByPolicyReason2) {
6
+ RejectedByPolicyReason2["NO_ACCESS"] = "no-access";
7
+ RejectedByPolicyReason2["CANNOT_READ_BACK"] = "cannot-read-back";
8
+ RejectedByPolicyReason2["OTHER"] = "other";
9
+ return RejectedByPolicyReason2;
10
+ }({});
5
11
  var RejectedByPolicyError = class extends Error {
6
12
  static {
7
13
  __name(this, "RejectedByPolicyError");
8
14
  }
9
15
  model;
10
16
  reason;
11
- constructor(model, reason) {
12
- super(reason ?? `Operation rejected by policy${model ? ": " + model : ""}`), this.model = model, this.reason = reason;
17
+ constructor(model, reason = "no-access", message) {
18
+ super(message ?? `Operation rejected by policy${model ? ": " + model : ""}`), this.model = model, this.reason = reason;
13
19
  }
14
20
  };
15
21
 
16
- // src/plugins/policy/policy-handler.ts
17
- import { invariant as invariant6 } from "@zenstackhq/common-helpers";
18
- import { AliasNode as AliasNode3, BinaryOperationNode as BinaryOperationNode3, ColumnNode as ColumnNode2, DeleteQueryNode, FromNode as FromNode2, IdentifierNode as IdentifierNode2, InsertQueryNode, OperationNodeTransformer, OperatorNode as OperatorNode3, PrimitiveValueListNode, RawNode, ReturningNode, SelectionNode as SelectionNode2, SelectQueryNode as SelectQueryNode2, TableNode as TableNode3, UpdateQueryNode, ValueNode as ValueNode3, ValuesNode, WhereNode as WhereNode2 } from "kysely";
19
- import { match as match8 } from "ts-pattern";
22
+ // src/plugins/policy/functions.ts
23
+ import { invariant as invariant8 } from "@zenstackhq/common-helpers";
24
+ import { ExpressionWrapper as ExpressionWrapper2, ValueNode as ValueNode4 } from "kysely";
20
25
 
21
- // src/client/crud/dialects/index.ts
22
- import { match as match5 } from "ts-pattern";
23
-
24
- // src/client/crud/dialects/postgresql.ts
25
- import { invariant as invariant2 } from "@zenstackhq/common-helpers";
26
- import { sql as sql2 } from "kysely";
27
- import { match as match3 } from "ts-pattern";
28
-
29
- // src/client/constants.ts
30
- var DELEGATE_JOINED_FIELD_PREFIX = "$delegate$";
31
- var LOGICAL_COMBINATORS = [
32
- "AND",
33
- "OR",
34
- "NOT"
35
- ];
36
- var AGGREGATE_OPERATORS = [
37
- "_count",
38
- "_sum",
39
- "_avg",
40
- "_min",
41
- "_max"
26
+ // src/client/contract.ts
27
+ var CRUD = [
28
+ "create",
29
+ "read",
30
+ "update",
31
+ "delete"
42
32
  ];
43
33
 
34
+ // src/client/kysely-utils.ts
35
+ import { AliasNode, ColumnNode, ReferenceNode, TableNode } from "kysely";
36
+ function extractFieldName(node) {
37
+ if (ReferenceNode.is(node) && ColumnNode.is(node.column)) {
38
+ return node.column.column.name;
39
+ } else if (ColumnNode.is(node)) {
40
+ return node.column.name;
41
+ } else {
42
+ return void 0;
43
+ }
44
+ }
45
+ __name(extractFieldName, "extractFieldName");
46
+
44
47
  // src/client/query-utils.ts
48
+ import { invariant } from "@zenstackhq/common-helpers";
45
49
  import { match } from "ts-pattern";
46
50
 
47
51
  // src/schema/expression.ts
@@ -109,6 +113,9 @@ var ExpressionUtils = {
109
113
  or: /* @__PURE__ */ __name((expr2, ...expressions) => {
110
114
  return expressions.reduce((acc, exp) => ExpressionUtils.binary(acc, "||", exp), expr2);
111
115
  }, "or"),
116
+ not: /* @__PURE__ */ __name((expr2) => {
117
+ return ExpressionUtils.unary("!", expr2);
118
+ }, "not"),
112
119
  is: /* @__PURE__ */ __name((value, kind) => {
113
120
  return !!value && typeof value === "object" && "kind" in value && value.kind === kind;
114
121
  }, "is"),
@@ -145,15 +152,19 @@ var InternalError = class extends Error {
145
152
 
146
153
  // src/client/query-utils.ts
147
154
  function getModel(schema, model) {
148
- return schema.models[model];
155
+ return Object.values(schema.models).find((m) => m.name.toLowerCase() === model.toLowerCase());
149
156
  }
150
157
  __name(getModel, "getModel");
158
+ function getTypeDef(schema, type) {
159
+ return schema.typeDefs?.[type];
160
+ }
161
+ __name(getTypeDef, "getTypeDef");
151
162
  function requireModel(schema, model) {
152
- const matchedName = Object.keys(schema.models).find((k) => k.toLowerCase() === model.toLowerCase());
153
- if (!matchedName) {
163
+ const modelDef = getModel(schema, model);
164
+ if (!modelDef) {
154
165
  throw new QueryError(`Model "${model}" not found in schema`);
155
166
  }
156
- return schema.models[matchedName];
167
+ return modelDef;
157
168
  }
158
169
  __name(requireModel, "requireModel");
159
170
  function getField(schema, model, field) {
@@ -161,19 +172,35 @@ function getField(schema, model, field) {
161
172
  return modelDef?.fields[field];
162
173
  }
163
174
  __name(getField, "getField");
164
- function requireField(schema, model, field) {
165
- const modelDef = requireModel(schema, model);
166
- if (!modelDef.fields[field]) {
167
- throw new QueryError(`Field "${field}" not found in model "${model}"`);
175
+ function requireField(schema, modelOrType, field) {
176
+ const modelDef = getModel(schema, modelOrType);
177
+ if (modelDef) {
178
+ if (!modelDef.fields[field]) {
179
+ throw new QueryError(`Field "${field}" not found in model "${modelOrType}"`);
180
+ } else {
181
+ return modelDef.fields[field];
182
+ }
183
+ }
184
+ const typeDef = getTypeDef(schema, modelOrType);
185
+ if (typeDef) {
186
+ if (!typeDef.fields[field]) {
187
+ throw new QueryError(`Field "${field}" not found in type "${modelOrType}"`);
188
+ } else {
189
+ return typeDef.fields[field];
190
+ }
168
191
  }
169
- return modelDef.fields[field];
192
+ throw new QueryError(`Model or type "${modelOrType}" not found in schema`);
170
193
  }
171
194
  __name(requireField, "requireField");
172
- function getIdFields(schema, model) {
195
+ function requireIdFields(schema, model) {
173
196
  const modelDef = requireModel(schema, model);
174
- return modelDef?.idFields;
197
+ const result = modelDef?.idFields;
198
+ if (!result) {
199
+ throw new InternalError(`Model "${model}" does not have ID field(s)`);
200
+ }
201
+ return result;
175
202
  }
176
- __name(getIdFields, "getIdFields");
203
+ __name(requireIdFields, "requireIdFields");
177
204
  function getRelationForeignKeyFieldPairs(schema, model, relationField) {
178
205
  const fieldDef = requireField(schema, model, relationField);
179
206
  if (!fieldDef?.relation) {
@@ -266,7 +293,7 @@ function buildFieldRef(schema, model, field, options, eb, modelAlias, inlineComp
266
293
  throw new QueryError(`Computed field "${field}" implementation not provided for model "${model}"`);
267
294
  }
268
295
  return computer(eb, {
269
- currentModel: modelAlias
296
+ modelAlias
270
297
  });
271
298
  }
272
299
  }
@@ -293,7 +320,7 @@ function buildJoinPairs(schema, model, modelAlias, relationField, relationModelA
293
320
  }
294
321
  __name(buildJoinPairs, "buildJoinPairs");
295
322
  function makeDefaultOrderBy(schema, model) {
296
- const idFields = getIdFields(schema, model);
323
+ const idFields = requireIdFields(schema, model);
297
324
  return idFields.map((f) => ({
298
325
  [f]: "asc"
299
326
  }));
@@ -332,11 +359,17 @@ function getManyToManyRelation(schema, model, field) {
332
359
  "A"
333
360
  ];
334
361
  }
362
+ const modelIdFields = requireIdFields(schema, model);
363
+ invariant(modelIdFields.length === 1, "Only single-field ID is supported for many-to-many relation");
364
+ const otherIdFields = requireIdFields(schema, fieldDef.type);
365
+ invariant(otherIdFields.length === 1, "Only single-field ID is supported for many-to-many relation");
335
366
  return {
336
367
  parentFkName: orderedFK[0],
368
+ parentPKName: modelIdFields[0],
337
369
  otherModel: fieldDef.type,
338
370
  otherField: fieldDef.relation.opposite,
339
371
  otherFkName: orderedFK[1],
372
+ otherPKName: otherIdFields[0],
340
373
  joinTable: fieldDef.relation.name ? `_${fieldDef.relation.name}` : `_${sortedModelNames[0]}To${sortedModelNames[1]}`
341
374
  };
342
375
  } else {
@@ -392,8 +425,37 @@ function aggregate(eb, expr2, op) {
392
425
  }
393
426
  __name(aggregate, "aggregate");
394
427
 
395
- // src/client/crud/dialects/base.ts
396
- import { invariant, isPlainObject } from "@zenstackhq/common-helpers";
428
+ // src/plugins/policy/policy-handler.ts
429
+ import { invariant as invariant7 } from "@zenstackhq/common-helpers";
430
+ import { AliasNode as AliasNode4, BinaryOperationNode as BinaryOperationNode3, ColumnNode as ColumnNode3, DeleteQueryNode, expressionBuilder as expressionBuilder3, ExpressionWrapper, FromNode as FromNode2, FunctionNode as FunctionNode3, IdentifierNode as IdentifierNode2, InsertQueryNode, OperationNodeTransformer, OperatorNode as OperatorNode3, ParensNode as ParensNode2, PrimitiveValueListNode, RawNode, ReturningNode, SelectionNode as SelectionNode2, SelectQueryNode as SelectQueryNode2, sql as sql4, TableNode as TableNode4, UpdateQueryNode, ValueListNode as ValueListNode2, ValueNode as ValueNode3, ValuesNode, WhereNode as WhereNode2 } from "kysely";
431
+ import { match as match8 } from "ts-pattern";
432
+
433
+ // src/client/crud/dialects/index.ts
434
+ import { match as match5 } from "ts-pattern";
435
+
436
+ // src/client/crud/dialects/postgresql.ts
437
+ import { invariant as invariant3 } from "@zenstackhq/common-helpers";
438
+ import Decimal from "decimal.js";
439
+ import { sql as sql2 } from "kysely";
440
+ import { match as match3 } from "ts-pattern";
441
+
442
+ // src/client/constants.ts
443
+ var DELEGATE_JOINED_FIELD_PREFIX = "$delegate$";
444
+ var LOGICAL_COMBINATORS = [
445
+ "AND",
446
+ "OR",
447
+ "NOT"
448
+ ];
449
+ var AGGREGATE_OPERATORS = [
450
+ "_count",
451
+ "_sum",
452
+ "_avg",
453
+ "_min",
454
+ "_max"
455
+ ];
456
+
457
+ // src/client/crud/dialects/base-dialect.ts
458
+ import { invariant as invariant2, isPlainObject } from "@zenstackhq/common-helpers";
397
459
  import { expressionBuilder, sql } from "kysely";
398
460
  import { match as match2, P } from "ts-pattern";
399
461
 
@@ -411,7 +473,7 @@ function enumerate(x) {
411
473
  }
412
474
  __name(enumerate, "enumerate");
413
475
 
414
- // src/client/crud/dialects/base.ts
476
+ // src/client/crud/dialects/base-dialect.ts
415
477
  var BaseCrudDialect = class {
416
478
  static {
417
479
  __name(this, "BaseCrudDialect");
@@ -425,6 +487,9 @@ var BaseCrudDialect = class {
425
487
  transformPrimitive(value, _type, _forArrayField) {
426
488
  return value;
427
489
  }
490
+ transformOutput(value, _type) {
491
+ return value;
492
+ }
428
493
  // #region common query builders
429
494
  buildSelectModel(eb, model, modelAlias) {
430
495
  const modelDef = requireModel(this.schema, model);
@@ -592,9 +657,11 @@ var BaseCrudDialect = class {
592
657
  const buildPkFkWhereRefs = /* @__PURE__ */ __name((eb2) => {
593
658
  const m2m = getManyToManyRelation(this.schema, model, field);
594
659
  if (m2m) {
595
- const modelIdField = getIdFields(this.schema, model)[0];
596
- const relationIdField = getIdFields(this.schema, relationModel)[0];
597
- return eb2(sql.ref(`${relationFilterSelectAlias}.${relationIdField}`), "in", eb2.selectFrom(m2m.joinTable).select(`${m2m.joinTable}.${m2m.otherFkName}`).whereRef(sql.ref(`${m2m.joinTable}.${m2m.parentFkName}`), "=", sql.ref(`${modelAlias}.${modelIdField}`)));
660
+ const modelIdFields = requireIdFields(this.schema, model);
661
+ invariant2(modelIdFields.length === 1, "many-to-many relation must have exactly one id field");
662
+ const relationIdFields = requireIdFields(this.schema, relationModel);
663
+ invariant2(relationIdFields.length === 1, "many-to-many relation must have exactly one id field");
664
+ return eb2(sql.ref(`${relationFilterSelectAlias}.${relationIdFields[0]}`), "in", eb2.selectFrom(m2m.joinTable).select(`${m2m.joinTable}.${m2m.otherFkName}`).whereRef(sql.ref(`${m2m.joinTable}.${m2m.parentFkName}`), "=", sql.ref(`${modelAlias}.${modelIdFields[0]}`)));
598
665
  } else {
599
666
  const relationKeyPairs = getRelationForeignKeyFieldPairs(this.schema, model, field);
600
667
  let result2 = this.true(eb2);
@@ -704,14 +771,14 @@ var BaseCrudDialect = class {
704
771
  }
705
772
  const rhs = Array.isArray(value) ? value.map(getRhs) : getRhs(value);
706
773
  const condition = match2(op).with("equals", () => rhs === null ? eb(lhs, "is", null) : eb(lhs, "=", rhs)).with("in", () => {
707
- invariant(Array.isArray(rhs), "right hand side must be an array");
774
+ invariant2(Array.isArray(rhs), "right hand side must be an array");
708
775
  if (rhs.length === 0) {
709
776
  return this.false(eb);
710
777
  } else {
711
778
  return eb(lhs, "in", rhs);
712
779
  }
713
780
  }).with("notIn", () => {
714
- invariant(Array.isArray(rhs), "right hand side must be an array");
781
+ invariant2(Array.isArray(rhs), "right hand side must be an array");
715
782
  if (rhs.length === 0) {
716
783
  return this.true(eb);
717
784
  } else {
@@ -829,18 +896,18 @@ var BaseCrudDialect = class {
829
896
  "_min",
830
897
  "_max"
831
898
  ].includes(field)) {
832
- invariant(value && typeof value === "object", `invalid orderBy value for field "${field}"`);
899
+ invariant2(value && typeof value === "object", `invalid orderBy value for field "${field}"`);
833
900
  for (const [k, v] of Object.entries(value)) {
834
- invariant(v === "asc" || v === "desc", `invalid orderBy value for field "${field}"`);
901
+ invariant2(v === "asc" || v === "desc", `invalid orderBy value for field "${field}"`);
835
902
  result = result.orderBy((eb) => aggregate(eb, this.fieldRef(model, k, eb, modelAlias), field), sql.raw(this.negateSort(v, negated)));
836
903
  }
837
904
  continue;
838
905
  }
839
906
  switch (field) {
840
907
  case "_count": {
841
- invariant(value && typeof value === "object", 'invalid orderBy value for field "_count"');
908
+ invariant2(value && typeof value === "object", 'invalid orderBy value for field "_count"');
842
909
  for (const [k, v] of Object.entries(value)) {
843
- invariant(v === "asc" || v === "desc", `invalid orderBy value for field "${field}"`);
910
+ invariant2(v === "asc" || v === "desc", `invalid orderBy value for field "${field}"`);
844
911
  result = result.orderBy((eb) => eb.fn.count(this.fieldRef(model, k, eb, modelAlias)), sql.raw(this.negateSort(v, negated)));
845
912
  }
846
913
  continue;
@@ -863,7 +930,7 @@ var BaseCrudDialect = class {
863
930
  throw new QueryError(`invalid orderBy value for field "${field}"`);
864
931
  }
865
932
  if ("_count" in value) {
866
- invariant(value._count === "asc" || value._count === "desc", 'invalid orderBy value for field "_count"');
933
+ invariant2(value._count === "asc" || value._count === "desc", 'invalid orderBy value for field "_count"');
867
934
  const sort = this.negateSort(value._count, negated);
868
935
  result = result.orderBy((eb) => {
869
936
  const subQueryAlias = `${modelAlias}$orderBy$${field}$count`;
@@ -935,7 +1002,7 @@ var BaseCrudDialect = class {
935
1002
  }
936
1003
  }
937
1004
  buildDelegateJoin(thisModel, thisModelAlias, otherModelAlias, query) {
938
- const idFields = getIdFields(this.schema, thisModel);
1005
+ const idFields = requireIdFields(this.schema, thisModel);
939
1006
  query = query.leftJoin(otherModelAlias, (qb) => {
940
1007
  for (const idField of idFields) {
941
1008
  qb = qb.onRef(`${thisModelAlias}.${idField}`, "=", `${otherModelAlias}.${idField}`);
@@ -957,10 +1024,16 @@ var BaseCrudDialect = class {
957
1024
  for (const [field, value] of Object.entries(selections.select)) {
958
1025
  const fieldDef = requireField(this.schema, model, field);
959
1026
  const fieldModel = fieldDef.type;
960
- const joinPairs = buildJoinPairs(this.schema, model, parentAlias, field, fieldModel);
961
- let fieldCountQuery = eb.selectFrom(fieldModel).select(eb.fn.countAll().as(`_count$${field}`));
962
- for (const [left, right] of joinPairs) {
963
- fieldCountQuery = fieldCountQuery.whereRef(left, "=", right);
1027
+ let fieldCountQuery;
1028
+ const m2m = getManyToManyRelation(this.schema, model, field);
1029
+ if (m2m) {
1030
+ fieldCountQuery = eb.selectFrom(fieldModel).innerJoin(m2m.joinTable, (join) => join.onRef(`${m2m.joinTable}.${m2m.otherFkName}`, "=", `${fieldModel}.${m2m.otherPKName}`).onRef(`${m2m.joinTable}.${m2m.parentFkName}`, "=", `${parentAlias}.${m2m.parentPKName}`)).select(eb.fn.countAll().as(`_count$${field}`));
1031
+ } else {
1032
+ fieldCountQuery = eb.selectFrom(fieldModel).select(eb.fn.countAll().as(`_count$${field}`));
1033
+ const joinPairs = buildJoinPairs(this.schema, model, parentAlias, field, fieldModel);
1034
+ for (const [left, right] of joinPairs) {
1035
+ fieldCountQuery = fieldCountQuery.whereRef(left, "=", right);
1036
+ }
964
1037
  }
965
1038
  if (value && typeof value === "object" && "where" in value && value.where && typeof value.where === "object") {
966
1039
  const filter = this.buildFilter(eb, fieldModel, fieldModel, value.where);
@@ -1040,6 +1113,9 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
1040
1113
  static {
1041
1114
  __name(this, "PostgresCrudDialect");
1042
1115
  }
1116
+ constructor(schema, options) {
1117
+ super(schema, options);
1118
+ }
1043
1119
  get provider() {
1044
1120
  return "postgresql";
1045
1121
  }
@@ -1054,9 +1130,41 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
1054
1130
  return value.map((v) => this.transformPrimitive(v, type, false));
1055
1131
  }
1056
1132
  } else {
1057
- return match3(type).with("DateTime", () => value instanceof Date ? value : typeof value === "string" ? new Date(value) : value).with("Decimal", () => value !== null ? value.toString() : value).otherwise(() => value);
1133
+ return match3(type).with("DateTime", () => value instanceof Date ? value.toISOString() : typeof value === "string" ? new Date(value).toISOString() : value).with("Decimal", () => value !== null ? value.toString() : value).otherwise(() => value);
1134
+ }
1135
+ }
1136
+ transformOutput(value, type) {
1137
+ if (value === null || value === void 0) {
1138
+ return value;
1139
+ }
1140
+ return match3(type).with("DateTime", () => this.transformOutputDate(value)).with("Bytes", () => this.transformOutputBytes(value)).with("BigInt", () => this.transformOutputBigInt(value)).with("Decimal", () => this.transformDecimal(value)).otherwise(() => super.transformOutput(value, type));
1141
+ }
1142
+ transformOutputBigInt(value) {
1143
+ if (typeof value === "bigint") {
1144
+ return value;
1145
+ }
1146
+ invariant3(typeof value === "string" || typeof value === "number", `Expected string or number, got ${typeof value}`);
1147
+ return BigInt(value);
1148
+ }
1149
+ transformDecimal(value) {
1150
+ if (value instanceof Decimal) {
1151
+ return value;
1152
+ }
1153
+ invariant3(typeof value === "string" || typeof value === "number" || value instanceof Decimal, `Expected string, number or Decimal, got ${typeof value}`);
1154
+ return new Decimal(value);
1155
+ }
1156
+ transformOutputDate(value) {
1157
+ if (typeof value === "string") {
1158
+ return new Date(value);
1159
+ } else if (value instanceof Date && this.options.fixPostgresTimezone !== false) {
1160
+ return new Date(value.getTime() - value.getTimezoneOffset() * 60 * 1e3);
1161
+ } else {
1162
+ return value;
1058
1163
  }
1059
1164
  }
1165
+ transformOutputBytes(value) {
1166
+ return Buffer.isBuffer(value) ? Uint8Array.from(value) : value;
1167
+ }
1060
1168
  buildRelationSelection(query, model, relationField, parentAlias, payload) {
1061
1169
  const relationResultName = `${parentAlias}$${relationField}`;
1062
1170
  const joinedQuery = this.buildRelationJSON(model, query, relationField, parentAlias, payload, relationResultName);
@@ -1087,10 +1195,10 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
1087
1195
  buildRelationJoinFilter(query, model, relationField, relationModel, relationModelAlias, parentAlias) {
1088
1196
  const m2m = getManyToManyRelation(this.schema, model, relationField);
1089
1197
  if (m2m) {
1090
- const parentIds = getIdFields(this.schema, model);
1091
- const relationIds = getIdFields(this.schema, relationModel);
1092
- invariant2(parentIds.length === 1, "many-to-many relation must have exactly one id field");
1093
- invariant2(relationIds.length === 1, "many-to-many relation must have exactly one id field");
1198
+ const parentIds = requireIdFields(this.schema, model);
1199
+ const relationIds = requireIdFields(this.schema, relationModel);
1200
+ invariant3(parentIds.length === 1, "many-to-many relation must have exactly one id field");
1201
+ invariant3(relationIds.length === 1, "many-to-many relation must have exactly one id field");
1094
1202
  query = query.where((eb) => eb(eb.ref(`${relationModelAlias}.${relationIds[0]}`), "in", eb.selectFrom(m2m.joinTable).select(`${m2m.joinTable}.${m2m.otherFkName}`).whereRef(`${parentAlias}.${parentIds[0]}`, "=", `${m2m.joinTable}.${m2m.parentFkName}`)));
1095
1203
  } else {
1096
1204
  const joinPairs = buildJoinPairs(this.schema, model, parentAlias, relationField, relationModelAlias);
@@ -1202,10 +1310,32 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
1202
1310
  get supportInsertWithDefault() {
1203
1311
  return true;
1204
1312
  }
1313
+ getFieldSqlType(fieldDef) {
1314
+ if (fieldDef.relation) {
1315
+ throw new QueryError("Cannot get SQL type of a relation field");
1316
+ }
1317
+ let result;
1318
+ if (this.schema.enums?.[fieldDef.type]) {
1319
+ result = "text";
1320
+ } else {
1321
+ result = match3(fieldDef.type).with("String", () => "text").with("Boolean", () => "boolean").with("Int", () => "integer").with("BigInt", () => "bigint").with("Float", () => "double precision").with("Decimal", () => "decimal").with("DateTime", () => "timestamp").with("Bytes", () => "bytea").with("Json", () => "jsonb").otherwise(() => "text");
1322
+ }
1323
+ if (fieldDef.array) {
1324
+ result += "[]";
1325
+ }
1326
+ return result;
1327
+ }
1328
+ getStringCasingBehavior() {
1329
+ return {
1330
+ supportsILike: true,
1331
+ likeCaseSensitive: true
1332
+ };
1333
+ }
1205
1334
  };
1206
1335
 
1207
1336
  // src/client/crud/dialects/sqlite.ts
1208
- import { invariant as invariant3 } from "@zenstackhq/common-helpers";
1337
+ import { invariant as invariant4 } from "@zenstackhq/common-helpers";
1338
+ import Decimal2 from "decimal.js";
1209
1339
  import { sql as sql3 } from "kysely";
1210
1340
  import { match as match4 } from "ts-pattern";
1211
1341
  var SqliteCrudDialect = class extends BaseCrudDialect {
@@ -1225,10 +1355,58 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
1225
1355
  if (this.schema.typeDefs && type in this.schema.typeDefs) {
1226
1356
  return JSON.stringify(value);
1227
1357
  } else {
1228
- return match4(type).with("Boolean", () => value ? 1 : 0).with("DateTime", () => value instanceof Date ? value.toISOString() : value).with("Decimal", () => value.toString()).with("Bytes", () => Buffer.from(value)).with("Json", () => JSON.stringify(value)).otherwise(() => value);
1358
+ return match4(type).with("Boolean", () => value ? 1 : 0).with("DateTime", () => value instanceof Date ? value.toISOString() : typeof value === "string" ? new Date(value).toISOString() : value).with("Decimal", () => value.toString()).with("Bytes", () => Buffer.from(value)).with("Json", () => JSON.stringify(value)).otherwise(() => value);
1229
1359
  }
1230
1360
  }
1231
1361
  }
1362
+ transformOutput(value, type) {
1363
+ if (value === null || value === void 0) {
1364
+ return value;
1365
+ } else if (this.schema.typeDefs && type in this.schema.typeDefs) {
1366
+ return this.transformOutputJson(value);
1367
+ } else {
1368
+ return match4(type).with("Boolean", () => this.transformOutputBoolean(value)).with("DateTime", () => this.transformOutputDate(value)).with("Bytes", () => this.transformOutputBytes(value)).with("Decimal", () => this.transformOutputDecimal(value)).with("BigInt", () => this.transformOutputBigInt(value)).with("Json", () => this.transformOutputJson(value)).otherwise(() => super.transformOutput(value, type));
1369
+ }
1370
+ }
1371
+ transformOutputDecimal(value) {
1372
+ if (value instanceof Decimal2) {
1373
+ return value;
1374
+ }
1375
+ invariant4(typeof value === "string" || typeof value === "number" || value instanceof Decimal2, `Expected string, number or Decimal, got ${typeof value}`);
1376
+ return new Decimal2(value);
1377
+ }
1378
+ transformOutputBigInt(value) {
1379
+ if (typeof value === "bigint") {
1380
+ return value;
1381
+ }
1382
+ invariant4(typeof value === "string" || typeof value === "number", `Expected string or number, got ${typeof value}`);
1383
+ return BigInt(value);
1384
+ }
1385
+ transformOutputBoolean(value) {
1386
+ return !!value;
1387
+ }
1388
+ transformOutputDate(value) {
1389
+ if (typeof value === "number") {
1390
+ return new Date(value);
1391
+ } else if (typeof value === "string") {
1392
+ return new Date(value);
1393
+ } else {
1394
+ return value;
1395
+ }
1396
+ }
1397
+ transformOutputBytes(value) {
1398
+ return Buffer.isBuffer(value) ? Uint8Array.from(value) : value;
1399
+ }
1400
+ transformOutputJson(value) {
1401
+ if (typeof value === "string") {
1402
+ try {
1403
+ return JSON.parse(value);
1404
+ } catch (e) {
1405
+ throw new QueryError("Invalid JSON returned", e);
1406
+ }
1407
+ }
1408
+ return value;
1409
+ }
1232
1410
  buildRelationSelection(query, model, relationField, parentAlias, payload) {
1233
1411
  return query.select((eb) => this.buildRelationJSON(model, eb, relationField, parentAlias, payload).as(relationField));
1234
1412
  }
@@ -1310,10 +1488,10 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
1310
1488
  const relationModel = fieldDef.type;
1311
1489
  const m2m = getManyToManyRelation(this.schema, model, relationField);
1312
1490
  if (m2m) {
1313
- const parentIds = getIdFields(this.schema, model);
1314
- const relationIds = getIdFields(this.schema, relationModel);
1315
- invariant3(parentIds.length === 1, "many-to-many relation must have exactly one id field");
1316
- invariant3(relationIds.length === 1, "many-to-many relation must have exactly one id field");
1491
+ const parentIds = requireIdFields(this.schema, model);
1492
+ const relationIds = requireIdFields(this.schema, relationModel);
1493
+ invariant4(parentIds.length === 1, "many-to-many relation must have exactly one id field");
1494
+ invariant4(relationIds.length === 1, "many-to-many relation must have exactly one id field");
1317
1495
  selectModelQuery = selectModelQuery.where((eb) => eb(eb.ref(`${relationModelAlias}.${relationIds[0]}`), "in", eb.selectFrom(m2m.joinTable).select(`${m2m.joinTable}.${m2m.otherFkName}`).whereRef(`${parentAlias}.${parentIds[0]}`, "=", `${m2m.joinTable}.${m2m.parentFkName}`)));
1318
1496
  } else {
1319
1497
  const { keyPairs, ownedByModel } = getRelationForeignKeyFieldPairs(this.schema, model, relationField);
@@ -1365,6 +1543,24 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
1365
1543
  get supportInsertWithDefault() {
1366
1544
  return false;
1367
1545
  }
1546
+ getFieldSqlType(fieldDef) {
1547
+ if (fieldDef.relation) {
1548
+ throw new QueryError("Cannot get SQL type of a relation field");
1549
+ }
1550
+ if (fieldDef.array) {
1551
+ throw new QueryError("SQLite does not support scalar list type");
1552
+ }
1553
+ if (this.schema.enums?.[fieldDef.type]) {
1554
+ return "text";
1555
+ }
1556
+ return match4(fieldDef.type).with("String", () => "text").with("Boolean", () => "integer").with("Int", () => "integer").with("BigInt", () => "integer").with("Float", () => "real").with("Decimal", () => "decimal").with("DateTime", () => "numeric").with("Bytes", () => "blob").with("Json", () => "jsonb").otherwise(() => "text");
1557
+ }
1558
+ getStringCasingBehavior() {
1559
+ return {
1560
+ supportsILike: false,
1561
+ likeCaseSensitive: false
1562
+ };
1563
+ }
1368
1564
  };
1369
1565
 
1370
1566
  // src/client/crud/dialects/index.ts
@@ -1692,12 +1888,12 @@ var ColumnCollector = class extends DefaultOperationNodeVisitor {
1692
1888
  };
1693
1889
 
1694
1890
  // src/plugins/policy/expression-transformer.ts
1695
- import { invariant as invariant5 } from "@zenstackhq/common-helpers";
1696
- import { AliasNode as AliasNode2, BinaryOperationNode as BinaryOperationNode2, ColumnNode, expressionBuilder as expressionBuilder2, FromNode, FunctionNode as FunctionNode2, IdentifierNode, OperatorNode as OperatorNode2, ReferenceNode as ReferenceNode2, SelectionNode, SelectQueryNode, TableNode as TableNode2, ValueListNode, ValueNode as ValueNode2, WhereNode } from "kysely";
1891
+ import { invariant as invariant6 } from "@zenstackhq/common-helpers";
1892
+ import { AliasNode as AliasNode3, BinaryOperationNode as BinaryOperationNode2, ColumnNode as ColumnNode2, expressionBuilder as expressionBuilder2, FromNode, FunctionNode as FunctionNode2, IdentifierNode, OperatorNode as OperatorNode2, ReferenceNode as ReferenceNode3, SelectionNode, SelectQueryNode, TableNode as TableNode3, ValueListNode, ValueNode as ValueNode2, WhereNode } from "kysely";
1697
1893
  import { match as match7 } from "ts-pattern";
1698
1894
 
1699
1895
  // src/plugins/policy/expression-evaluator.ts
1700
- import { invariant as invariant4 } from "@zenstackhq/common-helpers";
1896
+ import { invariant as invariant5 } from "@zenstackhq/common-helpers";
1701
1897
  import { match as match6 } from "ts-pattern";
1702
1898
  var ExpressionEvaluator = class {
1703
1899
  static {
@@ -1741,18 +1937,18 @@ var ExpressionEvaluator = class {
1741
1937
  const right = this.evaluate(expr2.right, context);
1742
1938
  return match6(expr2.op).with("==", () => left === right).with("!=", () => left !== right).with(">", () => left > right).with(">=", () => left >= right).with("<", () => left < right).with("<=", () => left <= right).with("&&", () => left && right).with("||", () => left || right).with("in", () => {
1743
1939
  const _right = right ?? [];
1744
- invariant4(Array.isArray(_right), 'expected array for "in" operator');
1940
+ invariant5(Array.isArray(_right), 'expected array for "in" operator');
1745
1941
  return _right.includes(left);
1746
1942
  }).exhaustive();
1747
1943
  }
1748
1944
  evaluateCollectionPredicate(expr2, context) {
1749
1945
  const op = expr2.op;
1750
- invariant4(op === "?" || op === "!" || op === "^", 'expected "?" or "!" or "^" operator');
1946
+ invariant5(op === "?" || op === "!" || op === "^", 'expected "?" or "!" or "^" operator');
1751
1947
  const left = this.evaluate(expr2.left, context);
1752
1948
  if (!left) {
1753
1949
  return false;
1754
1950
  }
1755
- invariant4(Array.isArray(left), "expected array");
1951
+ invariant5(Array.isArray(left), "expected array");
1756
1952
  return match6(op).with("?", () => left.some((item) => this.evaluate(expr2.right, {
1757
1953
  ...context,
1758
1954
  thisValue: item
@@ -1767,7 +1963,7 @@ var ExpressionEvaluator = class {
1767
1963
  };
1768
1964
 
1769
1965
  // src/plugins/policy/utils.ts
1770
- import { AliasNode, AndNode, BinaryOperationNode, FunctionNode, OperatorNode, OrNode, ParensNode, ReferenceNode, TableNode, UnaryOperationNode, ValueNode } from "kysely";
1966
+ import { AliasNode as AliasNode2, AndNode, BinaryOperationNode, FunctionNode, OperatorNode, OrNode, ParensNode, ReferenceNode as ReferenceNode2, TableNode as TableNode2, UnaryOperationNode, ValueNode } from "kysely";
1771
1967
  function trueNode(dialect) {
1772
1968
  return ValueNode.createImmediate(dialect.transformPrimitive(true, "Boolean", false));
1773
1969
  }
@@ -1785,6 +1981,12 @@ function isFalseNode(node) {
1785
1981
  }
1786
1982
  __name(isFalseNode, "isFalseNode");
1787
1983
  function conjunction(dialect, nodes) {
1984
+ if (nodes.length === 0) {
1985
+ return trueNode(dialect);
1986
+ }
1987
+ if (nodes.length === 1) {
1988
+ return nodes[0];
1989
+ }
1788
1990
  if (nodes.some(isFalseNode)) {
1789
1991
  return falseNode(dialect);
1790
1992
  }
@@ -1792,10 +1994,16 @@ function conjunction(dialect, nodes) {
1792
1994
  if (items.length === 0) {
1793
1995
  return trueNode(dialect);
1794
1996
  }
1795
- return items.reduce((acc, node) => OrNode.is(node) ? AndNode.create(acc, ParensNode.create(node)) : AndNode.create(acc, node));
1997
+ return items.reduce((acc, node) => AndNode.create(wrapParensIf(acc, OrNode.is), wrapParensIf(node, OrNode.is)));
1796
1998
  }
1797
1999
  __name(conjunction, "conjunction");
1798
2000
  function disjunction(dialect, nodes) {
2001
+ if (nodes.length === 0) {
2002
+ return falseNode(dialect);
2003
+ }
2004
+ if (nodes.length === 1) {
2005
+ return nodes[0];
2006
+ }
1799
2007
  if (nodes.some(isTrueNode)) {
1800
2008
  return trueNode(dialect);
1801
2009
  }
@@ -1803,13 +2011,23 @@ function disjunction(dialect, nodes) {
1803
2011
  if (items.length === 0) {
1804
2012
  return falseNode(dialect);
1805
2013
  }
1806
- return items.reduce((acc, node) => AndNode.is(node) ? OrNode.create(acc, ParensNode.create(node)) : OrNode.create(acc, node));
2014
+ return items.reduce((acc, node) => OrNode.create(wrapParensIf(acc, AndNode.is), wrapParensIf(node, AndNode.is)));
1807
2015
  }
1808
2016
  __name(disjunction, "disjunction");
1809
- function logicalNot(node) {
1810
- return UnaryOperationNode.create(OperatorNode.create("not"), AndNode.is(node) || OrNode.is(node) ? ParensNode.create(node) : node);
2017
+ function logicalNot(dialect, node) {
2018
+ if (isTrueNode(node)) {
2019
+ return falseNode(dialect);
2020
+ }
2021
+ if (isFalseNode(node)) {
2022
+ return trueNode(dialect);
2023
+ }
2024
+ return UnaryOperationNode.create(OperatorNode.create("not"), wrapParensIf(node, (n) => AndNode.is(n) || OrNode.is(n)));
1811
2025
  }
1812
2026
  __name(logicalNot, "logicalNot");
2027
+ function wrapParensIf(node, predicate) {
2028
+ return predicate(node) ? ParensNode.create(node) : node;
2029
+ }
2030
+ __name(wrapParensIf, "wrapParensIf");
1813
2031
  function buildIsFalse(node, dialect) {
1814
2032
  if (isFalseNode(node)) {
1815
2033
  return trueNode(dialect);
@@ -1831,11 +2049,11 @@ function getTableName(node) {
1831
2049
  if (!node) {
1832
2050
  return node;
1833
2051
  }
1834
- if (TableNode.is(node)) {
2052
+ if (TableNode2.is(node)) {
1835
2053
  return node.table.identifier.name;
1836
- } else if (AliasNode.is(node)) {
2054
+ } else if (AliasNode2.is(node)) {
1837
2055
  return getTableName(node.node);
1838
- } else if (ReferenceNode.is(node) && node.table) {
2056
+ } else if (ReferenceNode2.is(node) && node.table) {
1839
2057
  return getTableName(node.table);
1840
2058
  }
1841
2059
  return void 0;
@@ -1868,16 +2086,21 @@ var ExpressionTransformer = class {
1868
2086
  static {
1869
2087
  __name(this, "ExpressionTransformer");
1870
2088
  }
1871
- schema;
1872
- clientOptions;
1873
- auth;
2089
+ client;
1874
2090
  dialect;
1875
- constructor(schema, clientOptions, auth) {
1876
- this.schema = schema;
1877
- this.clientOptions = clientOptions;
1878
- this.auth = auth;
2091
+ constructor(client) {
2092
+ this.client = client;
1879
2093
  this.dialect = getCrudDialect(this.schema, this.clientOptions);
1880
2094
  }
2095
+ get schema() {
2096
+ return this.client.$schema;
2097
+ }
2098
+ get clientOptions() {
2099
+ return this.client.$options;
2100
+ }
2101
+ get auth() {
2102
+ return this.client.$auth;
2103
+ }
1881
2104
  get authType() {
1882
2105
  if (!this.schema.authType) {
1883
2106
  throw new InternalError('Schema does not have an "authType" specified');
@@ -1900,11 +2123,7 @@ var ExpressionTransformer = class {
1900
2123
  _field(expr2, context) {
1901
2124
  const fieldDef = requireField(this.schema, context.model, expr2.field);
1902
2125
  if (!fieldDef.relation) {
1903
- if (context.thisEntity) {
1904
- return context.thisEntity[expr2.field];
1905
- } else {
1906
- return this.createColumnRef(expr2.field, context);
1907
- }
2126
+ return this.createColumnRef(expr2.field, context);
1908
2127
  } else {
1909
2128
  const { memberFilter, memberSelect, ...restContext } = context;
1910
2129
  const relation = this.transformRelationAccess(expr2.field, fieldDef.type, restContext);
@@ -1945,14 +2164,15 @@ var ExpressionTransformer = class {
1945
2164
  ]);
1946
2165
  }
1947
2166
  if (this.isAuthCall(expr2.left) || this.isAuthCall(expr2.right)) {
1948
- return this.transformAuthBinary(expr2);
2167
+ return this.transformAuthBinary(expr2, context);
1949
2168
  }
1950
2169
  const op = expr2.op;
1951
2170
  if (op === "?" || op === "!" || op === "^") {
1952
2171
  return this.transformCollectionPredicate(expr2, context);
1953
2172
  }
1954
- const left = this.transform(expr2.left, context);
1955
- const right = this.transform(expr2.right, context);
2173
+ const { normalizedLeft, normalizedRight } = this.normalizeBinaryOperationOperands(expr2, context);
2174
+ const left = this.transform(normalizedLeft, context);
2175
+ const right = this.transform(normalizedRight, context);
1956
2176
  if (op === "in") {
1957
2177
  if (this.isNullNode(left)) {
1958
2178
  return this.transformValue(false, "Boolean");
@@ -1967,29 +2187,65 @@ var ExpressionTransformer = class {
1967
2187
  }
1968
2188
  }
1969
2189
  if (this.isNullNode(right)) {
1970
- return expr2.op === "==" ? BinaryOperationNode2.create(left, OperatorNode2.create("is"), right) : BinaryOperationNode2.create(left, OperatorNode2.create("is not"), right);
2190
+ return this.transformNullCheck(left, expr2.op);
1971
2191
  } else if (this.isNullNode(left)) {
1972
- return expr2.op === "==" ? BinaryOperationNode2.create(right, OperatorNode2.create("is"), ValueNode2.createImmediate(null)) : BinaryOperationNode2.create(right, OperatorNode2.create("is not"), ValueNode2.createImmediate(null));
2192
+ return this.transformNullCheck(right, expr2.op);
2193
+ } else {
2194
+ return BinaryOperationNode2.create(left, this.transformOperator(op), right);
2195
+ }
2196
+ }
2197
+ transformNullCheck(expr2, operator) {
2198
+ invariant6(operator === "==" || operator === "!=", 'operator must be "==" or "!=" for null comparison');
2199
+ if (ValueNode2.is(expr2)) {
2200
+ if (expr2.value === null) {
2201
+ return operator === "==" ? trueNode(this.dialect) : falseNode(this.dialect);
2202
+ } else {
2203
+ return operator === "==" ? falseNode(this.dialect) : trueNode(this.dialect);
2204
+ }
2205
+ } else {
2206
+ return operator === "==" ? BinaryOperationNode2.create(expr2, OperatorNode2.create("is"), ValueNode2.createImmediate(null)) : BinaryOperationNode2.create(expr2, OperatorNode2.create("is not"), ValueNode2.createImmediate(null));
2207
+ }
2208
+ }
2209
+ normalizeBinaryOperationOperands(expr2, context) {
2210
+ let normalizedLeft = expr2.left;
2211
+ if (this.isRelationField(expr2.left, context.model)) {
2212
+ invariant6(ExpressionUtils.isNull(expr2.right), "only null comparison is supported for relation field");
2213
+ const leftRelDef = this.getFieldDefFromFieldRef(expr2.left, context.model);
2214
+ invariant6(leftRelDef, "failed to get relation field definition");
2215
+ const idFields = requireIdFields(this.schema, leftRelDef.type);
2216
+ normalizedLeft = this.makeOrAppendMember(normalizedLeft, idFields[0]);
2217
+ }
2218
+ let normalizedRight = expr2.right;
2219
+ if (this.isRelationField(expr2.right, context.model)) {
2220
+ invariant6(ExpressionUtils.isNull(expr2.left), "only null comparison is supported for relation field");
2221
+ const rightRelDef = this.getFieldDefFromFieldRef(expr2.right, context.model);
2222
+ invariant6(rightRelDef, "failed to get relation field definition");
2223
+ const idFields = requireIdFields(this.schema, rightRelDef.type);
2224
+ normalizedRight = this.makeOrAppendMember(normalizedRight, idFields[0]);
1973
2225
  }
1974
- return BinaryOperationNode2.create(left, this.transformOperator(op), right);
2226
+ return {
2227
+ normalizedLeft,
2228
+ normalizedRight
2229
+ };
1975
2230
  }
1976
2231
  transformCollectionPredicate(expr2, context) {
1977
- invariant5(expr2.op === "?" || expr2.op === "!" || expr2.op === "^", 'expected "?" or "!" or "^" operator');
2232
+ invariant6(expr2.op === "?" || expr2.op === "!" || expr2.op === "^", 'expected "?" or "!" or "^" operator');
1978
2233
  if (this.isAuthCall(expr2.left) || this.isAuthMember(expr2.left)) {
1979
2234
  const value = new ExpressionEvaluator().evaluate(expr2, {
1980
2235
  auth: this.auth
1981
2236
  });
1982
2237
  return this.transformValue(value, "Boolean");
1983
2238
  }
1984
- invariant5(ExpressionUtils.isField(expr2.left) || ExpressionUtils.isMember(expr2.left), "left operand must be field or member access");
2239
+ invariant6(ExpressionUtils.isField(expr2.left) || ExpressionUtils.isMember(expr2.left), "left operand must be field or member access");
1985
2240
  let newContextModel;
1986
- if (ExpressionUtils.isField(expr2.left)) {
1987
- const fieldDef = requireField(this.schema, context.model, expr2.left.field);
2241
+ const fieldDef = this.getFieldDefFromFieldRef(expr2.left, context.model);
2242
+ if (fieldDef) {
2243
+ invariant6(fieldDef.relation, `field is not a relation: ${JSON.stringify(expr2.left)}`);
1988
2244
  newContextModel = fieldDef.type;
1989
2245
  } else {
1990
- invariant5(ExpressionUtils.isField(expr2.left.receiver));
1991
- const fieldDef = requireField(this.schema, context.model, expr2.left.receiver.field);
1992
- newContextModel = fieldDef.type;
2246
+ invariant6(ExpressionUtils.isMember(expr2.left) && ExpressionUtils.isField(expr2.left.receiver), "left operand must be member access with field receiver");
2247
+ const fieldDef2 = requireField(this.schema, context.model, expr2.left.receiver.field);
2248
+ newContextModel = fieldDef2.type;
1993
2249
  for (const member of expr2.left.members) {
1994
2250
  const memberDef = requireField(this.schema, newContextModel, member);
1995
2251
  newContextModel = memberDef.type;
@@ -1998,11 +2254,10 @@ var ExpressionTransformer = class {
1998
2254
  let predicateFilter = this.transform(expr2.right, {
1999
2255
  ...context,
2000
2256
  model: newContextModel,
2001
- alias: void 0,
2002
- thisEntity: void 0
2257
+ alias: void 0
2003
2258
  });
2004
2259
  if (expr2.op === "!") {
2005
- predicateFilter = logicalNot(predicateFilter);
2260
+ predicateFilter = logicalNot(this.dialect, predicateFilter);
2006
2261
  }
2007
2262
  const count = FunctionNode2.create("count", [
2008
2263
  ValueNode2.createImmediate(1)
@@ -2010,32 +2265,66 @@ var ExpressionTransformer = class {
2010
2265
  const predicateResult = match7(expr2.op).with("?", () => BinaryOperationNode2.create(count, OperatorNode2.create(">"), ValueNode2.createImmediate(0))).with("!", () => BinaryOperationNode2.create(count, OperatorNode2.create("="), ValueNode2.createImmediate(0))).with("^", () => BinaryOperationNode2.create(count, OperatorNode2.create("="), ValueNode2.createImmediate(0))).exhaustive();
2011
2266
  return this.transform(expr2.left, {
2012
2267
  ...context,
2013
- memberSelect: SelectionNode.create(AliasNode2.create(predicateResult, IdentifierNode.create("$t"))),
2268
+ memberSelect: SelectionNode.create(AliasNode3.create(predicateResult, IdentifierNode.create("$t"))),
2014
2269
  memberFilter: predicateFilter
2015
2270
  });
2016
2271
  }
2017
- transformAuthBinary(expr2) {
2272
+ transformAuthBinary(expr2, context) {
2018
2273
  if (expr2.op !== "==" && expr2.op !== "!=") {
2019
- throw new Error(`Unsupported operator for auth call: ${expr2.op}`);
2274
+ throw new QueryError(`Unsupported operator for \`auth()\` in policy of model "${context.model}": ${expr2.op}`);
2020
2275
  }
2276
+ let authExpr;
2021
2277
  let other;
2022
2278
  if (this.isAuthCall(expr2.left)) {
2279
+ authExpr = expr2.left;
2023
2280
  other = expr2.right;
2024
2281
  } else {
2282
+ authExpr = expr2.right;
2025
2283
  other = expr2.left;
2026
2284
  }
2027
2285
  if (ExpressionUtils.isNull(other)) {
2028
2286
  return this.transformValue(expr2.op === "==" ? !this.auth : !!this.auth, "Boolean");
2029
2287
  } else {
2030
- throw new Error("Unsupported binary expression with `auth()`");
2288
+ const authModel = getModel(this.schema, this.authType);
2289
+ if (!authModel) {
2290
+ throw new QueryError(`Unsupported use of \`auth()\` in policy of model "${context.model}", comparing with \`auth()\` is only possible when auth type is a model`);
2291
+ }
2292
+ const idFields = Object.values(authModel.fields).filter((f) => f.id).map((f) => f.name);
2293
+ invariant6(idFields.length > 0, "auth type model must have at least one id field");
2294
+ const conditions = idFields.map((fieldName) => ExpressionUtils.binary(ExpressionUtils.member(authExpr, [
2295
+ fieldName
2296
+ ]), "==", this.makeOrAppendMember(other, fieldName)));
2297
+ let result = this.buildAnd(conditions);
2298
+ if (expr2.op === "!=") {
2299
+ result = this.buildLogicalNot(result);
2300
+ }
2301
+ return this.transform(result, context);
2302
+ }
2303
+ }
2304
+ makeOrAppendMember(other, fieldName) {
2305
+ if (ExpressionUtils.isMember(other)) {
2306
+ return ExpressionUtils.member(other.receiver, [
2307
+ ...other.members,
2308
+ fieldName
2309
+ ]);
2310
+ } else {
2311
+ return ExpressionUtils.member(other, [
2312
+ fieldName
2313
+ ]);
2031
2314
  }
2032
2315
  }
2033
2316
  transformValue(value, type) {
2034
- return ValueNode2.create(this.dialect.transformPrimitive(value, type, false) ?? null);
2317
+ if (value === true) {
2318
+ return trueNode(this.dialect);
2319
+ } else if (value === false) {
2320
+ return falseNode(this.dialect);
2321
+ } else {
2322
+ return ValueNode2.create(this.dialect.transformPrimitive(value, type, false) ?? null);
2323
+ }
2035
2324
  }
2036
2325
  _unary(expr2, context) {
2037
- invariant5(expr2.op === "!", 'only "!" operator is supported');
2038
- return BinaryOperationNode2.create(this.transform(expr2.operand, context), this.transformOperator("!="), trueNode(this.dialect));
2326
+ invariant6(expr2.op === "!", 'only "!" operator is supported');
2327
+ return logicalNot(this.dialect, this.transform(expr2.operand, context));
2039
2328
  }
2040
2329
  transformOperator(op) {
2041
2330
  const mappedOp = match7(op).with("==", () => "=").otherwise(() => op);
@@ -2046,23 +2335,37 @@ var ExpressionTransformer = class {
2046
2335
  return result.toOperationNode();
2047
2336
  }
2048
2337
  transformCall(expr2, context) {
2049
- const func = this.clientOptions.functions?.[expr2.function];
2338
+ const func = this.getFunctionImpl(expr2.function);
2050
2339
  if (!func) {
2051
2340
  throw new QueryError(`Function not implemented: ${expr2.function}`);
2052
2341
  }
2053
2342
  const eb = expressionBuilder2();
2054
2343
  return func(eb, (expr2.args ?? []).map((arg) => this.transformCallArg(eb, arg, context)), {
2344
+ client: this.client,
2055
2345
  dialect: this.dialect,
2056
2346
  model: context.model,
2347
+ modelAlias: context.alias ?? context.model,
2057
2348
  operation: context.operation
2058
2349
  });
2059
2350
  }
2351
+ getFunctionImpl(functionName) {
2352
+ let func = this.clientOptions.functions?.[functionName];
2353
+ if (!func) {
2354
+ for (const plugin of this.clientOptions.plugins ?? []) {
2355
+ if (plugin.functions?.[functionName]) {
2356
+ func = plugin.functions[functionName];
2357
+ break;
2358
+ }
2359
+ }
2360
+ }
2361
+ return func;
2362
+ }
2060
2363
  transformCallArg(eb, arg, context) {
2061
2364
  if (ExpressionUtils.isLiteral(arg)) {
2062
2365
  return eb.val(arg.value);
2063
2366
  }
2064
2367
  if (ExpressionUtils.isField(arg)) {
2065
- return context.thisEntityRaw ? eb.val(context.thisEntityRaw[arg.field]) : eb.ref(arg.field);
2368
+ return eb.ref(arg.field);
2066
2369
  }
2067
2370
  if (ExpressionUtils.isCall(arg)) {
2068
2371
  return this.transformCall(arg, context);
@@ -2077,14 +2380,32 @@ var ExpressionTransformer = class {
2077
2380
  if (this.isAuthCall(expr2.receiver)) {
2078
2381
  return this.valueMemberAccess(this.auth, expr2, this.authType);
2079
2382
  }
2080
- invariant5(ExpressionUtils.isField(expr2.receiver), "expect receiver to be field expression");
2383
+ invariant6(ExpressionUtils.isField(expr2.receiver) || ExpressionUtils.isThis(expr2.receiver), 'expect receiver to be field expression or "this"');
2384
+ let members = expr2.members;
2385
+ let receiver;
2081
2386
  const { memberFilter, memberSelect, ...restContext } = context;
2082
- const receiver = this.transform(expr2.receiver, restContext);
2083
- invariant5(SelectQueryNode.is(receiver), "expected receiver to be select query");
2084
- const receiverField = requireField(this.schema, context.model, expr2.receiver.field);
2387
+ if (ExpressionUtils.isThis(expr2.receiver)) {
2388
+ if (expr2.members.length === 1) {
2389
+ return this._field(ExpressionUtils.field(expr2.members[0]), context);
2390
+ } else {
2391
+ const firstMemberFieldDef = requireField(this.schema, context.model, expr2.members[0]);
2392
+ receiver = this.transformRelationAccess(expr2.members[0], firstMemberFieldDef.type, restContext);
2393
+ members = expr2.members.slice(1);
2394
+ }
2395
+ } else {
2396
+ receiver = this.transform(expr2.receiver, restContext);
2397
+ }
2398
+ invariant6(SelectQueryNode.is(receiver), "expected receiver to be select query");
2399
+ let startType;
2400
+ if (ExpressionUtils.isField(expr2.receiver)) {
2401
+ const receiverField = requireField(this.schema, context.model, expr2.receiver.field);
2402
+ startType = receiverField.type;
2403
+ } else {
2404
+ startType = context.model;
2405
+ }
2085
2406
  const memberFields = [];
2086
- let currType = receiverField.type;
2087
- for (const member of expr2.members) {
2407
+ let currType = startType;
2408
+ for (const member of members) {
2088
2409
  const fieldDef = requireField(this.schema, currType, member);
2089
2410
  memberFields.push({
2090
2411
  fieldDef,
@@ -2093,22 +2414,21 @@ var ExpressionTransformer = class {
2093
2414
  currType = fieldDef.type;
2094
2415
  }
2095
2416
  let currNode = void 0;
2096
- for (let i = expr2.members.length - 1; i >= 0; i--) {
2097
- const member = expr2.members[i];
2417
+ for (let i = members.length - 1; i >= 0; i--) {
2418
+ const member = members[i];
2098
2419
  const { fieldDef, fromModel } = memberFields[i];
2099
2420
  if (fieldDef.relation) {
2100
2421
  const relation = this.transformRelationAccess(member, fieldDef.type, {
2101
2422
  ...restContext,
2102
2423
  model: fromModel,
2103
- alias: void 0,
2104
- thisEntity: void 0
2424
+ alias: void 0
2105
2425
  });
2106
2426
  if (currNode) {
2107
- invariant5(SelectQueryNode.is(currNode), "expected select query node");
2427
+ invariant6(SelectQueryNode.is(currNode), "expected select query node");
2108
2428
  currNode = {
2109
2429
  ...relation,
2110
2430
  selections: [
2111
- SelectionNode.create(AliasNode2.create(currNode, IdentifierNode.create(expr2.members[i + 1])))
2431
+ SelectionNode.create(AliasNode3.create(currNode, IdentifierNode.create(members[i + 1])))
2112
2432
  ]
2113
2433
  };
2114
2434
  } else {
@@ -2121,15 +2441,15 @@ var ExpressionTransformer = class {
2121
2441
  };
2122
2442
  }
2123
2443
  } else {
2124
- invariant5(i === expr2.members.length - 1, "plain field access must be the last segment");
2125
- invariant5(!currNode, "plain field access must be the last segment");
2126
- currNode = ColumnNode.create(member);
2444
+ invariant6(i === members.length - 1, "plain field access must be the last segment");
2445
+ invariant6(!currNode, "plain field access must be the last segment");
2446
+ currNode = ColumnNode2.create(member);
2127
2447
  }
2128
2448
  }
2129
2449
  return {
2130
2450
  ...receiver,
2131
2451
  selections: [
2132
- SelectionNode.create(AliasNode2.create(currNode, IdentifierNode.create("$t")))
2452
+ SelectionNode.create(AliasNode3.create(currNode, IdentifierNode.create("$t")))
2133
2453
  ]
2134
2454
  };
2135
2455
  }
@@ -2146,40 +2466,33 @@ var ExpressionTransformer = class {
2146
2466
  return this.transformValue(fieldValue, fieldDef.type);
2147
2467
  }
2148
2468
  transformRelationAccess(field, relationModel, context) {
2469
+ const m2m = getManyToManyRelation(this.schema, context.model, field);
2470
+ if (m2m) {
2471
+ return this.transformManyToManyRelationAccess(m2m, context);
2472
+ }
2149
2473
  const fromModel = context.model;
2150
2474
  const { keyPairs, ownedByModel } = getRelationForeignKeyFieldPairs(this.schema, fromModel, field);
2151
- if (context.thisEntity) {
2152
- let condition;
2153
- if (ownedByModel) {
2154
- condition = conjunction(this.dialect, keyPairs.map(({ fk, pk }) => BinaryOperationNode2.create(ReferenceNode2.create(ColumnNode.create(pk), TableNode2.create(relationModel)), OperatorNode2.create("="), context.thisEntity[fk])));
2155
- } else {
2156
- condition = conjunction(this.dialect, keyPairs.map(({ fk, pk }) => BinaryOperationNode2.create(ReferenceNode2.create(ColumnNode.create(fk), TableNode2.create(relationModel)), OperatorNode2.create("="), context.thisEntity[pk])));
2157
- }
2158
- return {
2159
- kind: "SelectQueryNode",
2160
- from: FromNode.create([
2161
- TableNode2.create(relationModel)
2162
- ]),
2163
- where: WhereNode.create(condition)
2164
- };
2475
+ let condition;
2476
+ if (ownedByModel) {
2477
+ condition = conjunction(this.dialect, keyPairs.map(({ fk, pk }) => BinaryOperationNode2.create(ReferenceNode3.create(ColumnNode2.create(fk), TableNode3.create(context.alias ?? fromModel)), OperatorNode2.create("="), ReferenceNode3.create(ColumnNode2.create(pk), TableNode3.create(relationModel)))));
2165
2478
  } else {
2166
- let condition;
2167
- if (ownedByModel) {
2168
- condition = conjunction(this.dialect, keyPairs.map(({ fk, pk }) => BinaryOperationNode2.create(ReferenceNode2.create(ColumnNode.create(fk), TableNode2.create(context.alias ?? fromModel)), OperatorNode2.create("="), ReferenceNode2.create(ColumnNode.create(pk), TableNode2.create(relationModel)))));
2169
- } else {
2170
- condition = conjunction(this.dialect, keyPairs.map(({ fk, pk }) => BinaryOperationNode2.create(ReferenceNode2.create(ColumnNode.create(pk), TableNode2.create(context.alias ?? fromModel)), OperatorNode2.create("="), ReferenceNode2.create(ColumnNode.create(fk), TableNode2.create(relationModel)))));
2171
- }
2172
- return {
2173
- kind: "SelectQueryNode",
2174
- from: FromNode.create([
2175
- TableNode2.create(relationModel)
2176
- ]),
2177
- where: WhereNode.create(condition)
2178
- };
2479
+ condition = conjunction(this.dialect, keyPairs.map(({ fk, pk }) => BinaryOperationNode2.create(ReferenceNode3.create(ColumnNode2.create(pk), TableNode3.create(context.alias ?? fromModel)), OperatorNode2.create("="), ReferenceNode3.create(ColumnNode2.create(fk), TableNode3.create(relationModel)))));
2179
2480
  }
2481
+ return {
2482
+ kind: "SelectQueryNode",
2483
+ from: FromNode.create([
2484
+ TableNode3.create(relationModel)
2485
+ ]),
2486
+ where: WhereNode.create(condition)
2487
+ };
2488
+ }
2489
+ transformManyToManyRelationAccess(m2m, context) {
2490
+ const eb = expressionBuilder2();
2491
+ const relationQuery = eb.selectFrom(m2m.otherModel).innerJoin(m2m.joinTable, (join) => join.onRef(`${m2m.otherModel}.${m2m.otherPKName}`, "=", `${m2m.joinTable}.${m2m.otherFkName}`).onRef(`${m2m.joinTable}.${m2m.parentFkName}`, "=", `${context.alias ?? context.model}.${m2m.parentPKName}`));
2492
+ return relationQuery.toOperationNode();
2180
2493
  }
2181
2494
  createColumnRef(column, context) {
2182
- return ReferenceNode2.create(ColumnNode.create(column), TableNode2.create(context.alias ?? context.model));
2495
+ return ReferenceNode3.create(ColumnNode2.create(column), TableNode3.create(context.alias ?? context.model));
2183
2496
  }
2184
2497
  isAuthCall(value) {
2185
2498
  return ExpressionUtils.isCall(value) && value.function === "auth";
@@ -2190,6 +2503,31 @@ var ExpressionTransformer = class {
2190
2503
  isNullNode(node) {
2191
2504
  return ValueNode2.is(node) && node.value === null;
2192
2505
  }
2506
+ buildLogicalNot(result) {
2507
+ return ExpressionUtils.unary("!", result);
2508
+ }
2509
+ buildAnd(conditions) {
2510
+ if (conditions.length === 0) {
2511
+ return ExpressionUtils.literal(true);
2512
+ } else if (conditions.length === 1) {
2513
+ return conditions[0];
2514
+ } else {
2515
+ return conditions.reduce((acc, condition) => ExpressionUtils.binary(acc, "&&", condition));
2516
+ }
2517
+ }
2518
+ isRelationField(expr2, model) {
2519
+ const fieldDef = this.getFieldDefFromFieldRef(expr2, model);
2520
+ return !!fieldDef?.relation;
2521
+ }
2522
+ getFieldDefFromFieldRef(expr2, model) {
2523
+ if (ExpressionUtils.isField(expr2)) {
2524
+ return requireField(this.schema, model, expr2.field);
2525
+ } else if (ExpressionUtils.isMember(expr2) && expr2.members.length === 1 && ExpressionUtils.isThis(expr2.receiver)) {
2526
+ return requireField(this.schema, model, expr2.members[0]);
2527
+ } else {
2528
+ return void 0;
2529
+ }
2530
+ }
2193
2531
  };
2194
2532
  _ts_decorate([
2195
2533
  expr("literal"),
@@ -2276,103 +2614,288 @@ var PolicyHandler = class extends OperationNodeTransformer {
2276
2614
  }
2277
2615
  async handle(node, proceed) {
2278
2616
  if (!this.isCrudQueryNode(node)) {
2279
- throw new RejectedByPolicyError(void 0, "non-CRUD queries are not allowed");
2617
+ throw new RejectedByPolicyError(void 0, RejectedByPolicyReason.OTHER, "non-CRUD queries are not allowed");
2280
2618
  }
2281
2619
  if (!this.isMutationQueryNode(node)) {
2282
2620
  return proceed(this.transformNode(node));
2283
2621
  }
2284
- let mutationRequiresTransaction = false;
2285
- const mutationModel = this.getMutationModel(node);
2622
+ const { mutationModel } = this.getMutationModel(node);
2286
2623
  if (InsertQueryNode.is(node)) {
2287
- const constCondition = this.tryGetConstantPolicy(mutationModel, "create");
2288
- if (constCondition === false) {
2289
- throw new RejectedByPolicyError(mutationModel);
2290
- } else if (constCondition === void 0) {
2291
- mutationRequiresTransaction = true;
2624
+ const isManyToManyJoinTable = this.isManyToManyJoinTable(mutationModel);
2625
+ let needCheckPreCreate = true;
2626
+ if (!isManyToManyJoinTable) {
2627
+ const constCondition = this.tryGetConstantPolicy(mutationModel, "create");
2628
+ if (constCondition === true) {
2629
+ needCheckPreCreate = false;
2630
+ } else if (constCondition === false) {
2631
+ throw new RejectedByPolicyError(mutationModel);
2632
+ }
2633
+ }
2634
+ if (needCheckPreCreate) {
2635
+ await this.enforcePreCreatePolicy(node, mutationModel, isManyToManyJoinTable, proceed);
2292
2636
  }
2293
2637
  }
2294
- if (!mutationRequiresTransaction && !node.returning) {
2295
- return proceed(this.transformNode(node));
2296
- }
2297
- if (InsertQueryNode.is(node)) {
2298
- await this.enforcePreCreatePolicy(node, proceed);
2299
- }
2300
- const transformedNode = this.transformNode(node);
2301
- const result = await proceed(transformedNode);
2302
- if (!this.onlyReturningId(node)) {
2638
+ const result = await proceed(this.transformNode(node));
2639
+ if (!node.returning || this.onlyReturningId(node)) {
2640
+ return result;
2641
+ } else {
2303
2642
  const readBackResult = await this.processReadBack(node, result, proceed);
2304
2643
  if (readBackResult.rows.length !== result.rows.length) {
2305
- throw new RejectedByPolicyError(mutationModel, "result is not allowed to be read back");
2644
+ throw new RejectedByPolicyError(mutationModel, RejectedByPolicyReason.CANNOT_READ_BACK, "result is not allowed to be read back");
2306
2645
  }
2307
2646
  return readBackResult;
2308
- } else {
2647
+ }
2648
+ }
2649
+ // #region overrides
2650
+ transformSelectQuery(node) {
2651
+ let whereNode = this.transformNode(node.where);
2652
+ const policyFilter = this.createPolicyFilterForFrom(node.from);
2653
+ if (policyFilter) {
2654
+ whereNode = WhereNode2.create(whereNode?.where ? conjunction(this.dialect, [
2655
+ whereNode.where,
2656
+ policyFilter
2657
+ ]) : policyFilter);
2658
+ }
2659
+ const baseResult = super.transformSelectQuery({
2660
+ ...node,
2661
+ where: void 0
2662
+ });
2663
+ return {
2664
+ ...baseResult,
2665
+ where: whereNode
2666
+ };
2667
+ }
2668
+ transformJoin(node) {
2669
+ const table = this.extractTableName(node.table);
2670
+ if (!table) {
2671
+ return super.transformJoin(node);
2672
+ }
2673
+ const filter = this.buildPolicyFilter(table.model, table.alias, "read");
2674
+ const nestedSelect = {
2675
+ kind: "SelectQueryNode",
2676
+ from: FromNode2.create([
2677
+ node.table
2678
+ ]),
2679
+ selections: [
2680
+ SelectionNode2.createSelectAll()
2681
+ ],
2682
+ where: WhereNode2.create(filter)
2683
+ };
2684
+ return {
2685
+ ...node,
2686
+ table: AliasNode4.create(ParensNode2.create(nestedSelect), IdentifierNode2.create(table.alias ?? table.model))
2687
+ };
2688
+ }
2689
+ transformInsertQuery(node) {
2690
+ let onConflict = node.onConflict;
2691
+ if (onConflict?.updates) {
2692
+ const { mutationModel, alias } = this.getMutationModel(node);
2693
+ const filter = this.buildPolicyFilter(mutationModel, alias, "update");
2694
+ if (onConflict.updateWhere) {
2695
+ onConflict = {
2696
+ ...onConflict,
2697
+ updateWhere: WhereNode2.create(conjunction(this.dialect, [
2698
+ onConflict.updateWhere.where,
2699
+ filter
2700
+ ]))
2701
+ };
2702
+ } else {
2703
+ onConflict = {
2704
+ ...onConflict,
2705
+ updateWhere: WhereNode2.create(filter)
2706
+ };
2707
+ }
2708
+ }
2709
+ const processedNode = onConflict ? {
2710
+ ...node,
2711
+ onConflict
2712
+ } : node;
2713
+ const result = super.transformInsertQuery(processedNode);
2714
+ if (!node.returning) {
2715
+ return result;
2716
+ }
2717
+ if (this.onlyReturningId(node)) {
2309
2718
  return result;
2719
+ } else {
2720
+ const { mutationModel } = this.getMutationModel(node);
2721
+ const idFields = requireIdFields(this.client.$schema, mutationModel);
2722
+ return {
2723
+ ...result,
2724
+ returning: ReturningNode.create(idFields.map((field) => SelectionNode2.create(ColumnNode3.create(field))))
2725
+ };
2726
+ }
2727
+ }
2728
+ transformUpdateQuery(node) {
2729
+ const result = super.transformUpdateQuery(node);
2730
+ const { mutationModel, alias } = this.getMutationModel(node);
2731
+ let filter = this.buildPolicyFilter(mutationModel, alias, "update");
2732
+ if (node.from) {
2733
+ const joinFilter = this.createPolicyFilterForFrom(node.from);
2734
+ if (joinFilter) {
2735
+ filter = conjunction(this.dialect, [
2736
+ filter,
2737
+ joinFilter
2738
+ ]);
2739
+ }
2740
+ }
2741
+ return {
2742
+ ...result,
2743
+ where: WhereNode2.create(result.where ? conjunction(this.dialect, [
2744
+ result.where.where,
2745
+ filter
2746
+ ]) : filter)
2747
+ };
2748
+ }
2749
+ transformDeleteQuery(node) {
2750
+ const result = super.transformDeleteQuery(node);
2751
+ const { mutationModel, alias } = this.getMutationModel(node);
2752
+ let filter = this.buildPolicyFilter(mutationModel, alias, "delete");
2753
+ if (node.using) {
2754
+ const joinFilter = this.createPolicyFilterForTables(node.using.tables);
2755
+ if (joinFilter) {
2756
+ filter = conjunction(this.dialect, [
2757
+ filter,
2758
+ joinFilter
2759
+ ]);
2760
+ }
2310
2761
  }
2762
+ return {
2763
+ ...result,
2764
+ where: WhereNode2.create(result.where ? conjunction(this.dialect, [
2765
+ result.where.where,
2766
+ filter
2767
+ ]) : filter)
2768
+ };
2311
2769
  }
2770
+ // #endregion
2771
+ // #region helpers
2312
2772
  onlyReturningId(node) {
2313
2773
  if (!node.returning) {
2314
2774
  return true;
2315
2775
  }
2316
- const idFields = getIdFields(this.client.$schema, this.getMutationModel(node));
2776
+ const { mutationModel } = this.getMutationModel(node);
2777
+ const idFields = requireIdFields(this.client.$schema, mutationModel);
2317
2778
  const collector = new ColumnCollector();
2318
2779
  const selectedColumns = collector.collect(node.returning);
2319
2780
  return selectedColumns.every((c) => idFields.includes(c));
2320
2781
  }
2321
- async enforcePreCreatePolicy(node, proceed) {
2322
- if (!node.columns || !node.values) {
2323
- return;
2324
- }
2325
- const model = this.getMutationModel(node);
2326
- const fields = node.columns.map((c) => c.column.name);
2327
- const valueRows = this.unwrapCreateValueRows(node.values, model, fields);
2782
+ async enforcePreCreatePolicy(node, mutationModel, isManyToManyJoinTable, proceed) {
2783
+ const fields = node.columns?.map((c) => c.column.name) ?? [];
2784
+ const valueRows = node.values ? this.unwrapCreateValueRows(node.values, mutationModel, fields, isManyToManyJoinTable) : [
2785
+ []
2786
+ ];
2328
2787
  for (const values of valueRows) {
2329
- await this.enforcePreCreatePolicyForOne(model, fields, values.map((v) => v.node), values.map((v) => v.raw), proceed);
2788
+ if (isManyToManyJoinTable) {
2789
+ await this.enforcePreCreatePolicyForManyToManyJoinTable(mutationModel, fields, values.map((v) => v.node), proceed);
2790
+ } else {
2791
+ await this.enforcePreCreatePolicyForOne(mutationModel, fields, values.map((v) => v.node), proceed);
2792
+ }
2330
2793
  }
2331
2794
  }
2332
- async enforcePreCreatePolicyForOne(model, fields, values, valuesRaw, proceed) {
2333
- const thisEntity = {};
2334
- const thisEntityRaw = {};
2335
- for (let i = 0; i < fields.length; i++) {
2336
- thisEntity[fields[i]] = values[i];
2337
- thisEntityRaw[fields[i]] = valuesRaw[i];
2795
+ async enforcePreCreatePolicyForManyToManyJoinTable(tableName, fields, values, proceed) {
2796
+ const m2m = this.resolveManyToManyJoinTable(tableName);
2797
+ invariant7(m2m);
2798
+ invariant7(fields.includes("A") && fields.includes("B"), "many-to-many join table must have A and B fk fields");
2799
+ const aIndex = fields.indexOf("A");
2800
+ const aNode = values[aIndex];
2801
+ const bIndex = fields.indexOf("B");
2802
+ const bNode = values[bIndex];
2803
+ invariant7(ValueNode3.is(aNode) && ValueNode3.is(bNode), "A and B values must be ValueNode");
2804
+ const aValue = aNode.value;
2805
+ const bValue = bNode.value;
2806
+ invariant7(aValue !== null && aValue !== void 0, "A value cannot be null or undefined");
2807
+ invariant7(bValue !== null && bValue !== void 0, "B value cannot be null or undefined");
2808
+ const eb = expressionBuilder3();
2809
+ const filterA = this.buildPolicyFilter(m2m.firstModel, void 0, "update");
2810
+ const queryA = eb.selectFrom(m2m.firstModel).where(eb(eb.ref(`${m2m.firstModel}.${m2m.firstIdField}`), "=", aValue)).select(() => new ExpressionWrapper(filterA).as("$t"));
2811
+ const filterB = this.buildPolicyFilter(m2m.secondModel, void 0, "update");
2812
+ const queryB = eb.selectFrom(m2m.secondModel).where(eb(eb.ref(`${m2m.secondModel}.${m2m.secondIdField}`), "=", bValue)).select(() => new ExpressionWrapper(filterB).as("$t"));
2813
+ const queryNode = {
2814
+ kind: "SelectQueryNode",
2815
+ selections: [
2816
+ SelectionNode2.create(AliasNode4.create(queryA.toOperationNode(), IdentifierNode2.create("$conditionA"))),
2817
+ SelectionNode2.create(AliasNode4.create(queryB.toOperationNode(), IdentifierNode2.create("$conditionB")))
2818
+ ]
2819
+ };
2820
+ const result = await proceed(queryNode);
2821
+ if (!result.rows[0]?.$conditionA) {
2822
+ throw new RejectedByPolicyError(m2m.firstModel, RejectedByPolicyReason.CANNOT_READ_BACK, `many-to-many relation participant model "${m2m.firstModel}" not updatable`);
2823
+ }
2824
+ if (!result.rows[0]?.$conditionB) {
2825
+ throw new RejectedByPolicyError(m2m.secondModel, RejectedByPolicyReason.NO_ACCESS, `many-to-many relation participant model "${m2m.secondModel}" not updatable`);
2826
+ }
2827
+ }
2828
+ async enforcePreCreatePolicyForOne(model, fields, values, proceed) {
2829
+ const allFields = Object.entries(requireModel(this.client.$schema, model).fields).filter(([, def]) => !def.relation);
2830
+ const allValues = [];
2831
+ for (const [name, _def] of allFields) {
2832
+ const index = fields.indexOf(name);
2833
+ if (index >= 0) {
2834
+ allValues.push(values[index]);
2835
+ } else {
2836
+ allValues.push(ValueNode3.createImmediate(null));
2837
+ }
2338
2838
  }
2339
- const filter = this.buildPolicyFilter(model, void 0, "create", thisEntity, thisEntityRaw);
2839
+ const eb = expressionBuilder3();
2840
+ const constTable = {
2841
+ kind: "SelectQueryNode",
2842
+ from: FromNode2.create([
2843
+ AliasNode4.create(ParensNode2.create(ValuesNode.create([
2844
+ ValueListNode2.create(allValues)
2845
+ ])), IdentifierNode2.create("$t"))
2846
+ ]),
2847
+ selections: allFields.map(([name, def], index) => {
2848
+ const castedColumnRef = sql4`CAST(${eb.ref(`column${index + 1}`)} as ${sql4.raw(this.dialect.getFieldSqlType(def))})`.as(name);
2849
+ return SelectionNode2.create(castedColumnRef.toOperationNode());
2850
+ })
2851
+ };
2852
+ const filter = this.buildPolicyFilter(model, void 0, "create");
2340
2853
  const preCreateCheck = {
2341
2854
  kind: "SelectQueryNode",
2855
+ from: FromNode2.create([
2856
+ AliasNode4.create(constTable, IdentifierNode2.create(model))
2857
+ ]),
2342
2858
  selections: [
2343
- SelectionNode2.create(AliasNode3.create(filter, IdentifierNode2.create("$condition")))
2344
- ]
2859
+ SelectionNode2.create(AliasNode4.create(BinaryOperationNode3.create(FunctionNode3.create("COUNT", [
2860
+ ValueNode3.createImmediate(1)
2861
+ ]), OperatorNode3.create(">"), ValueNode3.createImmediate(0)), IdentifierNode2.create("$condition")))
2862
+ ],
2863
+ where: WhereNode2.create(filter)
2345
2864
  };
2346
2865
  const result = await proceed(preCreateCheck);
2347
2866
  if (!result.rows[0]?.$condition) {
2348
2867
  throw new RejectedByPolicyError(model);
2349
2868
  }
2350
2869
  }
2351
- unwrapCreateValueRows(node, model, fields) {
2870
+ unwrapCreateValueRows(node, model, fields, isManyToManyJoinTable) {
2352
2871
  if (ValuesNode.is(node)) {
2353
- return node.values.map((v) => this.unwrapCreateValueRow(v.values, model, fields));
2872
+ return node.values.map((v) => this.unwrapCreateValueRow(v.values, model, fields, isManyToManyJoinTable));
2354
2873
  } else if (PrimitiveValueListNode.is(node)) {
2355
2874
  return [
2356
- this.unwrapCreateValueRow(node.values, model, fields)
2875
+ this.unwrapCreateValueRow(node.values, model, fields, isManyToManyJoinTable)
2357
2876
  ];
2358
2877
  } else {
2359
2878
  throw new InternalError(`Unexpected node kind: ${node.kind} for unwrapping create values`);
2360
2879
  }
2361
2880
  }
2362
- unwrapCreateValueRow(data, model, fields) {
2363
- invariant6(data.length === fields.length, "data length must match fields length");
2881
+ unwrapCreateValueRow(data, model, fields, isImplicitManyToManyJoinTable) {
2882
+ invariant7(data.length === fields.length, "data length must match fields length");
2364
2883
  const result = [];
2365
2884
  for (let i = 0; i < data.length; i++) {
2366
2885
  const item = data[i];
2367
- const fieldDef = requireField(this.client.$schema, model, fields[i]);
2368
2886
  if (typeof item === "object" && item && "kind" in item) {
2369
- invariant6(item.kind === "ValueNode", "expecting a ValueNode");
2887
+ const fieldDef = requireField(this.client.$schema, model, fields[i]);
2888
+ invariant7(item.kind === "ValueNode", "expecting a ValueNode");
2370
2889
  result.push({
2371
2890
  node: ValueNode3.create(this.dialect.transformPrimitive(item.value, fieldDef.type, !!fieldDef.array)),
2372
2891
  raw: item.value
2373
2892
  });
2374
2893
  } else {
2375
- const value = this.dialect.transformPrimitive(item, fieldDef.type, !!fieldDef.array);
2894
+ let value = item;
2895
+ if (!isImplicitManyToManyJoinTable) {
2896
+ const fieldDef = requireField(this.client.$schema, model, fields[i]);
2897
+ value = this.dialect.transformPrimitive(item, fieldDef.type, !!fieldDef.array);
2898
+ }
2376
2899
  if (Array.isArray(value)) {
2377
2900
  result.push({
2378
2901
  node: RawNode.createWithSql(this.dialect.buildArrayLiteralSQL(value)),
@@ -2416,16 +2939,13 @@ var PolicyHandler = class extends OperationNodeTransformer {
2416
2939
  if (!this.isMutationQueryNode(node) || !node.returning) {
2417
2940
  return result;
2418
2941
  }
2419
- const table = this.getMutationModel(node);
2420
- if (!table) {
2421
- throw new InternalError(`Unable to get table name for query node: ${node}`);
2422
- }
2423
- const idConditions = this.buildIdConditions(table, result.rows);
2424
- const policyFilter = this.buildPolicyFilter(table, void 0, "read");
2942
+ const { mutationModel } = this.getMutationModel(node);
2943
+ const idConditions = this.buildIdConditions(mutationModel, result.rows);
2944
+ const policyFilter = this.buildPolicyFilter(mutationModel, void 0, "read");
2425
2945
  const select = {
2426
2946
  kind: "SelectQueryNode",
2427
2947
  from: FromNode2.create([
2428
- TableNode3.create(table)
2948
+ TableNode4.create(mutationModel)
2429
2949
  ]),
2430
2950
  where: WhereNode2.create(conjunction(this.dialect, [
2431
2951
  idConditions,
@@ -2437,15 +2957,31 @@ var PolicyHandler = class extends OperationNodeTransformer {
2437
2957
  return selectResult;
2438
2958
  }
2439
2959
  buildIdConditions(table, rows) {
2440
- const idFields = getIdFields(this.client.$schema, table);
2441
- return disjunction(this.dialect, rows.map((row) => conjunction(this.dialect, idFields.map((field) => BinaryOperationNode3.create(ColumnNode2.create(field), OperatorNode3.create("="), ValueNode3.create(row[field]))))));
2960
+ const idFields = requireIdFields(this.client.$schema, table);
2961
+ return disjunction(this.dialect, rows.map((row) => conjunction(this.dialect, idFields.map((field) => BinaryOperationNode3.create(ColumnNode3.create(field), OperatorNode3.create("="), ValueNode3.create(row[field]))))));
2442
2962
  }
2443
2963
  getMutationModel(node) {
2444
- const r = match8(node).when(InsertQueryNode.is, (node2) => getTableName(node2.into)).when(UpdateQueryNode.is, (node2) => getTableName(node2.table)).when(DeleteQueryNode.is, (node2) => {
2964
+ const r = match8(node).when(InsertQueryNode.is, (node2) => ({
2965
+ mutationModel: getTableName(node2.into),
2966
+ alias: void 0
2967
+ })).when(UpdateQueryNode.is, (node2) => {
2968
+ if (!node2.table) {
2969
+ throw new QueryError("Update query must have a table");
2970
+ }
2971
+ const r2 = this.extractTableName(node2.table);
2972
+ return r2 ? {
2973
+ mutationModel: r2.model,
2974
+ alias: r2.alias
2975
+ } : void 0;
2976
+ }).when(DeleteQueryNode.is, (node2) => {
2445
2977
  if (node2.from.froms.length !== 1) {
2446
- throw new InternalError("Only one from table is supported for delete");
2978
+ throw new QueryError("Only one from table is supported for delete");
2447
2979
  }
2448
- return getTableName(node2.from.froms[0]);
2980
+ const r2 = this.extractTableName(node2.from.froms[0]);
2981
+ return r2 ? {
2982
+ mutationModel: r2.model,
2983
+ alias: r2.alias
2984
+ } : void 0;
2449
2985
  }).exhaustive();
2450
2986
  if (!r) {
2451
2987
  throw new InternalError(`Unable to get table name for query node: ${node}`);
@@ -2458,13 +2994,17 @@ var PolicyHandler = class extends OperationNodeTransformer {
2458
2994
  isMutationQueryNode(node) {
2459
2995
  return InsertQueryNode.is(node) || UpdateQueryNode.is(node) || DeleteQueryNode.is(node);
2460
2996
  }
2461
- buildPolicyFilter(model, alias, operation, thisEntity, thisEntityRaw) {
2997
+ buildPolicyFilter(model, alias, operation) {
2998
+ const m2mFilter = this.getModelPolicyFilterForManyToManyJoinTable(model, alias, operation);
2999
+ if (m2mFilter) {
3000
+ return m2mFilter;
3001
+ }
2462
3002
  const policies = this.getModelPolicies(model, operation);
2463
3003
  if (policies.length === 0) {
2464
3004
  return falseNode(this.dialect);
2465
3005
  }
2466
- const allows = policies.filter((policy) => policy.kind === "allow").map((policy) => this.transformPolicyCondition(model, alias, operation, policy, thisEntity, thisEntityRaw));
2467
- const denies = policies.filter((policy) => policy.kind === "deny").map((policy) => this.transformPolicyCondition(model, alias, operation, policy, thisEntity, thisEntityRaw));
3006
+ const allows = policies.filter((policy) => policy.kind === "allow").map((policy) => this.compilePolicyCondition(model, alias, operation, policy));
3007
+ const denies = policies.filter((policy) => policy.kind === "deny").map((policy) => this.compilePolicyCondition(model, alias, operation, policy));
2468
3008
  let combinedPolicy;
2469
3009
  if (allows.length === 0) {
2470
3010
  combinedPolicy = falseNode(this.dialect);
@@ -2480,102 +3020,59 @@ var PolicyHandler = class extends OperationNodeTransformer {
2480
3020
  }
2481
3021
  return combinedPolicy;
2482
3022
  }
2483
- transformSelectQuery(node) {
2484
- let whereNode = node.where;
2485
- node.from?.froms.forEach((from) => {
2486
- const extractResult = this.extractTableName(from);
2487
- if (extractResult) {
2488
- const { model, alias } = extractResult;
2489
- const filter = this.buildPolicyFilter(model, alias, "read");
2490
- whereNode = WhereNode2.create(whereNode?.where ? conjunction(this.dialect, [
2491
- whereNode.where,
2492
- filter
2493
- ]) : filter);
2494
- }
2495
- });
2496
- const baseResult = super.transformSelectQuery({
2497
- ...node,
2498
- where: void 0
2499
- });
2500
- return {
2501
- ...baseResult,
2502
- where: whereNode
2503
- };
2504
- }
2505
- transformInsertQuery(node) {
2506
- const result = super.transformInsertQuery(node);
2507
- if (!node.returning) {
2508
- return result;
2509
- }
2510
- if (this.onlyReturningId(node)) {
2511
- return result;
2512
- } else {
2513
- const idFields = getIdFields(this.client.$schema, this.getMutationModel(node));
2514
- return {
2515
- ...result,
2516
- returning: ReturningNode.create(idFields.map((field) => SelectionNode2.create(ColumnNode2.create(field))))
2517
- };
2518
- }
2519
- }
2520
- transformUpdateQuery(node) {
2521
- const result = super.transformUpdateQuery(node);
2522
- const mutationModel = this.getMutationModel(node);
2523
- const filter = this.buildPolicyFilter(mutationModel, void 0, "update");
2524
- return {
2525
- ...result,
2526
- where: WhereNode2.create(result.where ? conjunction(this.dialect, [
2527
- result.where.where,
2528
- filter
2529
- ]) : filter)
2530
- };
2531
- }
2532
- transformDeleteQuery(node) {
2533
- const result = super.transformDeleteQuery(node);
2534
- const mutationModel = this.getMutationModel(node);
2535
- const filter = this.buildPolicyFilter(mutationModel, void 0, "delete");
2536
- return {
2537
- ...result,
2538
- where: WhereNode2.create(result.where ? conjunction(this.dialect, [
2539
- result.where.where,
2540
- filter
2541
- ]) : filter)
2542
- };
2543
- }
2544
- extractTableName(from) {
2545
- if (TableNode3.is(from)) {
3023
+ extractTableName(node) {
3024
+ if (TableNode4.is(node)) {
2546
3025
  return {
2547
- model: from.table.identifier.name
3026
+ model: node.table.identifier.name
2548
3027
  };
2549
3028
  }
2550
- if (AliasNode3.is(from)) {
2551
- const inner = this.extractTableName(from.node);
3029
+ if (AliasNode4.is(node)) {
3030
+ const inner = this.extractTableName(node.node);
2552
3031
  if (!inner) {
2553
3032
  return void 0;
2554
3033
  }
2555
3034
  return {
2556
3035
  model: inner.model,
2557
- alias: IdentifierNode2.is(from.alias) ? from.alias.name : void 0
3036
+ alias: IdentifierNode2.is(node.alias) ? node.alias.name : void 0
2558
3037
  };
2559
3038
  } else {
2560
3039
  return void 0;
2561
3040
  }
2562
3041
  }
2563
- transformPolicyCondition(model, alias, operation, policy, thisEntity, thisEntityRaw) {
2564
- return new ExpressionTransformer(this.client.$schema, this.client.$options, this.client.$auth).transform(policy.condition, {
3042
+ createPolicyFilterForFrom(node) {
3043
+ if (!node) {
3044
+ return void 0;
3045
+ }
3046
+ return this.createPolicyFilterForTables(node.froms);
3047
+ }
3048
+ createPolicyFilterForTables(tables) {
3049
+ return tables.reduce((acc, table) => {
3050
+ const extractResult = this.extractTableName(table);
3051
+ if (extractResult) {
3052
+ const { model, alias } = extractResult;
3053
+ const filter = this.buildPolicyFilter(model, alias, "read");
3054
+ return acc ? conjunction(this.dialect, [
3055
+ acc,
3056
+ filter
3057
+ ]) : filter;
3058
+ }
3059
+ return acc;
3060
+ }, void 0);
3061
+ }
3062
+ compilePolicyCondition(model, alias, operation, policy) {
3063
+ return new ExpressionTransformer(this.client).transform(policy.condition, {
2565
3064
  model,
2566
3065
  alias,
2567
3066
  operation,
2568
- thisEntity,
2569
- thisEntityRaw,
2570
3067
  auth: this.client.$auth
2571
3068
  });
2572
3069
  }
2573
- getModelPolicies(modelName, operation) {
2574
- const modelDef = requireModel(this.client.$schema, modelName);
3070
+ getModelPolicies(model, operation) {
3071
+ const modelDef = requireModel(this.client.$schema, model);
2575
3072
  const result = [];
2576
3073
  const extractOperations = /* @__PURE__ */ __name((expr2) => {
2577
- invariant6(ExpressionUtils.isLiteral(expr2), "expecting a literal");
2578
- invariant6(typeof expr2.value === "string", "expecting a string literal");
3074
+ invariant7(ExpressionUtils.isLiteral(expr2), "expecting a literal");
3075
+ invariant7(typeof expr2.value === "string", "expecting a string literal");
2579
3076
  return expr2.value.split(",").filter((v) => !!v).map((v) => v.trim());
2580
3077
  }, "extractOperations");
2581
3078
  if (modelDef.attributes) {
@@ -2587,8 +3084,84 @@ var PolicyHandler = class extends OperationNodeTransformer {
2587
3084
  }
2588
3085
  return result;
2589
3086
  }
3087
+ resolveManyToManyJoinTable(tableName) {
3088
+ for (const model of Object.values(this.client.$schema.models)) {
3089
+ for (const field of Object.values(model.fields)) {
3090
+ const m2m = getManyToManyRelation(this.client.$schema, model.name, field.name);
3091
+ if (m2m?.joinTable === tableName) {
3092
+ const sortedRecord = [
3093
+ {
3094
+ model: model.name,
3095
+ field: field.name
3096
+ },
3097
+ {
3098
+ model: m2m.otherModel,
3099
+ field: m2m.otherField
3100
+ }
3101
+ ].sort(this.manyToManySorter);
3102
+ const firstIdFields = requireIdFields(this.client.$schema, sortedRecord[0].model);
3103
+ const secondIdFields = requireIdFields(this.client.$schema, sortedRecord[1].model);
3104
+ invariant7(firstIdFields.length === 1 && secondIdFields.length === 1, "only single-field id is supported for implicit many-to-many join table");
3105
+ return {
3106
+ firstModel: sortedRecord[0].model,
3107
+ firstField: sortedRecord[0].field,
3108
+ firstIdField: firstIdFields[0],
3109
+ secondModel: sortedRecord[1].model,
3110
+ secondField: sortedRecord[1].field,
3111
+ secondIdField: secondIdFields[0]
3112
+ };
3113
+ }
3114
+ }
3115
+ }
3116
+ return void 0;
3117
+ }
3118
+ manyToManySorter(a, b) {
3119
+ return a.model !== b.model ? a.model.localeCompare(b.model) : a.field.localeCompare(b.field);
3120
+ }
3121
+ isManyToManyJoinTable(tableName) {
3122
+ return !!this.resolveManyToManyJoinTable(tableName);
3123
+ }
3124
+ getModelPolicyFilterForManyToManyJoinTable(tableName, alias, operation) {
3125
+ const m2m = this.resolveManyToManyJoinTable(tableName);
3126
+ if (!m2m) {
3127
+ return void 0;
3128
+ }
3129
+ const checkForOperation = operation === "read" ? "read" : "update";
3130
+ const eb = expressionBuilder3();
3131
+ const joinTable = alias ?? tableName;
3132
+ const aQuery = eb.selectFrom(m2m.firstModel).whereRef(`${m2m.firstModel}.${m2m.firstIdField}`, "=", `${joinTable}.A`).select(() => new ExpressionWrapper(this.buildPolicyFilter(m2m.firstModel, void 0, checkForOperation)).as("$conditionA"));
3133
+ const bQuery = eb.selectFrom(m2m.secondModel).whereRef(`${m2m.secondModel}.${m2m.secondIdField}`, "=", `${joinTable}.B`).select(() => new ExpressionWrapper(this.buildPolicyFilter(m2m.secondModel, void 0, checkForOperation)).as("$conditionB"));
3134
+ return eb.and([
3135
+ aQuery,
3136
+ bQuery
3137
+ ]).toOperationNode();
3138
+ }
2590
3139
  };
2591
3140
 
3141
+ // src/plugins/policy/functions.ts
3142
+ var check = /* @__PURE__ */ __name((eb, args, { client, model, modelAlias, operation }) => {
3143
+ invariant8(args.length === 1 || args.length === 2, '"check" function requires 1 or 2 arguments');
3144
+ const arg1Node = args[0].toOperationNode();
3145
+ const arg2Node = args.length === 2 ? args[1].toOperationNode() : void 0;
3146
+ if (arg2Node) {
3147
+ invariant8(ValueNode4.is(arg2Node) && typeof arg2Node.value === "string", '"operation" parameter must be a string literal when provided');
3148
+ invariant8(CRUD.includes(arg2Node.value), '"operation" parameter must be one of "create", "read", "update", "delete"');
3149
+ }
3150
+ const fieldName = extractFieldName(arg1Node);
3151
+ invariant8(fieldName, 'Failed to extract field name from the first argument of "check" function');
3152
+ const fieldDef = requireField(client.$schema, model, fieldName);
3153
+ invariant8(fieldDef.relation, `Field "${fieldName}" is not a relation field in model "${model}"`);
3154
+ invariant8(!fieldDef.array, `Field "${fieldName}" is a to-many relation, which is not supported by "check"`);
3155
+ const relationModel = fieldDef.type;
3156
+ const op = arg2Node ? arg2Node.value : operation;
3157
+ const policyHandler = new PolicyHandler(client);
3158
+ const joinPairs = buildJoinPairs(client.$schema, model, modelAlias, fieldName, relationModel);
3159
+ const joinCondition = joinPairs.length === 1 ? eb(eb.ref(joinPairs[0][0]), "=", eb.ref(joinPairs[0][1])) : eb.and(joinPairs.map(([left, right]) => eb(eb.ref(left), "=", eb.ref(right))));
3160
+ const policyCondition = policyHandler.buildPolicyFilter(relationModel, void 0, op);
3161
+ const result = eb.selectFrom(relationModel).where(joinCondition).select(new ExpressionWrapper2(policyCondition).as("$condition"));
3162
+ return result;
3163
+ }, "check");
3164
+
2592
3165
  // src/plugins/policy/plugin.ts
2593
3166
  var PolicyPlugin = class {
2594
3167
  static {
@@ -2603,6 +3176,11 @@ var PolicyPlugin = class {
2603
3176
  get description() {
2604
3177
  return "Enforces access policies defined in the schema.";
2605
3178
  }
3179
+ get functions() {
3180
+ return {
3181
+ check
3182
+ };
3183
+ }
2606
3184
  onKyselyQuery({
2607
3185
  query,
2608
3186
  client,
@@ -2619,6 +3197,7 @@ var PolicyPlugin = class {
2619
3197
  };
2620
3198
  export {
2621
3199
  PolicyPlugin,
2622
- RejectedByPolicyError
3200
+ RejectedByPolicyError,
3201
+ RejectedByPolicyReason
2623
3202
  };
2624
3203
  //# sourceMappingURL=index.js.map