prisma-sql 1.74.0 → 1.75.1

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