@restura/core 1.9.1 → 1.10.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.js CHANGED
@@ -1743,6 +1743,32 @@ function deepResolveSchemaRefs(schema, definitions, seen = /* @__PURE__ */ new S
1743
1743
  function resolveSchemaRef(schema, definitions) {
1744
1744
  return deepResolveSchemaRefs(schema, definitions);
1745
1745
  }
1746
+ function summarizeSubSchema(sub) {
1747
+ if (!sub || typeof sub !== "object") return "unknown";
1748
+ if (sub.type === "object" && sub.properties && typeof sub.properties === "object") {
1749
+ const props = sub.properties;
1750
+ const parts = [];
1751
+ if (props.type && props.type.enum) {
1752
+ const enumVal = props.type.enum[0];
1753
+ parts.push(`type: '${String(enumVal)}'`);
1754
+ }
1755
+ for (const key of Object.keys(props)) {
1756
+ if (key === "type") continue;
1757
+ parts.push(key);
1758
+ }
1759
+ return parts.length ? `{ ${parts.join(", ")} }` : "object";
1760
+ }
1761
+ return "object";
1762
+ }
1763
+ function formatValidationErrorMessage(message, errSchema) {
1764
+ const schema = errSchema;
1765
+ const options = schema?.oneOf ?? schema?.anyOf;
1766
+ if (options && Array.isArray(options) && (message.includes("subschema") || message.includes("any of") || message.includes("exactly one from"))) {
1767
+ const summaries = options.map((sub, i) => `(${i + 1}) ${summarizeSubSchema(sub)}`);
1768
+ return `must be one of: ${summaries.join(" or ")}`;
1769
+ }
1770
+ return message;
1771
+ }
1746
1772
  function requestValidator(req, routeData, customValidationSchema, standardValidationSchema) {
1747
1773
  const routeKey = `${routeData.method}:${routeData.path}`;
1748
1774
  const isCustom = routeData.type === "CUSTOM_ONE" || routeData.type === "CUSTOM_ARRAY" || routeData.type === "CUSTOM_PAGED";
@@ -1780,18 +1806,14 @@ function requestValidator(req, routeData, customValidationSchema, standardValida
1780
1806
  if (!executeValidation.valid) {
1781
1807
  const errorMessages = executeValidation.errors.map((err) => {
1782
1808
  const property = err.property.replace("instance.", "");
1783
- return `${property}: ${err.message}`;
1809
+ const message = formatValidationErrorMessage(err.message, err.schema);
1810
+ return `${property}: ${message}`;
1784
1811
  }).join(", ");
1785
1812
  throw new RsError("BAD_REQUEST", `Request validation failed: ${errorMessages}`);
1786
1813
  }
1787
1814
  }
1788
1815
  function getRequestData(req, schema) {
1789
- let body = "";
1790
- if (req.method === "GET" || req.method === "DELETE") {
1791
- body = "query";
1792
- } else {
1793
- body = "body";
1794
- }
1816
+ const body = req.method === "GET" || req.method === "DELETE" ? "query" : "body";
1795
1817
  const bodyData = req[body];
1796
1818
  if (bodyData && body === "query" && schema) {
1797
1819
  return coerceBySchema(bodyData, schema);
@@ -1830,6 +1852,16 @@ function coerceValue(value, propertySchema) {
1830
1852
  if (value === "") {
1831
1853
  return targetType === "string" ? "" : void 0;
1832
1854
  }
1855
+ const isRefOrUnion = propertySchema.$ref != null || Array.isArray(propertySchema.oneOf) || Array.isArray(propertySchema.anyOf);
1856
+ if (isRefOrUnion && typeof value === "string") {
1857
+ const trimmed = value.trim();
1858
+ if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
1859
+ try {
1860
+ return JSON.parse(value);
1861
+ } catch {
1862
+ }
1863
+ }
1864
+ }
1833
1865
  switch (targetType) {
1834
1866
  case "number":
1835
1867
  case "integer":
@@ -2498,7 +2530,15 @@ NullOperator
2498
2530
  / "null"i { return function(col) { return formatColumn(col) + ' IS NULL'; }; }
2499
2531
 
2500
2532
  OperatorWithValue
2501
- = "in"i _ "," _ val:CastedValueWithPipes { return function(col) { return buildInClauseWithCast(formatColumn(col), val.value, val.cast); }; }
2533
+ = "in"i _ "," _ vals:InValueList cast:TypeCast? { return function(col) {
2534
+ var formattedCol = formatColumn(col);
2535
+ var literals = vals.map(function(v) {
2536
+ var unescaped = unescapeValue(v);
2537
+ var formatted = formatValue(unescaped);
2538
+ return cast ? formatted + '::' + cast : formatted;
2539
+ });
2540
+ return formattedCol + ' IN (' + literals.join(', ') + ')';
2541
+ }; }
2502
2542
  / "ne"i _ "," _ val:CastedValue { return function(col) { return formatColumn(col) + ' <> ' + formatValueWithCast(val.value, val.cast); }; }
2503
2543
  / "gte"i _ "," _ val:CastedValue { return function(col) { return formatColumn(col) + ' >= ' + formatValueWithCast(val.value, val.cast); }; }
2504
2544
  / "gt"i _ "," _ val:CastedValue { return function(col) { return formatColumn(col) + ' > ' + formatValueWithCast(val.value, val.cast); }; }
@@ -2508,6 +2548,26 @@ OperatorWithValue
2508
2548
  / "sw"i _ "," _ val:CastedValue { return function(col) { var formatted = format.literal(unescapeValue(val.value) + '%'); return formatColumn(col) + ' ILIKE ' + (val.cast ? formatted + '::' + val.cast : formatted); }; }
2509
2549
  / "ew"i _ "," _ val:CastedValue { return function(col) { var formatted = format.literal('%' + unescapeValue(val.value)); return formatColumn(col) + ' ILIKE ' + (val.cast ? formatted + '::' + val.cast : formatted); }; }
2510
2550
 
2551
+ InValueList
2552
+ = first:InValue rest:("|" InValue)* {
2553
+ var values = [first];
2554
+ for (var i = 0; i < rest.length; i++) {
2555
+ values.push(rest[i][1]);
2556
+ }
2557
+ return values;
2558
+ }
2559
+
2560
+ InValue
2561
+ = QuotedString
2562
+ / chars:InValueChar+ { return chars.join(''); }
2563
+
2564
+ InValueChar
2565
+ = "\\\\\\\\" { return '\\\\\\\\'; }
2566
+ / "\\\\," { return '\\\\,'; }
2567
+ / "\\\\|" { return '\\\\|'; }
2568
+ / [a-zA-Z0-9_\\-!#/@$%^&*+=<>?~.;'" ]
2569
+ / c:":" !":" { return c; }
2570
+
2511
2571
  CastedValue
2512
2572
  = val:Value cast:TypeCast? { return { value: val, cast: cast }; }
2513
2573
 
@@ -2518,24 +2578,40 @@ TypeCast
2518
2578
  = "::" type:("timestamptz"i / "timestamp"i / "boolean"i / "numeric"i / "bigint"i / "text"i / "date"i / "int"i)
2519
2579
  { return type.toLowerCase(); }
2520
2580
 
2581
+ QuotedString
2582
+ = '"' chars:DoubleQuotedChar* '"' { return chars.join(''); }
2583
+ / "'" chars:SingleQuotedChar* "'" { return chars.join(''); }
2584
+
2585
+ DoubleQuotedChar
2586
+ = '\\\\"' { return '"'; }
2587
+ / '\\\\\\\\' { return '\\\\'; }
2588
+ / [^"\\\\]
2589
+
2590
+ SingleQuotedChar
2591
+ = "\\\\'" { return "'"; }
2592
+ / '\\\\\\\\' { return '\\\\'; }
2593
+ / [^'\\\\]
2594
+
2521
2595
  Value
2522
- = chars:ValueChar+ { return chars.join(''); }
2596
+ = QuotedString
2597
+ / chars:ValueChar+ { return chars.join(''); }
2523
2598
 
2524
2599
  ValueChar
2525
2600
  = "\\\\\\\\" { return '\\\\\\\\'; }
2526
2601
  / "\\\\," { return '\\\\,'; }
2527
2602
  / "\\\\|" { return '\\\\|'; }
2528
- / [^,()\\\\|:]
2603
+ / [a-zA-Z0-9_\\-!#/@$%^&*+=<>?~.;'" ]
2529
2604
  / c:":" !":" { return c; }
2530
2605
 
2531
2606
  ValueWithPipes
2532
- = chars:ValueWithPipesChar+ { return chars.join(''); }
2607
+ = QuotedString
2608
+ / chars:ValueWithPipesChar+ { return chars.join(''); }
2533
2609
 
2534
2610
  ValueWithPipesChar
2535
2611
  = "\\\\\\\\" { return '\\\\\\\\'; }
2536
2612
  / "\\\\," { return '\\\\,'; }
2537
2613
  / "\\\\|" { return '\\\\|'; }
2538
- / [^,()\\\\:]
2614
+ / [a-zA-Z0-9_\\-!#/@$%^&*+=<>?~.;'" |]
2539
2615
  / c:":" !":" { return c; }
2540
2616
  `;
2541
2617
  var fullGrammar = entryGrammar + oldGrammar + newGrammar;