@zenstackhq/runtime 3.0.0-alpha.8 → 3.0.0-alpha.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -31,12 +31,17 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  // src/index.ts
32
32
  var src_exports = {};
33
33
  __export(src_exports, {
34
- ZenStackClient: () => ZenStackClient
34
+ InputValidationError: () => InputValidationError,
35
+ InternalError: () => InternalError,
36
+ NotFoundError: () => NotFoundError,
37
+ QueryError: () => QueryError,
38
+ ZenStackClient: () => ZenStackClient,
39
+ definePlugin: () => definePlugin
35
40
  });
36
41
  module.exports = __toCommonJS(src_exports);
37
42
 
38
43
  // src/client/client-impl.ts
39
- var import_common_helpers12 = require("@zenstackhq/common-helpers");
44
+ var import_common_helpers13 = require("@zenstackhq/common-helpers");
40
45
  var import_kysely16 = require("kysely");
41
46
  var import_ts_pattern19 = require("ts-pattern");
42
47
 
@@ -45,12 +50,24 @@ var import_kysely9 = require("kysely");
45
50
  var import_ts_pattern9 = require("ts-pattern");
46
51
 
47
52
  // src/client/errors.ts
53
+ var InputValidationError = class extends Error {
54
+ static {
55
+ __name(this, "InputValidationError");
56
+ }
57
+ constructor(message, cause) {
58
+ super(message, {
59
+ cause
60
+ });
61
+ }
62
+ };
48
63
  var QueryError = class extends Error {
49
64
  static {
50
65
  __name(this, "QueryError");
51
66
  }
52
- constructor(message) {
53
- super(message);
67
+ constructor(message, cause) {
68
+ super(message, {
69
+ cause
70
+ });
54
71
  }
55
72
  };
56
73
  var InternalError = class extends Error {
@@ -78,7 +95,7 @@ __name(getModel, "getModel");
78
95
  function requireModel(schema, model) {
79
96
  const matchedName = Object.keys(schema.models).find((k) => k.toLowerCase() === model.toLowerCase());
80
97
  if (!matchedName) {
81
- throw new QueryError(`Model "${model}" not found`);
98
+ throw new QueryError(`Model "${model}" not found in schema`);
82
99
  }
83
100
  return schema.models[matchedName];
84
101
  }
@@ -203,7 +220,7 @@ function buildFieldRef(schema, model, field, options, eb, modelAlias) {
203
220
  computer = computedFields?.[model]?.[field];
204
221
  }
205
222
  if (!computer) {
206
- throw new QueryError(`Computed field "${field}" implementation not provided`);
223
+ throw new QueryError(`Computed field "${field}" implementation not provided for model "${model}"`);
207
224
  }
208
225
  return computer(eb);
209
226
  }
@@ -314,6 +331,7 @@ var import_cuid2 = require("@paralleldrive/cuid2");
314
331
  var import_common_helpers8 = require("@zenstackhq/common-helpers");
315
332
  var import_kysely8 = require("kysely");
316
333
  var import_nanoid = require("nanoid");
334
+ var import_node_util = require("util");
317
335
  var import_ts_pattern8 = require("ts-pattern");
318
336
  var import_ulid = require("ulid");
319
337
  var uuid = __toESM(require("uuid"), 1);
@@ -373,7 +391,7 @@ var BaseCrudDialect = class {
373
391
  this.schema = schema;
374
392
  this.options = options;
375
393
  }
376
- transformPrimitive(value, _type) {
394
+ transformPrimitive(value, _type, _forArrayField) {
377
395
  return value;
378
396
  }
379
397
  buildFilter(eb, model, modelAlias, where) {
@@ -516,7 +534,7 @@ var BaseCrudDialect = class {
516
534
  if (_value === void 0) {
517
535
  continue;
518
536
  }
519
- const value = this.transformPrimitive(_value, fieldType);
537
+ const value = this.transformPrimitive(_value, fieldType, !!fieldDef.array);
520
538
  switch (key) {
521
539
  case "equals": {
522
540
  clauses.push(this.buildLiteralFilter(eb, fieldRef, fieldType, eb.val(value)));
@@ -554,10 +572,14 @@ var BaseCrudDialect = class {
554
572
  if (isEnum(this.schema, fieldDef.type)) {
555
573
  return this.buildEnumFilter(eb, modelAlias, field, fieldDef, payload);
556
574
  }
557
- return (0, import_ts_pattern.match)(fieldDef.type).with("String", () => this.buildStringFilter(eb, modelAlias, field, payload)).with(import_ts_pattern.P.union("Int", "Float", "Decimal", "BigInt"), (type) => this.buildNumberFilter(eb, model, modelAlias, field, type, payload)).with("Boolean", () => this.buildBooleanFilter(eb, modelAlias, field, payload)).with("DateTime", () => this.buildDateTimeFilter(eb, modelAlias, field, payload)).with("Bytes", () => this.buildBytesFilter(eb, modelAlias, field, payload)).exhaustive();
575
+ return (0, import_ts_pattern.match)(fieldDef.type).with("String", () => this.buildStringFilter(eb, modelAlias, field, payload)).with(import_ts_pattern.P.union("Int", "Float", "Decimal", "BigInt"), (type) => this.buildNumberFilter(eb, model, modelAlias, field, type, payload)).with("Boolean", () => this.buildBooleanFilter(eb, modelAlias, field, payload)).with("DateTime", () => this.buildDateTimeFilter(eb, modelAlias, field, payload)).with("Bytes", () => this.buildBytesFilter(eb, modelAlias, field, payload)).with("Json", () => {
576
+ throw new InternalError("JSON filters are not supported yet");
577
+ }).with("Unsupported", () => {
578
+ throw new QueryError(`Unsupported field cannot be used in filters`);
579
+ }).exhaustive();
558
580
  }
559
581
  buildLiteralFilter(eb, lhs, type, rhs) {
560
- return eb(lhs, "=", rhs !== null && rhs !== void 0 ? this.transformPrimitive(rhs, type) : rhs);
582
+ return eb(lhs, "=", rhs !== null && rhs !== void 0 ? this.transformPrimitive(rhs, type, false) : rhs);
561
583
  }
562
584
  buildStandardFilter(eb, type, payload, lhs, getRhs, recurse, throwIfInvalid = false, onlyForKeys = void 0) {
563
585
  if (payload === null || !(0, import_common_helpers.isPlainObject)(payload)) {
@@ -644,22 +666,22 @@ var BaseCrudDialect = class {
644
666
  }
645
667
  }
646
668
  buildNumberFilter(eb, model, table, field, type, payload) {
647
- const { conditions } = this.buildStandardFilter(eb, type, payload, buildFieldRef(this.schema, model, field, this.options, eb), (value) => this.transformPrimitive(value, type), (value) => this.buildNumberFilter(eb, model, table, field, type, value));
669
+ const { conditions } = this.buildStandardFilter(eb, type, payload, buildFieldRef(this.schema, model, field, this.options, eb), (value) => this.transformPrimitive(value, type, false), (value) => this.buildNumberFilter(eb, model, table, field, type, value));
648
670
  return this.and(eb, ...conditions);
649
671
  }
650
672
  buildBooleanFilter(eb, table, field, payload) {
651
- const { conditions } = this.buildStandardFilter(eb, "Boolean", payload, import_kysely.sql.ref(`${table}.${field}`), (value) => this.transformPrimitive(value, "Boolean"), (value) => this.buildBooleanFilter(eb, table, field, value), true, [
673
+ const { conditions } = this.buildStandardFilter(eb, "Boolean", payload, import_kysely.sql.ref(`${table}.${field}`), (value) => this.transformPrimitive(value, "Boolean", false), (value) => this.buildBooleanFilter(eb, table, field, value), true, [
652
674
  "equals",
653
675
  "not"
654
676
  ]);
655
677
  return this.and(eb, ...conditions);
656
678
  }
657
679
  buildDateTimeFilter(eb, table, field, payload) {
658
- const { conditions } = this.buildStandardFilter(eb, "DateTime", payload, import_kysely.sql.ref(`${table}.${field}`), (value) => this.transformPrimitive(value, "DateTime"), (value) => this.buildDateTimeFilter(eb, table, field, value), true);
680
+ const { conditions } = this.buildStandardFilter(eb, "DateTime", payload, import_kysely.sql.ref(`${table}.${field}`), (value) => this.transformPrimitive(value, "DateTime", false), (value) => this.buildDateTimeFilter(eb, table, field, value), true);
659
681
  return this.and(eb, ...conditions);
660
682
  }
661
683
  buildBytesFilter(eb, table, field, payload) {
662
- const conditions = this.buildStandardFilter(eb, "Bytes", payload, import_kysely.sql.ref(`${table}.${field}`), (value) => this.transformPrimitive(value, "Bytes"), (value) => this.buildBytesFilter(eb, table, field, value), true, [
684
+ const conditions = this.buildStandardFilter(eb, "Bytes", payload, import_kysely.sql.ref(`${table}.${field}`), (value) => this.transformPrimitive(value, "Bytes", false), (value) => this.buildBytesFilter(eb, table, field, value), true, [
663
685
  "equals",
664
686
  "in",
665
687
  "notIn",
@@ -758,10 +780,10 @@ var BaseCrudDialect = class {
758
780
  return negated ? sort === "asc" ? "desc" : "asc" : sort;
759
781
  }
760
782
  true(eb) {
761
- return eb.lit(this.transformPrimitive(true, "Boolean"));
783
+ return eb.lit(this.transformPrimitive(true, "Boolean", false));
762
784
  }
763
785
  false(eb) {
764
- return eb.lit(this.transformPrimitive(false, "Boolean"));
786
+ return eb.lit(this.transformPrimitive(false, "Boolean", false));
765
787
  }
766
788
  isTrue(expression) {
767
789
  const node = expression.toOperationNode();
@@ -810,14 +832,18 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
810
832
  get provider() {
811
833
  return "postgresql";
812
834
  }
813
- transformPrimitive(value, type) {
835
+ transformPrimitive(value, type, forArrayField) {
814
836
  if (value === void 0) {
815
837
  return value;
816
838
  }
817
839
  if (Array.isArray(value)) {
818
- return value.map((v) => this.transformPrimitive(v, type));
840
+ if (type === "Json" && !forArrayField) {
841
+ return JSON.stringify(value);
842
+ } else {
843
+ return value.map((v) => this.transformPrimitive(v, type, false));
844
+ }
819
845
  } else {
820
- return (0, import_ts_pattern2.match)(type).with("DateTime", () => value instanceof Date ? value : typeof value === "string" ? new Date(value) : value).otherwise(() => value);
846
+ return (0, import_ts_pattern2.match)(type).with("DateTime", () => value instanceof Date ? value : typeof value === "string" ? new Date(value) : value).with("Decimal", () => value !== null ? value.toString() : value).otherwise(() => value);
821
847
  }
822
848
  }
823
849
  buildRelationSelection(query, model, relationField, parentAlias, payload) {
@@ -884,25 +910,33 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
884
910
  buildFieldRef(this.schema, relationModel, field, this.options, eb)
885
911
  ]).flatMap((v) => v));
886
912
  } else if (payload.select) {
887
- objArgs.push(...Object.entries(payload.select).filter(([, value]) => value).map(([field]) => [
888
- import_kysely2.sql.lit(field),
889
- buildFieldRef(this.schema, relationModel, field, this.options, eb)
890
- ]).flatMap((v) => v));
913
+ objArgs.push(...Object.entries(payload.select).filter(([, value]) => value).map(([field]) => {
914
+ const fieldDef = requireField(this.schema, relationModel, field);
915
+ const fieldValue = fieldDef.relation ? eb.ref(`${parentName}$${relationField}$${field}.$j`) : buildFieldRef(this.schema, relationModel, field, this.options, eb);
916
+ return [
917
+ import_kysely2.sql.lit(field),
918
+ fieldValue
919
+ ];
920
+ }).flatMap((v) => v));
891
921
  }
892
922
  if (typeof payload === "object" && payload.include && typeof payload.include === "object") {
893
923
  objArgs.push(...Object.entries(payload.include).filter(([, value]) => value).map(([field]) => [
894
924
  import_kysely2.sql.lit(field),
925
+ // reference the synthesized JSON field
895
926
  eb.ref(`${parentName}$${relationField}$${field}.$j`)
896
927
  ]).flatMap((v) => v));
897
928
  }
898
929
  return objArgs;
899
930
  }
900
- buildRelationJoins(model, relationField, qb, payload, parentName) {
931
+ buildRelationJoins(relationModel, relationField, qb, payload, parentName) {
901
932
  let result = qb;
902
- if (typeof payload === "object" && payload.include && typeof payload.include === "object") {
903
- Object.entries(payload.include).filter(([, value]) => value).forEach(([field, value]) => {
904
- result = this.buildRelationJSON(model, result, field, `${parentName}$${relationField}`, value);
905
- });
933
+ if (typeof payload === "object") {
934
+ const selectInclude = payload.include ?? payload.select;
935
+ if (selectInclude && typeof selectInclude === "object") {
936
+ Object.entries(selectInclude).filter(([, value]) => value).filter(([field]) => isRelationField(this.schema, relationModel, field)).forEach(([field, value]) => {
937
+ result = this.buildRelationJSON(relationModel, result, field, `${parentName}$${relationField}`, value);
938
+ });
939
+ }
906
940
  }
907
941
  return result;
908
942
  }
@@ -955,14 +989,14 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
955
989
  get provider() {
956
990
  return "sqlite";
957
991
  }
958
- transformPrimitive(value, type) {
992
+ transformPrimitive(value, type, _forArrayField) {
959
993
  if (value === void 0) {
960
994
  return value;
961
995
  }
962
996
  if (Array.isArray(value)) {
963
- return value.map((v) => this.transformPrimitive(v, type));
997
+ return value.map((v) => this.transformPrimitive(v, type, false));
964
998
  } else {
965
- return (0, import_ts_pattern3.match)(type).with("Boolean", () => value ? 1 : 0).with("DateTime", () => value instanceof Date ? value.toISOString() : value).with("Decimal", () => value.toString()).with("Bytes", () => Buffer.from(value)).otherwise(() => value);
999
+ return (0, import_ts_pattern3.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);
966
1000
  }
967
1001
  }
968
1002
  buildRelationSelection(query, model, relationField, parentAlias, payload) {
@@ -1567,11 +1601,11 @@ var ExpressionEvaluator = class {
1567
1601
  // src/plugins/policy/utils.ts
1568
1602
  var import_kysely5 = require("kysely");
1569
1603
  function trueNode(dialect) {
1570
- return import_kysely5.ValueNode.createImmediate(dialect.transformPrimitive(true, "Boolean"));
1604
+ return import_kysely5.ValueNode.createImmediate(dialect.transformPrimitive(true, "Boolean", false));
1571
1605
  }
1572
1606
  __name(trueNode, "trueNode");
1573
1607
  function falseNode(dialect) {
1574
- return import_kysely5.ValueNode.createImmediate(dialect.transformPrimitive(false, "Boolean"));
1608
+ return import_kysely5.ValueNode.createImmediate(dialect.transformPrimitive(false, "Boolean", false));
1575
1609
  }
1576
1610
  __name(falseNode, "falseNode");
1577
1611
  function isTrueNode(node) {
@@ -1829,7 +1863,7 @@ var ExpressionTransformer = class {
1829
1863
  }
1830
1864
  }
1831
1865
  transformValue(value, type) {
1832
- return import_kysely6.ValueNode.create(this.dialect.transformPrimitive(value, type) ?? null);
1866
+ return import_kysely6.ValueNode.create(this.dialect.transformPrimitive(value, type, false) ?? null);
1833
1867
  }
1834
1868
  _unary(expr2, context) {
1835
1869
  (0, import_common_helpers5.invariant)(expr2.op === "!", 'only "!" operator is supported');
@@ -2072,7 +2106,7 @@ var PolicyHandler = class extends import_kysely7.OperationNodeTransformer {
2072
2106
  get kysely() {
2073
2107
  return this.client.$qb;
2074
2108
  }
2075
- async handle(node, proceed, transaction) {
2109
+ async handle(node, proceed) {
2076
2110
  if (!this.isCrudQueryNode(node)) {
2077
2111
  throw new RejectedByPolicyError(void 0, "non-CRUD queries are not allowed");
2078
2112
  }
@@ -2092,27 +2126,20 @@ var PolicyHandler = class extends import_kysely7.OperationNodeTransformer {
2092
2126
  if (!mutationRequiresTransaction && !node.returning) {
2093
2127
  return proceed(this.transformNode(node));
2094
2128
  }
2095
- let readBackError = false;
2096
- const result = await transaction(async (txProceed) => {
2097
- if (import_kysely7.InsertQueryNode.is(node)) {
2098
- await this.enforcePreCreatePolicy(node, txProceed);
2099
- }
2100
- const transformedNode = this.transformNode(node);
2101
- const result2 = await txProceed(transformedNode);
2102
- if (!this.onlyReturningId(node)) {
2103
- const readBackResult = await this.processReadBack(node, result2, txProceed);
2104
- if (readBackResult.rows.length !== result2.rows.length) {
2105
- readBackError = true;
2106
- }
2107
- return readBackResult;
2108
- } else {
2109
- return result2;
2129
+ if (import_kysely7.InsertQueryNode.is(node)) {
2130
+ await this.enforcePreCreatePolicy(node, proceed);
2131
+ }
2132
+ const transformedNode = this.transformNode(node);
2133
+ const result = await proceed(transformedNode);
2134
+ if (!this.onlyReturningId(node)) {
2135
+ const readBackResult = await this.processReadBack(node, result, proceed);
2136
+ if (readBackResult.rows.length !== result.rows.length) {
2137
+ throw new RejectedByPolicyError(mutationModel, "result is not allowed to be read back");
2110
2138
  }
2111
- });
2112
- if (readBackError) {
2113
- throw new RejectedByPolicyError(mutationModel, "result is not allowed to be read back");
2139
+ return readBackResult;
2140
+ } else {
2141
+ return result;
2114
2142
  }
2115
- return result;
2116
2143
  }
2117
2144
  onlyReturningId(node) {
2118
2145
  if (!node.returning) {
@@ -2173,11 +2200,11 @@ var PolicyHandler = class extends import_kysely7.OperationNodeTransformer {
2173
2200
  if (typeof item === "object" && item && "kind" in item) {
2174
2201
  (0, import_common_helpers6.invariant)(item.kind === "ValueNode", "expecting a ValueNode");
2175
2202
  result.push({
2176
- node: import_kysely7.ValueNode.create(this.dialect.transformPrimitive(item.value, fieldDef.type)),
2203
+ node: import_kysely7.ValueNode.create(this.dialect.transformPrimitive(item.value, fieldDef.type, !!fieldDef.array)),
2177
2204
  raw: item.value
2178
2205
  });
2179
2206
  } else {
2180
- const value = this.dialect.transformPrimitive(item, fieldDef.type);
2207
+ const value = this.dialect.transformPrimitive(item, fieldDef.type, !!fieldDef.array);
2181
2208
  if (Array.isArray(value)) {
2182
2209
  result.push({
2183
2210
  node: import_kysely7.RawNode.createWithSql(this.dialect.buildArrayLiteralSQL(value)),
@@ -2408,9 +2435,18 @@ var PolicyPlugin = class {
2408
2435
  get description() {
2409
2436
  return "Enforces access policies defined in the schema.";
2410
2437
  }
2411
- onKyselyQuery({ query, client, proceed, transaction }) {
2438
+ onKyselyQuery({
2439
+ query,
2440
+ client,
2441
+ proceed
2442
+ /*, transaction*/
2443
+ }) {
2412
2444
  const handler = new PolicyHandler(client);
2413
- return handler.handle(query, proceed, transaction);
2445
+ return handler.handle(
2446
+ query,
2447
+ proceed
2448
+ /*, transaction*/
2449
+ );
2414
2450
  }
2415
2451
  };
2416
2452
 
@@ -2551,7 +2587,12 @@ var BaseOperationHandler = class {
2551
2587
  result = await query.execute();
2552
2588
  } catch (err) {
2553
2589
  const { sql: sql11, parameters } = query.compile();
2554
- throw new QueryError(`Failed to execute query: ${err}, sql: ${sql11}, parameters: ${parameters}`);
2590
+ let message = `Failed to execute query: ${err}, sql: ${sql11}`;
2591
+ if (this.options.debug) {
2592
+ message += `, parameters:
2593
+ ${parameters.map((p) => (0, import_node_util.inspect)(p)).join("\n")}`;
2594
+ }
2595
+ throw new QueryError(message, err);
2555
2596
  }
2556
2597
  if (inMemoryDistinct) {
2557
2598
  const distinctResult = [];
@@ -2610,30 +2651,20 @@ var BaseOperationHandler = class {
2610
2651
  for (const [field, value] of Object.entries(selections.select)) {
2611
2652
  const fieldDef = requireField(this.schema, model, field);
2612
2653
  const fieldModel = fieldDef.type;
2613
- const jointTable = `${parentAlias}$${field}$count`;
2614
- const joinPairs = buildJoinPairs(this.schema, model, parentAlias, field, jointTable);
2615
- query = query.leftJoin((eb2) => {
2616
- let result = eb2.selectFrom(fieldModel).selectAll();
2617
- if (value && typeof value === "object" && "where" in value && value.where && typeof value.where === "object") {
2618
- const filter = this.dialect.buildFilter(eb2, fieldModel, fieldModel, value.where);
2619
- result = result.where(filter);
2620
- }
2621
- return result.as(jointTable);
2622
- }, (join) => {
2623
- for (const [left, right] of joinPairs) {
2624
- join = join.onRef(left, "=", right);
2625
- }
2626
- return join;
2627
- });
2628
- jsonObject[field] = this.countIdDistinct(eb, fieldDef.type, jointTable);
2654
+ const joinPairs = buildJoinPairs(this.schema, model, parentAlias, field, fieldModel);
2655
+ let fieldCountQuery = eb.selectFrom(fieldModel).select(eb.fn.countAll().as(`_count$${field}`));
2656
+ for (const [left, right] of joinPairs) {
2657
+ fieldCountQuery = fieldCountQuery.whereRef(left, "=", right);
2658
+ }
2659
+ if (value && typeof value === "object" && "where" in value && value.where && typeof value.where === "object") {
2660
+ const filter = this.dialect.buildFilter(eb, fieldModel, fieldModel, value.where);
2661
+ fieldCountQuery = fieldCountQuery.where(filter);
2662
+ }
2663
+ jsonObject[field] = fieldCountQuery;
2629
2664
  }
2630
2665
  query = query.select((eb2) => this.dialect.buildJsonObject(eb2, jsonObject).as("_count"));
2631
2666
  return query;
2632
2667
  }
2633
- countIdDistinct(eb, model, table) {
2634
- const idFields = getIdFields(this.schema, model);
2635
- return eb.fn.count(import_kysely8.sql.join(idFields.map((f) => import_kysely8.sql.ref(`${table}.${f}`)))).distinct();
2636
- }
2637
2668
  buildSelectAllScalarFields(model, query, omit) {
2638
2669
  const modelDef = this.requireModel(model);
2639
2670
  return Object.keys(modelDef.fields).filter((f) => !isRelationField(this.schema, model, f)).filter((f) => omit?.[f] !== true).reduce((acc, f) => this.selectField(acc, model, model, f), query);
@@ -2699,14 +2730,14 @@ var BaseOperationHandler = class {
2699
2730
  const fieldDef = this.requireField(model, field);
2700
2731
  if (isScalarField(this.schema, model, field) || isForeignKeyField(this.schema, model, field)) {
2701
2732
  if (fieldDef.array && value && typeof value === "object" && "set" in value && Array.isArray(value.set)) {
2702
- createFields[field] = this.dialect.transformPrimitive(value.set, fieldDef.type);
2733
+ createFields[field] = this.dialect.transformPrimitive(value.set, fieldDef.type, true);
2703
2734
  } else {
2704
- createFields[field] = this.dialect.transformPrimitive(value, fieldDef.type);
2735
+ createFields[field] = this.dialect.transformPrimitive(value, fieldDef.type, !!fieldDef.array);
2705
2736
  }
2706
2737
  } else {
2707
2738
  const subM2M = getManyToManyRelation(this.schema, model, field);
2708
2739
  if (!subM2M && fieldDef.relation?.fields && fieldDef.relation?.references) {
2709
- const fkValues = await this.processOwnedRelation(kysely, fieldDef, value);
2740
+ const fkValues = await this.processOwnedRelationForCreate(kysely, fieldDef, value);
2710
2741
  for (let i = 0; i < fieldDef.relation.fields.length; i++) {
2711
2742
  createFields[fieldDef.relation.fields[i]] = fkValues[fieldDef.relation.references[i]];
2712
2743
  }
@@ -2727,7 +2758,7 @@ var BaseOperationHandler = class {
2727
2758
  const createdEntity = await query.executeTakeFirst();
2728
2759
  if (Object.keys(postCreateRelations).length > 0) {
2729
2760
  const relationPromises = Object.entries(postCreateRelations).map(([field, subPayload]) => {
2730
- return this.processNoneOwnedRelation(kysely, model, field, subPayload, createdEntity);
2761
+ return this.processNoneOwnedRelationForCreate(kysely, model, field, subPayload, createdEntity);
2731
2762
  });
2732
2763
  await Promise.all(relationPromises);
2733
2764
  }
@@ -2794,7 +2825,7 @@ var BaseOperationHandler = class {
2794
2825
  const eb = (0, import_kysely8.expressionBuilder)();
2795
2826
  return kysely.deleteFrom(m2m.joinTable).where(eb(`${m2m.joinTable}.${m2m.parentFkName}`, "=", parentId)).execute();
2796
2827
  }
2797
- async processOwnedRelation(kysely, relationField, payload) {
2828
+ async processOwnedRelationForCreate(kysely, relationField, payload) {
2798
2829
  if (!payload) {
2799
2830
  return;
2800
2831
  }
@@ -2844,21 +2875,27 @@ var BaseOperationHandler = class {
2844
2875
  }
2845
2876
  return result;
2846
2877
  }
2847
- processNoneOwnedRelation(kysely, contextModel, relationFieldName, payload, parentEntity) {
2878
+ processNoneOwnedRelationForCreate(kysely, contextModel, relationFieldName, payload, parentEntity) {
2848
2879
  const relationFieldDef = this.requireField(contextModel, relationFieldName);
2849
2880
  const relationModel = relationFieldDef.type;
2850
2881
  const tasks = [];
2882
+ const fromRelationContext = {
2883
+ model: contextModel,
2884
+ field: relationFieldName,
2885
+ ids: parentEntity
2886
+ };
2851
2887
  for (const [action, subPayload] of Object.entries(payload)) {
2852
2888
  if (!subPayload) {
2853
2889
  continue;
2854
2890
  }
2855
2891
  switch (action) {
2856
2892
  case "create": {
2857
- tasks.push(...enumerate(subPayload).map((item) => this.create(kysely, relationModel, item, {
2858
- model: contextModel,
2859
- field: relationFieldName,
2860
- ids: parentEntity
2861
- })));
2893
+ tasks.push(...enumerate(subPayload).map((item) => this.create(kysely, relationModel, item, fromRelationContext)));
2894
+ break;
2895
+ }
2896
+ case "createMany": {
2897
+ (0, import_common_helpers8.invariant)(relationFieldDef.array, "relation must be an array for createMany");
2898
+ tasks.push(this.createMany(kysely, relationModel, subPayload, false, fromRelationContext));
2862
2899
  break;
2863
2900
  }
2864
2901
  case "connect": {
@@ -2888,6 +2925,11 @@ var BaseOperationHandler = class {
2888
2925
  return Promise.all(tasks);
2889
2926
  }
2890
2927
  async createMany(kysely, model, input, returnData, fromRelation) {
2928
+ if (!input.data || Array.isArray(input.data) && input.data.length === 0) {
2929
+ return returnData ? [] : {
2930
+ count: 0
2931
+ };
2932
+ }
2891
2933
  const modelDef = this.requireModel(model);
2892
2934
  let relationKeyPairs = [];
2893
2935
  if (fromRelation) {
@@ -2902,7 +2944,7 @@ var BaseOperationHandler = class {
2902
2944
  for (const [name, value] of Object.entries(item)) {
2903
2945
  const fieldDef = this.requireField(model, name);
2904
2946
  (0, import_common_helpers8.invariant)(!fieldDef.relation, "createMany does not support relations");
2905
- newItem[name] = this.dialect.transformPrimitive(value, fieldDef.type);
2947
+ newItem[name] = this.dialect.transformPrimitive(value, fieldDef.type, !!fieldDef.array);
2906
2948
  }
2907
2949
  if (fromRelation) {
2908
2950
  for (const { fk, pk } of relationKeyPairs) {
@@ -2937,7 +2979,7 @@ var BaseOperationHandler = class {
2937
2979
  values[field] = generated;
2938
2980
  }
2939
2981
  } else if (fields[field]?.updatedAt) {
2940
- values[field] = this.dialect.transformPrimitive(/* @__PURE__ */ new Date(), "DateTime");
2982
+ values[field] = this.dialect.transformPrimitive(/* @__PURE__ */ new Date(), "DateTime", false);
2941
2983
  }
2942
2984
  }
2943
2985
  }
@@ -3002,7 +3044,7 @@ var BaseOperationHandler = class {
3002
3044
  if (finalData === data) {
3003
3045
  finalData = clone(data);
3004
3046
  }
3005
- finalData[fieldName] = this.dialect.transformPrimitive(/* @__PURE__ */ new Date(), "DateTime");
3047
+ finalData[fieldName] = this.dialect.transformPrimitive(/* @__PURE__ */ new Date(), "DateTime", false);
3006
3048
  }
3007
3049
  }
3008
3050
  if (Object.keys(finalData).length === 0) {
@@ -3027,7 +3069,7 @@ var BaseOperationHandler = class {
3027
3069
  updateFields[field] = this.transformScalarListUpdate(model, field, fieldDef, finalData[field]);
3028
3070
  continue;
3029
3071
  }
3030
- updateFields[field] = this.dialect.transformPrimitive(finalData[field], fieldDef.type);
3072
+ updateFields[field] = this.dialect.transformPrimitive(finalData[field], fieldDef.type, !!fieldDef.array);
3031
3073
  } else {
3032
3074
  if (!allowRelationUpdate) {
3033
3075
  throw new QueryError(`Relation update not allowed for field "${field}"`);
@@ -3072,7 +3114,7 @@ var BaseOperationHandler = class {
3072
3114
  transformIncrementalUpdate(model, field, fieldDef, payload) {
3073
3115
  (0, import_common_helpers8.invariant)(Object.keys(payload).length === 1, 'Only one of "set", "increment", "decrement", "multiply", or "divide" can be provided');
3074
3116
  const key = Object.keys(payload)[0];
3075
- const value = this.dialect.transformPrimitive(payload[key], fieldDef.type);
3117
+ const value = this.dialect.transformPrimitive(payload[key], fieldDef.type, false);
3076
3118
  const eb = (0, import_kysely8.expressionBuilder)();
3077
3119
  const fieldRef = buildFieldRef(this.schema, model, field, this.options, eb);
3078
3120
  return (0, import_ts_pattern8.match)(key).with("set", () => value).with("increment", () => eb(fieldRef, "+", value)).with("decrement", () => eb(fieldRef, "-", value)).with("multiply", () => eb(fieldRef, "*", value)).with("divide", () => eb(fieldRef, "/", value)).otherwise(() => {
@@ -3082,7 +3124,7 @@ var BaseOperationHandler = class {
3082
3124
  transformScalarListUpdate(model, field, fieldDef, payload) {
3083
3125
  (0, import_common_helpers8.invariant)(Object.keys(payload).length === 1, 'Only one of "set", "push" can be provided');
3084
3126
  const key = Object.keys(payload)[0];
3085
- const value = this.dialect.transformPrimitive(payload[key], fieldDef.type);
3127
+ const value = this.dialect.transformPrimitive(payload[key], fieldDef.type, true);
3086
3128
  const eb = (0, import_kysely8.expressionBuilder)();
3087
3129
  const fieldRef = buildFieldRef(this.schema, model, field, this.options, eb);
3088
3130
  return (0, import_ts_pattern8.match)(key).with("set", () => value).with("push", () => {
@@ -3112,7 +3154,7 @@ var BaseOperationHandler = class {
3112
3154
  if (isRelationField(this.schema, model, field)) {
3113
3155
  continue;
3114
3156
  }
3115
- updateFields[field] = this.dialect.transformPrimitive(data[field], fieldDef.type);
3157
+ updateFields[field] = this.dialect.transformPrimitive(data[field], fieldDef.type, !!fieldDef.array);
3116
3158
  }
3117
3159
  let query = kysely.updateTable(model).set(updateFields);
3118
3160
  if (limit === void 0) {
@@ -3130,20 +3172,15 @@ var BaseOperationHandler = class {
3130
3172
  model,
3131
3173
  operation: "update"
3132
3174
  }));
3133
- try {
3134
- if (!returnData) {
3135
- const result = await query.executeTakeFirstOrThrow();
3136
- return {
3137
- count: Number(result.numUpdatedRows)
3138
- };
3139
- } else {
3140
- const idFields = getIdFields(this.schema, model);
3141
- const result = await query.returning(idFields).execute();
3142
- return result;
3143
- }
3144
- } catch (err) {
3145
- const { sql: sql11, parameters } = query.compile();
3146
- throw new QueryError(`Error during updateMany: ${err}, sql: ${sql11}, parameters: ${parameters}`);
3175
+ if (!returnData) {
3176
+ const result = await query.executeTakeFirstOrThrow();
3177
+ return {
3178
+ count: Number(result.numUpdatedRows)
3179
+ };
3180
+ } else {
3181
+ const idFields = getIdFields(this.schema, model);
3182
+ const result = await query.returning(idFields).execute();
3183
+ return result;
3147
3184
  }
3148
3185
  }
3149
3186
  buildIdFieldRefs(kysely, model) {
@@ -3563,11 +3600,15 @@ var BaseOperationHandler = class {
3563
3600
  }
3564
3601
  return returnRelation;
3565
3602
  }
3566
- async safeTransaction(callback) {
3603
+ async safeTransaction(callback, isolationLevel) {
3567
3604
  if (this.kysely.isTransaction) {
3568
3605
  return callback(this.kysely);
3569
3606
  } else {
3570
- return this.kysely.transaction().setIsolationLevel("repeatable read").execute(callback);
3607
+ let txBuilder = this.kysely.transaction();
3608
+ if (isolationLevel) {
3609
+ txBuilder = txBuilder.setIsolationLevel(isolationLevel);
3610
+ }
3611
+ return txBuilder.execute(callback);
3571
3612
  }
3572
3613
  }
3573
3614
  // Given a unique filter of a model, return the entity ids by trying to
@@ -3586,6 +3627,28 @@ var BaseOperationHandler = class {
3586
3627
  where: uniqueFilter
3587
3628
  });
3588
3629
  }
3630
+ /**
3631
+ * Normalize input args to strip `undefined` fields
3632
+ */
3633
+ normalizeArgs(args) {
3634
+ if (!args) {
3635
+ return;
3636
+ }
3637
+ const newArgs = clone(args);
3638
+ this.doNormalizeArgs(newArgs);
3639
+ return newArgs;
3640
+ }
3641
+ doNormalizeArgs(args) {
3642
+ if (args && typeof args === "object") {
3643
+ for (const [key, value] of Object.entries(args)) {
3644
+ if (value === void 0) {
3645
+ delete args[key];
3646
+ } else if (value && (0, import_common_helpers8.isPlainObject)(value)) {
3647
+ this.doNormalizeArgs(value);
3648
+ }
3649
+ }
3650
+ }
3651
+ }
3589
3652
  };
3590
3653
 
3591
3654
  // src/client/crud/operations/aggregate.ts
@@ -3594,21 +3657,22 @@ var AggregateOperationHandler = class extends BaseOperationHandler {
3594
3657
  __name(this, "AggregateOperationHandler");
3595
3658
  }
3596
3659
  async handle(_operation, args) {
3597
- const validatedArgs = this.inputValidator.validateAggregateArgs(this.model, args);
3660
+ const normalizeArgs = this.normalizeArgs(args);
3661
+ const parsedArgs = this.inputValidator.validateAggregateArgs(this.model, normalizeArgs);
3598
3662
  let query = this.kysely.selectFrom((eb) => {
3599
- let subQuery = eb.selectFrom(this.model).selectAll(this.model).where((eb1) => this.dialect.buildFilter(eb1, this.model, this.model, validatedArgs?.where));
3600
- const skip = validatedArgs?.skip;
3601
- let take = validatedArgs?.take;
3663
+ let subQuery = eb.selectFrom(this.model).selectAll(this.model).where((eb1) => this.dialect.buildFilter(eb1, this.model, this.model, parsedArgs?.where));
3664
+ const skip = parsedArgs?.skip;
3665
+ let take = parsedArgs?.take;
3602
3666
  let negateOrderBy = false;
3603
3667
  if (take !== void 0 && take < 0) {
3604
3668
  negateOrderBy = true;
3605
3669
  take = -take;
3606
3670
  }
3607
3671
  subQuery = this.dialect.buildSkipTake(subQuery, skip, take);
3608
- subQuery = this.dialect.buildOrderBy(subQuery, this.model, this.model, validatedArgs.orderBy, skip !== void 0 || take !== void 0, negateOrderBy);
3672
+ subQuery = this.dialect.buildOrderBy(subQuery, this.model, this.model, parsedArgs.orderBy, skip !== void 0 || take !== void 0, negateOrderBy);
3609
3673
  return subQuery.as("$sub");
3610
3674
  });
3611
- for (const [key, value] of Object.entries(validatedArgs)) {
3675
+ for (const [key, value] of Object.entries(parsedArgs)) {
3612
3676
  switch (key) {
3613
3677
  case "_count": {
3614
3678
  if (value === true) {
@@ -3687,14 +3751,15 @@ var CountOperationHandler = class extends BaseOperationHandler {
3687
3751
  __name(this, "CountOperationHandler");
3688
3752
  }
3689
3753
  async handle(_operation, args) {
3690
- const validatedArgs = this.inputValidator.validateCountArgs(this.model, args);
3754
+ const normalizeArgs = this.normalizeArgs(args);
3755
+ const parsedArgs = this.inputValidator.validateCountArgs(this.model, normalizeArgs);
3691
3756
  let query = this.kysely.selectFrom((eb) => {
3692
- let subQuery = eb.selectFrom(this.model).selectAll().where((eb1) => this.dialect.buildFilter(eb1, this.model, this.model, validatedArgs?.where));
3693
- subQuery = this.dialect.buildSkipTake(subQuery, validatedArgs?.skip, validatedArgs?.take);
3757
+ let subQuery = eb.selectFrom(this.model).selectAll().where((eb1) => this.dialect.buildFilter(eb1, this.model, this.model, parsedArgs?.where));
3758
+ subQuery = this.dialect.buildSkipTake(subQuery, parsedArgs?.skip, parsedArgs?.take);
3694
3759
  return subQuery.as("$sub");
3695
3760
  });
3696
- if (validatedArgs?.select && typeof validatedArgs.select === "object") {
3697
- query = query.select((eb) => Object.keys(validatedArgs.select).map((key) => key === "_all" ? eb.cast(eb.fn.countAll(), "integer").as("_all") : eb.cast(eb.fn.count(import_kysely10.sql.ref(`$sub.${key}`)), "integer").as(key)));
3761
+ if (parsedArgs?.select && typeof parsedArgs.select === "object") {
3762
+ query = query.select((eb) => Object.keys(parsedArgs.select).map((key) => key === "_all" ? eb.cast(eb.fn.countAll(), "integer").as("_all") : eb.cast(eb.fn.count(import_kysely10.sql.ref(`$sub.${key}`)), "integer").as(key)));
3698
3763
  return query.executeTakeFirstOrThrow();
3699
3764
  } else {
3700
3765
  query = query.select((eb) => eb.cast(eb.fn.countAll(), "integer").as("count"));
@@ -3711,10 +3776,11 @@ var CreateOperationHandler = class extends BaseOperationHandler {
3711
3776
  __name(this, "CreateOperationHandler");
3712
3777
  }
3713
3778
  async handle(operation, args) {
3714
- return (0, import_ts_pattern10.match)(operation).with("create", () => this.runCreate(this.inputValidator.validateCreateArgs(this.model, args))).with("createMany", () => {
3715
- return this.runCreateMany(this.inputValidator.validateCreateManyArgs(this.model, args));
3779
+ const normalizeArgs = this.normalizeArgs(args);
3780
+ return (0, import_ts_pattern10.match)(operation).with("create", () => this.runCreate(this.inputValidator.validateCreateArgs(this.model, normalizeArgs))).with("createMany", () => {
3781
+ return this.runCreateMany(this.inputValidator.validateCreateManyArgs(this.model, normalizeArgs));
3716
3782
  }).with("createManyAndReturn", () => {
3717
- return this.runCreateManyAndReturn(this.inputValidator.validateCreateManyAndReturnArgs(this.model, args));
3783
+ return this.runCreateManyAndReturn(this.inputValidator.validateCreateManyAndReturnArgs(this.model, normalizeArgs));
3718
3784
  }).exhaustive();
3719
3785
  }
3720
3786
  async runCreate(args) {
@@ -3764,7 +3830,8 @@ var DeleteOperationHandler = class extends BaseOperationHandler {
3764
3830
  __name(this, "DeleteOperationHandler");
3765
3831
  }
3766
3832
  async handle(operation, args) {
3767
- return (0, import_ts_pattern11.match)(operation).with("delete", () => this.runDelete(this.inputValidator.validateDeleteArgs(this.model, args))).with("deleteMany", () => this.runDeleteMany(this.inputValidator.validateDeleteManyArgs(this.model, args))).exhaustive();
3833
+ const normalizeArgs = this.normalizeArgs(args);
3834
+ return (0, import_ts_pattern11.match)(operation).with("delete", () => this.runDelete(this.inputValidator.validateDeleteArgs(this.model, normalizeArgs))).with("deleteMany", () => this.runDeleteMany(this.inputValidator.validateDeleteManyArgs(this.model, normalizeArgs))).exhaustive();
3768
3835
  }
3769
3836
  async runDelete(args) {
3770
3837
  const existing = await this.readUnique(this.kysely, this.model, {
@@ -3794,7 +3861,8 @@ var FindOperationHandler = class extends BaseOperationHandler {
3794
3861
  __name(this, "FindOperationHandler");
3795
3862
  }
3796
3863
  async handle(operation, args, validateArgs = true) {
3797
- const parsedArgs = validateArgs ? this.inputValidator.validateFindArgs(this.model, operation === "findUnique", args) : args;
3864
+ const normalizeArgs = this.normalizeArgs(args);
3865
+ const parsedArgs = validateArgs ? this.inputValidator.validateFindArgs(this.model, operation === "findUnique", normalizeArgs) : normalizeArgs;
3798
3866
  const result = await this.read(this.client.$qb, this.model, parsedArgs);
3799
3867
  const finalResult = operation === "findMany" ? result : result[0] ?? null;
3800
3868
  return finalResult;
@@ -3809,11 +3877,12 @@ var GroupByeOperationHandler = class extends BaseOperationHandler {
3809
3877
  __name(this, "GroupByeOperationHandler");
3810
3878
  }
3811
3879
  async handle(_operation, args) {
3812
- const validatedArgs = this.inputValidator.validateGroupByArgs(this.model, args);
3880
+ const normalizeArgs = this.normalizeArgs(args);
3881
+ const parsedArgs = this.inputValidator.validateGroupByArgs(this.model, normalizeArgs);
3813
3882
  let query = this.kysely.selectFrom((eb) => {
3814
- let subQuery = eb.selectFrom(this.model).selectAll().where((eb1) => this.dialect.buildFilter(eb1, this.model, this.model, validatedArgs?.where));
3815
- const skip = validatedArgs?.skip;
3816
- let take = validatedArgs?.take;
3883
+ let subQuery = eb.selectFrom(this.model).selectAll().where((eb1) => this.dialect.buildFilter(eb1, this.model, this.model, parsedArgs?.where));
3884
+ const skip = parsedArgs?.skip;
3885
+ let take = parsedArgs?.take;
3817
3886
  let negateOrderBy = false;
3818
3887
  if (take !== void 0 && take < 0) {
3819
3888
  negateOrderBy = true;
@@ -3823,20 +3892,20 @@ var GroupByeOperationHandler = class extends BaseOperationHandler {
3823
3892
  subQuery = this.dialect.buildOrderBy(subQuery, this.model, this.model, void 0, skip !== void 0 || take !== void 0, negateOrderBy);
3824
3893
  return subQuery.as("$sub");
3825
3894
  });
3826
- const bys = typeof validatedArgs.by === "string" ? [
3827
- validatedArgs.by
3828
- ] : validatedArgs.by;
3895
+ const bys = typeof parsedArgs.by === "string" ? [
3896
+ parsedArgs.by
3897
+ ] : parsedArgs.by;
3829
3898
  query = query.groupBy(bys);
3830
- if (validatedArgs.orderBy) {
3831
- query = this.dialect.buildOrderBy(query, this.model, "$sub", validatedArgs.orderBy, false, false);
3899
+ if (parsedArgs.orderBy) {
3900
+ query = this.dialect.buildOrderBy(query, this.model, "$sub", parsedArgs.orderBy, false, false);
3832
3901
  }
3833
- if (validatedArgs.having) {
3834
- query = query.having((eb1) => this.dialect.buildFilter(eb1, this.model, "$sub", validatedArgs.having));
3902
+ if (parsedArgs.having) {
3903
+ query = query.having((eb1) => this.dialect.buildFilter(eb1, this.model, "$sub", parsedArgs.having));
3835
3904
  }
3836
3905
  for (const by of bys) {
3837
3906
  query = query.select(() => import_kysely11.sql.ref(`$sub.${by}`).as(by));
3838
3907
  }
3839
- for (const [key, value] of Object.entries(validatedArgs)) {
3908
+ for (const [key, value] of Object.entries(parsedArgs)) {
3840
3909
  switch (key) {
3841
3910
  case "_count": {
3842
3911
  if (value === true) {
@@ -3919,7 +3988,8 @@ var UpdateOperationHandler = class extends BaseOperationHandler {
3919
3988
  __name(this, "UpdateOperationHandler");
3920
3989
  }
3921
3990
  async handle(operation, args) {
3922
- return (0, import_ts_pattern13.match)(operation).with("update", () => this.runUpdate(this.inputValidator.validateUpdateArgs(this.model, args))).with("updateMany", () => this.runUpdateMany(this.inputValidator.validateUpdateManyArgs(this.model, args))).with("updateManyAndReturn", () => this.runUpdateManyAndReturn(this.inputValidator.validateUpdateManyAndReturnArgs(this.model, args))).with("upsert", () => this.runUpsert(this.inputValidator.validateUpsertArgs(this.model, args))).exhaustive();
3991
+ const normalizeArgs = this.normalizeArgs(args);
3992
+ return (0, import_ts_pattern13.match)(operation).with("update", () => this.runUpdate(this.inputValidator.validateUpdateArgs(this.model, normalizeArgs))).with("updateMany", () => this.runUpdateMany(this.inputValidator.validateUpdateManyArgs(this.model, normalizeArgs))).with("updateManyAndReturn", () => this.runUpdateManyAndReturn(this.inputValidator.validateUpdateManyAndReturnArgs(this.model, normalizeArgs))).with("upsert", () => this.runUpsert(this.inputValidator.validateUpsertArgs(this.model, normalizeArgs))).exhaustive();
3923
3993
  }
3924
3994
  async runUpdate(args) {
3925
3995
  const result = await this.safeTransaction(async (tx) => {
@@ -3975,6 +4045,7 @@ var UpdateOperationHandler = class extends BaseOperationHandler {
3975
4045
  };
3976
4046
 
3977
4047
  // src/client/crud/validator.ts
4048
+ var import_common_helpers9 = require("@zenstackhq/common-helpers");
3978
4049
  var import_decimal = __toESM(require("decimal.js"), 1);
3979
4050
  var import_json_stable_stringify = __toESM(require("json-stable-stringify"), 1);
3980
4051
  var import_ts_pattern14 = require("ts-pattern");
@@ -4043,7 +4114,7 @@ var InputValidator = class {
4043
4114
  }
4044
4115
  const { error } = schema.safeParse(args);
4045
4116
  if (error) {
4046
- throw new QueryError(`Invalid ${operation} args: ${error.message}`);
4117
+ throw new InputValidationError(`Invalid ${operation} args: ${error.message}`, error);
4047
4118
  }
4048
4119
  return args;
4049
4120
  }
@@ -4090,7 +4161,7 @@ var InputValidator = class {
4090
4161
  makeWhereSchema(model, unique, withoutRelationFields = false) {
4091
4162
  const modelDef = getModel(this.schema, model);
4092
4163
  if (!modelDef) {
4093
- throw new QueryError(`Model "${model}" not found`);
4164
+ throw new QueryError(`Model "${model}" not found in schema`);
4094
4165
  }
4095
4166
  const fields = {};
4096
4167
  for (const field of Object.keys(modelDef.fields)) {
@@ -4140,10 +4211,24 @@ var InputValidator = class {
4140
4211
  const uniqueFields = getUniqueFields(this.schema, model);
4141
4212
  for (const uniqueField of uniqueFields) {
4142
4213
  if ("defs" in uniqueField) {
4143
- fields[uniqueField.name] = import_v4.z.object(Object.fromEntries(Object.entries(uniqueField.defs).map(([key, def]) => [
4144
- key,
4145
- this.makePrimitiveFilterSchema(def.type, !!def.optional)
4146
- ]))).optional();
4214
+ fields[uniqueField.name] = import_v4.z.object(Object.fromEntries(Object.entries(uniqueField.defs).map(([key, def]) => {
4215
+ (0, import_common_helpers9.invariant)(!def.relation, "unique field cannot be a relation");
4216
+ let fieldSchema;
4217
+ const enumDef = getEnum(this.schema, def.type);
4218
+ if (enumDef) {
4219
+ if (Object.keys(enumDef).length > 0) {
4220
+ fieldSchema = this.makeEnumFilterSchema(enumDef, !!def.optional);
4221
+ } else {
4222
+ fieldSchema = import_v4.z.never();
4223
+ }
4224
+ } else {
4225
+ fieldSchema = this.makePrimitiveFilterSchema(def.type, !!def.optional);
4226
+ }
4227
+ return [
4228
+ key,
4229
+ fieldSchema
4230
+ ];
4231
+ }))).optional();
4147
4232
  }
4148
4233
  }
4149
4234
  }
@@ -4193,7 +4278,7 @@ var InputValidator = class {
4193
4278
  });
4194
4279
  }
4195
4280
  makePrimitiveFilterSchema(type, optional) {
4196
- return (0, import_ts_pattern14.match)(type).with("String", () => this.makeStringFilterSchema(optional)).with(import_ts_pattern14.P.union("Int", "Float", "Decimal", "BigInt"), (type2) => this.makeNumberFilterSchema(this.makePrimitiveSchema(type2), optional)).with("Boolean", () => this.makeBooleanFilterSchema(optional)).with("DateTime", () => this.makeDateTimeFilterSchema(optional)).with("Bytes", () => this.makeBytesFilterSchema(optional)).exhaustive();
4281
+ return (0, import_ts_pattern14.match)(type).with("String", () => this.makeStringFilterSchema(optional)).with(import_ts_pattern14.P.union("Int", "Float", "Decimal", "BigInt"), (type2) => this.makeNumberFilterSchema(this.makePrimitiveSchema(type2), optional)).with("Boolean", () => this.makeBooleanFilterSchema(optional)).with("DateTime", () => this.makeDateTimeFilterSchema(optional)).with("Bytes", () => this.makeBytesFilterSchema(optional)).with("Json", () => import_v4.z.any()).with("Unsupported", () => import_v4.z.never()).exhaustive();
4197
4282
  }
4198
4283
  makeDateTimeFilterSchema(optional) {
4199
4284
  return this.makeCommonPrimitiveFilterSchema(import_v4.z.union([
@@ -4387,8 +4472,8 @@ var InputValidator = class {
4387
4472
  return this.refineForSelectOmitMutuallyExclusive(result).optional();
4388
4473
  }
4389
4474
  makeCreateDataSchema(model, canBeArray, withoutFields = [], withoutRelationFields = false) {
4390
- const regularAndFkFields = {};
4391
- const regularAndRelationFields = {};
4475
+ const uncheckedVariantFields = {};
4476
+ const checkedVariantFields = {};
4392
4477
  const modelDef = requireModel(this.schema, model);
4393
4478
  const hasRelation = !withoutRelationFields && Object.entries(modelDef.fields).some(([f, def]) => !withoutFields.includes(f) && def.relation);
4394
4479
  Object.keys(modelDef.fields).forEach((field) => {
@@ -4430,7 +4515,10 @@ var InputValidator = class {
4430
4515
  if (fieldDef.optional && !fieldDef.array) {
4431
4516
  fieldSchema = fieldSchema.nullable();
4432
4517
  }
4433
- regularAndRelationFields[field] = fieldSchema;
4518
+ checkedVariantFields[field] = fieldSchema;
4519
+ if (fieldDef.array || !fieldDef.relation.references) {
4520
+ uncheckedVariantFields[field] = fieldSchema;
4521
+ }
4434
4522
  } else {
4435
4523
  let fieldSchema = this.makePrimitiveSchema(fieldDef.type);
4436
4524
  if (fieldDef.array) {
@@ -4447,23 +4535,23 @@ var InputValidator = class {
4447
4535
  if (fieldDef.optional) {
4448
4536
  fieldSchema = fieldSchema.nullable();
4449
4537
  }
4450
- regularAndFkFields[field] = fieldSchema;
4538
+ uncheckedVariantFields[field] = fieldSchema;
4451
4539
  if (!fieldDef.foreignKeyFor) {
4452
- regularAndRelationFields[field] = fieldSchema;
4540
+ checkedVariantFields[field] = fieldSchema;
4453
4541
  }
4454
4542
  }
4455
4543
  });
4456
4544
  if (!hasRelation) {
4457
- return this.orArray(import_v4.z.object(regularAndFkFields).strict(), canBeArray);
4545
+ return this.orArray(import_v4.z.object(uncheckedVariantFields).strict(), canBeArray);
4458
4546
  } else {
4459
4547
  return import_v4.z.union([
4460
- import_v4.z.object(regularAndFkFields).strict(),
4461
- import_v4.z.object(regularAndRelationFields).strict(),
4548
+ import_v4.z.object(uncheckedVariantFields).strict(),
4549
+ import_v4.z.object(checkedVariantFields).strict(),
4462
4550
  ...canBeArray ? [
4463
- import_v4.z.array(import_v4.z.object(regularAndFkFields).strict())
4551
+ import_v4.z.array(import_v4.z.object(uncheckedVariantFields).strict())
4464
4552
  ] : [],
4465
4553
  ...canBeArray ? [
4466
- import_v4.z.array(import_v4.z.object(regularAndRelationFields).strict())
4554
+ import_v4.z.array(import_v4.z.object(checkedVariantFields).strict())
4467
4555
  ] : []
4468
4556
  ]);
4469
4557
  }
@@ -4508,7 +4596,7 @@ var InputValidator = class {
4508
4596
  fields["deleteMany"] = this.makeDeleteRelationDataSchema(fieldType, true, false).optional();
4509
4597
  }
4510
4598
  }
4511
- return import_v4.z.object(fields).strict().refine((v) => Object.keys(v).length > 0, "At least one action is required");
4599
+ return import_v4.z.object(fields).strict();
4512
4600
  }
4513
4601
  makeSetDataSchema(model, canBeArray) {
4514
4602
  return this.orArray(this.makeWhereSchema(model, true), canBeArray);
@@ -4945,6 +5033,7 @@ __name(performanceNow, "performanceNow");
4945
5033
  // src/client/executor/zenstack-query-executor.ts
4946
5034
  var import_kysely13 = require("kysely");
4947
5035
  var import_nanoid2 = require("nanoid");
5036
+ var import_node_util2 = require("util");
4948
5037
  var import_ts_pattern15 = require("ts-pattern");
4949
5038
 
4950
5039
  // src/client/executor/name-mapper.ts
@@ -5179,7 +5268,9 @@ var ZenStackQueryExecutor = class _ZenStackQueryExecutor extends import_kysely13
5179
5268
  mutationInterceptionInfo = await this.callMutationInterceptionFilters(queryNode);
5180
5269
  }
5181
5270
  const task = /* @__PURE__ */ __name(async () => {
5182
- await this.callBeforeMutationHooks(queryNode, mutationInterceptionInfo);
5271
+ if (this.isMutationNode(queryNode)) {
5272
+ await this.callBeforeMutationHooks(queryNode, mutationInterceptionInfo);
5273
+ }
5183
5274
  const oldQueryNode = queryNode;
5184
5275
  if ((import_kysely13.InsertQueryNode.is(queryNode) || import_kysely13.DeleteQueryNode.is(queryNode)) && mutationInterceptionInfo?.loadAfterMutationEntity) {
5185
5276
  queryNode = {
@@ -5191,18 +5282,17 @@ var ZenStackQueryExecutor = class _ZenStackQueryExecutor extends import_kysely13
5191
5282
  }
5192
5283
  const queryParams = compiledQuery.$raw ? compiledQuery.parameters : void 0;
5193
5284
  const result = await this.proceedQueryWithKyselyInterceptors(queryNode, queryParams, queryId);
5194
- await this.callAfterQueryInterceptionFilters(result, queryNode, mutationInterceptionInfo);
5285
+ if (this.isMutationNode(queryNode)) {
5286
+ await this.callAfterQueryInterceptionFilters(result, queryNode, mutationInterceptionInfo);
5287
+ }
5195
5288
  if (oldQueryNode !== queryNode) {
5196
5289
  }
5197
5290
  return result;
5198
5291
  }, "task");
5199
- return this.executeWithTransaction(task, !!mutationInterceptionInfo?.useTransactionForMutation);
5292
+ return task();
5200
5293
  }
5201
5294
  proceedQueryWithKyselyInterceptors(queryNode, parameters, queryId) {
5202
5295
  let proceed = /* @__PURE__ */ __name((q) => this.proceedQuery(q, parameters, queryId), "proceed");
5203
- const makeTx = /* @__PURE__ */ __name((p) => (callback) => {
5204
- return this.executeWithTransaction(() => callback(p));
5205
- }, "makeTx");
5206
5296
  const hooks = this.options.plugins?.filter((plugin) => typeof plugin.onKyselyQuery === "function").map((plugin) => plugin.onKyselyQuery.bind(plugin)) ?? [];
5207
5297
  for (const hook of hooks) {
5208
5298
  const _proceed = proceed;
@@ -5212,8 +5302,7 @@ var ZenStackQueryExecutor = class _ZenStackQueryExecutor extends import_kysely13
5212
5302
  schema: this.client.$schema,
5213
5303
  kysely: this.kysely,
5214
5304
  query,
5215
- proceed: _proceed,
5216
- transaction: makeTx(_proceed)
5305
+ proceed: _proceed
5217
5306
  });
5218
5307
  }, "proceed");
5219
5308
  }
@@ -5229,9 +5318,14 @@ var ZenStackQueryExecutor = class _ZenStackQueryExecutor extends import_kysely13
5229
5318
  };
5230
5319
  }
5231
5320
  try {
5232
- return this.driver.txConnection ? await super.withConnectionProvider(new import_kysely13.SingleConnectionProvider(this.driver.txConnection)).executeQuery(compiled, queryId) : await super.executeQuery(compiled, queryId);
5321
+ return await super.executeQuery(compiled, queryId);
5233
5322
  } catch (err) {
5234
- throw new QueryError(`Failed to execute query: ${err}, sql: ${compiled.sql}, parameters: ${compiled.parameters}`);
5323
+ let message = `Failed to execute query: ${err}, sql: ${compiled.sql}`;
5324
+ if (this.options.debug) {
5325
+ message += `, parameters:
5326
+ ${compiled.parameters.map((p) => (0, import_node_util2.inspect)(p)).join("\n")}`;
5327
+ }
5328
+ throw new QueryError(message, err);
5235
5329
  }
5236
5330
  }
5237
5331
  isMutationNode(queryNode) {
@@ -5259,24 +5353,9 @@ var ZenStackQueryExecutor = class _ZenStackQueryExecutor extends import_kysely13
5259
5353
  return new _ZenStackQueryExecutor(this.client, this.driver, this.compiler, this.adapter, this.connectionProvider, []);
5260
5354
  }
5261
5355
  withConnectionProvider(connectionProvider) {
5262
- return new _ZenStackQueryExecutor(this.client, this.driver, this.compiler, this.adapter, connectionProvider);
5263
- }
5264
- async executeWithTransaction(callback, useTransaction = true) {
5265
- if (!useTransaction || this.driver.txConnection) {
5266
- return callback();
5267
- } else {
5268
- return this.provideConnection(async (connection) => {
5269
- try {
5270
- await this.driver.beginTransaction(connection, {});
5271
- const result = await callback();
5272
- await this.driver.commitTransaction(connection);
5273
- return result;
5274
- } catch (error) {
5275
- await this.driver.rollbackTransaction(connection);
5276
- throw error;
5277
- }
5278
- });
5279
- }
5356
+ const newExecutor = new _ZenStackQueryExecutor(this.client, this.driver, this.compiler, this.adapter, connectionProvider);
5357
+ newExecutor.client = this.client.withExecutor(newExecutor);
5358
+ return newExecutor;
5280
5359
  }
5281
5360
  get hasMutationHooks() {
5282
5361
  return this.client.$options.plugins?.some((plugin) => plugin.beforeEntityMutation || plugin.afterEntityMutation);
@@ -5318,14 +5397,13 @@ var ZenStackQueryExecutor = class _ZenStackQueryExecutor extends import_kysely13
5318
5397
  queryNode
5319
5398
  });
5320
5399
  result.intercept ||= filterResult.intercept;
5321
- result.useTransactionForMutation ||= filterResult.useTransactionForMutation;
5322
5400
  result.loadBeforeMutationEntity ||= filterResult.loadBeforeMutationEntity;
5323
5401
  result.loadAfterMutationEntity ||= filterResult.loadAfterMutationEntity;
5324
5402
  }
5325
5403
  }
5326
5404
  let beforeMutationEntities;
5327
5405
  if (result.loadBeforeMutationEntity && (import_kysely13.UpdateQueryNode.is(queryNode) || import_kysely13.DeleteQueryNode.is(queryNode))) {
5328
- beforeMutationEntities = await this.loadEntities(this.kysely, mutationModel, where);
5406
+ beforeMutationEntities = await this.loadEntities(mutationModel, where);
5329
5407
  }
5330
5408
  return {
5331
5409
  ...result,
@@ -5338,15 +5416,14 @@ var ZenStackQueryExecutor = class _ZenStackQueryExecutor extends import_kysely13
5338
5416
  return void 0;
5339
5417
  }
5340
5418
  }
5341
- callBeforeMutationHooks(queryNode, mutationInterceptionInfo) {
5419
+ async callBeforeMutationHooks(queryNode, mutationInterceptionInfo) {
5342
5420
  if (!mutationInterceptionInfo?.intercept) {
5343
5421
  return;
5344
5422
  }
5345
5423
  if (this.options.plugins) {
5346
5424
  for (const plugin of this.options.plugins) {
5347
5425
  if (plugin.beforeEntityMutation) {
5348
- plugin.beforeEntityMutation({
5349
- // context: this.queryContext,
5426
+ await plugin.beforeEntityMutation({
5350
5427
  model: this.getMutationModel(queryNode),
5351
5428
  action: mutationInterceptionInfo.action,
5352
5429
  queryNode,
@@ -5367,12 +5444,12 @@ var ZenStackQueryExecutor = class _ZenStackQueryExecutor extends import_kysely13
5367
5444
  let afterMutationEntities = void 0;
5368
5445
  if (mutationInterceptionInfo.loadAfterMutationEntity) {
5369
5446
  if (import_kysely13.UpdateQueryNode.is(queryNode)) {
5370
- afterMutationEntities = await this.loadEntities(this.kysely, mutationModel, mutationInterceptionInfo.where);
5447
+ afterMutationEntities = await this.loadEntities(mutationModel, mutationInterceptionInfo.where);
5371
5448
  } else {
5372
5449
  afterMutationEntities = queryResult.rows;
5373
5450
  }
5374
5451
  }
5375
- plugin.afterEntityMutation({
5452
+ await plugin.afterEntityMutation({
5376
5453
  model: this.getMutationModel(queryNode),
5377
5454
  action: mutationInterceptionInfo.action,
5378
5455
  queryNode,
@@ -5383,17 +5460,17 @@ var ZenStackQueryExecutor = class _ZenStackQueryExecutor extends import_kysely13
5383
5460
  }
5384
5461
  }
5385
5462
  }
5386
- async loadEntities(kysely, model, where) {
5387
- const selectQuery = kysely.selectFrom(model).selectAll();
5463
+ async loadEntities(model, where) {
5464
+ const selectQuery = this.kysely.selectFrom(model).selectAll();
5388
5465
  let selectQueryNode = selectQuery.toOperationNode();
5389
5466
  selectQueryNode = {
5390
5467
  ...selectQueryNode,
5391
5468
  where: this.andNodes(selectQueryNode.where, where)
5392
5469
  };
5393
- const compiled = kysely.getExecutor().compileQuery(selectQueryNode, {
5470
+ const compiled = this.compileQuery(selectQueryNode);
5471
+ const result = await this.executeQuery(compiled, {
5394
5472
  queryId: `zenstack-${(0, import_nanoid2.nanoid)()}`
5395
5473
  });
5396
- const result = await kysely.executeQuery(compiled);
5397
5474
  return result.rows;
5398
5475
  }
5399
5476
  andNodes(condition1, condition2) {
@@ -5422,7 +5499,7 @@ __export(functions_exports, {
5422
5499
  search: () => search,
5423
5500
  startsWith: () => startsWith
5424
5501
  });
5425
- var import_common_helpers9 = require("@zenstackhq/common-helpers");
5502
+ var import_common_helpers10 = require("@zenstackhq/common-helpers");
5426
5503
  var import_kysely14 = require("kysely");
5427
5504
  var import_ts_pattern16 = require("ts-pattern");
5428
5505
  var contains = /* @__PURE__ */ __name((eb, args) => {
@@ -5529,8 +5606,8 @@ var currentOperation = /* @__PURE__ */ __name((_eb, args, { operation }) => {
5529
5606
  }, "currentOperation");
5530
5607
  function processCasing(casing, result, model) {
5531
5608
  const opNode = casing.toOperationNode();
5532
- (0, import_common_helpers9.invariant)(import_kysely14.ValueNode.is(opNode) && typeof opNode.value === "string", '"casting" parameter must be a string value');
5533
- result = (0, import_ts_pattern16.match)(opNode.value).with("original", () => model).with("upper", () => result.toUpperCase()).with("lower", () => result.toLowerCase()).with("capitalize", () => (0, import_common_helpers9.upperCaseFirst)(result)).with("uncapitalize", () => (0, import_common_helpers9.lowerCaseFirst)(result)).otherwise(() => {
5609
+ (0, import_common_helpers10.invariant)(import_kysely14.ValueNode.is(opNode) && typeof opNode.value === "string", '"casting" parameter must be a string value');
5610
+ result = (0, import_ts_pattern16.match)(opNode.value).with("original", () => model).with("upper", () => result.toUpperCase()).with("lower", () => result.toLowerCase()).with("capitalize", () => (0, import_common_helpers10.upperCaseFirst)(result)).with("uncapitalize", () => (0, import_common_helpers10.lowerCaseFirst)(result)).otherwise(() => {
5534
5611
  throw new Error(`Invalid casing value: ${opNode.value}. Must be "original", "upper", "lower", "capitalize", or "uncapitalize".`);
5535
5612
  });
5536
5613
  return result;
@@ -5538,7 +5615,7 @@ function processCasing(casing, result, model) {
5538
5615
  __name(processCasing, "processCasing");
5539
5616
 
5540
5617
  // src/client/helpers/schema-db-pusher.ts
5541
- var import_common_helpers10 = require("@zenstackhq/common-helpers");
5618
+ var import_common_helpers11 = require("@zenstackhq/common-helpers");
5542
5619
  var import_kysely15 = require("kysely");
5543
5620
  var import_ts_pattern17 = require("ts-pattern");
5544
5621
  var SchemaDbPusher = class {
@@ -5595,7 +5672,7 @@ var SchemaDbPusher = class {
5595
5672
  }
5596
5673
  addUniqueConstraint(table, modelDef) {
5597
5674
  for (const [key, value] of Object.entries(modelDef.uniqueFields)) {
5598
- (0, import_common_helpers10.invariant)(typeof value === "object", "expecting an object");
5675
+ (0, import_common_helpers11.invariant)(typeof value === "object", "expecting an object");
5599
5676
  if ("type" in value) {
5600
5677
  const fieldDef = modelDef.fields[key];
5601
5678
  if (fieldDef.unique) {
@@ -5641,7 +5718,7 @@ var SchemaDbPusher = class {
5641
5718
  return "serial";
5642
5719
  }
5643
5720
  const type = fieldDef.type;
5644
- const result = (0, import_ts_pattern17.match)(type).with("String", () => "text").with("Boolean", () => "boolean").with("Int", () => "integer").with("Float", () => "real").with("BigInt", () => "bigint").with("Decimal", () => "decimal").with("DateTime", () => "timestamp").with("Bytes", () => this.schema.provider.type === "postgresql" ? "bytea" : "blob").otherwise(() => {
5721
+ const result = (0, import_ts_pattern17.match)(type).with("String", () => "text").with("Boolean", () => "boolean").with("Int", () => "integer").with("Float", () => "real").with("BigInt", () => "bigint").with("Decimal", () => "decimal").with("DateTime", () => "timestamp").with("Bytes", () => this.schema.provider.type === "postgresql" ? "bytea" : "blob").with("Json", () => "jsonb").otherwise(() => {
5645
5722
  throw new Error(`Unsupported field type: ${type}`);
5646
5723
  });
5647
5724
  if (fieldDef.array) {
@@ -5654,7 +5731,7 @@ var SchemaDbPusher = class {
5654
5731
  return fieldDef.default && ExpressionUtils.isCall(fieldDef.default) && fieldDef.default.function === "autoincrement";
5655
5732
  }
5656
5733
  addForeignKeyConstraint(table, model, fieldName, fieldDef) {
5657
- (0, import_common_helpers10.invariant)(fieldDef.relation, "field must be a relation");
5734
+ (0, import_common_helpers11.invariant)(fieldDef.relation, "field must be a relation");
5658
5735
  if (!fieldDef.relation.fields || !fieldDef.relation.references) {
5659
5736
  return table;
5660
5737
  }
@@ -5675,11 +5752,11 @@ var SchemaDbPusher = class {
5675
5752
  };
5676
5753
 
5677
5754
  // src/client/promise.ts
5678
- function createDeferredPromise(callback) {
5755
+ function createZenStackPromise(callback) {
5679
5756
  let promise;
5680
- const cb = /* @__PURE__ */ __name(() => {
5757
+ const cb = /* @__PURE__ */ __name((txClient) => {
5681
5758
  try {
5682
- return promise ??= valueToPromise(callback());
5759
+ return promise ??= valueToPromise(callback(txClient));
5683
5760
  } catch (err) {
5684
5761
  return Promise.reject(err);
5685
5762
  }
@@ -5694,10 +5771,11 @@ function createDeferredPromise(callback) {
5694
5771
  finally(onFinally) {
5695
5772
  return cb().finally(onFinally);
5696
5773
  },
5774
+ cb,
5697
5775
  [Symbol.toStringTag]: "ZenStackPromise"
5698
5776
  };
5699
5777
  }
5700
- __name(createDeferredPromise, "createDeferredPromise");
5778
+ __name(createZenStackPromise, "createZenStackPromise");
5701
5779
  function valueToPromise(thing) {
5702
5780
  if (typeof thing === "object" && typeof thing?.then === "function") {
5703
5781
  return thing;
@@ -5708,7 +5786,7 @@ function valueToPromise(thing) {
5708
5786
  __name(valueToPromise, "valueToPromise");
5709
5787
 
5710
5788
  // src/client/result-processor.ts
5711
- var import_common_helpers11 = require("@zenstackhq/common-helpers");
5789
+ var import_common_helpers12 = require("@zenstackhq/common-helpers");
5712
5790
  var import_decimal2 = __toESM(require("decimal.js"), 1);
5713
5791
  var import_ts_pattern18 = require("ts-pattern");
5714
5792
  var ResultProcessor = class {
@@ -5783,20 +5861,20 @@ var ResultProcessor = class {
5783
5861
  return this.doProcessResult(relationData, fieldDef.type);
5784
5862
  }
5785
5863
  transformScalar(value, type) {
5786
- return (0, import_ts_pattern18.match)(type).with("Boolean", () => this.transformBoolean(value)).with("DateTime", () => this.transformDate(value)).with("Bytes", () => this.transformBytes(value)).with("Decimal", () => this.transformDecimal(value)).with("BigInt", () => this.transformBigInt(value)).otherwise(() => value);
5864
+ return (0, import_ts_pattern18.match)(type).with("Boolean", () => this.transformBoolean(value)).with("DateTime", () => this.transformDate(value)).with("Bytes", () => this.transformBytes(value)).with("Decimal", () => this.transformDecimal(value)).with("BigInt", () => this.transformBigInt(value)).with("Json", () => this.transformJson(value)).otherwise(() => value);
5787
5865
  }
5788
5866
  transformDecimal(value) {
5789
5867
  if (value instanceof import_decimal2.default) {
5790
5868
  return value;
5791
5869
  }
5792
- (0, import_common_helpers11.invariant)(typeof value === "string" || typeof value === "number" || value instanceof import_decimal2.default, `Expected string, number or Decimal, got ${typeof value}`);
5870
+ (0, import_common_helpers12.invariant)(typeof value === "string" || typeof value === "number" || value instanceof import_decimal2.default, `Expected string, number or Decimal, got ${typeof value}`);
5793
5871
  return new import_decimal2.default(value);
5794
5872
  }
5795
5873
  transformBigInt(value) {
5796
5874
  if (typeof value === "bigint") {
5797
5875
  return value;
5798
5876
  }
5799
- (0, import_common_helpers11.invariant)(typeof value === "string" || typeof value === "number", `Expected string or number, got ${typeof value}`);
5877
+ (0, import_common_helpers12.invariant)(typeof value === "string" || typeof value === "number", `Expected string or number, got ${typeof value}`);
5800
5878
  return BigInt(value);
5801
5879
  }
5802
5880
  transformBoolean(value) {
@@ -5815,6 +5893,9 @@ var ResultProcessor = class {
5815
5893
  return Buffer.isBuffer(value) ? Uint8Array.from(value) : value;
5816
5894
  }
5817
5895
  fixReversedResult(data, model, args) {
5896
+ if (!data) {
5897
+ return;
5898
+ }
5818
5899
  if (Array.isArray(data) && typeof args === "object" && args && args.take !== void 0 && args.take < 0) {
5819
5900
  data.reverse();
5820
5901
  }
@@ -5828,13 +5909,19 @@ var ResultProcessor = class {
5828
5909
  continue;
5829
5910
  }
5830
5911
  const fieldDef = getField(this.schema, model, field);
5831
- if (!fieldDef?.relation) {
5912
+ if (!fieldDef || !fieldDef.relation || !fieldDef.array) {
5832
5913
  continue;
5833
5914
  }
5834
5915
  this.fixReversedResult(row[field], fieldDef.type, value);
5835
5916
  }
5836
5917
  }
5837
5918
  }
5919
+ transformJson(value) {
5920
+ return (0, import_ts_pattern18.match)(this.schema.provider.type).with("sqlite", () => {
5921
+ (0, import_common_helpers12.invariant)(typeof value === "string", "Expected string, got " + typeof value);
5922
+ return JSON.parse(value);
5923
+ }).otherwise(() => value);
5924
+ }
5838
5925
  };
5839
5926
 
5840
5927
  // src/client/client-impl.ts
@@ -5853,7 +5940,7 @@ var ClientImpl = class _ClientImpl {
5853
5940
  $schema;
5854
5941
  kyselyProps;
5855
5942
  auth;
5856
- constructor(schema, options, baseClient) {
5943
+ constructor(schema, options, baseClient, executor) {
5857
5944
  this.schema = schema;
5858
5945
  this.options = options;
5859
5946
  this.$schema = schema;
@@ -5865,16 +5952,16 @@ var ClientImpl = class _ClientImpl {
5865
5952
  if (baseClient) {
5866
5953
  this.kyselyProps = {
5867
5954
  ...baseClient.kyselyProps,
5868
- executor: new ZenStackQueryExecutor(this, baseClient.kyselyProps.driver, baseClient.kyselyProps.dialect.createQueryCompiler(), baseClient.kyselyProps.dialect.createAdapter(), new import_kysely16.DefaultConnectionProvider(baseClient.kyselyProps.driver))
5955
+ executor: executor ?? new ZenStackQueryExecutor(this, baseClient.kyselyProps.driver, baseClient.kyselyProps.dialect.createQueryCompiler(), baseClient.kyselyProps.dialect.createAdapter(), new import_kysely16.DefaultConnectionProvider(baseClient.kyselyProps.driver))
5869
5956
  };
5870
5957
  this.kyselyRaw = baseClient.kyselyRaw;
5958
+ this.auth = baseClient.auth;
5871
5959
  } else {
5872
5960
  const dialect = this.getKyselyDialect();
5873
5961
  const driver = new ZenStackDriver(dialect.createDriver(), new import_kysely16.Log(this.$options.log ?? []));
5874
5962
  const compiler = dialect.createQueryCompiler();
5875
5963
  const adapter = dialect.createAdapter();
5876
5964
  const connectionProvider = new import_kysely16.DefaultConnectionProvider(driver);
5877
- const executor = new ZenStackQueryExecutor(this, driver, compiler, adapter, connectionProvider);
5878
5965
  this.kyselyProps = {
5879
5966
  config: {
5880
5967
  dialect,
@@ -5882,7 +5969,7 @@ var ClientImpl = class _ClientImpl {
5882
5969
  },
5883
5970
  dialect,
5884
5971
  driver,
5885
- executor
5972
+ executor: executor ?? new ZenStackQueryExecutor(this, driver, compiler, adapter, connectionProvider)
5886
5973
  };
5887
5974
  this.kyselyRaw = new import_kysely16.Kysely({
5888
5975
  ...this.kyselyProps,
@@ -5898,6 +5985,15 @@ var ClientImpl = class _ClientImpl {
5898
5985
  get $qbRaw() {
5899
5986
  return this.kyselyRaw;
5900
5987
  }
5988
+ get isTransaction() {
5989
+ return this.kysely.isTransaction;
5990
+ }
5991
+ /**
5992
+ * Create a new client with a new query executor.
5993
+ */
5994
+ withExecutor(executor) {
5995
+ return new _ClientImpl(this.schema, this.$options, this, executor);
5996
+ }
5901
5997
  getKyselyDialect() {
5902
5998
  return (0, import_ts_pattern19.match)(this.schema.provider.type).with("sqlite", () => this.makeSqliteKyselyDialect()).with("postgresql", () => this.makePostgresKyselyDialect()).exhaustive();
5903
5999
  }
@@ -5907,12 +6003,49 @@ var ClientImpl = class _ClientImpl {
5907
6003
  makeSqliteKyselyDialect() {
5908
6004
  return new import_kysely16.SqliteDialect(this.options.dialectConfig);
5909
6005
  }
5910
- async $transaction(callback) {
5911
- return this.kysely.transaction().execute((tx) => {
5912
- const txClient = new _ClientImpl(this.schema, this.$options);
6006
+ // implementation
6007
+ async $transaction(input, options) {
6008
+ (0, import_common_helpers13.invariant)(typeof input === "function" || Array.isArray(input) && input.every((p) => p.then && p.cb), "Invalid transaction input, expected a function or an array of ZenStackPromise");
6009
+ if (typeof input === "function") {
6010
+ return this.interactiveTransaction(input, options);
6011
+ } else {
6012
+ return this.sequentialTransaction(input, options);
6013
+ }
6014
+ }
6015
+ async interactiveTransaction(callback, options) {
6016
+ if (this.kysely.isTransaction) {
6017
+ return callback(this);
6018
+ } else {
6019
+ let txBuilder = this.kysely.transaction();
6020
+ if (options?.isolationLevel) {
6021
+ txBuilder = txBuilder.setIsolationLevel(options.isolationLevel);
6022
+ }
6023
+ return txBuilder.execute((tx) => {
6024
+ const txClient = new _ClientImpl(this.schema, this.$options, this);
6025
+ txClient.kysely = tx;
6026
+ return callback(txClient);
6027
+ });
6028
+ }
6029
+ }
6030
+ async sequentialTransaction(arg, options) {
6031
+ const execute = /* @__PURE__ */ __name(async (tx) => {
6032
+ const txClient = new _ClientImpl(this.schema, this.$options, this);
5913
6033
  txClient.kysely = tx;
5914
- return callback(txClient);
5915
- });
6034
+ const result = [];
6035
+ for (const promise of arg) {
6036
+ result.push(await promise.cb(txClient));
6037
+ }
6038
+ return result;
6039
+ }, "execute");
6040
+ if (this.kysely.isTransaction) {
6041
+ return execute(this.kysely);
6042
+ } else {
6043
+ let txBuilder = this.kysely.transaction();
6044
+ if (options?.isolationLevel) {
6045
+ txBuilder = txBuilder.setIsolationLevel(options.isolationLevel);
6046
+ }
6047
+ return txBuilder.execute((tx) => execute(tx));
6048
+ }
5916
6049
  }
5917
6050
  get $procedures() {
5918
6051
  return Object.keys(this.$schema.procedures ?? {}).reduce((acc, name) => {
@@ -5975,26 +6108,26 @@ var ClientImpl = class _ClientImpl {
5975
6108
  return this.auth;
5976
6109
  }
5977
6110
  $executeRaw(query, ...values) {
5978
- return createDeferredPromise(async () => {
6111
+ return createZenStackPromise(async () => {
5979
6112
  const result = await (0, import_kysely16.sql)(query, ...values).execute(this.kysely);
5980
6113
  return Number(result.numAffectedRows ?? 0);
5981
6114
  });
5982
6115
  }
5983
6116
  $executeRawUnsafe(query, ...values) {
5984
- return createDeferredPromise(async () => {
6117
+ return createZenStackPromise(async () => {
5985
6118
  const compiledQuery = this.createRawCompiledQuery(query, values);
5986
6119
  const result = await this.kysely.executeQuery(compiledQuery);
5987
6120
  return Number(result.numAffectedRows ?? 0);
5988
6121
  });
5989
6122
  }
5990
6123
  $queryRaw(query, ...values) {
5991
- return createDeferredPromise(async () => {
6124
+ return createZenStackPromise(async () => {
5992
6125
  const result = await (0, import_kysely16.sql)(query, ...values).execute(this.kysely);
5993
6126
  return result.rows;
5994
6127
  });
5995
6128
  }
5996
6129
  $queryRawUnsafe(query, ...values) {
5997
- return createDeferredPromise(async () => {
6130
+ return createZenStackPromise(async () => {
5998
6131
  const compiledQuery = this.createRawCompiledQuery(query, values);
5999
6132
  const result = await this.kysely.executeQuery(compiledQuery);
6000
6133
  return result.rows;
@@ -6029,9 +6162,9 @@ function createClientProxy(client) {
6029
6162
  __name(createClientProxy, "createClientProxy");
6030
6163
  function createModelCrudHandler(client, model, inputValidator, resultProcessor) {
6031
6164
  const createPromise = /* @__PURE__ */ __name((operation, args, handler, postProcess = false, throwIfNoResult = false) => {
6032
- return createDeferredPromise(async () => {
6033
- let proceed = /* @__PURE__ */ __name(async (_args, tx) => {
6034
- const _handler = tx ? handler.withClient(tx) : handler;
6165
+ return createZenStackPromise(async (txClient) => {
6166
+ let proceed = /* @__PURE__ */ __name(async (_args) => {
6167
+ const _handler = txClient ? handler.withClient(txClient) : handler;
6035
6168
  const r = await _handler.handle(operation, _args ?? args);
6036
6169
  if (!r && throwIfNoResult) {
6037
6170
  throw new NotFoundError(model);
@@ -6050,7 +6183,7 @@ function createModelCrudHandler(client, model, inputValidator, resultProcessor)
6050
6183
  for (const plugin of plugins) {
6051
6184
  if (plugin.onQuery && typeof plugin.onQuery === "object") {
6052
6185
  for (const [_model, modelHooks] of Object.entries(plugin.onQuery)) {
6053
- if (_model === (0, import_common_helpers12.lowerCaseFirst)(model) || _model === "$allModels") {
6186
+ if (_model === (0, import_common_helpers13.lowerCaseFirst)(model) || _model === "$allModels") {
6054
6187
  if (modelHooks && typeof modelHooks === "object") {
6055
6188
  for (const [op, opHooks] of Object.entries(modelHooks)) {
6056
6189
  if (op === operation || op === "$allOperations") {
@@ -6129,8 +6262,19 @@ function createModelCrudHandler(client, model, inputValidator, resultProcessor)
6129
6262
  };
6130
6263
  }
6131
6264
  __name(createModelCrudHandler, "createModelCrudHandler");
6265
+
6266
+ // src/client/plugin.ts
6267
+ function definePlugin(plugin) {
6268
+ return plugin;
6269
+ }
6270
+ __name(definePlugin, "definePlugin");
6132
6271
  // Annotate the CommonJS export names for ESM import in node:
6133
6272
  0 && (module.exports = {
6134
- ZenStackClient
6273
+ InputValidationError,
6274
+ InternalError,
6275
+ NotFoundError,
6276
+ QueryError,
6277
+ ZenStackClient,
6278
+ definePlugin
6135
6279
  });
6136
6280
  //# sourceMappingURL=index.cjs.map