@zenstackhq/runtime 3.0.0-alpha.18 → 3.0.0-alpha.19

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.
@@ -151,6 +151,10 @@ var InternalError = class extends Error {
151
151
  };
152
152
 
153
153
  // src/client/query-utils.ts
154
+ function getModel(schema, model) {
155
+ return schema.models[model];
156
+ }
157
+ __name(getModel, "getModel");
154
158
  function requireModel(schema, model) {
155
159
  const matchedName = Object.keys(schema.models).find((k) => k.toLowerCase() === model.toLowerCase());
156
160
  if (!matchedName) {
@@ -159,6 +163,11 @@ function requireModel(schema, model) {
159
163
  return schema.models[matchedName];
160
164
  }
161
165
  __name(requireModel, "requireModel");
166
+ function getField(schema, model, field) {
167
+ const modelDef = getModel(schema, model);
168
+ return modelDef?.fields[field];
169
+ }
170
+ __name(getField, "getField");
162
171
  function requireField(schema, model, field) {
163
172
  const modelDef = requireModel(schema, model);
164
173
  if (!modelDef.fields[field]) {
@@ -213,13 +222,13 @@ function getRelationForeignKeyFieldPairs(schema, model, relationField) {
213
222
  }
214
223
  __name(getRelationForeignKeyFieldPairs, "getRelationForeignKeyFieldPairs");
215
224
  function isRelationField(schema, model, field) {
216
- const fieldDef = requireField(schema, model, field);
217
- return !!fieldDef.relation;
225
+ const fieldDef = getField(schema, model, field);
226
+ return !!fieldDef?.relation;
218
227
  }
219
228
  __name(isRelationField, "isRelationField");
220
229
  function isInheritedField(schema, model, field) {
221
- const fieldDef = requireField(schema, model, field);
222
- return !!fieldDef.originModel;
230
+ const fieldDef = getField(schema, model, field);
231
+ return !!fieldDef?.originModel;
223
232
  }
224
233
  __name(isInheritedField, "isInheritedField");
225
234
  function getUniqueFields(schema, model) {
@@ -589,7 +598,7 @@ var BaseCrudDialect = class {
589
598
  buildLiteralFilter(eb, lhs, type, rhs) {
590
599
  return eb(lhs, "=", rhs !== null && rhs !== void 0 ? this.transformPrimitive(rhs, type, false) : rhs);
591
600
  }
592
- buildStandardFilter(eb, type, payload, lhs, getRhs, recurse, throwIfInvalid = false, onlyForKeys = void 0) {
601
+ buildStandardFilter(eb, type, payload, lhs, getRhs, recurse, throwIfInvalid = false, onlyForKeys = void 0, excludeKeys = []) {
593
602
  if (payload === null || !(0, import_common_helpers.isPlainObject)(payload)) {
594
603
  return {
595
604
  conditions: [
@@ -604,6 +613,9 @@ var BaseCrudDialect = class {
604
613
  if (onlyForKeys && !onlyForKeys.includes(op)) {
605
614
  continue;
606
615
  }
616
+ if (excludeKeys.includes(op)) {
617
+ continue;
618
+ }
607
619
  const rhs = Array.isArray(value) ? value.map(getRhs) : getRhs(value);
608
620
  const condition = (0, import_ts_pattern.match)(op).with("equals", () => rhs === null ? eb(lhs, "is", null) : eb(lhs, "=", rhs)).with("in", () => {
609
621
  (0, import_common_helpers.invariant)(Array.isArray(rhs), "right hand side must be an array");
@@ -637,21 +649,20 @@ var BaseCrudDialect = class {
637
649
  };
638
650
  }
639
651
  buildStringFilter(eb, fieldRef, payload) {
640
- let insensitive = false;
641
- if (payload && typeof payload === "object" && "mode" in payload && payload.mode === "insensitive") {
642
- insensitive = true;
643
- fieldRef = eb.fn("lower", [
644
- fieldRef
645
- ]);
652
+ let mode;
653
+ if (payload && typeof payload === "object" && "mode" in payload) {
654
+ mode = payload.mode;
646
655
  }
647
- const { conditions, consumedKeys } = this.buildStandardFilter(eb, "String", payload, fieldRef, (value) => this.prepStringCasing(eb, value, insensitive), (value) => this.buildStringFilter(eb, fieldRef, value));
656
+ const { conditions, consumedKeys } = this.buildStandardFilter(eb, "String", payload, mode === "insensitive" ? eb.fn("lower", [
657
+ fieldRef
658
+ ]) : fieldRef, (value) => this.prepStringCasing(eb, value, mode), (value) => this.buildStringFilter(eb, fieldRef, value));
648
659
  if (payload && typeof payload === "object") {
649
660
  for (const [key, value] of Object.entries(payload)) {
650
661
  if (key === "mode" || consumedKeys.includes(key)) {
651
662
  continue;
652
663
  }
653
- const condition = (0, import_ts_pattern.match)(key).with("contains", () => insensitive ? eb(fieldRef, "ilike", import_kysely.sql.lit(`%${value}%`)) : eb(fieldRef, "like", import_kysely.sql.lit(`%${value}%`))).with("startsWith", () => insensitive ? eb(fieldRef, "ilike", import_kysely.sql.lit(`${value}%`)) : eb(fieldRef, "like", import_kysely.sql.lit(`${value}%`))).with("endsWith", () => insensitive ? eb(fieldRef, "ilike", import_kysely.sql.lit(`%${value}`)) : eb(fieldRef, "like", import_kysely.sql.lit(`%${value}`))).otherwise(() => {
654
- throw new Error(`Invalid string filter key: ${key}`);
664
+ const condition = (0, import_ts_pattern.match)(key).with("contains", () => mode === "insensitive" ? eb(fieldRef, "ilike", import_kysely.sql.val(`%${value}%`)) : eb(fieldRef, "like", import_kysely.sql.val(`%${value}%`))).with("startsWith", () => mode === "insensitive" ? eb(fieldRef, "ilike", import_kysely.sql.val(`${value}%`)) : eb(fieldRef, "like", import_kysely.sql.val(`${value}%`))).with("endsWith", () => mode === "insensitive" ? eb(fieldRef, "ilike", import_kysely.sql.val(`%${value}`)) : eb(fieldRef, "like", import_kysely.sql.val(`%${value}`))).otherwise(() => {
665
+ throw new QueryError(`Invalid string filter key: ${key}`);
655
666
  });
656
667
  if (condition) {
657
668
  conditions.push(condition);
@@ -660,15 +671,18 @@ var BaseCrudDialect = class {
660
671
  }
661
672
  return this.and(eb, ...conditions);
662
673
  }
663
- prepStringCasing(eb, value, toLower = true) {
674
+ prepStringCasing(eb, value, mode) {
675
+ if (!mode || mode === "default") {
676
+ return value === null ? value : import_kysely.sql.val(value);
677
+ }
664
678
  if (typeof value === "string") {
665
- return toLower ? eb.fn("lower", [
666
- import_kysely.sql.lit(value)
667
- ]) : import_kysely.sql.lit(value);
679
+ return eb.fn("lower", [
680
+ import_kysely.sql.val(value)
681
+ ]);
668
682
  } else if (Array.isArray(value)) {
669
- return value.map((v) => this.prepStringCasing(eb, v, toLower));
683
+ return value.map((v) => this.prepStringCasing(eb, v, mode));
670
684
  } else {
671
- return value === null ? null : import_kysely.sql.lit(value);
685
+ return value === null ? null : import_kysely.sql.val(value);
672
686
  }
673
687
  }
674
688
  buildNumberFilter(eb, fieldRef, type, payload) {
@@ -830,6 +844,32 @@ var BaseCrudDialect = class {
830
844
  });
831
845
  return query;
832
846
  }
847
+ buildCountJson(model, eb, parentAlias, payload) {
848
+ const modelDef = requireModel(this.schema, model);
849
+ const toManyRelations = Object.entries(modelDef.fields).filter(([, field]) => field.relation && field.array);
850
+ const selections = payload === true ? {
851
+ select: toManyRelations.reduce((acc, [field]) => {
852
+ acc[field] = true;
853
+ return acc;
854
+ }, {})
855
+ } : payload;
856
+ const jsonObject = {};
857
+ for (const [field, value] of Object.entries(selections.select)) {
858
+ const fieldDef = requireField(this.schema, model, field);
859
+ const fieldModel = fieldDef.type;
860
+ const joinPairs = buildJoinPairs(this.schema, model, parentAlias, field, fieldModel);
861
+ let fieldCountQuery = eb.selectFrom(fieldModel).select(eb.fn.countAll().as(`_count$${field}`));
862
+ for (const [left, right] of joinPairs) {
863
+ fieldCountQuery = fieldCountQuery.whereRef(left, "=", right);
864
+ }
865
+ if (value && typeof value === "object" && "where" in value && value.where && typeof value.where === "object") {
866
+ const filter = this.buildFilter(eb, fieldModel, fieldModel, value.where);
867
+ fieldCountQuery = fieldCountQuery.where(filter);
868
+ }
869
+ jsonObject[field] = fieldCountQuery;
870
+ }
871
+ return this.buildJsonObject(eb, jsonObject);
872
+ }
833
873
  // #endregion
834
874
  // #region utils
835
875
  negateSort(sort, negated) {
@@ -958,7 +998,7 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
958
998
  });
959
999
  return qb;
960
1000
  }
961
- buildRelationObjectArgs(relationModel, relationField, eb, payload, parentName) {
1001
+ buildRelationObjectArgs(relationModel, relationField, eb, payload, parentAlias) {
962
1002
  const relationModelDef = requireModel(this.schema, relationModel);
963
1003
  const objArgs = [];
964
1004
  const descendantModels = getDelegateDescendantModels(this.schema, relationModel);
@@ -974,20 +1014,28 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
974
1014
  buildFieldRef(this.schema, relationModel, field, this.options, eb)
975
1015
  ]).flatMap((v) => v));
976
1016
  } else if (payload.select) {
977
- objArgs.push(...Object.entries(payload.select).filter(([, value]) => value).map(([field]) => {
978
- const fieldDef = requireField(this.schema, relationModel, field);
979
- const fieldValue = fieldDef.relation ? eb.ref(`${parentName}$${relationField}$${field}.$j`) : buildFieldRef(this.schema, relationModel, field, this.options, eb);
980
- return [
981
- import_kysely2.sql.lit(field),
982
- fieldValue
983
- ];
1017
+ objArgs.push(...Object.entries(payload.select).filter(([, value]) => value).map(([field, value]) => {
1018
+ if (field === "_count") {
1019
+ const subJson = this.buildCountJson(relationModel, eb, `${parentAlias}$${relationField}`, value);
1020
+ return [
1021
+ import_kysely2.sql.lit(field),
1022
+ subJson
1023
+ ];
1024
+ } else {
1025
+ const fieldDef = requireField(this.schema, relationModel, field);
1026
+ const fieldValue = fieldDef.relation ? eb.ref(`${parentAlias}$${relationField}$${field}.$j`) : buildFieldRef(this.schema, relationModel, field, this.options, eb);
1027
+ return [
1028
+ import_kysely2.sql.lit(field),
1029
+ fieldValue
1030
+ ];
1031
+ }
984
1032
  }).flatMap((v) => v));
985
1033
  }
986
1034
  if (typeof payload === "object" && payload.include && typeof payload.include === "object") {
987
1035
  objArgs.push(...Object.entries(payload.include).filter(([, value]) => value).map(([field]) => [
988
1036
  import_kysely2.sql.lit(field),
989
1037
  // reference the synthesized JSON field
990
- eb.ref(`${parentName}$${relationField}$${field}.$j`)
1038
+ eb.ref(`${parentAlias}$${relationField}$${field}.$j`)
991
1039
  ]).flatMap((v) => v));
992
1040
  }
993
1041
  return objArgs;
@@ -1073,11 +1121,11 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
1073
1121
  buildRelationSelection(query, model, relationField, parentAlias, payload) {
1074
1122
  return query.select((eb) => this.buildRelationJSON(model, eb, relationField, parentAlias, payload).as(relationField));
1075
1123
  }
1076
- buildRelationJSON(model, eb, relationField, parentName, payload) {
1124
+ buildRelationJSON(model, eb, relationField, parentAlias, payload) {
1077
1125
  const relationFieldDef = requireField(this.schema, model, relationField);
1078
1126
  const relationModel = relationFieldDef.type;
1079
1127
  const relationModelDef = requireModel(this.schema, relationModel);
1080
- const subQueryName = `${parentName}$${relationField}`;
1128
+ const subQueryName = `${parentAlias}$${relationField}`;
1081
1129
  let tbl = eb.selectFrom(() => {
1082
1130
  let subQuery = this.buildSelectModel(eb, relationModel);
1083
1131
  subQuery = this.buildSelectAllFields(relationModel, subQuery, typeof payload === "object" ? payload?.omit : void 0);
@@ -1101,14 +1149,14 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
1101
1149
  const relationIds = getIdFields(this.schema, relationModel);
1102
1150
  (0, import_common_helpers3.invariant)(parentIds.length === 1, "many-to-many relation must have exactly one id field");
1103
1151
  (0, import_common_helpers3.invariant)(relationIds.length === 1, "many-to-many relation must have exactly one id field");
1104
- subQuery = subQuery.where(eb(eb.ref(`${relationModel}.${relationIds[0]}`), "in", eb.selectFrom(m2m.joinTable).select(`${m2m.joinTable}.${m2m.otherFkName}`).whereRef(`${parentName}.${parentIds[0]}`, "=", `${m2m.joinTable}.${m2m.parentFkName}`)));
1152
+ subQuery = subQuery.where(eb(eb.ref(`${relationModel}.${relationIds[0]}`), "in", eb.selectFrom(m2m.joinTable).select(`${m2m.joinTable}.${m2m.otherFkName}`).whereRef(`${parentAlias}.${parentIds[0]}`, "=", `${m2m.joinTable}.${m2m.parentFkName}`)));
1105
1153
  } else {
1106
1154
  const { keyPairs, ownedByModel } = getRelationForeignKeyFieldPairs(this.schema, model, relationField);
1107
1155
  keyPairs.forEach(({ fk, pk }) => {
1108
1156
  if (ownedByModel) {
1109
- subQuery = subQuery.whereRef(`${relationModel}.${pk}`, "=", `${parentName}.${fk}`);
1157
+ subQuery = subQuery.whereRef(`${relationModel}.${pk}`, "=", `${parentAlias}.${fk}`);
1110
1158
  } else {
1111
- subQuery = subQuery.whereRef(`${relationModel}.${fk}`, "=", `${parentName}.${pk}`);
1159
+ subQuery = subQuery.whereRef(`${relationModel}.${fk}`, "=", `${parentAlias}.${pk}`);
1112
1160
  }
1113
1161
  });
1114
1162
  }
@@ -1130,24 +1178,32 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
1130
1178
  ]).flatMap((v) => v));
1131
1179
  } else if (payload.select) {
1132
1180
  objArgs.push(...Object.entries(payload.select).filter(([, value]) => value).map(([field, value]) => {
1133
- const fieldDef = requireField(this.schema, relationModel, field);
1134
- if (fieldDef.relation) {
1135
- const subJson = this.buildRelationJSON(relationModel, eb, field, `${parentName}$${relationField}`, value);
1181
+ if (field === "_count") {
1182
+ const subJson = this.buildCountJson(relationModel, eb, `${parentAlias}$${relationField}`, value);
1136
1183
  return [
1137
1184
  import_kysely3.sql.lit(field),
1138
1185
  subJson
1139
1186
  ];
1140
1187
  } else {
1141
- return [
1142
- import_kysely3.sql.lit(field),
1143
- buildFieldRef(this.schema, relationModel, field, this.options, eb)
1144
- ];
1188
+ const fieldDef = requireField(this.schema, relationModel, field);
1189
+ if (fieldDef.relation) {
1190
+ const subJson = this.buildRelationJSON(relationModel, eb, field, `${parentAlias}$${relationField}`, value);
1191
+ return [
1192
+ import_kysely3.sql.lit(field),
1193
+ subJson
1194
+ ];
1195
+ } else {
1196
+ return [
1197
+ import_kysely3.sql.lit(field),
1198
+ buildFieldRef(this.schema, relationModel, field, this.options, eb)
1199
+ ];
1200
+ }
1145
1201
  }
1146
1202
  }).flatMap((v) => v));
1147
1203
  }
1148
1204
  if (typeof payload === "object" && payload.include && typeof payload.include === "object") {
1149
1205
  objArgs.push(...Object.entries(payload.include).filter(([, value]) => value).map(([field, value]) => {
1150
- const subJson = this.buildRelationJSON(relationModel, eb, field, `${parentName}$${relationField}`, value);
1206
+ const subJson = this.buildRelationJSON(relationModel, eb, field, `${parentAlias}$${relationField}`, value);
1151
1207
  return [
1152
1208
  import_kysely3.sql.lit(field),
1153
1209
  subJson