@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.
@@ -1,5 +1,5 @@
1
1
  import * as kysely from 'kysely';
2
- import { R as RuntimePlugin, V as OnKyselyQueryArgs } from '../../contract-Cn4sSxg8.cjs';
2
+ import { R as RuntimePlugin, V as OnKyselyQueryArgs } from '../../contract-CxX20JtH.cjs';
3
3
  import { SchemaDef } from '@zenstackhq/sdk/schema';
4
4
  import 'decimal.js';
5
5
 
@@ -1,5 +1,5 @@
1
1
  import * as kysely from 'kysely';
2
- import { R as RuntimePlugin, V as OnKyselyQueryArgs } from '../../contract-Cn4sSxg8.js';
2
+ import { R as RuntimePlugin, V as OnKyselyQueryArgs } from '../../contract-CxX20JtH.js';
3
3
  import { SchemaDef } from '@zenstackhq/sdk/schema';
4
4
  import 'decimal.js';
5
5
 
@@ -126,6 +126,10 @@ var InternalError = class extends Error {
126
126
  };
127
127
 
128
128
  // src/client/query-utils.ts
129
+ function getModel(schema, model) {
130
+ return schema.models[model];
131
+ }
132
+ __name(getModel, "getModel");
129
133
  function requireModel(schema, model) {
130
134
  const matchedName = Object.keys(schema.models).find((k) => k.toLowerCase() === model.toLowerCase());
131
135
  if (!matchedName) {
@@ -134,6 +138,11 @@ function requireModel(schema, model) {
134
138
  return schema.models[matchedName];
135
139
  }
136
140
  __name(requireModel, "requireModel");
141
+ function getField(schema, model, field) {
142
+ const modelDef = getModel(schema, model);
143
+ return modelDef?.fields[field];
144
+ }
145
+ __name(getField, "getField");
137
146
  function requireField(schema, model, field) {
138
147
  const modelDef = requireModel(schema, model);
139
148
  if (!modelDef.fields[field]) {
@@ -188,13 +197,13 @@ function getRelationForeignKeyFieldPairs(schema, model, relationField) {
188
197
  }
189
198
  __name(getRelationForeignKeyFieldPairs, "getRelationForeignKeyFieldPairs");
190
199
  function isRelationField(schema, model, field) {
191
- const fieldDef = requireField(schema, model, field);
192
- return !!fieldDef.relation;
200
+ const fieldDef = getField(schema, model, field);
201
+ return !!fieldDef?.relation;
193
202
  }
194
203
  __name(isRelationField, "isRelationField");
195
204
  function isInheritedField(schema, model, field) {
196
- const fieldDef = requireField(schema, model, field);
197
- return !!fieldDef.originModel;
205
+ const fieldDef = getField(schema, model, field);
206
+ return !!fieldDef?.originModel;
198
207
  }
199
208
  __name(isInheritedField, "isInheritedField");
200
209
  function getUniqueFields(schema, model) {
@@ -564,7 +573,7 @@ var BaseCrudDialect = class {
564
573
  buildLiteralFilter(eb, lhs, type, rhs) {
565
574
  return eb(lhs, "=", rhs !== null && rhs !== void 0 ? this.transformPrimitive(rhs, type, false) : rhs);
566
575
  }
567
- buildStandardFilter(eb, type, payload, lhs, getRhs, recurse, throwIfInvalid = false, onlyForKeys = void 0) {
576
+ buildStandardFilter(eb, type, payload, lhs, getRhs, recurse, throwIfInvalid = false, onlyForKeys = void 0, excludeKeys = []) {
568
577
  if (payload === null || !isPlainObject(payload)) {
569
578
  return {
570
579
  conditions: [
@@ -579,6 +588,9 @@ var BaseCrudDialect = class {
579
588
  if (onlyForKeys && !onlyForKeys.includes(op)) {
580
589
  continue;
581
590
  }
591
+ if (excludeKeys.includes(op)) {
592
+ continue;
593
+ }
582
594
  const rhs = Array.isArray(value) ? value.map(getRhs) : getRhs(value);
583
595
  const condition = match(op).with("equals", () => rhs === null ? eb(lhs, "is", null) : eb(lhs, "=", rhs)).with("in", () => {
584
596
  invariant(Array.isArray(rhs), "right hand side must be an array");
@@ -612,21 +624,20 @@ var BaseCrudDialect = class {
612
624
  };
613
625
  }
614
626
  buildStringFilter(eb, fieldRef, payload) {
615
- let insensitive = false;
616
- if (payload && typeof payload === "object" && "mode" in payload && payload.mode === "insensitive") {
617
- insensitive = true;
618
- fieldRef = eb.fn("lower", [
619
- fieldRef
620
- ]);
627
+ let mode;
628
+ if (payload && typeof payload === "object" && "mode" in payload) {
629
+ mode = payload.mode;
621
630
  }
622
- const { conditions, consumedKeys } = this.buildStandardFilter(eb, "String", payload, fieldRef, (value) => this.prepStringCasing(eb, value, insensitive), (value) => this.buildStringFilter(eb, fieldRef, value));
631
+ const { conditions, consumedKeys } = this.buildStandardFilter(eb, "String", payload, mode === "insensitive" ? eb.fn("lower", [
632
+ fieldRef
633
+ ]) : fieldRef, (value) => this.prepStringCasing(eb, value, mode), (value) => this.buildStringFilter(eb, fieldRef, value));
623
634
  if (payload && typeof payload === "object") {
624
635
  for (const [key, value] of Object.entries(payload)) {
625
636
  if (key === "mode" || consumedKeys.includes(key)) {
626
637
  continue;
627
638
  }
628
- const condition = match(key).with("contains", () => insensitive ? eb(fieldRef, "ilike", sql.lit(`%${value}%`)) : eb(fieldRef, "like", sql.lit(`%${value}%`))).with("startsWith", () => insensitive ? eb(fieldRef, "ilike", sql.lit(`${value}%`)) : eb(fieldRef, "like", sql.lit(`${value}%`))).with("endsWith", () => insensitive ? eb(fieldRef, "ilike", sql.lit(`%${value}`)) : eb(fieldRef, "like", sql.lit(`%${value}`))).otherwise(() => {
629
- throw new Error(`Invalid string filter key: ${key}`);
639
+ const condition = match(key).with("contains", () => mode === "insensitive" ? eb(fieldRef, "ilike", sql.val(`%${value}%`)) : eb(fieldRef, "like", sql.val(`%${value}%`))).with("startsWith", () => mode === "insensitive" ? eb(fieldRef, "ilike", sql.val(`${value}%`)) : eb(fieldRef, "like", sql.val(`${value}%`))).with("endsWith", () => mode === "insensitive" ? eb(fieldRef, "ilike", sql.val(`%${value}`)) : eb(fieldRef, "like", sql.val(`%${value}`))).otherwise(() => {
640
+ throw new QueryError(`Invalid string filter key: ${key}`);
630
641
  });
631
642
  if (condition) {
632
643
  conditions.push(condition);
@@ -635,15 +646,18 @@ var BaseCrudDialect = class {
635
646
  }
636
647
  return this.and(eb, ...conditions);
637
648
  }
638
- prepStringCasing(eb, value, toLower = true) {
649
+ prepStringCasing(eb, value, mode) {
650
+ if (!mode || mode === "default") {
651
+ return value === null ? value : sql.val(value);
652
+ }
639
653
  if (typeof value === "string") {
640
- return toLower ? eb.fn("lower", [
641
- sql.lit(value)
642
- ]) : sql.lit(value);
654
+ return eb.fn("lower", [
655
+ sql.val(value)
656
+ ]);
643
657
  } else if (Array.isArray(value)) {
644
- return value.map((v) => this.prepStringCasing(eb, v, toLower));
658
+ return value.map((v) => this.prepStringCasing(eb, v, mode));
645
659
  } else {
646
- return value === null ? null : sql.lit(value);
660
+ return value === null ? null : sql.val(value);
647
661
  }
648
662
  }
649
663
  buildNumberFilter(eb, fieldRef, type, payload) {
@@ -805,6 +819,32 @@ var BaseCrudDialect = class {
805
819
  });
806
820
  return query;
807
821
  }
822
+ buildCountJson(model, eb, parentAlias, payload) {
823
+ const modelDef = requireModel(this.schema, model);
824
+ const toManyRelations = Object.entries(modelDef.fields).filter(([, field]) => field.relation && field.array);
825
+ const selections = payload === true ? {
826
+ select: toManyRelations.reduce((acc, [field]) => {
827
+ acc[field] = true;
828
+ return acc;
829
+ }, {})
830
+ } : payload;
831
+ const jsonObject = {};
832
+ for (const [field, value] of Object.entries(selections.select)) {
833
+ const fieldDef = requireField(this.schema, model, field);
834
+ const fieldModel = fieldDef.type;
835
+ const joinPairs = buildJoinPairs(this.schema, model, parentAlias, field, fieldModel);
836
+ let fieldCountQuery = eb.selectFrom(fieldModel).select(eb.fn.countAll().as(`_count$${field}`));
837
+ for (const [left, right] of joinPairs) {
838
+ fieldCountQuery = fieldCountQuery.whereRef(left, "=", right);
839
+ }
840
+ if (value && typeof value === "object" && "where" in value && value.where && typeof value.where === "object") {
841
+ const filter = this.buildFilter(eb, fieldModel, fieldModel, value.where);
842
+ fieldCountQuery = fieldCountQuery.where(filter);
843
+ }
844
+ jsonObject[field] = fieldCountQuery;
845
+ }
846
+ return this.buildJsonObject(eb, jsonObject);
847
+ }
808
848
  // #endregion
809
849
  // #region utils
810
850
  negateSort(sort, negated) {
@@ -933,7 +973,7 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
933
973
  });
934
974
  return qb;
935
975
  }
936
- buildRelationObjectArgs(relationModel, relationField, eb, payload, parentName) {
976
+ buildRelationObjectArgs(relationModel, relationField, eb, payload, parentAlias) {
937
977
  const relationModelDef = requireModel(this.schema, relationModel);
938
978
  const objArgs = [];
939
979
  const descendantModels = getDelegateDescendantModels(this.schema, relationModel);
@@ -949,20 +989,28 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
949
989
  buildFieldRef(this.schema, relationModel, field, this.options, eb)
950
990
  ]).flatMap((v) => v));
951
991
  } else if (payload.select) {
952
- objArgs.push(...Object.entries(payload.select).filter(([, value]) => value).map(([field]) => {
953
- const fieldDef = requireField(this.schema, relationModel, field);
954
- const fieldValue = fieldDef.relation ? eb.ref(`${parentName}$${relationField}$${field}.$j`) : buildFieldRef(this.schema, relationModel, field, this.options, eb);
955
- return [
956
- sql2.lit(field),
957
- fieldValue
958
- ];
992
+ objArgs.push(...Object.entries(payload.select).filter(([, value]) => value).map(([field, value]) => {
993
+ if (field === "_count") {
994
+ const subJson = this.buildCountJson(relationModel, eb, `${parentAlias}$${relationField}`, value);
995
+ return [
996
+ sql2.lit(field),
997
+ subJson
998
+ ];
999
+ } else {
1000
+ const fieldDef = requireField(this.schema, relationModel, field);
1001
+ const fieldValue = fieldDef.relation ? eb.ref(`${parentAlias}$${relationField}$${field}.$j`) : buildFieldRef(this.schema, relationModel, field, this.options, eb);
1002
+ return [
1003
+ sql2.lit(field),
1004
+ fieldValue
1005
+ ];
1006
+ }
959
1007
  }).flatMap((v) => v));
960
1008
  }
961
1009
  if (typeof payload === "object" && payload.include && typeof payload.include === "object") {
962
1010
  objArgs.push(...Object.entries(payload.include).filter(([, value]) => value).map(([field]) => [
963
1011
  sql2.lit(field),
964
1012
  // reference the synthesized JSON field
965
- eb.ref(`${parentName}$${relationField}$${field}.$j`)
1013
+ eb.ref(`${parentAlias}$${relationField}$${field}.$j`)
966
1014
  ]).flatMap((v) => v));
967
1015
  }
968
1016
  return objArgs;
@@ -1048,11 +1096,11 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
1048
1096
  buildRelationSelection(query, model, relationField, parentAlias, payload) {
1049
1097
  return query.select((eb) => this.buildRelationJSON(model, eb, relationField, parentAlias, payload).as(relationField));
1050
1098
  }
1051
- buildRelationJSON(model, eb, relationField, parentName, payload) {
1099
+ buildRelationJSON(model, eb, relationField, parentAlias, payload) {
1052
1100
  const relationFieldDef = requireField(this.schema, model, relationField);
1053
1101
  const relationModel = relationFieldDef.type;
1054
1102
  const relationModelDef = requireModel(this.schema, relationModel);
1055
- const subQueryName = `${parentName}$${relationField}`;
1103
+ const subQueryName = `${parentAlias}$${relationField}`;
1056
1104
  let tbl = eb.selectFrom(() => {
1057
1105
  let subQuery = this.buildSelectModel(eb, relationModel);
1058
1106
  subQuery = this.buildSelectAllFields(relationModel, subQuery, typeof payload === "object" ? payload?.omit : void 0);
@@ -1076,14 +1124,14 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
1076
1124
  const relationIds = getIdFields(this.schema, relationModel);
1077
1125
  invariant3(parentIds.length === 1, "many-to-many relation must have exactly one id field");
1078
1126
  invariant3(relationIds.length === 1, "many-to-many relation must have exactly one id field");
1079
- 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}`)));
1127
+ 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}`)));
1080
1128
  } else {
1081
1129
  const { keyPairs, ownedByModel } = getRelationForeignKeyFieldPairs(this.schema, model, relationField);
1082
1130
  keyPairs.forEach(({ fk, pk }) => {
1083
1131
  if (ownedByModel) {
1084
- subQuery = subQuery.whereRef(`${relationModel}.${pk}`, "=", `${parentName}.${fk}`);
1132
+ subQuery = subQuery.whereRef(`${relationModel}.${pk}`, "=", `${parentAlias}.${fk}`);
1085
1133
  } else {
1086
- subQuery = subQuery.whereRef(`${relationModel}.${fk}`, "=", `${parentName}.${pk}`);
1134
+ subQuery = subQuery.whereRef(`${relationModel}.${fk}`, "=", `${parentAlias}.${pk}`);
1087
1135
  }
1088
1136
  });
1089
1137
  }
@@ -1105,24 +1153,32 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
1105
1153
  ]).flatMap((v) => v));
1106
1154
  } else if (payload.select) {
1107
1155
  objArgs.push(...Object.entries(payload.select).filter(([, value]) => value).map(([field, value]) => {
1108
- const fieldDef = requireField(this.schema, relationModel, field);
1109
- if (fieldDef.relation) {
1110
- const subJson = this.buildRelationJSON(relationModel, eb, field, `${parentName}$${relationField}`, value);
1156
+ if (field === "_count") {
1157
+ const subJson = this.buildCountJson(relationModel, eb, `${parentAlias}$${relationField}`, value);
1111
1158
  return [
1112
1159
  sql3.lit(field),
1113
1160
  subJson
1114
1161
  ];
1115
1162
  } else {
1116
- return [
1117
- sql3.lit(field),
1118
- buildFieldRef(this.schema, relationModel, field, this.options, eb)
1119
- ];
1163
+ const fieldDef = requireField(this.schema, relationModel, field);
1164
+ if (fieldDef.relation) {
1165
+ const subJson = this.buildRelationJSON(relationModel, eb, field, `${parentAlias}$${relationField}`, value);
1166
+ return [
1167
+ sql3.lit(field),
1168
+ subJson
1169
+ ];
1170
+ } else {
1171
+ return [
1172
+ sql3.lit(field),
1173
+ buildFieldRef(this.schema, relationModel, field, this.options, eb)
1174
+ ];
1175
+ }
1120
1176
  }
1121
1177
  }).flatMap((v) => v));
1122
1178
  }
1123
1179
  if (typeof payload === "object" && payload.include && typeof payload.include === "object") {
1124
1180
  objArgs.push(...Object.entries(payload.include).filter(([, value]) => value).map(([field, value]) => {
1125
- const subJson = this.buildRelationJSON(relationModel, eb, field, `${parentName}$${relationField}`, value);
1181
+ const subJson = this.buildRelationJSON(relationModel, eb, field, `${parentAlias}$${relationField}`, value);
1126
1182
  return [
1127
1183
  sql3.lit(field),
1128
1184
  subJson