@restura/core 1.4.0 → 1.5.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
@@ -878,7 +878,7 @@ declare namespace Restura {
878
878
  }
879
879
 
880
880
  // src/restura/restura.ts
881
- import { ObjectUtils as ObjectUtils5, StringUtils as StringUtils3 } from "@redskytech/core-utils";
881
+ import { ObjectUtils as ObjectUtils4, StringUtils as StringUtils3 } from "@redskytech/core-utils";
882
882
  import { config as config2 } from "@restura/internal";
883
883
 
884
884
  // ../../node_modules/.pnpm/autobind-decorator@2.4.0/node_modules/autobind-decorator/lib/esm/index.js
@@ -1093,7 +1093,120 @@ import fs2 from "fs";
1093
1093
  import path2, { resolve } from "path";
1094
1094
  import tmp from "tmp";
1095
1095
  import * as TJS from "typescript-json-schema";
1096
- function customTypeValidationGenerator(currentSchema) {
1096
+
1097
+ // src/restura/generators/schemaGeneratorUtils.ts
1098
+ function buildRouteSchema(requestParams) {
1099
+ const properties = {};
1100
+ const required = [];
1101
+ for (const param of requestParams) {
1102
+ if (param.required) {
1103
+ required.push(param.name);
1104
+ }
1105
+ const propertySchema = buildPropertySchemaFromRequest(param);
1106
+ properties[param.name] = propertySchema;
1107
+ }
1108
+ return {
1109
+ type: "object",
1110
+ properties,
1111
+ ...required.length > 0 && { required },
1112
+ // Only include if not empty
1113
+ additionalProperties: false
1114
+ };
1115
+ }
1116
+ function buildPropertySchemaFromRequest(param) {
1117
+ const propertySchema = {};
1118
+ const typeCheckValidator = param.validator.find((v) => v.type === "TYPE_CHECK");
1119
+ const oneOfValidator = param.validator.find((v) => v.type === "ONE_OF");
1120
+ if (oneOfValidator && Array.isArray(oneOfValidator.value)) {
1121
+ propertySchema.enum = oneOfValidator.value;
1122
+ if (!typeCheckValidator && oneOfValidator.value.length > 0) {
1123
+ const firstValue = oneOfValidator.value[0];
1124
+ propertySchema.type = typeof firstValue === "number" ? "number" : "string";
1125
+ return propertySchema;
1126
+ }
1127
+ }
1128
+ if (!typeCheckValidator) {
1129
+ return propertySchema;
1130
+ }
1131
+ const typeValue = typeCheckValidator.value;
1132
+ if (typeof typeValue === "string" && typeValue.endsWith("[]")) {
1133
+ const itemType = typeValue.replace("[]", "");
1134
+ if (param.isNullable) {
1135
+ propertySchema.type = ["array", "null"];
1136
+ } else {
1137
+ propertySchema.type = "array";
1138
+ }
1139
+ propertySchema.items = {
1140
+ type: mapTypeToJsonSchemaType(itemType)
1141
+ };
1142
+ applyArrayValidators(propertySchema, param);
1143
+ } else {
1144
+ if (param.isNullable) {
1145
+ propertySchema.type = [mapTypeToJsonSchemaType(typeValue), "null"];
1146
+ } else {
1147
+ propertySchema.type = mapTypeToJsonSchemaType(typeValue);
1148
+ }
1149
+ const type = propertySchema.type;
1150
+ const isNumericType = type === "number" || type === "integer" || Array.isArray(type) && (type.includes("number") || type.includes("integer"));
1151
+ const isStringType = type === "string" || Array.isArray(type) && type.includes("string");
1152
+ if (isNumericType) {
1153
+ applyNumericValidators(propertySchema, param);
1154
+ } else if (isStringType) {
1155
+ applyStringValidators(propertySchema, param);
1156
+ }
1157
+ }
1158
+ return propertySchema;
1159
+ }
1160
+ function mapTypeToJsonSchemaType(type) {
1161
+ if (typeof type !== "string") {
1162
+ throw new Error(`Invalid type for JSON Schema mapping: ${type}`);
1163
+ }
1164
+ switch (type) {
1165
+ case "number":
1166
+ return "number";
1167
+ case "string":
1168
+ return "string";
1169
+ case "boolean":
1170
+ return "boolean";
1171
+ case "object":
1172
+ return "object";
1173
+ default:
1174
+ throw new Error(`Unknown type: ${type}`);
1175
+ }
1176
+ }
1177
+ function applyNumericValidators(propertySchema, param) {
1178
+ for (const validator of param.validator) {
1179
+ if (validator.type === "MIN" && typeof validator.value === "number") {
1180
+ propertySchema.minimum = validator.value;
1181
+ }
1182
+ if (validator.type === "MAX" && typeof validator.value === "number") {
1183
+ propertySchema.maximum = validator.value;
1184
+ }
1185
+ }
1186
+ }
1187
+ function applyStringValidators(propertySchema, param) {
1188
+ for (const validator of param.validator) {
1189
+ if (validator.type === "MIN" && typeof validator.value === "number") {
1190
+ propertySchema.minLength = validator.value;
1191
+ }
1192
+ if (validator.type === "MAX" && typeof validator.value === "number") {
1193
+ propertySchema.maxLength = validator.value;
1194
+ }
1195
+ }
1196
+ }
1197
+ function applyArrayValidators(propertySchema, param) {
1198
+ for (const validator of param.validator) {
1199
+ if (validator.type === "MIN" && typeof validator.value === "number") {
1200
+ propertySchema.minItems = validator.value;
1201
+ }
1202
+ if (validator.type === "MAX" && typeof validator.value === "number") {
1203
+ propertySchema.maxItems = validator.value;
1204
+ }
1205
+ }
1206
+ }
1207
+
1208
+ // src/restura/generators/customTypeValidationGenerator.ts
1209
+ function customTypeValidationGenerator(currentSchema, ignoreGeneratedTypes = false) {
1097
1210
  const schemaObject = {};
1098
1211
  const customInterfaceNames = currentSchema.customTypes.map((customType) => {
1099
1212
  const matches = customType.match(/(?<=interface\s)(\w+)|(?<=type\s)(\w+)/g);
@@ -1111,9 +1224,11 @@ function customTypeValidationGenerator(currentSchema) {
1111
1224
  const program = TJS.getProgramFromFiles(
1112
1225
  [
1113
1226
  resolve(temporaryFile.name),
1114
- path2.join(restura.resturaConfig.generatedTypesPath, "restura.d.ts"),
1115
- path2.join(restura.resturaConfig.generatedTypesPath, "models.d.ts"),
1116
- path2.join(restura.resturaConfig.generatedTypesPath, "api.d.ts")
1227
+ ...ignoreGeneratedTypes ? [] : [
1228
+ path2.join(restura.resturaConfig.generatedTypesPath, "restura.d.ts"),
1229
+ path2.join(restura.resturaConfig.generatedTypesPath, "models.d.ts"),
1230
+ path2.join(restura.resturaConfig.generatedTypesPath, "api.d.ts")
1231
+ ]
1117
1232
  ],
1118
1233
  compilerOptions
1119
1234
  );
@@ -1124,6 +1239,35 @@ function customTypeValidationGenerator(currentSchema) {
1124
1239
  schemaObject[item] = ddlSchema || {};
1125
1240
  });
1126
1241
  temporaryFile.removeCallback();
1242
+ for (const endpoint of currentSchema.endpoints) {
1243
+ for (const route of endpoint.routes) {
1244
+ if (route.type !== "CUSTOM_ONE" && route.type !== "CUSTOM_ARRAY" && route.type !== "CUSTOM_PAGED") continue;
1245
+ if (!route.request || !Array.isArray(route.request)) continue;
1246
+ const routeKey = `${route.method}:${route.path}`;
1247
+ schemaObject[routeKey] = buildRouteSchema(route.request);
1248
+ }
1249
+ }
1250
+ return schemaObject;
1251
+ }
1252
+
1253
+ // src/restura/generators/standardTypeValidationGenerator.ts
1254
+ function standardTypeValidationGenerator(currentSchema) {
1255
+ const schemaObject = {};
1256
+ for (const endpoint of currentSchema.endpoints) {
1257
+ for (const route of endpoint.routes) {
1258
+ if (route.type !== "ONE" && route.type !== "ARRAY" && route.type !== "PAGED") continue;
1259
+ const routeKey = `${route.method}:${route.path}`;
1260
+ if (!route.request || route.request.length === 0) {
1261
+ schemaObject[routeKey] = {
1262
+ type: "object",
1263
+ properties: {},
1264
+ additionalProperties: false
1265
+ };
1266
+ } else {
1267
+ schemaObject[routeKey] = buildRouteSchema(route.request);
1268
+ }
1269
+ }
1270
+ }
1127
1271
  return schemaObject;
1128
1272
  }
1129
1273
 
@@ -1496,174 +1640,41 @@ async function isSchemaValid(schemaToCheck) {
1496
1640
  }
1497
1641
 
1498
1642
  // src/restura/validators/requestValidator.ts
1499
- import { ObjectUtils as ObjectUtils2 } from "@redskytech/core-utils";
1500
1643
  import jsonschema from "jsonschema";
1501
- import { z as z4 } from "zod";
1502
-
1503
- // src/restura/utils/utils.ts
1504
- function addQuotesToStrings(variable) {
1505
- if (typeof variable === "string") {
1506
- return `'${variable}'`;
1507
- } else if (Array.isArray(variable)) {
1508
- const arrayWithQuotes = variable.map(addQuotesToStrings);
1509
- return arrayWithQuotes;
1510
- } else {
1511
- return variable;
1512
- }
1513
- }
1514
- function sortObjectKeysAlphabetically(obj) {
1515
- if (Array.isArray(obj)) {
1516
- return obj.map(sortObjectKeysAlphabetically);
1517
- } else if (obj !== null && typeof obj === "object") {
1518
- return Object.keys(obj).sort().reduce((sorted, key) => {
1519
- sorted[key] = sortObjectKeysAlphabetically(obj[key]);
1520
- return sorted;
1521
- }, {});
1522
- }
1523
- return obj;
1524
- }
1525
-
1526
- // src/restura/validators/requestValidator.ts
1527
- function requestValidator(req, routeData, validationSchema) {
1528
- const requestData = getRequestData(req);
1529
- req.data = requestData;
1530
- if (routeData.request === void 0) {
1531
- if (routeData.type !== "CUSTOM_ONE" && routeData.type !== "CUSTOM_ARRAY" && routeData.type !== "CUSTOM_PAGED")
1532
- throw new RsError("BAD_REQUEST", `No request parameters provided for standard request.`);
1644
+ function requestValidator(req, routeData, customValidationSchema, standardValidationSchema) {
1645
+ let schemaForCoercion;
1646
+ if (routeData.type === "ONE" || routeData.type === "ARRAY" || routeData.type === "PAGED") {
1647
+ const routeKey = `${routeData.method}:${routeData.path}`;
1648
+ schemaForCoercion = standardValidationSchema[routeKey];
1649
+ if (!schemaForCoercion) {
1650
+ throw new RsError("BAD_REQUEST", `No schema found for standard request route: ${routeKey}.`);
1651
+ }
1652
+ } else if (routeData.type === "CUSTOM_ONE" || routeData.type === "CUSTOM_ARRAY" || routeData.type === "CUSTOM_PAGED") {
1533
1653
  if (!routeData.responseType) throw new RsError("BAD_REQUEST", `No response type defined for custom request.`);
1534
- if (!routeData.requestType) throw new RsError("BAD_REQUEST", `No request type defined for custom request.`);
1535
- const currentInterface = validationSchema[routeData.requestType];
1536
- const validator = new jsonschema.Validator();
1537
- const strictSchema = {
1654
+ if (!routeData.requestType && !routeData.request)
1655
+ throw new RsError("BAD_REQUEST", `No request type defined for custom request.`);
1656
+ const routeKey = `${routeData.method}:${routeData.path}`;
1657
+ const currentInterface = customValidationSchema[routeData.requestType || routeKey];
1658
+ schemaForCoercion = {
1538
1659
  ...currentInterface,
1539
1660
  additionalProperties: false
1540
1661
  };
1541
- const executeValidation = validator.validate(req.data, strictSchema);
1542
- if (!executeValidation.valid) {
1543
- throw new RsError(
1544
- "BAD_REQUEST",
1545
- `Request custom setup has failed the following check: (${executeValidation.errors})`
1546
- );
1547
- }
1548
- return;
1549
- }
1550
- Object.keys(req.data).forEach((requestParamName) => {
1551
- const requestParam = routeData.request.find((param) => param.name === requestParamName);
1552
- if (!requestParam) {
1553
- throw new RsError("BAD_REQUEST", `Request param (${requestParamName}) is not allowed`);
1554
- }
1555
- });
1556
- routeData.request.forEach((requestParam) => {
1557
- const requestValue = requestData[requestParam.name];
1558
- if (requestParam.required && requestValue === void 0)
1559
- throw new RsError("BAD_REQUEST", `Request param (${requestParam.name}) is required but missing`);
1560
- else if (!requestParam.required && requestValue === void 0) return;
1561
- validateRequestSingleParam(requestValue, requestParam);
1562
- });
1563
- }
1564
- function validateRequestSingleParam(requestValue, requestParam) {
1565
- if (requestParam.isNullable && requestValue === null) return;
1566
- requestParam.validator.forEach((validator) => {
1567
- switch (validator.type) {
1568
- case "TYPE_CHECK":
1569
- performTypeCheck(requestValue, validator, requestParam.name);
1570
- break;
1571
- case "MIN":
1572
- performMinCheck(requestValue, validator, requestParam.name);
1573
- break;
1574
- case "MAX":
1575
- performMaxCheck(requestValue, validator, requestParam.name);
1576
- break;
1577
- case "ONE_OF":
1578
- performOneOfCheck(requestValue, validator, requestParam.name);
1579
- break;
1580
- }
1581
- });
1582
- }
1583
- function isValidType(type, requestValue) {
1584
- try {
1585
- expectValidType(type, requestValue);
1586
- return true;
1587
- } catch {
1588
- return false;
1589
- }
1590
- }
1591
- function expectValidType(type, requestValue) {
1592
- if (type === "number") {
1593
- return z4.number().parse(requestValue);
1594
- }
1595
- if (type === "string") {
1596
- return z4.string().parse(requestValue);
1597
- }
1598
- if (type === "boolean") {
1599
- return z4.boolean().parse(requestValue);
1600
- }
1601
- if (type === "string[]") {
1602
- return z4.array(z4.string()).parse(requestValue);
1603
- }
1604
- if (type === "number[]") {
1605
- return z4.array(z4.number()).parse(requestValue);
1606
- }
1607
- if (type === "any[]") {
1608
- return z4.array(z4.any()).parse(requestValue);
1609
- }
1610
- if (type === "object") {
1611
- return z4.object({}).strict().parse(requestValue);
1612
- }
1613
- }
1614
- function performTypeCheck(requestValue, validator, requestParamName) {
1615
- if (!isValidType(validator.value, requestValue)) {
1616
- throw new RsError(
1617
- "BAD_REQUEST",
1618
- `Request param (${requestParamName}) with value (${addQuotesToStrings(requestValue)}) is not of type (${validator.value})`
1619
- );
1662
+ } else {
1663
+ throw new RsError("BAD_REQUEST", `Invalid route type: ${routeData.type}`);
1620
1664
  }
1621
- try {
1622
- validatorDataSchemeValue.parse(validator.value);
1623
- } catch {
1624
- throw new RsError("SCHEMA_ERROR", `Schema validator value (${validator.value}) is not a valid type`);
1665
+ const requestData = getRequestData(req, schemaForCoercion);
1666
+ req.data = requestData;
1667
+ const validator = new jsonschema.Validator();
1668
+ const executeValidation = validator.validate(req.data, schemaForCoercion);
1669
+ if (!executeValidation.valid) {
1670
+ const errorMessages = executeValidation.errors.map((err) => {
1671
+ const property = err.property.replace("instance.", "");
1672
+ return `${property}: ${err.message}`;
1673
+ }).join(", ");
1674
+ throw new RsError("BAD_REQUEST", `Request validation failed: ${errorMessages}`);
1625
1675
  }
1626
1676
  }
1627
- function expectOnlyNumbers(requestValue, validator, requestParamName) {
1628
- if (!isValueNumber(requestValue))
1629
- throw new RsError(
1630
- "BAD_REQUEST",
1631
- `Request param (${requestParamName}) with value (${requestValue}) is not of type number`
1632
- );
1633
- if (!isValueNumber(validator.value))
1634
- throw new RsError("SCHEMA_ERROR", `Schema validator value (${validator.value} is not of type number`);
1635
- }
1636
- function performMinCheck(requestValue, validator, requestParamName) {
1637
- expectOnlyNumbers(requestValue, validator, requestParamName);
1638
- if (requestValue < validator.value)
1639
- throw new RsError(
1640
- "BAD_REQUEST",
1641
- `Request param (${requestParamName}) with value (${requestValue}) is less than (${validator.value})`
1642
- );
1643
- }
1644
- function performMaxCheck(requestValue, validator, requestParamName) {
1645
- expectOnlyNumbers(requestValue, validator, requestParamName);
1646
- if (requestValue > validator.value)
1647
- throw new RsError(
1648
- "BAD_REQUEST",
1649
- `Request param (${requestParamName}) with value (${requestValue}) is more than (${validator.value})`
1650
- );
1651
- }
1652
- function performOneOfCheck(requestValue, validator, requestParamName) {
1653
- if (!ObjectUtils2.isArrayWithData(validator.value))
1654
- throw new RsError("SCHEMA_ERROR", `Schema validator value (${validator.value}) is not of type array`);
1655
- if (typeof requestValue === "object")
1656
- throw new RsError("BAD_REQUEST", `Request param (${requestParamName}) is not of type string or number`);
1657
- if (!validator.value.includes(requestValue))
1658
- throw new RsError(
1659
- "BAD_REQUEST",
1660
- `Request param (${requestParamName}) with value (${requestValue}) is not one of (${validator.value.join(", ")})`
1661
- );
1662
- }
1663
- function isValueNumber(value) {
1664
- return !isNaN(Number(value));
1665
- }
1666
- function getRequestData(req) {
1677
+ function getRequestData(req, schema) {
1667
1678
  let body = "";
1668
1679
  if (req.method === "GET" || req.method === "DELETE") {
1669
1680
  body = "query";
@@ -1671,50 +1682,74 @@ function getRequestData(req) {
1671
1682
  body = "body";
1672
1683
  }
1673
1684
  const bodyData = req[body];
1674
- if (bodyData && body === "query") {
1675
- const normalizedData = {};
1676
- for (const attr in bodyData) {
1677
- if (attr.includes("[]") && !(bodyData[attr] instanceof Array)) {
1678
- bodyData[attr] = [bodyData[attr]];
1685
+ if (bodyData && body === "query" && schema) {
1686
+ return coerceBySchema(bodyData, schema);
1687
+ }
1688
+ return bodyData;
1689
+ }
1690
+ function coerceBySchema(data, schema) {
1691
+ const normalized = {};
1692
+ const properties = schema.properties || {};
1693
+ for (const attr in data) {
1694
+ const cleanAttr = attr.replace(/\[\]$/, "");
1695
+ const isArrayNotation = attr.includes("[]");
1696
+ let value = data[attr];
1697
+ const propertySchema = properties[cleanAttr];
1698
+ if (isArrayNotation && !Array.isArray(value)) {
1699
+ value = [value];
1700
+ }
1701
+ if (!propertySchema) {
1702
+ normalized[cleanAttr] = value;
1703
+ continue;
1704
+ }
1705
+ if (Array.isArray(value)) {
1706
+ const itemSchema = Array.isArray(propertySchema.items) ? propertySchema.items[0] : propertySchema.items || { type: "string" };
1707
+ normalized[cleanAttr] = value.map((item) => coerceValue(item, itemSchema));
1708
+ } else {
1709
+ normalized[cleanAttr] = coerceValue(value, propertySchema);
1710
+ }
1711
+ }
1712
+ return normalized;
1713
+ }
1714
+ function coerceValue(value, propertySchema) {
1715
+ if (value === void 0 || value === null) {
1716
+ return value;
1717
+ }
1718
+ const targetType = Array.isArray(propertySchema.type) ? propertySchema.type[0] : propertySchema.type;
1719
+ if (value === "") {
1720
+ return targetType === "string" ? "" : void 0;
1721
+ }
1722
+ switch (targetType) {
1723
+ case "number":
1724
+ case "integer":
1725
+ const num = Number(value);
1726
+ return isNaN(num) ? value : num;
1727
+ case "boolean":
1728
+ if (value === "true") return true;
1729
+ if (value === "false") return false;
1730
+ if (typeof value === "string") {
1731
+ return value === "true" || value === "1";
1679
1732
  }
1680
- const cleanAttr = attr.replace(/\[\]$/, "");
1681
- if (bodyData[attr] instanceof Array) {
1682
- const parsedList = bodyData[attr].map((value) => {
1683
- if (value === "true") return true;
1684
- if (value === "false") return false;
1685
- if (value === void 0) return void 0;
1686
- if (value === "") return "";
1687
- const parsed = ObjectUtils2.safeParse(value);
1688
- return isNaN(Number(parsed)) ? parsed : Number(parsed);
1689
- });
1690
- normalizedData[cleanAttr] = parsedList;
1691
- } else {
1692
- let value = bodyData[attr];
1693
- if (value === "true") {
1694
- value = true;
1695
- } else if (value === "false") {
1696
- value = false;
1697
- } else if (value === void 0) {
1698
- value = void 0;
1699
- } else if (value === "") {
1700
- value = "";
1701
- } else {
1702
- value = ObjectUtils2.safeParse(value);
1703
- if (!isNaN(Number(value))) {
1704
- value = Number(value);
1705
- }
1733
+ return Boolean(value);
1734
+ case "string":
1735
+ return String(value);
1736
+ case "object":
1737
+ if (typeof value === "string") {
1738
+ try {
1739
+ return JSON.parse(value);
1740
+ } catch {
1741
+ return value;
1706
1742
  }
1707
- normalizedData[cleanAttr] = value;
1708
1743
  }
1709
- }
1710
- return normalizedData;
1744
+ return value;
1745
+ default:
1746
+ return value;
1711
1747
  }
1712
- return bodyData;
1713
1748
  }
1714
1749
 
1715
1750
  // src/restura/middleware/schemaValidation.ts
1716
1751
  async function schemaValidation(req, res, next) {
1717
- req.data = getRequestData(req);
1752
+ req.data = getRequestData(req, {});
1718
1753
  try {
1719
1754
  resturaSchema.parse(req.data);
1720
1755
  next();
@@ -1725,22 +1760,22 @@ async function schemaValidation(req, res, next) {
1725
1760
  }
1726
1761
 
1727
1762
  // src/restura/schemas/resturaConfigSchema.ts
1728
- import { z as z5 } from "zod";
1763
+ import { z as z4 } from "zod";
1729
1764
  var isTsx = process.argv[1]?.endsWith(".ts");
1730
1765
  var isTsNode = process.env.TS_NODE_DEV || process.env.TS_NODE_PROJECT;
1731
1766
  var customApiFolderPath = isTsx || isTsNode ? "/src/api" : "/dist/api";
1732
- var resturaConfigSchema = z5.object({
1733
- authToken: z5.string().min(1, "Missing Restura Auth Token"),
1734
- sendErrorStackTrace: z5.boolean().default(false),
1735
- schemaFilePath: z5.string().default(process.cwd() + "/restura.schema.json"),
1736
- customApiFolderPath: z5.string().default(process.cwd() + customApiFolderPath),
1737
- generatedTypesPath: z5.string().default(process.cwd() + "/src/@types"),
1738
- fileTempCachePath: z5.string().optional(),
1739
- scratchDatabaseSuffix: z5.string().optional()
1767
+ var resturaConfigSchema = z4.object({
1768
+ authToken: z4.string().min(1, "Missing Restura Auth Token"),
1769
+ sendErrorStackTrace: z4.boolean().default(false),
1770
+ schemaFilePath: z4.string().default(process.cwd() + "/restura.schema.json"),
1771
+ customApiFolderPath: z4.string().default(process.cwd() + customApiFolderPath),
1772
+ generatedTypesPath: z4.string().default(process.cwd() + "/src/@types"),
1773
+ fileTempCachePath: z4.string().optional(),
1774
+ scratchDatabaseSuffix: z4.string().optional()
1740
1775
  });
1741
1776
 
1742
1777
  // src/restura/sql/PsqlEngine.ts
1743
- import { ObjectUtils as ObjectUtils4 } from "@redskytech/core-utils";
1778
+ import { ObjectUtils as ObjectUtils3 } from "@redskytech/core-utils";
1744
1779
  import getDiff from "@wmfs/pg-diff-sync";
1745
1780
  import pgInfo from "@wmfs/pg-info";
1746
1781
  import pg2 from "pg";
@@ -1752,7 +1787,7 @@ import pg from "pg";
1752
1787
  import crypto from "crypto";
1753
1788
  import format2 from "pg-format";
1754
1789
  import { format as sqlFormat } from "sql-formatter";
1755
- import { z as z6 } from "zod";
1790
+ import { z as z5 } from "zod";
1756
1791
 
1757
1792
  // src/restura/sql/PsqlUtils.ts
1758
1793
  import format from "pg-format";
@@ -1788,13 +1823,15 @@ function insertObjectQuery(table, obj) {
1788
1823
  INSERT INTO "${table}" (${columns})
1789
1824
  VALUES (${values})
1790
1825
  RETURNING *`;
1791
- query = query.replace(/'(\?)'/, "?");
1826
+ query = query.replace(/'(\?)'/g, "?");
1792
1827
  return query;
1793
1828
  }
1794
1829
  function updateObjectQuery(table, obj, whereStatement, incrementSyncVersion = false) {
1795
1830
  const setArray = [];
1796
1831
  for (const i in obj) {
1797
- setArray.push(`${escapeColumnName(i)} = ` + SQL`${obj[i]}`);
1832
+ let value = SQL`${obj[i]}`;
1833
+ value = value.replace(/'(\?)'/g, "?");
1834
+ setArray.push(`${escapeColumnName(i)} = ` + value);
1798
1835
  }
1799
1836
  if (incrementSyncVersion) {
1800
1837
  setArray.push(`"syncVersion" = "syncVersion" + 1`);
@@ -1804,7 +1841,7 @@ function updateObjectQuery(table, obj, whereStatement, incrementSyncVersion = fa
1804
1841
  SET ${setArray.join(", ")} ${whereStatement}
1805
1842
  RETURNING *`;
1806
1843
  }
1807
- function isValueNumber2(value) {
1844
+ function isValueNumber(value) {
1808
1845
  return !isNaN(Number(value));
1809
1846
  }
1810
1847
  function SQL(strings, ...values) {
@@ -1856,10 +1893,10 @@ var PsqlConnection = class {
1856
1893
  try {
1857
1894
  return zodSchema.parse(result);
1858
1895
  } catch (error) {
1859
- if (error instanceof z6.ZodError) {
1896
+ if (error instanceof z5.ZodError) {
1860
1897
  logger.error("Invalid data returned from database:");
1861
1898
  logger.silly("\n" + JSON.stringify(result, null, 2));
1862
- logger.error("\n" + z6.prettifyError(error));
1899
+ logger.error("\n" + z5.prettifyError(error));
1863
1900
  } else {
1864
1901
  logger.error(error);
1865
1902
  }
@@ -1887,12 +1924,12 @@ var PsqlConnection = class {
1887
1924
  async runQuerySchema(query, params, requesterDetails, zodSchema) {
1888
1925
  const result = await this.runQuery(query, params, requesterDetails);
1889
1926
  try {
1890
- return z6.array(zodSchema).parse(result);
1927
+ return z5.array(zodSchema).parse(result);
1891
1928
  } catch (error) {
1892
- if (error instanceof z6.ZodError) {
1929
+ if (error instanceof z5.ZodError) {
1893
1930
  logger.error("Invalid data returned from database:");
1894
1931
  logger.silly("\n" + JSON.stringify(result, null, 2));
1895
- logger.error("\n" + z6.prettifyError(error));
1932
+ logger.error("\n" + z5.prettifyError(error));
1896
1933
  } else {
1897
1934
  logger.error(error);
1898
1935
  }
@@ -1965,7 +2002,7 @@ var PsqlPool = class extends PsqlConnection {
1965
2002
  };
1966
2003
 
1967
2004
  // src/restura/sql/SqlEngine.ts
1968
- import { ObjectUtils as ObjectUtils3 } from "@redskytech/core-utils";
2005
+ import { ObjectUtils as ObjectUtils2 } from "@redskytech/core-utils";
1969
2006
  var SqlEngine = class {
1970
2007
  async runQueryForRoute(req, routeData, schema) {
1971
2008
  if (!this.canRequesterAccessTable(
@@ -2008,18 +2045,18 @@ var SqlEngine = class {
2008
2045
  const columnSchema = tableSchema.columns.find((item2) => item2.name === columnName);
2009
2046
  if (!columnSchema)
2010
2047
  throw new RsError("SCHEMA_ERROR", `Column ${columnName} not found in table ${tableName}`);
2011
- if (ObjectUtils3.isArrayWithData(columnSchema.roles)) {
2048
+ if (ObjectUtils2.isArrayWithData(columnSchema.roles)) {
2012
2049
  if (!requesterRole) return false;
2013
2050
  return columnSchema.roles.includes(requesterRole);
2014
2051
  }
2015
- if (ObjectUtils3.isArrayWithData(columnSchema.scopes)) {
2052
+ if (ObjectUtils2.isArrayWithData(columnSchema.scopes)) {
2016
2053
  if (!requesterScopes) return false;
2017
2054
  return columnSchema.scopes.every((scope) => requesterScopes.includes(scope));
2018
2055
  }
2019
2056
  return true;
2020
2057
  }
2021
2058
  if (item.subquery) {
2022
- return ObjectUtils3.isArrayWithData(
2059
+ return ObjectUtils2.isArrayWithData(
2023
2060
  item.subquery.properties.filter((nestedItem) => {
2024
2061
  return this.canRequesterAccessColumn(requesterRole, requesterScopes, schema, nestedItem, joins);
2025
2062
  })
@@ -2029,11 +2066,11 @@ var SqlEngine = class {
2029
2066
  }
2030
2067
  canRequesterAccessTable(requesterRole, requesterScopes, schema, tableName) {
2031
2068
  const tableSchema = this.getTableSchema(schema, tableName);
2032
- if (ObjectUtils3.isArrayWithData(tableSchema.roles)) {
2069
+ if (ObjectUtils2.isArrayWithData(tableSchema.roles)) {
2033
2070
  if (!requesterRole) return false;
2034
2071
  return tableSchema.roles.includes(requesterRole);
2035
2072
  }
2036
- if (ObjectUtils3.isArrayWithData(tableSchema.scopes)) {
2073
+ if (ObjectUtils2.isArrayWithData(tableSchema.scopes)) {
2037
2074
  if (!requesterScopes) return false;
2038
2075
  return tableSchema.scopes.some((scope) => requesterScopes.includes(scope));
2039
2076
  }
@@ -2309,7 +2346,7 @@ var PsqlEngine = class extends SqlEngine {
2309
2346
  });
2310
2347
  this.triggerClient.on("notification", async (msg) => {
2311
2348
  if (msg.channel === "insert" || msg.channel === "update" || msg.channel === "delete") {
2312
- const payload = ObjectUtils4.safeParse(msg.payload);
2349
+ const payload = ObjectUtils3.safeParse(msg.payload);
2313
2350
  await this.handleTrigger(payload, msg.channel.toUpperCase());
2314
2351
  }
2315
2352
  });
@@ -2483,7 +2520,7 @@ var PsqlEngine = class extends SqlEngine {
2483
2520
  }
2484
2521
  createNestedSelect(req, schema, item, routeData, sqlParams) {
2485
2522
  if (!item.subquery) return "";
2486
- if (!ObjectUtils4.isArrayWithData(
2523
+ if (!ObjectUtils3.isArrayWithData(
2487
2524
  item.subquery.properties.filter((nestedItem) => {
2488
2525
  return this.canRequesterAccessColumn(
2489
2526
  req.requesterDetails.role,
@@ -2519,7 +2556,7 @@ var PsqlEngine = class extends SqlEngine {
2519
2556
  }
2520
2557
  return `'${nestedItem.name}', ${escapeColumnName(nestedItem.selector)}`;
2521
2558
  }).filter(Boolean).join(", ")}
2522
- ))
2559
+ ))
2523
2560
  FROM
2524
2561
  "${item.subquery.table}"
2525
2562
  ${this.generateJoinStatements(req, item.subquery.joins, item.subquery.table, routeData, schema, sqlParams)}
@@ -2627,7 +2664,7 @@ var PsqlEngine = class extends SqlEngine {
2627
2664
  );
2628
2665
  const [pageResults, totalResponse] = await Promise.all([pagePromise, totalPromise]);
2629
2666
  let total = 0;
2630
- if (ObjectUtils4.isArrayWithData(totalResponse)) {
2667
+ if (ObjectUtils3.isArrayWithData(totalResponse)) {
2631
2668
  total = totalResponse[0].total;
2632
2669
  }
2633
2670
  return { data: pageResults, total };
@@ -2657,14 +2694,9 @@ var PsqlEngine = class extends SqlEngine {
2657
2694
  }
2658
2695
  let incrementSyncVersion = false;
2659
2696
  if (table.columns.find((column) => column.name === "syncVersion")) incrementSyncVersion = true;
2660
- for (const assignment of routeData.assignments) {
2661
- const column = table.columns.find((column2) => column2.name === assignment.name);
2662
- if (!column) continue;
2663
- const assignmentEscaped = escapeColumnName(assignment.name);
2664
- if (SqlUtils.convertDatabaseTypeToTypescript(column.type) === "number")
2665
- bodyNoId[assignmentEscaped] = Number(assignment.value);
2666
- else bodyNoId[assignmentEscaped] = assignment.value;
2667
- }
2697
+ (routeData.assignments || []).forEach((assignment) => {
2698
+ bodyNoId[assignment.name] = this.replaceParamKeywords(assignment.value, routeData, req, sqlParams);
2699
+ });
2668
2700
  let whereClause = this.generateWhereClause(req, routeData.where, routeData, sqlParams);
2669
2701
  const originalWhereClause = whereClause;
2670
2702
  const originalSqlParams = [...sqlParams];
@@ -3088,6 +3120,19 @@ var TempCache = class {
3088
3120
  }
3089
3121
  };
3090
3122
 
3123
+ // src/restura/utils/utils.ts
3124
+ function sortObjectKeysAlphabetically(obj) {
3125
+ if (Array.isArray(obj)) {
3126
+ return obj.map(sortObjectKeysAlphabetically);
3127
+ } else if (obj !== null && typeof obj === "object") {
3128
+ return Object.keys(obj).sort().reduce((sorted, key) => {
3129
+ sorted[key] = sortObjectKeysAlphabetically(obj[key]);
3130
+ return sorted;
3131
+ }, {});
3132
+ }
3133
+ return obj;
3134
+ }
3135
+
3091
3136
  // src/restura/restura.ts
3092
3137
  var ResturaEngine = class {
3093
3138
  // Make public so other modules can access without re-parsing the config
@@ -3106,6 +3151,7 @@ var ResturaEngine = class {
3106
3151
  responseValidator;
3107
3152
  authenticationHandler;
3108
3153
  customTypeValidation;
3154
+ standardTypeValidation;
3109
3155
  psqlConnectionPool;
3110
3156
  psqlEngine;
3111
3157
  /**
@@ -3215,7 +3261,7 @@ var ResturaEngine = class {
3215
3261
  throw new Error("Missing restura schema file");
3216
3262
  }
3217
3263
  const schemaFileData = fs4.readFileSync(this.resturaConfig.schemaFilePath, { encoding: "utf8" });
3218
- const schema = ObjectUtils5.safeParse(schemaFileData);
3264
+ const schema = ObjectUtils4.safeParse(schemaFileData);
3219
3265
  const isValid = await isSchemaValid(schema);
3220
3266
  if (!isValid) {
3221
3267
  logger.error("Schema is not valid");
@@ -3226,6 +3272,7 @@ var ResturaEngine = class {
3226
3272
  async reloadEndpoints() {
3227
3273
  this.schema = await this.getLatestFileSystemSchema();
3228
3274
  this.customTypeValidation = customTypeValidationGenerator(this.schema);
3275
+ this.standardTypeValidation = standardTypeValidationGenerator(this.schema);
3229
3276
  this.resturaRouter = express.Router();
3230
3277
  this.resetPublicEndpoints();
3231
3278
  let routeCount = 0;
@@ -3325,7 +3372,12 @@ var ResturaEngine = class {
3325
3372
  const routeData = this.getRouteData(req.method, req.baseUrl, req.path);
3326
3373
  this.validateAuthorization(req, routeData);
3327
3374
  await this.getMulterFilesIfAny(req, res, routeData);
3328
- requestValidator(req, routeData, this.customTypeValidation);
3375
+ requestValidator(
3376
+ req,
3377
+ routeData,
3378
+ this.customTypeValidation,
3379
+ this.standardTypeValidation
3380
+ );
3329
3381
  if (this.isCustomRoute(routeData)) {
3330
3382
  await this.runCustomRouteLogic(req, res, routeData);
3331
3383
  return;
@@ -3502,7 +3554,7 @@ export {
3502
3554
  filterPsqlParser_default as filterPsqlParser,
3503
3555
  insertObjectQuery,
3504
3556
  isSchemaValid,
3505
- isValueNumber2 as isValueNumber,
3557
+ isValueNumber,
3506
3558
  logger,
3507
3559
  modelGenerator,
3508
3560
  questionMarksToOrderedParams,