velocious 1.0.466 → 1.0.467

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.
Files changed (29) hide show
  1. package/README.md +2 -1
  2. package/build/configuration.js +2 -2
  3. package/build/database/pool/async-tracked-multi-connection.js +7 -2
  4. package/build/database/pool/single-multi-use.js +38 -0
  5. package/build/frontend-model-controller.js +162 -56
  6. package/build/frontend-models/query.js +55 -33
  7. package/build/src/configuration.js +3 -3
  8. package/build/src/database/pool/async-tracked-multi-connection.d.ts.map +1 -1
  9. package/build/src/database/pool/async-tracked-multi-connection.js +9 -3
  10. package/build/src/database/pool/single-multi-use.d.ts +1 -0
  11. package/build/src/database/pool/single-multi-use.d.ts.map +1 -1
  12. package/build/src/database/pool/single-multi-use.js +34 -1
  13. package/build/src/frontend-model-controller.d.ts +45 -5
  14. package/build/src/frontend-model-controller.d.ts.map +1 -1
  15. package/build/src/frontend-model-controller.js +155 -57
  16. package/build/src/frontend-models/query.d.ts +8 -0
  17. package/build/src/frontend-models/query.d.ts.map +1 -1
  18. package/build/src/frontend-models/query.js +53 -34
  19. package/build/src/utils/ransack.d.ts +11 -3
  20. package/build/src/utils/ransack.d.ts.map +1 -1
  21. package/build/src/utils/ransack.js +42 -23
  22. package/build/utils/ransack.js +44 -22
  23. package/package.json +1 -1
  24. package/src/configuration.js +2 -2
  25. package/src/database/pool/async-tracked-multi-connection.js +7 -2
  26. package/src/database/pool/single-multi-use.js +38 -0
  27. package/src/frontend-model-controller.js +162 -56
  28. package/src/frontend-models/query.js +55 -33
  29. package/src/utils/ransack.js +44 -22
@@ -5,6 +5,28 @@ import {normalizeRansackGroup, parseRansackSort} from "../utils/ransack.js"
5
5
  import {isModelScopeDescriptor} from "../utils/model-scope.js"
6
6
  import isPlainObject from "../utils/plain-object.js"
7
7
 
8
+ /** Error raised when a frontend-model query descriptor is malformed. */
9
+ export class FrontendModelQueryError extends Error {
10
+ /**
11
+ * Creates a frontend-model query error.
12
+ * @param {string} message - Error message.
13
+ */
14
+ constructor(message) {
15
+ super(message)
16
+
17
+ this.name = "FrontendModelQueryError"
18
+ }
19
+ }
20
+
21
+ /**
22
+ * Builds a query descriptor error.
23
+ * @param {string} message - Error message.
24
+ * @returns {FrontendModelQueryError} - Query descriptor error.
25
+ */
26
+ function frontendModelQueryError(message) {
27
+ return new FrontendModelQueryError(message)
28
+ }
29
+
8
30
  /**
9
31
  * FrontendModelSearch type.
10
32
  * @typedef {object} FrontendModelSearch
@@ -376,7 +398,7 @@ export function normalizeSearchOperator(operator) {
376
398
  const supportedOperators = new Set(["eq", "like", "notEq", "gt", "gteq", "lt", "lteq"])
377
399
 
378
400
  if (!supportedOperators.has(normalizedOperator)) {
379
- throw new Error(`search operator must be one of: eq, like, notEq, gt, gteq, lt, lteq, >, >=, <, <= (got: ${operator})`)
401
+ throw frontendModelQueryError(`search operator must be one of: eq, like, notEq, gt, gteq, lt, lteq, >, >=, <, <= (got: ${operator})`)
380
402
  }
381
403
 
382
404
  return /** @type {"eq" | "like" | "notEq" | "gt" | "gteq" | "lt" | "lteq"} */ (normalizedOperator)
@@ -400,7 +422,7 @@ function mergeJoinRecord(targetJoins, incomingJoins) {
400
422
  }
401
423
 
402
424
  if (!isPlainObject(incomingValue)) {
403
- throw new Error(`Invalid join value for ${relationshipName}: ${typeof incomingValue}`)
425
+ throw frontendModelQueryError(`Invalid join value for ${relationshipName}: ${typeof incomingValue}`)
404
426
  }
405
427
 
406
428
  if (isPlainObject(existingValue)) {
@@ -433,7 +455,7 @@ export function normalizeJoins(joins) {
433
455
 
434
456
  for (const joinEntry of joins) {
435
457
  if (!isPlainObject(joinEntry)) {
436
- throw new Error(`Invalid joins entry type: ${typeof joinEntry}`)
458
+ throw frontendModelQueryError(`Invalid joins entry type: ${typeof joinEntry}`)
437
459
  }
438
460
 
439
461
  mergeJoinRecord(normalized, normalizeJoins(joinEntry))
@@ -443,7 +465,7 @@ export function normalizeJoins(joins) {
443
465
  }
444
466
 
445
467
  if (!isPlainObject(joins)) {
446
- throw new Error(`Invalid joins type: ${typeof joins}`)
468
+ throw frontendModelQueryError(`Invalid joins type: ${typeof joins}`)
447
469
  }
448
470
 
449
471
  /**
@@ -462,7 +484,7 @@ export function normalizeJoins(joins) {
462
484
  continue
463
485
  }
464
486
 
465
- throw new Error(`Invalid join definition for "${relationshipName}": ${typeof relationshipJoin}`)
487
+ throw frontendModelQueryError(`Invalid join definition for "${relationshipName}": ${typeof relationshipJoin}`)
466
488
  }
467
489
 
468
490
  return normalized
@@ -497,13 +519,13 @@ export function normalizeJoins(joins) {
497
519
  */
498
520
  function normalizeSortDirection(direction) {
499
521
  if (typeof direction !== "string") {
500
- throw new Error(`Invalid sort direction type: ${typeof direction}`)
522
+ throw frontendModelQueryError(`Invalid sort direction type: ${typeof direction}`)
501
523
  }
502
524
 
503
525
  const normalizedDirection = direction.trim().toLowerCase()
504
526
 
505
527
  if (normalizedDirection !== "asc" && normalizedDirection !== "desc") {
506
- throw new Error(`Invalid sort direction: ${direction}`)
528
+ throw frontendModelQueryError(`Invalid sort direction: ${direction}`)
507
529
  }
508
530
 
509
531
  return normalizedDirection
@@ -551,14 +573,14 @@ function parseSortString(sortValue, path = []) {
551
573
  const trimmed = sortValue.trim()
552
574
 
553
575
  if (trimmed.length < 1) {
554
- throw new Error("sort value must be a non-empty string")
576
+ throw frontendModelQueryError("sort value must be a non-empty string")
555
577
  }
556
578
 
557
579
  if (trimmed.startsWith("-")) {
558
580
  const column = trimmed.slice(1).trim()
559
581
 
560
582
  if (column.length < 1) {
561
- throw new Error(`Invalid sort definition: ${sortValue}`)
583
+ throw frontendModelQueryError(`Invalid sort definition: ${sortValue}`)
562
584
  }
563
585
 
564
586
  return {
@@ -571,13 +593,13 @@ function parseSortString(sortValue, path = []) {
571
593
  const sortParts = trimmed.split(/\s+/).filter(Boolean)
572
594
 
573
595
  if (sortParts.length > 2) {
574
- throw new Error(`Invalid sort definition: ${sortValue}`)
596
+ throw frontendModelQueryError(`Invalid sort definition: ${sortValue}`)
575
597
  }
576
598
 
577
599
  const column = sortParts[0]
578
600
 
579
601
  if (column.length < 1) {
580
- throw new Error(`Invalid sort definition: ${sortValue}`)
602
+ throw frontendModelQueryError(`Invalid sort definition: ${sortValue}`)
581
603
  }
582
604
 
583
605
  const direction = sortParts.length === 2
@@ -602,7 +624,7 @@ function parseSortTuple(sortValue, path = []) {
602
624
  const column = columnValue.trim()
603
625
 
604
626
  if (column.length < 1) {
605
- throw new Error("sort tuple column must be a non-empty string")
627
+ throw frontendModelQueryError("sort tuple column must be a non-empty string")
606
628
  }
607
629
 
608
630
  return {
@@ -641,12 +663,12 @@ function normalizeSortObject(sortValue, path) {
641
663
 
642
664
  if (Array.isArray(sortEntry)) {
643
665
  if (sortEntry.length < 1) {
644
- throw new Error(`Invalid sort definition for "${sortKey}": empty array`)
666
+ throw frontendModelQueryError(`Invalid sort definition for "${sortKey}": empty array`)
645
667
  }
646
668
 
647
669
  for (const nestedSortEntry of sortEntry) {
648
670
  if (!sortTuple(nestedSortEntry)) {
649
- throw new Error(`Invalid sort definition for "${sortKey}": expected [column, direction] tuples`)
671
+ throw frontendModelQueryError(`Invalid sort definition for "${sortKey}": expected [column, direction] tuples`)
650
672
  }
651
673
 
652
674
  normalizedSorts.push(parseSortTuple(nestedSortEntry, [...path, sortKey]))
@@ -659,7 +681,7 @@ function normalizeSortObject(sortValue, path) {
659
681
  continue
660
682
  }
661
683
 
662
- throw new Error(`Invalid sort definition for "${sortKey}": ${typeof sortEntry}`)
684
+ throw frontendModelQueryError(`Invalid sort definition for "${sortKey}": ${typeof sortEntry}`)
663
685
  }
664
686
 
665
687
  return normalizedSorts
@@ -724,13 +746,13 @@ export function normalizeSort(sort) {
724
746
  continue
725
747
  }
726
748
 
727
- throw new Error(`Invalid sort entry type: ${typeof sortEntry}`)
749
+ throw frontendModelQueryError(`Invalid sort entry type: ${typeof sortEntry}`)
728
750
  }
729
751
 
730
752
  return normalized
731
753
  }
732
754
 
733
- throw new Error(`Invalid sort type: ${typeof sort}`)
755
+ throw frontendModelQueryError(`Invalid sort type: ${typeof sort}`)
734
756
  }
735
757
 
736
758
  /**
@@ -743,7 +765,7 @@ function parseGroupString(groupValue, path = []) {
743
765
  const trimmed = groupValue.trim()
744
766
 
745
767
  if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(trimmed)) {
746
- throw new Error(`Invalid group column: ${groupValue}`)
768
+ throw frontendModelQueryError(`Invalid group column: ${groupValue}`)
747
769
  }
748
770
 
749
771
  return {
@@ -789,12 +811,12 @@ function normalizeColumnProjectionObject(value, path, parseString, label) {
789
811
 
790
812
  if (Array.isArray(projectionEntry)) {
791
813
  if (projectionEntry.length < 1) {
792
- throw new Error(`Invalid ${label} definition for "${projectionKey}": empty array`)
814
+ throw frontendModelQueryError(`Invalid ${label} definition for "${projectionKey}": empty array`)
793
815
  }
794
816
 
795
817
  for (const nestedProjectionEntry of projectionEntry) {
796
818
  if (typeof nestedProjectionEntry !== "string") {
797
- throw new Error(`Invalid ${label} definition for "${projectionKey}": expected string columns`)
819
+ throw frontendModelQueryError(`Invalid ${label} definition for "${projectionKey}": expected string columns`)
798
820
  }
799
821
 
800
822
  normalized.push(parseString(nestedProjectionEntry, [...path, projectionKey]))
@@ -808,7 +830,7 @@ function normalizeColumnProjectionObject(value, path, parseString, label) {
808
830
  continue
809
831
  }
810
832
 
811
- throw new Error(`Invalid ${label} definition for "${projectionKey}": ${typeof projectionEntry}`)
833
+ throw frontendModelQueryError(`Invalid ${label} definition for "${projectionKey}": ${typeof projectionEntry}`)
812
834
  }
813
835
 
814
836
  return normalized
@@ -862,13 +884,13 @@ export function normalizeGroup(group) {
862
884
  continue
863
885
  }
864
886
 
865
- throw new Error(`Invalid group entry type: ${typeof groupEntry}`)
887
+ throw frontendModelQueryError(`Invalid group entry type: ${typeof groupEntry}`)
866
888
  }
867
889
 
868
890
  return normalized
869
891
  }
870
892
 
871
- throw new Error(`Invalid group type: ${typeof group}`)
893
+ throw frontendModelQueryError(`Invalid group type: ${typeof group}`)
872
894
  }
873
895
 
874
896
  /**
@@ -881,7 +903,7 @@ function parsePluckString(pluckValue, path = []) {
881
903
  const trimmed = pluckValue.trim()
882
904
 
883
905
  if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(trimmed)) {
884
- throw new Error(`Invalid pluck column: ${pluckValue}`)
906
+ throw frontendModelQueryError(`Invalid pluck column: ${pluckValue}`)
885
907
  }
886
908
 
887
909
  return {
@@ -938,13 +960,13 @@ export function normalizePluck(pluck) {
938
960
  continue
939
961
  }
940
962
 
941
- throw new Error(`Invalid pluck entry type: ${typeof pluckEntry}`)
963
+ throw frontendModelQueryError(`Invalid pluck entry type: ${typeof pluckEntry}`)
942
964
  }
943
965
 
944
966
  return normalized
945
967
  }
946
968
 
947
- throw new Error(`Invalid pluck type: ${typeof pluck}`)
969
+ throw frontendModelQueryError(`Invalid pluck type: ${typeof pluck}`)
948
970
  }
949
971
 
950
972
  /**
@@ -997,13 +1019,13 @@ function frontendModelPluckTargetModelClass(modelClass, path) {
997
1019
  }
998
1020
 
999
1021
  /**
1000
- * Runs validate pluck definitions.
1001
- * @param {object} args - Pluck validation args.
1022
+ * Runs assert pluck definitions allowed.
1023
+ * @param {object} args - Pluck assertion args.
1002
1024
  * @param {import("./base.js").FrontendModelClass} args.modelClass - Root model class.
1003
1025
  * @param {FrontendModelPluck[]} args.pluck - Pluck descriptors.
1004
- * @returns {FrontendModelPluck[]} - Validated pluck descriptors.
1026
+ * @returns {FrontendModelPluck[]} - Allowed pluck descriptors.
1005
1027
  */
1006
- function validatePluckDefinitions({modelClass, pluck}) {
1028
+ function assertPluckDefinitionsAllowed({modelClass, pluck}) {
1007
1029
  return pluck.map((pluckEntry) => {
1008
1030
  const targetModelClass = frontendModelPluckTargetModelClass(modelClass, pluckEntry.path)
1009
1031
  const targetAttributes = frontendModelResourceAttributes(targetModelClass)
@@ -1036,7 +1058,7 @@ function serializeFindConditions(conditions) {
1036
1058
  * Runs normalize integer argument.
1037
1059
  * @param {?} value - Candidate integer value.
1038
1060
  * @param {string} argumentName - Argument name for errors.
1039
- * @param {object} options - Validation options.
1061
+ * @param {object} options - Integer options.
1040
1062
  * @param {number} options.min - Minimum allowed value.
1041
1063
  * @returns {number} - Normalized integer value.
1042
1064
  */
@@ -1961,7 +1983,7 @@ export default class FrontendModelQuery {
1961
1983
  }
1962
1984
 
1963
1985
  const normalizedPluck = normalizePluck(columns.length === 1 ? columns[0] : columns)
1964
- const validatedPluck = validatePluckDefinitions({
1986
+ const allowedPluck = assertPluckDefinitionsAllowed({
1965
1987
  modelClass: this.modelClass,
1966
1988
  pluck: normalizedPluck
1967
1989
  })
@@ -1973,7 +1995,7 @@ export default class FrontendModelQuery {
1973
1995
  ...this.sortPayload(),
1974
1996
  ...this.wherePayload(),
1975
1997
  ...this.paginationPayload(),
1976
- pluck: validatedPluck
1998
+ pluck: allowedPluck
1977
1999
  })
1978
2000
 
1979
2001
  if (!response || typeof response !== "object") {
@@ -4,6 +4,28 @@ import * as inflection from "inflection"
4
4
  import {isPlainObject} from "is-plain-object"
5
5
  import {resolveFrontendModelClass} from "../frontend-models/model-registry.js"
6
6
 
7
+ /** Error raised when a Ransack descriptor is malformed. */
8
+ export class RansackQueryError extends Error {
9
+ /**
10
+ * Creates a Ransack query error.
11
+ * @param {string} message - Error message.
12
+ */
13
+ constructor(message) {
14
+ super(message)
15
+
16
+ this.name = "RansackQueryError"
17
+ }
18
+ }
19
+
20
+ /**
21
+ * Builds a Ransack query error.
22
+ * @param {string} message - Error message.
23
+ * @returns {RansackQueryError} - Ransack query error.
24
+ */
25
+ function ransackQueryError(message) {
26
+ return new RansackQueryError(message)
27
+ }
28
+
7
29
  /**
8
30
  * RansackPredicate type.
9
31
  * @typedef {"cont" | "end" | "eq" | "gt" | "gteq" | "in" | "lt" | "lteq" | "not_eq" | "not_in" | "null" | "start"} RansackPredicate
@@ -76,7 +98,7 @@ export function normalizeRansackParams(modelClass, params) {
76
98
  */
77
99
  export function normalizeRansackGroup(modelClass, params) {
78
100
  if (!isPlainObject(params)) {
79
- throw new Error(`ransack params must be a plain object, got: ${typeof params}`)
101
+ throw ransackQueryError(`ransack params must be a plain object, got: ${typeof params}`)
80
102
  }
81
103
 
82
104
  /**
@@ -124,7 +146,7 @@ function normalizeSimpleRansackCondition({key, modelClass, rawValue}) {
124
146
  const parsedKey = parseRansackKey(key)
125
147
 
126
148
  if (!parsedKey) {
127
- throw new Error(`Unsupported ransack predicate in key: ${key}`)
149
+ throw ransackQueryError(`Unsupported ransack predicate in key: ${key}`)
128
150
  }
129
151
 
130
152
  const value = normalizeRansackValue({
@@ -159,17 +181,17 @@ function normalizeAdvancedRansackConditions({modelClass, value}) {
159
181
 
160
182
  for (const entry of normalizeRansackCollection(value, "conditions")) {
161
183
  if (!isPlainObject(entry)) {
162
- throw new Error(`Ransack condition entries must be plain objects, got: ${typeof entry}`)
184
+ throw ransackQueryError(`Ransack condition entries must be plain objects, got: ${typeof entry}`)
163
185
  }
164
186
 
165
187
  const predicateValue = entry.p
166
188
 
167
189
  if (typeof predicateValue !== "string") {
168
- throw new Error("Ransack condition predicate must be a string")
190
+ throw ransackQueryError("Ransack condition predicate must be a string")
169
191
  }
170
192
 
171
193
  if (!supportedPredicates.includes(predicateValue)) {
172
- throw new Error(`Unsupported ransack predicate in condition: ${predicateValue}`)
194
+ throw ransackQueryError(`Unsupported ransack predicate in condition: ${predicateValue}`)
173
195
  }
174
196
 
175
197
  const predicate = /** @type {RansackPredicate} */ (predicateValue)
@@ -206,7 +228,7 @@ function normalizeAdvancedRansackGroups({modelClass, value}) {
206
228
 
207
229
  for (const entry of normalizeRansackCollection(value, "groupings")) {
208
230
  if (!isPlainObject(entry)) {
209
- throw new Error(`Ransack grouping entries must be plain objects, got: ${typeof entry}`)
231
+ throw ransackQueryError(`Ransack grouping entries must be plain objects, got: ${typeof entry}`)
210
232
  }
211
233
 
212
234
  groupings.push(normalizeRansackGroup(modelClass, entry))
@@ -225,7 +247,7 @@ function normalizeRansackCombinator(value, defaultValue) {
225
247
  if (value === undefined || value === null || value === "") return defaultValue
226
248
  if (value === "and" || value === "or") return value
227
249
 
228
- throw new Error(`Invalid ransack combinator: ${String(value)}`)
250
+ throw ransackQueryError(`Invalid ransack combinator: ${String(value)}`)
229
251
  }
230
252
 
231
253
  /**
@@ -253,7 +275,7 @@ function normalizeRansackCollection(value, name) {
253
275
  .map((key) => value[key])
254
276
  }
255
277
 
256
- throw new Error(`Ransack ${name} must be an array or object, got: ${typeof value}`)
278
+ throw ransackQueryError(`Ransack ${name} must be an array or object, got: ${typeof value}`)
257
279
  }
258
280
 
259
281
  /**
@@ -292,7 +314,7 @@ function resolveRansackAttributesFromAdvancedValue({modelClass, value}) {
292
314
  }
293
315
 
294
316
  if (attributes.length < 1) {
295
- throw new Error("Ransack condition must include at least one attribute")
317
+ throw ransackQueryError("Ransack condition must include at least one attribute")
296
318
  }
297
319
 
298
320
  return attributes
@@ -314,7 +336,7 @@ function normalizeAdvancedAttributeValues(value) {
314
336
  return normalizeRansackCollection(value, "attributes").map((entry) => normalizeAdvancedAttributeValue(entry))
315
337
  }
316
338
 
317
- throw new Error(`Ransack condition attributes must be strings, arrays, or objects, got: ${typeof value}`)
339
+ throw ransackQueryError(`Ransack condition attributes must be strings, arrays, or objects, got: ${typeof value}`)
318
340
  }
319
341
 
320
342
  /**
@@ -329,7 +351,7 @@ function normalizeAdvancedAttributeValue(value) {
329
351
  return value.name
330
352
  }
331
353
 
332
- throw new Error(`Ransack condition attribute entries must be strings or {name}, got: ${typeof value}`)
354
+ throw ransackQueryError(`Ransack condition attribute entries must be strings or {name}, got: ${typeof value}`)
333
355
  }
334
356
 
335
357
  /**
@@ -346,7 +368,7 @@ function resolveRansackAttributes({modelClass, value}) {
346
368
  const attributeName = resolveAttributeName({modelClass: targetModelClass, value: resolvedPath.attributeValue})
347
369
 
348
370
  if (!attributeName) {
349
- throw new Error(`Unknown ransack attribute "${resolvedPath.attributeValue}" for ${targetModelClass.name}`)
371
+ throw ransackQueryError(`Unknown ransack attribute "${resolvedPath.attributeValue}" for ${targetModelClass.name}`)
350
372
  }
351
373
 
352
374
  return {
@@ -370,7 +392,7 @@ function modelClassAtPath({modelClass, path}) {
370
392
  const relationship = relationshipEntries(currentModelClass)[relationshipName]
371
393
 
372
394
  if (!relationship) {
373
- throw new Error(`Unknown ransack relationship "${relationshipName}" for ${currentModelClass.name}`)
395
+ throw ransackQueryError(`Unknown ransack relationship "${relationshipName}" for ${currentModelClass.name}`)
374
396
  }
375
397
 
376
398
  currentModelClass = relationship.targetModelClass
@@ -412,7 +434,7 @@ function resolveRansackPath({modelClass, value}) {
412
434
  }
413
435
 
414
436
  if (remainingValue.length < 1) {
415
- throw new Error(`Invalid ransack key path: ${value}`)
437
+ throw ransackQueryError(`Invalid ransack key path: ${value}`)
416
438
  }
417
439
 
418
440
  return {
@@ -648,7 +670,7 @@ function parseRansackKey(key) {
648
670
  const pathValue = key.slice(0, key.length - suffix.length)
649
671
 
650
672
  if (pathValue.length < 1) {
651
- throw new Error(`Invalid ransack key: ${key}`)
673
+ throw ransackQueryError(`Invalid ransack key: ${key}`)
652
674
  }
653
675
 
654
676
  return {
@@ -665,7 +687,7 @@ function parseRansackKey(key) {
665
687
  const pathValue = key.slice(0, key.length - camelSuffix.length)
666
688
 
667
689
  if (pathValue.length < 1) {
668
- throw new Error(`Invalid ransack key: ${key}`)
690
+ throw ransackQueryError(`Invalid ransack key: ${key}`)
669
691
  }
670
692
 
671
693
  return {
@@ -750,7 +772,7 @@ function normalizeRansackBoolean(value) {
750
772
  if (ransackFalseValues.has(value)) return false
751
773
  if (ransackValueIsBlank(value)) return null
752
774
 
753
- throw new Error(`Invalid ransack boolean value: ${String(value)}`)
775
+ throw ransackQueryError(`Invalid ransack boolean value: ${String(value)}`)
754
776
  }
755
777
 
756
778
  /**
@@ -789,10 +811,10 @@ function normalizeRansackArray(value) {
789
811
  */
790
812
 
791
813
  /**
792
- * Parses and validates a ransack `s` sort string against model attributes.
793
- * @param {RansackModelClass} modelClass - Model class for attribute validation.
814
+ * Parses a ransack `s` sort string against model attributes.
815
+ * @param {RansackModelClass} modelClass - Model class for attribute lookup.
794
816
  * @param {string} sortString - Ransack sort string (e.g., "name asc" or "name asc, createdAt desc").
795
- * @returns {RansackSort[]} - Validated sort definitions.
817
+ * @returns {RansackSort[]} - Parsed sort definitions.
796
818
  */
797
819
  export function parseRansackSort(modelClass, sortString) {
798
820
  const segments = sortString.split(",").map((segment) => segment.trim()).filter((segment) => segment.length > 0)
@@ -808,13 +830,13 @@ export function parseRansackSort(modelClass, sortString) {
808
830
  const directionCandidate = parts.length > 1 ? parts[1].toLowerCase() : "asc"
809
831
 
810
832
  if (directionCandidate !== "asc" && directionCandidate !== "desc") {
811
- throw new Error(`Invalid ransack sort direction "${directionCandidate}" in: ${segment}`)
833
+ throw ransackQueryError(`Invalid ransack sort direction "${directionCandidate}" in: ${segment}`)
812
834
  }
813
835
 
814
836
  const resolvedAttribute = resolveAttributeName({modelClass, value: columnCandidate})
815
837
 
816
838
  if (!resolvedAttribute) {
817
- throw new Error(`Unknown ransack sort attribute "${columnCandidate}" for ${modelClass.name}`)
839
+ throw ransackQueryError(`Unknown ransack sort attribute "${columnCandidate}" for ${modelClass.name}`)
818
840
  }
819
841
 
820
842
  sorts.push({attribute: resolvedAttribute, direction: directionCandidate})