@zenstackhq/runtime 3.0.0-alpha.7 → 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.js CHANGED
@@ -6,8 +6,8 @@ var __export = (target, all) => {
6
6
  };
7
7
 
8
8
  // src/client/client-impl.ts
9
- import { lowerCaseFirst as lowerCaseFirst2 } from "@zenstackhq/common-helpers";
10
- import { DefaultConnectionProvider, DefaultQueryExecutor as DefaultQueryExecutor2, Kysely, Log, PostgresDialect, SqliteDialect } from "kysely";
9
+ import { invariant as invariant12, lowerCaseFirst as lowerCaseFirst2 } from "@zenstackhq/common-helpers";
10
+ import { CompiledQuery, DefaultConnectionProvider, DefaultQueryExecutor as DefaultQueryExecutor2, Kysely, Log, PostgresDialect, sql as sql10, SqliteDialect } from "kysely";
11
11
  import { match as match19 } from "ts-pattern";
12
12
 
13
13
  // src/client/crud/operations/aggregate.ts
@@ -15,12 +15,24 @@ import { sql as sql5 } from "kysely";
15
15
  import { match as match9 } from "ts-pattern";
16
16
 
17
17
  // src/client/errors.ts
18
+ var InputValidationError = class extends Error {
19
+ static {
20
+ __name(this, "InputValidationError");
21
+ }
22
+ constructor(message, cause) {
23
+ super(message, {
24
+ cause
25
+ });
26
+ }
27
+ };
18
28
  var QueryError = class extends Error {
19
29
  static {
20
30
  __name(this, "QueryError");
21
31
  }
22
- constructor(message) {
23
- super(message);
32
+ constructor(message, cause) {
33
+ super(message, {
34
+ cause
35
+ });
24
36
  }
25
37
  };
26
38
  var InternalError = class extends Error {
@@ -48,7 +60,7 @@ __name(getModel, "getModel");
48
60
  function requireModel(schema, model) {
49
61
  const matchedName = Object.keys(schema.models).find((k) => k.toLowerCase() === model.toLowerCase());
50
62
  if (!matchedName) {
51
- throw new QueryError(`Model "${model}" not found`);
63
+ throw new QueryError(`Model "${model}" not found in schema`);
52
64
  }
53
65
  return schema.models[matchedName];
54
66
  }
@@ -173,7 +185,7 @@ function buildFieldRef(schema, model, field, options, eb, modelAlias) {
173
185
  computer = computedFields?.[model]?.[field];
174
186
  }
175
187
  if (!computer) {
176
- throw new QueryError(`Computed field "${field}" implementation not provided`);
188
+ throw new QueryError(`Computed field "${field}" implementation not provided for model "${model}"`);
177
189
  }
178
190
  return computer(eb);
179
191
  }
@@ -281,9 +293,10 @@ __name(safeJSONStringify, "safeJSONStringify");
281
293
 
282
294
  // src/client/crud/operations/base.ts
283
295
  import { createId } from "@paralleldrive/cuid2";
284
- import { invariant as invariant7 } from "@zenstackhq/common-helpers";
296
+ import { invariant as invariant7, isPlainObject as isPlainObject3 } from "@zenstackhq/common-helpers";
285
297
  import { expressionBuilder as expressionBuilder2, sql as sql4 } from "kysely";
286
298
  import { nanoid } from "nanoid";
299
+ import { inspect } from "util";
287
300
  import { match as match8 } from "ts-pattern";
288
301
  import { ulid } from "ulid";
289
302
  import * as uuid from "uuid";
@@ -343,7 +356,7 @@ var BaseCrudDialect = class {
343
356
  this.schema = schema;
344
357
  this.options = options;
345
358
  }
346
- transformPrimitive(value, _type) {
359
+ transformPrimitive(value, _type, _forArrayField) {
347
360
  return value;
348
361
  }
349
362
  buildFilter(eb, model, modelAlias, where) {
@@ -486,7 +499,7 @@ var BaseCrudDialect = class {
486
499
  if (_value === void 0) {
487
500
  continue;
488
501
  }
489
- const value = this.transformPrimitive(_value, fieldType);
502
+ const value = this.transformPrimitive(_value, fieldType, !!fieldDef.array);
490
503
  switch (key) {
491
504
  case "equals": {
492
505
  clauses.push(this.buildLiteralFilter(eb, fieldRef, fieldType, eb.val(value)));
@@ -524,10 +537,14 @@ var BaseCrudDialect = class {
524
537
  if (isEnum(this.schema, fieldDef.type)) {
525
538
  return this.buildEnumFilter(eb, modelAlias, field, fieldDef, payload);
526
539
  }
527
- return match(fieldDef.type).with("String", () => this.buildStringFilter(eb, modelAlias, field, payload)).with(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();
540
+ return match(fieldDef.type).with("String", () => this.buildStringFilter(eb, modelAlias, field, payload)).with(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", () => {
541
+ throw new InternalError("JSON filters are not supported yet");
542
+ }).with("Unsupported", () => {
543
+ throw new QueryError(`Unsupported field cannot be used in filters`);
544
+ }).exhaustive();
528
545
  }
529
546
  buildLiteralFilter(eb, lhs, type, rhs) {
530
- return eb(lhs, "=", rhs !== null && rhs !== void 0 ? this.transformPrimitive(rhs, type) : rhs);
547
+ return eb(lhs, "=", rhs !== null && rhs !== void 0 ? this.transformPrimitive(rhs, type, false) : rhs);
531
548
  }
532
549
  buildStandardFilter(eb, type, payload, lhs, getRhs, recurse, throwIfInvalid = false, onlyForKeys = void 0) {
533
550
  if (payload === null || !isPlainObject(payload)) {
@@ -614,22 +631,22 @@ var BaseCrudDialect = class {
614
631
  }
615
632
  }
616
633
  buildNumberFilter(eb, model, table, field, type, payload) {
617
- 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));
634
+ 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));
618
635
  return this.and(eb, ...conditions);
619
636
  }
620
637
  buildBooleanFilter(eb, table, field, payload) {
621
- const { conditions } = this.buildStandardFilter(eb, "Boolean", payload, sql.ref(`${table}.${field}`), (value) => this.transformPrimitive(value, "Boolean"), (value) => this.buildBooleanFilter(eb, table, field, value), true, [
638
+ const { conditions } = this.buildStandardFilter(eb, "Boolean", payload, sql.ref(`${table}.${field}`), (value) => this.transformPrimitive(value, "Boolean", false), (value) => this.buildBooleanFilter(eb, table, field, value), true, [
622
639
  "equals",
623
640
  "not"
624
641
  ]);
625
642
  return this.and(eb, ...conditions);
626
643
  }
627
644
  buildDateTimeFilter(eb, table, field, payload) {
628
- const { conditions } = this.buildStandardFilter(eb, "DateTime", payload, sql.ref(`${table}.${field}`), (value) => this.transformPrimitive(value, "DateTime"), (value) => this.buildDateTimeFilter(eb, table, field, value), true);
645
+ const { conditions } = this.buildStandardFilter(eb, "DateTime", payload, sql.ref(`${table}.${field}`), (value) => this.transformPrimitive(value, "DateTime", false), (value) => this.buildDateTimeFilter(eb, table, field, value), true);
629
646
  return this.and(eb, ...conditions);
630
647
  }
631
648
  buildBytesFilter(eb, table, field, payload) {
632
- const conditions = this.buildStandardFilter(eb, "Bytes", payload, sql.ref(`${table}.${field}`), (value) => this.transformPrimitive(value, "Bytes"), (value) => this.buildBytesFilter(eb, table, field, value), true, [
649
+ const conditions = this.buildStandardFilter(eb, "Bytes", payload, sql.ref(`${table}.${field}`), (value) => this.transformPrimitive(value, "Bytes", false), (value) => this.buildBytesFilter(eb, table, field, value), true, [
633
650
  "equals",
634
651
  "in",
635
652
  "notIn",
@@ -728,10 +745,10 @@ var BaseCrudDialect = class {
728
745
  return negated ? sort === "asc" ? "desc" : "asc" : sort;
729
746
  }
730
747
  true(eb) {
731
- return eb.lit(this.transformPrimitive(true, "Boolean"));
748
+ return eb.lit(this.transformPrimitive(true, "Boolean", false));
732
749
  }
733
750
  false(eb) {
734
- return eb.lit(this.transformPrimitive(false, "Boolean"));
751
+ return eb.lit(this.transformPrimitive(false, "Boolean", false));
735
752
  }
736
753
  isTrue(expression) {
737
754
  const node = expression.toOperationNode();
@@ -780,14 +797,18 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
780
797
  get provider() {
781
798
  return "postgresql";
782
799
  }
783
- transformPrimitive(value, type) {
800
+ transformPrimitive(value, type, forArrayField) {
784
801
  if (value === void 0) {
785
802
  return value;
786
803
  }
787
804
  if (Array.isArray(value)) {
788
- return value.map((v) => this.transformPrimitive(v, type));
805
+ if (type === "Json" && !forArrayField) {
806
+ return JSON.stringify(value);
807
+ } else {
808
+ return value.map((v) => this.transformPrimitive(v, type, false));
809
+ }
789
810
  } else {
790
- return match2(type).with("DateTime", () => value instanceof Date ? value : typeof value === "string" ? new Date(value) : value).otherwise(() => value);
811
+ return match2(type).with("DateTime", () => value instanceof Date ? value : typeof value === "string" ? new Date(value) : value).with("Decimal", () => value !== null ? value.toString() : value).otherwise(() => value);
791
812
  }
792
813
  }
793
814
  buildRelationSelection(query, model, relationField, parentAlias, payload) {
@@ -854,25 +875,33 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
854
875
  buildFieldRef(this.schema, relationModel, field, this.options, eb)
855
876
  ]).flatMap((v) => v));
856
877
  } else if (payload.select) {
857
- objArgs.push(...Object.entries(payload.select).filter(([, value]) => value).map(([field]) => [
858
- sql2.lit(field),
859
- buildFieldRef(this.schema, relationModel, field, this.options, eb)
860
- ]).flatMap((v) => v));
878
+ objArgs.push(...Object.entries(payload.select).filter(([, value]) => value).map(([field]) => {
879
+ const fieldDef = requireField(this.schema, relationModel, field);
880
+ const fieldValue = fieldDef.relation ? eb.ref(`${parentName}$${relationField}$${field}.$j`) : buildFieldRef(this.schema, relationModel, field, this.options, eb);
881
+ return [
882
+ sql2.lit(field),
883
+ fieldValue
884
+ ];
885
+ }).flatMap((v) => v));
861
886
  }
862
887
  if (typeof payload === "object" && payload.include && typeof payload.include === "object") {
863
888
  objArgs.push(...Object.entries(payload.include).filter(([, value]) => value).map(([field]) => [
864
889
  sql2.lit(field),
890
+ // reference the synthesized JSON field
865
891
  eb.ref(`${parentName}$${relationField}$${field}.$j`)
866
892
  ]).flatMap((v) => v));
867
893
  }
868
894
  return objArgs;
869
895
  }
870
- buildRelationJoins(model, relationField, qb, payload, parentName) {
896
+ buildRelationJoins(relationModel, relationField, qb, payload, parentName) {
871
897
  let result = qb;
872
- if (typeof payload === "object" && payload.include && typeof payload.include === "object") {
873
- Object.entries(payload.include).filter(([, value]) => value).forEach(([field, value]) => {
874
- result = this.buildRelationJSON(model, result, field, `${parentName}$${relationField}`, value);
875
- });
898
+ if (typeof payload === "object") {
899
+ const selectInclude = payload.include ?? payload.select;
900
+ if (selectInclude && typeof selectInclude === "object") {
901
+ Object.entries(selectInclude).filter(([, value]) => value).filter(([field]) => isRelationField(this.schema, relationModel, field)).forEach(([field, value]) => {
902
+ result = this.buildRelationJSON(relationModel, result, field, `${parentName}$${relationField}`, value);
903
+ });
904
+ }
876
905
  }
877
906
  return result;
878
907
  }
@@ -925,14 +954,14 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
925
954
  get provider() {
926
955
  return "sqlite";
927
956
  }
928
- transformPrimitive(value, type) {
957
+ transformPrimitive(value, type, _forArrayField) {
929
958
  if (value === void 0) {
930
959
  return value;
931
960
  }
932
961
  if (Array.isArray(value)) {
933
- return value.map((v) => this.transformPrimitive(v, type));
962
+ return value.map((v) => this.transformPrimitive(v, type, false));
934
963
  } else {
935
- return match3(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);
964
+ return match3(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);
936
965
  }
937
966
  }
938
967
  buildRelationSelection(query, model, relationField, parentAlias, payload) {
@@ -1537,11 +1566,11 @@ var ExpressionEvaluator = class {
1537
1566
  // src/plugins/policy/utils.ts
1538
1567
  import { AliasNode, AndNode, BinaryOperationNode, FunctionNode, OperatorNode, OrNode, ParensNode, ReferenceNode, TableNode, UnaryOperationNode, ValueNode } from "kysely";
1539
1568
  function trueNode(dialect) {
1540
- return ValueNode.createImmediate(dialect.transformPrimitive(true, "Boolean"));
1569
+ return ValueNode.createImmediate(dialect.transformPrimitive(true, "Boolean", false));
1541
1570
  }
1542
1571
  __name(trueNode, "trueNode");
1543
1572
  function falseNode(dialect) {
1544
- return ValueNode.createImmediate(dialect.transformPrimitive(false, "Boolean"));
1573
+ return ValueNode.createImmediate(dialect.transformPrimitive(false, "Boolean", false));
1545
1574
  }
1546
1575
  __name(falseNode, "falseNode");
1547
1576
  function isTrueNode(node) {
@@ -1799,7 +1828,7 @@ var ExpressionTransformer = class {
1799
1828
  }
1800
1829
  }
1801
1830
  transformValue(value, type) {
1802
- return ValueNode2.create(this.dialect.transformPrimitive(value, type) ?? null);
1831
+ return ValueNode2.create(this.dialect.transformPrimitive(value, type, false) ?? null);
1803
1832
  }
1804
1833
  _unary(expr2, context) {
1805
1834
  invariant5(expr2.op === "!", 'only "!" operator is supported');
@@ -2042,7 +2071,7 @@ var PolicyHandler = class extends OperationNodeTransformer {
2042
2071
  get kysely() {
2043
2072
  return this.client.$qb;
2044
2073
  }
2045
- async handle(node, proceed, transaction) {
2074
+ async handle(node, proceed) {
2046
2075
  if (!this.isCrudQueryNode(node)) {
2047
2076
  throw new RejectedByPolicyError(void 0, "non-CRUD queries are not allowed");
2048
2077
  }
@@ -2062,27 +2091,20 @@ var PolicyHandler = class extends OperationNodeTransformer {
2062
2091
  if (!mutationRequiresTransaction && !node.returning) {
2063
2092
  return proceed(this.transformNode(node));
2064
2093
  }
2065
- let readBackError = false;
2066
- const result = await transaction(async (txProceed) => {
2067
- if (InsertQueryNode.is(node)) {
2068
- await this.enforcePreCreatePolicy(node, txProceed);
2069
- }
2070
- const transformedNode = this.transformNode(node);
2071
- const result2 = await txProceed(transformedNode);
2072
- if (!this.onlyReturningId(node)) {
2073
- const readBackResult = await this.processReadBack(node, result2, txProceed);
2074
- if (readBackResult.rows.length !== result2.rows.length) {
2075
- readBackError = true;
2076
- }
2077
- return readBackResult;
2078
- } else {
2079
- return result2;
2094
+ if (InsertQueryNode.is(node)) {
2095
+ await this.enforcePreCreatePolicy(node, proceed);
2096
+ }
2097
+ const transformedNode = this.transformNode(node);
2098
+ const result = await proceed(transformedNode);
2099
+ if (!this.onlyReturningId(node)) {
2100
+ const readBackResult = await this.processReadBack(node, result, proceed);
2101
+ if (readBackResult.rows.length !== result.rows.length) {
2102
+ throw new RejectedByPolicyError(mutationModel, "result is not allowed to be read back");
2080
2103
  }
2081
- });
2082
- if (readBackError) {
2083
- throw new RejectedByPolicyError(mutationModel, "result is not allowed to be read back");
2104
+ return readBackResult;
2105
+ } else {
2106
+ return result;
2084
2107
  }
2085
- return result;
2086
2108
  }
2087
2109
  onlyReturningId(node) {
2088
2110
  if (!node.returning) {
@@ -2143,11 +2165,11 @@ var PolicyHandler = class extends OperationNodeTransformer {
2143
2165
  if (typeof item === "object" && item && "kind" in item) {
2144
2166
  invariant6(item.kind === "ValueNode", "expecting a ValueNode");
2145
2167
  result.push({
2146
- node: ValueNode3.create(this.dialect.transformPrimitive(item.value, fieldDef.type)),
2168
+ node: ValueNode3.create(this.dialect.transformPrimitive(item.value, fieldDef.type, !!fieldDef.array)),
2147
2169
  raw: item.value
2148
2170
  });
2149
2171
  } else {
2150
- const value = this.dialect.transformPrimitive(item, fieldDef.type);
2172
+ const value = this.dialect.transformPrimitive(item, fieldDef.type, !!fieldDef.array);
2151
2173
  if (Array.isArray(value)) {
2152
2174
  result.push({
2153
2175
  node: RawNode.createWithSql(this.dialect.buildArrayLiteralSQL(value)),
@@ -2378,9 +2400,18 @@ var PolicyPlugin = class {
2378
2400
  get description() {
2379
2401
  return "Enforces access policies defined in the schema.";
2380
2402
  }
2381
- onKyselyQuery({ query, client, proceed, transaction }) {
2403
+ onKyselyQuery({
2404
+ query,
2405
+ client,
2406
+ proceed
2407
+ /*, transaction*/
2408
+ }) {
2382
2409
  const handler = new PolicyHandler(client);
2383
- return handler.handle(query, proceed, transaction);
2410
+ return handler.handle(
2411
+ query,
2412
+ proceed
2413
+ /*, transaction*/
2414
+ );
2384
2415
  }
2385
2416
  };
2386
2417
 
@@ -2520,8 +2551,13 @@ var BaseOperationHandler = class {
2520
2551
  try {
2521
2552
  result = await query.execute();
2522
2553
  } catch (err) {
2523
- const { sql: sql10, parameters } = query.compile();
2524
- throw new QueryError(`Failed to execute query: ${err}, sql: ${sql10}, parameters: ${parameters}`);
2554
+ const { sql: sql11, parameters } = query.compile();
2555
+ let message = `Failed to execute query: ${err}, sql: ${sql11}`;
2556
+ if (this.options.debug) {
2557
+ message += `, parameters:
2558
+ ${parameters.map((p) => inspect(p)).join("\n")}`;
2559
+ }
2560
+ throw new QueryError(message, err);
2525
2561
  }
2526
2562
  if (inMemoryDistinct) {
2527
2563
  const distinctResult = [];
@@ -2580,30 +2616,20 @@ var BaseOperationHandler = class {
2580
2616
  for (const [field, value] of Object.entries(selections.select)) {
2581
2617
  const fieldDef = requireField(this.schema, model, field);
2582
2618
  const fieldModel = fieldDef.type;
2583
- const jointTable = `${parentAlias}$${field}$count`;
2584
- const joinPairs = buildJoinPairs(this.schema, model, parentAlias, field, jointTable);
2585
- query = query.leftJoin((eb2) => {
2586
- let result = eb2.selectFrom(fieldModel).selectAll();
2587
- if (value && typeof value === "object" && "where" in value && value.where && typeof value.where === "object") {
2588
- const filter = this.dialect.buildFilter(eb2, fieldModel, fieldModel, value.where);
2589
- result = result.where(filter);
2590
- }
2591
- return result.as(jointTable);
2592
- }, (join) => {
2593
- for (const [left, right] of joinPairs) {
2594
- join = join.onRef(left, "=", right);
2595
- }
2596
- return join;
2597
- });
2598
- jsonObject[field] = this.countIdDistinct(eb, fieldDef.type, jointTable);
2619
+ const joinPairs = buildJoinPairs(this.schema, model, parentAlias, field, fieldModel);
2620
+ let fieldCountQuery = eb.selectFrom(fieldModel).select(eb.fn.countAll().as(`_count$${field}`));
2621
+ for (const [left, right] of joinPairs) {
2622
+ fieldCountQuery = fieldCountQuery.whereRef(left, "=", right);
2623
+ }
2624
+ if (value && typeof value === "object" && "where" in value && value.where && typeof value.where === "object") {
2625
+ const filter = this.dialect.buildFilter(eb, fieldModel, fieldModel, value.where);
2626
+ fieldCountQuery = fieldCountQuery.where(filter);
2627
+ }
2628
+ jsonObject[field] = fieldCountQuery;
2599
2629
  }
2600
2630
  query = query.select((eb2) => this.dialect.buildJsonObject(eb2, jsonObject).as("_count"));
2601
2631
  return query;
2602
2632
  }
2603
- countIdDistinct(eb, model, table) {
2604
- const idFields = getIdFields(this.schema, model);
2605
- return eb.fn.count(sql4.join(idFields.map((f) => sql4.ref(`${table}.${f}`)))).distinct();
2606
- }
2607
2633
  buildSelectAllScalarFields(model, query, omit) {
2608
2634
  const modelDef = this.requireModel(model);
2609
2635
  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);
@@ -2669,14 +2695,14 @@ var BaseOperationHandler = class {
2669
2695
  const fieldDef = this.requireField(model, field);
2670
2696
  if (isScalarField(this.schema, model, field) || isForeignKeyField(this.schema, model, field)) {
2671
2697
  if (fieldDef.array && value && typeof value === "object" && "set" in value && Array.isArray(value.set)) {
2672
- createFields[field] = this.dialect.transformPrimitive(value.set, fieldDef.type);
2698
+ createFields[field] = this.dialect.transformPrimitive(value.set, fieldDef.type, true);
2673
2699
  } else {
2674
- createFields[field] = this.dialect.transformPrimitive(value, fieldDef.type);
2700
+ createFields[field] = this.dialect.transformPrimitive(value, fieldDef.type, !!fieldDef.array);
2675
2701
  }
2676
2702
  } else {
2677
2703
  const subM2M = getManyToManyRelation(this.schema, model, field);
2678
2704
  if (!subM2M && fieldDef.relation?.fields && fieldDef.relation?.references) {
2679
- const fkValues = await this.processOwnedRelation(kysely, fieldDef, value);
2705
+ const fkValues = await this.processOwnedRelationForCreate(kysely, fieldDef, value);
2680
2706
  for (let i = 0; i < fieldDef.relation.fields.length; i++) {
2681
2707
  createFields[fieldDef.relation.fields[i]] = fkValues[fieldDef.relation.references[i]];
2682
2708
  }
@@ -2697,7 +2723,7 @@ var BaseOperationHandler = class {
2697
2723
  const createdEntity = await query.executeTakeFirst();
2698
2724
  if (Object.keys(postCreateRelations).length > 0) {
2699
2725
  const relationPromises = Object.entries(postCreateRelations).map(([field, subPayload]) => {
2700
- return this.processNoneOwnedRelation(kysely, model, field, subPayload, createdEntity);
2726
+ return this.processNoneOwnedRelationForCreate(kysely, model, field, subPayload, createdEntity);
2701
2727
  });
2702
2728
  await Promise.all(relationPromises);
2703
2729
  }
@@ -2764,7 +2790,7 @@ var BaseOperationHandler = class {
2764
2790
  const eb = expressionBuilder2();
2765
2791
  return kysely.deleteFrom(m2m.joinTable).where(eb(`${m2m.joinTable}.${m2m.parentFkName}`, "=", parentId)).execute();
2766
2792
  }
2767
- async processOwnedRelation(kysely, relationField, payload) {
2793
+ async processOwnedRelationForCreate(kysely, relationField, payload) {
2768
2794
  if (!payload) {
2769
2795
  return;
2770
2796
  }
@@ -2814,21 +2840,27 @@ var BaseOperationHandler = class {
2814
2840
  }
2815
2841
  return result;
2816
2842
  }
2817
- processNoneOwnedRelation(kysely, contextModel, relationFieldName, payload, parentEntity) {
2843
+ processNoneOwnedRelationForCreate(kysely, contextModel, relationFieldName, payload, parentEntity) {
2818
2844
  const relationFieldDef = this.requireField(contextModel, relationFieldName);
2819
2845
  const relationModel = relationFieldDef.type;
2820
2846
  const tasks = [];
2847
+ const fromRelationContext = {
2848
+ model: contextModel,
2849
+ field: relationFieldName,
2850
+ ids: parentEntity
2851
+ };
2821
2852
  for (const [action, subPayload] of Object.entries(payload)) {
2822
2853
  if (!subPayload) {
2823
2854
  continue;
2824
2855
  }
2825
2856
  switch (action) {
2826
2857
  case "create": {
2827
- tasks.push(...enumerate(subPayload).map((item) => this.create(kysely, relationModel, item, {
2828
- model: contextModel,
2829
- field: relationFieldName,
2830
- ids: parentEntity
2831
- })));
2858
+ tasks.push(...enumerate(subPayload).map((item) => this.create(kysely, relationModel, item, fromRelationContext)));
2859
+ break;
2860
+ }
2861
+ case "createMany": {
2862
+ invariant7(relationFieldDef.array, "relation must be an array for createMany");
2863
+ tasks.push(this.createMany(kysely, relationModel, subPayload, false, fromRelationContext));
2832
2864
  break;
2833
2865
  }
2834
2866
  case "connect": {
@@ -2858,6 +2890,11 @@ var BaseOperationHandler = class {
2858
2890
  return Promise.all(tasks);
2859
2891
  }
2860
2892
  async createMany(kysely, model, input, returnData, fromRelation) {
2893
+ if (!input.data || Array.isArray(input.data) && input.data.length === 0) {
2894
+ return returnData ? [] : {
2895
+ count: 0
2896
+ };
2897
+ }
2861
2898
  const modelDef = this.requireModel(model);
2862
2899
  let relationKeyPairs = [];
2863
2900
  if (fromRelation) {
@@ -2872,7 +2909,7 @@ var BaseOperationHandler = class {
2872
2909
  for (const [name, value] of Object.entries(item)) {
2873
2910
  const fieldDef = this.requireField(model, name);
2874
2911
  invariant7(!fieldDef.relation, "createMany does not support relations");
2875
- newItem[name] = this.dialect.transformPrimitive(value, fieldDef.type);
2912
+ newItem[name] = this.dialect.transformPrimitive(value, fieldDef.type, !!fieldDef.array);
2876
2913
  }
2877
2914
  if (fromRelation) {
2878
2915
  for (const { fk, pk } of relationKeyPairs) {
@@ -2907,7 +2944,7 @@ var BaseOperationHandler = class {
2907
2944
  values[field] = generated;
2908
2945
  }
2909
2946
  } else if (fields[field]?.updatedAt) {
2910
- values[field] = this.dialect.transformPrimitive(/* @__PURE__ */ new Date(), "DateTime");
2947
+ values[field] = this.dialect.transformPrimitive(/* @__PURE__ */ new Date(), "DateTime", false);
2911
2948
  }
2912
2949
  }
2913
2950
  }
@@ -2972,7 +3009,7 @@ var BaseOperationHandler = class {
2972
3009
  if (finalData === data) {
2973
3010
  finalData = clone(data);
2974
3011
  }
2975
- finalData[fieldName] = this.dialect.transformPrimitive(/* @__PURE__ */ new Date(), "DateTime");
3012
+ finalData[fieldName] = this.dialect.transformPrimitive(/* @__PURE__ */ new Date(), "DateTime", false);
2976
3013
  }
2977
3014
  }
2978
3015
  if (Object.keys(finalData).length === 0) {
@@ -2997,7 +3034,7 @@ var BaseOperationHandler = class {
2997
3034
  updateFields[field] = this.transformScalarListUpdate(model, field, fieldDef, finalData[field]);
2998
3035
  continue;
2999
3036
  }
3000
- updateFields[field] = this.dialect.transformPrimitive(finalData[field], fieldDef.type);
3037
+ updateFields[field] = this.dialect.transformPrimitive(finalData[field], fieldDef.type, !!fieldDef.array);
3001
3038
  } else {
3002
3039
  if (!allowRelationUpdate) {
3003
3040
  throw new QueryError(`Relation update not allowed for field "${field}"`);
@@ -3042,7 +3079,7 @@ var BaseOperationHandler = class {
3042
3079
  transformIncrementalUpdate(model, field, fieldDef, payload) {
3043
3080
  invariant7(Object.keys(payload).length === 1, 'Only one of "set", "increment", "decrement", "multiply", or "divide" can be provided');
3044
3081
  const key = Object.keys(payload)[0];
3045
- const value = this.dialect.transformPrimitive(payload[key], fieldDef.type);
3082
+ const value = this.dialect.transformPrimitive(payload[key], fieldDef.type, false);
3046
3083
  const eb = expressionBuilder2();
3047
3084
  const fieldRef = buildFieldRef(this.schema, model, field, this.options, eb);
3048
3085
  return match8(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(() => {
@@ -3052,7 +3089,7 @@ var BaseOperationHandler = class {
3052
3089
  transformScalarListUpdate(model, field, fieldDef, payload) {
3053
3090
  invariant7(Object.keys(payload).length === 1, 'Only one of "set", "push" can be provided');
3054
3091
  const key = Object.keys(payload)[0];
3055
- const value = this.dialect.transformPrimitive(payload[key], fieldDef.type);
3092
+ const value = this.dialect.transformPrimitive(payload[key], fieldDef.type, true);
3056
3093
  const eb = expressionBuilder2();
3057
3094
  const fieldRef = buildFieldRef(this.schema, model, field, this.options, eb);
3058
3095
  return match8(key).with("set", () => value).with("push", () => {
@@ -3082,7 +3119,7 @@ var BaseOperationHandler = class {
3082
3119
  if (isRelationField(this.schema, model, field)) {
3083
3120
  continue;
3084
3121
  }
3085
- updateFields[field] = this.dialect.transformPrimitive(data[field], fieldDef.type);
3122
+ updateFields[field] = this.dialect.transformPrimitive(data[field], fieldDef.type, !!fieldDef.array);
3086
3123
  }
3087
3124
  let query = kysely.updateTable(model).set(updateFields);
3088
3125
  if (limit === void 0) {
@@ -3100,20 +3137,15 @@ var BaseOperationHandler = class {
3100
3137
  model,
3101
3138
  operation: "update"
3102
3139
  }));
3103
- try {
3104
- if (!returnData) {
3105
- const result = await query.executeTakeFirstOrThrow();
3106
- return {
3107
- count: Number(result.numUpdatedRows)
3108
- };
3109
- } else {
3110
- const idFields = getIdFields(this.schema, model);
3111
- const result = await query.returning(idFields).execute();
3112
- return result;
3113
- }
3114
- } catch (err) {
3115
- const { sql: sql10, parameters } = query.compile();
3116
- throw new QueryError(`Error during updateMany: ${err}, sql: ${sql10}, parameters: ${parameters}`);
3140
+ if (!returnData) {
3141
+ const result = await query.executeTakeFirstOrThrow();
3142
+ return {
3143
+ count: Number(result.numUpdatedRows)
3144
+ };
3145
+ } else {
3146
+ const idFields = getIdFields(this.schema, model);
3147
+ const result = await query.returning(idFields).execute();
3148
+ return result;
3117
3149
  }
3118
3150
  }
3119
3151
  buildIdFieldRefs(kysely, model) {
@@ -3533,11 +3565,15 @@ var BaseOperationHandler = class {
3533
3565
  }
3534
3566
  return returnRelation;
3535
3567
  }
3536
- async safeTransaction(callback) {
3568
+ async safeTransaction(callback, isolationLevel) {
3537
3569
  if (this.kysely.isTransaction) {
3538
3570
  return callback(this.kysely);
3539
3571
  } else {
3540
- return this.kysely.transaction().setIsolationLevel("repeatable read").execute(callback);
3572
+ let txBuilder = this.kysely.transaction();
3573
+ if (isolationLevel) {
3574
+ txBuilder = txBuilder.setIsolationLevel(isolationLevel);
3575
+ }
3576
+ return txBuilder.execute(callback);
3541
3577
  }
3542
3578
  }
3543
3579
  // Given a unique filter of a model, return the entity ids by trying to
@@ -3556,6 +3592,28 @@ var BaseOperationHandler = class {
3556
3592
  where: uniqueFilter
3557
3593
  });
3558
3594
  }
3595
+ /**
3596
+ * Normalize input args to strip `undefined` fields
3597
+ */
3598
+ normalizeArgs(args) {
3599
+ if (!args) {
3600
+ return;
3601
+ }
3602
+ const newArgs = clone(args);
3603
+ this.doNormalizeArgs(newArgs);
3604
+ return newArgs;
3605
+ }
3606
+ doNormalizeArgs(args) {
3607
+ if (args && typeof args === "object") {
3608
+ for (const [key, value] of Object.entries(args)) {
3609
+ if (value === void 0) {
3610
+ delete args[key];
3611
+ } else if (value && isPlainObject3(value)) {
3612
+ this.doNormalizeArgs(value);
3613
+ }
3614
+ }
3615
+ }
3616
+ }
3559
3617
  };
3560
3618
 
3561
3619
  // src/client/crud/operations/aggregate.ts
@@ -3564,21 +3622,22 @@ var AggregateOperationHandler = class extends BaseOperationHandler {
3564
3622
  __name(this, "AggregateOperationHandler");
3565
3623
  }
3566
3624
  async handle(_operation, args) {
3567
- const validatedArgs = this.inputValidator.validateAggregateArgs(this.model, args);
3625
+ const normalizeArgs = this.normalizeArgs(args);
3626
+ const parsedArgs = this.inputValidator.validateAggregateArgs(this.model, normalizeArgs);
3568
3627
  let query = this.kysely.selectFrom((eb) => {
3569
- let subQuery = eb.selectFrom(this.model).selectAll(this.model).where((eb1) => this.dialect.buildFilter(eb1, this.model, this.model, validatedArgs?.where));
3570
- const skip = validatedArgs?.skip;
3571
- let take = validatedArgs?.take;
3628
+ let subQuery = eb.selectFrom(this.model).selectAll(this.model).where((eb1) => this.dialect.buildFilter(eb1, this.model, this.model, parsedArgs?.where));
3629
+ const skip = parsedArgs?.skip;
3630
+ let take = parsedArgs?.take;
3572
3631
  let negateOrderBy = false;
3573
3632
  if (take !== void 0 && take < 0) {
3574
3633
  negateOrderBy = true;
3575
3634
  take = -take;
3576
3635
  }
3577
3636
  subQuery = this.dialect.buildSkipTake(subQuery, skip, take);
3578
- subQuery = this.dialect.buildOrderBy(subQuery, this.model, this.model, validatedArgs.orderBy, skip !== void 0 || take !== void 0, negateOrderBy);
3637
+ subQuery = this.dialect.buildOrderBy(subQuery, this.model, this.model, parsedArgs.orderBy, skip !== void 0 || take !== void 0, negateOrderBy);
3579
3638
  return subQuery.as("$sub");
3580
3639
  });
3581
- for (const [key, value] of Object.entries(validatedArgs)) {
3640
+ for (const [key, value] of Object.entries(parsedArgs)) {
3582
3641
  switch (key) {
3583
3642
  case "_count": {
3584
3643
  if (value === true) {
@@ -3657,14 +3716,15 @@ var CountOperationHandler = class extends BaseOperationHandler {
3657
3716
  __name(this, "CountOperationHandler");
3658
3717
  }
3659
3718
  async handle(_operation, args) {
3660
- const validatedArgs = this.inputValidator.validateCountArgs(this.model, args);
3719
+ const normalizeArgs = this.normalizeArgs(args);
3720
+ const parsedArgs = this.inputValidator.validateCountArgs(this.model, normalizeArgs);
3661
3721
  let query = this.kysely.selectFrom((eb) => {
3662
- let subQuery = eb.selectFrom(this.model).selectAll().where((eb1) => this.dialect.buildFilter(eb1, this.model, this.model, validatedArgs?.where));
3663
- subQuery = this.dialect.buildSkipTake(subQuery, validatedArgs?.skip, validatedArgs?.take);
3722
+ let subQuery = eb.selectFrom(this.model).selectAll().where((eb1) => this.dialect.buildFilter(eb1, this.model, this.model, parsedArgs?.where));
3723
+ subQuery = this.dialect.buildSkipTake(subQuery, parsedArgs?.skip, parsedArgs?.take);
3664
3724
  return subQuery.as("$sub");
3665
3725
  });
3666
- if (validatedArgs?.select && typeof validatedArgs.select === "object") {
3667
- 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(sql6.ref(`$sub.${key}`)), "integer").as(key)));
3726
+ if (parsedArgs?.select && typeof parsedArgs.select === "object") {
3727
+ 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(sql6.ref(`$sub.${key}`)), "integer").as(key)));
3668
3728
  return query.executeTakeFirstOrThrow();
3669
3729
  } else {
3670
3730
  query = query.select((eb) => eb.cast(eb.fn.countAll(), "integer").as("count"));
@@ -3681,10 +3741,11 @@ var CreateOperationHandler = class extends BaseOperationHandler {
3681
3741
  __name(this, "CreateOperationHandler");
3682
3742
  }
3683
3743
  async handle(operation, args) {
3684
- return match10(operation).with("create", () => this.runCreate(this.inputValidator.validateCreateArgs(this.model, args))).with("createMany", () => {
3685
- return this.runCreateMany(this.inputValidator.validateCreateManyArgs(this.model, args));
3744
+ const normalizeArgs = this.normalizeArgs(args);
3745
+ return match10(operation).with("create", () => this.runCreate(this.inputValidator.validateCreateArgs(this.model, normalizeArgs))).with("createMany", () => {
3746
+ return this.runCreateMany(this.inputValidator.validateCreateManyArgs(this.model, normalizeArgs));
3686
3747
  }).with("createManyAndReturn", () => {
3687
- return this.runCreateManyAndReturn(this.inputValidator.validateCreateManyAndReturnArgs(this.model, args));
3748
+ return this.runCreateManyAndReturn(this.inputValidator.validateCreateManyAndReturnArgs(this.model, normalizeArgs));
3688
3749
  }).exhaustive();
3689
3750
  }
3690
3751
  async runCreate(args) {
@@ -3734,7 +3795,8 @@ var DeleteOperationHandler = class extends BaseOperationHandler {
3734
3795
  __name(this, "DeleteOperationHandler");
3735
3796
  }
3736
3797
  async handle(operation, args) {
3737
- return match11(operation).with("delete", () => this.runDelete(this.inputValidator.validateDeleteArgs(this.model, args))).with("deleteMany", () => this.runDeleteMany(this.inputValidator.validateDeleteManyArgs(this.model, args))).exhaustive();
3798
+ const normalizeArgs = this.normalizeArgs(args);
3799
+ return match11(operation).with("delete", () => this.runDelete(this.inputValidator.validateDeleteArgs(this.model, normalizeArgs))).with("deleteMany", () => this.runDeleteMany(this.inputValidator.validateDeleteManyArgs(this.model, normalizeArgs))).exhaustive();
3738
3800
  }
3739
3801
  async runDelete(args) {
3740
3802
  const existing = await this.readUnique(this.kysely, this.model, {
@@ -3764,7 +3826,8 @@ var FindOperationHandler = class extends BaseOperationHandler {
3764
3826
  __name(this, "FindOperationHandler");
3765
3827
  }
3766
3828
  async handle(operation, args, validateArgs = true) {
3767
- const parsedArgs = validateArgs ? this.inputValidator.validateFindArgs(this.model, operation === "findUnique", args) : args;
3829
+ const normalizeArgs = this.normalizeArgs(args);
3830
+ const parsedArgs = validateArgs ? this.inputValidator.validateFindArgs(this.model, operation === "findUnique", normalizeArgs) : normalizeArgs;
3768
3831
  const result = await this.read(this.client.$qb, this.model, parsedArgs);
3769
3832
  const finalResult = operation === "findMany" ? result : result[0] ?? null;
3770
3833
  return finalResult;
@@ -3779,11 +3842,12 @@ var GroupByeOperationHandler = class extends BaseOperationHandler {
3779
3842
  __name(this, "GroupByeOperationHandler");
3780
3843
  }
3781
3844
  async handle(_operation, args) {
3782
- const validatedArgs = this.inputValidator.validateGroupByArgs(this.model, args);
3845
+ const normalizeArgs = this.normalizeArgs(args);
3846
+ const parsedArgs = this.inputValidator.validateGroupByArgs(this.model, normalizeArgs);
3783
3847
  let query = this.kysely.selectFrom((eb) => {
3784
- let subQuery = eb.selectFrom(this.model).selectAll().where((eb1) => this.dialect.buildFilter(eb1, this.model, this.model, validatedArgs?.where));
3785
- const skip = validatedArgs?.skip;
3786
- let take = validatedArgs?.take;
3848
+ let subQuery = eb.selectFrom(this.model).selectAll().where((eb1) => this.dialect.buildFilter(eb1, this.model, this.model, parsedArgs?.where));
3849
+ const skip = parsedArgs?.skip;
3850
+ let take = parsedArgs?.take;
3787
3851
  let negateOrderBy = false;
3788
3852
  if (take !== void 0 && take < 0) {
3789
3853
  negateOrderBy = true;
@@ -3793,20 +3857,20 @@ var GroupByeOperationHandler = class extends BaseOperationHandler {
3793
3857
  subQuery = this.dialect.buildOrderBy(subQuery, this.model, this.model, void 0, skip !== void 0 || take !== void 0, negateOrderBy);
3794
3858
  return subQuery.as("$sub");
3795
3859
  });
3796
- const bys = typeof validatedArgs.by === "string" ? [
3797
- validatedArgs.by
3798
- ] : validatedArgs.by;
3860
+ const bys = typeof parsedArgs.by === "string" ? [
3861
+ parsedArgs.by
3862
+ ] : parsedArgs.by;
3799
3863
  query = query.groupBy(bys);
3800
- if (validatedArgs.orderBy) {
3801
- query = this.dialect.buildOrderBy(query, this.model, "$sub", validatedArgs.orderBy, false, false);
3864
+ if (parsedArgs.orderBy) {
3865
+ query = this.dialect.buildOrderBy(query, this.model, "$sub", parsedArgs.orderBy, false, false);
3802
3866
  }
3803
- if (validatedArgs.having) {
3804
- query = query.having((eb1) => this.dialect.buildFilter(eb1, this.model, "$sub", validatedArgs.having));
3867
+ if (parsedArgs.having) {
3868
+ query = query.having((eb1) => this.dialect.buildFilter(eb1, this.model, "$sub", parsedArgs.having));
3805
3869
  }
3806
3870
  for (const by of bys) {
3807
3871
  query = query.select(() => sql7.ref(`$sub.${by}`).as(by));
3808
3872
  }
3809
- for (const [key, value] of Object.entries(validatedArgs)) {
3873
+ for (const [key, value] of Object.entries(parsedArgs)) {
3810
3874
  switch (key) {
3811
3875
  case "_count": {
3812
3876
  if (value === true) {
@@ -3889,7 +3953,8 @@ var UpdateOperationHandler = class extends BaseOperationHandler {
3889
3953
  __name(this, "UpdateOperationHandler");
3890
3954
  }
3891
3955
  async handle(operation, args) {
3892
- return match13(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();
3956
+ const normalizeArgs = this.normalizeArgs(args);
3957
+ return match13(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();
3893
3958
  }
3894
3959
  async runUpdate(args) {
3895
3960
  const result = await this.safeTransaction(async (tx) => {
@@ -3945,6 +4010,7 @@ var UpdateOperationHandler = class extends BaseOperationHandler {
3945
4010
  };
3946
4011
 
3947
4012
  // src/client/crud/validator.ts
4013
+ import { invariant as invariant8 } from "@zenstackhq/common-helpers";
3948
4014
  import Decimal from "decimal.js";
3949
4015
  import stableStringify from "json-stable-stringify";
3950
4016
  import { match as match14, P as P2 } from "ts-pattern";
@@ -4013,7 +4079,7 @@ var InputValidator = class {
4013
4079
  }
4014
4080
  const { error } = schema.safeParse(args);
4015
4081
  if (error) {
4016
- throw new QueryError(`Invalid ${operation} args: ${error.message}`);
4082
+ throw new InputValidationError(`Invalid ${operation} args: ${error.message}`, error);
4017
4083
  }
4018
4084
  return args;
4019
4085
  }
@@ -4060,7 +4126,7 @@ var InputValidator = class {
4060
4126
  makeWhereSchema(model, unique, withoutRelationFields = false) {
4061
4127
  const modelDef = getModel(this.schema, model);
4062
4128
  if (!modelDef) {
4063
- throw new QueryError(`Model "${model}" not found`);
4129
+ throw new QueryError(`Model "${model}" not found in schema`);
4064
4130
  }
4065
4131
  const fields = {};
4066
4132
  for (const field of Object.keys(modelDef.fields)) {
@@ -4110,10 +4176,24 @@ var InputValidator = class {
4110
4176
  const uniqueFields = getUniqueFields(this.schema, model);
4111
4177
  for (const uniqueField of uniqueFields) {
4112
4178
  if ("defs" in uniqueField) {
4113
- fields[uniqueField.name] = z.object(Object.fromEntries(Object.entries(uniqueField.defs).map(([key, def]) => [
4114
- key,
4115
- this.makePrimitiveFilterSchema(def.type, !!def.optional)
4116
- ]))).optional();
4179
+ fields[uniqueField.name] = z.object(Object.fromEntries(Object.entries(uniqueField.defs).map(([key, def]) => {
4180
+ invariant8(!def.relation, "unique field cannot be a relation");
4181
+ let fieldSchema;
4182
+ const enumDef = getEnum(this.schema, def.type);
4183
+ if (enumDef) {
4184
+ if (Object.keys(enumDef).length > 0) {
4185
+ fieldSchema = this.makeEnumFilterSchema(enumDef, !!def.optional);
4186
+ } else {
4187
+ fieldSchema = z.never();
4188
+ }
4189
+ } else {
4190
+ fieldSchema = this.makePrimitiveFilterSchema(def.type, !!def.optional);
4191
+ }
4192
+ return [
4193
+ key,
4194
+ fieldSchema
4195
+ ];
4196
+ }))).optional();
4117
4197
  }
4118
4198
  }
4119
4199
  }
@@ -4163,7 +4243,7 @@ var InputValidator = class {
4163
4243
  });
4164
4244
  }
4165
4245
  makePrimitiveFilterSchema(type, optional) {
4166
- return match14(type).with("String", () => this.makeStringFilterSchema(optional)).with(P2.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();
4246
+ return match14(type).with("String", () => this.makeStringFilterSchema(optional)).with(P2.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", () => z.any()).with("Unsupported", () => z.never()).exhaustive();
4167
4247
  }
4168
4248
  makeDateTimeFilterSchema(optional) {
4169
4249
  return this.makeCommonPrimitiveFilterSchema(z.union([
@@ -4357,8 +4437,8 @@ var InputValidator = class {
4357
4437
  return this.refineForSelectOmitMutuallyExclusive(result).optional();
4358
4438
  }
4359
4439
  makeCreateDataSchema(model, canBeArray, withoutFields = [], withoutRelationFields = false) {
4360
- const regularAndFkFields = {};
4361
- const regularAndRelationFields = {};
4440
+ const uncheckedVariantFields = {};
4441
+ const checkedVariantFields = {};
4362
4442
  const modelDef = requireModel(this.schema, model);
4363
4443
  const hasRelation = !withoutRelationFields && Object.entries(modelDef.fields).some(([f, def]) => !withoutFields.includes(f) && def.relation);
4364
4444
  Object.keys(modelDef.fields).forEach((field) => {
@@ -4400,7 +4480,10 @@ var InputValidator = class {
4400
4480
  if (fieldDef.optional && !fieldDef.array) {
4401
4481
  fieldSchema = fieldSchema.nullable();
4402
4482
  }
4403
- regularAndRelationFields[field] = fieldSchema;
4483
+ checkedVariantFields[field] = fieldSchema;
4484
+ if (fieldDef.array || !fieldDef.relation.references) {
4485
+ uncheckedVariantFields[field] = fieldSchema;
4486
+ }
4404
4487
  } else {
4405
4488
  let fieldSchema = this.makePrimitiveSchema(fieldDef.type);
4406
4489
  if (fieldDef.array) {
@@ -4417,23 +4500,23 @@ var InputValidator = class {
4417
4500
  if (fieldDef.optional) {
4418
4501
  fieldSchema = fieldSchema.nullable();
4419
4502
  }
4420
- regularAndFkFields[field] = fieldSchema;
4503
+ uncheckedVariantFields[field] = fieldSchema;
4421
4504
  if (!fieldDef.foreignKeyFor) {
4422
- regularAndRelationFields[field] = fieldSchema;
4505
+ checkedVariantFields[field] = fieldSchema;
4423
4506
  }
4424
4507
  }
4425
4508
  });
4426
4509
  if (!hasRelation) {
4427
- return this.orArray(z.object(regularAndFkFields).strict(), canBeArray);
4510
+ return this.orArray(z.object(uncheckedVariantFields).strict(), canBeArray);
4428
4511
  } else {
4429
4512
  return z.union([
4430
- z.object(regularAndFkFields).strict(),
4431
- z.object(regularAndRelationFields).strict(),
4513
+ z.object(uncheckedVariantFields).strict(),
4514
+ z.object(checkedVariantFields).strict(),
4432
4515
  ...canBeArray ? [
4433
- z.array(z.object(regularAndFkFields).strict())
4516
+ z.array(z.object(uncheckedVariantFields).strict())
4434
4517
  ] : [],
4435
4518
  ...canBeArray ? [
4436
- z.array(z.object(regularAndRelationFields).strict())
4519
+ z.array(z.object(checkedVariantFields).strict())
4437
4520
  ] : []
4438
4521
  ]);
4439
4522
  }
@@ -4478,7 +4561,7 @@ var InputValidator = class {
4478
4561
  fields["deleteMany"] = this.makeDeleteRelationDataSchema(fieldType, true, false).optional();
4479
4562
  }
4480
4563
  }
4481
- return z.object(fields).strict().refine((v) => Object.keys(v).length > 0, "At least one action is required");
4564
+ return z.object(fields).strict();
4482
4565
  }
4483
4566
  makeSetDataSchema(model, canBeArray) {
4484
4567
  return this.orArray(this.makeWhereSchema(model, true), canBeArray);
@@ -4913,8 +4996,9 @@ function performanceNow() {
4913
4996
  __name(performanceNow, "performanceNow");
4914
4997
 
4915
4998
  // src/client/executor/zenstack-query-executor.ts
4916
- import { AndNode as AndNode2, DefaultQueryExecutor, DeleteQueryNode as DeleteQueryNode2, InsertQueryNode as InsertQueryNode2, ReturningNode as ReturningNode3, SelectionNode as SelectionNode4, SingleConnectionProvider, UpdateQueryNode as UpdateQueryNode2, WhereNode as WhereNode3 } from "kysely";
4999
+ import { AndNode as AndNode2, DefaultQueryExecutor, DeleteQueryNode as DeleteQueryNode2, InsertQueryNode as InsertQueryNode2, ReturningNode as ReturningNode3, SelectionNode as SelectionNode4, UpdateQueryNode as UpdateQueryNode2, WhereNode as WhereNode3 } from "kysely";
4917
5000
  import { nanoid as nanoid2 } from "nanoid";
5001
+ import { inspect as inspect2 } from "util";
4918
5002
  import { match as match15 } from "ts-pattern";
4919
5003
 
4920
5004
  // src/client/executor/name-mapper.ts
@@ -5149,7 +5233,9 @@ var ZenStackQueryExecutor = class _ZenStackQueryExecutor extends DefaultQueryExe
5149
5233
  mutationInterceptionInfo = await this.callMutationInterceptionFilters(queryNode);
5150
5234
  }
5151
5235
  const task = /* @__PURE__ */ __name(async () => {
5152
- await this.callBeforeMutationHooks(queryNode, mutationInterceptionInfo);
5236
+ if (this.isMutationNode(queryNode)) {
5237
+ await this.callBeforeMutationHooks(queryNode, mutationInterceptionInfo);
5238
+ }
5153
5239
  const oldQueryNode = queryNode;
5154
5240
  if ((InsertQueryNode2.is(queryNode) || DeleteQueryNode2.is(queryNode)) && mutationInterceptionInfo?.loadAfterMutationEntity) {
5155
5241
  queryNode = {
@@ -5159,19 +5245,19 @@ var ZenStackQueryExecutor = class _ZenStackQueryExecutor extends DefaultQueryExe
5159
5245
  ])
5160
5246
  };
5161
5247
  }
5162
- const result = await this.proceedQueryWithKyselyInterceptors(queryNode, queryId);
5163
- await this.callAfterQueryInterceptionFilters(result, queryNode, mutationInterceptionInfo);
5248
+ const queryParams = compiledQuery.$raw ? compiledQuery.parameters : void 0;
5249
+ const result = await this.proceedQueryWithKyselyInterceptors(queryNode, queryParams, queryId);
5250
+ if (this.isMutationNode(queryNode)) {
5251
+ await this.callAfterQueryInterceptionFilters(result, queryNode, mutationInterceptionInfo);
5252
+ }
5164
5253
  if (oldQueryNode !== queryNode) {
5165
5254
  }
5166
5255
  return result;
5167
5256
  }, "task");
5168
- return this.executeWithTransaction(task, !!mutationInterceptionInfo?.useTransactionForMutation);
5257
+ return task();
5169
5258
  }
5170
- proceedQueryWithKyselyInterceptors(queryNode, queryId) {
5171
- let proceed = /* @__PURE__ */ __name((q) => this.proceedQuery(q, queryId), "proceed");
5172
- const makeTx = /* @__PURE__ */ __name((p) => (callback) => {
5173
- return this.executeWithTransaction(() => callback(p));
5174
- }, "makeTx");
5259
+ proceedQueryWithKyselyInterceptors(queryNode, parameters, queryId) {
5260
+ let proceed = /* @__PURE__ */ __name((q) => this.proceedQuery(q, parameters, queryId), "proceed");
5175
5261
  const hooks = this.options.plugins?.filter((plugin) => typeof plugin.onKyselyQuery === "function").map((plugin) => plugin.onKyselyQuery.bind(plugin)) ?? [];
5176
5262
  for (const hook of hooks) {
5177
5263
  const _proceed = proceed;
@@ -5181,20 +5267,30 @@ var ZenStackQueryExecutor = class _ZenStackQueryExecutor extends DefaultQueryExe
5181
5267
  schema: this.client.$schema,
5182
5268
  kysely: this.kysely,
5183
5269
  query,
5184
- proceed: _proceed,
5185
- transaction: makeTx(_proceed)
5270
+ proceed: _proceed
5186
5271
  });
5187
5272
  }, "proceed");
5188
5273
  }
5189
5274
  return proceed(queryNode);
5190
5275
  }
5191
- async proceedQuery(query, queryId) {
5276
+ async proceedQuery(query, parameters, queryId) {
5192
5277
  const finalQuery = this.nameMapper.transformNode(query);
5193
- const compiled = this.compileQuery(finalQuery);
5278
+ let compiled = this.compileQuery(finalQuery);
5279
+ if (parameters) {
5280
+ compiled = {
5281
+ ...compiled,
5282
+ parameters
5283
+ };
5284
+ }
5194
5285
  try {
5195
- return this.driver.txConnection ? await super.withConnectionProvider(new SingleConnectionProvider(this.driver.txConnection)).executeQuery(compiled, queryId) : await super.executeQuery(compiled, queryId);
5286
+ return await super.executeQuery(compiled, queryId);
5196
5287
  } catch (err) {
5197
- throw new QueryError(`Failed to execute query: ${err}, sql: ${compiled.sql}, parameters: ${compiled.parameters}`);
5288
+ let message = `Failed to execute query: ${err}, sql: ${compiled.sql}`;
5289
+ if (this.options.debug) {
5290
+ message += `, parameters:
5291
+ ${compiled.parameters.map((p) => inspect2(p)).join("\n")}`;
5292
+ }
5293
+ throw new QueryError(message, err);
5198
5294
  }
5199
5295
  }
5200
5296
  isMutationNode(queryNode) {
@@ -5222,24 +5318,9 @@ var ZenStackQueryExecutor = class _ZenStackQueryExecutor extends DefaultQueryExe
5222
5318
  return new _ZenStackQueryExecutor(this.client, this.driver, this.compiler, this.adapter, this.connectionProvider, []);
5223
5319
  }
5224
5320
  withConnectionProvider(connectionProvider) {
5225
- return new _ZenStackQueryExecutor(this.client, this.driver, this.compiler, this.adapter, connectionProvider);
5226
- }
5227
- async executeWithTransaction(callback, useTransaction = true) {
5228
- if (!useTransaction || this.driver.txConnection) {
5229
- return callback();
5230
- } else {
5231
- return this.provideConnection(async (connection) => {
5232
- try {
5233
- await this.driver.beginTransaction(connection, {});
5234
- const result = await callback();
5235
- await this.driver.commitTransaction(connection);
5236
- return result;
5237
- } catch (error) {
5238
- await this.driver.rollbackTransaction(connection);
5239
- throw error;
5240
- }
5241
- });
5242
- }
5321
+ const newExecutor = new _ZenStackQueryExecutor(this.client, this.driver, this.compiler, this.adapter, connectionProvider);
5322
+ newExecutor.client = this.client.withExecutor(newExecutor);
5323
+ return newExecutor;
5243
5324
  }
5244
5325
  get hasMutationHooks() {
5245
5326
  return this.client.$options.plugins?.some((plugin) => plugin.beforeEntityMutation || plugin.afterEntityMutation);
@@ -5281,14 +5362,13 @@ var ZenStackQueryExecutor = class _ZenStackQueryExecutor extends DefaultQueryExe
5281
5362
  queryNode
5282
5363
  });
5283
5364
  result.intercept ||= filterResult.intercept;
5284
- result.useTransactionForMutation ||= filterResult.useTransactionForMutation;
5285
5365
  result.loadBeforeMutationEntity ||= filterResult.loadBeforeMutationEntity;
5286
5366
  result.loadAfterMutationEntity ||= filterResult.loadAfterMutationEntity;
5287
5367
  }
5288
5368
  }
5289
5369
  let beforeMutationEntities;
5290
5370
  if (result.loadBeforeMutationEntity && (UpdateQueryNode2.is(queryNode) || DeleteQueryNode2.is(queryNode))) {
5291
- beforeMutationEntities = await this.loadEntities(this.kysely, mutationModel, where);
5371
+ beforeMutationEntities = await this.loadEntities(mutationModel, where);
5292
5372
  }
5293
5373
  return {
5294
5374
  ...result,
@@ -5301,15 +5381,14 @@ var ZenStackQueryExecutor = class _ZenStackQueryExecutor extends DefaultQueryExe
5301
5381
  return void 0;
5302
5382
  }
5303
5383
  }
5304
- callBeforeMutationHooks(queryNode, mutationInterceptionInfo) {
5384
+ async callBeforeMutationHooks(queryNode, mutationInterceptionInfo) {
5305
5385
  if (!mutationInterceptionInfo?.intercept) {
5306
5386
  return;
5307
5387
  }
5308
5388
  if (this.options.plugins) {
5309
5389
  for (const plugin of this.options.plugins) {
5310
5390
  if (plugin.beforeEntityMutation) {
5311
- plugin.beforeEntityMutation({
5312
- // context: this.queryContext,
5391
+ await plugin.beforeEntityMutation({
5313
5392
  model: this.getMutationModel(queryNode),
5314
5393
  action: mutationInterceptionInfo.action,
5315
5394
  queryNode,
@@ -5330,12 +5409,12 @@ var ZenStackQueryExecutor = class _ZenStackQueryExecutor extends DefaultQueryExe
5330
5409
  let afterMutationEntities = void 0;
5331
5410
  if (mutationInterceptionInfo.loadAfterMutationEntity) {
5332
5411
  if (UpdateQueryNode2.is(queryNode)) {
5333
- afterMutationEntities = await this.loadEntities(this.kysely, mutationModel, mutationInterceptionInfo.where);
5412
+ afterMutationEntities = await this.loadEntities(mutationModel, mutationInterceptionInfo.where);
5334
5413
  } else {
5335
5414
  afterMutationEntities = queryResult.rows;
5336
5415
  }
5337
5416
  }
5338
- plugin.afterEntityMutation({
5417
+ await plugin.afterEntityMutation({
5339
5418
  model: this.getMutationModel(queryNode),
5340
5419
  action: mutationInterceptionInfo.action,
5341
5420
  queryNode,
@@ -5346,17 +5425,17 @@ var ZenStackQueryExecutor = class _ZenStackQueryExecutor extends DefaultQueryExe
5346
5425
  }
5347
5426
  }
5348
5427
  }
5349
- async loadEntities(kysely, model, where) {
5350
- const selectQuery = kysely.selectFrom(model).selectAll();
5428
+ async loadEntities(model, where) {
5429
+ const selectQuery = this.kysely.selectFrom(model).selectAll();
5351
5430
  let selectQueryNode = selectQuery.toOperationNode();
5352
5431
  selectQueryNode = {
5353
5432
  ...selectQueryNode,
5354
5433
  where: this.andNodes(selectQueryNode.where, where)
5355
5434
  };
5356
- const compiled = kysely.getExecutor().compileQuery(selectQueryNode, {
5435
+ const compiled = this.compileQuery(selectQueryNode);
5436
+ const result = await this.executeQuery(compiled, {
5357
5437
  queryId: `zenstack-${nanoid2()}`
5358
5438
  });
5359
- const result = await kysely.executeQuery(compiled);
5360
5439
  return result.rows;
5361
5440
  }
5362
5441
  andNodes(condition1, condition2) {
@@ -5385,7 +5464,7 @@ __export(functions_exports, {
5385
5464
  search: () => search,
5386
5465
  startsWith: () => startsWith
5387
5466
  });
5388
- import { invariant as invariant8, lowerCaseFirst, upperCaseFirst } from "@zenstackhq/common-helpers";
5467
+ import { invariant as invariant9, lowerCaseFirst, upperCaseFirst } from "@zenstackhq/common-helpers";
5389
5468
  import { sql as sql8, ValueNode as ValueNode4 } from "kysely";
5390
5469
  import { match as match16 } from "ts-pattern";
5391
5470
  var contains = /* @__PURE__ */ __name((eb, args) => {
@@ -5492,7 +5571,7 @@ var currentOperation = /* @__PURE__ */ __name((_eb, args, { operation }) => {
5492
5571
  }, "currentOperation");
5493
5572
  function processCasing(casing, result, model) {
5494
5573
  const opNode = casing.toOperationNode();
5495
- invariant8(ValueNode4.is(opNode) && typeof opNode.value === "string", '"casting" parameter must be a string value');
5574
+ invariant9(ValueNode4.is(opNode) && typeof opNode.value === "string", '"casting" parameter must be a string value');
5496
5575
  result = match16(opNode.value).with("original", () => model).with("upper", () => result.toUpperCase()).with("lower", () => result.toLowerCase()).with("capitalize", () => upperCaseFirst(result)).with("uncapitalize", () => lowerCaseFirst(result)).otherwise(() => {
5497
5576
  throw new Error(`Invalid casing value: ${opNode.value}. Must be "original", "upper", "lower", "capitalize", or "uncapitalize".`);
5498
5577
  });
@@ -5501,7 +5580,7 @@ function processCasing(casing, result, model) {
5501
5580
  __name(processCasing, "processCasing");
5502
5581
 
5503
5582
  // src/client/helpers/schema-db-pusher.ts
5504
- import { invariant as invariant9 } from "@zenstackhq/common-helpers";
5583
+ import { invariant as invariant10 } from "@zenstackhq/common-helpers";
5505
5584
  import { sql as sql9 } from "kysely";
5506
5585
  import { match as match17 } from "ts-pattern";
5507
5586
  var SchemaDbPusher = class {
@@ -5558,7 +5637,7 @@ var SchemaDbPusher = class {
5558
5637
  }
5559
5638
  addUniqueConstraint(table, modelDef) {
5560
5639
  for (const [key, value] of Object.entries(modelDef.uniqueFields)) {
5561
- invariant9(typeof value === "object", "expecting an object");
5640
+ invariant10(typeof value === "object", "expecting an object");
5562
5641
  if ("type" in value) {
5563
5642
  const fieldDef = modelDef.fields[key];
5564
5643
  if (fieldDef.unique) {
@@ -5604,7 +5683,7 @@ var SchemaDbPusher = class {
5604
5683
  return "serial";
5605
5684
  }
5606
5685
  const type = fieldDef.type;
5607
- const result = match17(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(() => {
5686
+ const result = match17(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(() => {
5608
5687
  throw new Error(`Unsupported field type: ${type}`);
5609
5688
  });
5610
5689
  if (fieldDef.array) {
@@ -5617,7 +5696,7 @@ var SchemaDbPusher = class {
5617
5696
  return fieldDef.default && ExpressionUtils.isCall(fieldDef.default) && fieldDef.default.function === "autoincrement";
5618
5697
  }
5619
5698
  addForeignKeyConstraint(table, model, fieldName, fieldDef) {
5620
- invariant9(fieldDef.relation, "field must be a relation");
5699
+ invariant10(fieldDef.relation, "field must be a relation");
5621
5700
  if (!fieldDef.relation.fields || !fieldDef.relation.references) {
5622
5701
  return table;
5623
5702
  }
@@ -5638,11 +5717,11 @@ var SchemaDbPusher = class {
5638
5717
  };
5639
5718
 
5640
5719
  // src/client/promise.ts
5641
- function createDeferredPromise(callback) {
5720
+ function createZenStackPromise(callback) {
5642
5721
  let promise;
5643
- const cb = /* @__PURE__ */ __name(() => {
5722
+ const cb = /* @__PURE__ */ __name((txClient) => {
5644
5723
  try {
5645
- return promise ??= valueToPromise(callback());
5724
+ return promise ??= valueToPromise(callback(txClient));
5646
5725
  } catch (err) {
5647
5726
  return Promise.reject(err);
5648
5727
  }
@@ -5657,10 +5736,11 @@ function createDeferredPromise(callback) {
5657
5736
  finally(onFinally) {
5658
5737
  return cb().finally(onFinally);
5659
5738
  },
5739
+ cb,
5660
5740
  [Symbol.toStringTag]: "ZenStackPromise"
5661
5741
  };
5662
5742
  }
5663
- __name(createDeferredPromise, "createDeferredPromise");
5743
+ __name(createZenStackPromise, "createZenStackPromise");
5664
5744
  function valueToPromise(thing) {
5665
5745
  if (typeof thing === "object" && typeof thing?.then === "function") {
5666
5746
  return thing;
@@ -5671,7 +5751,7 @@ function valueToPromise(thing) {
5671
5751
  __name(valueToPromise, "valueToPromise");
5672
5752
 
5673
5753
  // src/client/result-processor.ts
5674
- import { invariant as invariant10 } from "@zenstackhq/common-helpers";
5754
+ import { invariant as invariant11 } from "@zenstackhq/common-helpers";
5675
5755
  import Decimal2 from "decimal.js";
5676
5756
  import { match as match18 } from "ts-pattern";
5677
5757
  var ResultProcessor = class {
@@ -5746,20 +5826,20 @@ var ResultProcessor = class {
5746
5826
  return this.doProcessResult(relationData, fieldDef.type);
5747
5827
  }
5748
5828
  transformScalar(value, type) {
5749
- return match18(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);
5829
+ return match18(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);
5750
5830
  }
5751
5831
  transformDecimal(value) {
5752
5832
  if (value instanceof Decimal2) {
5753
5833
  return value;
5754
5834
  }
5755
- invariant10(typeof value === "string" || typeof value === "number" || value instanceof Decimal2, `Expected string, number or Decimal, got ${typeof value}`);
5835
+ invariant11(typeof value === "string" || typeof value === "number" || value instanceof Decimal2, `Expected string, number or Decimal, got ${typeof value}`);
5756
5836
  return new Decimal2(value);
5757
5837
  }
5758
5838
  transformBigInt(value) {
5759
5839
  if (typeof value === "bigint") {
5760
5840
  return value;
5761
5841
  }
5762
- invariant10(typeof value === "string" || typeof value === "number", `Expected string or number, got ${typeof value}`);
5842
+ invariant11(typeof value === "string" || typeof value === "number", `Expected string or number, got ${typeof value}`);
5763
5843
  return BigInt(value);
5764
5844
  }
5765
5845
  transformBoolean(value) {
@@ -5778,6 +5858,9 @@ var ResultProcessor = class {
5778
5858
  return Buffer.isBuffer(value) ? Uint8Array.from(value) : value;
5779
5859
  }
5780
5860
  fixReversedResult(data, model, args) {
5861
+ if (!data) {
5862
+ return;
5863
+ }
5781
5864
  if (Array.isArray(data) && typeof args === "object" && args && args.take !== void 0 && args.take < 0) {
5782
5865
  data.reverse();
5783
5866
  }
@@ -5791,13 +5874,19 @@ var ResultProcessor = class {
5791
5874
  continue;
5792
5875
  }
5793
5876
  const fieldDef = getField(this.schema, model, field);
5794
- if (!fieldDef?.relation) {
5877
+ if (!fieldDef || !fieldDef.relation || !fieldDef.array) {
5795
5878
  continue;
5796
5879
  }
5797
5880
  this.fixReversedResult(row[field], fieldDef.type, value);
5798
5881
  }
5799
5882
  }
5800
5883
  }
5884
+ transformJson(value) {
5885
+ return match18(this.schema.provider.type).with("sqlite", () => {
5886
+ invariant11(typeof value === "string", "Expected string, got " + typeof value);
5887
+ return JSON.parse(value);
5888
+ }).otherwise(() => value);
5889
+ }
5801
5890
  };
5802
5891
 
5803
5892
  // src/client/client-impl.ts
@@ -5816,7 +5905,7 @@ var ClientImpl = class _ClientImpl {
5816
5905
  $schema;
5817
5906
  kyselyProps;
5818
5907
  auth;
5819
- constructor(schema, options, baseClient) {
5908
+ constructor(schema, options, baseClient, executor) {
5820
5909
  this.schema = schema;
5821
5910
  this.options = options;
5822
5911
  this.$schema = schema;
@@ -5828,16 +5917,16 @@ var ClientImpl = class _ClientImpl {
5828
5917
  if (baseClient) {
5829
5918
  this.kyselyProps = {
5830
5919
  ...baseClient.kyselyProps,
5831
- executor: new ZenStackQueryExecutor(this, baseClient.kyselyProps.driver, baseClient.kyselyProps.dialect.createQueryCompiler(), baseClient.kyselyProps.dialect.createAdapter(), new DefaultConnectionProvider(baseClient.kyselyProps.driver))
5920
+ executor: executor ?? new ZenStackQueryExecutor(this, baseClient.kyselyProps.driver, baseClient.kyselyProps.dialect.createQueryCompiler(), baseClient.kyselyProps.dialect.createAdapter(), new DefaultConnectionProvider(baseClient.kyselyProps.driver))
5832
5921
  };
5833
5922
  this.kyselyRaw = baseClient.kyselyRaw;
5923
+ this.auth = baseClient.auth;
5834
5924
  } else {
5835
5925
  const dialect = this.getKyselyDialect();
5836
5926
  const driver = new ZenStackDriver(dialect.createDriver(), new Log(this.$options.log ?? []));
5837
5927
  const compiler = dialect.createQueryCompiler();
5838
5928
  const adapter = dialect.createAdapter();
5839
5929
  const connectionProvider = new DefaultConnectionProvider(driver);
5840
- const executor = new ZenStackQueryExecutor(this, driver, compiler, adapter, connectionProvider);
5841
5930
  this.kyselyProps = {
5842
5931
  config: {
5843
5932
  dialect,
@@ -5845,7 +5934,7 @@ var ClientImpl = class _ClientImpl {
5845
5934
  },
5846
5935
  dialect,
5847
5936
  driver,
5848
- executor
5937
+ executor: executor ?? new ZenStackQueryExecutor(this, driver, compiler, adapter, connectionProvider)
5849
5938
  };
5850
5939
  this.kyselyRaw = new Kysely({
5851
5940
  ...this.kyselyProps,
@@ -5861,6 +5950,15 @@ var ClientImpl = class _ClientImpl {
5861
5950
  get $qbRaw() {
5862
5951
  return this.kyselyRaw;
5863
5952
  }
5953
+ get isTransaction() {
5954
+ return this.kysely.isTransaction;
5955
+ }
5956
+ /**
5957
+ * Create a new client with a new query executor.
5958
+ */
5959
+ withExecutor(executor) {
5960
+ return new _ClientImpl(this.schema, this.$options, this, executor);
5961
+ }
5864
5962
  getKyselyDialect() {
5865
5963
  return match19(this.schema.provider.type).with("sqlite", () => this.makeSqliteKyselyDialect()).with("postgresql", () => this.makePostgresKyselyDialect()).exhaustive();
5866
5964
  }
@@ -5870,12 +5968,49 @@ var ClientImpl = class _ClientImpl {
5870
5968
  makeSqliteKyselyDialect() {
5871
5969
  return new SqliteDialect(this.options.dialectConfig);
5872
5970
  }
5873
- async $transaction(callback) {
5874
- return this.kysely.transaction().execute((tx) => {
5875
- const txClient = new _ClientImpl(this.schema, this.$options);
5971
+ // implementation
5972
+ async $transaction(input, options) {
5973
+ invariant12(typeof input === "function" || Array.isArray(input) && input.every((p) => p.then && p.cb), "Invalid transaction input, expected a function or an array of ZenStackPromise");
5974
+ if (typeof input === "function") {
5975
+ return this.interactiveTransaction(input, options);
5976
+ } else {
5977
+ return this.sequentialTransaction(input, options);
5978
+ }
5979
+ }
5980
+ async interactiveTransaction(callback, options) {
5981
+ if (this.kysely.isTransaction) {
5982
+ return callback(this);
5983
+ } else {
5984
+ let txBuilder = this.kysely.transaction();
5985
+ if (options?.isolationLevel) {
5986
+ txBuilder = txBuilder.setIsolationLevel(options.isolationLevel);
5987
+ }
5988
+ return txBuilder.execute((tx) => {
5989
+ const txClient = new _ClientImpl(this.schema, this.$options, this);
5990
+ txClient.kysely = tx;
5991
+ return callback(txClient);
5992
+ });
5993
+ }
5994
+ }
5995
+ async sequentialTransaction(arg, options) {
5996
+ const execute = /* @__PURE__ */ __name(async (tx) => {
5997
+ const txClient = new _ClientImpl(this.schema, this.$options, this);
5876
5998
  txClient.kysely = tx;
5877
- return callback(txClient);
5878
- });
5999
+ const result = [];
6000
+ for (const promise of arg) {
6001
+ result.push(await promise.cb(txClient));
6002
+ }
6003
+ return result;
6004
+ }, "execute");
6005
+ if (this.kysely.isTransaction) {
6006
+ return execute(this.kysely);
6007
+ } else {
6008
+ let txBuilder = this.kysely.transaction();
6009
+ if (options?.isolationLevel) {
6010
+ txBuilder = txBuilder.setIsolationLevel(options.isolationLevel);
6011
+ }
6012
+ return txBuilder.execute((tx) => execute(tx));
6013
+ }
5879
6014
  }
5880
6015
  get $procedures() {
5881
6016
  return Object.keys(this.$schema.procedures ?? {}).reduce((acc, name) => {
@@ -5937,6 +6072,39 @@ var ClientImpl = class _ClientImpl {
5937
6072
  get $auth() {
5938
6073
  return this.auth;
5939
6074
  }
6075
+ $executeRaw(query, ...values) {
6076
+ return createZenStackPromise(async () => {
6077
+ const result = await sql10(query, ...values).execute(this.kysely);
6078
+ return Number(result.numAffectedRows ?? 0);
6079
+ });
6080
+ }
6081
+ $executeRawUnsafe(query, ...values) {
6082
+ return createZenStackPromise(async () => {
6083
+ const compiledQuery = this.createRawCompiledQuery(query, values);
6084
+ const result = await this.kysely.executeQuery(compiledQuery);
6085
+ return Number(result.numAffectedRows ?? 0);
6086
+ });
6087
+ }
6088
+ $queryRaw(query, ...values) {
6089
+ return createZenStackPromise(async () => {
6090
+ const result = await sql10(query, ...values).execute(this.kysely);
6091
+ return result.rows;
6092
+ });
6093
+ }
6094
+ $queryRawUnsafe(query, ...values) {
6095
+ return createZenStackPromise(async () => {
6096
+ const compiledQuery = this.createRawCompiledQuery(query, values);
6097
+ const result = await this.kysely.executeQuery(compiledQuery);
6098
+ return result.rows;
6099
+ });
6100
+ }
6101
+ createRawCompiledQuery(query, values) {
6102
+ const q = CompiledQuery.raw(query, values);
6103
+ return {
6104
+ ...q,
6105
+ $raw: true
6106
+ };
6107
+ }
5940
6108
  };
5941
6109
  function createClientProxy(client) {
5942
6110
  const inputValidator = new InputValidator(client.$schema);
@@ -5959,9 +6127,9 @@ function createClientProxy(client) {
5959
6127
  __name(createClientProxy, "createClientProxy");
5960
6128
  function createModelCrudHandler(client, model, inputValidator, resultProcessor) {
5961
6129
  const createPromise = /* @__PURE__ */ __name((operation, args, handler, postProcess = false, throwIfNoResult = false) => {
5962
- return createDeferredPromise(async () => {
5963
- let proceed = /* @__PURE__ */ __name(async (_args, tx) => {
5964
- const _handler = tx ? handler.withClient(tx) : handler;
6130
+ return createZenStackPromise(async (txClient) => {
6131
+ let proceed = /* @__PURE__ */ __name(async (_args) => {
6132
+ const _handler = txClient ? handler.withClient(txClient) : handler;
5965
6133
  const r = await _handler.handle(operation, _args ?? args);
5966
6134
  if (!r && throwIfNoResult) {
5967
6135
  throw new NotFoundError(model);
@@ -6059,7 +6227,18 @@ function createModelCrudHandler(client, model, inputValidator, resultProcessor)
6059
6227
  };
6060
6228
  }
6061
6229
  __name(createModelCrudHandler, "createModelCrudHandler");
6230
+
6231
+ // src/client/plugin.ts
6232
+ function definePlugin(plugin) {
6233
+ return plugin;
6234
+ }
6235
+ __name(definePlugin, "definePlugin");
6062
6236
  export {
6063
- ZenStackClient
6237
+ InputValidationError,
6238
+ InternalError,
6239
+ NotFoundError,
6240
+ QueryError,
6241
+ ZenStackClient,
6242
+ definePlugin
6064
6243
  };
6065
6244
  //# sourceMappingURL=index.js.map