prisma-sql 1.74.0 → 1.75.0

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
@@ -1228,7 +1228,7 @@ var getNextSort = (sortRaw) => {
1228
1228
  if (s === "desc") return "asc";
1229
1229
  return sortRaw;
1230
1230
  };
1231
- var flipObjectSort = (obj) => {
1231
+ var flipScalarSortObject = (obj) => {
1232
1232
  const out = __spreadValues({}, obj);
1233
1233
  const hasSort = Object.prototype.hasOwnProperty.call(obj, "sort");
1234
1234
  const hasDirection = Object.prototype.hasOwnProperty.call(obj, "direction");
@@ -1244,26 +1244,64 @@ var flipObjectSort = (obj) => {
1244
1244
  }
1245
1245
  return out;
1246
1246
  };
1247
+ function isScalarSortConfig(obj) {
1248
+ return Object.prototype.hasOwnProperty.call(obj, "sort") || Object.prototype.hasOwnProperty.call(obj, "direction");
1249
+ }
1250
+ function flipRelationOrderByValue(obj) {
1251
+ const out = {};
1252
+ for (const [k, v] of Object.entries(obj)) {
1253
+ out[k] = flipValue(v);
1254
+ }
1255
+ return out;
1256
+ }
1247
1257
  var flipValue = (v) => {
1248
1258
  if (typeof v === "string") return flipSortString(v);
1249
- if (isPlainObject(v)) return flipObjectSort(v);
1259
+ if (isPlainObject(v)) {
1260
+ if (isScalarSortConfig(v)) return flipScalarSortObject(v);
1261
+ return flipRelationOrderByValue(v);
1262
+ }
1250
1263
  return v;
1251
1264
  };
1252
- var assertSingleFieldObject = (item) => {
1265
+ var expandToSingleFieldEntries = (item) => {
1253
1266
  if (!isPlainObject(item)) {
1254
1267
  throw new Error("orderBy array entries must be objects");
1255
1268
  }
1256
1269
  const entries = Object.entries(item);
1257
- if (entries.length !== 1) {
1258
- throw new Error("orderBy array entries must have exactly one field");
1270
+ if (entries.length === 0) {
1271
+ throw new Error("orderBy array entries must have at least one field");
1259
1272
  }
1260
- return entries[0];
1273
+ return entries;
1261
1274
  };
1275
+ function expandOrderByInput(orderBy) {
1276
+ if (!isNotNullish(orderBy)) return [];
1277
+ if (Array.isArray(orderBy)) {
1278
+ const result = [];
1279
+ for (const item of orderBy) {
1280
+ result.push(...expandToSingleFieldEntries(item));
1281
+ }
1282
+ return result;
1283
+ }
1284
+ if (isPlainObject(orderBy)) {
1285
+ return Object.entries(orderBy);
1286
+ }
1287
+ throw new Error("orderBy must be an object or array of objects");
1288
+ }
1289
+ function isScalarOrderByValue(v) {
1290
+ if (typeof v === "string") {
1291
+ const lower = v.toLowerCase();
1292
+ return lower === "asc" || lower === "desc";
1293
+ }
1294
+ if (isPlainObject(v) && isScalarSortConfig(v)) return true;
1295
+ return false;
1296
+ }
1262
1297
  var flipOrderByArray = (orderBy) => {
1263
- return orderBy.map((item) => {
1264
- const [k, v] = assertSingleFieldObject(item);
1265
- return { [k]: flipValue(v) };
1266
- });
1298
+ const result = [];
1299
+ for (const item of orderBy) {
1300
+ for (const [k, v] of expandToSingleFieldEntries(item)) {
1301
+ result.push({ [k]: flipValue(v) });
1302
+ }
1303
+ }
1304
+ return result;
1267
1305
  };
1268
1306
  var flipOrderByObject = (orderBy) => {
1269
1307
  const out = {};
@@ -1283,18 +1321,24 @@ function reverseOrderByInput(orderBy) {
1283
1321
  throw new Error("orderBy must be an object or array of objects");
1284
1322
  }
1285
1323
  var normalizePairs = (pairs, parseValue) => {
1286
- return pairs.map(([field, rawValue]) => {
1324
+ const result = [];
1325
+ for (const [field, rawValue] of pairs) {
1326
+ if (!isScalarOrderByValue(rawValue)) continue;
1287
1327
  const parsed = parseValue(rawValue, field);
1288
- return {
1328
+ result.push({
1289
1329
  [field]: parsed.nulls !== void 0 ? { direction: parsed.direction, nulls: parsed.nulls } : parsed.direction
1290
- };
1291
- });
1330
+ });
1331
+ }
1332
+ return result;
1292
1333
  };
1293
1334
  function normalizeOrderByInput(orderBy, parseValue) {
1294
1335
  if (!isNotNullish(orderBy)) return [];
1295
1336
  if (Array.isArray(orderBy)) {
1296
- const pairs = orderBy.map(assertSingleFieldObject);
1297
- return normalizePairs(pairs, parseValue);
1337
+ const allPairs = [];
1338
+ for (const item of orderBy) {
1339
+ allPairs.push(...expandToSingleFieldEntries(item));
1340
+ }
1341
+ return normalizePairs(allPairs, parseValue);
1298
1342
  }
1299
1343
  if (isPlainObject(orderBy)) {
1300
1344
  return normalizePairs(Object.entries(orderBy), parseValue);
@@ -1306,9 +1350,11 @@ function normalizeAndValidateOrderBy(orderBy, model, parseValue) {
1306
1350
  const normalized = normalizeOrderByInput(orderBy, parseValue);
1307
1351
  const entries = [];
1308
1352
  const scalarSet = getScalarFieldSet(model);
1353
+ const relationSet = getRelationFieldSet(model);
1309
1354
  for (const item of normalized) {
1310
1355
  const [[field, value]] = Object.entries(item);
1311
1356
  if (!scalarSet.has(field)) {
1357
+ if (relationSet.has(field)) continue;
1312
1358
  throw new Error(
1313
1359
  `orderBy field '${field}' not found on model ${model.name}`
1314
1360
  );
@@ -1767,16 +1813,6 @@ function buildOrderBy(orderBy, alias, dialect, model) {
1767
1813
  const d = dialect != null ? dialect : getGlobalDialect();
1768
1814
  return buildOrderByFragment(entries, alias, d, model);
1769
1815
  }
1770
- function buildOrderByClause(args, alias, dialect, model) {
1771
- if (!isNotNullish(args.orderBy)) return "";
1772
- const result = buildOrderBy(args.orderBy, alias, dialect, model);
1773
- if (!isNonEmptyString(result)) {
1774
- throw new Error(
1775
- "buildOrderByClause: orderBy specified but produced empty result"
1776
- );
1777
- }
1778
- return result;
1779
- }
1780
1816
  function normalizeTakeLike(v) {
1781
1817
  const n = normalizeIntLike("take", v, {
1782
1818
  min: Number.MIN_SAFE_INTEGER,
@@ -5571,6 +5607,114 @@ function buildIncludeSql(args, model, schemas, parentAlias, params, dialect, out
5571
5607
  });
5572
5608
  }
5573
5609
 
5610
+ // src/builder/shared/order-by-relation.ts
5611
+ function resolveTableRef(model, dialect) {
5612
+ const tableName = model.tableName || model.dbName || model.name;
5613
+ if (dialect === "sqlite") {
5614
+ return quote(tableName);
5615
+ }
5616
+ const schema = model.schema || model.schemaName || "public";
5617
+ return buildTableReference(schema, tableName, dialect);
5618
+ }
5619
+ function findRelationField(model, fieldName) {
5620
+ return model.fields.find((f) => f.name === fieldName && f.isRelation);
5621
+ }
5622
+ function buildOrderByWithRelations(orderBy, alias, dialect, model, schemas) {
5623
+ if (!isNotNullish(orderBy)) return { sql: "", joins: [] };
5624
+ const expanded = expandOrderByInput(orderBy);
5625
+ if (expanded.length === 0) return { sql: "", joins: [] };
5626
+ const relationSet = getRelationFieldSet(model);
5627
+ const scalarSet = getScalarFieldSet(model);
5628
+ const orderFragments = [];
5629
+ const joins = [];
5630
+ const usedAliases = /* @__PURE__ */ new Set();
5631
+ let relAliasCounter = 0;
5632
+ for (const [fieldName, value] of expanded) {
5633
+ if (scalarSet.has(fieldName)) {
5634
+ const entries = normalizeAndValidateOrderBy(
5635
+ [{ [fieldName]: value }],
5636
+ model,
5637
+ parseOrderByValue
5638
+ );
5639
+ const sql = buildOrderByFragment(entries, alias, dialect, model);
5640
+ if (sql) orderFragments.push(sql);
5641
+ continue;
5642
+ }
5643
+ if (relationSet.has(fieldName)) {
5644
+ if (!isPlainObject(value)) {
5645
+ throw new Error(`Relation orderBy for '${fieldName}' must be an object`);
5646
+ }
5647
+ const nestedEntries = Object.entries(value);
5648
+ if (nestedEntries.length === 0) continue;
5649
+ if ("_count" in value) {
5650
+ throw new Error(
5651
+ `Relation orderBy with _count on '${fieldName}' is not yet supported by prisma-sql`
5652
+ );
5653
+ }
5654
+ const field = findRelationField(model, fieldName);
5655
+ if (!field) {
5656
+ throw new Error(
5657
+ `Relation field '${fieldName}' not found on model ${model.name}`
5658
+ );
5659
+ }
5660
+ const relatedModel = getModelByName(schemas, field.relatedModel);
5661
+ if (!relatedModel) {
5662
+ throw new Error(
5663
+ `Related model '${field.relatedModel}' not found for relation '${fieldName}'`
5664
+ );
5665
+ }
5666
+ const relScalarSet = getScalarFieldSet(relatedModel);
5667
+ const relRelationSet = getRelationFieldSet(relatedModel);
5668
+ for (const [nestedField] of nestedEntries) {
5669
+ if (relRelationSet.has(nestedField)) {
5670
+ throw new Error(
5671
+ `Nested relation orderBy (${fieldName}.${nestedField}) is not yet supported by prisma-sql`
5672
+ );
5673
+ }
5674
+ if (!relScalarSet.has(nestedField)) {
5675
+ throw new Error(
5676
+ `orderBy field '${nestedField}' does not exist on related model '${relatedModel.name}'`
5677
+ );
5678
+ }
5679
+ }
5680
+ let joinAlias;
5681
+ do {
5682
+ joinAlias = `ob_${relAliasCounter++}`;
5683
+ } while (usedAliases.has(joinAlias));
5684
+ usedAliases.add(joinAlias);
5685
+ const tableRef = resolveTableRef(relatedModel, dialect);
5686
+ const cond = joinCondition(
5687
+ field,
5688
+ model,
5689
+ relatedModel,
5690
+ alias,
5691
+ joinAlias
5692
+ );
5693
+ joins.push(`LEFT JOIN ${tableRef} ${joinAlias} ON ${cond}`);
5694
+ const entries = normalizeAndValidateOrderBy(
5695
+ [value],
5696
+ relatedModel,
5697
+ parseOrderByValue
5698
+ );
5699
+ const sql = buildOrderByFragment(
5700
+ entries,
5701
+ joinAlias,
5702
+ dialect,
5703
+ relatedModel
5704
+ );
5705
+ if (sql) orderFragments.push(sql);
5706
+ continue;
5707
+ }
5708
+ throw new Error(
5709
+ `orderBy field '${fieldName}' does not exist on model ${model.name}`
5710
+ );
5711
+ }
5712
+ return {
5713
+ sql: orderFragments.join(SQL_SEPARATORS.ORDER_BY),
5714
+ joins
5715
+ };
5716
+ }
5717
+
5574
5718
  // src/builder/select.ts
5575
5719
  function normalizeOrderByInput3(orderBy) {
5576
5720
  return normalizeOrderByInput(orderBy, parseOrderByValue);
@@ -5650,20 +5794,13 @@ Available fields: ${[...scalarSet].join(", ")}`
5650
5794
  assertScalarField(model, f, "distinct");
5651
5795
  }
5652
5796
  }
5653
- function validateOrderBy(model, orderBy) {
5797
+ function validateOrderBy(model, orderBy, schemas) {
5654
5798
  if (!isNotNullish(orderBy)) return;
5655
- const items = normalizeOrderByInput3(orderBy);
5656
- if (items.length === 0) return;
5799
+ const expanded = expandOrderByInput(orderBy);
5800
+ if (expanded.length === 0) return;
5657
5801
  const scalarSet = getScalarFieldSet(model);
5658
5802
  const relationSet = getRelationFieldSet(model);
5659
- for (const it of items) {
5660
- const entries = Object.entries(it);
5661
- if (entries.length !== 1) {
5662
- throw new Error(
5663
- `orderBy array entries must have exactly one field. Got ${entries.length} fields: ${Object.keys(it).join(", ")}`
5664
- );
5665
- }
5666
- const [fieldName, value] = entries[0];
5803
+ for (const [fieldName, value] of expanded) {
5667
5804
  const f = String(fieldName).trim();
5668
5805
  if (f.length === 0) {
5669
5806
  throw new Error("orderBy field name cannot be empty");
@@ -5673,20 +5810,21 @@ function validateOrderBy(model, orderBy) {
5673
5810
  `orderBy field name too long (${f.length} chars, max 255): ${f.slice(0, 50)}...`
5674
5811
  );
5675
5812
  }
5676
- if (!scalarSet.has(f)) {
5677
- if (relationSet.has(f)) {
5678
- throw new Error(
5679
- `orderBy field '${f}' is a relation field. Only scalar fields are allowed.
5680
- Available scalar fields: ${[...scalarSet].join(", ")}`
5681
- );
5813
+ if (scalarSet.has(f)) {
5814
+ assertScalarField(model, f, "orderBy");
5815
+ parseOrderByValue(value, f);
5816
+ continue;
5817
+ }
5818
+ if (relationSet.has(f)) {
5819
+ if (!isPlainObject(value)) {
5820
+ throw new Error(`Relation orderBy for '${f}' must be an object`);
5682
5821
  }
5683
- throw new Error(
5684
- `orderBy field '${f}' does not exist on model ${model.name}.
5685
- Available fields: ${[...scalarSet].join(", ")}`
5686
- );
5822
+ continue;
5687
5823
  }
5688
- assertScalarField(model, f, "orderBy");
5689
- parseOrderByValue(value, f);
5824
+ throw new Error(
5825
+ `orderBy field '${f}' does not exist on model ${model.name}.
5826
+ Available fields: ${[...scalarSet].join(", ")}`
5827
+ );
5690
5828
  }
5691
5829
  }
5692
5830
  function validateCursor(model, cursor, distinct) {
@@ -5769,11 +5907,12 @@ function buildSelectSpec(input) {
5769
5907
  model,
5770
5908
  alias
5771
5909
  );
5772
- const orderByClause = buildOrderByClause(
5773
- normalizedArgs,
5910
+ const orderByResult = buildOrderByWithRelations(
5911
+ normalizedArgs.orderBy,
5774
5912
  alias,
5775
5913
  dialect,
5776
- model
5914
+ model,
5915
+ schemas
5777
5916
  );
5778
5917
  const { take, skip, cursor } = getPaginationParams(method, normalizedArgs);
5779
5918
  const params = createParamStoreFrom(
@@ -5806,13 +5945,15 @@ function buildSelectSpec(input) {
5806
5945
  "Cursor pagination with distinct is not supported in SQLite due to window function limitations. Use findMany with skip/take instead, or remove distinct."
5807
5946
  );
5808
5947
  }
5948
+ const orderByJoins = orderByResult.joins;
5949
+ const combinedWhereJoins = whereResult.joins ? [...whereResult.joins, ...orderByJoins] : orderByJoins.length > 0 ? orderByJoins : [];
5809
5950
  return {
5810
5951
  select: selectFields,
5811
5952
  includes,
5812
5953
  from: { table: tableName, alias },
5813
5954
  whereClause: whereResult.clause,
5814
- whereJoins: whereResult.joins,
5815
- orderBy: orderByClause,
5955
+ whereJoins: combinedWhereJoins,
5956
+ orderBy: orderByResult.sql,
5816
5957
  pagination: { take, skip },
5817
5958
  distinct: normalizedArgs.distinct,
5818
5959
  method,