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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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");
58
+ // src/plugins/policy/functions.ts
59
+ var import_common_helpers8 = require("@zenstackhq/common-helpers");
60
+ var import_kysely9 = require("kysely");
48
61
 
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");
53
-
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
@@ -134,6 +149,9 @@ var ExpressionUtils = {
134
149
  or: /* @__PURE__ */ __name((expr2, ...expressions) => {
135
150
  return expressions.reduce((acc, exp) => ExpressionUtils.binary(acc, "||", exp), expr2);
136
151
  }, "or"),
152
+ not: /* @__PURE__ */ __name((expr2) => {
153
+ return ExpressionUtils.unary("!", expr2);
154
+ }, "not"),
137
155
  is: /* @__PURE__ */ __name((value, kind) => {
138
156
  return !!value && typeof value === "object" && "kind" in value && value.kind === kind;
139
157
  }, "is"),
@@ -170,15 +188,19 @@ var InternalError = class extends Error {
170
188
 
171
189
  // src/client/query-utils.ts
172
190
  function getModel(schema, model) {
173
- return schema.models[model];
191
+ return Object.values(schema.models).find((m) => m.name.toLowerCase() === model.toLowerCase());
174
192
  }
175
193
  __name(getModel, "getModel");
194
+ function getTypeDef(schema, type) {
195
+ return schema.typeDefs?.[type];
196
+ }
197
+ __name(getTypeDef, "getTypeDef");
176
198
  function requireModel(schema, model) {
177
- const matchedName = Object.keys(schema.models).find((k) => k.toLowerCase() === model.toLowerCase());
178
- if (!matchedName) {
199
+ const modelDef = getModel(schema, model);
200
+ if (!modelDef) {
179
201
  throw new QueryError(`Model "${model}" not found in schema`);
180
202
  }
181
- return schema.models[matchedName];
203
+ return modelDef;
182
204
  }
183
205
  __name(requireModel, "requireModel");
184
206
  function getField(schema, model, field) {
@@ -186,19 +208,35 @@ function getField(schema, model, field) {
186
208
  return modelDef?.fields[field];
187
209
  }
188
210
  __name(getField, "getField");
189
- function requireField(schema, model, field) {
190
- const modelDef = requireModel(schema, model);
191
- if (!modelDef.fields[field]) {
192
- throw new QueryError(`Field "${field}" not found in model "${model}"`);
211
+ function requireField(schema, modelOrType, field) {
212
+ const modelDef = getModel(schema, modelOrType);
213
+ if (modelDef) {
214
+ if (!modelDef.fields[field]) {
215
+ throw new QueryError(`Field "${field}" not found in model "${modelOrType}"`);
216
+ } else {
217
+ return modelDef.fields[field];
218
+ }
193
219
  }
194
- return modelDef.fields[field];
220
+ const typeDef = getTypeDef(schema, modelOrType);
221
+ if (typeDef) {
222
+ if (!typeDef.fields[field]) {
223
+ throw new QueryError(`Field "${field}" not found in type "${modelOrType}"`);
224
+ } else {
225
+ return typeDef.fields[field];
226
+ }
227
+ }
228
+ throw new QueryError(`Model or type "${modelOrType}" not found in schema`);
195
229
  }
196
230
  __name(requireField, "requireField");
197
- function getIdFields(schema, model) {
231
+ function requireIdFields(schema, model) {
198
232
  const modelDef = requireModel(schema, model);
199
- 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;
200
238
  }
201
- __name(getIdFields, "getIdFields");
239
+ __name(requireIdFields, "requireIdFields");
202
240
  function getRelationForeignKeyFieldPairs(schema, model, relationField) {
203
241
  const fieldDef = requireField(schema, model, relationField);
204
242
  if (!fieldDef?.relation) {
@@ -291,7 +329,7 @@ function buildFieldRef(schema, model, field, options, eb, modelAlias, inlineComp
291
329
  throw new QueryError(`Computed field "${field}" implementation not provided for model "${model}"`);
292
330
  }
293
331
  return computer(eb, {
294
- currentModel: modelAlias
332
+ modelAlias
295
333
  });
296
334
  }
297
335
  }
@@ -318,7 +356,7 @@ function buildJoinPairs(schema, model, modelAlias, relationField, relationModelA
318
356
  }
319
357
  __name(buildJoinPairs, "buildJoinPairs");
320
358
  function makeDefaultOrderBy(schema, model) {
321
- const idFields = getIdFields(schema, model);
359
+ const idFields = requireIdFields(schema, model);
322
360
  return idFields.map((f) => ({
323
361
  [f]: "asc"
324
362
  }));
@@ -357,11 +395,17 @@ function getManyToManyRelation(schema, model, field) {
357
395
  "A"
358
396
  ];
359
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");
360
402
  return {
361
403
  parentFkName: orderedFK[0],
404
+ parentPKName: modelIdFields[0],
362
405
  otherModel: fieldDef.type,
363
406
  otherField: fieldDef.relation.opposite,
364
407
  otherFkName: orderedFK[1],
408
+ otherPKName: otherIdFields[0],
365
409
  joinTable: fieldDef.relation.name ? `_${fieldDef.relation.name}` : `_${sortedModelNames[0]}To${sortedModelNames[1]}`
366
410
  };
367
411
  } else {
@@ -417,9 +461,38 @@ function aggregate(eb, expr2, op) {
417
461
  }
418
462
  __name(aggregate, "aggregate");
419
463
 
420
- // src/client/crud/dialects/base.ts
421
- var import_common_helpers = require("@zenstackhq/common-helpers");
422
- var import_kysely = require("kysely");
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
+
493
+ // src/client/crud/dialects/base-dialect.ts
494
+ var import_common_helpers2 = require("@zenstackhq/common-helpers");
495
+ var import_kysely2 = require("kysely");
423
496
  var import_ts_pattern2 = require("ts-pattern");
424
497
 
425
498
  // src/utils/enumerate.ts
@@ -436,7 +509,7 @@ function enumerate(x) {
436
509
  }
437
510
  __name(enumerate, "enumerate");
438
511
 
439
- // src/client/crud/dialects/base.ts
512
+ // src/client/crud/dialects/base-dialect.ts
440
513
  var BaseCrudDialect = class {
441
514
  static {
442
515
  __name(this, "BaseCrudDialect");
@@ -450,6 +523,9 @@ var BaseCrudDialect = class {
450
523
  transformPrimitive(value, _type, _forArrayField) {
451
524
  return value;
452
525
  }
526
+ transformOutput(value, _type) {
527
+ return value;
528
+ }
453
529
  // #region common query builders
454
530
  buildSelectModel(eb, model, modelAlias) {
455
531
  const modelDef = requireModel(this.schema, model);
@@ -478,7 +554,7 @@ var BaseCrudDialect = class {
478
554
  if ("distinct" in args && args.distinct) {
479
555
  const distinct = ensureArray(args.distinct);
480
556
  if (this.supportsDistinctOn) {
481
- 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}`)));
482
558
  } else {
483
559
  throw new QueryError(`"distinct" is not supported by "${this.schema.provider.type}" provider`);
484
560
  }
@@ -528,7 +604,7 @@ var BaseCrudDialect = class {
528
604
  buildCursorFilter(model, query, cursor, orderBy, negateOrderBy, modelAlias) {
529
605
  const _orderBy = orderBy ?? makeDefaultOrderBy(this.schema, model);
530
606
  const orderByItems = ensureArray(_orderBy).flatMap((obj) => Object.entries(obj));
531
- const eb = (0, import_kysely.expressionBuilder)();
607
+ const eb = (0, import_kysely2.expressionBuilder)();
532
608
  const subQueryAlias = `${model}$cursor$sub`;
533
609
  const cursorFilter = this.buildFilter(eb, model, subQueryAlias, cursor);
534
610
  let result = query;
@@ -563,7 +639,7 @@ var BaseCrudDialect = class {
563
639
  if (payload === null) {
564
640
  const { ownedByModel, keyPairs } = getRelationForeignKeyFieldPairs(this.schema, model, field);
565
641
  if (ownedByModel && !fieldDef.originModel) {
566
- 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)));
567
643
  } else {
568
644
  return this.buildToOneRelationFilter(eb, model, modelAlias, field, fieldDef, {
569
645
  is: null
@@ -580,7 +656,7 @@ var BaseCrudDialect = class {
580
656
  joinAlias
581
657
  );
582
658
  const filterResultField = `${field}$filter`;
583
- 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));
584
660
  const conditions = [];
585
661
  if ("is" in payload || "isNot" in payload) {
586
662
  if ("is" in payload) {
@@ -610,24 +686,26 @@ var BaseCrudDialect = class {
610
686
  }
611
687
  buildToManyRelationFilter(eb, model, modelAlias, field, fieldDef, payload) {
612
688
  if (payload === null) {
613
- return eb(import_kysely.sql.ref(`${modelAlias}.${field}`), "is", null);
689
+ return eb(import_kysely2.sql.ref(`${modelAlias}.${field}`), "is", null);
614
690
  }
615
691
  const relationModel = fieldDef.type;
616
692
  const relationFilterSelectAlias = `${modelAlias}$${field}$filter`;
617
693
  const buildPkFkWhereRefs = /* @__PURE__ */ __name((eb2) => {
618
694
  const m2m = getManyToManyRelation(this.schema, model, field);
619
695
  if (m2m) {
620
- const modelIdField = getIdFields(this.schema, model)[0];
621
- const relationIdField = getIdFields(this.schema, relationModel)[0];
622
- 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]}`)));
623
701
  } else {
624
702
  const relationKeyPairs = getRelationForeignKeyFieldPairs(this.schema, model, field);
625
703
  let result2 = this.true(eb2);
626
704
  for (const { fk, pk } of relationKeyPairs.keyPairs) {
627
705
  if (relationKeyPairs.ownedByModel) {
628
- 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}`)));
629
707
  } else {
630
- 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}`)));
631
709
  }
632
710
  }
633
711
  return result2;
@@ -710,7 +788,7 @@ var BaseCrudDialect = class {
710
788
  return eb(lhs, "=", rhs !== null && rhs !== void 0 ? this.transformPrimitive(rhs, type, false) : rhs);
711
789
  }
712
790
  buildStandardFilter(eb, type, payload, lhs, getRhs, recurse, throwIfInvalid = false, onlyForKeys = void 0, excludeKeys = []) {
713
- if (payload === null || !(0, import_common_helpers.isPlainObject)(payload)) {
791
+ if (payload === null || !(0, import_common_helpers2.isPlainObject)(payload)) {
714
792
  return {
715
793
  conditions: [
716
794
  this.buildLiteralFilter(eb, lhs, type, payload)
@@ -729,14 +807,14 @@ var BaseCrudDialect = class {
729
807
  }
730
808
  const rhs = Array.isArray(value) ? value.map(getRhs) : getRhs(value);
731
809
  const condition = (0, import_ts_pattern2.match)(op).with("equals", () => rhs === null ? eb(lhs, "is", null) : eb(lhs, "=", rhs)).with("in", () => {
732
- (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");
733
811
  if (rhs.length === 0) {
734
812
  return this.false(eb);
735
813
  } else {
736
814
  return eb(lhs, "in", rhs);
737
815
  }
738
816
  }).with("notIn", () => {
739
- (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");
740
818
  if (rhs.length === 0) {
741
819
  return this.true(eb);
742
820
  } else {
@@ -776,7 +854,7 @@ var BaseCrudDialect = class {
776
854
  if (key === "mode" || consumedKeys.includes(key)) {
777
855
  continue;
778
856
  }
779
- 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(() => {
780
858
  throw new QueryError(`Invalid string filter key: ${key}`);
781
859
  });
782
860
  if (condition) {
@@ -788,16 +866,16 @@ var BaseCrudDialect = class {
788
866
  }
789
867
  prepStringCasing(eb, value, mode) {
790
868
  if (!mode || mode === "default") {
791
- return value === null ? value : import_kysely.sql.val(value);
869
+ return value === null ? value : import_kysely2.sql.val(value);
792
870
  }
793
871
  if (typeof value === "string") {
794
872
  return eb.fn("lower", [
795
- import_kysely.sql.val(value)
873
+ import_kysely2.sql.val(value)
796
874
  ]);
797
875
  } else if (Array.isArray(value)) {
798
876
  return value.map((v) => this.prepStringCasing(eb, v, mode));
799
877
  } else {
800
- return value === null ? null : import_kysely.sql.val(value);
878
+ return value === null ? null : import_kysely2.sql.val(value);
801
879
  }
802
880
  }
803
881
  buildNumberFilter(eb, fieldRef, type, payload) {
@@ -854,19 +932,19 @@ var BaseCrudDialect = class {
854
932
  "_min",
855
933
  "_max"
856
934
  ].includes(field)) {
857
- (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}"`);
858
936
  for (const [k, v] of Object.entries(value)) {
859
- (0, import_common_helpers.invariant)(v === "asc" || v === "desc", `invalid orderBy value for field "${field}"`);
860
- 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)));
861
939
  }
862
940
  continue;
863
941
  }
864
942
  switch (field) {
865
943
  case "_count": {
866
- (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"');
867
945
  for (const [k, v] of Object.entries(value)) {
868
- (0, import_common_helpers.invariant)(v === "asc" || v === "desc", `invalid orderBy value for field "${field}"`);
869
- 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)));
870
948
  }
871
949
  continue;
872
950
  }
@@ -875,11 +953,11 @@ var BaseCrudDialect = class {
875
953
  }
876
954
  const fieldDef = requireField(this.schema, model, field);
877
955
  if (!fieldDef.relation) {
878
- const fieldRef = this.fieldRef(model, field, (0, import_kysely.expressionBuilder)(), modelAlias);
956
+ const fieldRef = this.fieldRef(model, field, (0, import_kysely2.expressionBuilder)(), modelAlias);
879
957
  if (value === "asc" || value === "desc") {
880
958
  result = result.orderBy(fieldRef, this.negateSort(value, negated));
881
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")) {
882
- 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}`));
883
961
  }
884
962
  } else {
885
963
  const relationModel = fieldDef.type;
@@ -888,13 +966,13 @@ var BaseCrudDialect = class {
888
966
  throw new QueryError(`invalid orderBy value for field "${field}"`);
889
967
  }
890
968
  if ("_count" in value) {
891
- (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"');
892
970
  const sort = this.negateSort(value._count, negated);
893
971
  result = result.orderBy((eb) => {
894
972
  const subQueryAlias = `${modelAlias}$orderBy$${field}$count`;
895
973
  let subQuery = this.buildSelectModel(eb, relationModel, subQueryAlias);
896
974
  const joinPairs = buildJoinPairs(this.schema, model, modelAlias, field, subQueryAlias);
897
- 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)))));
898
976
  subQuery = subQuery.select(() => eb.fn.count(eb.lit(1)).as("_count"));
899
977
  return subQuery;
900
978
  }, sort);
@@ -902,7 +980,7 @@ var BaseCrudDialect = class {
902
980
  } else {
903
981
  result = result.leftJoin(relationModel, (join) => {
904
982
  const joinPairs = buildJoinPairs(this.schema, model, modelAlias, field, relationModel);
905
- 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)))));
906
984
  });
907
985
  result = this.buildOrderBy(result, fieldDef.type, relationModel, value, false, negated);
908
986
  }
@@ -954,13 +1032,13 @@ var BaseCrudDialect = class {
954
1032
  if (fieldDef.computed) {
955
1033
  return query.select((eb) => this.fieldRef(model, field, eb, modelAlias).as(field));
956
1034
  } else if (!fieldDef.originModel) {
957
- return query.select(import_kysely.sql.ref(`${modelAlias}.${field}`).as(field));
1035
+ return query.select(import_kysely2.sql.ref(`${modelAlias}.${field}`).as(field));
958
1036
  } else {
959
1037
  return this.buildSelectField(query, fieldDef.originModel, fieldDef.originModel, field);
960
1038
  }
961
1039
  }
962
1040
  buildDelegateJoin(thisModel, thisModelAlias, otherModelAlias, query) {
963
- const idFields = getIdFields(this.schema, thisModel);
1041
+ const idFields = requireIdFields(this.schema, thisModel);
964
1042
  query = query.leftJoin(otherModelAlias, (qb) => {
965
1043
  for (const idField of idFields) {
966
1044
  qb = qb.onRef(`${thisModelAlias}.${idField}`, "=", `${otherModelAlias}.${idField}`);
@@ -982,10 +1060,16 @@ var BaseCrudDialect = class {
982
1060
  for (const [field, value] of Object.entries(selections.select)) {
983
1061
  const fieldDef = requireField(this.schema, model, field);
984
1062
  const fieldModel = fieldDef.type;
985
- const joinPairs = buildJoinPairs(this.schema, model, parentAlias, field, fieldModel);
986
- let fieldCountQuery = eb.selectFrom(fieldModel).select(eb.fn.countAll().as(`_count$${field}`));
987
- for (const [left, right] of joinPairs) {
988
- 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
+ }
989
1073
  }
990
1074
  if (value && typeof value === "object" && "where" in value && value.where && typeof value.where === "object") {
991
1075
  const filter = this.buildFilter(eb, fieldModel, fieldModel, value.where);
@@ -1065,6 +1149,9 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
1065
1149
  static {
1066
1150
  __name(this, "PostgresCrudDialect");
1067
1151
  }
1152
+ constructor(schema, options) {
1153
+ super(schema, options);
1154
+ }
1068
1155
  get provider() {
1069
1156
  return "postgresql";
1070
1157
  }
@@ -1079,8 +1166,40 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
1079
1166
  return value.map((v) => this.transformPrimitive(v, type, false));
1080
1167
  }
1081
1168
  } else {
1082
- 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;
1083
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;
1199
+ }
1200
+ }
1201
+ transformOutputBytes(value) {
1202
+ return Buffer.isBuffer(value) ? Uint8Array.from(value) : value;
1084
1203
  }
1085
1204
  buildRelationSelection(query, model, relationField, parentAlias, payload) {
1086
1205
  const relationResultName = `${parentAlias}$${relationField}`;
@@ -1112,14 +1231,14 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
1112
1231
  buildRelationJoinFilter(query, model, relationField, relationModel, relationModelAlias, parentAlias) {
1113
1232
  const m2m = getManyToManyRelation(this.schema, model, relationField);
1114
1233
  if (m2m) {
1115
- const parentIds = getIdFields(this.schema, model);
1116
- const relationIds = getIdFields(this.schema, relationModel);
1117
- (0, import_common_helpers2.invariant)(parentIds.length === 1, "many-to-many relation must have exactly one id field");
1118
- (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");
1119
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}`)));
1120
1239
  } else {
1121
1240
  const joinPairs = buildJoinPairs(this.schema, model, parentAlias, relationField, relationModelAlias);
1122
- 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)))));
1123
1242
  }
1124
1243
  return query;
1125
1244
  }
@@ -1127,9 +1246,9 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
1127
1246
  qb = qb.select((eb) => {
1128
1247
  const objArgs = this.buildRelationObjectArgs(relationModel, relationModelAlias, eb, payload, parentResultName);
1129
1248
  if (relationFieldDef.array) {
1130
- 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");
1131
1250
  } else {
1132
- 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");
1133
1252
  }
1134
1253
  });
1135
1254
  return qb;
@@ -1140,13 +1259,13 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
1140
1259
  const descendantModels = getDelegateDescendantModels(this.schema, relationModel);
1141
1260
  if (descendantModels.length > 0) {
1142
1261
  objArgs.push(...descendantModels.map((subModel) => [
1143
- import_kysely2.sql.lit(`${DELEGATE_JOINED_FIELD_PREFIX}${subModel.name}`),
1262
+ import_kysely3.sql.lit(`${DELEGATE_JOINED_FIELD_PREFIX}${subModel.name}`),
1144
1263
  eb.ref(`${DELEGATE_JOINED_FIELD_PREFIX}${subModel.name}`)
1145
1264
  ]).flatMap((v) => v));
1146
1265
  }
1147
1266
  if (payload === true || !payload.select) {
1148
1267
  objArgs.push(...Object.entries(relationModelDef.fields).filter(([, value]) => !value.relation).filter(([name]) => !(typeof payload === "object" && payload.omit?.[name] === true)).map(([field]) => [
1149
- import_kysely2.sql.lit(field),
1268
+ import_kysely3.sql.lit(field),
1150
1269
  this.fieldRef(relationModel, field, eb, relationModelAlias, false)
1151
1270
  ]).flatMap((v) => v));
1152
1271
  } else if (payload.select) {
@@ -1154,14 +1273,14 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
1154
1273
  if (field === "_count") {
1155
1274
  const subJson = this.buildCountJson(relationModel, eb, relationModelAlias, value);
1156
1275
  return [
1157
- import_kysely2.sql.lit(field),
1276
+ import_kysely3.sql.lit(field),
1158
1277
  subJson
1159
1278
  ];
1160
1279
  } else {
1161
1280
  const fieldDef = requireField(this.schema, relationModel, field);
1162
1281
  const fieldValue = fieldDef.relation ? eb.ref(`${parentResultName}$${field}.$data`) : this.fieldRef(relationModel, field, eb, relationModelAlias, false);
1163
1282
  return [
1164
- import_kysely2.sql.lit(field),
1283
+ import_kysely3.sql.lit(field),
1165
1284
  fieldValue
1166
1285
  ];
1167
1286
  }
@@ -1169,7 +1288,7 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
1169
1288
  }
1170
1289
  if (typeof payload === "object" && payload.include && typeof payload.include === "object") {
1171
1290
  objArgs.push(...Object.entries(payload.include).filter(([, value]) => value).map(([field]) => [
1172
- import_kysely2.sql.lit(field),
1291
+ import_kysely3.sql.lit(field),
1173
1292
  // reference the synthesized JSON field
1174
1293
  eb.ref(`${parentResultName}$${field}.$data`)
1175
1294
  ]).flatMap((v) => v));
@@ -1199,7 +1318,7 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
1199
1318
  }
1200
1319
  buildJsonObject(eb, value) {
1201
1320
  return eb.fn("jsonb_build_object", Object.entries(value).flatMap(([key, value2]) => [
1202
- import_kysely2.sql.lit(key),
1321
+ import_kysely3.sql.lit(key),
1203
1322
  value2
1204
1323
  ]));
1205
1324
  }
@@ -1227,11 +1346,33 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
1227
1346
  get supportInsertWithDefault() {
1228
1347
  return true;
1229
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
+ }
1230
1370
  };
1231
1371
 
1232
1372
  // src/client/crud/dialects/sqlite.ts
1233
- var import_common_helpers3 = require("@zenstackhq/common-helpers");
1234
- 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");
1235
1376
  var import_ts_pattern4 = require("ts-pattern");
1236
1377
  var SqliteCrudDialect = class extends BaseCrudDialect {
1237
1378
  static {
@@ -1250,9 +1391,57 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
1250
1391
  if (this.schema.typeDefs && type in this.schema.typeDefs) {
1251
1392
  return JSON.stringify(value);
1252
1393
  } else {
1253
- 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);
1254
1442
  }
1255
1443
  }
1444
+ return value;
1256
1445
  }
1257
1446
  buildRelationSelection(query, model, relationField, parentAlias, payload) {
1258
1447
  return query.select((eb) => this.buildRelationJSON(model, eb, relationField, parentAlias, payload).as(relationField));
@@ -1279,13 +1468,13 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
1279
1468
  const descendantModels = getDelegateDescendantModels(this.schema, relationModel);
1280
1469
  if (descendantModels.length > 0) {
1281
1470
  objArgs.push(...descendantModels.map((subModel) => [
1282
- import_kysely3.sql.lit(`${DELEGATE_JOINED_FIELD_PREFIX}${subModel.name}`),
1471
+ import_kysely4.sql.lit(`${DELEGATE_JOINED_FIELD_PREFIX}${subModel.name}`),
1283
1472
  eb.ref(`${DELEGATE_JOINED_FIELD_PREFIX}${subModel.name}`)
1284
1473
  ]).flatMap((v) => v));
1285
1474
  }
1286
1475
  if (payload === true || !payload.select) {
1287
1476
  objArgs.push(...Object.entries(relationModelDef.fields).filter(([, value]) => !value.relation).filter(([name]) => !(typeof payload === "object" && payload.omit?.[name] === true)).map(([field]) => [
1288
- import_kysely3.sql.lit(field),
1477
+ import_kysely4.sql.lit(field),
1289
1478
  this.fieldRef(relationModel, field, eb, subQueryName, false)
1290
1479
  ]).flatMap((v) => v));
1291
1480
  } else if (payload.select) {
@@ -1293,7 +1482,7 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
1293
1482
  if (field === "_count") {
1294
1483
  const subJson = this.buildCountJson(relationModel, eb, `${parentAlias}$${relationField}`, value);
1295
1484
  return [
1296
- import_kysely3.sql.lit(field),
1485
+ import_kysely4.sql.lit(field),
1297
1486
  subJson
1298
1487
  ];
1299
1488
  } else {
@@ -1301,12 +1490,12 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
1301
1490
  if (fieldDef.relation) {
1302
1491
  const subJson = this.buildRelationJSON(relationModel, eb, field, `${parentAlias}$${relationField}`, value);
1303
1492
  return [
1304
- import_kysely3.sql.lit(field),
1493
+ import_kysely4.sql.lit(field),
1305
1494
  subJson
1306
1495
  ];
1307
1496
  } else {
1308
1497
  return [
1309
- import_kysely3.sql.lit(field),
1498
+ import_kysely4.sql.lit(field),
1310
1499
  this.fieldRef(relationModel, field, eb, subQueryName, false)
1311
1500
  ];
1312
1501
  }
@@ -1317,15 +1506,15 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
1317
1506
  objArgs.push(...Object.entries(payload.include).filter(([, value]) => value).map(([field, value]) => {
1318
1507
  const subJson = this.buildRelationJSON(relationModel, eb, field, `${parentAlias}$${relationField}`, value);
1319
1508
  return [
1320
- import_kysely3.sql.lit(field),
1509
+ import_kysely4.sql.lit(field),
1321
1510
  subJson
1322
1511
  ];
1323
1512
  }).flatMap((v) => v));
1324
1513
  }
1325
1514
  if (relationFieldDef.array) {
1326
- 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");
1327
1516
  } else {
1328
- 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");
1329
1518
  }
1330
1519
  });
1331
1520
  return tbl;
@@ -1335,10 +1524,10 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
1335
1524
  const relationModel = fieldDef.type;
1336
1525
  const m2m = getManyToManyRelation(this.schema, model, relationField);
1337
1526
  if (m2m) {
1338
- const parentIds = getIdFields(this.schema, model);
1339
- const relationIds = getIdFields(this.schema, relationModel);
1340
- (0, import_common_helpers3.invariant)(parentIds.length === 1, "many-to-many relation must have exactly one id field");
1341
- (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");
1342
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}`)));
1343
1532
  } else {
1344
1533
  const { keyPairs, ownedByModel } = getRelationForeignKeyFieldPairs(this.schema, model, relationField);
@@ -1366,7 +1555,7 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
1366
1555
  }
1367
1556
  buildJsonObject(eb, value) {
1368
1557
  return eb.fn("json_object", Object.entries(value).flatMap(([key, value2]) => [
1369
- import_kysely3.sql.lit(key),
1558
+ import_kysely4.sql.lit(key),
1370
1559
  value2
1371
1560
  ]));
1372
1561
  }
@@ -1390,6 +1579,24 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
1390
1579
  get supportInsertWithDefault() {
1391
1580
  return false;
1392
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
+ }
1393
1600
  };
1394
1601
 
1395
1602
  // src/client/crud/dialects/index.ts
@@ -1399,8 +1606,8 @@ function getCrudDialect(schema, options) {
1399
1606
  __name(getCrudDialect, "getCrudDialect");
1400
1607
 
1401
1608
  // src/utils/default-operation-node-visitor.ts
1402
- var import_kysely4 = require("kysely");
1403
- var DefaultOperationNodeVisitor = class extends import_kysely4.OperationNodeVisitor {
1609
+ var import_kysely5 = require("kysely");
1610
+ var DefaultOperationNodeVisitor = class extends import_kysely5.OperationNodeVisitor {
1404
1611
  static {
1405
1612
  __name(this, "DefaultOperationNodeVisitor");
1406
1613
  }
@@ -1717,12 +1924,12 @@ var ColumnCollector = class extends DefaultOperationNodeVisitor {
1717
1924
  };
1718
1925
 
1719
1926
  // src/plugins/policy/expression-transformer.ts
1720
- var import_common_helpers5 = require("@zenstackhq/common-helpers");
1721
- var import_kysely6 = require("kysely");
1927
+ var import_common_helpers6 = require("@zenstackhq/common-helpers");
1928
+ var import_kysely7 = require("kysely");
1722
1929
  var import_ts_pattern7 = require("ts-pattern");
1723
1930
 
1724
1931
  // src/plugins/policy/expression-evaluator.ts
1725
- var import_common_helpers4 = require("@zenstackhq/common-helpers");
1932
+ var import_common_helpers5 = require("@zenstackhq/common-helpers");
1726
1933
  var import_ts_pattern6 = require("ts-pattern");
1727
1934
  var ExpressionEvaluator = class {
1728
1935
  static {
@@ -1766,18 +1973,18 @@ var ExpressionEvaluator = class {
1766
1973
  const right = this.evaluate(expr2.right, context);
1767
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", () => {
1768
1975
  const _right = right ?? [];
1769
- (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');
1770
1977
  return _right.includes(left);
1771
1978
  }).exhaustive();
1772
1979
  }
1773
1980
  evaluateCollectionPredicate(expr2, context) {
1774
1981
  const op = expr2.op;
1775
- (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');
1776
1983
  const left = this.evaluate(expr2.left, context);
1777
1984
  if (!left) {
1778
1985
  return false;
1779
1986
  }
1780
- (0, import_common_helpers4.invariant)(Array.isArray(left), "expected array");
1987
+ (0, import_common_helpers5.invariant)(Array.isArray(left), "expected array");
1781
1988
  return (0, import_ts_pattern6.match)(op).with("?", () => left.some((item) => this.evaluate(expr2.right, {
1782
1989
  ...context,
1783
1990
  thisValue: item
@@ -1792,24 +1999,30 @@ var ExpressionEvaluator = class {
1792
1999
  };
1793
2000
 
1794
2001
  // src/plugins/policy/utils.ts
1795
- var import_kysely5 = require("kysely");
2002
+ var import_kysely6 = require("kysely");
1796
2003
  function trueNode(dialect) {
1797
- return import_kysely5.ValueNode.createImmediate(dialect.transformPrimitive(true, "Boolean", false));
2004
+ return import_kysely6.ValueNode.createImmediate(dialect.transformPrimitive(true, "Boolean", false));
1798
2005
  }
1799
2006
  __name(trueNode, "trueNode");
1800
2007
  function falseNode(dialect) {
1801
- return import_kysely5.ValueNode.createImmediate(dialect.transformPrimitive(false, "Boolean", false));
2008
+ return import_kysely6.ValueNode.createImmediate(dialect.transformPrimitive(false, "Boolean", false));
1802
2009
  }
1803
2010
  __name(falseNode, "falseNode");
1804
2011
  function isTrueNode(node) {
1805
- 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);
1806
2013
  }
1807
2014
  __name(isTrueNode, "isTrueNode");
1808
2015
  function isFalseNode(node) {
1809
- 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);
1810
2017
  }
1811
2018
  __name(isFalseNode, "isFalseNode");
1812
2019
  function conjunction(dialect, nodes) {
2020
+ if (nodes.length === 0) {
2021
+ return trueNode(dialect);
2022
+ }
2023
+ if (nodes.length === 1) {
2024
+ return nodes[0];
2025
+ }
1813
2026
  if (nodes.some(isFalseNode)) {
1814
2027
  return falseNode(dialect);
1815
2028
  }
@@ -1817,10 +2030,16 @@ function conjunction(dialect, nodes) {
1817
2030
  if (items.length === 0) {
1818
2031
  return trueNode(dialect);
1819
2032
  }
1820
- return items.reduce((acc, node) => import_kysely5.OrNode.is(node) ? import_kysely5.AndNode.create(acc, import_kysely5.ParensNode.create(node)) : import_kysely5.AndNode.create(acc, node));
2033
+ return items.reduce((acc, node) => import_kysely6.AndNode.create(wrapParensIf(acc, import_kysely6.OrNode.is), wrapParensIf(node, import_kysely6.OrNode.is)));
1821
2034
  }
1822
2035
  __name(conjunction, "conjunction");
1823
2036
  function disjunction(dialect, nodes) {
2037
+ if (nodes.length === 0) {
2038
+ return falseNode(dialect);
2039
+ }
2040
+ if (nodes.length === 1) {
2041
+ return nodes[0];
2042
+ }
1824
2043
  if (nodes.some(isTrueNode)) {
1825
2044
  return trueNode(dialect);
1826
2045
  }
@@ -1828,26 +2047,36 @@ function disjunction(dialect, nodes) {
1828
2047
  if (items.length === 0) {
1829
2048
  return falseNode(dialect);
1830
2049
  }
1831
- return items.reduce((acc, node) => import_kysely5.AndNode.is(node) ? import_kysely5.OrNode.create(acc, import_kysely5.ParensNode.create(node)) : import_kysely5.OrNode.create(acc, node));
2050
+ return items.reduce((acc, node) => import_kysely6.OrNode.create(wrapParensIf(acc, import_kysely6.AndNode.is), wrapParensIf(node, import_kysely6.AndNode.is)));
1832
2051
  }
1833
2052
  __name(disjunction, "disjunction");
1834
- function logicalNot(node) {
1835
- return import_kysely5.UnaryOperationNode.create(import_kysely5.OperatorNode.create("not"), import_kysely5.AndNode.is(node) || import_kysely5.OrNode.is(node) ? import_kysely5.ParensNode.create(node) : node);
2053
+ function logicalNot(dialect, node) {
2054
+ if (isTrueNode(node)) {
2055
+ return falseNode(dialect);
2056
+ }
2057
+ if (isFalseNode(node)) {
2058
+ return trueNode(dialect);
2059
+ }
2060
+ return import_kysely6.UnaryOperationNode.create(import_kysely6.OperatorNode.create("not"), wrapParensIf(node, (n) => import_kysely6.AndNode.is(n) || import_kysely6.OrNode.is(n)));
1836
2061
  }
1837
2062
  __name(logicalNot, "logicalNot");
2063
+ function wrapParensIf(node, predicate) {
2064
+ return predicate(node) ? import_kysely6.ParensNode.create(node) : node;
2065
+ }
2066
+ __name(wrapParensIf, "wrapParensIf");
1838
2067
  function buildIsFalse(node, dialect) {
1839
2068
  if (isFalseNode(node)) {
1840
2069
  return trueNode(dialect);
1841
2070
  } else if (isTrueNode(node)) {
1842
2071
  return falseNode(dialect);
1843
2072
  }
1844
- return import_kysely5.BinaryOperationNode.create(
2073
+ return import_kysely6.BinaryOperationNode.create(
1845
2074
  // coalesce so null is treated as false
1846
- import_kysely5.FunctionNode.create("coalesce", [
2075
+ import_kysely6.FunctionNode.create("coalesce", [
1847
2076
  node,
1848
2077
  falseNode(dialect)
1849
2078
  ]),
1850
- import_kysely5.OperatorNode.create("="),
2079
+ import_kysely6.OperatorNode.create("="),
1851
2080
  falseNode(dialect)
1852
2081
  );
1853
2082
  }
@@ -1856,11 +2085,11 @@ function getTableName(node) {
1856
2085
  if (!node) {
1857
2086
  return node;
1858
2087
  }
1859
- if (import_kysely5.TableNode.is(node)) {
2088
+ if (import_kysely6.TableNode.is(node)) {
1860
2089
  return node.table.identifier.name;
1861
- } else if (import_kysely5.AliasNode.is(node)) {
2090
+ } else if (import_kysely6.AliasNode.is(node)) {
1862
2091
  return getTableName(node.node);
1863
- } else if (import_kysely5.ReferenceNode.is(node) && node.table) {
2092
+ } else if (import_kysely6.ReferenceNode.is(node) && node.table) {
1864
2093
  return getTableName(node.table);
1865
2094
  }
1866
2095
  return void 0;
@@ -1893,16 +2122,21 @@ var ExpressionTransformer = class {
1893
2122
  static {
1894
2123
  __name(this, "ExpressionTransformer");
1895
2124
  }
1896
- schema;
1897
- clientOptions;
1898
- auth;
2125
+ client;
1899
2126
  dialect;
1900
- constructor(schema, clientOptions, auth) {
1901
- this.schema = schema;
1902
- this.clientOptions = clientOptions;
1903
- this.auth = auth;
2127
+ constructor(client) {
2128
+ this.client = client;
1904
2129
  this.dialect = getCrudDialect(this.schema, this.clientOptions);
1905
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
+ }
1906
2140
  get authType() {
1907
2141
  if (!this.schema.authType) {
1908
2142
  throw new InternalError('Schema does not have an "authType" specified');
@@ -1920,16 +2154,12 @@ var ExpressionTransformer = class {
1920
2154
  return this.transformValue(expr2.value, typeof expr2.value === "string" ? "String" : typeof expr2.value === "boolean" ? "Boolean" : "Int");
1921
2155
  }
1922
2156
  _array(expr2, context) {
1923
- 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)));
1924
2158
  }
1925
2159
  _field(expr2, context) {
1926
2160
  const fieldDef = requireField(this.schema, context.model, expr2.field);
1927
2161
  if (!fieldDef.relation) {
1928
- if (context.thisEntity) {
1929
- return context.thisEntity[expr2.field];
1930
- } else {
1931
- return this.createColumnRef(expr2.field, context);
1932
- }
2162
+ return this.createColumnRef(expr2.field, context);
1933
2163
  } else {
1934
2164
  const { memberFilter, memberSelect, ...restContext } = context;
1935
2165
  const relation = this.transformRelationAccess(expr2.field, fieldDef.type, restContext);
@@ -1944,18 +2174,18 @@ var ExpressionTransformer = class {
1944
2174
  }
1945
2175
  mergeWhere(where, memberFilter) {
1946
2176
  if (!where) {
1947
- return import_kysely6.WhereNode.create(memberFilter ?? trueNode(this.dialect));
2177
+ return import_kysely7.WhereNode.create(memberFilter ?? trueNode(this.dialect));
1948
2178
  }
1949
2179
  if (!memberFilter) {
1950
2180
  return where;
1951
2181
  }
1952
- return import_kysely6.WhereNode.create(conjunction(this.dialect, [
2182
+ return import_kysely7.WhereNode.create(conjunction(this.dialect, [
1953
2183
  where.where,
1954
2184
  memberFilter
1955
2185
  ]));
1956
2186
  }
1957
2187
  _null() {
1958
- return import_kysely6.ValueNode.createImmediate(null);
2188
+ return import_kysely7.ValueNode.createImmediate(null);
1959
2189
  }
1960
2190
  _binary(expr2, context) {
1961
2191
  if (expr2.op === "&&") {
@@ -1970,51 +2200,88 @@ var ExpressionTransformer = class {
1970
2200
  ]);
1971
2201
  }
1972
2202
  if (this.isAuthCall(expr2.left) || this.isAuthCall(expr2.right)) {
1973
- return this.transformAuthBinary(expr2);
2203
+ return this.transformAuthBinary(expr2, context);
1974
2204
  }
1975
2205
  const op = expr2.op;
1976
2206
  if (op === "?" || op === "!" || op === "^") {
1977
2207
  return this.transformCollectionPredicate(expr2, context);
1978
2208
  }
1979
- const left = this.transform(expr2.left, context);
1980
- 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);
1981
2212
  if (op === "in") {
1982
2213
  if (this.isNullNode(left)) {
1983
2214
  return this.transformValue(false, "Boolean");
1984
2215
  } else {
1985
- if (import_kysely6.ValueListNode.is(right)) {
1986
- 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);
1987
2218
  } else {
1988
- 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", [
1989
2220
  right
1990
2221
  ]));
1991
2222
  }
1992
2223
  }
1993
2224
  }
1994
2225
  if (this.isNullNode(right)) {
1995
- 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);
1996
2227
  } else if (this.isNullNode(left)) {
1997
- 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);
1998
2231
  }
1999
- 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
+ };
2000
2266
  }
2001
2267
  transformCollectionPredicate(expr2, context) {
2002
- (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');
2003
2269
  if (this.isAuthCall(expr2.left) || this.isAuthMember(expr2.left)) {
2004
2270
  const value = new ExpressionEvaluator().evaluate(expr2, {
2005
2271
  auth: this.auth
2006
2272
  });
2007
2273
  return this.transformValue(value, "Boolean");
2008
2274
  }
2009
- (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");
2010
2276
  let newContextModel;
2011
- if (ExpressionUtils.isField(expr2.left)) {
2012
- 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)}`);
2013
2280
  newContextModel = fieldDef.type;
2014
2281
  } else {
2015
- (0, import_common_helpers5.invariant)(ExpressionUtils.isField(expr2.left.receiver));
2016
- const fieldDef = requireField(this.schema, context.model, expr2.left.receiver.field);
2017
- 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;
2018
2285
  for (const member of expr2.left.members) {
2019
2286
  const memberDef = requireField(this.schema, newContextModel, member);
2020
2287
  newContextModel = memberDef.type;
@@ -2023,71 +2290,118 @@ var ExpressionTransformer = class {
2023
2290
  let predicateFilter = this.transform(expr2.right, {
2024
2291
  ...context,
2025
2292
  model: newContextModel,
2026
- alias: void 0,
2027
- thisEntity: void 0
2293
+ alias: void 0
2028
2294
  });
2029
2295
  if (expr2.op === "!") {
2030
- predicateFilter = logicalNot(predicateFilter);
2296
+ predicateFilter = logicalNot(this.dialect, predicateFilter);
2031
2297
  }
2032
- const count = import_kysely6.FunctionNode.create("count", [
2033
- import_kysely6.ValueNode.createImmediate(1)
2298
+ const count = import_kysely7.FunctionNode.create("count", [
2299
+ import_kysely7.ValueNode.createImmediate(1)
2034
2300
  ]);
2035
- 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();
2036
2302
  return this.transform(expr2.left, {
2037
2303
  ...context,
2038
- 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"))),
2039
2305
  memberFilter: predicateFilter
2040
2306
  });
2041
2307
  }
2042
- transformAuthBinary(expr2) {
2308
+ transformAuthBinary(expr2, context) {
2043
2309
  if (expr2.op !== "==" && expr2.op !== "!=") {
2044
- throw new Error(`Unsupported operator for auth call: ${expr2.op}`);
2310
+ throw new QueryError(`Unsupported operator for \`auth()\` in policy of model "${context.model}": ${expr2.op}`);
2045
2311
  }
2312
+ let authExpr;
2046
2313
  let other;
2047
2314
  if (this.isAuthCall(expr2.left)) {
2315
+ authExpr = expr2.left;
2048
2316
  other = expr2.right;
2049
2317
  } else {
2318
+ authExpr = expr2.right;
2050
2319
  other = expr2.left;
2051
2320
  }
2052
2321
  if (ExpressionUtils.isNull(other)) {
2053
2322
  return this.transformValue(expr2.op === "==" ? !this.auth : !!this.auth, "Boolean");
2054
2323
  } else {
2055
- throw new Error("Unsupported binary expression with `auth()`");
2324
+ const authModel = getModel(this.schema, this.authType);
2325
+ if (!authModel) {
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`);
2327
+ }
2328
+ const idFields = Object.values(authModel.fields).filter((f) => f.id).map((f) => f.name);
2329
+ (0, import_common_helpers6.invariant)(idFields.length > 0, "auth type model must have at least one id field");
2330
+ const conditions = idFields.map((fieldName) => ExpressionUtils.binary(ExpressionUtils.member(authExpr, [
2331
+ fieldName
2332
+ ]), "==", this.makeOrAppendMember(other, fieldName)));
2333
+ let result = this.buildAnd(conditions);
2334
+ if (expr2.op === "!=") {
2335
+ result = this.buildLogicalNot(result);
2336
+ }
2337
+ return this.transform(result, context);
2338
+ }
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
+ ]);
2056
2350
  }
2057
2351
  }
2058
2352
  transformValue(value, type) {
2059
- 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
+ }
2060
2360
  }
2061
2361
  _unary(expr2, context) {
2062
- (0, import_common_helpers5.invariant)(expr2.op === "!", 'only "!" operator is supported');
2063
- return import_kysely6.BinaryOperationNode.create(this.transform(expr2.operand, context), this.transformOperator("!="), trueNode(this.dialect));
2362
+ (0, import_common_helpers6.invariant)(expr2.op === "!", 'only "!" operator is supported');
2363
+ return logicalNot(this.dialect, this.transform(expr2.operand, context));
2064
2364
  }
2065
2365
  transformOperator(op) {
2066
2366
  const mappedOp = (0, import_ts_pattern7.match)(op).with("==", () => "=").otherwise(() => op);
2067
- return import_kysely6.OperatorNode.create(mappedOp);
2367
+ return import_kysely7.OperatorNode.create(mappedOp);
2068
2368
  }
2069
2369
  _call(expr2, context) {
2070
2370
  const result = this.transformCall(expr2, context);
2071
2371
  return result.toOperationNode();
2072
2372
  }
2073
2373
  transformCall(expr2, context) {
2074
- const func = this.clientOptions.functions?.[expr2.function];
2374
+ const func = this.getFunctionImpl(expr2.function);
2075
2375
  if (!func) {
2076
2376
  throw new QueryError(`Function not implemented: ${expr2.function}`);
2077
2377
  }
2078
- const eb = (0, import_kysely6.expressionBuilder)();
2378
+ const eb = (0, import_kysely7.expressionBuilder)();
2079
2379
  return func(eb, (expr2.args ?? []).map((arg) => this.transformCallArg(eb, arg, context)), {
2380
+ client: this.client,
2080
2381
  dialect: this.dialect,
2081
2382
  model: context.model,
2383
+ modelAlias: context.alias ?? context.model,
2082
2384
  operation: context.operation
2083
2385
  });
2084
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
+ }
2085
2399
  transformCallArg(eb, arg, context) {
2086
2400
  if (ExpressionUtils.isLiteral(arg)) {
2087
2401
  return eb.val(arg.value);
2088
2402
  }
2089
2403
  if (ExpressionUtils.isField(arg)) {
2090
- return context.thisEntityRaw ? eb.val(context.thisEntityRaw[arg.field]) : eb.ref(arg.field);
2404
+ return eb.ref(arg.field);
2091
2405
  }
2092
2406
  if (ExpressionUtils.isCall(arg)) {
2093
2407
  return this.transformCall(arg, context);
@@ -2102,14 +2416,32 @@ var ExpressionTransformer = class {
2102
2416
  if (this.isAuthCall(expr2.receiver)) {
2103
2417
  return this.valueMemberAccess(this.auth, expr2, this.authType);
2104
2418
  }
2105
- (0, import_common_helpers5.invariant)(ExpressionUtils.isField(expr2.receiver), "expect receiver to be field expression");
2419
+ (0, import_common_helpers6.invariant)(ExpressionUtils.isField(expr2.receiver) || ExpressionUtils.isThis(expr2.receiver), 'expect receiver to be field expression or "this"');
2420
+ let members = expr2.members;
2421
+ let receiver;
2106
2422
  const { memberFilter, memberSelect, ...restContext } = context;
2107
- const receiver = this.transform(expr2.receiver, restContext);
2108
- (0, import_common_helpers5.invariant)(import_kysely6.SelectQueryNode.is(receiver), "expected receiver to be select query");
2109
- const receiverField = requireField(this.schema, context.model, expr2.receiver.field);
2423
+ if (ExpressionUtils.isThis(expr2.receiver)) {
2424
+ if (expr2.members.length === 1) {
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);
2430
+ }
2431
+ } else {
2432
+ receiver = this.transform(expr2.receiver, restContext);
2433
+ }
2434
+ (0, import_common_helpers6.invariant)(import_kysely7.SelectQueryNode.is(receiver), "expected receiver to be select query");
2435
+ let startType;
2436
+ if (ExpressionUtils.isField(expr2.receiver)) {
2437
+ const receiverField = requireField(this.schema, context.model, expr2.receiver.field);
2438
+ startType = receiverField.type;
2439
+ } else {
2440
+ startType = context.model;
2441
+ }
2110
2442
  const memberFields = [];
2111
- let currType = receiverField.type;
2112
- for (const member of expr2.members) {
2443
+ let currType = startType;
2444
+ for (const member of members) {
2113
2445
  const fieldDef = requireField(this.schema, currType, member);
2114
2446
  memberFields.push({
2115
2447
  fieldDef,
@@ -2118,22 +2450,21 @@ var ExpressionTransformer = class {
2118
2450
  currType = fieldDef.type;
2119
2451
  }
2120
2452
  let currNode = void 0;
2121
- for (let i = expr2.members.length - 1; i >= 0; i--) {
2122
- const member = expr2.members[i];
2453
+ for (let i = members.length - 1; i >= 0; i--) {
2454
+ const member = members[i];
2123
2455
  const { fieldDef, fromModel } = memberFields[i];
2124
2456
  if (fieldDef.relation) {
2125
2457
  const relation = this.transformRelationAccess(member, fieldDef.type, {
2126
2458
  ...restContext,
2127
2459
  model: fromModel,
2128
- alias: void 0,
2129
- thisEntity: void 0
2460
+ alias: void 0
2130
2461
  });
2131
2462
  if (currNode) {
2132
- (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");
2133
2464
  currNode = {
2134
2465
  ...relation,
2135
2466
  selections: [
2136
- import_kysely6.SelectionNode.create(import_kysely6.AliasNode.create(currNode, import_kysely6.IdentifierNode.create(expr2.members[i + 1])))
2467
+ import_kysely7.SelectionNode.create(import_kysely7.AliasNode.create(currNode, import_kysely7.IdentifierNode.create(members[i + 1])))
2137
2468
  ]
2138
2469
  };
2139
2470
  } else {
@@ -2146,21 +2477,21 @@ var ExpressionTransformer = class {
2146
2477
  };
2147
2478
  }
2148
2479
  } else {
2149
- (0, import_common_helpers5.invariant)(i === expr2.members.length - 1, "plain field access must be the last segment");
2150
- (0, import_common_helpers5.invariant)(!currNode, "plain field access must be the last segment");
2151
- 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);
2152
2483
  }
2153
2484
  }
2154
2485
  return {
2155
2486
  ...receiver,
2156
2487
  selections: [
2157
- 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")))
2158
2489
  ]
2159
2490
  };
2160
2491
  }
2161
2492
  valueMemberAccess(receiver, expr2, receiverType) {
2162
2493
  if (!receiver) {
2163
- return import_kysely6.ValueNode.createImmediate(null);
2494
+ return import_kysely7.ValueNode.createImmediate(null);
2164
2495
  }
2165
2496
  if (expr2.members.length !== 1) {
2166
2497
  throw new Error(`Only single member access is supported`);
@@ -2171,40 +2502,33 @@ var ExpressionTransformer = class {
2171
2502
  return this.transformValue(fieldValue, fieldDef.type);
2172
2503
  }
2173
2504
  transformRelationAccess(field, relationModel, context) {
2505
+ const m2m = getManyToManyRelation(this.schema, context.model, field);
2506
+ if (m2m) {
2507
+ return this.transformManyToManyRelationAccess(m2m, context);
2508
+ }
2174
2509
  const fromModel = context.model;
2175
2510
  const { keyPairs, ownedByModel } = getRelationForeignKeyFieldPairs(this.schema, fromModel, field);
2176
- if (context.thisEntity) {
2177
- let condition;
2178
- if (ownedByModel) {
2179
- 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(relationModel)), import_kysely6.OperatorNode.create("="), context.thisEntity[fk])));
2180
- } else {
2181
- 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(relationModel)), import_kysely6.OperatorNode.create("="), context.thisEntity[pk])));
2182
- }
2183
- return {
2184
- kind: "SelectQueryNode",
2185
- from: import_kysely6.FromNode.create([
2186
- import_kysely6.TableNode.create(relationModel)
2187
- ]),
2188
- where: import_kysely6.WhereNode.create(condition)
2189
- };
2511
+ let condition;
2512
+ if (ownedByModel) {
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)))));
2190
2514
  } else {
2191
- let condition;
2192
- if (ownedByModel) {
2193
- 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)))));
2194
- } else {
2195
- 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)))));
2196
- }
2197
- return {
2198
- kind: "SelectQueryNode",
2199
- from: import_kysely6.FromNode.create([
2200
- import_kysely6.TableNode.create(relationModel)
2201
- ]),
2202
- where: import_kysely6.WhereNode.create(condition)
2203
- };
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)))));
2204
2516
  }
2517
+ return {
2518
+ kind: "SelectQueryNode",
2519
+ from: import_kysely7.FromNode.create([
2520
+ import_kysely7.TableNode.create(relationModel)
2521
+ ]),
2522
+ where: import_kysely7.WhereNode.create(condition)
2523
+ };
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();
2205
2529
  }
2206
2530
  createColumnRef(column, context) {
2207
- 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));
2208
2532
  }
2209
2533
  isAuthCall(value) {
2210
2534
  return ExpressionUtils.isCall(value) && value.function === "auth";
@@ -2213,7 +2537,32 @@ var ExpressionTransformer = class {
2213
2537
  return ExpressionUtils.isMember(expr2) && this.isAuthCall(expr2.receiver);
2214
2538
  }
2215
2539
  isNullNode(node) {
2216
- return import_kysely6.ValueNode.is(node) && node.value === null;
2540
+ return import_kysely7.ValueNode.is(node) && node.value === null;
2541
+ }
2542
+ buildLogicalNot(result) {
2543
+ return ExpressionUtils.unary("!", result);
2544
+ }
2545
+ buildAnd(conditions) {
2546
+ if (conditions.length === 0) {
2547
+ return ExpressionUtils.literal(true);
2548
+ } else if (conditions.length === 1) {
2549
+ return conditions[0];
2550
+ } else {
2551
+ return conditions.reduce((acc, condition) => ExpressionUtils.binary(acc, "&&", condition));
2552
+ }
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
+ }
2217
2566
  }
2218
2567
  };
2219
2568
  _ts_decorate([
@@ -2286,7 +2635,7 @@ _ts_decorate([
2286
2635
  ], ExpressionTransformer.prototype, "_member", null);
2287
2636
 
2288
2637
  // src/plugins/policy/policy-handler.ts
2289
- var PolicyHandler = class extends import_kysely7.OperationNodeTransformer {
2638
+ var PolicyHandler = class extends import_kysely8.OperationNodeTransformer {
2290
2639
  static {
2291
2640
  __name(this, "PolicyHandler");
2292
2641
  }
@@ -2301,111 +2650,296 @@ var PolicyHandler = class extends import_kysely7.OperationNodeTransformer {
2301
2650
  }
2302
2651
  async handle(node, proceed) {
2303
2652
  if (!this.isCrudQueryNode(node)) {
2304
- 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");
2305
2654
  }
2306
2655
  if (!this.isMutationQueryNode(node)) {
2307
2656
  return proceed(this.transformNode(node));
2308
2657
  }
2309
- let mutationRequiresTransaction = false;
2310
- const mutationModel = this.getMutationModel(node);
2311
- if (import_kysely7.InsertQueryNode.is(node)) {
2312
- const constCondition = this.tryGetConstantPolicy(mutationModel, "create");
2313
- if (constCondition === false) {
2314
- throw new RejectedByPolicyError(mutationModel);
2315
- } else if (constCondition === void 0) {
2316
- 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);
2317
2672
  }
2318
2673
  }
2319
- if (!mutationRequiresTransaction && !node.returning) {
2320
- return proceed(this.transformNode(node));
2321
- }
2322
- if (import_kysely7.InsertQueryNode.is(node)) {
2323
- await this.enforcePreCreatePolicy(node, proceed);
2324
- }
2325
- const transformedNode = this.transformNode(node);
2326
- const result = await proceed(transformedNode);
2327
- if (!this.onlyReturningId(node)) {
2674
+ const result = await proceed(this.transformNode(node));
2675
+ if (!node.returning || this.onlyReturningId(node)) {
2676
+ return result;
2677
+ } else {
2328
2678
  const readBackResult = await this.processReadBack(node, result, proceed);
2329
2679
  if (readBackResult.rows.length !== result.rows.length) {
2330
- 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");
2331
2681
  }
2332
2682
  return readBackResult;
2333
- } 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)) {
2334
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
+ };
2335
2762
  }
2336
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
+ }
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
+ };
2805
+ }
2806
+ // #endregion
2807
+ // #region helpers
2337
2808
  onlyReturningId(node) {
2338
2809
  if (!node.returning) {
2339
2810
  return true;
2340
2811
  }
2341
- const idFields = getIdFields(this.client.$schema, this.getMutationModel(node));
2812
+ const { mutationModel } = this.getMutationModel(node);
2813
+ const idFields = requireIdFields(this.client.$schema, mutationModel);
2342
2814
  const collector = new ColumnCollector();
2343
2815
  const selectedColumns = collector.collect(node.returning);
2344
2816
  return selectedColumns.every((c) => idFields.includes(c));
2345
2817
  }
2346
- async enforcePreCreatePolicy(node, proceed) {
2347
- if (!node.columns || !node.values) {
2348
- return;
2349
- }
2350
- const model = this.getMutationModel(node);
2351
- const fields = node.columns.map((c) => c.column.name);
2352
- const valueRows = this.unwrapCreateValueRows(node.values, model, fields);
2818
+ async enforcePreCreatePolicy(node, mutationModel, isManyToManyJoinTable, proceed) {
2819
+ const fields = node.columns?.map((c) => c.column.name) ?? [];
2820
+ const valueRows = node.values ? this.unwrapCreateValueRows(node.values, mutationModel, fields, isManyToManyJoinTable) : [
2821
+ []
2822
+ ];
2353
2823
  for (const values of valueRows) {
2354
- await this.enforcePreCreatePolicyForOne(model, fields, values.map((v) => v.node), values.map((v) => v.raw), 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
+ }
2355
2829
  }
2356
2830
  }
2357
- async enforcePreCreatePolicyForOne(model, fields, values, valuesRaw, proceed) {
2358
- const thisEntity = {};
2359
- const thisEntityRaw = {};
2360
- for (let i = 0; i < fields.length; i++) {
2361
- thisEntity[fields[i]] = values[i];
2362
- thisEntityRaw[fields[i]] = valuesRaw[i];
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`);
2363
2862
  }
2364
- const filter = this.buildPolicyFilter(model, void 0, "create", thisEntity, thisEntityRaw);
2863
+ }
2864
+ async enforcePreCreatePolicyForOne(model, fields, values, proceed) {
2865
+ const allFields = Object.entries(requireModel(this.client.$schema, model).fields).filter(([, def]) => !def.relation);
2866
+ const allValues = [];
2867
+ for (const [name, _def] of allFields) {
2868
+ const index = fields.indexOf(name);
2869
+ if (index >= 0) {
2870
+ allValues.push(values[index]);
2871
+ } else {
2872
+ allValues.push(import_kysely8.ValueNode.createImmediate(null));
2873
+ }
2874
+ }
2875
+ const eb = (0, import_kysely8.expressionBuilder)();
2876
+ const constTable = {
2877
+ kind: "SelectQueryNode",
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"))
2882
+ ]),
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
+ })
2887
+ };
2888
+ const filter = this.buildPolicyFilter(model, void 0, "create");
2365
2889
  const preCreateCheck = {
2366
2890
  kind: "SelectQueryNode",
2891
+ from: import_kysely8.FromNode.create([
2892
+ import_kysely8.AliasNode.create(constTable, import_kysely8.IdentifierNode.create(model))
2893
+ ]),
2367
2894
  selections: [
2368
- import_kysely7.SelectionNode.create(import_kysely7.AliasNode.create(filter, import_kysely7.IdentifierNode.create("$condition")))
2369
- ]
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")))
2898
+ ],
2899
+ where: import_kysely8.WhereNode.create(filter)
2370
2900
  };
2371
2901
  const result = await proceed(preCreateCheck);
2372
2902
  if (!result.rows[0]?.$condition) {
2373
2903
  throw new RejectedByPolicyError(model);
2374
2904
  }
2375
2905
  }
2376
- unwrapCreateValueRows(node, model, fields) {
2377
- if (import_kysely7.ValuesNode.is(node)) {
2378
- return node.values.map((v) => this.unwrapCreateValueRow(v.values, model, fields));
2379
- } 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)) {
2380
2910
  return [
2381
- this.unwrapCreateValueRow(node.values, model, fields)
2911
+ this.unwrapCreateValueRow(node.values, model, fields, isManyToManyJoinTable)
2382
2912
  ];
2383
2913
  } else {
2384
2914
  throw new InternalError(`Unexpected node kind: ${node.kind} for unwrapping create values`);
2385
2915
  }
2386
2916
  }
2387
- unwrapCreateValueRow(data, model, fields) {
2388
- (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");
2389
2919
  const result = [];
2390
2920
  for (let i = 0; i < data.length; i++) {
2391
2921
  const item = data[i];
2392
- const fieldDef = requireField(this.client.$schema, model, fields[i]);
2393
2922
  if (typeof item === "object" && item && "kind" in item) {
2394
- (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");
2395
2925
  result.push({
2396
- 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)),
2397
2927
  raw: item.value
2398
2928
  });
2399
2929
  } else {
2400
- 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
+ }
2401
2935
  if (Array.isArray(value)) {
2402
2936
  result.push({
2403
- node: import_kysely7.RawNode.createWithSql(this.dialect.buildArrayLiteralSQL(value)),
2937
+ node: import_kysely8.RawNode.createWithSql(this.dialect.buildArrayLiteralSQL(value)),
2404
2938
  raw: value
2405
2939
  });
2406
2940
  } else {
2407
2941
  result.push({
2408
- node: import_kysely7.ValueNode.create(value),
2942
+ node: import_kysely8.ValueNode.create(value),
2409
2943
  raw: value
2410
2944
  });
2411
2945
  }
@@ -2441,18 +2975,15 @@ var PolicyHandler = class extends import_kysely7.OperationNodeTransformer {
2441
2975
  if (!this.isMutationQueryNode(node) || !node.returning) {
2442
2976
  return result;
2443
2977
  }
2444
- const table = this.getMutationModel(node);
2445
- if (!table) {
2446
- throw new InternalError(`Unable to get table name for query node: ${node}`);
2447
- }
2448
- const idConditions = this.buildIdConditions(table, result.rows);
2449
- 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");
2450
2981
  const select = {
2451
2982
  kind: "SelectQueryNode",
2452
- from: import_kysely7.FromNode.create([
2453
- import_kysely7.TableNode.create(table)
2983
+ from: import_kysely8.FromNode.create([
2984
+ import_kysely8.TableNode.create(mutationModel)
2454
2985
  ]),
2455
- where: import_kysely7.WhereNode.create(conjunction(this.dialect, [
2986
+ where: import_kysely8.WhereNode.create(conjunction(this.dialect, [
2456
2987
  idConditions,
2457
2988
  policyFilter
2458
2989
  ])),
@@ -2462,15 +2993,31 @@ var PolicyHandler = class extends import_kysely7.OperationNodeTransformer {
2462
2993
  return selectResult;
2463
2994
  }
2464
2995
  buildIdConditions(table, rows) {
2465
- const idFields = getIdFields(this.client.$schema, table);
2466
- 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]))))));
2467
2998
  }
2468
2999
  getMutationModel(node) {
2469
- 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) => {
2470
3013
  if (node2.from.froms.length !== 1) {
2471
- throw new InternalError("Only one from table is supported for delete");
3014
+ throw new QueryError("Only one from table is supported for delete");
2472
3015
  }
2473
- 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;
2474
3021
  }).exhaustive();
2475
3022
  if (!r) {
2476
3023
  throw new InternalError(`Unable to get table name for query node: ${node}`);
@@ -2478,18 +3025,22 @@ var PolicyHandler = class extends import_kysely7.OperationNodeTransformer {
2478
3025
  return r;
2479
3026
  }
2480
3027
  isCrudQueryNode(node) {
2481
- 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);
2482
3029
  }
2483
3030
  isMutationQueryNode(node) {
2484
- 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);
2485
3032
  }
2486
- buildPolicyFilter(model, alias, operation, thisEntity, thisEntityRaw) {
3033
+ buildPolicyFilter(model, alias, operation) {
3034
+ const m2mFilter = this.getModelPolicyFilterForManyToManyJoinTable(model, alias, operation);
3035
+ if (m2mFilter) {
3036
+ return m2mFilter;
3037
+ }
2487
3038
  const policies = this.getModelPolicies(model, operation);
2488
3039
  if (policies.length === 0) {
2489
3040
  return falseNode(this.dialect);
2490
3041
  }
2491
- const allows = policies.filter((policy) => policy.kind === "allow").map((policy) => this.transformPolicyCondition(model, alias, operation, policy, thisEntity, thisEntityRaw));
2492
- const denies = policies.filter((policy) => policy.kind === "deny").map((policy) => this.transformPolicyCondition(model, alias, operation, policy, thisEntity, thisEntityRaw));
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));
2493
3044
  let combinedPolicy;
2494
3045
  if (allows.length === 0) {
2495
3046
  combinedPolicy = falseNode(this.dialect);
@@ -2505,102 +3056,59 @@ var PolicyHandler = class extends import_kysely7.OperationNodeTransformer {
2505
3056
  }
2506
3057
  return combinedPolicy;
2507
3058
  }
2508
- transformSelectQuery(node) {
2509
- let whereNode = node.where;
2510
- node.from?.froms.forEach((from) => {
2511
- const extractResult = this.extractTableName(from);
2512
- if (extractResult) {
2513
- const { model, alias } = extractResult;
2514
- const filter = this.buildPolicyFilter(model, alias, "read");
2515
- whereNode = import_kysely7.WhereNode.create(whereNode?.where ? conjunction(this.dialect, [
2516
- whereNode.where,
2517
- filter
2518
- ]) : filter);
2519
- }
2520
- });
2521
- const baseResult = super.transformSelectQuery({
2522
- ...node,
2523
- where: void 0
2524
- });
2525
- return {
2526
- ...baseResult,
2527
- where: whereNode
2528
- };
2529
- }
2530
- transformInsertQuery(node) {
2531
- const result = super.transformInsertQuery(node);
2532
- if (!node.returning) {
2533
- return result;
2534
- }
2535
- if (this.onlyReturningId(node)) {
2536
- return result;
2537
- } else {
2538
- const idFields = getIdFields(this.client.$schema, this.getMutationModel(node));
2539
- return {
2540
- ...result,
2541
- returning: import_kysely7.ReturningNode.create(idFields.map((field) => import_kysely7.SelectionNode.create(import_kysely7.ColumnNode.create(field))))
2542
- };
2543
- }
2544
- }
2545
- transformUpdateQuery(node) {
2546
- const result = super.transformUpdateQuery(node);
2547
- const mutationModel = this.getMutationModel(node);
2548
- const filter = this.buildPolicyFilter(mutationModel, void 0, "update");
2549
- return {
2550
- ...result,
2551
- where: import_kysely7.WhereNode.create(result.where ? conjunction(this.dialect, [
2552
- result.where.where,
2553
- filter
2554
- ]) : filter)
2555
- };
2556
- }
2557
- transformDeleteQuery(node) {
2558
- const result = super.transformDeleteQuery(node);
2559
- const mutationModel = this.getMutationModel(node);
2560
- const filter = this.buildPolicyFilter(mutationModel, void 0, "delete");
2561
- return {
2562
- ...result,
2563
- where: import_kysely7.WhereNode.create(result.where ? conjunction(this.dialect, [
2564
- result.where.where,
2565
- filter
2566
- ]) : filter)
2567
- };
2568
- }
2569
- extractTableName(from) {
2570
- if (import_kysely7.TableNode.is(from)) {
3059
+ extractTableName(node) {
3060
+ if (import_kysely8.TableNode.is(node)) {
2571
3061
  return {
2572
- model: from.table.identifier.name
3062
+ model: node.table.identifier.name
2573
3063
  };
2574
3064
  }
2575
- if (import_kysely7.AliasNode.is(from)) {
2576
- const inner = this.extractTableName(from.node);
3065
+ if (import_kysely8.AliasNode.is(node)) {
3066
+ const inner = this.extractTableName(node.node);
2577
3067
  if (!inner) {
2578
3068
  return void 0;
2579
3069
  }
2580
3070
  return {
2581
3071
  model: inner.model,
2582
- 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
2583
3073
  };
2584
3074
  } else {
2585
3075
  return void 0;
2586
3076
  }
2587
3077
  }
2588
- transformPolicyCondition(model, alias, operation, policy, thisEntity, thisEntityRaw) {
2589
- 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, {
2590
3100
  model,
2591
3101
  alias,
2592
3102
  operation,
2593
- thisEntity,
2594
- thisEntityRaw,
2595
3103
  auth: this.client.$auth
2596
3104
  });
2597
3105
  }
2598
- getModelPolicies(modelName, operation) {
2599
- const modelDef = requireModel(this.client.$schema, modelName);
3106
+ getModelPolicies(model, operation) {
3107
+ const modelDef = requireModel(this.client.$schema, model);
2600
3108
  const result = [];
2601
3109
  const extractOperations = /* @__PURE__ */ __name((expr2) => {
2602
- (0, import_common_helpers6.invariant)(ExpressionUtils.isLiteral(expr2), "expecting a literal");
2603
- (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");
2604
3112
  return expr2.value.split(",").filter((v) => !!v).map((v) => v.trim());
2605
3113
  }, "extractOperations");
2606
3114
  if (modelDef.attributes) {
@@ -2612,8 +3120,84 @@ var PolicyHandler = class extends import_kysely7.OperationNodeTransformer {
2612
3120
  }
2613
3121
  return result;
2614
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
+ }
2615
3175
  };
2616
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
+
2617
3201
  // src/plugins/policy/plugin.ts
2618
3202
  var PolicyPlugin = class {
2619
3203
  static {
@@ -2628,6 +3212,11 @@ var PolicyPlugin = class {
2628
3212
  get description() {
2629
3213
  return "Enforces access policies defined in the schema.";
2630
3214
  }
3215
+ get functions() {
3216
+ return {
3217
+ check
3218
+ };
3219
+ }
2631
3220
  onKyselyQuery({
2632
3221
  query,
2633
3222
  client,
@@ -2645,6 +3234,7 @@ var PolicyPlugin = class {
2645
3234
  // Annotate the CommonJS export names for ESM import in node:
2646
3235
  0 && (module.exports = {
2647
3236
  PolicyPlugin,
2648
- RejectedByPolicyError
3237
+ RejectedByPolicyError,
3238
+ RejectedByPolicyReason
2649
3239
  });
2650
3240
  //# sourceMappingURL=index.cjs.map