@zenstackhq/runtime 3.0.0-alpha.32 → 3.0.0-alpha.33

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.
@@ -145,7 +145,10 @@ var ExpressionUtils = {
145
145
  isUnary: /* @__PURE__ */ __name((value) => ExpressionUtils.is(value, "unary"), "isUnary"),
146
146
  isBinary: /* @__PURE__ */ __name((value) => ExpressionUtils.is(value, "binary"), "isBinary"),
147
147
  isField: /* @__PURE__ */ __name((value) => ExpressionUtils.is(value, "field"), "isField"),
148
- isMember: /* @__PURE__ */ __name((value) => ExpressionUtils.is(value, "member"), "isMember")
148
+ isMember: /* @__PURE__ */ __name((value) => ExpressionUtils.is(value, "member"), "isMember"),
149
+ getLiteralValue: /* @__PURE__ */ __name((expr2) => {
150
+ return ExpressionUtils.isLiteral(expr2) ? expr2.value : void 0;
151
+ }, "getLiteralValue")
149
152
  };
150
153
 
151
154
  // src/client/errors.ts
@@ -936,6 +939,16 @@ var BaseCrudDialect = class {
936
939
  }
937
940
  return result;
938
941
  }
942
+ buildModelSelect(eb, model, subQueryAlias, payload, selectAllFields) {
943
+ let subQuery = this.buildSelectModel(eb, model, subQueryAlias);
944
+ if (selectAllFields) {
945
+ subQuery = this.buildSelectAllFields(model, subQuery, typeof payload === "object" ? payload?.omit : void 0, subQueryAlias);
946
+ }
947
+ if (payload && typeof payload === "object") {
948
+ subQuery = this.buildFilterSortTake(model, payload, subQuery, subQueryAlias);
949
+ }
950
+ return subQuery;
951
+ }
939
952
  buildSelectField(query, model, modelAlias, field) {
940
953
  const fieldDef = requireField(this.schema, model, field);
941
954
  if (fieldDef.computed) {
@@ -1033,6 +1046,18 @@ var BaseCrudDialect = class {
1033
1046
  fieldRef(model, field, eb, modelAlias, inlineComputedField = true) {
1034
1047
  return buildFieldRef(this.schema, model, field, this.options, eb, modelAlias, inlineComputedField);
1035
1048
  }
1049
+ canJoinWithoutNestedSelect(modelDef, payload) {
1050
+ if (modelDef.computedFields) {
1051
+ return false;
1052
+ }
1053
+ if (modelDef.baseModel || modelDef.isDelegate) {
1054
+ return false;
1055
+ }
1056
+ if (typeof payload === "object" && (payload.orderBy || payload.skip !== void 0 || payload.take !== void 0 || payload.cursor || payload.distinct)) {
1057
+ return false;
1058
+ }
1059
+ return true;
1060
+ }
1036
1061
  };
1037
1062
 
1038
1063
  // src/client/crud/dialects/postgresql.ts
@@ -1058,52 +1083,58 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
1058
1083
  }
1059
1084
  }
1060
1085
  buildRelationSelection(query, model, relationField, parentAlias, payload) {
1061
- const joinedQuery = this.buildRelationJSON(model, query, relationField, parentAlias, payload);
1062
- return joinedQuery.select(`${parentAlias}$${relationField}.$j as ${relationField}`);
1086
+ const relationResultName = `${parentAlias}$${relationField}`;
1087
+ const joinedQuery = this.buildRelationJSON(model, query, relationField, parentAlias, payload, relationResultName);
1088
+ return joinedQuery.select(`${relationResultName}.$data as ${relationField}`);
1063
1089
  }
1064
- buildRelationJSON(model, qb, relationField, parentName, payload) {
1090
+ buildRelationJSON(model, qb, relationField, parentAlias, payload, resultName) {
1065
1091
  const relationFieldDef = requireField(this.schema, model, relationField);
1066
1092
  const relationModel = relationFieldDef.type;
1067
1093
  return qb.leftJoinLateral((eb) => {
1068
- const joinTableName = `${parentName}$${relationField}`;
1069
- let result = eb.selectFrom(`${relationModel} as ${joinTableName}`);
1070
- const subQueryAlias = `${relationModel}$${relationField}$sub`;
1071
- result = eb.selectFrom(() => {
1072
- let subQuery = this.buildSelectModel(eb, relationModel, subQueryAlias);
1073
- subQuery = this.buildSelectAllFields(relationModel, subQuery, typeof payload === "object" ? payload?.omit : void 0, subQueryAlias);
1074
- if (payload && typeof payload === "object") {
1075
- subQuery = this.buildFilterSortTake(relationModel, payload, subQuery, subQueryAlias);
1076
- }
1077
- const m2m = getManyToManyRelation(this.schema, model, relationField);
1078
- if (m2m) {
1079
- const parentIds = getIdFields(this.schema, model);
1080
- const relationIds = getIdFields(this.schema, relationModel);
1081
- (0, import_common_helpers2.invariant)(parentIds.length === 1, "many-to-many relation must have exactly one id field");
1082
- (0, import_common_helpers2.invariant)(relationIds.length === 1, "many-to-many relation must have exactly one id field");
1083
- subQuery = subQuery.where(eb(eb.ref(`${subQueryAlias}.${relationIds[0]}`), "in", eb.selectFrom(m2m.joinTable).select(`${m2m.joinTable}.${m2m.otherFkName}`).whereRef(`${parentName}.${parentIds[0]}`, "=", `${m2m.joinTable}.${m2m.parentFkName}`)));
1084
- } else {
1085
- const joinPairs = buildJoinPairs(this.schema, model, parentName, relationField, subQueryAlias);
1086
- subQuery = subQuery.where((eb2) => this.and(eb2, ...joinPairs.map(([left, right]) => eb2(import_kysely2.sql.ref(left), "=", import_kysely2.sql.ref(right)))));
1087
- }
1088
- return subQuery.as(joinTableName);
1089
- });
1090
- result = this.buildRelationObjectSelect(relationModel, joinTableName, relationField, relationFieldDef, result, payload, parentName);
1091
- result = this.buildRelationJoins(relationModel, relationField, result, payload, parentName);
1092
- return result.as(joinTableName);
1094
+ const relationSelectName = `${resultName}$sub`;
1095
+ const relationModelDef = requireModel(this.schema, relationModel);
1096
+ let tbl;
1097
+ if (this.canJoinWithoutNestedSelect(relationModelDef, payload)) {
1098
+ tbl = this.buildModelSelect(eb, relationModel, relationSelectName, payload, false);
1099
+ tbl = this.buildRelationJoinFilter(tbl, model, relationField, relationModel, relationSelectName, parentAlias);
1100
+ } else {
1101
+ tbl = eb.selectFrom(() => {
1102
+ let subQuery = this.buildModelSelect(eb, relationModel, `${relationSelectName}$t`, payload, true);
1103
+ subQuery = this.buildRelationJoinFilter(subQuery, model, relationField, relationModel, `${relationSelectName}$t`, parentAlias);
1104
+ return subQuery.as(relationSelectName);
1105
+ });
1106
+ }
1107
+ tbl = this.buildRelationObjectSelect(relationModel, relationSelectName, relationFieldDef, tbl, payload, resultName);
1108
+ tbl = this.buildRelationJoins(tbl, relationModel, relationSelectName, payload, resultName);
1109
+ return tbl.as(resultName);
1093
1110
  }, (join) => join.onTrue());
1094
1111
  }
1095
- buildRelationObjectSelect(relationModel, relationModelAlias, relationField, relationFieldDef, qb, payload, parentName) {
1112
+ buildRelationJoinFilter(query, model, relationField, relationModel, relationModelAlias, parentAlias) {
1113
+ const m2m = getManyToManyRelation(this.schema, model, relationField);
1114
+ if (m2m) {
1115
+ const parentIds = getIdFields(this.schema, model);
1116
+ const relationIds = getIdFields(this.schema, relationModel);
1117
+ (0, import_common_helpers2.invariant)(parentIds.length === 1, "many-to-many relation must have exactly one id field");
1118
+ (0, import_common_helpers2.invariant)(relationIds.length === 1, "many-to-many relation must have exactly one id field");
1119
+ query = query.where((eb) => eb(eb.ref(`${relationModelAlias}.${relationIds[0]}`), "in", eb.selectFrom(m2m.joinTable).select(`${m2m.joinTable}.${m2m.otherFkName}`).whereRef(`${parentAlias}.${parentIds[0]}`, "=", `${m2m.joinTable}.${m2m.parentFkName}`)));
1120
+ } else {
1121
+ const joinPairs = buildJoinPairs(this.schema, model, parentAlias, relationField, relationModelAlias);
1122
+ query = query.where((eb) => this.and(eb, ...joinPairs.map(([left, right]) => eb(import_kysely2.sql.ref(left), "=", import_kysely2.sql.ref(right)))));
1123
+ }
1124
+ return query;
1125
+ }
1126
+ buildRelationObjectSelect(relationModel, relationModelAlias, relationFieldDef, qb, payload, parentResultName) {
1096
1127
  qb = qb.select((eb) => {
1097
- const objArgs = this.buildRelationObjectArgs(relationModel, relationModelAlias, relationField, eb, payload, parentName);
1128
+ const objArgs = this.buildRelationObjectArgs(relationModel, relationModelAlias, eb, payload, parentResultName);
1098
1129
  if (relationFieldDef.array) {
1099
- return eb.fn.coalesce(import_kysely2.sql`jsonb_agg(jsonb_build_object(${import_kysely2.sql.join(objArgs)}))`, import_kysely2.sql`'[]'::jsonb`).as("$j");
1130
+ return eb.fn.coalesce(import_kysely2.sql`jsonb_agg(jsonb_build_object(${import_kysely2.sql.join(objArgs)}))`, import_kysely2.sql`'[]'::jsonb`).as("$data");
1100
1131
  } else {
1101
- return import_kysely2.sql`jsonb_build_object(${import_kysely2.sql.join(objArgs)})`.as("$j");
1132
+ return import_kysely2.sql`jsonb_build_object(${import_kysely2.sql.join(objArgs)})`.as("$data");
1102
1133
  }
1103
1134
  });
1104
1135
  return qb;
1105
1136
  }
1106
- buildRelationObjectArgs(relationModel, relationModelAlias, relationField, eb, payload, parentAlias) {
1137
+ buildRelationObjectArgs(relationModel, relationModelAlias, eb, payload, parentResultName) {
1107
1138
  const relationModelDef = requireModel(this.schema, relationModel);
1108
1139
  const objArgs = [];
1109
1140
  const descendantModels = getDelegateDescendantModels(this.schema, relationModel);
@@ -1121,14 +1152,14 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
1121
1152
  } else if (payload.select) {
1122
1153
  objArgs.push(...Object.entries(payload.select).filter(([, value]) => value).map(([field, value]) => {
1123
1154
  if (field === "_count") {
1124
- const subJson = this.buildCountJson(relationModel, eb, `${parentAlias}$${relationField}`, value);
1155
+ const subJson = this.buildCountJson(relationModel, eb, relationModelAlias, value);
1125
1156
  return [
1126
1157
  import_kysely2.sql.lit(field),
1127
1158
  subJson
1128
1159
  ];
1129
1160
  } else {
1130
1161
  const fieldDef = requireField(this.schema, relationModel, field);
1131
- const fieldValue = fieldDef.relation ? eb.ref(`${parentAlias}$${relationField}$${field}.$j`) : this.fieldRef(relationModel, field, eb, void 0, false);
1162
+ const fieldValue = fieldDef.relation ? eb.ref(`${parentResultName}$${field}.$data`) : this.fieldRef(relationModel, field, eb, relationModelAlias, false);
1132
1163
  return [
1133
1164
  import_kysely2.sql.lit(field),
1134
1165
  fieldValue
@@ -1140,18 +1171,18 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
1140
1171
  objArgs.push(...Object.entries(payload.include).filter(([, value]) => value).map(([field]) => [
1141
1172
  import_kysely2.sql.lit(field),
1142
1173
  // reference the synthesized JSON field
1143
- eb.ref(`${parentAlias}$${relationField}$${field}.$j`)
1174
+ eb.ref(`${parentResultName}$${field}.$data`)
1144
1175
  ]).flatMap((v) => v));
1145
1176
  }
1146
1177
  return objArgs;
1147
1178
  }
1148
- buildRelationJoins(relationModel, relationField, qb, payload, parentName) {
1149
- let result = qb;
1179
+ buildRelationJoins(query, relationModel, relationModelAlias, payload, parentResultName) {
1180
+ let result = query;
1150
1181
  if (typeof payload === "object") {
1151
1182
  const selectInclude = payload.include ?? payload.select;
1152
1183
  if (selectInclude && typeof selectInclude === "object") {
1153
1184
  Object.entries(selectInclude).filter(([, value]) => value).filter(([field]) => isRelationField(this.schema, relationModel, field)).forEach(([field, value]) => {
1154
- result = this.buildRelationJSON(relationModel, result, field, `${parentName}$${relationField}`, value);
1185
+ result = this.buildRelationJSON(relationModel, result, field, relationModelAlias, value, `${parentResultName}$${field}`);
1155
1186
  });
1156
1187
  }
1157
1188
  }
@@ -1231,32 +1262,18 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
1231
1262
  const relationModel = relationFieldDef.type;
1232
1263
  const relationModelDef = requireModel(this.schema, relationModel);
1233
1264
  const subQueryName = `${parentAlias}$${relationField}`;
1234
- let tbl = eb.selectFrom(() => {
1235
- const subQueryAlias = `${parentAlias}$${relationField}$sub`;
1236
- let subQuery = this.buildSelectModel(eb, relationModel, subQueryAlias);
1237
- subQuery = this.buildSelectAllFields(relationModel, subQuery, typeof payload === "object" ? payload?.omit : void 0, subQueryAlias);
1238
- if (payload && typeof payload === "object") {
1239
- subQuery = this.buildFilterSortTake(relationModel, payload, subQuery, subQueryAlias);
1240
- }
1241
- const m2m = getManyToManyRelation(this.schema, model, relationField);
1242
- if (m2m) {
1243
- const parentIds = getIdFields(this.schema, model);
1244
- const relationIds = getIdFields(this.schema, relationModel);
1245
- (0, import_common_helpers3.invariant)(parentIds.length === 1, "many-to-many relation must have exactly one id field");
1246
- (0, import_common_helpers3.invariant)(relationIds.length === 1, "many-to-many relation must have exactly one id field");
1247
- subQuery = subQuery.where(eb(eb.ref(`${subQueryAlias}.${relationIds[0]}`), "in", eb.selectFrom(m2m.joinTable).select(`${m2m.joinTable}.${m2m.otherFkName}`).whereRef(`${parentAlias}.${parentIds[0]}`, "=", `${m2m.joinTable}.${m2m.parentFkName}`)));
1248
- } else {
1249
- const { keyPairs, ownedByModel } = getRelationForeignKeyFieldPairs(this.schema, model, relationField);
1250
- keyPairs.forEach(({ fk, pk }) => {
1251
- if (ownedByModel) {
1252
- subQuery = subQuery.whereRef(`${subQueryAlias}.${pk}`, "=", `${parentAlias}.${fk}`);
1253
- } else {
1254
- subQuery = subQuery.whereRef(`${subQueryAlias}.${fk}`, "=", `${parentAlias}.${pk}`);
1255
- }
1256
- });
1257
- }
1258
- return subQuery.as(subQueryName);
1259
- });
1265
+ let tbl;
1266
+ if (this.canJoinWithoutNestedSelect(relationModelDef, payload)) {
1267
+ tbl = this.buildModelSelect(eb, relationModel, subQueryName, payload, false);
1268
+ tbl = this.buildRelationJoinFilter(tbl, model, relationField, subQueryName, parentAlias);
1269
+ } else {
1270
+ tbl = eb.selectFrom(() => {
1271
+ const selectModelAlias = `${parentAlias}$${relationField}$sub`;
1272
+ let selectModelQuery = this.buildModelSelect(eb, relationModel, selectModelAlias, payload, true);
1273
+ selectModelQuery = this.buildRelationJoinFilter(selectModelQuery, model, relationField, selectModelAlias, parentAlias);
1274
+ return selectModelQuery.as(subQueryName);
1275
+ });
1276
+ }
1260
1277
  tbl = tbl.select(() => {
1261
1278
  const objArgs = [];
1262
1279
  const descendantModels = getDelegateDescendantModels(this.schema, relationModel);
@@ -1269,7 +1286,7 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
1269
1286
  if (payload === true || !payload.select) {
1270
1287
  objArgs.push(...Object.entries(relationModelDef.fields).filter(([, value]) => !value.relation).filter(([name]) => !(typeof payload === "object" && payload.omit?.[name] === true)).map(([field]) => [
1271
1288
  import_kysely3.sql.lit(field),
1272
- this.fieldRef(relationModel, field, eb, void 0, false)
1289
+ this.fieldRef(relationModel, field, eb, subQueryName, false)
1273
1290
  ]).flatMap((v) => v));
1274
1291
  } else if (payload.select) {
1275
1292
  objArgs.push(...Object.entries(payload.select).filter(([, value]) => value).map(([field, value]) => {
@@ -1290,7 +1307,7 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
1290
1307
  } else {
1291
1308
  return [
1292
1309
  import_kysely3.sql.lit(field),
1293
- this.fieldRef(relationModel, field, eb, void 0, false)
1310
+ this.fieldRef(relationModel, field, eb, subQueryName, false)
1294
1311
  ];
1295
1312
  }
1296
1313
  }
@@ -1306,13 +1323,35 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
1306
1323
  }).flatMap((v) => v));
1307
1324
  }
1308
1325
  if (relationFieldDef.array) {
1309
- return eb.fn.coalesce(import_kysely3.sql`json_group_array(json_object(${import_kysely3.sql.join(objArgs)}))`, import_kysely3.sql`json_array()`).as("$j");
1326
+ return eb.fn.coalesce(import_kysely3.sql`json_group_array(json_object(${import_kysely3.sql.join(objArgs)}))`, import_kysely3.sql`json_array()`).as("$data");
1310
1327
  } else {
1311
- return import_kysely3.sql`json_object(${import_kysely3.sql.join(objArgs)})`.as("data");
1328
+ return import_kysely3.sql`json_object(${import_kysely3.sql.join(objArgs)})`.as("$data");
1312
1329
  }
1313
1330
  });
1314
1331
  return tbl;
1315
1332
  }
1333
+ buildRelationJoinFilter(selectModelQuery, model, relationField, relationModelAlias, parentAlias) {
1334
+ const fieldDef = requireField(this.schema, model, relationField);
1335
+ const relationModel = fieldDef.type;
1336
+ const m2m = getManyToManyRelation(this.schema, model, relationField);
1337
+ if (m2m) {
1338
+ const parentIds = getIdFields(this.schema, model);
1339
+ const relationIds = getIdFields(this.schema, relationModel);
1340
+ (0, import_common_helpers3.invariant)(parentIds.length === 1, "many-to-many relation must have exactly one id field");
1341
+ (0, import_common_helpers3.invariant)(relationIds.length === 1, "many-to-many relation must have exactly one id field");
1342
+ selectModelQuery = selectModelQuery.where((eb) => eb(eb.ref(`${relationModelAlias}.${relationIds[0]}`), "in", eb.selectFrom(m2m.joinTable).select(`${m2m.joinTable}.${m2m.otherFkName}`).whereRef(`${parentAlias}.${parentIds[0]}`, "=", `${m2m.joinTable}.${m2m.parentFkName}`)));
1343
+ } else {
1344
+ const { keyPairs, ownedByModel } = getRelationForeignKeyFieldPairs(this.schema, model, relationField);
1345
+ keyPairs.forEach(({ fk, pk }) => {
1346
+ if (ownedByModel) {
1347
+ selectModelQuery = selectModelQuery.whereRef(`${relationModelAlias}.${pk}`, "=", `${parentAlias}.${fk}`);
1348
+ } else {
1349
+ selectModelQuery = selectModelQuery.whereRef(`${relationModelAlias}.${fk}`, "=", `${parentAlias}.${pk}`);
1350
+ }
1351
+ });
1352
+ }
1353
+ return selectModelQuery;
1354
+ }
1316
1355
  buildSkipTake(query, skip, take) {
1317
1356
  if (take !== void 0) {
1318
1357
  query = query.limit(take);