@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.js CHANGED
@@ -6,7 +6,7 @@ var __export = (target, all) => {
6
6
  };
7
7
 
8
8
  // src/client/client-impl.ts
9
- import { lowerCaseFirst as lowerCaseFirst2 } from "@zenstackhq/common-helpers";
9
+ import { invariant as invariant12, lowerCaseFirst as lowerCaseFirst2 } from "@zenstackhq/common-helpers";
10
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
 
@@ -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
 
@@ -2521,7 +2552,12 @@ var BaseOperationHandler = class {
2521
2552
  result = await query.execute();
2522
2553
  } catch (err) {
2523
2554
  const { sql: sql11, parameters } = query.compile();
2524
- throw new QueryError(`Failed to execute query: ${err}, sql: ${sql11}, parameters: ${parameters}`);
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: sql11, parameters } = query.compile();
3116
- throw new QueryError(`Error during updateMany: ${err}, sql: ${sql11}, 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 = {
@@ -5161,18 +5247,17 @@ var ZenStackQueryExecutor = class _ZenStackQueryExecutor extends DefaultQueryExe
5161
5247
  }
5162
5248
  const queryParams = compiledQuery.$raw ? compiledQuery.parameters : void 0;
5163
5249
  const result = await this.proceedQueryWithKyselyInterceptors(queryNode, queryParams, queryId);
5164
- await this.callAfterQueryInterceptionFilters(result, queryNode, mutationInterceptionInfo);
5250
+ if (this.isMutationNode(queryNode)) {
5251
+ await this.callAfterQueryInterceptionFilters(result, queryNode, mutationInterceptionInfo);
5252
+ }
5165
5253
  if (oldQueryNode !== queryNode) {
5166
5254
  }
5167
5255
  return result;
5168
5256
  }, "task");
5169
- return this.executeWithTransaction(task, !!mutationInterceptionInfo?.useTransactionForMutation);
5257
+ return task();
5170
5258
  }
5171
5259
  proceedQueryWithKyselyInterceptors(queryNode, parameters, queryId) {
5172
5260
  let proceed = /* @__PURE__ */ __name((q) => this.proceedQuery(q, parameters, queryId), "proceed");
5173
- const makeTx = /* @__PURE__ */ __name((p) => (callback) => {
5174
- return this.executeWithTransaction(() => callback(p));
5175
- }, "makeTx");
5176
5261
  const hooks = this.options.plugins?.filter((plugin) => typeof plugin.onKyselyQuery === "function").map((plugin) => plugin.onKyselyQuery.bind(plugin)) ?? [];
5177
5262
  for (const hook of hooks) {
5178
5263
  const _proceed = proceed;
@@ -5182,8 +5267,7 @@ var ZenStackQueryExecutor = class _ZenStackQueryExecutor extends DefaultQueryExe
5182
5267
  schema: this.client.$schema,
5183
5268
  kysely: this.kysely,
5184
5269
  query,
5185
- proceed: _proceed,
5186
- transaction: makeTx(_proceed)
5270
+ proceed: _proceed
5187
5271
  });
5188
5272
  }, "proceed");
5189
5273
  }
@@ -5199,9 +5283,14 @@ var ZenStackQueryExecutor = class _ZenStackQueryExecutor extends DefaultQueryExe
5199
5283
  };
5200
5284
  }
5201
5285
  try {
5202
- 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);
5203
5287
  } catch (err) {
5204
- 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);
5205
5294
  }
5206
5295
  }
5207
5296
  isMutationNode(queryNode) {
@@ -5229,24 +5318,9 @@ var ZenStackQueryExecutor = class _ZenStackQueryExecutor extends DefaultQueryExe
5229
5318
  return new _ZenStackQueryExecutor(this.client, this.driver, this.compiler, this.adapter, this.connectionProvider, []);
5230
5319
  }
5231
5320
  withConnectionProvider(connectionProvider) {
5232
- return new _ZenStackQueryExecutor(this.client, this.driver, this.compiler, this.adapter, connectionProvider);
5233
- }
5234
- async executeWithTransaction(callback, useTransaction = true) {
5235
- if (!useTransaction || this.driver.txConnection) {
5236
- return callback();
5237
- } else {
5238
- return this.provideConnection(async (connection) => {
5239
- try {
5240
- await this.driver.beginTransaction(connection, {});
5241
- const result = await callback();
5242
- await this.driver.commitTransaction(connection);
5243
- return result;
5244
- } catch (error) {
5245
- await this.driver.rollbackTransaction(connection);
5246
- throw error;
5247
- }
5248
- });
5249
- }
5321
+ const newExecutor = new _ZenStackQueryExecutor(this.client, this.driver, this.compiler, this.adapter, connectionProvider);
5322
+ newExecutor.client = this.client.withExecutor(newExecutor);
5323
+ return newExecutor;
5250
5324
  }
5251
5325
  get hasMutationHooks() {
5252
5326
  return this.client.$options.plugins?.some((plugin) => plugin.beforeEntityMutation || plugin.afterEntityMutation);
@@ -5288,14 +5362,13 @@ var ZenStackQueryExecutor = class _ZenStackQueryExecutor extends DefaultQueryExe
5288
5362
  queryNode
5289
5363
  });
5290
5364
  result.intercept ||= filterResult.intercept;
5291
- result.useTransactionForMutation ||= filterResult.useTransactionForMutation;
5292
5365
  result.loadBeforeMutationEntity ||= filterResult.loadBeforeMutationEntity;
5293
5366
  result.loadAfterMutationEntity ||= filterResult.loadAfterMutationEntity;
5294
5367
  }
5295
5368
  }
5296
5369
  let beforeMutationEntities;
5297
5370
  if (result.loadBeforeMutationEntity && (UpdateQueryNode2.is(queryNode) || DeleteQueryNode2.is(queryNode))) {
5298
- beforeMutationEntities = await this.loadEntities(this.kysely, mutationModel, where);
5371
+ beforeMutationEntities = await this.loadEntities(mutationModel, where);
5299
5372
  }
5300
5373
  return {
5301
5374
  ...result,
@@ -5308,15 +5381,14 @@ var ZenStackQueryExecutor = class _ZenStackQueryExecutor extends DefaultQueryExe
5308
5381
  return void 0;
5309
5382
  }
5310
5383
  }
5311
- callBeforeMutationHooks(queryNode, mutationInterceptionInfo) {
5384
+ async callBeforeMutationHooks(queryNode, mutationInterceptionInfo) {
5312
5385
  if (!mutationInterceptionInfo?.intercept) {
5313
5386
  return;
5314
5387
  }
5315
5388
  if (this.options.plugins) {
5316
5389
  for (const plugin of this.options.plugins) {
5317
5390
  if (plugin.beforeEntityMutation) {
5318
- plugin.beforeEntityMutation({
5319
- // context: this.queryContext,
5391
+ await plugin.beforeEntityMutation({
5320
5392
  model: this.getMutationModel(queryNode),
5321
5393
  action: mutationInterceptionInfo.action,
5322
5394
  queryNode,
@@ -5337,12 +5409,12 @@ var ZenStackQueryExecutor = class _ZenStackQueryExecutor extends DefaultQueryExe
5337
5409
  let afterMutationEntities = void 0;
5338
5410
  if (mutationInterceptionInfo.loadAfterMutationEntity) {
5339
5411
  if (UpdateQueryNode2.is(queryNode)) {
5340
- afterMutationEntities = await this.loadEntities(this.kysely, mutationModel, mutationInterceptionInfo.where);
5412
+ afterMutationEntities = await this.loadEntities(mutationModel, mutationInterceptionInfo.where);
5341
5413
  } else {
5342
5414
  afterMutationEntities = queryResult.rows;
5343
5415
  }
5344
5416
  }
5345
- plugin.afterEntityMutation({
5417
+ await plugin.afterEntityMutation({
5346
5418
  model: this.getMutationModel(queryNode),
5347
5419
  action: mutationInterceptionInfo.action,
5348
5420
  queryNode,
@@ -5353,17 +5425,17 @@ var ZenStackQueryExecutor = class _ZenStackQueryExecutor extends DefaultQueryExe
5353
5425
  }
5354
5426
  }
5355
5427
  }
5356
- async loadEntities(kysely, model, where) {
5357
- const selectQuery = kysely.selectFrom(model).selectAll();
5428
+ async loadEntities(model, where) {
5429
+ const selectQuery = this.kysely.selectFrom(model).selectAll();
5358
5430
  let selectQueryNode = selectQuery.toOperationNode();
5359
5431
  selectQueryNode = {
5360
5432
  ...selectQueryNode,
5361
5433
  where: this.andNodes(selectQueryNode.where, where)
5362
5434
  };
5363
- const compiled = kysely.getExecutor().compileQuery(selectQueryNode, {
5435
+ const compiled = this.compileQuery(selectQueryNode);
5436
+ const result = await this.executeQuery(compiled, {
5364
5437
  queryId: `zenstack-${nanoid2()}`
5365
5438
  });
5366
- const result = await kysely.executeQuery(compiled);
5367
5439
  return result.rows;
5368
5440
  }
5369
5441
  andNodes(condition1, condition2) {
@@ -5392,7 +5464,7 @@ __export(functions_exports, {
5392
5464
  search: () => search,
5393
5465
  startsWith: () => startsWith
5394
5466
  });
5395
- import { invariant as invariant8, lowerCaseFirst, upperCaseFirst } from "@zenstackhq/common-helpers";
5467
+ import { invariant as invariant9, lowerCaseFirst, upperCaseFirst } from "@zenstackhq/common-helpers";
5396
5468
  import { sql as sql8, ValueNode as ValueNode4 } from "kysely";
5397
5469
  import { match as match16 } from "ts-pattern";
5398
5470
  var contains = /* @__PURE__ */ __name((eb, args) => {
@@ -5499,7 +5571,7 @@ var currentOperation = /* @__PURE__ */ __name((_eb, args, { operation }) => {
5499
5571
  }, "currentOperation");
5500
5572
  function processCasing(casing, result, model) {
5501
5573
  const opNode = casing.toOperationNode();
5502
- 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');
5503
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(() => {
5504
5576
  throw new Error(`Invalid casing value: ${opNode.value}. Must be "original", "upper", "lower", "capitalize", or "uncapitalize".`);
5505
5577
  });
@@ -5508,7 +5580,7 @@ function processCasing(casing, result, model) {
5508
5580
  __name(processCasing, "processCasing");
5509
5581
 
5510
5582
  // src/client/helpers/schema-db-pusher.ts
5511
- import { invariant as invariant9 } from "@zenstackhq/common-helpers";
5583
+ import { invariant as invariant10 } from "@zenstackhq/common-helpers";
5512
5584
  import { sql as sql9 } from "kysely";
5513
5585
  import { match as match17 } from "ts-pattern";
5514
5586
  var SchemaDbPusher = class {
@@ -5565,7 +5637,7 @@ var SchemaDbPusher = class {
5565
5637
  }
5566
5638
  addUniqueConstraint(table, modelDef) {
5567
5639
  for (const [key, value] of Object.entries(modelDef.uniqueFields)) {
5568
- invariant9(typeof value === "object", "expecting an object");
5640
+ invariant10(typeof value === "object", "expecting an object");
5569
5641
  if ("type" in value) {
5570
5642
  const fieldDef = modelDef.fields[key];
5571
5643
  if (fieldDef.unique) {
@@ -5611,7 +5683,7 @@ var SchemaDbPusher = class {
5611
5683
  return "serial";
5612
5684
  }
5613
5685
  const type = fieldDef.type;
5614
- 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(() => {
5615
5687
  throw new Error(`Unsupported field type: ${type}`);
5616
5688
  });
5617
5689
  if (fieldDef.array) {
@@ -5624,7 +5696,7 @@ var SchemaDbPusher = class {
5624
5696
  return fieldDef.default && ExpressionUtils.isCall(fieldDef.default) && fieldDef.default.function === "autoincrement";
5625
5697
  }
5626
5698
  addForeignKeyConstraint(table, model, fieldName, fieldDef) {
5627
- invariant9(fieldDef.relation, "field must be a relation");
5699
+ invariant10(fieldDef.relation, "field must be a relation");
5628
5700
  if (!fieldDef.relation.fields || !fieldDef.relation.references) {
5629
5701
  return table;
5630
5702
  }
@@ -5645,11 +5717,11 @@ var SchemaDbPusher = class {
5645
5717
  };
5646
5718
 
5647
5719
  // src/client/promise.ts
5648
- function createDeferredPromise(callback) {
5720
+ function createZenStackPromise(callback) {
5649
5721
  let promise;
5650
- const cb = /* @__PURE__ */ __name(() => {
5722
+ const cb = /* @__PURE__ */ __name((txClient) => {
5651
5723
  try {
5652
- return promise ??= valueToPromise(callback());
5724
+ return promise ??= valueToPromise(callback(txClient));
5653
5725
  } catch (err) {
5654
5726
  return Promise.reject(err);
5655
5727
  }
@@ -5664,10 +5736,11 @@ function createDeferredPromise(callback) {
5664
5736
  finally(onFinally) {
5665
5737
  return cb().finally(onFinally);
5666
5738
  },
5739
+ cb,
5667
5740
  [Symbol.toStringTag]: "ZenStackPromise"
5668
5741
  };
5669
5742
  }
5670
- __name(createDeferredPromise, "createDeferredPromise");
5743
+ __name(createZenStackPromise, "createZenStackPromise");
5671
5744
  function valueToPromise(thing) {
5672
5745
  if (typeof thing === "object" && typeof thing?.then === "function") {
5673
5746
  return thing;
@@ -5678,7 +5751,7 @@ function valueToPromise(thing) {
5678
5751
  __name(valueToPromise, "valueToPromise");
5679
5752
 
5680
5753
  // src/client/result-processor.ts
5681
- import { invariant as invariant10 } from "@zenstackhq/common-helpers";
5754
+ import { invariant as invariant11 } from "@zenstackhq/common-helpers";
5682
5755
  import Decimal2 from "decimal.js";
5683
5756
  import { match as match18 } from "ts-pattern";
5684
5757
  var ResultProcessor = class {
@@ -5753,20 +5826,20 @@ var ResultProcessor = class {
5753
5826
  return this.doProcessResult(relationData, fieldDef.type);
5754
5827
  }
5755
5828
  transformScalar(value, type) {
5756
- 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);
5757
5830
  }
5758
5831
  transformDecimal(value) {
5759
5832
  if (value instanceof Decimal2) {
5760
5833
  return value;
5761
5834
  }
5762
- 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}`);
5763
5836
  return new Decimal2(value);
5764
5837
  }
5765
5838
  transformBigInt(value) {
5766
5839
  if (typeof value === "bigint") {
5767
5840
  return value;
5768
5841
  }
5769
- 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}`);
5770
5843
  return BigInt(value);
5771
5844
  }
5772
5845
  transformBoolean(value) {
@@ -5785,6 +5858,9 @@ var ResultProcessor = class {
5785
5858
  return Buffer.isBuffer(value) ? Uint8Array.from(value) : value;
5786
5859
  }
5787
5860
  fixReversedResult(data, model, args) {
5861
+ if (!data) {
5862
+ return;
5863
+ }
5788
5864
  if (Array.isArray(data) && typeof args === "object" && args && args.take !== void 0 && args.take < 0) {
5789
5865
  data.reverse();
5790
5866
  }
@@ -5798,13 +5874,19 @@ var ResultProcessor = class {
5798
5874
  continue;
5799
5875
  }
5800
5876
  const fieldDef = getField(this.schema, model, field);
5801
- if (!fieldDef?.relation) {
5877
+ if (!fieldDef || !fieldDef.relation || !fieldDef.array) {
5802
5878
  continue;
5803
5879
  }
5804
5880
  this.fixReversedResult(row[field], fieldDef.type, value);
5805
5881
  }
5806
5882
  }
5807
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
+ }
5808
5890
  };
5809
5891
 
5810
5892
  // src/client/client-impl.ts
@@ -5823,7 +5905,7 @@ var ClientImpl = class _ClientImpl {
5823
5905
  $schema;
5824
5906
  kyselyProps;
5825
5907
  auth;
5826
- constructor(schema, options, baseClient) {
5908
+ constructor(schema, options, baseClient, executor) {
5827
5909
  this.schema = schema;
5828
5910
  this.options = options;
5829
5911
  this.$schema = schema;
@@ -5835,16 +5917,16 @@ var ClientImpl = class _ClientImpl {
5835
5917
  if (baseClient) {
5836
5918
  this.kyselyProps = {
5837
5919
  ...baseClient.kyselyProps,
5838
- 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))
5839
5921
  };
5840
5922
  this.kyselyRaw = baseClient.kyselyRaw;
5923
+ this.auth = baseClient.auth;
5841
5924
  } else {
5842
5925
  const dialect = this.getKyselyDialect();
5843
5926
  const driver = new ZenStackDriver(dialect.createDriver(), new Log(this.$options.log ?? []));
5844
5927
  const compiler = dialect.createQueryCompiler();
5845
5928
  const adapter = dialect.createAdapter();
5846
5929
  const connectionProvider = new DefaultConnectionProvider(driver);
5847
- const executor = new ZenStackQueryExecutor(this, driver, compiler, adapter, connectionProvider);
5848
5930
  this.kyselyProps = {
5849
5931
  config: {
5850
5932
  dialect,
@@ -5852,7 +5934,7 @@ var ClientImpl = class _ClientImpl {
5852
5934
  },
5853
5935
  dialect,
5854
5936
  driver,
5855
- executor
5937
+ executor: executor ?? new ZenStackQueryExecutor(this, driver, compiler, adapter, connectionProvider)
5856
5938
  };
5857
5939
  this.kyselyRaw = new Kysely({
5858
5940
  ...this.kyselyProps,
@@ -5868,6 +5950,15 @@ var ClientImpl = class _ClientImpl {
5868
5950
  get $qbRaw() {
5869
5951
  return this.kyselyRaw;
5870
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
+ }
5871
5962
  getKyselyDialect() {
5872
5963
  return match19(this.schema.provider.type).with("sqlite", () => this.makeSqliteKyselyDialect()).with("postgresql", () => this.makePostgresKyselyDialect()).exhaustive();
5873
5964
  }
@@ -5877,12 +5968,49 @@ var ClientImpl = class _ClientImpl {
5877
5968
  makeSqliteKyselyDialect() {
5878
5969
  return new SqliteDialect(this.options.dialectConfig);
5879
5970
  }
5880
- async $transaction(callback) {
5881
- return this.kysely.transaction().execute((tx) => {
5882
- 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);
5883
5998
  txClient.kysely = tx;
5884
- return callback(txClient);
5885
- });
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
+ }
5886
6014
  }
5887
6015
  get $procedures() {
5888
6016
  return Object.keys(this.$schema.procedures ?? {}).reduce((acc, name) => {
@@ -5945,26 +6073,26 @@ var ClientImpl = class _ClientImpl {
5945
6073
  return this.auth;
5946
6074
  }
5947
6075
  $executeRaw(query, ...values) {
5948
- return createDeferredPromise(async () => {
6076
+ return createZenStackPromise(async () => {
5949
6077
  const result = await sql10(query, ...values).execute(this.kysely);
5950
6078
  return Number(result.numAffectedRows ?? 0);
5951
6079
  });
5952
6080
  }
5953
6081
  $executeRawUnsafe(query, ...values) {
5954
- return createDeferredPromise(async () => {
6082
+ return createZenStackPromise(async () => {
5955
6083
  const compiledQuery = this.createRawCompiledQuery(query, values);
5956
6084
  const result = await this.kysely.executeQuery(compiledQuery);
5957
6085
  return Number(result.numAffectedRows ?? 0);
5958
6086
  });
5959
6087
  }
5960
6088
  $queryRaw(query, ...values) {
5961
- return createDeferredPromise(async () => {
6089
+ return createZenStackPromise(async () => {
5962
6090
  const result = await sql10(query, ...values).execute(this.kysely);
5963
6091
  return result.rows;
5964
6092
  });
5965
6093
  }
5966
6094
  $queryRawUnsafe(query, ...values) {
5967
- return createDeferredPromise(async () => {
6095
+ return createZenStackPromise(async () => {
5968
6096
  const compiledQuery = this.createRawCompiledQuery(query, values);
5969
6097
  const result = await this.kysely.executeQuery(compiledQuery);
5970
6098
  return result.rows;
@@ -5999,9 +6127,9 @@ function createClientProxy(client) {
5999
6127
  __name(createClientProxy, "createClientProxy");
6000
6128
  function createModelCrudHandler(client, model, inputValidator, resultProcessor) {
6001
6129
  const createPromise = /* @__PURE__ */ __name((operation, args, handler, postProcess = false, throwIfNoResult = false) => {
6002
- return createDeferredPromise(async () => {
6003
- let proceed = /* @__PURE__ */ __name(async (_args, tx) => {
6004
- 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;
6005
6133
  const r = await _handler.handle(operation, _args ?? args);
6006
6134
  if (!r && throwIfNoResult) {
6007
6135
  throw new NotFoundError(model);
@@ -6099,7 +6227,18 @@ function createModelCrudHandler(client, model, inputValidator, resultProcessor)
6099
6227
  };
6100
6228
  }
6101
6229
  __name(createModelCrudHandler, "createModelCrudHandler");
6230
+
6231
+ // src/client/plugin.ts
6232
+ function definePlugin(plugin) {
6233
+ return plugin;
6234
+ }
6235
+ __name(definePlugin, "definePlugin");
6102
6236
  export {
6103
- ZenStackClient
6237
+ InputValidationError,
6238
+ InternalError,
6239
+ NotFoundError,
6240
+ QueryError,
6241
+ ZenStackClient,
6242
+ definePlugin
6104
6243
  };
6105
6244
  //# sourceMappingURL=index.js.map