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