@zenstackhq/orm 3.0.0-beta.27 → 3.0.0-beta.29

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
@@ -32,9 +32,15 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
32
32
  // src/index.ts
33
33
  var src_exports = {};
34
34
  __export(src_exports, {
35
+ AnyNull: () => AnyNull,
36
+ AnyNullClass: () => AnyNullClass,
35
37
  BaseCrudDialect: () => BaseCrudDialect,
36
38
  CRUD: () => CRUD,
37
39
  CRUD_EXT: () => CRUD_EXT,
40
+ DbNull: () => DbNull,
41
+ DbNullClass: () => DbNullClass,
42
+ JsonNull: () => JsonNull,
43
+ JsonNullClass: () => JsonNullClass,
38
44
  KyselyUtils: () => kysely_utils_exports,
39
45
  ORMError: () => ORMError,
40
46
  ORMErrorReason: () => ORMErrorReason,
@@ -83,10 +89,12 @@ __export(query_utils_exports, {
83
89
  isInheritedField: () => isInheritedField,
84
90
  isRelationField: () => isRelationField,
85
91
  isScalarField: () => isScalarField,
92
+ isTypeDef: () => isTypeDef,
86
93
  makeDefaultOrderBy: () => makeDefaultOrderBy,
87
94
  requireField: () => requireField,
88
95
  requireIdFields: () => requireIdFields,
89
96
  requireModel: () => requireModel,
97
+ requireTypeDef: () => requireTypeDef,
90
98
  stripAlias: () => stripAlias
91
99
  });
92
100
  var import_common_helpers = require("@zenstackhq/common-helpers");
@@ -236,6 +244,14 @@ function requireModel(schema, model) {
236
244
  return modelDef;
237
245
  }
238
246
  __name(requireModel, "requireModel");
247
+ function requireTypeDef(schema, type) {
248
+ const typeDef = getTypeDef(schema, type);
249
+ if (!typeDef) {
250
+ throw createInternalError(`Type "${type}" not found in schema`, type);
251
+ }
252
+ return typeDef;
253
+ }
254
+ __name(requireTypeDef, "requireTypeDef");
239
255
  function getField(schema, model, field) {
240
256
  const modelDef = getModel(schema, model);
241
257
  return modelDef?.fields[field];
@@ -383,6 +399,10 @@ function getEnum(schema, type) {
383
399
  return schema.enums?.[type];
384
400
  }
385
401
  __name(getEnum, "getEnum");
402
+ function isTypeDef(schema, type) {
403
+ return !!schema.typeDefs?.[type];
404
+ }
405
+ __name(isTypeDef, "isTypeDef");
386
406
  function buildJoinPairs(schema, model, modelAlias, relationField, relationModelAlias) {
387
407
  const { keyPairs, ownedByModel } = getRelationForeignKeyFieldPairs(schema, model, relationField);
388
408
  return keyPairs.map(({ fk, pk }) => {
@@ -634,6 +654,32 @@ var import_kysely3 = require("kysely");
634
654
  var import_ts_pattern3 = require("ts-pattern");
635
655
  var import_zod = __toESM(require("zod"), 1);
636
656
 
657
+ // src/common-types.ts
658
+ var DbNullClass = class {
659
+ static {
660
+ __name(this, "DbNullClass");
661
+ }
662
+ // @ts-ignore
663
+ __brand = "DbNull";
664
+ };
665
+ var DbNull = new DbNullClass();
666
+ var JsonNullClass = class {
667
+ static {
668
+ __name(this, "JsonNullClass");
669
+ }
670
+ // @ts-ignore
671
+ __brand = "JsonNull";
672
+ };
673
+ var JsonNull = new JsonNullClass();
674
+ var AnyNullClass = class {
675
+ static {
676
+ __name(this, "AnyNullClass");
677
+ }
678
+ // @ts-ignore
679
+ __brand = "AnyNull";
680
+ };
681
+ var AnyNull = new AnyNullClass();
682
+
637
683
  // src/client/crud/dialects/base-dialect.ts
638
684
  var import_common_helpers2 = require("@zenstackhq/common-helpers");
639
685
  var import_kysely2 = require("kysely");
@@ -905,12 +951,174 @@ var BaseCrudDialect = class {
905
951
  if (isEnum(this.schema, fieldDef.type)) {
906
952
  return this.buildEnumFilter(fieldRef, fieldDef, payload);
907
953
  }
908
- return (0, import_ts_pattern2.match)(fieldDef.type).with("String", () => this.buildStringFilter(fieldRef, payload)).with(import_ts_pattern2.P.union("Int", "Float", "Decimal", "BigInt"), (type) => this.buildNumberFilter(fieldRef, type, payload)).with("Boolean", () => this.buildBooleanFilter(fieldRef, payload)).with("DateTime", () => this.buildDateTimeFilter(fieldRef, payload)).with("Bytes", () => this.buildBytesFilter(fieldRef, payload)).with("Json", () => {
909
- throw createNotSupportedError("JSON filters are not supported yet");
910
- }).with("Unsupported", () => {
954
+ if (isTypeDef(this.schema, fieldDef.type)) {
955
+ return this.buildJsonFilter(fieldRef, payload, fieldDef);
956
+ }
957
+ return (0, import_ts_pattern2.match)(fieldDef.type).with("String", () => this.buildStringFilter(fieldRef, payload)).with(import_ts_pattern2.P.union("Int", "Float", "Decimal", "BigInt"), (type) => this.buildNumberFilter(fieldRef, type, payload)).with("Boolean", () => this.buildBooleanFilter(fieldRef, payload)).with("DateTime", () => this.buildDateTimeFilter(fieldRef, payload)).with("Bytes", () => this.buildBytesFilter(fieldRef, payload)).with("Json", () => this.buildJsonFilter(fieldRef, payload, fieldDef)).with("Unsupported", () => {
911
958
  throw createInvalidInputError(`Unsupported field cannot be used in filters`);
912
959
  }).exhaustive();
913
960
  }
961
+ buildJsonFilter(receiver, filter, fieldDef) {
962
+ (0, import_common_helpers2.invariant)(filter && typeof filter === "object", "Json filter payload must be an object");
963
+ if ([
964
+ "path",
965
+ "equals",
966
+ "not",
967
+ "string_contains",
968
+ "string_starts_with",
969
+ "string_ends_with",
970
+ "array_contains",
971
+ "array_starts_with",
972
+ "array_ends_with"
973
+ ].some((k) => k in filter)) {
974
+ return this.buildPlainJsonFilter(receiver, filter);
975
+ } else if (isTypeDef(this.schema, fieldDef.type)) {
976
+ return this.buildTypedJsonFilter(receiver, filter, fieldDef.type, !!fieldDef.array);
977
+ } else {
978
+ throw createInvalidInputError(`Invalid JSON filter payload`);
979
+ }
980
+ }
981
+ buildPlainJsonFilter(receiver, filter) {
982
+ const clauses = [];
983
+ const path = filter.path;
984
+ const jsonReceiver = this.buildJsonPathSelection(receiver, path);
985
+ const stringReceiver = this.eb.cast(jsonReceiver, "text");
986
+ const mode = filter.mode ?? "default";
987
+ (0, import_common_helpers2.invariant)(mode === "default" || mode === "insensitive", "Invalid JSON filter mode");
988
+ for (const [key, value] of Object.entries(filter)) {
989
+ switch (key) {
990
+ case "equals": {
991
+ clauses.push(this.buildJsonValueFilterClause(jsonReceiver, value));
992
+ break;
993
+ }
994
+ case "not": {
995
+ clauses.push(this.eb.not(this.buildJsonValueFilterClause(jsonReceiver, value)));
996
+ break;
997
+ }
998
+ case "string_contains": {
999
+ (0, import_common_helpers2.invariant)(typeof value === "string", "string_contains value must be a string");
1000
+ clauses.push(this.buildJsonStringFilter(stringReceiver, key, value, mode));
1001
+ break;
1002
+ }
1003
+ case "string_starts_with": {
1004
+ (0, import_common_helpers2.invariant)(typeof value === "string", "string_starts_with value must be a string");
1005
+ clauses.push(this.buildJsonStringFilter(stringReceiver, key, value, mode));
1006
+ break;
1007
+ }
1008
+ case "string_ends_with": {
1009
+ (0, import_common_helpers2.invariant)(typeof value === "string", "string_ends_with value must be a string");
1010
+ clauses.push(this.buildJsonStringFilter(stringReceiver, key, value, mode));
1011
+ break;
1012
+ }
1013
+ case "array_contains": {
1014
+ clauses.push(this.buildJsonArrayFilter(jsonReceiver, key, value));
1015
+ break;
1016
+ }
1017
+ case "array_starts_with": {
1018
+ clauses.push(this.buildJsonArrayFilter(jsonReceiver, key, value));
1019
+ break;
1020
+ }
1021
+ case "array_ends_with": {
1022
+ clauses.push(this.buildJsonArrayFilter(jsonReceiver, key, value));
1023
+ break;
1024
+ }
1025
+ case "path":
1026
+ case "mode":
1027
+ break;
1028
+ default:
1029
+ throw createInvalidInputError(`Invalid JSON filter key: ${key}`);
1030
+ }
1031
+ }
1032
+ return this.and(...clauses);
1033
+ }
1034
+ buildTypedJsonFilter(receiver, filter, typeDefName, array) {
1035
+ if (array) {
1036
+ return this.buildTypedJsonArrayFilter(receiver, filter, typeDefName);
1037
+ } else {
1038
+ return this.buildTypeJsonNonArrayFilter(receiver, filter, typeDefName);
1039
+ }
1040
+ }
1041
+ buildTypedJsonArrayFilter(receiver, filter, typeDefName) {
1042
+ (0, import_common_helpers2.invariant)(filter && typeof filter === "object", "Typed JSON array filter payload must be an object");
1043
+ const makeExistsPred = /* @__PURE__ */ __name((filter2) => this.buildJsonArrayExistsPredicate(receiver, (elem) => this.buildTypedJsonFilter(elem, filter2, typeDefName, false)), "makeExistsPred");
1044
+ const makeExistsNegatedPred = /* @__PURE__ */ __name((filter2) => this.buildJsonArrayExistsPredicate(receiver, (elem) => this.eb.not(this.buildTypedJsonFilter(elem, filter2, typeDefName, false))), "makeExistsNegatedPred");
1045
+ const clauses = [];
1046
+ for (const [key, value] of Object.entries(filter)) {
1047
+ if (!value || typeof value !== "object") {
1048
+ continue;
1049
+ }
1050
+ switch (key) {
1051
+ case "some":
1052
+ clauses.push(makeExistsPred(value));
1053
+ break;
1054
+ case "none":
1055
+ clauses.push(this.eb.not(makeExistsPred(value)));
1056
+ break;
1057
+ case "every":
1058
+ clauses.push(this.eb.not(makeExistsNegatedPred(value)));
1059
+ break;
1060
+ default:
1061
+ (0, import_common_helpers2.invariant)(false, `Invalid typed JSON array filter key: ${key}`);
1062
+ }
1063
+ }
1064
+ return this.and(...clauses);
1065
+ }
1066
+ buildTypeJsonNonArrayFilter(receiver, filter, typeDefName) {
1067
+ const clauses = [];
1068
+ if (filter === null) {
1069
+ return this.eb(receiver, "=", "null");
1070
+ }
1071
+ (0, import_common_helpers2.invariant)(filter && typeof filter === "object", "Typed JSON filter payload must be an object");
1072
+ if ("is" in filter || "isNot" in filter) {
1073
+ if ("is" in filter && filter.is && typeof filter.is === "object") {
1074
+ clauses.push(this.buildTypedJsonFilter(receiver, filter.is, typeDefName, false));
1075
+ }
1076
+ if ("isNot" in filter && filter.isNot && typeof filter.isNot === "object") {
1077
+ clauses.push(this.eb.not(this.buildTypedJsonFilter(receiver, filter.isNot, typeDefName, false)));
1078
+ }
1079
+ } else {
1080
+ const typeDef = requireTypeDef(this.schema, typeDefName);
1081
+ for (const [key, value] of Object.entries(filter)) {
1082
+ const fieldDef = typeDef.fields[key];
1083
+ (0, import_common_helpers2.invariant)(fieldDef, `Field "${key}" not found in type definition "${typeDefName}"`);
1084
+ const fieldReceiver = this.buildJsonPathSelection(receiver, `$.${key}`);
1085
+ if (isTypeDef(this.schema, fieldDef.type)) {
1086
+ clauses.push(this.buildTypedJsonFilter(fieldReceiver, value, fieldDef.type, !!fieldDef.array));
1087
+ } else {
1088
+ if (fieldDef.array) {
1089
+ clauses.push(this.buildArrayFilter(fieldReceiver, fieldDef, value));
1090
+ } else {
1091
+ let _receiver = fieldReceiver;
1092
+ if (fieldDef.type === "String") {
1093
+ _receiver = this.eb.fn("trim", [
1094
+ this.eb.cast(fieldReceiver, "text"),
1095
+ import_kysely2.sql.lit('"')
1096
+ ]);
1097
+ }
1098
+ clauses.push(this.buildPrimitiveFilter(_receiver, fieldDef, value));
1099
+ }
1100
+ }
1101
+ }
1102
+ }
1103
+ return this.and(...clauses);
1104
+ }
1105
+ buildJsonValueFilterClause(lhs, value) {
1106
+ if (value instanceof DbNullClass) {
1107
+ return this.eb(lhs, "is", null);
1108
+ } else if (value instanceof JsonNullClass) {
1109
+ return this.eb.and([
1110
+ this.eb(lhs, "=", "null"),
1111
+ this.eb(lhs, "is not", null)
1112
+ ]);
1113
+ } else if (value instanceof AnyNullClass) {
1114
+ return this.eb.or([
1115
+ this.eb(lhs, "is", null),
1116
+ this.eb(lhs, "=", "null")
1117
+ ]);
1118
+ } else {
1119
+ return this.buildLiteralFilter(lhs, "Json", value);
1120
+ }
1121
+ }
914
1122
  buildLiteralFilter(lhs, type, rhs) {
915
1123
  return this.eb(lhs, "=", rhs !== null && rhs !== void 0 ? this.transformPrimitive(rhs, type, false) : rhs);
916
1124
  }
@@ -981,7 +1189,9 @@ var BaseCrudDialect = class {
981
1189
  if (key === "mode" || consumedKeys.includes(key)) {
982
1190
  continue;
983
1191
  }
984
- const condition = (0, import_ts_pattern2.match)(key).with("contains", () => mode === "insensitive" ? this.eb(fieldRef, "ilike", import_kysely2.sql.val(`%${value}%`)) : this.eb(fieldRef, "like", import_kysely2.sql.val(`%${value}%`))).with("startsWith", () => mode === "insensitive" ? this.eb(fieldRef, "ilike", import_kysely2.sql.val(`${value}%`)) : this.eb(fieldRef, "like", import_kysely2.sql.val(`${value}%`))).with("endsWith", () => mode === "insensitive" ? this.eb(fieldRef, "ilike", import_kysely2.sql.val(`%${value}`)) : this.eb(fieldRef, "like", import_kysely2.sql.val(`%${value}`))).otherwise(() => {
1192
+ (0, import_common_helpers2.invariant)(typeof value === "string", `${key} value must be a string`);
1193
+ const escapedValue = this.escapeLikePattern(value);
1194
+ const condition = (0, import_ts_pattern2.match)(key).with("contains", () => this.buildStringLike(fieldRef, `%${escapedValue}%`, mode === "insensitive")).with("startsWith", () => this.buildStringLike(fieldRef, `${escapedValue}%`, mode === "insensitive")).with("endsWith", () => this.buildStringLike(fieldRef, `%${escapedValue}`, mode === "insensitive")).otherwise(() => {
985
1195
  throw createInvalidInputError(`Invalid string filter key: ${key}`);
986
1196
  });
987
1197
  if (condition) {
@@ -991,6 +1201,19 @@ var BaseCrudDialect = class {
991
1201
  }
992
1202
  return this.and(...conditions);
993
1203
  }
1204
+ buildJsonStringFilter(receiver, operation, value, mode) {
1205
+ const escapedValue = this.escapeLikePattern(value);
1206
+ const pattern = (0, import_ts_pattern2.match)(operation).with("string_contains", () => `"%${escapedValue}%"`).with("string_starts_with", () => `"${escapedValue}%"`).with("string_ends_with", () => `"%${escapedValue}"`).exhaustive();
1207
+ return this.buildStringLike(receiver, pattern, mode === "insensitive");
1208
+ }
1209
+ escapeLikePattern(pattern) {
1210
+ return pattern.replace(/\\/g, "\\\\").replace(/%/g, "\\%").replace(/_/g, "\\_");
1211
+ }
1212
+ buildStringLike(receiver, pattern, insensitive) {
1213
+ const { supportsILike } = this.getStringCasingBehavior();
1214
+ const op = insensitive && supportsILike ? "ilike" : "like";
1215
+ return import_kysely2.sql`${receiver} ${import_kysely2.sql.raw(op)} ${import_kysely2.sql.val(pattern)} escape '\\'`;
1216
+ }
994
1217
  prepStringCasing(eb, value, mode) {
995
1218
  if (!mode || mode === "default") {
996
1219
  return value === null ? value : import_kysely2.sql.val(value);
@@ -1316,6 +1539,13 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
1316
1539
  if (value === void 0) {
1317
1540
  return value;
1318
1541
  }
1542
+ if (value instanceof JsonNullClass) {
1543
+ return "null";
1544
+ } else if (value instanceof DbNullClass) {
1545
+ return null;
1546
+ } else if (value instanceof AnyNullClass) {
1547
+ (0, import_common_helpers3.invariant)(false, "should not reach here: AnyNull is not a valid input value");
1548
+ }
1319
1549
  if (Array.isArray(value)) {
1320
1550
  if (type === "Json" && !forArrayField) {
1321
1551
  return JSON.stringify(value);
@@ -1512,6 +1742,35 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
1512
1742
  return `ARRAY[${values.map((v) => typeof v === "string" ? `'${v}'` : v)}]`;
1513
1743
  }
1514
1744
  }
1745
+ buildJsonPathSelection(receiver, path) {
1746
+ if (path) {
1747
+ return this.eb.fn("jsonb_path_query_first", [
1748
+ receiver,
1749
+ this.eb.val(path)
1750
+ ]);
1751
+ } else {
1752
+ return receiver;
1753
+ }
1754
+ }
1755
+ buildJsonArrayFilter(lhs, operation, value) {
1756
+ return (0, import_ts_pattern3.match)(operation).with("array_contains", () => {
1757
+ const v = Array.isArray(value) ? value : [
1758
+ value
1759
+ ];
1760
+ return import_kysely3.sql`${lhs} @> ${import_kysely3.sql.val(JSON.stringify(v))}::jsonb`;
1761
+ }).with("array_starts_with", () => this.eb(this.eb.fn("jsonb_extract_path", [
1762
+ lhs,
1763
+ this.eb.val("0")
1764
+ ]), "=", this.transformPrimitive(value, "Json", false))).with("array_ends_with", () => this.eb(this.eb.fn("jsonb_extract_path", [
1765
+ lhs,
1766
+ import_kysely3.sql`(jsonb_array_length(${lhs}) - 1)::text`
1767
+ ]), "=", this.transformPrimitive(value, "Json", false))).exhaustive();
1768
+ }
1769
+ buildJsonArrayExistsPredicate(receiver, buildFilter) {
1770
+ return this.eb.exists(this.eb.selectFrom(this.eb.fn("jsonb_array_elements", [
1771
+ receiver
1772
+ ]).as("$items")).select(this.eb.lit(1).as("$t")).where(buildFilter(this.eb.ref("$items.value"))));
1773
+ }
1515
1774
  get supportInsertWithDefault() {
1516
1775
  return true;
1517
1776
  }
@@ -1554,14 +1813,20 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
1554
1813
  if (value === void 0) {
1555
1814
  return value;
1556
1815
  }
1816
+ if (value instanceof JsonNullClass) {
1817
+ return "null";
1818
+ } else if (value instanceof DbNullClass) {
1819
+ return null;
1820
+ } else if (value instanceof AnyNullClass) {
1821
+ (0, import_common_helpers4.invariant)(false, "should not reach here: AnyNull is not a valid input value");
1822
+ }
1823
+ if (type === "Json" || this.schema.typeDefs && type in this.schema.typeDefs) {
1824
+ return JSON.stringify(value);
1825
+ }
1557
1826
  if (Array.isArray(value)) {
1558
1827
  return value.map((v) => this.transformPrimitive(v, type, false));
1559
1828
  } else {
1560
- if (this.schema.typeDefs && type in this.schema.typeDefs) {
1561
- return JSON.stringify(value);
1562
- } else {
1563
- return (0, import_ts_pattern4.match)(type).with("Boolean", () => value ? 1 : 0).with("DateTime", () => value instanceof Date ? value.toISOString() : typeof value === "string" ? new Date(value).toISOString() : value).with("Decimal", () => value.toString()).with("Bytes", () => Buffer.from(value)).with("Json", () => JSON.stringify(value)).otherwise(() => value);
1564
- }
1829
+ return (0, import_ts_pattern4.match)(type).with("Boolean", () => value ? 1 : 0).with("DateTime", () => value instanceof Date ? value.toISOString() : typeof value === "string" ? new Date(value).toISOString() : value).with("Decimal", () => value.toString()).with("Bytes", () => Buffer.from(value)).otherwise(() => value);
1565
1830
  }
1566
1831
  }
1567
1832
  transformOutput(value, type) {
@@ -1731,6 +1996,30 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
1731
1996
  value2
1732
1997
  ]));
1733
1998
  }
1999
+ buildJsonPathSelection(receiver, path) {
2000
+ if (!path) {
2001
+ return receiver;
2002
+ } else {
2003
+ return import_kysely4.sql`${receiver} -> ${this.eb.val(path)}`;
2004
+ }
2005
+ }
2006
+ buildJsonArrayFilter(lhs, operation, value) {
2007
+ return (0, import_ts_pattern4.match)(operation).with("array_contains", () => {
2008
+ if (Array.isArray(value)) {
2009
+ throw createNotSupportedError('SQLite "array_contains" only supports checking for a single value, not an array of values');
2010
+ } else {
2011
+ return import_kysely4.sql`EXISTS (SELECT 1 FROM json_each(${lhs}) WHERE value = ${value})`;
2012
+ }
2013
+ }).with("array_starts_with", () => this.eb(this.eb.fn("json_extract", [
2014
+ lhs,
2015
+ this.eb.val("$[0]")
2016
+ ]), "=", value)).with("array_ends_with", () => this.eb(import_kysely4.sql`json_extract(${lhs}, '$[' || (json_array_length(${lhs}) - 1) || ']')`, "=", value)).exhaustive();
2017
+ }
2018
+ buildJsonArrayExistsPredicate(receiver, buildFilter) {
2019
+ return this.eb.exists(this.eb.selectFrom(this.eb.fn("json_each", [
2020
+ receiver
2021
+ ]).as("$items")).select(this.eb.lit(1).as("$t")).where(buildFilter(this.eb.ref("$items.value"))));
2022
+ }
1734
2023
  get supportsUpdateWithLimit() {
1735
2024
  return false;
1736
2025
  }
@@ -2349,8 +2638,16 @@ var BaseOperationHandler = class {
2349
2638
  autoUpdatedFields.push(fieldName);
2350
2639
  }
2351
2640
  }
2641
+ const thisEntity = await this.getEntityIds(kysely, model, combinedWhere);
2642
+ if (!thisEntity) {
2643
+ if (throwIfNotFound) {
2644
+ throw createNotFoundError(model);
2645
+ } else {
2646
+ return null;
2647
+ }
2648
+ }
2352
2649
  if (Object.keys(finalData).length === 0) {
2353
- return combinedWhere;
2650
+ return thisEntity;
2354
2651
  }
2355
2652
  let needIdRead = false;
2356
2653
  if (modelDef.baseModel && !this.isIdFilter(model, combinedWhere)) {
@@ -2370,9 +2667,15 @@ var BaseOperationHandler = class {
2370
2667
  const baseUpdateResult = await this.processBaseModelUpdate(kysely, modelDef.baseModel, combinedWhere, finalData, throwIfNotFound);
2371
2668
  finalData = baseUpdateResult.remainingFields;
2372
2669
  combinedWhere = baseUpdateResult.baseEntity;
2670
+ if (baseUpdateResult.baseEntity) {
2671
+ for (const [key, value] of Object.entries(baseUpdateResult.baseEntity)) {
2672
+ if (key in thisEntity) {
2673
+ thisEntity[key] = value;
2674
+ }
2675
+ }
2676
+ }
2373
2677
  }
2374
2678
  const updateFields = {};
2375
- let thisEntity = void 0;
2376
2679
  for (const field in finalData) {
2377
2680
  const fieldDef = this.requireField(model, field);
2378
2681
  if (isScalarField(this.schema, model, field) || isForeignKeyField(this.schema, model, field)) {
@@ -2381,17 +2684,7 @@ var BaseOperationHandler = class {
2381
2684
  if (!allowRelationUpdate) {
2382
2685
  throw createNotSupportedError(`Relation update not allowed for field "${field}"`);
2383
2686
  }
2384
- if (!thisEntity) {
2385
- thisEntity = await this.getEntityIds(kysely, model, combinedWhere);
2386
- if (!thisEntity) {
2387
- if (throwIfNotFound) {
2388
- throw createNotFoundError(model);
2389
- } else {
2390
- return null;
2391
- }
2392
- }
2393
- }
2394
- const parentUpdates = await this.processRelationUpdates(kysely, model, field, fieldDef, thisEntity, finalData[field], throwIfNotFound);
2687
+ const parentUpdates = await this.processRelationUpdates(kysely, model, field, fieldDef, thisEntity, finalData[field]);
2395
2688
  if (Object.keys(parentUpdates).length > 0) {
2396
2689
  Object.assign(updateFields, parentUpdates);
2397
2690
  }
@@ -2402,7 +2695,7 @@ var BaseOperationHandler = class {
2402
2695
  hasFieldUpdate = Object.keys(updateFields).some((f) => !autoUpdatedFields.includes(f));
2403
2696
  }
2404
2697
  if (!hasFieldUpdate) {
2405
- return combinedWhere;
2698
+ return thisEntity;
2406
2699
  } else {
2407
2700
  fieldsToReturn = fieldsToReturn ?? requireIdFields(this.schema, model);
2408
2701
  const query = kysely.updateTable(model).where(() => this.dialect.buildFilter(model, model, combinedWhere)).set(updateFields).returning(fieldsToReturn).modifyEnd(this.makeContextComment({
@@ -2581,7 +2874,7 @@ var BaseOperationHandler = class {
2581
2874
  const idFields = requireIdFields(this.schema, model);
2582
2875
  return idFields.map((f) => kysely.dynamic.ref(`${model}.${f}`));
2583
2876
  }
2584
- async processRelationUpdates(kysely, model, field, fieldDef, parentIds, args, throwIfNotFound) {
2877
+ async processRelationUpdates(kysely, model, field, fieldDef, parentIds, args) {
2585
2878
  const fieldModel = fieldDef.type;
2586
2879
  const fromRelationContext = {
2587
2880
  model,
@@ -2632,6 +2925,7 @@ var BaseOperationHandler = class {
2632
2925
  where = void 0;
2633
2926
  data = item;
2634
2927
  }
2928
+ const throwIfNotFound = !fieldDef.array || !!where;
2635
2929
  await this.update(kysely, fieldModel, where, data, fromRelationContext, true, throwIfNotFound);
2636
2930
  }
2637
2931
  break;
@@ -4136,7 +4430,7 @@ var InputValidator = class {
4136
4430
  }
4137
4431
  return result;
4138
4432
  }
4139
- makePrimitiveSchema(type, attributes) {
4433
+ makeScalarSchema(type, attributes) {
4140
4434
  if (this.schema.typeDefs && type in this.schema.typeDefs) {
4141
4435
  return this.makeTypeDefSchema(type);
4142
4436
  } else if (this.schema.enums && type in this.schema.enums) {
@@ -4153,8 +4447,8 @@ var InputValidator = class {
4153
4447
  ]);
4154
4448
  }).with("DateTime", () => import_zod3.z.union([
4155
4449
  import_zod3.z.date(),
4156
- import_zod3.z.string().datetime()
4157
- ])).with("Bytes", () => import_zod3.z.instanceof(Uint8Array)).otherwise(() => import_zod3.z.unknown());
4450
+ import_zod3.z.iso.datetime()
4451
+ ])).with("Bytes", () => import_zod3.z.instanceof(Uint8Array)).with("Json", () => this.makeJsonValueSchema(false, false)).otherwise(() => import_zod3.z.unknown());
4158
4452
  }
4159
4453
  }
4160
4454
  makeEnumSchema(type) {
@@ -4185,20 +4479,23 @@ var InputValidator = class {
4185
4479
  const typeDef = getTypeDef(this.schema, type);
4186
4480
  (0, import_common_helpers7.invariant)(typeDef, `Type definition "${type}" not found in schema`);
4187
4481
  schema = import_zod3.z.looseObject(Object.fromEntries(Object.entries(typeDef.fields).map(([field, def]) => {
4188
- let fieldSchema = this.makePrimitiveSchema(def.type);
4482
+ let fieldSchema = this.makeScalarSchema(def.type);
4189
4483
  if (def.array) {
4190
4484
  fieldSchema = fieldSchema.array();
4191
4485
  }
4192
4486
  if (def.optional) {
4193
- fieldSchema = fieldSchema.optional();
4487
+ fieldSchema = fieldSchema.nullish();
4194
4488
  }
4195
4489
  return [
4196
4490
  field,
4197
4491
  fieldSchema
4198
4492
  ];
4199
4493
  })));
4200
- this.setSchemaCache(key, schema);
4201
- return schema;
4494
+ const finalSchema = import_zod3.z.custom((v) => {
4495
+ return schema.safeParse(v).success;
4496
+ });
4497
+ this.setSchemaCache(key, finalSchema);
4498
+ return finalSchema;
4202
4499
  }
4203
4500
  makeWhereSchema(model, unique, withoutRelationFields = false, withAggregations = false) {
4204
4501
  const modelDef = requireModel(this.schema, model);
@@ -4238,6 +4535,8 @@ var InputValidator = class {
4238
4535
  }
4239
4536
  } else if (fieldDef.array) {
4240
4537
  fieldSchema = this.makeArrayFilterSchema(fieldDef.type);
4538
+ } else if (this.isTypeDefType(fieldDef.type)) {
4539
+ fieldSchema = this.makeTypedJsonFilterSchema(fieldDef.type, !!fieldDef.optional, !!fieldDef.array);
4241
4540
  } else {
4242
4541
  fieldSchema = this.makePrimitiveFilterSchema(fieldDef.type, !!fieldDef.optional, withAggregations);
4243
4542
  }
@@ -4294,6 +4593,52 @@ var InputValidator = class {
4294
4593
  }
4295
4594
  return result;
4296
4595
  }
4596
+ makeTypedJsonFilterSchema(type, optional, array) {
4597
+ const typeDef = getTypeDef(this.schema, type);
4598
+ (0, import_common_helpers7.invariant)(typeDef, `Type definition "${type}" not found in schema`);
4599
+ const candidates = [];
4600
+ if (!array) {
4601
+ const fieldSchemas = {};
4602
+ for (const [fieldName, fieldDef] of Object.entries(typeDef.fields)) {
4603
+ if (this.isTypeDefType(fieldDef.type)) {
4604
+ fieldSchemas[fieldName] = this.makeTypedJsonFilterSchema(fieldDef.type, !!fieldDef.optional, !!fieldDef.array).optional();
4605
+ } else {
4606
+ if (fieldDef.array) {
4607
+ fieldSchemas[fieldName] = this.makeArrayFilterSchema(fieldDef.type).optional();
4608
+ } else {
4609
+ const enumDef = getEnum(this.schema, fieldDef.type);
4610
+ if (enumDef) {
4611
+ fieldSchemas[fieldName] = this.makeEnumFilterSchema(enumDef, !!fieldDef.optional, false).optional();
4612
+ } else {
4613
+ fieldSchemas[fieldName] = this.makePrimitiveFilterSchema(fieldDef.type, !!fieldDef.optional, false).optional();
4614
+ }
4615
+ }
4616
+ }
4617
+ }
4618
+ candidates.push(import_zod3.z.strictObject(fieldSchemas));
4619
+ }
4620
+ const recursiveSchema = import_zod3.z.lazy(() => this.makeTypedJsonFilterSchema(type, optional, false)).optional();
4621
+ if (array) {
4622
+ candidates.push(import_zod3.z.strictObject({
4623
+ some: recursiveSchema,
4624
+ every: recursiveSchema,
4625
+ none: recursiveSchema
4626
+ }));
4627
+ } else {
4628
+ candidates.push(import_zod3.z.strictObject({
4629
+ is: recursiveSchema,
4630
+ isNot: recursiveSchema
4631
+ }));
4632
+ }
4633
+ candidates.push(this.makeJsonFilterSchema(optional));
4634
+ if (optional) {
4635
+ candidates.push(import_zod3.z.null());
4636
+ }
4637
+ return import_zod3.z.union(candidates);
4638
+ }
4639
+ isTypeDefType(type) {
4640
+ return this.schema.typeDefs && type in this.schema.typeDefs;
4641
+ }
4297
4642
  makeEnumFilterSchema(enumDef, optional, withAggregations) {
4298
4643
  const baseSchema = import_zod3.z.enum(Object.keys(enumDef.values));
4299
4644
  const components = this.makeCommonPrimitiveFilterComponents(baseSchema, optional, () => import_zod3.z.lazy(() => this.makeEnumFilterSchema(enumDef, optional, withAggregations)), [
@@ -4313,25 +4658,64 @@ var InputValidator = class {
4313
4658
  }
4314
4659
  makeArrayFilterSchema(type) {
4315
4660
  return import_zod3.z.strictObject({
4316
- equals: this.makePrimitiveSchema(type).array().optional(),
4317
- has: this.makePrimitiveSchema(type).optional(),
4318
- hasEvery: this.makePrimitiveSchema(type).array().optional(),
4319
- hasSome: this.makePrimitiveSchema(type).array().optional(),
4661
+ equals: this.makeScalarSchema(type).array().optional(),
4662
+ has: this.makeScalarSchema(type).optional(),
4663
+ hasEvery: this.makeScalarSchema(type).array().optional(),
4664
+ hasSome: this.makeScalarSchema(type).array().optional(),
4320
4665
  isEmpty: import_zod3.z.boolean().optional()
4321
4666
  });
4322
4667
  }
4323
4668
  makePrimitiveFilterSchema(type, optional, withAggregations) {
4324
- if (this.schema.typeDefs && type in this.schema.typeDefs) {
4325
- return this.makeTypeDefFilterSchema(type, optional);
4326
- }
4327
- return (0, import_ts_pattern13.match)(type).with("String", () => this.makeStringFilterSchema(optional, withAggregations)).with(import_ts_pattern13.P.union("Int", "Float", "Decimal", "BigInt"), (type2) => this.makeNumberFilterSchema(this.makePrimitiveSchema(type2), optional, withAggregations)).with("Boolean", () => this.makeBooleanFilterSchema(optional, withAggregations)).with("DateTime", () => this.makeDateTimeFilterSchema(optional, withAggregations)).with("Bytes", () => this.makeBytesFilterSchema(optional, withAggregations)).with("Json", () => import_zod3.z.any()).with("Unsupported", () => import_zod3.z.never()).exhaustive();
4669
+ return (0, import_ts_pattern13.match)(type).with("String", () => this.makeStringFilterSchema(optional, withAggregations)).with(import_ts_pattern13.P.union("Int", "Float", "Decimal", "BigInt"), (type2) => this.makeNumberFilterSchema(this.makeScalarSchema(type2), optional, withAggregations)).with("Boolean", () => this.makeBooleanFilterSchema(optional, withAggregations)).with("DateTime", () => this.makeDateTimeFilterSchema(optional, withAggregations)).with("Bytes", () => this.makeBytesFilterSchema(optional, withAggregations)).with("Json", () => this.makeJsonFilterSchema(optional)).with("Unsupported", () => import_zod3.z.never()).exhaustive();
4328
4670
  }
4329
- makeTypeDefFilterSchema(_type, _optional) {
4330
- return import_zod3.z.never();
4671
+ makeJsonValueSchema(nullable, forFilter) {
4672
+ const options = [
4673
+ import_zod3.z.string(),
4674
+ import_zod3.z.number(),
4675
+ import_zod3.z.boolean(),
4676
+ import_zod3.z.instanceof(JsonNullClass)
4677
+ ];
4678
+ if (forFilter) {
4679
+ options.push(import_zod3.z.instanceof(DbNullClass));
4680
+ } else {
4681
+ if (nullable) {
4682
+ options.push(import_zod3.z.instanceof(DbNullClass));
4683
+ }
4684
+ }
4685
+ if (forFilter) {
4686
+ options.push(import_zod3.z.instanceof(AnyNullClass));
4687
+ }
4688
+ const schema = import_zod3.z.union([
4689
+ ...options,
4690
+ import_zod3.z.lazy(() => import_zod3.z.union([
4691
+ this.makeJsonValueSchema(false, false),
4692
+ import_zod3.z.null()
4693
+ ]).array()),
4694
+ import_zod3.z.record(import_zod3.z.string(), import_zod3.z.lazy(() => import_zod3.z.union([
4695
+ this.makeJsonValueSchema(false, false),
4696
+ import_zod3.z.null()
4697
+ ])))
4698
+ ]);
4699
+ return this.nullableIf(schema, nullable);
4700
+ }
4701
+ makeJsonFilterSchema(optional) {
4702
+ const valueSchema = this.makeJsonValueSchema(optional, true);
4703
+ return import_zod3.z.strictObject({
4704
+ path: import_zod3.z.string().optional(),
4705
+ equals: valueSchema.optional(),
4706
+ not: valueSchema.optional(),
4707
+ string_contains: import_zod3.z.string().optional(),
4708
+ string_starts_with: import_zod3.z.string().optional(),
4709
+ string_ends_with: import_zod3.z.string().optional(),
4710
+ mode: this.makeStringModeSchema().optional(),
4711
+ array_contains: valueSchema.optional(),
4712
+ array_starts_with: valueSchema.optional(),
4713
+ array_ends_with: valueSchema.optional()
4714
+ });
4331
4715
  }
4332
4716
  makeDateTimeFilterSchema(optional, withAggregations) {
4333
4717
  return this.makeCommonPrimitiveFilterSchema(import_zod3.z.union([
4334
- import_zod3.z.string().datetime(),
4718
+ import_zod3.z.iso.datetime(),
4335
4719
  import_zod3.z.date()
4336
4720
  ]), optional, () => import_zod3.z.lazy(() => this.makeDateTimeFilterSchema(optional, withAggregations)), withAggregations ? [
4337
4721
  "_count",
@@ -4676,7 +5060,7 @@ var InputValidator = class {
4676
5060
  uncheckedVariantFields[field] = fieldSchema;
4677
5061
  }
4678
5062
  } else {
4679
- let fieldSchema = this.makePrimitiveSchema(fieldDef.type, fieldDef.attributes);
5063
+ let fieldSchema = this.makeScalarSchema(fieldDef.type, fieldDef.attributes);
4680
5064
  if (fieldDef.array) {
4681
5065
  fieldSchema = addListValidation(fieldSchema.array(), fieldDef.attributes);
4682
5066
  fieldSchema = import_zod3.z.union([
@@ -4690,7 +5074,14 @@ var InputValidator = class {
4690
5074
  fieldSchema = fieldSchema.optional();
4691
5075
  }
4692
5076
  if (fieldDef.optional) {
4693
- fieldSchema = fieldSchema.nullable();
5077
+ if (fieldDef.type === "Json") {
5078
+ fieldSchema = import_zod3.z.union([
5079
+ fieldSchema,
5080
+ import_zod3.z.instanceof(DbNullClass)
5081
+ ]);
5082
+ } else {
5083
+ fieldSchema = fieldSchema.nullable();
5084
+ }
4694
5085
  }
4695
5086
  uncheckedVariantFields[field] = fieldSchema;
4696
5087
  if (!fieldDef.foreignKeyFor) {
@@ -4739,11 +5130,11 @@ var InputValidator = class {
4739
5130
  fields["delete"] = this.makeDeleteRelationDataSchema(fieldType, array, true).optional();
4740
5131
  }
4741
5132
  fields["update"] = array ? this.orArray(import_zod3.z.strictObject({
4742
- where: this.makeWhereSchema(fieldType, true).optional(),
5133
+ where: this.makeWhereSchema(fieldType, true),
4743
5134
  data: this.makeUpdateDataSchema(fieldType, withoutFields)
4744
5135
  }), true).optional() : import_zod3.z.union([
4745
5136
  import_zod3.z.strictObject({
4746
- where: this.makeWhereSchema(fieldType, true).optional(),
5137
+ where: this.makeWhereSchema(fieldType, false).optional(),
4747
5138
  data: this.makeUpdateDataSchema(fieldType, withoutFields)
4748
5139
  }),
4749
5140
  this.makeUpdateDataSchema(fieldType, withoutFields)
@@ -4879,7 +5270,7 @@ var InputValidator = class {
4879
5270
  uncheckedVariantFields[field] = fieldSchema;
4880
5271
  }
4881
5272
  } else {
4882
- let fieldSchema = this.makePrimitiveSchema(fieldDef.type, fieldDef.attributes);
5273
+ let fieldSchema = this.makeScalarSchema(fieldDef.type, fieldDef.attributes);
4883
5274
  if (this.isNumericField(fieldDef)) {
4884
5275
  fieldSchema = import_zod3.z.union([
4885
5276
  fieldSchema,
@@ -4906,7 +5297,14 @@ var InputValidator = class {
4906
5297
  ]);
4907
5298
  }
4908
5299
  if (fieldDef.optional) {
4909
- fieldSchema = fieldSchema.nullable();
5300
+ if (fieldDef.type === "Json") {
5301
+ fieldSchema = import_zod3.z.union([
5302
+ fieldSchema,
5303
+ import_zod3.z.instanceof(DbNullClass)
5304
+ ]);
5305
+ } else {
5306
+ fieldSchema = fieldSchema.nullable();
5307
+ }
4910
5308
  }
4911
5309
  fieldSchema = fieldSchema.optional();
4912
5310
  uncheckedVariantFields[field] = fieldSchema;
@@ -6190,18 +6588,19 @@ var textMatch = /* @__PURE__ */ __name((eb, args, { dialect }, method) => {
6190
6588
  } else {
6191
6589
  op = "like";
6192
6590
  }
6591
+ const escapedSearch = import_kysely8.sql`REPLACE(REPLACE(REPLACE(CAST(${searchExpr} as text), '\\', '\\\\'), '%', '\\%'), '_', '\\_')`;
6193
6592
  searchExpr = (0, import_ts_pattern15.match)(method).with("contains", () => eb.fn("CONCAT", [
6194
6593
  import_kysely8.sql.lit("%"),
6195
- import_kysely8.sql`CAST(${searchExpr} as text)`,
6594
+ escapedSearch,
6196
6595
  import_kysely8.sql.lit("%")
6197
6596
  ])).with("startsWith", () => eb.fn("CONCAT", [
6198
- import_kysely8.sql`CAST(${searchExpr} as text)`,
6597
+ escapedSearch,
6199
6598
  import_kysely8.sql.lit("%")
6200
6599
  ])).with("endsWith", () => eb.fn("CONCAT", [
6201
6600
  import_kysely8.sql.lit("%"),
6202
- import_kysely8.sql`CAST(${searchExpr} as text)`
6601
+ escapedSearch
6203
6602
  ])).exhaustive();
6204
- return eb(fieldExpr, op, searchExpr);
6603
+ return import_kysely8.sql`${fieldExpr} ${import_kysely8.sql.raw(op)} ${searchExpr} escape '\\'`;
6205
6604
  }, "textMatch");
6206
6605
  var has = /* @__PURE__ */ __name((eb, args) => {
6207
6606
  const [field, search2] = args;
@@ -7432,9 +7831,15 @@ var MatchingExpressionVisitor = class extends ExpressionVisitor {
7432
7831
  };
7433
7832
  // Annotate the CommonJS export names for ESM import in node:
7434
7833
  0 && (module.exports = {
7834
+ AnyNull,
7835
+ AnyNullClass,
7435
7836
  BaseCrudDialect,
7436
7837
  CRUD,
7437
7838
  CRUD_EXT,
7839
+ DbNull,
7840
+ DbNullClass,
7841
+ JsonNull,
7842
+ JsonNullClass,
7438
7843
  KyselyUtils,
7439
7844
  ORMError,
7440
7845
  ORMErrorReason,