@zenstackhq/runtime 3.0.0-beta.4 → 3.0.0-beta.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
7
9
  var __export = (target, all) => {
@@ -16,57 +18,70 @@ var __copyProps = (to, from, except, desc) => {
16
18
  }
17
19
  return to;
18
20
  };
21
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
+ // If the importer is in node compatibility mode or this is not an ESM
23
+ // file that has been converted to a CommonJS file using a Babel-
24
+ // compatible transform (i.e. "__esModule" has not been set), then set
25
+ // "default" to the CommonJS "module.exports" for node compatibility.
26
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
+ mod
28
+ ));
19
29
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
30
 
21
31
  // src/plugins/policy/index.ts
22
32
  var policy_exports = {};
23
33
  __export(policy_exports, {
24
34
  PolicyPlugin: () => PolicyPlugin,
25
- RejectedByPolicyError: () => RejectedByPolicyError
35
+ RejectedByPolicyError: () => RejectedByPolicyError,
36
+ RejectedByPolicyReason: () => RejectedByPolicyReason
26
37
  });
27
38
  module.exports = __toCommonJS(policy_exports);
28
39
 
29
40
  // src/plugins/policy/errors.ts
41
+ var RejectedByPolicyReason = /* @__PURE__ */ function(RejectedByPolicyReason2) {
42
+ RejectedByPolicyReason2["NO_ACCESS"] = "no-access";
43
+ RejectedByPolicyReason2["CANNOT_READ_BACK"] = "cannot-read-back";
44
+ RejectedByPolicyReason2["OTHER"] = "other";
45
+ return RejectedByPolicyReason2;
46
+ }({});
30
47
  var RejectedByPolicyError = class extends Error {
31
48
  static {
32
49
  __name(this, "RejectedByPolicyError");
33
50
  }
34
51
  model;
35
52
  reason;
36
- constructor(model, reason) {
37
- super(reason ?? `Operation rejected by policy${model ? ": " + model : ""}`), this.model = model, this.reason = reason;
53
+ constructor(model, reason = "no-access", message) {
54
+ super(message ?? `Operation rejected by policy${model ? ": " + model : ""}`), this.model = model, this.reason = reason;
38
55
  }
39
56
  };
40
57
 
41
- // src/plugins/policy/policy-handler.ts
42
- var import_common_helpers6 = require("@zenstackhq/common-helpers");
43
- var import_kysely7 = require("kysely");
44
- var import_ts_pattern8 = require("ts-pattern");
45
-
46
- // src/client/crud/dialects/index.ts
47
- var import_ts_pattern5 = require("ts-pattern");
48
-
49
- // src/client/crud/dialects/postgresql.ts
50
- var import_common_helpers2 = require("@zenstackhq/common-helpers");
51
- var import_kysely2 = require("kysely");
52
- var import_ts_pattern3 = require("ts-pattern");
58
+ // src/plugins/policy/functions.ts
59
+ var import_common_helpers8 = require("@zenstackhq/common-helpers");
60
+ var import_kysely9 = require("kysely");
53
61
 
54
- // src/client/constants.ts
55
- var DELEGATE_JOINED_FIELD_PREFIX = "$delegate$";
56
- var LOGICAL_COMBINATORS = [
57
- "AND",
58
- "OR",
59
- "NOT"
60
- ];
61
- var AGGREGATE_OPERATORS = [
62
- "_count",
63
- "_sum",
64
- "_avg",
65
- "_min",
66
- "_max"
62
+ // src/client/contract.ts
63
+ var CRUD = [
64
+ "create",
65
+ "read",
66
+ "update",
67
+ "delete"
67
68
  ];
68
69
 
70
+ // src/client/kysely-utils.ts
71
+ var import_kysely = require("kysely");
72
+ function extractFieldName(node) {
73
+ if (import_kysely.ReferenceNode.is(node) && import_kysely.ColumnNode.is(node.column)) {
74
+ return node.column.column.name;
75
+ } else if (import_kysely.ColumnNode.is(node)) {
76
+ return node.column.name;
77
+ } else {
78
+ return void 0;
79
+ }
80
+ }
81
+ __name(extractFieldName, "extractFieldName");
82
+
69
83
  // src/client/query-utils.ts
84
+ var import_common_helpers = require("@zenstackhq/common-helpers");
70
85
  var import_ts_pattern = require("ts-pattern");
71
86
 
72
87
  // src/schema/expression.ts
@@ -213,11 +228,15 @@ function requireField(schema, modelOrType, field) {
213
228
  throw new QueryError(`Model or type "${modelOrType}" not found in schema`);
214
229
  }
215
230
  __name(requireField, "requireField");
216
- function getIdFields(schema, model) {
231
+ function requireIdFields(schema, model) {
217
232
  const modelDef = requireModel(schema, model);
218
- return modelDef?.idFields;
233
+ const result = modelDef?.idFields;
234
+ if (!result) {
235
+ throw new InternalError(`Model "${model}" does not have ID field(s)`);
236
+ }
237
+ return result;
219
238
  }
220
- __name(getIdFields, "getIdFields");
239
+ __name(requireIdFields, "requireIdFields");
221
240
  function getRelationForeignKeyFieldPairs(schema, model, relationField) {
222
241
  const fieldDef = requireField(schema, model, relationField);
223
242
  if (!fieldDef?.relation) {
@@ -310,7 +329,7 @@ function buildFieldRef(schema, model, field, options, eb, modelAlias, inlineComp
310
329
  throw new QueryError(`Computed field "${field}" implementation not provided for model "${model}"`);
311
330
  }
312
331
  return computer(eb, {
313
- currentModel: modelAlias
332
+ modelAlias
314
333
  });
315
334
  }
316
335
  }
@@ -337,7 +356,7 @@ function buildJoinPairs(schema, model, modelAlias, relationField, relationModelA
337
356
  }
338
357
  __name(buildJoinPairs, "buildJoinPairs");
339
358
  function makeDefaultOrderBy(schema, model) {
340
- const idFields = getIdFields(schema, model);
359
+ const idFields = requireIdFields(schema, model);
341
360
  return idFields.map((f) => ({
342
361
  [f]: "asc"
343
362
  }));
@@ -376,11 +395,17 @@ function getManyToManyRelation(schema, model, field) {
376
395
  "A"
377
396
  ];
378
397
  }
398
+ const modelIdFields = requireIdFields(schema, model);
399
+ (0, import_common_helpers.invariant)(modelIdFields.length === 1, "Only single-field ID is supported for many-to-many relation");
400
+ const otherIdFields = requireIdFields(schema, fieldDef.type);
401
+ (0, import_common_helpers.invariant)(otherIdFields.length === 1, "Only single-field ID is supported for many-to-many relation");
379
402
  return {
380
403
  parentFkName: orderedFK[0],
404
+ parentPKName: modelIdFields[0],
381
405
  otherModel: fieldDef.type,
382
406
  otherField: fieldDef.relation.opposite,
383
407
  otherFkName: orderedFK[1],
408
+ otherPKName: otherIdFields[0],
384
409
  joinTable: fieldDef.relation.name ? `_${fieldDef.relation.name}` : `_${sortedModelNames[0]}To${sortedModelNames[1]}`
385
410
  };
386
411
  } else {
@@ -436,9 +461,38 @@ function aggregate(eb, expr2, op) {
436
461
  }
437
462
  __name(aggregate, "aggregate");
438
463
 
464
+ // src/plugins/policy/policy-handler.ts
465
+ var import_common_helpers7 = require("@zenstackhq/common-helpers");
466
+ var import_kysely8 = require("kysely");
467
+ var import_ts_pattern8 = require("ts-pattern");
468
+
469
+ // src/client/crud/dialects/index.ts
470
+ var import_ts_pattern5 = require("ts-pattern");
471
+
472
+ // src/client/crud/dialects/postgresql.ts
473
+ var import_common_helpers3 = require("@zenstackhq/common-helpers");
474
+ var import_decimal = __toESM(require("decimal.js"), 1);
475
+ var import_kysely3 = require("kysely");
476
+ var import_ts_pattern3 = require("ts-pattern");
477
+
478
+ // src/client/constants.ts
479
+ var DELEGATE_JOINED_FIELD_PREFIX = "$delegate$";
480
+ var LOGICAL_COMBINATORS = [
481
+ "AND",
482
+ "OR",
483
+ "NOT"
484
+ ];
485
+ var AGGREGATE_OPERATORS = [
486
+ "_count",
487
+ "_sum",
488
+ "_avg",
489
+ "_min",
490
+ "_max"
491
+ ];
492
+
439
493
  // src/client/crud/dialects/base-dialect.ts
440
- var import_common_helpers = require("@zenstackhq/common-helpers");
441
- var import_kysely = require("kysely");
494
+ var import_common_helpers2 = require("@zenstackhq/common-helpers");
495
+ var import_kysely2 = require("kysely");
442
496
  var import_ts_pattern2 = require("ts-pattern");
443
497
 
444
498
  // src/utils/enumerate.ts
@@ -469,6 +523,9 @@ var BaseCrudDialect = class {
469
523
  transformPrimitive(value, _type, _forArrayField) {
470
524
  return value;
471
525
  }
526
+ transformOutput(value, _type) {
527
+ return value;
528
+ }
472
529
  // #region common query builders
473
530
  buildSelectModel(eb, model, modelAlias) {
474
531
  const modelDef = requireModel(this.schema, model);
@@ -497,7 +554,7 @@ var BaseCrudDialect = class {
497
554
  if ("distinct" in args && args.distinct) {
498
555
  const distinct = ensureArray(args.distinct);
499
556
  if (this.supportsDistinctOn) {
500
- result = result.distinctOn(distinct.map((f) => import_kysely.sql.ref(`${modelAlias}.${f}`)));
557
+ result = result.distinctOn(distinct.map((f) => import_kysely2.sql.ref(`${modelAlias}.${f}`)));
501
558
  } else {
502
559
  throw new QueryError(`"distinct" is not supported by "${this.schema.provider.type}" provider`);
503
560
  }
@@ -547,7 +604,7 @@ var BaseCrudDialect = class {
547
604
  buildCursorFilter(model, query, cursor, orderBy, negateOrderBy, modelAlias) {
548
605
  const _orderBy = orderBy ?? makeDefaultOrderBy(this.schema, model);
549
606
  const orderByItems = ensureArray(_orderBy).flatMap((obj) => Object.entries(obj));
550
- const eb = (0, import_kysely.expressionBuilder)();
607
+ const eb = (0, import_kysely2.expressionBuilder)();
551
608
  const subQueryAlias = `${model}$cursor$sub`;
552
609
  const cursorFilter = this.buildFilter(eb, model, subQueryAlias, cursor);
553
610
  let result = query;
@@ -582,7 +639,7 @@ var BaseCrudDialect = class {
582
639
  if (payload === null) {
583
640
  const { ownedByModel, keyPairs } = getRelationForeignKeyFieldPairs(this.schema, model, field);
584
641
  if (ownedByModel && !fieldDef.originModel) {
585
- return this.and(eb, ...keyPairs.map(({ fk }) => eb(import_kysely.sql.ref(`${modelAlias}.${fk}`), "is", null)));
642
+ return this.and(eb, ...keyPairs.map(({ fk }) => eb(import_kysely2.sql.ref(`${modelAlias}.${fk}`), "is", null)));
586
643
  } else {
587
644
  return this.buildToOneRelationFilter(eb, model, modelAlias, field, fieldDef, {
588
645
  is: null
@@ -599,7 +656,7 @@ var BaseCrudDialect = class {
599
656
  joinAlias
600
657
  );
601
658
  const filterResultField = `${field}$filter`;
602
- const joinSelect = eb.selectFrom(`${fieldDef.type} as ${joinAlias}`).where(() => this.and(eb, ...joinPairs.map(([left, right]) => eb(import_kysely.sql.ref(left), "=", import_kysely.sql.ref(right))))).select(() => eb.fn.count(eb.lit(1)).as(filterResultField));
659
+ const joinSelect = eb.selectFrom(`${fieldDef.type} as ${joinAlias}`).where(() => this.and(eb, ...joinPairs.map(([left, right]) => eb(import_kysely2.sql.ref(left), "=", import_kysely2.sql.ref(right))))).select(() => eb.fn.count(eb.lit(1)).as(filterResultField));
603
660
  const conditions = [];
604
661
  if ("is" in payload || "isNot" in payload) {
605
662
  if ("is" in payload) {
@@ -629,24 +686,26 @@ var BaseCrudDialect = class {
629
686
  }
630
687
  buildToManyRelationFilter(eb, model, modelAlias, field, fieldDef, payload) {
631
688
  if (payload === null) {
632
- return eb(import_kysely.sql.ref(`${modelAlias}.${field}`), "is", null);
689
+ return eb(import_kysely2.sql.ref(`${modelAlias}.${field}`), "is", null);
633
690
  }
634
691
  const relationModel = fieldDef.type;
635
692
  const relationFilterSelectAlias = `${modelAlias}$${field}$filter`;
636
693
  const buildPkFkWhereRefs = /* @__PURE__ */ __name((eb2) => {
637
694
  const m2m = getManyToManyRelation(this.schema, model, field);
638
695
  if (m2m) {
639
- const modelIdField = getIdFields(this.schema, model)[0];
640
- const relationIdField = getIdFields(this.schema, relationModel)[0];
641
- return eb2(import_kysely.sql.ref(`${relationFilterSelectAlias}.${relationIdField}`), "in", eb2.selectFrom(m2m.joinTable).select(`${m2m.joinTable}.${m2m.otherFkName}`).whereRef(import_kysely.sql.ref(`${m2m.joinTable}.${m2m.parentFkName}`), "=", import_kysely.sql.ref(`${modelAlias}.${modelIdField}`)));
696
+ const modelIdFields = requireIdFields(this.schema, model);
697
+ (0, import_common_helpers2.invariant)(modelIdFields.length === 1, "many-to-many relation must have exactly one id field");
698
+ const relationIdFields = requireIdFields(this.schema, relationModel);
699
+ (0, import_common_helpers2.invariant)(relationIdFields.length === 1, "many-to-many relation must have exactly one id field");
700
+ return eb2(import_kysely2.sql.ref(`${relationFilterSelectAlias}.${relationIdFields[0]}`), "in", eb2.selectFrom(m2m.joinTable).select(`${m2m.joinTable}.${m2m.otherFkName}`).whereRef(import_kysely2.sql.ref(`${m2m.joinTable}.${m2m.parentFkName}`), "=", import_kysely2.sql.ref(`${modelAlias}.${modelIdFields[0]}`)));
642
701
  } else {
643
702
  const relationKeyPairs = getRelationForeignKeyFieldPairs(this.schema, model, field);
644
703
  let result2 = this.true(eb2);
645
704
  for (const { fk, pk } of relationKeyPairs.keyPairs) {
646
705
  if (relationKeyPairs.ownedByModel) {
647
- result2 = this.and(eb2, result2, eb2(import_kysely.sql.ref(`${modelAlias}.${fk}`), "=", import_kysely.sql.ref(`${relationFilterSelectAlias}.${pk}`)));
706
+ result2 = this.and(eb2, result2, eb2(import_kysely2.sql.ref(`${modelAlias}.${fk}`), "=", import_kysely2.sql.ref(`${relationFilterSelectAlias}.${pk}`)));
648
707
  } else {
649
- result2 = this.and(eb2, result2, eb2(import_kysely.sql.ref(`${modelAlias}.${pk}`), "=", import_kysely.sql.ref(`${relationFilterSelectAlias}.${fk}`)));
708
+ result2 = this.and(eb2, result2, eb2(import_kysely2.sql.ref(`${modelAlias}.${pk}`), "=", import_kysely2.sql.ref(`${relationFilterSelectAlias}.${fk}`)));
650
709
  }
651
710
  }
652
711
  return result2;
@@ -729,7 +788,7 @@ var BaseCrudDialect = class {
729
788
  return eb(lhs, "=", rhs !== null && rhs !== void 0 ? this.transformPrimitive(rhs, type, false) : rhs);
730
789
  }
731
790
  buildStandardFilter(eb, type, payload, lhs, getRhs, recurse, throwIfInvalid = false, onlyForKeys = void 0, excludeKeys = []) {
732
- if (payload === null || !(0, import_common_helpers.isPlainObject)(payload)) {
791
+ if (payload === null || !(0, import_common_helpers2.isPlainObject)(payload)) {
733
792
  return {
734
793
  conditions: [
735
794
  this.buildLiteralFilter(eb, lhs, type, payload)
@@ -748,14 +807,14 @@ var BaseCrudDialect = class {
748
807
  }
749
808
  const rhs = Array.isArray(value) ? value.map(getRhs) : getRhs(value);
750
809
  const condition = (0, import_ts_pattern2.match)(op).with("equals", () => rhs === null ? eb(lhs, "is", null) : eb(lhs, "=", rhs)).with("in", () => {
751
- (0, import_common_helpers.invariant)(Array.isArray(rhs), "right hand side must be an array");
810
+ (0, import_common_helpers2.invariant)(Array.isArray(rhs), "right hand side must be an array");
752
811
  if (rhs.length === 0) {
753
812
  return this.false(eb);
754
813
  } else {
755
814
  return eb(lhs, "in", rhs);
756
815
  }
757
816
  }).with("notIn", () => {
758
- (0, import_common_helpers.invariant)(Array.isArray(rhs), "right hand side must be an array");
817
+ (0, import_common_helpers2.invariant)(Array.isArray(rhs), "right hand side must be an array");
759
818
  if (rhs.length === 0) {
760
819
  return this.true(eb);
761
820
  } else {
@@ -795,7 +854,7 @@ var BaseCrudDialect = class {
795
854
  if (key === "mode" || consumedKeys.includes(key)) {
796
855
  continue;
797
856
  }
798
- const condition = (0, import_ts_pattern2.match)(key).with("contains", () => mode === "insensitive" ? eb(fieldRef, "ilike", import_kysely.sql.val(`%${value}%`)) : eb(fieldRef, "like", import_kysely.sql.val(`%${value}%`))).with("startsWith", () => mode === "insensitive" ? eb(fieldRef, "ilike", import_kysely.sql.val(`${value}%`)) : eb(fieldRef, "like", import_kysely.sql.val(`${value}%`))).with("endsWith", () => mode === "insensitive" ? eb(fieldRef, "ilike", import_kysely.sql.val(`%${value}`)) : eb(fieldRef, "like", import_kysely.sql.val(`%${value}`))).otherwise(() => {
857
+ const condition = (0, import_ts_pattern2.match)(key).with("contains", () => mode === "insensitive" ? eb(fieldRef, "ilike", import_kysely2.sql.val(`%${value}%`)) : eb(fieldRef, "like", import_kysely2.sql.val(`%${value}%`))).with("startsWith", () => mode === "insensitive" ? eb(fieldRef, "ilike", import_kysely2.sql.val(`${value}%`)) : eb(fieldRef, "like", import_kysely2.sql.val(`${value}%`))).with("endsWith", () => mode === "insensitive" ? eb(fieldRef, "ilike", import_kysely2.sql.val(`%${value}`)) : eb(fieldRef, "like", import_kysely2.sql.val(`%${value}`))).otherwise(() => {
799
858
  throw new QueryError(`Invalid string filter key: ${key}`);
800
859
  });
801
860
  if (condition) {
@@ -807,16 +866,16 @@ var BaseCrudDialect = class {
807
866
  }
808
867
  prepStringCasing(eb, value, mode) {
809
868
  if (!mode || mode === "default") {
810
- return value === null ? value : import_kysely.sql.val(value);
869
+ return value === null ? value : import_kysely2.sql.val(value);
811
870
  }
812
871
  if (typeof value === "string") {
813
872
  return eb.fn("lower", [
814
- import_kysely.sql.val(value)
873
+ import_kysely2.sql.val(value)
815
874
  ]);
816
875
  } else if (Array.isArray(value)) {
817
876
  return value.map((v) => this.prepStringCasing(eb, v, mode));
818
877
  } else {
819
- return value === null ? null : import_kysely.sql.val(value);
878
+ return value === null ? null : import_kysely2.sql.val(value);
820
879
  }
821
880
  }
822
881
  buildNumberFilter(eb, fieldRef, type, payload) {
@@ -873,19 +932,19 @@ var BaseCrudDialect = class {
873
932
  "_min",
874
933
  "_max"
875
934
  ].includes(field)) {
876
- (0, import_common_helpers.invariant)(value && typeof value === "object", `invalid orderBy value for field "${field}"`);
935
+ (0, import_common_helpers2.invariant)(value && typeof value === "object", `invalid orderBy value for field "${field}"`);
877
936
  for (const [k, v] of Object.entries(value)) {
878
- (0, import_common_helpers.invariant)(v === "asc" || v === "desc", `invalid orderBy value for field "${field}"`);
879
- result = result.orderBy((eb) => aggregate(eb, this.fieldRef(model, k, eb, modelAlias), field), import_kysely.sql.raw(this.negateSort(v, negated)));
937
+ (0, import_common_helpers2.invariant)(v === "asc" || v === "desc", `invalid orderBy value for field "${field}"`);
938
+ result = result.orderBy((eb) => aggregate(eb, this.fieldRef(model, k, eb, modelAlias), field), import_kysely2.sql.raw(this.negateSort(v, negated)));
880
939
  }
881
940
  continue;
882
941
  }
883
942
  switch (field) {
884
943
  case "_count": {
885
- (0, import_common_helpers.invariant)(value && typeof value === "object", 'invalid orderBy value for field "_count"');
944
+ (0, import_common_helpers2.invariant)(value && typeof value === "object", 'invalid orderBy value for field "_count"');
886
945
  for (const [k, v] of Object.entries(value)) {
887
- (0, import_common_helpers.invariant)(v === "asc" || v === "desc", `invalid orderBy value for field "${field}"`);
888
- result = result.orderBy((eb) => eb.fn.count(this.fieldRef(model, k, eb, modelAlias)), import_kysely.sql.raw(this.negateSort(v, negated)));
946
+ (0, import_common_helpers2.invariant)(v === "asc" || v === "desc", `invalid orderBy value for field "${field}"`);
947
+ result = result.orderBy((eb) => eb.fn.count(this.fieldRef(model, k, eb, modelAlias)), import_kysely2.sql.raw(this.negateSort(v, negated)));
889
948
  }
890
949
  continue;
891
950
  }
@@ -894,11 +953,11 @@ var BaseCrudDialect = class {
894
953
  }
895
954
  const fieldDef = requireField(this.schema, model, field);
896
955
  if (!fieldDef.relation) {
897
- const fieldRef = this.fieldRef(model, field, (0, import_kysely.expressionBuilder)(), modelAlias);
956
+ const fieldRef = this.fieldRef(model, field, (0, import_kysely2.expressionBuilder)(), modelAlias);
898
957
  if (value === "asc" || value === "desc") {
899
958
  result = result.orderBy(fieldRef, this.negateSort(value, negated));
900
959
  } else if (value && typeof value === "object" && "nulls" in value && "sort" in value && (value.sort === "asc" || value.sort === "desc") && (value.nulls === "first" || value.nulls === "last")) {
901
- result = result.orderBy(fieldRef, import_kysely.sql.raw(`${this.negateSort(value.sort, negated)} nulls ${value.nulls}`));
960
+ result = result.orderBy(fieldRef, import_kysely2.sql.raw(`${this.negateSort(value.sort, negated)} nulls ${value.nulls}`));
902
961
  }
903
962
  } else {
904
963
  const relationModel = fieldDef.type;
@@ -907,13 +966,13 @@ var BaseCrudDialect = class {
907
966
  throw new QueryError(`invalid orderBy value for field "${field}"`);
908
967
  }
909
968
  if ("_count" in value) {
910
- (0, import_common_helpers.invariant)(value._count === "asc" || value._count === "desc", 'invalid orderBy value for field "_count"');
969
+ (0, import_common_helpers2.invariant)(value._count === "asc" || value._count === "desc", 'invalid orderBy value for field "_count"');
911
970
  const sort = this.negateSort(value._count, negated);
912
971
  result = result.orderBy((eb) => {
913
972
  const subQueryAlias = `${modelAlias}$orderBy$${field}$count`;
914
973
  let subQuery = this.buildSelectModel(eb, relationModel, subQueryAlias);
915
974
  const joinPairs = buildJoinPairs(this.schema, model, modelAlias, field, subQueryAlias);
916
- subQuery = subQuery.where(() => this.and(eb, ...joinPairs.map(([left, right]) => eb(import_kysely.sql.ref(left), "=", import_kysely.sql.ref(right)))));
975
+ subQuery = subQuery.where(() => this.and(eb, ...joinPairs.map(([left, right]) => eb(import_kysely2.sql.ref(left), "=", import_kysely2.sql.ref(right)))));
917
976
  subQuery = subQuery.select(() => eb.fn.count(eb.lit(1)).as("_count"));
918
977
  return subQuery;
919
978
  }, sort);
@@ -921,7 +980,7 @@ var BaseCrudDialect = class {
921
980
  } else {
922
981
  result = result.leftJoin(relationModel, (join) => {
923
982
  const joinPairs = buildJoinPairs(this.schema, model, modelAlias, field, relationModel);
924
- return join.on((eb) => this.and(eb, ...joinPairs.map(([left, right]) => eb(import_kysely.sql.ref(left), "=", import_kysely.sql.ref(right)))));
983
+ return join.on((eb) => this.and(eb, ...joinPairs.map(([left, right]) => eb(import_kysely2.sql.ref(left), "=", import_kysely2.sql.ref(right)))));
925
984
  });
926
985
  result = this.buildOrderBy(result, fieldDef.type, relationModel, value, false, negated);
927
986
  }
@@ -973,13 +1032,13 @@ var BaseCrudDialect = class {
973
1032
  if (fieldDef.computed) {
974
1033
  return query.select((eb) => this.fieldRef(model, field, eb, modelAlias).as(field));
975
1034
  } else if (!fieldDef.originModel) {
976
- return query.select(import_kysely.sql.ref(`${modelAlias}.${field}`).as(field));
1035
+ return query.select(import_kysely2.sql.ref(`${modelAlias}.${field}`).as(field));
977
1036
  } else {
978
1037
  return this.buildSelectField(query, fieldDef.originModel, fieldDef.originModel, field);
979
1038
  }
980
1039
  }
981
1040
  buildDelegateJoin(thisModel, thisModelAlias, otherModelAlias, query) {
982
- const idFields = getIdFields(this.schema, thisModel);
1041
+ const idFields = requireIdFields(this.schema, thisModel);
983
1042
  query = query.leftJoin(otherModelAlias, (qb) => {
984
1043
  for (const idField of idFields) {
985
1044
  qb = qb.onRef(`${thisModelAlias}.${idField}`, "=", `${otherModelAlias}.${idField}`);
@@ -1001,10 +1060,16 @@ var BaseCrudDialect = class {
1001
1060
  for (const [field, value] of Object.entries(selections.select)) {
1002
1061
  const fieldDef = requireField(this.schema, model, field);
1003
1062
  const fieldModel = fieldDef.type;
1004
- const joinPairs = buildJoinPairs(this.schema, model, parentAlias, field, fieldModel);
1005
- let fieldCountQuery = eb.selectFrom(fieldModel).select(eb.fn.countAll().as(`_count$${field}`));
1006
- for (const [left, right] of joinPairs) {
1007
- fieldCountQuery = fieldCountQuery.whereRef(left, "=", right);
1063
+ let fieldCountQuery;
1064
+ const m2m = getManyToManyRelation(this.schema, model, field);
1065
+ if (m2m) {
1066
+ 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}`));
1067
+ } else {
1068
+ fieldCountQuery = eb.selectFrom(fieldModel).select(eb.fn.countAll().as(`_count$${field}`));
1069
+ const joinPairs = buildJoinPairs(this.schema, model, parentAlias, field, fieldModel);
1070
+ for (const [left, right] of joinPairs) {
1071
+ fieldCountQuery = fieldCountQuery.whereRef(left, "=", right);
1072
+ }
1008
1073
  }
1009
1074
  if (value && typeof value === "object" && "where" in value && value.where && typeof value.where === "object") {
1010
1075
  const filter = this.buildFilter(eb, fieldModel, fieldModel, value.where);
@@ -1084,6 +1149,9 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
1084
1149
  static {
1085
1150
  __name(this, "PostgresCrudDialect");
1086
1151
  }
1152
+ constructor(schema, options) {
1153
+ super(schema, options);
1154
+ }
1087
1155
  get provider() {
1088
1156
  return "postgresql";
1089
1157
  }
@@ -1098,9 +1166,41 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
1098
1166
  return value.map((v) => this.transformPrimitive(v, type, false));
1099
1167
  }
1100
1168
  } else {
1101
- return (0, import_ts_pattern3.match)(type).with("DateTime", () => value instanceof Date ? value : typeof value === "string" ? new Date(value) : value).with("Decimal", () => value !== null ? value.toString() : value).otherwise(() => value);
1169
+ return (0, import_ts_pattern3.match)(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);
1170
+ }
1171
+ }
1172
+ transformOutput(value, type) {
1173
+ if (value === null || value === void 0) {
1174
+ return value;
1175
+ }
1176
+ return (0, import_ts_pattern3.match)(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));
1177
+ }
1178
+ transformOutputBigInt(value) {
1179
+ if (typeof value === "bigint") {
1180
+ return value;
1181
+ }
1182
+ (0, import_common_helpers3.invariant)(typeof value === "string" || typeof value === "number", `Expected string or number, got ${typeof value}`);
1183
+ return BigInt(value);
1184
+ }
1185
+ transformDecimal(value) {
1186
+ if (value instanceof import_decimal.default) {
1187
+ return value;
1188
+ }
1189
+ (0, import_common_helpers3.invariant)(typeof value === "string" || typeof value === "number" || value instanceof import_decimal.default, `Expected string, number or Decimal, got ${typeof value}`);
1190
+ return new import_decimal.default(value);
1191
+ }
1192
+ transformOutputDate(value) {
1193
+ if (typeof value === "string") {
1194
+ return new Date(value);
1195
+ } else if (value instanceof Date && this.options.fixPostgresTimezone !== false) {
1196
+ return new Date(value.getTime() - value.getTimezoneOffset() * 60 * 1e3);
1197
+ } else {
1198
+ return value;
1102
1199
  }
1103
1200
  }
1201
+ transformOutputBytes(value) {
1202
+ return Buffer.isBuffer(value) ? Uint8Array.from(value) : value;
1203
+ }
1104
1204
  buildRelationSelection(query, model, relationField, parentAlias, payload) {
1105
1205
  const relationResultName = `${parentAlias}$${relationField}`;
1106
1206
  const joinedQuery = this.buildRelationJSON(model, query, relationField, parentAlias, payload, relationResultName);
@@ -1131,14 +1231,14 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
1131
1231
  buildRelationJoinFilter(query, model, relationField, relationModel, relationModelAlias, parentAlias) {
1132
1232
  const m2m = getManyToManyRelation(this.schema, model, relationField);
1133
1233
  if (m2m) {
1134
- const parentIds = getIdFields(this.schema, model);
1135
- const relationIds = getIdFields(this.schema, relationModel);
1136
- (0, import_common_helpers2.invariant)(parentIds.length === 1, "many-to-many relation must have exactly one id field");
1137
- (0, import_common_helpers2.invariant)(relationIds.length === 1, "many-to-many relation must have exactly one id field");
1234
+ const parentIds = requireIdFields(this.schema, model);
1235
+ const relationIds = requireIdFields(this.schema, relationModel);
1236
+ (0, import_common_helpers3.invariant)(parentIds.length === 1, "many-to-many relation must have exactly one id field");
1237
+ (0, import_common_helpers3.invariant)(relationIds.length === 1, "many-to-many relation must have exactly one id field");
1138
1238
  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}`)));
1139
1239
  } else {
1140
1240
  const joinPairs = buildJoinPairs(this.schema, model, parentAlias, relationField, relationModelAlias);
1141
- query = query.where((eb) => this.and(eb, ...joinPairs.map(([left, right]) => eb(import_kysely2.sql.ref(left), "=", import_kysely2.sql.ref(right)))));
1241
+ query = query.where((eb) => this.and(eb, ...joinPairs.map(([left, right]) => eb(import_kysely3.sql.ref(left), "=", import_kysely3.sql.ref(right)))));
1142
1242
  }
1143
1243
  return query;
1144
1244
  }
@@ -1146,9 +1246,9 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
1146
1246
  qb = qb.select((eb) => {
1147
1247
  const objArgs = this.buildRelationObjectArgs(relationModel, relationModelAlias, eb, payload, parentResultName);
1148
1248
  if (relationFieldDef.array) {
1149
- return eb.fn.coalesce(import_kysely2.sql`jsonb_agg(jsonb_build_object(${import_kysely2.sql.join(objArgs)}))`, import_kysely2.sql`'[]'::jsonb`).as("$data");
1249
+ return eb.fn.coalesce(import_kysely3.sql`jsonb_agg(jsonb_build_object(${import_kysely3.sql.join(objArgs)}))`, import_kysely3.sql`'[]'::jsonb`).as("$data");
1150
1250
  } else {
1151
- return import_kysely2.sql`jsonb_build_object(${import_kysely2.sql.join(objArgs)})`.as("$data");
1251
+ return import_kysely3.sql`jsonb_build_object(${import_kysely3.sql.join(objArgs)})`.as("$data");
1152
1252
  }
1153
1253
  });
1154
1254
  return qb;
@@ -1159,13 +1259,13 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
1159
1259
  const descendantModels = getDelegateDescendantModels(this.schema, relationModel);
1160
1260
  if (descendantModels.length > 0) {
1161
1261
  objArgs.push(...descendantModels.map((subModel) => [
1162
- import_kysely2.sql.lit(`${DELEGATE_JOINED_FIELD_PREFIX}${subModel.name}`),
1262
+ import_kysely3.sql.lit(`${DELEGATE_JOINED_FIELD_PREFIX}${subModel.name}`),
1163
1263
  eb.ref(`${DELEGATE_JOINED_FIELD_PREFIX}${subModel.name}`)
1164
1264
  ]).flatMap((v) => v));
1165
1265
  }
1166
1266
  if (payload === true || !payload.select) {
1167
1267
  objArgs.push(...Object.entries(relationModelDef.fields).filter(([, value]) => !value.relation).filter(([name]) => !(typeof payload === "object" && payload.omit?.[name] === true)).map(([field]) => [
1168
- import_kysely2.sql.lit(field),
1268
+ import_kysely3.sql.lit(field),
1169
1269
  this.fieldRef(relationModel, field, eb, relationModelAlias, false)
1170
1270
  ]).flatMap((v) => v));
1171
1271
  } else if (payload.select) {
@@ -1173,14 +1273,14 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
1173
1273
  if (field === "_count") {
1174
1274
  const subJson = this.buildCountJson(relationModel, eb, relationModelAlias, value);
1175
1275
  return [
1176
- import_kysely2.sql.lit(field),
1276
+ import_kysely3.sql.lit(field),
1177
1277
  subJson
1178
1278
  ];
1179
1279
  } else {
1180
1280
  const fieldDef = requireField(this.schema, relationModel, field);
1181
1281
  const fieldValue = fieldDef.relation ? eb.ref(`${parentResultName}$${field}.$data`) : this.fieldRef(relationModel, field, eb, relationModelAlias, false);
1182
1282
  return [
1183
- import_kysely2.sql.lit(field),
1283
+ import_kysely3.sql.lit(field),
1184
1284
  fieldValue
1185
1285
  ];
1186
1286
  }
@@ -1188,7 +1288,7 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
1188
1288
  }
1189
1289
  if (typeof payload === "object" && payload.include && typeof payload.include === "object") {
1190
1290
  objArgs.push(...Object.entries(payload.include).filter(([, value]) => value).map(([field]) => [
1191
- import_kysely2.sql.lit(field),
1291
+ import_kysely3.sql.lit(field),
1192
1292
  // reference the synthesized JSON field
1193
1293
  eb.ref(`${parentResultName}$${field}.$data`)
1194
1294
  ]).flatMap((v) => v));
@@ -1218,7 +1318,7 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
1218
1318
  }
1219
1319
  buildJsonObject(eb, value) {
1220
1320
  return eb.fn("jsonb_build_object", Object.entries(value).flatMap(([key, value2]) => [
1221
- import_kysely2.sql.lit(key),
1321
+ import_kysely3.sql.lit(key),
1222
1322
  value2
1223
1323
  ]));
1224
1324
  }
@@ -1246,11 +1346,33 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
1246
1346
  get supportInsertWithDefault() {
1247
1347
  return true;
1248
1348
  }
1349
+ getFieldSqlType(fieldDef) {
1350
+ if (fieldDef.relation) {
1351
+ throw new QueryError("Cannot get SQL type of a relation field");
1352
+ }
1353
+ let result;
1354
+ if (this.schema.enums?.[fieldDef.type]) {
1355
+ result = "text";
1356
+ } else {
1357
+ result = (0, import_ts_pattern3.match)(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");
1358
+ }
1359
+ if (fieldDef.array) {
1360
+ result += "[]";
1361
+ }
1362
+ return result;
1363
+ }
1364
+ getStringCasingBehavior() {
1365
+ return {
1366
+ supportsILike: true,
1367
+ likeCaseSensitive: true
1368
+ };
1369
+ }
1249
1370
  };
1250
1371
 
1251
1372
  // src/client/crud/dialects/sqlite.ts
1252
- var import_common_helpers3 = require("@zenstackhq/common-helpers");
1253
- var import_kysely3 = require("kysely");
1373
+ var import_common_helpers4 = require("@zenstackhq/common-helpers");
1374
+ var import_decimal2 = __toESM(require("decimal.js"), 1);
1375
+ var import_kysely4 = require("kysely");
1254
1376
  var import_ts_pattern4 = require("ts-pattern");
1255
1377
  var SqliteCrudDialect = class extends BaseCrudDialect {
1256
1378
  static {
@@ -1269,9 +1391,57 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
1269
1391
  if (this.schema.typeDefs && type in this.schema.typeDefs) {
1270
1392
  return JSON.stringify(value);
1271
1393
  } else {
1272
- return (0, import_ts_pattern4.match)(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);
1394
+ return (0, import_ts_pattern4.match)(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);
1395
+ }
1396
+ }
1397
+ }
1398
+ transformOutput(value, type) {
1399
+ if (value === null || value === void 0) {
1400
+ return value;
1401
+ } else if (this.schema.typeDefs && type in this.schema.typeDefs) {
1402
+ return this.transformOutputJson(value);
1403
+ } else {
1404
+ return (0, import_ts_pattern4.match)(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));
1405
+ }
1406
+ }
1407
+ transformOutputDecimal(value) {
1408
+ if (value instanceof import_decimal2.default) {
1409
+ return value;
1410
+ }
1411
+ (0, import_common_helpers4.invariant)(typeof value === "string" || typeof value === "number" || value instanceof import_decimal2.default, `Expected string, number or Decimal, got ${typeof value}`);
1412
+ return new import_decimal2.default(value);
1413
+ }
1414
+ transformOutputBigInt(value) {
1415
+ if (typeof value === "bigint") {
1416
+ return value;
1417
+ }
1418
+ (0, import_common_helpers4.invariant)(typeof value === "string" || typeof value === "number", `Expected string or number, got ${typeof value}`);
1419
+ return BigInt(value);
1420
+ }
1421
+ transformOutputBoolean(value) {
1422
+ return !!value;
1423
+ }
1424
+ transformOutputDate(value) {
1425
+ if (typeof value === "number") {
1426
+ return new Date(value);
1427
+ } else if (typeof value === "string") {
1428
+ return new Date(value);
1429
+ } else {
1430
+ return value;
1431
+ }
1432
+ }
1433
+ transformOutputBytes(value) {
1434
+ return Buffer.isBuffer(value) ? Uint8Array.from(value) : value;
1435
+ }
1436
+ transformOutputJson(value) {
1437
+ if (typeof value === "string") {
1438
+ try {
1439
+ return JSON.parse(value);
1440
+ } catch (e) {
1441
+ throw new QueryError("Invalid JSON returned", e);
1273
1442
  }
1274
1443
  }
1444
+ return value;
1275
1445
  }
1276
1446
  buildRelationSelection(query, model, relationField, parentAlias, payload) {
1277
1447
  return query.select((eb) => this.buildRelationJSON(model, eb, relationField, parentAlias, payload).as(relationField));
@@ -1298,13 +1468,13 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
1298
1468
  const descendantModels = getDelegateDescendantModels(this.schema, relationModel);
1299
1469
  if (descendantModels.length > 0) {
1300
1470
  objArgs.push(...descendantModels.map((subModel) => [
1301
- import_kysely3.sql.lit(`${DELEGATE_JOINED_FIELD_PREFIX}${subModel.name}`),
1471
+ import_kysely4.sql.lit(`${DELEGATE_JOINED_FIELD_PREFIX}${subModel.name}`),
1302
1472
  eb.ref(`${DELEGATE_JOINED_FIELD_PREFIX}${subModel.name}`)
1303
1473
  ]).flatMap((v) => v));
1304
1474
  }
1305
1475
  if (payload === true || !payload.select) {
1306
1476
  objArgs.push(...Object.entries(relationModelDef.fields).filter(([, value]) => !value.relation).filter(([name]) => !(typeof payload === "object" && payload.omit?.[name] === true)).map(([field]) => [
1307
- import_kysely3.sql.lit(field),
1477
+ import_kysely4.sql.lit(field),
1308
1478
  this.fieldRef(relationModel, field, eb, subQueryName, false)
1309
1479
  ]).flatMap((v) => v));
1310
1480
  } else if (payload.select) {
@@ -1312,7 +1482,7 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
1312
1482
  if (field === "_count") {
1313
1483
  const subJson = this.buildCountJson(relationModel, eb, `${parentAlias}$${relationField}`, value);
1314
1484
  return [
1315
- import_kysely3.sql.lit(field),
1485
+ import_kysely4.sql.lit(field),
1316
1486
  subJson
1317
1487
  ];
1318
1488
  } else {
@@ -1320,12 +1490,12 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
1320
1490
  if (fieldDef.relation) {
1321
1491
  const subJson = this.buildRelationJSON(relationModel, eb, field, `${parentAlias}$${relationField}`, value);
1322
1492
  return [
1323
- import_kysely3.sql.lit(field),
1493
+ import_kysely4.sql.lit(field),
1324
1494
  subJson
1325
1495
  ];
1326
1496
  } else {
1327
1497
  return [
1328
- import_kysely3.sql.lit(field),
1498
+ import_kysely4.sql.lit(field),
1329
1499
  this.fieldRef(relationModel, field, eb, subQueryName, false)
1330
1500
  ];
1331
1501
  }
@@ -1336,15 +1506,15 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
1336
1506
  objArgs.push(...Object.entries(payload.include).filter(([, value]) => value).map(([field, value]) => {
1337
1507
  const subJson = this.buildRelationJSON(relationModel, eb, field, `${parentAlias}$${relationField}`, value);
1338
1508
  return [
1339
- import_kysely3.sql.lit(field),
1509
+ import_kysely4.sql.lit(field),
1340
1510
  subJson
1341
1511
  ];
1342
1512
  }).flatMap((v) => v));
1343
1513
  }
1344
1514
  if (relationFieldDef.array) {
1345
- return eb.fn.coalesce(import_kysely3.sql`json_group_array(json_object(${import_kysely3.sql.join(objArgs)}))`, import_kysely3.sql`json_array()`).as("$data");
1515
+ return eb.fn.coalesce(import_kysely4.sql`json_group_array(json_object(${import_kysely4.sql.join(objArgs)}))`, import_kysely4.sql`json_array()`).as("$data");
1346
1516
  } else {
1347
- return import_kysely3.sql`json_object(${import_kysely3.sql.join(objArgs)})`.as("$data");
1517
+ return import_kysely4.sql`json_object(${import_kysely4.sql.join(objArgs)})`.as("$data");
1348
1518
  }
1349
1519
  });
1350
1520
  return tbl;
@@ -1354,10 +1524,10 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
1354
1524
  const relationModel = fieldDef.type;
1355
1525
  const m2m = getManyToManyRelation(this.schema, model, relationField);
1356
1526
  if (m2m) {
1357
- const parentIds = getIdFields(this.schema, model);
1358
- const relationIds = getIdFields(this.schema, relationModel);
1359
- (0, import_common_helpers3.invariant)(parentIds.length === 1, "many-to-many relation must have exactly one id field");
1360
- (0, import_common_helpers3.invariant)(relationIds.length === 1, "many-to-many relation must have exactly one id field");
1527
+ const parentIds = requireIdFields(this.schema, model);
1528
+ const relationIds = requireIdFields(this.schema, relationModel);
1529
+ (0, import_common_helpers4.invariant)(parentIds.length === 1, "many-to-many relation must have exactly one id field");
1530
+ (0, import_common_helpers4.invariant)(relationIds.length === 1, "many-to-many relation must have exactly one id field");
1361
1531
  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}`)));
1362
1532
  } else {
1363
1533
  const { keyPairs, ownedByModel } = getRelationForeignKeyFieldPairs(this.schema, model, relationField);
@@ -1385,7 +1555,7 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
1385
1555
  }
1386
1556
  buildJsonObject(eb, value) {
1387
1557
  return eb.fn("json_object", Object.entries(value).flatMap(([key, value2]) => [
1388
- import_kysely3.sql.lit(key),
1558
+ import_kysely4.sql.lit(key),
1389
1559
  value2
1390
1560
  ]));
1391
1561
  }
@@ -1409,6 +1579,24 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
1409
1579
  get supportInsertWithDefault() {
1410
1580
  return false;
1411
1581
  }
1582
+ getFieldSqlType(fieldDef) {
1583
+ if (fieldDef.relation) {
1584
+ throw new QueryError("Cannot get SQL type of a relation field");
1585
+ }
1586
+ if (fieldDef.array) {
1587
+ throw new QueryError("SQLite does not support scalar list type");
1588
+ }
1589
+ if (this.schema.enums?.[fieldDef.type]) {
1590
+ return "text";
1591
+ }
1592
+ return (0, import_ts_pattern4.match)(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");
1593
+ }
1594
+ getStringCasingBehavior() {
1595
+ return {
1596
+ supportsILike: false,
1597
+ likeCaseSensitive: false
1598
+ };
1599
+ }
1412
1600
  };
1413
1601
 
1414
1602
  // src/client/crud/dialects/index.ts
@@ -1418,8 +1606,8 @@ function getCrudDialect(schema, options) {
1418
1606
  __name(getCrudDialect, "getCrudDialect");
1419
1607
 
1420
1608
  // src/utils/default-operation-node-visitor.ts
1421
- var import_kysely4 = require("kysely");
1422
- var DefaultOperationNodeVisitor = class extends import_kysely4.OperationNodeVisitor {
1609
+ var import_kysely5 = require("kysely");
1610
+ var DefaultOperationNodeVisitor = class extends import_kysely5.OperationNodeVisitor {
1423
1611
  static {
1424
1612
  __name(this, "DefaultOperationNodeVisitor");
1425
1613
  }
@@ -1736,12 +1924,12 @@ var ColumnCollector = class extends DefaultOperationNodeVisitor {
1736
1924
  };
1737
1925
 
1738
1926
  // src/plugins/policy/expression-transformer.ts
1739
- var import_common_helpers5 = require("@zenstackhq/common-helpers");
1740
- var import_kysely6 = require("kysely");
1927
+ var import_common_helpers6 = require("@zenstackhq/common-helpers");
1928
+ var import_kysely7 = require("kysely");
1741
1929
  var import_ts_pattern7 = require("ts-pattern");
1742
1930
 
1743
1931
  // src/plugins/policy/expression-evaluator.ts
1744
- var import_common_helpers4 = require("@zenstackhq/common-helpers");
1932
+ var import_common_helpers5 = require("@zenstackhq/common-helpers");
1745
1933
  var import_ts_pattern6 = require("ts-pattern");
1746
1934
  var ExpressionEvaluator = class {
1747
1935
  static {
@@ -1785,18 +1973,18 @@ var ExpressionEvaluator = class {
1785
1973
  const right = this.evaluate(expr2.right, context);
1786
1974
  return (0, import_ts_pattern6.match)(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", () => {
1787
1975
  const _right = right ?? [];
1788
- (0, import_common_helpers4.invariant)(Array.isArray(_right), 'expected array for "in" operator');
1976
+ (0, import_common_helpers5.invariant)(Array.isArray(_right), 'expected array for "in" operator');
1789
1977
  return _right.includes(left);
1790
1978
  }).exhaustive();
1791
1979
  }
1792
1980
  evaluateCollectionPredicate(expr2, context) {
1793
1981
  const op = expr2.op;
1794
- (0, import_common_helpers4.invariant)(op === "?" || op === "!" || op === "^", 'expected "?" or "!" or "^" operator');
1982
+ (0, import_common_helpers5.invariant)(op === "?" || op === "!" || op === "^", 'expected "?" or "!" or "^" operator');
1795
1983
  const left = this.evaluate(expr2.left, context);
1796
1984
  if (!left) {
1797
1985
  return false;
1798
1986
  }
1799
- (0, import_common_helpers4.invariant)(Array.isArray(left), "expected array");
1987
+ (0, import_common_helpers5.invariant)(Array.isArray(left), "expected array");
1800
1988
  return (0, import_ts_pattern6.match)(op).with("?", () => left.some((item) => this.evaluate(expr2.right, {
1801
1989
  ...context,
1802
1990
  thisValue: item
@@ -1811,21 +1999,21 @@ var ExpressionEvaluator = class {
1811
1999
  };
1812
2000
 
1813
2001
  // src/plugins/policy/utils.ts
1814
- var import_kysely5 = require("kysely");
2002
+ var import_kysely6 = require("kysely");
1815
2003
  function trueNode(dialect) {
1816
- return import_kysely5.ValueNode.createImmediate(dialect.transformPrimitive(true, "Boolean", false));
2004
+ return import_kysely6.ValueNode.createImmediate(dialect.transformPrimitive(true, "Boolean", false));
1817
2005
  }
1818
2006
  __name(trueNode, "trueNode");
1819
2007
  function falseNode(dialect) {
1820
- return import_kysely5.ValueNode.createImmediate(dialect.transformPrimitive(false, "Boolean", false));
2008
+ return import_kysely6.ValueNode.createImmediate(dialect.transformPrimitive(false, "Boolean", false));
1821
2009
  }
1822
2010
  __name(falseNode, "falseNode");
1823
2011
  function isTrueNode(node) {
1824
- return import_kysely5.ValueNode.is(node) && (node.value === true || node.value === 1);
2012
+ return import_kysely6.ValueNode.is(node) && (node.value === true || node.value === 1);
1825
2013
  }
1826
2014
  __name(isTrueNode, "isTrueNode");
1827
2015
  function isFalseNode(node) {
1828
- return import_kysely5.ValueNode.is(node) && (node.value === false || node.value === 0);
2016
+ return import_kysely6.ValueNode.is(node) && (node.value === false || node.value === 0);
1829
2017
  }
1830
2018
  __name(isFalseNode, "isFalseNode");
1831
2019
  function conjunction(dialect, nodes) {
@@ -1842,7 +2030,7 @@ function conjunction(dialect, nodes) {
1842
2030
  if (items.length === 0) {
1843
2031
  return trueNode(dialect);
1844
2032
  }
1845
- return items.reduce((acc, node) => import_kysely5.AndNode.create(wrapParensIf(acc, import_kysely5.OrNode.is), wrapParensIf(node, import_kysely5.OrNode.is)));
2033
+ return items.reduce((acc, node) => import_kysely6.AndNode.create(wrapParensIf(acc, import_kysely6.OrNode.is), wrapParensIf(node, import_kysely6.OrNode.is)));
1846
2034
  }
1847
2035
  __name(conjunction, "conjunction");
1848
2036
  function disjunction(dialect, nodes) {
@@ -1859,7 +2047,7 @@ function disjunction(dialect, nodes) {
1859
2047
  if (items.length === 0) {
1860
2048
  return falseNode(dialect);
1861
2049
  }
1862
- return items.reduce((acc, node) => import_kysely5.OrNode.create(wrapParensIf(acc, import_kysely5.AndNode.is), wrapParensIf(node, import_kysely5.AndNode.is)));
2050
+ return items.reduce((acc, node) => import_kysely6.OrNode.create(wrapParensIf(acc, import_kysely6.AndNode.is), wrapParensIf(node, import_kysely6.AndNode.is)));
1863
2051
  }
1864
2052
  __name(disjunction, "disjunction");
1865
2053
  function logicalNot(dialect, node) {
@@ -1869,11 +2057,11 @@ function logicalNot(dialect, node) {
1869
2057
  if (isFalseNode(node)) {
1870
2058
  return trueNode(dialect);
1871
2059
  }
1872
- return import_kysely5.UnaryOperationNode.create(import_kysely5.OperatorNode.create("not"), wrapParensIf(node, (n) => import_kysely5.AndNode.is(n) || import_kysely5.OrNode.is(n)));
2060
+ return import_kysely6.UnaryOperationNode.create(import_kysely6.OperatorNode.create("not"), wrapParensIf(node, (n) => import_kysely6.AndNode.is(n) || import_kysely6.OrNode.is(n)));
1873
2061
  }
1874
2062
  __name(logicalNot, "logicalNot");
1875
2063
  function wrapParensIf(node, predicate) {
1876
- return predicate(node) ? import_kysely5.ParensNode.create(node) : node;
2064
+ return predicate(node) ? import_kysely6.ParensNode.create(node) : node;
1877
2065
  }
1878
2066
  __name(wrapParensIf, "wrapParensIf");
1879
2067
  function buildIsFalse(node, dialect) {
@@ -1882,13 +2070,13 @@ function buildIsFalse(node, dialect) {
1882
2070
  } else if (isTrueNode(node)) {
1883
2071
  return falseNode(dialect);
1884
2072
  }
1885
- return import_kysely5.BinaryOperationNode.create(
2073
+ return import_kysely6.BinaryOperationNode.create(
1886
2074
  // coalesce so null is treated as false
1887
- import_kysely5.FunctionNode.create("coalesce", [
2075
+ import_kysely6.FunctionNode.create("coalesce", [
1888
2076
  node,
1889
2077
  falseNode(dialect)
1890
2078
  ]),
1891
- import_kysely5.OperatorNode.create("="),
2079
+ import_kysely6.OperatorNode.create("="),
1892
2080
  falseNode(dialect)
1893
2081
  );
1894
2082
  }
@@ -1897,11 +2085,11 @@ function getTableName(node) {
1897
2085
  if (!node) {
1898
2086
  return node;
1899
2087
  }
1900
- if (import_kysely5.TableNode.is(node)) {
2088
+ if (import_kysely6.TableNode.is(node)) {
1901
2089
  return node.table.identifier.name;
1902
- } else if (import_kysely5.AliasNode.is(node)) {
2090
+ } else if (import_kysely6.AliasNode.is(node)) {
1903
2091
  return getTableName(node.node);
1904
- } else if (import_kysely5.ReferenceNode.is(node) && node.table) {
2092
+ } else if (import_kysely6.ReferenceNode.is(node) && node.table) {
1905
2093
  return getTableName(node.table);
1906
2094
  }
1907
2095
  return void 0;
@@ -1934,16 +2122,21 @@ var ExpressionTransformer = class {
1934
2122
  static {
1935
2123
  __name(this, "ExpressionTransformer");
1936
2124
  }
1937
- schema;
1938
- clientOptions;
1939
- auth;
2125
+ client;
1940
2126
  dialect;
1941
- constructor(schema, clientOptions, auth) {
1942
- this.schema = schema;
1943
- this.clientOptions = clientOptions;
1944
- this.auth = auth;
2127
+ constructor(client) {
2128
+ this.client = client;
1945
2129
  this.dialect = getCrudDialect(this.schema, this.clientOptions);
1946
2130
  }
2131
+ get schema() {
2132
+ return this.client.$schema;
2133
+ }
2134
+ get clientOptions() {
2135
+ return this.client.$options;
2136
+ }
2137
+ get auth() {
2138
+ return this.client.$auth;
2139
+ }
1947
2140
  get authType() {
1948
2141
  if (!this.schema.authType) {
1949
2142
  throw new InternalError('Schema does not have an "authType" specified');
@@ -1961,7 +2154,7 @@ var ExpressionTransformer = class {
1961
2154
  return this.transformValue(expr2.value, typeof expr2.value === "string" ? "String" : typeof expr2.value === "boolean" ? "Boolean" : "Int");
1962
2155
  }
1963
2156
  _array(expr2, context) {
1964
- return import_kysely6.ValueListNode.create(expr2.items.map((item) => this.transform(item, context)));
2157
+ return import_kysely7.ValueListNode.create(expr2.items.map((item) => this.transform(item, context)));
1965
2158
  }
1966
2159
  _field(expr2, context) {
1967
2160
  const fieldDef = requireField(this.schema, context.model, expr2.field);
@@ -1981,18 +2174,18 @@ var ExpressionTransformer = class {
1981
2174
  }
1982
2175
  mergeWhere(where, memberFilter) {
1983
2176
  if (!where) {
1984
- return import_kysely6.WhereNode.create(memberFilter ?? trueNode(this.dialect));
2177
+ return import_kysely7.WhereNode.create(memberFilter ?? trueNode(this.dialect));
1985
2178
  }
1986
2179
  if (!memberFilter) {
1987
2180
  return where;
1988
2181
  }
1989
- return import_kysely6.WhereNode.create(conjunction(this.dialect, [
2182
+ return import_kysely7.WhereNode.create(conjunction(this.dialect, [
1990
2183
  where.where,
1991
2184
  memberFilter
1992
2185
  ]));
1993
2186
  }
1994
2187
  _null() {
1995
- return import_kysely6.ValueNode.createImmediate(null);
2188
+ return import_kysely7.ValueNode.createImmediate(null);
1996
2189
  }
1997
2190
  _binary(expr2, context) {
1998
2191
  if (expr2.op === "&&") {
@@ -2013,45 +2206,82 @@ var ExpressionTransformer = class {
2013
2206
  if (op === "?" || op === "!" || op === "^") {
2014
2207
  return this.transformCollectionPredicate(expr2, context);
2015
2208
  }
2016
- const left = this.transform(expr2.left, context);
2017
- const right = this.transform(expr2.right, context);
2209
+ const { normalizedLeft, normalizedRight } = this.normalizeBinaryOperationOperands(expr2, context);
2210
+ const left = this.transform(normalizedLeft, context);
2211
+ const right = this.transform(normalizedRight, context);
2018
2212
  if (op === "in") {
2019
2213
  if (this.isNullNode(left)) {
2020
2214
  return this.transformValue(false, "Boolean");
2021
2215
  } else {
2022
- if (import_kysely6.ValueListNode.is(right)) {
2023
- return import_kysely6.BinaryOperationNode.create(left, import_kysely6.OperatorNode.create("in"), right);
2216
+ if (import_kysely7.ValueListNode.is(right)) {
2217
+ return import_kysely7.BinaryOperationNode.create(left, import_kysely7.OperatorNode.create("in"), right);
2024
2218
  } else {
2025
- return import_kysely6.BinaryOperationNode.create(left, import_kysely6.OperatorNode.create("="), import_kysely6.FunctionNode.create("any", [
2219
+ return import_kysely7.BinaryOperationNode.create(left, import_kysely7.OperatorNode.create("="), import_kysely7.FunctionNode.create("any", [
2026
2220
  right
2027
2221
  ]));
2028
2222
  }
2029
2223
  }
2030
2224
  }
2031
2225
  if (this.isNullNode(right)) {
2032
- return expr2.op === "==" ? import_kysely6.BinaryOperationNode.create(left, import_kysely6.OperatorNode.create("is"), right) : import_kysely6.BinaryOperationNode.create(left, import_kysely6.OperatorNode.create("is not"), right);
2226
+ return this.transformNullCheck(left, expr2.op);
2033
2227
  } else if (this.isNullNode(left)) {
2034
- return expr2.op === "==" ? import_kysely6.BinaryOperationNode.create(right, import_kysely6.OperatorNode.create("is"), import_kysely6.ValueNode.createImmediate(null)) : import_kysely6.BinaryOperationNode.create(right, import_kysely6.OperatorNode.create("is not"), import_kysely6.ValueNode.createImmediate(null));
2228
+ return this.transformNullCheck(right, expr2.op);
2229
+ } else {
2230
+ return import_kysely7.BinaryOperationNode.create(left, this.transformOperator(op), right);
2035
2231
  }
2036
- return import_kysely6.BinaryOperationNode.create(left, this.transformOperator(op), right);
2232
+ }
2233
+ transformNullCheck(expr2, operator) {
2234
+ (0, import_common_helpers6.invariant)(operator === "==" || operator === "!=", 'operator must be "==" or "!=" for null comparison');
2235
+ if (import_kysely7.ValueNode.is(expr2)) {
2236
+ if (expr2.value === null) {
2237
+ return operator === "==" ? trueNode(this.dialect) : falseNode(this.dialect);
2238
+ } else {
2239
+ return operator === "==" ? falseNode(this.dialect) : trueNode(this.dialect);
2240
+ }
2241
+ } else {
2242
+ return operator === "==" ? import_kysely7.BinaryOperationNode.create(expr2, import_kysely7.OperatorNode.create("is"), import_kysely7.ValueNode.createImmediate(null)) : import_kysely7.BinaryOperationNode.create(expr2, import_kysely7.OperatorNode.create("is not"), import_kysely7.ValueNode.createImmediate(null));
2243
+ }
2244
+ }
2245
+ normalizeBinaryOperationOperands(expr2, context) {
2246
+ let normalizedLeft = expr2.left;
2247
+ if (this.isRelationField(expr2.left, context.model)) {
2248
+ (0, import_common_helpers6.invariant)(ExpressionUtils.isNull(expr2.right), "only null comparison is supported for relation field");
2249
+ const leftRelDef = this.getFieldDefFromFieldRef(expr2.left, context.model);
2250
+ (0, import_common_helpers6.invariant)(leftRelDef, "failed to get relation field definition");
2251
+ const idFields = requireIdFields(this.schema, leftRelDef.type);
2252
+ normalizedLeft = this.makeOrAppendMember(normalizedLeft, idFields[0]);
2253
+ }
2254
+ let normalizedRight = expr2.right;
2255
+ if (this.isRelationField(expr2.right, context.model)) {
2256
+ (0, import_common_helpers6.invariant)(ExpressionUtils.isNull(expr2.left), "only null comparison is supported for relation field");
2257
+ const rightRelDef = this.getFieldDefFromFieldRef(expr2.right, context.model);
2258
+ (0, import_common_helpers6.invariant)(rightRelDef, "failed to get relation field definition");
2259
+ const idFields = requireIdFields(this.schema, rightRelDef.type);
2260
+ normalizedRight = this.makeOrAppendMember(normalizedRight, idFields[0]);
2261
+ }
2262
+ return {
2263
+ normalizedLeft,
2264
+ normalizedRight
2265
+ };
2037
2266
  }
2038
2267
  transformCollectionPredicate(expr2, context) {
2039
- (0, import_common_helpers5.invariant)(expr2.op === "?" || expr2.op === "!" || expr2.op === "^", 'expected "?" or "!" or "^" operator');
2268
+ (0, import_common_helpers6.invariant)(expr2.op === "?" || expr2.op === "!" || expr2.op === "^", 'expected "?" or "!" or "^" operator');
2040
2269
  if (this.isAuthCall(expr2.left) || this.isAuthMember(expr2.left)) {
2041
2270
  const value = new ExpressionEvaluator().evaluate(expr2, {
2042
2271
  auth: this.auth
2043
2272
  });
2044
2273
  return this.transformValue(value, "Boolean");
2045
2274
  }
2046
- (0, import_common_helpers5.invariant)(ExpressionUtils.isField(expr2.left) || ExpressionUtils.isMember(expr2.left), "left operand must be field or member access");
2275
+ (0, import_common_helpers6.invariant)(ExpressionUtils.isField(expr2.left) || ExpressionUtils.isMember(expr2.left), "left operand must be field or member access");
2047
2276
  let newContextModel;
2048
- if (ExpressionUtils.isField(expr2.left)) {
2049
- const fieldDef = requireField(this.schema, context.model, expr2.left.field);
2277
+ const fieldDef = this.getFieldDefFromFieldRef(expr2.left, context.model);
2278
+ if (fieldDef) {
2279
+ (0, import_common_helpers6.invariant)(fieldDef.relation, `field is not a relation: ${JSON.stringify(expr2.left)}`);
2050
2280
  newContextModel = fieldDef.type;
2051
2281
  } else {
2052
- (0, import_common_helpers5.invariant)(ExpressionUtils.isField(expr2.left.receiver));
2053
- const fieldDef = requireField(this.schema, context.model, expr2.left.receiver.field);
2054
- newContextModel = fieldDef.type;
2282
+ (0, import_common_helpers6.invariant)(ExpressionUtils.isMember(expr2.left) && ExpressionUtils.isField(expr2.left.receiver), "left operand must be member access with field receiver");
2283
+ const fieldDef2 = requireField(this.schema, context.model, expr2.left.receiver.field);
2284
+ newContextModel = fieldDef2.type;
2055
2285
  for (const member of expr2.left.members) {
2056
2286
  const memberDef = requireField(this.schema, newContextModel, member);
2057
2287
  newContextModel = memberDef.type;
@@ -2065,13 +2295,13 @@ var ExpressionTransformer = class {
2065
2295
  if (expr2.op === "!") {
2066
2296
  predicateFilter = logicalNot(this.dialect, predicateFilter);
2067
2297
  }
2068
- const count = import_kysely6.FunctionNode.create("count", [
2069
- import_kysely6.ValueNode.createImmediate(1)
2298
+ const count = import_kysely7.FunctionNode.create("count", [
2299
+ import_kysely7.ValueNode.createImmediate(1)
2070
2300
  ]);
2071
- const predicateResult = (0, import_ts_pattern7.match)(expr2.op).with("?", () => import_kysely6.BinaryOperationNode.create(count, import_kysely6.OperatorNode.create(">"), import_kysely6.ValueNode.createImmediate(0))).with("!", () => import_kysely6.BinaryOperationNode.create(count, import_kysely6.OperatorNode.create("="), import_kysely6.ValueNode.createImmediate(0))).with("^", () => import_kysely6.BinaryOperationNode.create(count, import_kysely6.OperatorNode.create("="), import_kysely6.ValueNode.createImmediate(0))).exhaustive();
2301
+ const predicateResult = (0, import_ts_pattern7.match)(expr2.op).with("?", () => import_kysely7.BinaryOperationNode.create(count, import_kysely7.OperatorNode.create(">"), import_kysely7.ValueNode.createImmediate(0))).with("!", () => import_kysely7.BinaryOperationNode.create(count, import_kysely7.OperatorNode.create("="), import_kysely7.ValueNode.createImmediate(0))).with("^", () => import_kysely7.BinaryOperationNode.create(count, import_kysely7.OperatorNode.create("="), import_kysely7.ValueNode.createImmediate(0))).exhaustive();
2072
2302
  return this.transform(expr2.left, {
2073
2303
  ...context,
2074
- memberSelect: import_kysely6.SelectionNode.create(import_kysely6.AliasNode.create(predicateResult, import_kysely6.IdentifierNode.create("$t"))),
2304
+ memberSelect: import_kysely7.SelectionNode.create(import_kysely7.AliasNode.create(predicateResult, import_kysely7.IdentifierNode.create("$t"))),
2075
2305
  memberFilter: predicateFilter
2076
2306
  });
2077
2307
  }
@@ -2096,12 +2326,10 @@ var ExpressionTransformer = class {
2096
2326
  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`);
2097
2327
  }
2098
2328
  const idFields = Object.values(authModel.fields).filter((f) => f.id).map((f) => f.name);
2099
- (0, import_common_helpers5.invariant)(idFields.length > 0, "auth type model must have at least one id field");
2329
+ (0, import_common_helpers6.invariant)(idFields.length > 0, "auth type model must have at least one id field");
2100
2330
  const conditions = idFields.map((fieldName) => ExpressionUtils.binary(ExpressionUtils.member(authExpr, [
2101
2331
  fieldName
2102
- ]), "==", ExpressionUtils.member(other, [
2103
- fieldName
2104
- ])));
2332
+ ]), "==", this.makeOrAppendMember(other, fieldName)));
2105
2333
  let result = this.buildAnd(conditions);
2106
2334
  if (expr2.op === "!=") {
2107
2335
  result = this.buildLogicalNot(result);
@@ -2109,33 +2337,65 @@ var ExpressionTransformer = class {
2109
2337
  return this.transform(result, context);
2110
2338
  }
2111
2339
  }
2340
+ makeOrAppendMember(other, fieldName) {
2341
+ if (ExpressionUtils.isMember(other)) {
2342
+ return ExpressionUtils.member(other.receiver, [
2343
+ ...other.members,
2344
+ fieldName
2345
+ ]);
2346
+ } else {
2347
+ return ExpressionUtils.member(other, [
2348
+ fieldName
2349
+ ]);
2350
+ }
2351
+ }
2112
2352
  transformValue(value, type) {
2113
- return import_kysely6.ValueNode.create(this.dialect.transformPrimitive(value, type, false) ?? null);
2353
+ if (value === true) {
2354
+ return trueNode(this.dialect);
2355
+ } else if (value === false) {
2356
+ return falseNode(this.dialect);
2357
+ } else {
2358
+ return import_kysely7.ValueNode.create(this.dialect.transformPrimitive(value, type, false) ?? null);
2359
+ }
2114
2360
  }
2115
2361
  _unary(expr2, context) {
2116
- (0, import_common_helpers5.invariant)(expr2.op === "!", 'only "!" operator is supported');
2362
+ (0, import_common_helpers6.invariant)(expr2.op === "!", 'only "!" operator is supported');
2117
2363
  return logicalNot(this.dialect, this.transform(expr2.operand, context));
2118
2364
  }
2119
2365
  transformOperator(op) {
2120
2366
  const mappedOp = (0, import_ts_pattern7.match)(op).with("==", () => "=").otherwise(() => op);
2121
- return import_kysely6.OperatorNode.create(mappedOp);
2367
+ return import_kysely7.OperatorNode.create(mappedOp);
2122
2368
  }
2123
2369
  _call(expr2, context) {
2124
2370
  const result = this.transformCall(expr2, context);
2125
2371
  return result.toOperationNode();
2126
2372
  }
2127
2373
  transformCall(expr2, context) {
2128
- const func = this.clientOptions.functions?.[expr2.function];
2374
+ const func = this.getFunctionImpl(expr2.function);
2129
2375
  if (!func) {
2130
2376
  throw new QueryError(`Function not implemented: ${expr2.function}`);
2131
2377
  }
2132
- const eb = (0, import_kysely6.expressionBuilder)();
2378
+ const eb = (0, import_kysely7.expressionBuilder)();
2133
2379
  return func(eb, (expr2.args ?? []).map((arg) => this.transformCallArg(eb, arg, context)), {
2380
+ client: this.client,
2134
2381
  dialect: this.dialect,
2135
2382
  model: context.model,
2383
+ modelAlias: context.alias ?? context.model,
2136
2384
  operation: context.operation
2137
2385
  });
2138
2386
  }
2387
+ getFunctionImpl(functionName) {
2388
+ let func = this.clientOptions.functions?.[functionName];
2389
+ if (!func) {
2390
+ for (const plugin of this.clientOptions.plugins ?? []) {
2391
+ if (plugin.functions?.[functionName]) {
2392
+ func = plugin.functions[functionName];
2393
+ break;
2394
+ }
2395
+ }
2396
+ }
2397
+ return func;
2398
+ }
2139
2399
  transformCallArg(eb, arg, context) {
2140
2400
  if (ExpressionUtils.isLiteral(arg)) {
2141
2401
  return eb.val(arg.value);
@@ -2156,23 +2416,22 @@ var ExpressionTransformer = class {
2156
2416
  if (this.isAuthCall(expr2.receiver)) {
2157
2417
  return this.valueMemberAccess(this.auth, expr2, this.authType);
2158
2418
  }
2159
- (0, import_common_helpers5.invariant)(ExpressionUtils.isField(expr2.receiver) || ExpressionUtils.isThis(expr2.receiver), 'expect receiver to be field expression or "this"');
2419
+ (0, import_common_helpers6.invariant)(ExpressionUtils.isField(expr2.receiver) || ExpressionUtils.isThis(expr2.receiver), 'expect receiver to be field expression or "this"');
2160
2420
  let members = expr2.members;
2161
2421
  let receiver;
2162
2422
  const { memberFilter, memberSelect, ...restContext } = context;
2163
2423
  if (ExpressionUtils.isThis(expr2.receiver)) {
2164
2424
  if (expr2.members.length === 1) {
2165
- const fieldDef = requireField(this.schema, context.model, expr2.members[0]);
2166
- (0, import_common_helpers5.invariant)(!fieldDef.relation, "this.relation access should have been transformed into relation access");
2167
- return this.createColumnRef(expr2.members[0], restContext);
2425
+ return this._field(ExpressionUtils.field(expr2.members[0]), context);
2426
+ } else {
2427
+ const firstMemberFieldDef = requireField(this.schema, context.model, expr2.members[0]);
2428
+ receiver = this.transformRelationAccess(expr2.members[0], firstMemberFieldDef.type, restContext);
2429
+ members = expr2.members.slice(1);
2168
2430
  }
2169
- const firstMemberFieldDef = requireField(this.schema, context.model, expr2.members[0]);
2170
- receiver = this.transformRelationAccess(expr2.members[0], firstMemberFieldDef.type, restContext);
2171
- members = expr2.members.slice(1);
2172
2431
  } else {
2173
2432
  receiver = this.transform(expr2.receiver, restContext);
2174
2433
  }
2175
- (0, import_common_helpers5.invariant)(import_kysely6.SelectQueryNode.is(receiver), "expected receiver to be select query");
2434
+ (0, import_common_helpers6.invariant)(import_kysely7.SelectQueryNode.is(receiver), "expected receiver to be select query");
2176
2435
  let startType;
2177
2436
  if (ExpressionUtils.isField(expr2.receiver)) {
2178
2437
  const receiverField = requireField(this.schema, context.model, expr2.receiver.field);
@@ -2201,11 +2460,11 @@ var ExpressionTransformer = class {
2201
2460
  alias: void 0
2202
2461
  });
2203
2462
  if (currNode) {
2204
- (0, import_common_helpers5.invariant)(import_kysely6.SelectQueryNode.is(currNode), "expected select query node");
2463
+ (0, import_common_helpers6.invariant)(import_kysely7.SelectQueryNode.is(currNode), "expected select query node");
2205
2464
  currNode = {
2206
2465
  ...relation,
2207
2466
  selections: [
2208
- import_kysely6.SelectionNode.create(import_kysely6.AliasNode.create(currNode, import_kysely6.IdentifierNode.create(members[i + 1])))
2467
+ import_kysely7.SelectionNode.create(import_kysely7.AliasNode.create(currNode, import_kysely7.IdentifierNode.create(members[i + 1])))
2209
2468
  ]
2210
2469
  };
2211
2470
  } else {
@@ -2218,21 +2477,21 @@ var ExpressionTransformer = class {
2218
2477
  };
2219
2478
  }
2220
2479
  } else {
2221
- (0, import_common_helpers5.invariant)(i === members.length - 1, "plain field access must be the last segment");
2222
- (0, import_common_helpers5.invariant)(!currNode, "plain field access must be the last segment");
2223
- currNode = import_kysely6.ColumnNode.create(member);
2480
+ (0, import_common_helpers6.invariant)(i === members.length - 1, "plain field access must be the last segment");
2481
+ (0, import_common_helpers6.invariant)(!currNode, "plain field access must be the last segment");
2482
+ currNode = import_kysely7.ColumnNode.create(member);
2224
2483
  }
2225
2484
  }
2226
2485
  return {
2227
2486
  ...receiver,
2228
2487
  selections: [
2229
- import_kysely6.SelectionNode.create(import_kysely6.AliasNode.create(currNode, import_kysely6.IdentifierNode.create("$t")))
2488
+ import_kysely7.SelectionNode.create(import_kysely7.AliasNode.create(currNode, import_kysely7.IdentifierNode.create("$t")))
2230
2489
  ]
2231
2490
  };
2232
2491
  }
2233
2492
  valueMemberAccess(receiver, expr2, receiverType) {
2234
2493
  if (!receiver) {
2235
- return import_kysely6.ValueNode.createImmediate(null);
2494
+ return import_kysely7.ValueNode.createImmediate(null);
2236
2495
  }
2237
2496
  if (expr2.members.length !== 1) {
2238
2497
  throw new Error(`Only single member access is supported`);
@@ -2243,24 +2502,33 @@ var ExpressionTransformer = class {
2243
2502
  return this.transformValue(fieldValue, fieldDef.type);
2244
2503
  }
2245
2504
  transformRelationAccess(field, relationModel, context) {
2505
+ const m2m = getManyToManyRelation(this.schema, context.model, field);
2506
+ if (m2m) {
2507
+ return this.transformManyToManyRelationAccess(m2m, context);
2508
+ }
2246
2509
  const fromModel = context.model;
2247
2510
  const { keyPairs, ownedByModel } = getRelationForeignKeyFieldPairs(this.schema, fromModel, field);
2248
2511
  let condition;
2249
2512
  if (ownedByModel) {
2250
- condition = conjunction(this.dialect, keyPairs.map(({ fk, pk }) => import_kysely6.BinaryOperationNode.create(import_kysely6.ReferenceNode.create(import_kysely6.ColumnNode.create(fk), import_kysely6.TableNode.create(context.alias ?? fromModel)), import_kysely6.OperatorNode.create("="), import_kysely6.ReferenceNode.create(import_kysely6.ColumnNode.create(pk), import_kysely6.TableNode.create(relationModel)))));
2513
+ condition = conjunction(this.dialect, keyPairs.map(({ fk, pk }) => import_kysely7.BinaryOperationNode.create(import_kysely7.ReferenceNode.create(import_kysely7.ColumnNode.create(fk), import_kysely7.TableNode.create(context.alias ?? fromModel)), import_kysely7.OperatorNode.create("="), import_kysely7.ReferenceNode.create(import_kysely7.ColumnNode.create(pk), import_kysely7.TableNode.create(relationModel)))));
2251
2514
  } else {
2252
- condition = conjunction(this.dialect, keyPairs.map(({ fk, pk }) => import_kysely6.BinaryOperationNode.create(import_kysely6.ReferenceNode.create(import_kysely6.ColumnNode.create(pk), import_kysely6.TableNode.create(context.alias ?? fromModel)), import_kysely6.OperatorNode.create("="), import_kysely6.ReferenceNode.create(import_kysely6.ColumnNode.create(fk), import_kysely6.TableNode.create(relationModel)))));
2515
+ condition = conjunction(this.dialect, keyPairs.map(({ fk, pk }) => import_kysely7.BinaryOperationNode.create(import_kysely7.ReferenceNode.create(import_kysely7.ColumnNode.create(pk), import_kysely7.TableNode.create(context.alias ?? fromModel)), import_kysely7.OperatorNode.create("="), import_kysely7.ReferenceNode.create(import_kysely7.ColumnNode.create(fk), import_kysely7.TableNode.create(relationModel)))));
2253
2516
  }
2254
2517
  return {
2255
2518
  kind: "SelectQueryNode",
2256
- from: import_kysely6.FromNode.create([
2257
- import_kysely6.TableNode.create(relationModel)
2519
+ from: import_kysely7.FromNode.create([
2520
+ import_kysely7.TableNode.create(relationModel)
2258
2521
  ]),
2259
- where: import_kysely6.WhereNode.create(condition)
2522
+ where: import_kysely7.WhereNode.create(condition)
2260
2523
  };
2261
2524
  }
2525
+ transformManyToManyRelationAccess(m2m, context) {
2526
+ const eb = (0, import_kysely7.expressionBuilder)();
2527
+ 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}`));
2528
+ return relationQuery.toOperationNode();
2529
+ }
2262
2530
  createColumnRef(column, context) {
2263
- return import_kysely6.ReferenceNode.create(import_kysely6.ColumnNode.create(column), import_kysely6.TableNode.create(context.alias ?? context.model));
2531
+ return import_kysely7.ReferenceNode.create(import_kysely7.ColumnNode.create(column), import_kysely7.TableNode.create(context.alias ?? context.model));
2264
2532
  }
2265
2533
  isAuthCall(value) {
2266
2534
  return ExpressionUtils.isCall(value) && value.function === "auth";
@@ -2269,7 +2537,7 @@ var ExpressionTransformer = class {
2269
2537
  return ExpressionUtils.isMember(expr2) && this.isAuthCall(expr2.receiver);
2270
2538
  }
2271
2539
  isNullNode(node) {
2272
- return import_kysely6.ValueNode.is(node) && node.value === null;
2540
+ return import_kysely7.ValueNode.is(node) && node.value === null;
2273
2541
  }
2274
2542
  buildLogicalNot(result) {
2275
2543
  return ExpressionUtils.unary("!", result);
@@ -2283,6 +2551,19 @@ var ExpressionTransformer = class {
2283
2551
  return conditions.reduce((acc, condition) => ExpressionUtils.binary(acc, "&&", condition));
2284
2552
  }
2285
2553
  }
2554
+ isRelationField(expr2, model) {
2555
+ const fieldDef = this.getFieldDefFromFieldRef(expr2, model);
2556
+ return !!fieldDef?.relation;
2557
+ }
2558
+ getFieldDefFromFieldRef(expr2, model) {
2559
+ if (ExpressionUtils.isField(expr2)) {
2560
+ return requireField(this.schema, model, expr2.field);
2561
+ } else if (ExpressionUtils.isMember(expr2) && expr2.members.length === 1 && ExpressionUtils.isThis(expr2.receiver)) {
2562
+ return requireField(this.schema, model, expr2.members[0]);
2563
+ } else {
2564
+ return void 0;
2565
+ }
2566
+ }
2286
2567
  };
2287
2568
  _ts_decorate([
2288
2569
  expr("literal"),
@@ -2354,7 +2635,7 @@ _ts_decorate([
2354
2635
  ], ExpressionTransformer.prototype, "_member", null);
2355
2636
 
2356
2637
  // src/plugins/policy/policy-handler.ts
2357
- var PolicyHandler = class extends import_kysely7.OperationNodeTransformer {
2638
+ var PolicyHandler = class extends import_kysely8.OperationNodeTransformer {
2358
2639
  static {
2359
2640
  __name(this, "PolicyHandler");
2360
2641
  }
@@ -2369,129 +2650,296 @@ var PolicyHandler = class extends import_kysely7.OperationNodeTransformer {
2369
2650
  }
2370
2651
  async handle(node, proceed) {
2371
2652
  if (!this.isCrudQueryNode(node)) {
2372
- throw new RejectedByPolicyError(void 0, "non-CRUD queries are not allowed");
2653
+ throw new RejectedByPolicyError(void 0, RejectedByPolicyReason.OTHER, "non-CRUD queries are not allowed");
2373
2654
  }
2374
2655
  if (!this.isMutationQueryNode(node)) {
2375
2656
  return proceed(this.transformNode(node));
2376
2657
  }
2377
- let mutationRequiresTransaction = false;
2378
- const mutationModel = this.getMutationModel(node);
2379
- if (import_kysely7.InsertQueryNode.is(node)) {
2380
- const constCondition = this.tryGetConstantPolicy(mutationModel, "create");
2381
- if (constCondition === false) {
2382
- throw new RejectedByPolicyError(mutationModel);
2383
- } else if (constCondition === void 0) {
2384
- mutationRequiresTransaction = true;
2658
+ const { mutationModel } = this.getMutationModel(node);
2659
+ if (import_kysely8.InsertQueryNode.is(node)) {
2660
+ const isManyToManyJoinTable = this.isManyToManyJoinTable(mutationModel);
2661
+ let needCheckPreCreate = true;
2662
+ if (!isManyToManyJoinTable) {
2663
+ const constCondition = this.tryGetConstantPolicy(mutationModel, "create");
2664
+ if (constCondition === true) {
2665
+ needCheckPreCreate = false;
2666
+ } else if (constCondition === false) {
2667
+ throw new RejectedByPolicyError(mutationModel);
2668
+ }
2669
+ }
2670
+ if (needCheckPreCreate) {
2671
+ await this.enforcePreCreatePolicy(node, mutationModel, isManyToManyJoinTable, proceed);
2385
2672
  }
2386
2673
  }
2387
- if (!mutationRequiresTransaction && !node.returning) {
2388
- return proceed(this.transformNode(node));
2389
- }
2390
- if (import_kysely7.InsertQueryNode.is(node)) {
2391
- await this.enforcePreCreatePolicy(node, proceed);
2392
- }
2393
- const transformedNode = this.transformNode(node);
2394
- const result = await proceed(transformedNode);
2395
- if (!this.onlyReturningId(node)) {
2674
+ const result = await proceed(this.transformNode(node));
2675
+ if (!node.returning || this.onlyReturningId(node)) {
2676
+ return result;
2677
+ } else {
2396
2678
  const readBackResult = await this.processReadBack(node, result, proceed);
2397
2679
  if (readBackResult.rows.length !== result.rows.length) {
2398
- throw new RejectedByPolicyError(mutationModel, "result is not allowed to be read back");
2680
+ throw new RejectedByPolicyError(mutationModel, RejectedByPolicyReason.CANNOT_READ_BACK, "result is not allowed to be read back");
2399
2681
  }
2400
2682
  return readBackResult;
2401
- } else {
2683
+ }
2684
+ }
2685
+ // #region overrides
2686
+ transformSelectQuery(node) {
2687
+ let whereNode = this.transformNode(node.where);
2688
+ const policyFilter = this.createPolicyFilterForFrom(node.from);
2689
+ if (policyFilter) {
2690
+ whereNode = import_kysely8.WhereNode.create(whereNode?.where ? conjunction(this.dialect, [
2691
+ whereNode.where,
2692
+ policyFilter
2693
+ ]) : policyFilter);
2694
+ }
2695
+ const baseResult = super.transformSelectQuery({
2696
+ ...node,
2697
+ where: void 0
2698
+ });
2699
+ return {
2700
+ ...baseResult,
2701
+ where: whereNode
2702
+ };
2703
+ }
2704
+ transformJoin(node) {
2705
+ const table = this.extractTableName(node.table);
2706
+ if (!table) {
2707
+ return super.transformJoin(node);
2708
+ }
2709
+ const filter = this.buildPolicyFilter(table.model, table.alias, "read");
2710
+ const nestedSelect = {
2711
+ kind: "SelectQueryNode",
2712
+ from: import_kysely8.FromNode.create([
2713
+ node.table
2714
+ ]),
2715
+ selections: [
2716
+ import_kysely8.SelectionNode.createSelectAll()
2717
+ ],
2718
+ where: import_kysely8.WhereNode.create(filter)
2719
+ };
2720
+ return {
2721
+ ...node,
2722
+ table: import_kysely8.AliasNode.create(import_kysely8.ParensNode.create(nestedSelect), import_kysely8.IdentifierNode.create(table.alias ?? table.model))
2723
+ };
2724
+ }
2725
+ transformInsertQuery(node) {
2726
+ let onConflict = node.onConflict;
2727
+ if (onConflict?.updates) {
2728
+ const { mutationModel, alias } = this.getMutationModel(node);
2729
+ const filter = this.buildPolicyFilter(mutationModel, alias, "update");
2730
+ if (onConflict.updateWhere) {
2731
+ onConflict = {
2732
+ ...onConflict,
2733
+ updateWhere: import_kysely8.WhereNode.create(conjunction(this.dialect, [
2734
+ onConflict.updateWhere.where,
2735
+ filter
2736
+ ]))
2737
+ };
2738
+ } else {
2739
+ onConflict = {
2740
+ ...onConflict,
2741
+ updateWhere: import_kysely8.WhereNode.create(filter)
2742
+ };
2743
+ }
2744
+ }
2745
+ const processedNode = onConflict ? {
2746
+ ...node,
2747
+ onConflict
2748
+ } : node;
2749
+ const result = super.transformInsertQuery(processedNode);
2750
+ if (!node.returning) {
2751
+ return result;
2752
+ }
2753
+ if (this.onlyReturningId(node)) {
2402
2754
  return result;
2755
+ } else {
2756
+ const { mutationModel } = this.getMutationModel(node);
2757
+ const idFields = requireIdFields(this.client.$schema, mutationModel);
2758
+ return {
2759
+ ...result,
2760
+ returning: import_kysely8.ReturningNode.create(idFields.map((field) => import_kysely8.SelectionNode.create(import_kysely8.ColumnNode.create(field))))
2761
+ };
2762
+ }
2763
+ }
2764
+ transformUpdateQuery(node) {
2765
+ const result = super.transformUpdateQuery(node);
2766
+ const { mutationModel, alias } = this.getMutationModel(node);
2767
+ let filter = this.buildPolicyFilter(mutationModel, alias, "update");
2768
+ if (node.from) {
2769
+ const joinFilter = this.createPolicyFilterForFrom(node.from);
2770
+ if (joinFilter) {
2771
+ filter = conjunction(this.dialect, [
2772
+ filter,
2773
+ joinFilter
2774
+ ]);
2775
+ }
2403
2776
  }
2777
+ return {
2778
+ ...result,
2779
+ where: import_kysely8.WhereNode.create(result.where ? conjunction(this.dialect, [
2780
+ result.where.where,
2781
+ filter
2782
+ ]) : filter)
2783
+ };
2784
+ }
2785
+ transformDeleteQuery(node) {
2786
+ const result = super.transformDeleteQuery(node);
2787
+ const { mutationModel, alias } = this.getMutationModel(node);
2788
+ let filter = this.buildPolicyFilter(mutationModel, alias, "delete");
2789
+ if (node.using) {
2790
+ const joinFilter = this.createPolicyFilterForTables(node.using.tables);
2791
+ if (joinFilter) {
2792
+ filter = conjunction(this.dialect, [
2793
+ filter,
2794
+ joinFilter
2795
+ ]);
2796
+ }
2797
+ }
2798
+ return {
2799
+ ...result,
2800
+ where: import_kysely8.WhereNode.create(result.where ? conjunction(this.dialect, [
2801
+ result.where.where,
2802
+ filter
2803
+ ]) : filter)
2804
+ };
2404
2805
  }
2806
+ // #endregion
2807
+ // #region helpers
2405
2808
  onlyReturningId(node) {
2406
2809
  if (!node.returning) {
2407
2810
  return true;
2408
2811
  }
2409
- const idFields = getIdFields(this.client.$schema, this.getMutationModel(node));
2812
+ const { mutationModel } = this.getMutationModel(node);
2813
+ const idFields = requireIdFields(this.client.$schema, mutationModel);
2410
2814
  const collector = new ColumnCollector();
2411
2815
  const selectedColumns = collector.collect(node.returning);
2412
2816
  return selectedColumns.every((c) => idFields.includes(c));
2413
2817
  }
2414
- async enforcePreCreatePolicy(node, proceed) {
2415
- const model = this.getMutationModel(node);
2818
+ async enforcePreCreatePolicy(node, mutationModel, isManyToManyJoinTable, proceed) {
2416
2819
  const fields = node.columns?.map((c) => c.column.name) ?? [];
2417
- const valueRows = node.values ? this.unwrapCreateValueRows(node.values, model, fields) : [
2820
+ const valueRows = node.values ? this.unwrapCreateValueRows(node.values, mutationModel, fields, isManyToManyJoinTable) : [
2418
2821
  []
2419
2822
  ];
2420
2823
  for (const values of valueRows) {
2421
- await this.enforcePreCreatePolicyForOne(model, fields, values.map((v) => v.node), proceed);
2824
+ if (isManyToManyJoinTable) {
2825
+ await this.enforcePreCreatePolicyForManyToManyJoinTable(mutationModel, fields, values.map((v) => v.node), proceed);
2826
+ } else {
2827
+ await this.enforcePreCreatePolicyForOne(mutationModel, fields, values.map((v) => v.node), proceed);
2828
+ }
2829
+ }
2830
+ }
2831
+ async enforcePreCreatePolicyForManyToManyJoinTable(tableName, fields, values, proceed) {
2832
+ const m2m = this.resolveManyToManyJoinTable(tableName);
2833
+ (0, import_common_helpers7.invariant)(m2m);
2834
+ (0, import_common_helpers7.invariant)(fields.includes("A") && fields.includes("B"), "many-to-many join table must have A and B fk fields");
2835
+ const aIndex = fields.indexOf("A");
2836
+ const aNode = values[aIndex];
2837
+ const bIndex = fields.indexOf("B");
2838
+ const bNode = values[bIndex];
2839
+ (0, import_common_helpers7.invariant)(import_kysely8.ValueNode.is(aNode) && import_kysely8.ValueNode.is(bNode), "A and B values must be ValueNode");
2840
+ const aValue = aNode.value;
2841
+ const bValue = bNode.value;
2842
+ (0, import_common_helpers7.invariant)(aValue !== null && aValue !== void 0, "A value cannot be null or undefined");
2843
+ (0, import_common_helpers7.invariant)(bValue !== null && bValue !== void 0, "B value cannot be null or undefined");
2844
+ const eb = (0, import_kysely8.expressionBuilder)();
2845
+ const filterA = this.buildPolicyFilter(m2m.firstModel, void 0, "update");
2846
+ const queryA = eb.selectFrom(m2m.firstModel).where(eb(eb.ref(`${m2m.firstModel}.${m2m.firstIdField}`), "=", aValue)).select(() => new import_kysely8.ExpressionWrapper(filterA).as("$t"));
2847
+ const filterB = this.buildPolicyFilter(m2m.secondModel, void 0, "update");
2848
+ const queryB = eb.selectFrom(m2m.secondModel).where(eb(eb.ref(`${m2m.secondModel}.${m2m.secondIdField}`), "=", bValue)).select(() => new import_kysely8.ExpressionWrapper(filterB).as("$t"));
2849
+ const queryNode = {
2850
+ kind: "SelectQueryNode",
2851
+ selections: [
2852
+ import_kysely8.SelectionNode.create(import_kysely8.AliasNode.create(queryA.toOperationNode(), import_kysely8.IdentifierNode.create("$conditionA"))),
2853
+ import_kysely8.SelectionNode.create(import_kysely8.AliasNode.create(queryB.toOperationNode(), import_kysely8.IdentifierNode.create("$conditionB")))
2854
+ ]
2855
+ };
2856
+ const result = await proceed(queryNode);
2857
+ if (!result.rows[0]?.$conditionA) {
2858
+ throw new RejectedByPolicyError(m2m.firstModel, RejectedByPolicyReason.CANNOT_READ_BACK, `many-to-many relation participant model "${m2m.firstModel}" not updatable`);
2859
+ }
2860
+ if (!result.rows[0]?.$conditionB) {
2861
+ throw new RejectedByPolicyError(m2m.secondModel, RejectedByPolicyReason.NO_ACCESS, `many-to-many relation participant model "${m2m.secondModel}" not updatable`);
2422
2862
  }
2423
2863
  }
2424
2864
  async enforcePreCreatePolicyForOne(model, fields, values, proceed) {
2425
- const allFields = Object.keys(requireModel(this.client.$schema, model).fields);
2865
+ const allFields = Object.entries(requireModel(this.client.$schema, model).fields).filter(([, def]) => !def.relation);
2426
2866
  const allValues = [];
2427
- for (const fieldName of allFields) {
2428
- const index = fields.indexOf(fieldName);
2867
+ for (const [name, _def] of allFields) {
2868
+ const index = fields.indexOf(name);
2429
2869
  if (index >= 0) {
2430
2870
  allValues.push(values[index]);
2431
2871
  } else {
2432
- allValues.push(import_kysely7.ValueNode.createImmediate(null));
2872
+ allValues.push(import_kysely8.ValueNode.createImmediate(null));
2433
2873
  }
2434
2874
  }
2875
+ const eb = (0, import_kysely8.expressionBuilder)();
2435
2876
  const constTable = {
2436
2877
  kind: "SelectQueryNode",
2437
- from: import_kysely7.FromNode.create([
2438
- import_kysely7.AliasNode.create(import_kysely7.ParensNode.create(import_kysely7.ValuesNode.create([
2439
- import_kysely7.ValueListNode.create(allValues)
2440
- ])), import_kysely7.IdentifierNode.create("$t"))
2878
+ from: import_kysely8.FromNode.create([
2879
+ import_kysely8.AliasNode.create(import_kysely8.ParensNode.create(import_kysely8.ValuesNode.create([
2880
+ import_kysely8.ValueListNode.create(allValues)
2881
+ ])), import_kysely8.IdentifierNode.create("$t"))
2441
2882
  ]),
2442
- selections: allFields.map((field, index) => import_kysely7.SelectionNode.create(import_kysely7.AliasNode.create(import_kysely7.ColumnNode.create(`column${index + 1}`), import_kysely7.IdentifierNode.create(field))))
2883
+ selections: allFields.map(([name, def], index) => {
2884
+ const castedColumnRef = import_kysely8.sql`CAST(${eb.ref(`column${index + 1}`)} as ${import_kysely8.sql.raw(this.dialect.getFieldSqlType(def))})`.as(name);
2885
+ return import_kysely8.SelectionNode.create(castedColumnRef.toOperationNode());
2886
+ })
2443
2887
  };
2444
2888
  const filter = this.buildPolicyFilter(model, void 0, "create");
2445
2889
  const preCreateCheck = {
2446
2890
  kind: "SelectQueryNode",
2447
- from: import_kysely7.FromNode.create([
2448
- import_kysely7.AliasNode.create(constTable, import_kysely7.IdentifierNode.create(model))
2891
+ from: import_kysely8.FromNode.create([
2892
+ import_kysely8.AliasNode.create(constTable, import_kysely8.IdentifierNode.create(model))
2449
2893
  ]),
2450
2894
  selections: [
2451
- import_kysely7.SelectionNode.create(import_kysely7.AliasNode.create(import_kysely7.BinaryOperationNode.create(import_kysely7.FunctionNode.create("COUNT", [
2452
- import_kysely7.ValueNode.createImmediate(1)
2453
- ]), import_kysely7.OperatorNode.create(">"), import_kysely7.ValueNode.createImmediate(0)), import_kysely7.IdentifierNode.create("$condition")))
2895
+ import_kysely8.SelectionNode.create(import_kysely8.AliasNode.create(import_kysely8.BinaryOperationNode.create(import_kysely8.FunctionNode.create("COUNT", [
2896
+ import_kysely8.ValueNode.createImmediate(1)
2897
+ ]), import_kysely8.OperatorNode.create(">"), import_kysely8.ValueNode.createImmediate(0)), import_kysely8.IdentifierNode.create("$condition")))
2454
2898
  ],
2455
- where: import_kysely7.WhereNode.create(filter)
2899
+ where: import_kysely8.WhereNode.create(filter)
2456
2900
  };
2457
2901
  const result = await proceed(preCreateCheck);
2458
2902
  if (!result.rows[0]?.$condition) {
2459
2903
  throw new RejectedByPolicyError(model);
2460
2904
  }
2461
2905
  }
2462
- unwrapCreateValueRows(node, model, fields) {
2463
- if (import_kysely7.ValuesNode.is(node)) {
2464
- return node.values.map((v) => this.unwrapCreateValueRow(v.values, model, fields));
2465
- } else if (import_kysely7.PrimitiveValueListNode.is(node)) {
2906
+ unwrapCreateValueRows(node, model, fields, isManyToManyJoinTable) {
2907
+ if (import_kysely8.ValuesNode.is(node)) {
2908
+ return node.values.map((v) => this.unwrapCreateValueRow(v.values, model, fields, isManyToManyJoinTable));
2909
+ } else if (import_kysely8.PrimitiveValueListNode.is(node)) {
2466
2910
  return [
2467
- this.unwrapCreateValueRow(node.values, model, fields)
2911
+ this.unwrapCreateValueRow(node.values, model, fields, isManyToManyJoinTable)
2468
2912
  ];
2469
2913
  } else {
2470
2914
  throw new InternalError(`Unexpected node kind: ${node.kind} for unwrapping create values`);
2471
2915
  }
2472
2916
  }
2473
- unwrapCreateValueRow(data, model, fields) {
2474
- (0, import_common_helpers6.invariant)(data.length === fields.length, "data length must match fields length");
2917
+ unwrapCreateValueRow(data, model, fields, isImplicitManyToManyJoinTable) {
2918
+ (0, import_common_helpers7.invariant)(data.length === fields.length, "data length must match fields length");
2475
2919
  const result = [];
2476
2920
  for (let i = 0; i < data.length; i++) {
2477
2921
  const item = data[i];
2478
- const fieldDef = requireField(this.client.$schema, model, fields[i]);
2479
2922
  if (typeof item === "object" && item && "kind" in item) {
2480
- (0, import_common_helpers6.invariant)(item.kind === "ValueNode", "expecting a ValueNode");
2923
+ const fieldDef = requireField(this.client.$schema, model, fields[i]);
2924
+ (0, import_common_helpers7.invariant)(item.kind === "ValueNode", "expecting a ValueNode");
2481
2925
  result.push({
2482
- node: import_kysely7.ValueNode.create(this.dialect.transformPrimitive(item.value, fieldDef.type, !!fieldDef.array)),
2926
+ node: import_kysely8.ValueNode.create(this.dialect.transformPrimitive(item.value, fieldDef.type, !!fieldDef.array)),
2483
2927
  raw: item.value
2484
2928
  });
2485
2929
  } else {
2486
- const value = this.dialect.transformPrimitive(item, fieldDef.type, !!fieldDef.array);
2930
+ let value = item;
2931
+ if (!isImplicitManyToManyJoinTable) {
2932
+ const fieldDef = requireField(this.client.$schema, model, fields[i]);
2933
+ value = this.dialect.transformPrimitive(item, fieldDef.type, !!fieldDef.array);
2934
+ }
2487
2935
  if (Array.isArray(value)) {
2488
2936
  result.push({
2489
- node: import_kysely7.RawNode.createWithSql(this.dialect.buildArrayLiteralSQL(value)),
2937
+ node: import_kysely8.RawNode.createWithSql(this.dialect.buildArrayLiteralSQL(value)),
2490
2938
  raw: value
2491
2939
  });
2492
2940
  } else {
2493
2941
  result.push({
2494
- node: import_kysely7.ValueNode.create(value),
2942
+ node: import_kysely8.ValueNode.create(value),
2495
2943
  raw: value
2496
2944
  });
2497
2945
  }
@@ -2527,18 +2975,15 @@ var PolicyHandler = class extends import_kysely7.OperationNodeTransformer {
2527
2975
  if (!this.isMutationQueryNode(node) || !node.returning) {
2528
2976
  return result;
2529
2977
  }
2530
- const table = this.getMutationModel(node);
2531
- if (!table) {
2532
- throw new InternalError(`Unable to get table name for query node: ${node}`);
2533
- }
2534
- const idConditions = this.buildIdConditions(table, result.rows);
2535
- const policyFilter = this.buildPolicyFilter(table, void 0, "read");
2978
+ const { mutationModel } = this.getMutationModel(node);
2979
+ const idConditions = this.buildIdConditions(mutationModel, result.rows);
2980
+ const policyFilter = this.buildPolicyFilter(mutationModel, void 0, "read");
2536
2981
  const select = {
2537
2982
  kind: "SelectQueryNode",
2538
- from: import_kysely7.FromNode.create([
2539
- import_kysely7.TableNode.create(table)
2983
+ from: import_kysely8.FromNode.create([
2984
+ import_kysely8.TableNode.create(mutationModel)
2540
2985
  ]),
2541
- where: import_kysely7.WhereNode.create(conjunction(this.dialect, [
2986
+ where: import_kysely8.WhereNode.create(conjunction(this.dialect, [
2542
2987
  idConditions,
2543
2988
  policyFilter
2544
2989
  ])),
@@ -2548,15 +2993,31 @@ var PolicyHandler = class extends import_kysely7.OperationNodeTransformer {
2548
2993
  return selectResult;
2549
2994
  }
2550
2995
  buildIdConditions(table, rows) {
2551
- const idFields = getIdFields(this.client.$schema, table);
2552
- return disjunction(this.dialect, rows.map((row) => conjunction(this.dialect, idFields.map((field) => import_kysely7.BinaryOperationNode.create(import_kysely7.ColumnNode.create(field), import_kysely7.OperatorNode.create("="), import_kysely7.ValueNode.create(row[field]))))));
2996
+ const idFields = requireIdFields(this.client.$schema, table);
2997
+ return disjunction(this.dialect, rows.map((row) => conjunction(this.dialect, idFields.map((field) => import_kysely8.BinaryOperationNode.create(import_kysely8.ColumnNode.create(field), import_kysely8.OperatorNode.create("="), import_kysely8.ValueNode.create(row[field]))))));
2553
2998
  }
2554
2999
  getMutationModel(node) {
2555
- const r = (0, import_ts_pattern8.match)(node).when(import_kysely7.InsertQueryNode.is, (node2) => getTableName(node2.into)).when(import_kysely7.UpdateQueryNode.is, (node2) => getTableName(node2.table)).when(import_kysely7.DeleteQueryNode.is, (node2) => {
3000
+ const r = (0, import_ts_pattern8.match)(node).when(import_kysely8.InsertQueryNode.is, (node2) => ({
3001
+ mutationModel: getTableName(node2.into),
3002
+ alias: void 0
3003
+ })).when(import_kysely8.UpdateQueryNode.is, (node2) => {
3004
+ if (!node2.table) {
3005
+ throw new QueryError("Update query must have a table");
3006
+ }
3007
+ const r2 = this.extractTableName(node2.table);
3008
+ return r2 ? {
3009
+ mutationModel: r2.model,
3010
+ alias: r2.alias
3011
+ } : void 0;
3012
+ }).when(import_kysely8.DeleteQueryNode.is, (node2) => {
2556
3013
  if (node2.from.froms.length !== 1) {
2557
- throw new InternalError("Only one from table is supported for delete");
3014
+ throw new QueryError("Only one from table is supported for delete");
2558
3015
  }
2559
- return getTableName(node2.from.froms[0]);
3016
+ const r2 = this.extractTableName(node2.from.froms[0]);
3017
+ return r2 ? {
3018
+ mutationModel: r2.model,
3019
+ alias: r2.alias
3020
+ } : void 0;
2560
3021
  }).exhaustive();
2561
3022
  if (!r) {
2562
3023
  throw new InternalError(`Unable to get table name for query node: ${node}`);
@@ -2564,18 +3025,22 @@ var PolicyHandler = class extends import_kysely7.OperationNodeTransformer {
2564
3025
  return r;
2565
3026
  }
2566
3027
  isCrudQueryNode(node) {
2567
- return import_kysely7.SelectQueryNode.is(node) || import_kysely7.InsertQueryNode.is(node) || import_kysely7.UpdateQueryNode.is(node) || import_kysely7.DeleteQueryNode.is(node);
3028
+ return import_kysely8.SelectQueryNode.is(node) || import_kysely8.InsertQueryNode.is(node) || import_kysely8.UpdateQueryNode.is(node) || import_kysely8.DeleteQueryNode.is(node);
2568
3029
  }
2569
3030
  isMutationQueryNode(node) {
2570
- return import_kysely7.InsertQueryNode.is(node) || import_kysely7.UpdateQueryNode.is(node) || import_kysely7.DeleteQueryNode.is(node);
3031
+ return import_kysely8.InsertQueryNode.is(node) || import_kysely8.UpdateQueryNode.is(node) || import_kysely8.DeleteQueryNode.is(node);
2571
3032
  }
2572
3033
  buildPolicyFilter(model, alias, operation) {
3034
+ const m2mFilter = this.getModelPolicyFilterForManyToManyJoinTable(model, alias, operation);
3035
+ if (m2mFilter) {
3036
+ return m2mFilter;
3037
+ }
2573
3038
  const policies = this.getModelPolicies(model, operation);
2574
3039
  if (policies.length === 0) {
2575
3040
  return falseNode(this.dialect);
2576
3041
  }
2577
- const allows = policies.filter((policy) => policy.kind === "allow").map((policy) => this.transformPolicyCondition(model, alias, operation, policy));
2578
- const denies = policies.filter((policy) => policy.kind === "deny").map((policy) => this.transformPolicyCondition(model, alias, operation, policy));
3042
+ const allows = policies.filter((policy) => policy.kind === "allow").map((policy) => this.compilePolicyCondition(model, alias, operation, policy));
3043
+ const denies = policies.filter((policy) => policy.kind === "deny").map((policy) => this.compilePolicyCondition(model, alias, operation, policy));
2579
3044
  let combinedPolicy;
2580
3045
  if (allows.length === 0) {
2581
3046
  combinedPolicy = falseNode(this.dialect);
@@ -2591,100 +3056,59 @@ var PolicyHandler = class extends import_kysely7.OperationNodeTransformer {
2591
3056
  }
2592
3057
  return combinedPolicy;
2593
3058
  }
2594
- transformSelectQuery(node) {
2595
- let whereNode = node.where;
2596
- node.from?.froms.forEach((from) => {
2597
- const extractResult = this.extractTableName(from);
2598
- if (extractResult) {
2599
- const { model, alias } = extractResult;
2600
- const filter = this.buildPolicyFilter(model, alias, "read");
2601
- whereNode = import_kysely7.WhereNode.create(whereNode?.where ? conjunction(this.dialect, [
2602
- whereNode.where,
2603
- filter
2604
- ]) : filter);
2605
- }
2606
- });
2607
- const baseResult = super.transformSelectQuery({
2608
- ...node,
2609
- where: void 0
2610
- });
2611
- return {
2612
- ...baseResult,
2613
- where: whereNode
2614
- };
2615
- }
2616
- transformInsertQuery(node) {
2617
- const result = super.transformInsertQuery(node);
2618
- if (!node.returning) {
2619
- return result;
2620
- }
2621
- if (this.onlyReturningId(node)) {
2622
- return result;
2623
- } else {
2624
- const idFields = getIdFields(this.client.$schema, this.getMutationModel(node));
2625
- return {
2626
- ...result,
2627
- returning: import_kysely7.ReturningNode.create(idFields.map((field) => import_kysely7.SelectionNode.create(import_kysely7.ColumnNode.create(field))))
2628
- };
2629
- }
2630
- }
2631
- transformUpdateQuery(node) {
2632
- const result = super.transformUpdateQuery(node);
2633
- const mutationModel = this.getMutationModel(node);
2634
- const filter = this.buildPolicyFilter(mutationModel, void 0, "update");
2635
- return {
2636
- ...result,
2637
- where: import_kysely7.WhereNode.create(result.where ? conjunction(this.dialect, [
2638
- result.where.where,
2639
- filter
2640
- ]) : filter)
2641
- };
2642
- }
2643
- transformDeleteQuery(node) {
2644
- const result = super.transformDeleteQuery(node);
2645
- const mutationModel = this.getMutationModel(node);
2646
- const filter = this.buildPolicyFilter(mutationModel, void 0, "delete");
2647
- return {
2648
- ...result,
2649
- where: import_kysely7.WhereNode.create(result.where ? conjunction(this.dialect, [
2650
- result.where.where,
2651
- filter
2652
- ]) : filter)
2653
- };
2654
- }
2655
- extractTableName(from) {
2656
- if (import_kysely7.TableNode.is(from)) {
3059
+ extractTableName(node) {
3060
+ if (import_kysely8.TableNode.is(node)) {
2657
3061
  return {
2658
- model: from.table.identifier.name
3062
+ model: node.table.identifier.name
2659
3063
  };
2660
3064
  }
2661
- if (import_kysely7.AliasNode.is(from)) {
2662
- const inner = this.extractTableName(from.node);
3065
+ if (import_kysely8.AliasNode.is(node)) {
3066
+ const inner = this.extractTableName(node.node);
2663
3067
  if (!inner) {
2664
3068
  return void 0;
2665
3069
  }
2666
3070
  return {
2667
3071
  model: inner.model,
2668
- alias: import_kysely7.IdentifierNode.is(from.alias) ? from.alias.name : void 0
3072
+ alias: import_kysely8.IdentifierNode.is(node.alias) ? node.alias.name : void 0
2669
3073
  };
2670
3074
  } else {
2671
3075
  return void 0;
2672
3076
  }
2673
3077
  }
2674
- transformPolicyCondition(model, alias, operation, policy) {
2675
- return new ExpressionTransformer(this.client.$schema, this.client.$options, this.client.$auth).transform(policy.condition, {
3078
+ createPolicyFilterForFrom(node) {
3079
+ if (!node) {
3080
+ return void 0;
3081
+ }
3082
+ return this.createPolicyFilterForTables(node.froms);
3083
+ }
3084
+ createPolicyFilterForTables(tables) {
3085
+ return tables.reduce((acc, table) => {
3086
+ const extractResult = this.extractTableName(table);
3087
+ if (extractResult) {
3088
+ const { model, alias } = extractResult;
3089
+ const filter = this.buildPolicyFilter(model, alias, "read");
3090
+ return acc ? conjunction(this.dialect, [
3091
+ acc,
3092
+ filter
3093
+ ]) : filter;
3094
+ }
3095
+ return acc;
3096
+ }, void 0);
3097
+ }
3098
+ compilePolicyCondition(model, alias, operation, policy) {
3099
+ return new ExpressionTransformer(this.client).transform(policy.condition, {
2676
3100
  model,
2677
3101
  alias,
2678
3102
  operation,
2679
3103
  auth: this.client.$auth
2680
3104
  });
2681
3105
  }
2682
- getModelPolicies(modelName, operation) {
2683
- const modelDef = requireModel(this.client.$schema, modelName);
3106
+ getModelPolicies(model, operation) {
3107
+ const modelDef = requireModel(this.client.$schema, model);
2684
3108
  const result = [];
2685
3109
  const extractOperations = /* @__PURE__ */ __name((expr2) => {
2686
- (0, import_common_helpers6.invariant)(ExpressionUtils.isLiteral(expr2), "expecting a literal");
2687
- (0, import_common_helpers6.invariant)(typeof expr2.value === "string", "expecting a string literal");
3110
+ (0, import_common_helpers7.invariant)(ExpressionUtils.isLiteral(expr2), "expecting a literal");
3111
+ (0, import_common_helpers7.invariant)(typeof expr2.value === "string", "expecting a string literal");
2688
3112
  return expr2.value.split(",").filter((v) => !!v).map((v) => v.trim());
2689
3113
  }, "extractOperations");
2690
3114
  if (modelDef.attributes) {
@@ -2696,8 +3120,84 @@ var PolicyHandler = class extends import_kysely7.OperationNodeTransformer {
2696
3120
  }
2697
3121
  return result;
2698
3122
  }
3123
+ resolveManyToManyJoinTable(tableName) {
3124
+ for (const model of Object.values(this.client.$schema.models)) {
3125
+ for (const field of Object.values(model.fields)) {
3126
+ const m2m = getManyToManyRelation(this.client.$schema, model.name, field.name);
3127
+ if (m2m?.joinTable === tableName) {
3128
+ const sortedRecord = [
3129
+ {
3130
+ model: model.name,
3131
+ field: field.name
3132
+ },
3133
+ {
3134
+ model: m2m.otherModel,
3135
+ field: m2m.otherField
3136
+ }
3137
+ ].sort(this.manyToManySorter);
3138
+ const firstIdFields = requireIdFields(this.client.$schema, sortedRecord[0].model);
3139
+ const secondIdFields = requireIdFields(this.client.$schema, sortedRecord[1].model);
3140
+ (0, import_common_helpers7.invariant)(firstIdFields.length === 1 && secondIdFields.length === 1, "only single-field id is supported for implicit many-to-many join table");
3141
+ return {
3142
+ firstModel: sortedRecord[0].model,
3143
+ firstField: sortedRecord[0].field,
3144
+ firstIdField: firstIdFields[0],
3145
+ secondModel: sortedRecord[1].model,
3146
+ secondField: sortedRecord[1].field,
3147
+ secondIdField: secondIdFields[0]
3148
+ };
3149
+ }
3150
+ }
3151
+ }
3152
+ return void 0;
3153
+ }
3154
+ manyToManySorter(a, b) {
3155
+ return a.model !== b.model ? a.model.localeCompare(b.model) : a.field.localeCompare(b.field);
3156
+ }
3157
+ isManyToManyJoinTable(tableName) {
3158
+ return !!this.resolveManyToManyJoinTable(tableName);
3159
+ }
3160
+ getModelPolicyFilterForManyToManyJoinTable(tableName, alias, operation) {
3161
+ const m2m = this.resolveManyToManyJoinTable(tableName);
3162
+ if (!m2m) {
3163
+ return void 0;
3164
+ }
3165
+ const checkForOperation = operation === "read" ? "read" : "update";
3166
+ const eb = (0, import_kysely8.expressionBuilder)();
3167
+ const joinTable = alias ?? tableName;
3168
+ const aQuery = eb.selectFrom(m2m.firstModel).whereRef(`${m2m.firstModel}.${m2m.firstIdField}`, "=", `${joinTable}.A`).select(() => new import_kysely8.ExpressionWrapper(this.buildPolicyFilter(m2m.firstModel, void 0, checkForOperation)).as("$conditionA"));
3169
+ const bQuery = eb.selectFrom(m2m.secondModel).whereRef(`${m2m.secondModel}.${m2m.secondIdField}`, "=", `${joinTable}.B`).select(() => new import_kysely8.ExpressionWrapper(this.buildPolicyFilter(m2m.secondModel, void 0, checkForOperation)).as("$conditionB"));
3170
+ return eb.and([
3171
+ aQuery,
3172
+ bQuery
3173
+ ]).toOperationNode();
3174
+ }
2699
3175
  };
2700
3176
 
3177
+ // src/plugins/policy/functions.ts
3178
+ var check = /* @__PURE__ */ __name((eb, args, { client, model, modelAlias, operation }) => {
3179
+ (0, import_common_helpers8.invariant)(args.length === 1 || args.length === 2, '"check" function requires 1 or 2 arguments');
3180
+ const arg1Node = args[0].toOperationNode();
3181
+ const arg2Node = args.length === 2 ? args[1].toOperationNode() : void 0;
3182
+ if (arg2Node) {
3183
+ (0, import_common_helpers8.invariant)(import_kysely9.ValueNode.is(arg2Node) && typeof arg2Node.value === "string", '"operation" parameter must be a string literal when provided');
3184
+ (0, import_common_helpers8.invariant)(CRUD.includes(arg2Node.value), '"operation" parameter must be one of "create", "read", "update", "delete"');
3185
+ }
3186
+ const fieldName = extractFieldName(arg1Node);
3187
+ (0, import_common_helpers8.invariant)(fieldName, 'Failed to extract field name from the first argument of "check" function');
3188
+ const fieldDef = requireField(client.$schema, model, fieldName);
3189
+ (0, import_common_helpers8.invariant)(fieldDef.relation, `Field "${fieldName}" is not a relation field in model "${model}"`);
3190
+ (0, import_common_helpers8.invariant)(!fieldDef.array, `Field "${fieldName}" is a to-many relation, which is not supported by "check"`);
3191
+ const relationModel = fieldDef.type;
3192
+ const op = arg2Node ? arg2Node.value : operation;
3193
+ const policyHandler = new PolicyHandler(client);
3194
+ const joinPairs = buildJoinPairs(client.$schema, model, modelAlias, fieldName, relationModel);
3195
+ 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))));
3196
+ const policyCondition = policyHandler.buildPolicyFilter(relationModel, void 0, op);
3197
+ const result = eb.selectFrom(relationModel).where(joinCondition).select(new import_kysely9.ExpressionWrapper(policyCondition).as("$condition"));
3198
+ return result;
3199
+ }, "check");
3200
+
2701
3201
  // src/plugins/policy/plugin.ts
2702
3202
  var PolicyPlugin = class {
2703
3203
  static {
@@ -2712,6 +3212,11 @@ var PolicyPlugin = class {
2712
3212
  get description() {
2713
3213
  return "Enforces access policies defined in the schema.";
2714
3214
  }
3215
+ get functions() {
3216
+ return {
3217
+ check
3218
+ };
3219
+ }
2715
3220
  onKyselyQuery({
2716
3221
  query,
2717
3222
  client,
@@ -2729,6 +3234,7 @@ var PolicyPlugin = class {
2729
3234
  // Annotate the CommonJS export names for ESM import in node:
2730
3235
  0 && (module.exports = {
2731
3236
  PolicyPlugin,
2732
- RejectedByPolicyError
3237
+ RejectedByPolicyError,
3238
+ RejectedByPolicyReason
2733
3239
  });
2734
3240
  //# sourceMappingURL=index.cjs.map