@restura/core 1.9.0 → 1.9.2

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.d.ts CHANGED
@@ -1130,6 +1130,10 @@ declare class PsqlEngine extends SqlEngine {
1130
1130
  private readonly INITIAL_RECONNECT_DELAY;
1131
1131
  constructor(psqlConnectionPool: PsqlPool, shouldListenForDbTriggers?: boolean, scratchDatabaseSuffix?: string);
1132
1132
  close(): Promise<void>;
1133
+ /**
1134
+ * Setup the return types for the PostgreSQL connection.
1135
+ * For example return DATE as a string instead of a Date object and BIGINT as a number instead of a string.
1136
+ */
1133
1137
  private setupPgReturnTypes;
1134
1138
  private reconnectTriggerClient;
1135
1139
  private listenForDbTriggers;
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":
@@ -2576,15 +2608,25 @@ var PsqlEngine = class extends SqlEngine {
2576
2608
  await this.triggerClient.end();
2577
2609
  }
2578
2610
  }
2611
+ /**
2612
+ * Setup the return types for the PostgreSQL connection.
2613
+ * For example return DATE as a string instead of a Date object and BIGINT as a number instead of a string.
2614
+ */
2579
2615
  setupPgReturnTypes() {
2580
- const TIMESTAMPTZ_OID = 1184;
2581
- types.setTypeParser(TIMESTAMPTZ_OID, (val) => {
2582
- return val === null ? null : new Date(val).toISOString();
2583
- });
2584
- const BIGINT_OID = 20;
2585
- types.setTypeParser(BIGINT_OID, (val) => {
2586
- return val === null ? null : Number(val);
2587
- });
2616
+ const PG_TYPE_OID = {
2617
+ BIGINT: 20,
2618
+ DATE: 1082,
2619
+ TIME: 1083,
2620
+ TIMESTAMP: 1114,
2621
+ TIMESTAMPTZ: 1184,
2622
+ TIMETZ: 1266
2623
+ };
2624
+ types.setTypeParser(PG_TYPE_OID.BIGINT, (val) => val === null ? null : Number(val));
2625
+ types.setTypeParser(PG_TYPE_OID.DATE, (val) => val);
2626
+ types.setTypeParser(PG_TYPE_OID.TIME, (val) => val);
2627
+ types.setTypeParser(PG_TYPE_OID.TIMETZ, (val) => val);
2628
+ types.setTypeParser(PG_TYPE_OID.TIMESTAMP, (val) => val === null ? null : new Date(val).toISOString());
2629
+ types.setTypeParser(PG_TYPE_OID.TIMESTAMPTZ, (val) => val === null ? null : new Date(val).toISOString());
2588
2630
  }
2589
2631
  async reconnectTriggerClient() {
2590
2632
  if (this.reconnectAttempts >= this.MAX_RECONNECT_ATTEMPTS) {