@restura/core 0.1.0-alpha.8 → 0.1.0-alpha.9

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 (38) hide show
  1. package/dist/index.d.mts +133 -13
  2. package/dist/index.d.ts +133 -13
  3. package/dist/index.js +711 -546
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.mjs +700 -543
  6. package/dist/index.mjs.map +1 -1
  7. package/package.json +8 -5
  8. package/dist/acorn-SW5GI5G7.mjs +0 -3016
  9. package/dist/acorn-SW5GI5G7.mjs.map +0 -1
  10. package/dist/angular-FYEH6QOL.mjs +0 -1547
  11. package/dist/angular-FYEH6QOL.mjs.map +0 -1
  12. package/dist/babel-V6GZHMYX.mjs +0 -6911
  13. package/dist/babel-V6GZHMYX.mjs.map +0 -1
  14. package/dist/chunk-TL4KRYOF.mjs +0 -58
  15. package/dist/chunk-TL4KRYOF.mjs.map +0 -1
  16. package/dist/estree-67ZCSSSI.mjs +0 -4396
  17. package/dist/estree-67ZCSSSI.mjs.map +0 -1
  18. package/dist/flow-SJW7PRXX.mjs +0 -26365
  19. package/dist/flow-SJW7PRXX.mjs.map +0 -1
  20. package/dist/glimmer-PF2X22V2.mjs +0 -2995
  21. package/dist/glimmer-PF2X22V2.mjs.map +0 -1
  22. package/dist/graphql-NOJ5HX7K.mjs +0 -1253
  23. package/dist/graphql-NOJ5HX7K.mjs.map +0 -1
  24. package/dist/html-ROPIWVPQ.mjs +0 -2780
  25. package/dist/html-ROPIWVPQ.mjs.map +0 -1
  26. package/dist/index.cjs +0 -34
  27. package/dist/index.cjs.map +0 -1
  28. package/dist/index.d.cts +0 -3
  29. package/dist/markdown-PFYT4MSP.mjs +0 -3423
  30. package/dist/markdown-PFYT4MSP.mjs.map +0 -1
  31. package/dist/meriyah-5NLZXLMA.mjs +0 -2356
  32. package/dist/meriyah-5NLZXLMA.mjs.map +0 -1
  33. package/dist/postcss-ITO6IEN5.mjs +0 -5027
  34. package/dist/postcss-ITO6IEN5.mjs.map +0 -1
  35. package/dist/typescript-6IE7K56Q.mjs +0 -13392
  36. package/dist/typescript-6IE7K56Q.mjs.map +0 -1
  37. package/dist/yaml-DB2OVPLH.mjs +0 -4230
  38. package/dist/yaml-DB2OVPLH.mjs.map +0 -1
package/dist/index.js CHANGED
@@ -67,9 +67,17 @@ var __decorateClass = (decorators, target, key, kind) => {
67
67
  // src/index.ts
68
68
  var src_exports = {};
69
69
  __export(src_exports, {
70
+ HtmlStatusCodes: () => HtmlStatusCodes,
70
71
  PsqlPool: () => PsqlPool,
72
+ RsError: () => RsError,
73
+ SQL: () => SQL,
74
+ escapeColumnName: () => escapeColumnName,
75
+ insertObjectQuery: () => insertObjectQuery,
76
+ isValueNumber: () => isValueNumber2,
71
77
  logger: () => logger,
72
- restura: () => restura
78
+ questionMarksToOrderedParams: () => questionMarksToOrderedParams,
79
+ restura: () => restura,
80
+ updateObjectQuery: () => updateObjectQuery
73
81
  });
74
82
  module.exports = __toCommonJS(src_exports);
75
83
 
@@ -81,12 +89,13 @@ var import_logform = require("logform");
81
89
  // src/config.schema.ts
82
90
  var import_zod = require("zod");
83
91
  var loggerConfigSchema = import_zod.z.object({
84
- level: import_zod.z.enum(["info", "warn", "error", "debug"]).default("info")
92
+ level: import_zod.z.enum(["info", "warn", "error", "debug", "silly"]).default("info")
85
93
  });
86
94
  var resturaConfigSchema = import_zod.z.object({
87
95
  authToken: import_zod.z.string().min(1, "Missing Restura Auth Token"),
88
96
  sendErrorStackTrace: import_zod.z.boolean().default(false),
89
97
  schemaFilePath: import_zod.z.string().default(process.cwd() + "/restura.schema.json"),
98
+ customApiFolderPath: import_zod.z.string().default(process.cwd() + "/dist/api"),
90
99
  generatedTypesPath: import_zod.z.string().default(process.cwd() + "/src/@types")
91
100
  });
92
101
 
@@ -125,6 +134,72 @@ var logger = import_winston.default.createLogger({
125
134
  ]
126
135
  });
127
136
 
137
+ // src/restura/errors.ts
138
+ var HtmlStatusCodes = /* @__PURE__ */ ((HtmlStatusCodes2) => {
139
+ HtmlStatusCodes2[HtmlStatusCodes2["BAD_REQUEST"] = 400] = "BAD_REQUEST";
140
+ HtmlStatusCodes2[HtmlStatusCodes2["UNAUTHORIZED"] = 401] = "UNAUTHORIZED";
141
+ HtmlStatusCodes2[HtmlStatusCodes2["FORBIDDEN"] = 403] = "FORBIDDEN";
142
+ HtmlStatusCodes2[HtmlStatusCodes2["NOT_FOUND"] = 404] = "NOT_FOUND";
143
+ HtmlStatusCodes2[HtmlStatusCodes2["METHOD_NOT_ALLOWED"] = 405] = "METHOD_NOT_ALLOWED";
144
+ HtmlStatusCodes2[HtmlStatusCodes2["CONFLICT"] = 409] = "CONFLICT";
145
+ HtmlStatusCodes2[HtmlStatusCodes2["VERSION_OUT_OF_DATE"] = 418] = "VERSION_OUT_OF_DATE";
146
+ HtmlStatusCodes2[HtmlStatusCodes2["SERVER_ERROR"] = 500] = "SERVER_ERROR";
147
+ HtmlStatusCodes2[HtmlStatusCodes2["SERVICE_UNAVAILABLE"] = 503] = "SERVICE_UNAVAILABLE";
148
+ HtmlStatusCodes2[HtmlStatusCodes2["NETWORK_CONNECT_TIMEOUT"] = 599] = "NETWORK_CONNECT_TIMEOUT";
149
+ return HtmlStatusCodes2;
150
+ })(HtmlStatusCodes || {});
151
+ var RsError = class _RsError {
152
+ constructor(errCode, message) {
153
+ this.err = errCode;
154
+ this.msg = message || "";
155
+ this.status = _RsError.htmlStatus(errCode);
156
+ this.stack = new Error().stack || "";
157
+ }
158
+ static htmlStatus(code) {
159
+ return htmlStatusMap[code];
160
+ }
161
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
162
+ static isRsError(error) {
163
+ return error instanceof _RsError;
164
+ }
165
+ };
166
+ var htmlStatusMap = {
167
+ UNKNOWN_ERROR: 500 /* SERVER_ERROR */,
168
+ NOT_FOUND: 404 /* NOT_FOUND */,
169
+ EMAIL_TAKEN: 409 /* CONFLICT */,
170
+ FORBIDDEN: 403 /* FORBIDDEN */,
171
+ CONFLICT: 409 /* CONFLICT */,
172
+ UNAUTHORIZED: 401 /* UNAUTHORIZED */,
173
+ UPDATE_FORBIDDEN: 403 /* FORBIDDEN */,
174
+ CREATE_FORBIDDEN: 403 /* FORBIDDEN */,
175
+ DELETE_FORBIDDEN: 403 /* FORBIDDEN */,
176
+ DELETE_FAILURE: 500 /* SERVER_ERROR */,
177
+ BAD_REQUEST: 400 /* BAD_REQUEST */,
178
+ INVALID_TOKEN: 401 /* UNAUTHORIZED */,
179
+ INCORRECT_EMAIL_OR_PASSWORD: 401 /* UNAUTHORIZED */,
180
+ DUPLICATE_TOKEN: 409 /* CONFLICT */,
181
+ DUPLICATE_USERNAME: 409 /* CONFLICT */,
182
+ DUPLICATE_EMAIL: 409 /* CONFLICT */,
183
+ DUPLICATE: 409 /* CONFLICT */,
184
+ EMAIL_NOT_VERIFIED: 400 /* BAD_REQUEST */,
185
+ UPDATE_WITHOUT_ID: 400 /* BAD_REQUEST */,
186
+ CONNECTION_ERROR: 599 /* NETWORK_CONNECT_TIMEOUT */,
187
+ INVALID_PAYMENT: 403 /* FORBIDDEN */,
188
+ DECLINED_PAYMENT: 403 /* FORBIDDEN */,
189
+ INTEGRATION_ERROR: 500 /* SERVER_ERROR */,
190
+ CANNOT_RESERVE: 403 /* FORBIDDEN */,
191
+ REFUND_FAILURE: 403 /* FORBIDDEN */,
192
+ INVALID_INVOICE: 403 /* FORBIDDEN */,
193
+ INVALID_COUPON: 403 /* FORBIDDEN */,
194
+ SERVICE_UNAVAILABLE: 503 /* SERVICE_UNAVAILABLE */,
195
+ METHOD_UNALLOWED: 405 /* METHOD_NOT_ALLOWED */,
196
+ LOGIN_EXPIRED: 401 /* UNAUTHORIZED */,
197
+ THIRD_PARTY_ERROR: 400 /* BAD_REQUEST */,
198
+ ACCESS_DENIED: 403 /* FORBIDDEN */,
199
+ DATABASE_ERROR: 500 /* SERVER_ERROR */,
200
+ SCHEMA_ERROR: 500 /* SERVER_ERROR */
201
+ };
202
+
128
203
  // src/restura/restura.ts
129
204
  var import_core_utils6 = require("@redskytech/core-utils");
130
205
  var import_internal2 = require("@restura/internal");
@@ -181,59 +256,11 @@ var import_compression = __toESM(require("compression"));
181
256
  var import_cookie_parser = __toESM(require("cookie-parser"));
182
257
  var import_crypto = require("crypto");
183
258
  var express = __toESM(require("express"));
184
- var import_fs2 = __toESM(require("fs"));
185
- var import_path2 = __toESM(require("path"));
259
+ var import_fs3 = __toESM(require("fs"));
260
+ var import_path3 = __toESM(require("path"));
261
+ var import_pg2 = __toESM(require("pg"));
186
262
  var prettier3 = __toESM(require("prettier"));
187
263
 
188
- // src/restura/errors.ts
189
- var RsError = class _RsError {
190
- constructor(errCode, message) {
191
- this.err = errCode;
192
- this.msg = message || "";
193
- this.status = _RsError.htmlStatus(errCode);
194
- this.stack = new Error().stack || "";
195
- }
196
- static htmlStatus(code) {
197
- return htmlStatusMap[code];
198
- }
199
- };
200
- var htmlStatusMap = {
201
- UNKNOWN_ERROR: 500 /* SERVER_ERROR */,
202
- NOT_FOUND: 404 /* NOT_FOUND */,
203
- EMAIL_TAKEN: 409 /* CONFLICT */,
204
- FORBIDDEN: 403 /* FORBIDDEN */,
205
- CONFLICT: 409 /* CONFLICT */,
206
- UNAUTHORIZED: 401 /* UNAUTHORIZED */,
207
- UPDATE_FORBIDDEN: 403 /* FORBIDDEN */,
208
- CREATE_FORBIDDEN: 403 /* FORBIDDEN */,
209
- DELETE_FORBIDDEN: 403 /* FORBIDDEN */,
210
- DELETE_FAILURE: 500 /* SERVER_ERROR */,
211
- BAD_REQUEST: 400 /* BAD_REQUEST */,
212
- INVALID_TOKEN: 401 /* UNAUTHORIZED */,
213
- INCORRECT_EMAIL_OR_PASSWORD: 401 /* UNAUTHORIZED */,
214
- DUPLICATE_TOKEN: 409 /* CONFLICT */,
215
- DUPLICATE_USERNAME: 409 /* CONFLICT */,
216
- DUPLICATE_EMAIL: 409 /* CONFLICT */,
217
- DUPLICATE: 409 /* CONFLICT */,
218
- EMAIL_NOT_VERIFIED: 400 /* BAD_REQUEST */,
219
- UPDATE_WITHOUT_ID: 400 /* BAD_REQUEST */,
220
- CONNECTION_ERROR: 599 /* NETWORK_CONNECT_TIMEOUT */,
221
- INVALID_PAYMENT: 403 /* FORBIDDEN */,
222
- DECLINED_PAYMENT: 403 /* FORBIDDEN */,
223
- INTEGRATION_ERROR: 500 /* SERVER_ERROR */,
224
- CANNOT_RESERVE: 403 /* FORBIDDEN */,
225
- REFUND_FAILURE: 403 /* FORBIDDEN */,
226
- INVALID_INVOICE: 403 /* FORBIDDEN */,
227
- INVALID_COUPON: 403 /* FORBIDDEN */,
228
- SERVICE_UNAVAILABLE: 503 /* SERVICE_UNAVAILABLE */,
229
- METHOD_UNALLOWED: 405 /* METHOD_NOT_ALLOWED */,
230
- LOGIN_EXPIRED: 401 /* UNAUTHORIZED */,
231
- THIRD_PARTY_ERROR: 400 /* BAD_REQUEST */,
232
- ACCESS_DENIED: 403 /* FORBIDDEN */,
233
- DATABASE_ERROR: 500 /* SERVER_ERROR */,
234
- SCHEMA_ERROR: 500 /* SERVER_ERROR */
235
- };
236
-
237
264
  // src/restura/sql/SqlUtils.ts
238
265
  var SqlUtils = class _SqlUtils {
239
266
  static convertDatabaseTypeToTypescript(type, value) {
@@ -325,9 +352,9 @@ var ResponseValidator = class _ResponseValidator {
325
352
  return { validator: "any" };
326
353
  }
327
354
  getTypeFromTable(selector, name) {
328
- const path3 = selector.split(".");
329
- if (path3.length === 0 || path3.length > 2 || path3[0] === "") return { validator: "any", isOptional: false };
330
- const tableName = path3.length == 2 ? path3[0] : name, columnName = path3.length == 2 ? path3[1] : path3[0];
355
+ const path4 = selector.split(".");
356
+ if (path4.length === 0 || path4.length > 2 || path4[0] === "") return { validator: "any", isOptional: false };
357
+ const tableName = path4.length == 2 ? path4[0] : name, columnName = path4.length == 2 ? path4[1] : path4[0];
331
358
  const table = this.database.find((t) => t.name == tableName);
332
359
  const column = table == null ? void 0 : table.columns.find((c) => c.name == columnName);
333
360
  if (!table || !column) return { validator: "any", isOptional: false };
@@ -543,10 +570,10 @@ var ApiTree = class _ApiTree {
543
570
  return `${p.name}${optional ? "?" : ""}:${responseType}${array ? "[]" : ""}`;
544
571
  }
545
572
  getTypeFromTable(selector, name) {
546
- const path3 = selector.split(".");
547
- if (path3.length === 0 || path3.length > 2 || path3[0] === "") return { responseType: "any", optional: false };
548
- let tableName = path3.length == 2 ? path3[0] : name;
549
- const columnName = path3.length == 2 ? path3[1] : path3[0];
573
+ const path4 = selector.split(".");
574
+ if (path4.length === 0 || path4.length > 2 || path4[0] === "") return { responseType: "any", optional: false };
575
+ let tableName = path4.length == 2 ? path4[0] : name;
576
+ const columnName = path4.length == 2 ? path4[1] : path4[0];
550
577
  let table = this.database.find((t) => t.name == tableName);
551
578
  if (!table && tableName.includes("_")) {
552
579
  const tableAliasSplit = tableName.split("_");
@@ -561,8 +588,8 @@ var ApiTree = class _ApiTree {
561
588
  };
562
589
  }
563
590
  };
564
- function pathToNamespaces(path3) {
565
- return path3.split("/").map((e) => import_core_utils.StringUtils.toPascalCasing(e)).filter((e) => e);
591
+ function pathToNamespaces(path4) {
592
+ return path4.split("/").map((e) => import_core_utils.StringUtils.toPascalCasing(e)).filter((e) => e);
566
593
  }
567
594
  function apiGenerator(schema, schemaHash) {
568
595
  let apiString = `/** Auto generated file from Schema Hash (${schemaHash}). DO NOT MODIFY **/`;
@@ -595,6 +622,92 @@ function apiGenerator(schema, schemaHash) {
595
622
  }));
596
623
  }
597
624
 
625
+ // src/restura/customApiFactory.ts
626
+ var import_fs = __toESM(require("fs"));
627
+ var import_path = __toESM(require("path"));
628
+ var CustomApiFactory = class {
629
+ constructor() {
630
+ this.customApis = {};
631
+ }
632
+ async loadApiFiles(baseFolderPath) {
633
+ const apiVersions = ["v1"];
634
+ for (const apiVersion of apiVersions) {
635
+ const apiVersionFolderPath = import_path.default.join(baseFolderPath, apiVersion);
636
+ if (!import_fs.default.existsSync(apiVersionFolderPath)) continue;
637
+ await this.addDirectory(apiVersionFolderPath, apiVersion);
638
+ }
639
+ }
640
+ getCustomApi(customApiName) {
641
+ return this.customApis[customApiName];
642
+ }
643
+ async addDirectory(directoryPath, apiVersion) {
644
+ const entries = import_fs.default.readdirSync(directoryPath, {
645
+ withFileTypes: true
646
+ });
647
+ for (const entry of entries) {
648
+ if (entry.isFile()) {
649
+ if (entry.name.endsWith(`.api.${apiVersion}.js`) === false) continue;
650
+ try {
651
+ const importPath = `${import_path.default.join(directoryPath, entry.name)}`;
652
+ const ApiImport = await import(importPath);
653
+ const customApiClass = new ApiImport.default();
654
+ logger.info(`Registering custom API: ${ApiImport.default.name}`);
655
+ this.bindMethodsToInstance(customApiClass);
656
+ this.customApis[ApiImport.default.name] = customApiClass;
657
+ } catch (e) {
658
+ console.error(e);
659
+ }
660
+ }
661
+ }
662
+ }
663
+ bindMethodsToInstance(instance) {
664
+ const proto = Object.getPrototypeOf(instance);
665
+ Object.getOwnPropertyNames(proto).forEach((key) => {
666
+ const property = instance[key];
667
+ if (typeof property === "function" && key !== "constructor") {
668
+ instance[key] = property.bind(instance);
669
+ }
670
+ });
671
+ }
672
+ };
673
+ var customApiFactory = new CustomApiFactory();
674
+ var customApiFactory_default = customApiFactory;
675
+
676
+ // src/restura/customTypeValidationGenerator.ts
677
+ var import_fs2 = __toESM(require("fs"));
678
+ var TJS = __toESM(require("typescript-json-schema"));
679
+ var import_path2 = __toESM(require("path"));
680
+ var import_tmp = __toESM(require("tmp"));
681
+ var process2 = __toESM(require("process"));
682
+ function customTypeValidationGenerator(currentSchema) {
683
+ const schemaObject = {};
684
+ const customInterfaceNames = currentSchema.customTypes.match(new RegExp("(?<=interface\\s)(\\w+)|(?<=type\\s)(\\w+)", "g"));
685
+ if (!customInterfaceNames) return {};
686
+ const temporaryFile = import_tmp.default.fileSync({ mode: 420, prefix: "prefix-", postfix: ".ts" });
687
+ import_fs2.default.writeFileSync(temporaryFile.name, currentSchema.customTypes);
688
+ const compilerOptions = {
689
+ strictNullChecks: true,
690
+ skipLibCheck: true
691
+ };
692
+ const program = TJS.getProgramFromFiles(
693
+ [
694
+ (0, import_path2.resolve)(temporaryFile.name),
695
+ // find a way to remove
696
+ import_path2.default.join(process2.cwd(), "src/@types/models.d.ts"),
697
+ import_path2.default.join(process2.cwd(), "src/@types/api.d.ts")
698
+ ],
699
+ compilerOptions
700
+ );
701
+ customInterfaceNames.forEach((item) => {
702
+ const ddlSchema = TJS.generateSchema(program, item, {
703
+ required: true
704
+ });
705
+ schemaObject[item] = ddlSchema || {};
706
+ });
707
+ temporaryFile.removeCallback();
708
+ return schemaObject;
709
+ }
710
+
598
711
  // src/restura/middleware/addApiResponseFunctions.ts
599
712
  function addApiResponseFunctions(req, res, next) {
600
713
  res.sendData = function(data, statusCode = 200) {
@@ -623,9 +736,17 @@ function addApiResponseFunctions(req, res, next) {
623
736
  next();
624
737
  }
625
738
 
626
- // src/restura/validateRequestParams.ts
627
- var import_core_utils2 = require("@redskytech/core-utils");
628
- var import_jsonschema = __toESM(require("jsonschema"));
739
+ // src/restura/middleware/authenticateUser.ts
740
+ function authenticateUser(applicationAuthenticateHandler) {
741
+ return (req, res, next) => {
742
+ applicationAuthenticateHandler(req, res, (userDetails) => {
743
+ req.requesterDetails = __spreadValues(__spreadValues({}, req.requesterDetails), userDetails);
744
+ next();
745
+ });
746
+ };
747
+ }
748
+
749
+ // src/restura/restura.schema.ts
629
750
  var import_zod3 = require("zod");
630
751
 
631
752
  // src/restura/types/validation.types.ts
@@ -636,264 +757,83 @@ var validatorDataSchema = import_zod2.z.object({
636
757
  value: validatorDataSchemeValue
637
758
  }).strict();
638
759
 
639
- // src/restura/utils/addQuotesToStrings.ts
640
- function addQuotesToStrings(variable) {
641
- if (typeof variable === "string") {
642
- return `'${variable}'`;
643
- } else if (Array.isArray(variable)) {
644
- const arrayWithQuotes = variable.map(addQuotesToStrings);
645
- return arrayWithQuotes;
646
- } else {
647
- return variable;
648
- }
649
- }
650
-
651
- // src/restura/validateRequestParams.ts
652
- function validateRequestParams(req, routeData, validationSchema) {
653
- const requestData = getRequestData(req);
654
- req.data = requestData;
655
- if (routeData.request === void 0) {
656
- if (routeData.type !== "CUSTOM_ONE" && routeData.type !== "CUSTOM_ARRAY" && routeData.type !== "CUSTOM_PAGED")
657
- throw new RsError("BAD_REQUEST", `No request parameters provided for standard request.`);
658
- if (!routeData.responseType) throw new RsError("BAD_REQUEST", `No response type defined for custom request.`);
659
- if (!routeData.requestType) throw new RsError("BAD_REQUEST", `No request type defined for custom request.`);
660
- const currentInterface = validationSchema[routeData.requestType];
661
- const validator = new import_jsonschema.default.Validator();
662
- const executeValidation = validator.validate(req.data, currentInterface);
663
- if (!executeValidation.valid) {
664
- throw new RsError(
665
- "BAD_REQUEST",
666
- `Request custom setup has failed the following check: (${executeValidation.errors})`
667
- );
668
- }
669
- return;
670
- }
671
- Object.keys(req.data).forEach((requestParamName) => {
672
- const requestParam = routeData.request.find((param) => param.name === requestParamName);
673
- if (!requestParam) {
674
- throw new RsError("BAD_REQUEST", `Request param (${requestParamName}) is not allowed`);
675
- }
676
- });
677
- routeData.request.forEach((requestParam) => {
678
- const requestValue = requestData[requestParam.name];
679
- if (requestParam.required && requestValue === void 0)
680
- throw new RsError("BAD_REQUEST", `Request param (${requestParam.name}) is required but missing`);
681
- else if (!requestParam.required && requestValue === void 0) return;
682
- validateRequestSingleParam(requestValue, requestParam);
683
- });
684
- }
685
- function validateRequestSingleParam(requestValue, requestParam) {
686
- requestParam.validator.forEach((validator) => {
687
- switch (validator.type) {
688
- case "TYPE_CHECK":
689
- performTypeCheck(requestValue, validator, requestParam.name);
690
- break;
691
- case "MIN":
692
- performMinCheck(requestValue, validator, requestParam.name);
693
- break;
694
- case "MAX":
695
- performMaxCheck(requestValue, validator, requestParam.name);
696
- break;
697
- case "ONE_OF":
698
- performOneOfCheck(requestValue, validator, requestParam.name);
699
- break;
700
- }
701
- });
702
- }
703
- function isValidType(type, requestValue) {
704
- try {
705
- expectValidType(type, requestValue);
706
- return true;
707
- } catch (e) {
708
- return false;
709
- }
710
- }
711
- function expectValidType(type, requestValue) {
712
- if (type === "number") {
713
- return import_zod3.z.number().parse(requestValue);
714
- }
715
- if (type === "string") {
716
- return import_zod3.z.string().parse(requestValue);
717
- }
718
- if (type === "boolean") {
719
- return import_zod3.z.boolean().parse(requestValue);
720
- }
721
- if (type === "string[]") {
722
- return import_zod3.z.array(import_zod3.z.string()).parse(requestValue);
723
- }
724
- if (type === "number[]") {
725
- return import_zod3.z.array(import_zod3.z.number()).parse(requestValue);
726
- }
727
- if (type === "any[]") {
728
- return import_zod3.z.array(import_zod3.z.any()).parse(requestValue);
729
- }
730
- if (type === "object") {
731
- return import_zod3.z.object({}).strict().parse(requestValue);
732
- }
733
- }
734
- function performTypeCheck(requestValue, validator, requestParamName) {
735
- if (!isValidType(validator.value, requestValue)) {
736
- throw new RsError(
737
- "BAD_REQUEST",
738
- `Request param (${requestParamName}) with value (${addQuotesToStrings(requestValue)}) is not of type (${validator.value})`
739
- );
740
- }
741
- try {
742
- validatorDataSchemeValue.parse(validator.value);
743
- } catch (e) {
744
- throw new RsError("SCHEMA_ERROR", `Schema validator value (${validator.value}) is not a valid type`);
745
- }
746
- }
747
- function expectOnlyNumbers(requestValue, validator, requestParamName) {
748
- if (!isValueNumber(requestValue))
749
- throw new RsError(
750
- "BAD_REQUEST",
751
- `Request param (${requestParamName}) with value (${requestValue}) is not of type number`
752
- );
753
- if (!isValueNumber(validator.value))
754
- throw new RsError("SCHEMA_ERROR", `Schema validator value (${validator.value} is not of type number`);
755
- }
756
- function performMinCheck(requestValue, validator, requestParamName) {
757
- expectOnlyNumbers(requestValue, validator, requestParamName);
758
- if (requestValue < validator.value)
759
- throw new RsError(
760
- "BAD_REQUEST",
761
- `Request param (${requestParamName}) with value (${requestValue}) is less than (${validator.value})`
762
- );
763
- }
764
- function performMaxCheck(requestValue, validator, requestParamName) {
765
- expectOnlyNumbers(requestValue, validator, requestParamName);
766
- if (requestValue > validator.value)
767
- throw new RsError(
768
- "BAD_REQUEST",
769
- `Request param (${requestParamName}) with value (${requestValue}) is more than (${validator.value})`
770
- );
771
- }
772
- function performOneOfCheck(requestValue, validator, requestParamName) {
773
- if (!import_core_utils2.ObjectUtils.isArrayWithData(validator.value))
774
- throw new RsError("SCHEMA_ERROR", `Schema validator value (${validator.value}) is not of type array`);
775
- if (typeof requestValue === "object")
776
- throw new RsError("BAD_REQUEST", `Request param (${requestParamName}) is not of type string or number`);
777
- if (!validator.value.includes(requestValue))
778
- throw new RsError(
779
- "BAD_REQUEST",
780
- `Request param (${requestParamName}) with value (${requestValue}) is not one of (${validator.value.join(", ")})`
781
- );
782
- }
783
- function isValueNumber(value) {
784
- return !isNaN(Number(value));
785
- }
786
- function getRequestData(req) {
787
- let body = "";
788
- if (req.method === "GET" || req.method === "DELETE") {
789
- body = "query";
790
- } else {
791
- body = "body";
792
- }
793
- const bodyData = req[body];
794
- if (bodyData) {
795
- for (const attr in bodyData) {
796
- if (attr === "token") {
797
- delete bodyData[attr];
798
- continue;
799
- }
800
- if (bodyData[attr] instanceof Array) {
801
- const attrList = [];
802
- for (const value of bodyData[attr]) {
803
- if (isNaN(Number(value))) continue;
804
- attrList.push(Number(value));
805
- }
806
- if (import_core_utils2.ObjectUtils.isArrayWithData(attrList)) {
807
- bodyData[attr] = attrList;
808
- }
809
- } else {
810
- bodyData[attr] = import_core_utils2.ObjectUtils.safeParse(bodyData[attr]);
811
- if (isNaN(Number(bodyData[attr]))) continue;
812
- bodyData[attr] = Number(bodyData[attr]);
813
- }
814
- }
815
- }
816
- return bodyData;
817
- }
818
-
819
760
  // src/restura/restura.schema.ts
820
- var import_zod4 = require("zod");
821
- var orderBySchema = import_zod4.z.object({
822
- columnName: import_zod4.z.string(),
823
- order: import_zod4.z.enum(["ASC", "DESC"]),
824
- tableName: import_zod4.z.string()
761
+ var orderBySchema = import_zod3.z.object({
762
+ columnName: import_zod3.z.string(),
763
+ order: import_zod3.z.enum(["ASC", "DESC"]),
764
+ tableName: import_zod3.z.string()
825
765
  }).strict();
826
- var groupBySchema = import_zod4.z.object({
827
- columnName: import_zod4.z.string(),
828
- tableName: import_zod4.z.string()
766
+ var groupBySchema = import_zod3.z.object({
767
+ columnName: import_zod3.z.string(),
768
+ tableName: import_zod3.z.string()
829
769
  }).strict();
830
- var whereDataSchema = import_zod4.z.object({
831
- tableName: import_zod4.z.string().optional(),
832
- columnName: import_zod4.z.string().optional(),
833
- operator: import_zod4.z.enum(["=", "<", ">", "<=", ">=", "!=", "LIKE", "IN", "NOT IN", "STARTS WITH", "ENDS WITH"]).optional(),
834
- value: import_zod4.z.string().or(import_zod4.z.number()).optional(),
835
- custom: import_zod4.z.string().optional(),
836
- conjunction: import_zod4.z.enum(["AND", "OR"]).optional()
770
+ var whereDataSchema = import_zod3.z.object({
771
+ tableName: import_zod3.z.string().optional(),
772
+ columnName: import_zod3.z.string().optional(),
773
+ operator: import_zod3.z.enum(["=", "<", ">", "<=", ">=", "!=", "LIKE", "IN", "NOT IN", "STARTS WITH", "ENDS WITH"]).optional(),
774
+ value: import_zod3.z.string().or(import_zod3.z.number()).optional(),
775
+ custom: import_zod3.z.string().optional(),
776
+ conjunction: import_zod3.z.enum(["AND", "OR"]).optional()
837
777
  }).strict();
838
- var assignmentDataSchema = import_zod4.z.object({
839
- name: import_zod4.z.string(),
840
- value: import_zod4.z.string()
778
+ var assignmentDataSchema = import_zod3.z.object({
779
+ name: import_zod3.z.string(),
780
+ value: import_zod3.z.string()
841
781
  }).strict();
842
- var joinDataSchema = import_zod4.z.object({
843
- table: import_zod4.z.string(),
844
- localColumnName: import_zod4.z.string().optional(),
845
- foreignColumnName: import_zod4.z.string().optional(),
846
- custom: import_zod4.z.string().optional(),
847
- type: import_zod4.z.enum(["LEFT", "INNER"]),
848
- alias: import_zod4.z.string().optional()
782
+ var joinDataSchema = import_zod3.z.object({
783
+ table: import_zod3.z.string(),
784
+ localColumnName: import_zod3.z.string().optional(),
785
+ foreignColumnName: import_zod3.z.string().optional(),
786
+ custom: import_zod3.z.string().optional(),
787
+ type: import_zod3.z.enum(["LEFT", "INNER"]),
788
+ alias: import_zod3.z.string().optional()
849
789
  }).strict();
850
- var requestDataSchema = import_zod4.z.object({
851
- name: import_zod4.z.string(),
852
- required: import_zod4.z.boolean(),
853
- validator: import_zod4.z.array(validatorDataSchema)
790
+ var requestDataSchema = import_zod3.z.object({
791
+ name: import_zod3.z.string(),
792
+ required: import_zod3.z.boolean(),
793
+ validator: import_zod3.z.array(validatorDataSchema)
854
794
  }).strict();
855
- var responseDataSchema = import_zod4.z.object({
856
- name: import_zod4.z.string(),
857
- selector: import_zod4.z.string().optional(),
858
- subquery: import_zod4.z.object({
859
- table: import_zod4.z.string(),
860
- joins: import_zod4.z.array(joinDataSchema),
861
- where: import_zod4.z.array(whereDataSchema),
862
- properties: import_zod4.z.array(import_zod4.z.lazy(() => responseDataSchema)),
795
+ var responseDataSchema = import_zod3.z.object({
796
+ name: import_zod3.z.string(),
797
+ selector: import_zod3.z.string().optional(),
798
+ subquery: import_zod3.z.object({
799
+ table: import_zod3.z.string(),
800
+ joins: import_zod3.z.array(joinDataSchema),
801
+ where: import_zod3.z.array(whereDataSchema),
802
+ properties: import_zod3.z.array(import_zod3.z.lazy(() => responseDataSchema)),
863
803
  // Explicit type for the lazy schema
864
804
  groupBy: groupBySchema.optional(),
865
805
  orderBy: orderBySchema.optional()
866
806
  }).optional()
867
807
  }).strict();
868
- var routeDataBaseSchema = import_zod4.z.object({
869
- method: import_zod4.z.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]),
870
- name: import_zod4.z.string(),
871
- description: import_zod4.z.string(),
872
- path: import_zod4.z.string(),
873
- roles: import_zod4.z.array(import_zod4.z.string())
808
+ var routeDataBaseSchema = import_zod3.z.object({
809
+ method: import_zod3.z.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]),
810
+ name: import_zod3.z.string(),
811
+ description: import_zod3.z.string(),
812
+ path: import_zod3.z.string(),
813
+ roles: import_zod3.z.array(import_zod3.z.string())
874
814
  }).strict();
875
815
  var standardRouteSchema = routeDataBaseSchema.extend({
876
- type: import_zod4.z.enum(["ONE", "ARRAY", "PAGED"]),
877
- table: import_zod4.z.string(),
878
- joins: import_zod4.z.array(joinDataSchema),
879
- assignments: import_zod4.z.array(assignmentDataSchema),
880
- where: import_zod4.z.array(whereDataSchema),
881
- request: import_zod4.z.array(requestDataSchema),
882
- response: import_zod4.z.array(responseDataSchema),
816
+ type: import_zod3.z.enum(["ONE", "ARRAY", "PAGED"]),
817
+ table: import_zod3.z.string(),
818
+ joins: import_zod3.z.array(joinDataSchema),
819
+ assignments: import_zod3.z.array(assignmentDataSchema),
820
+ where: import_zod3.z.array(whereDataSchema),
821
+ request: import_zod3.z.array(requestDataSchema),
822
+ response: import_zod3.z.array(responseDataSchema),
883
823
  groupBy: groupBySchema.optional(),
884
824
  orderBy: orderBySchema.optional()
885
825
  }).strict();
886
826
  var customRouteSchema = routeDataBaseSchema.extend({
887
- type: import_zod4.z.enum(["CUSTOM_ONE", "CUSTOM_ARRAY", "CUSTOM_PAGED"]),
888
- responseType: import_zod4.z.union([import_zod4.z.string(), import_zod4.z.enum(["string", "number", "boolean"])]),
889
- requestType: import_zod4.z.string().optional(),
890
- request: import_zod4.z.array(requestDataSchema).optional(),
891
- table: import_zod4.z.undefined(),
892
- joins: import_zod4.z.undefined(),
893
- assignments: import_zod4.z.undefined(),
894
- fileUploadType: import_zod4.z.enum(["SINGLE", "MULTIPLE"]).optional()
827
+ type: import_zod3.z.enum(["CUSTOM_ONE", "CUSTOM_ARRAY", "CUSTOM_PAGED"]),
828
+ responseType: import_zod3.z.union([import_zod3.z.string(), import_zod3.z.enum(["string", "number", "boolean"])]),
829
+ requestType: import_zod3.z.string().optional(),
830
+ request: import_zod3.z.array(requestDataSchema).optional(),
831
+ table: import_zod3.z.undefined(),
832
+ joins: import_zod3.z.undefined(),
833
+ assignments: import_zod3.z.undefined(),
834
+ fileUploadType: import_zod3.z.enum(["SINGLE", "MULTIPLE"]).optional()
895
835
  }).strict();
896
- var postgresColumnNumericTypesSchema = import_zod4.z.enum([
836
+ var postgresColumnNumericTypesSchema = import_zod3.z.enum([
897
837
  "SMALLINT",
898
838
  // 2 bytes, -32,768 to 32,767
899
839
  "INTEGER",
@@ -913,7 +853,7 @@ var postgresColumnNumericTypesSchema = import_zod4.z.enum([
913
853
  "BIGSERIAL"
914
854
  // auto-incrementing big integer
915
855
  ]);
916
- var postgresColumnStringTypesSchema = import_zod4.z.enum([
856
+ var postgresColumnStringTypesSchema = import_zod3.z.enum([
917
857
  "CHAR",
918
858
  // fixed-length, blank-padded
919
859
  "VARCHAR",
@@ -923,7 +863,7 @@ var postgresColumnStringTypesSchema = import_zod4.z.enum([
923
863
  "BYTEA"
924
864
  // binary data
925
865
  ]);
926
- var postgresColumnDateTypesSchema = import_zod4.z.enum([
866
+ var postgresColumnDateTypesSchema = import_zod3.z.enum([
927
867
  "DATE",
928
868
  // calendar date (year, month, day)
929
869
  "TIMESTAMP",
@@ -935,7 +875,7 @@ var postgresColumnDateTypesSchema = import_zod4.z.enum([
935
875
  "INTERVAL"
936
876
  // time span
937
877
  ]);
938
- var mariaDbColumnNumericTypesSchema = import_zod4.z.enum([
878
+ var mariaDbColumnNumericTypesSchema = import_zod3.z.enum([
939
879
  "BOOLEAN",
940
880
  // 1-byte A synonym for "TINYINT(1)". Supported from version 1.2.0 onwards.
941
881
  "TINYINT",
@@ -955,7 +895,7 @@ var mariaDbColumnNumericTypesSchema = import_zod4.z.enum([
955
895
  "DOUBLE"
956
896
  // 8 bytes Stored in 64-bit IEEE-754 floating point format. As such, the number of significant digits is about 15 and the range of values is approximately +/-1e308.
957
897
  ]);
958
- var mariaDbColumnStringTypesSchema = import_zod4.z.enum([
898
+ var mariaDbColumnStringTypesSchema = import_zod3.z.enum([
959
899
  "CHAR",
960
900
  // 1, 2, 4, or 8 bytes Holds letters and special characters of fixed length. Max length is 255. Default and minimum size is 1 byte.
961
901
  "VARCHAR",
@@ -981,7 +921,7 @@ var mariaDbColumnStringTypesSchema = import_zod4.z.enum([
981
921
  "ENUM"
982
922
  // Enum type
983
923
  ]);
984
- var mariaDbColumnDateTypesSchema = import_zod4.z.enum([
924
+ var mariaDbColumnDateTypesSchema = import_zod3.z.enum([
985
925
  "DATE",
986
926
  // 4-bytes Date has year, month, and day.
987
927
  "DATETIME",
@@ -991,9 +931,9 @@ var mariaDbColumnDateTypesSchema = import_zod4.z.enum([
991
931
  "TIMESTAMP"
992
932
  // 4-bytes Values are stored as the number of seconds since 1970-01-01 00:00:00 UTC, and optionally microseconds.
993
933
  ]);
994
- var columnDataSchema = import_zod4.z.object({
995
- name: import_zod4.z.string(),
996
- type: import_zod4.z.union([
934
+ var columnDataSchema = import_zod3.z.object({
935
+ name: import_zod3.z.string(),
936
+ type: import_zod3.z.union([
997
937
  postgresColumnNumericTypesSchema,
998
938
  postgresColumnStringTypesSchema,
999
939
  postgresColumnDateTypesSchema,
@@ -1001,24 +941,24 @@ var columnDataSchema = import_zod4.z.object({
1001
941
  mariaDbColumnStringTypesSchema,
1002
942
  mariaDbColumnDateTypesSchema
1003
943
  ]),
1004
- isNullable: import_zod4.z.boolean(),
1005
- roles: import_zod4.z.array(import_zod4.z.string()),
1006
- comment: import_zod4.z.string().optional(),
1007
- default: import_zod4.z.string().optional(),
1008
- value: import_zod4.z.string().optional(),
1009
- isPrimary: import_zod4.z.boolean().optional(),
1010
- isUnique: import_zod4.z.boolean().optional(),
1011
- hasAutoIncrement: import_zod4.z.boolean().optional(),
1012
- length: import_zod4.z.number().optional()
944
+ isNullable: import_zod3.z.boolean(),
945
+ roles: import_zod3.z.array(import_zod3.z.string()),
946
+ comment: import_zod3.z.string().optional(),
947
+ default: import_zod3.z.string().optional(),
948
+ value: import_zod3.z.string().optional(),
949
+ isPrimary: import_zod3.z.boolean().optional(),
950
+ isUnique: import_zod3.z.boolean().optional(),
951
+ hasAutoIncrement: import_zod3.z.boolean().optional(),
952
+ length: import_zod3.z.number().optional()
1013
953
  }).strict();
1014
- var indexDataSchema = import_zod4.z.object({
1015
- name: import_zod4.z.string(),
1016
- columns: import_zod4.z.array(import_zod4.z.string()),
1017
- isUnique: import_zod4.z.boolean(),
1018
- isPrimaryKey: import_zod4.z.boolean(),
1019
- order: import_zod4.z.enum(["ASC", "DESC"])
954
+ var indexDataSchema = import_zod3.z.object({
955
+ name: import_zod3.z.string(),
956
+ columns: import_zod3.z.array(import_zod3.z.string()),
957
+ isUnique: import_zod3.z.boolean(),
958
+ isPrimaryKey: import_zod3.z.boolean(),
959
+ order: import_zod3.z.enum(["ASC", "DESC"])
1020
960
  }).strict();
1021
- var foreignKeyActionsSchema = import_zod4.z.enum([
961
+ var foreignKeyActionsSchema = import_zod3.z.enum([
1022
962
  "CASCADE",
1023
963
  // CASCADE action for foreign keys
1024
964
  "SET NULL",
@@ -1030,48 +970,229 @@ var foreignKeyActionsSchema = import_zod4.z.enum([
1030
970
  "SET DEFAULT"
1031
971
  // SET DEFAULT action for foreign keys
1032
972
  ]);
1033
- var foreignKeyDataSchema = import_zod4.z.object({
1034
- name: import_zod4.z.string(),
1035
- column: import_zod4.z.string(),
1036
- refTable: import_zod4.z.string(),
1037
- refColumn: import_zod4.z.string(),
973
+ var foreignKeyDataSchema = import_zod3.z.object({
974
+ name: import_zod3.z.string(),
975
+ column: import_zod3.z.string(),
976
+ refTable: import_zod3.z.string(),
977
+ refColumn: import_zod3.z.string(),
1038
978
  onDelete: foreignKeyActionsSchema,
1039
979
  onUpdate: foreignKeyActionsSchema
1040
980
  }).strict();
1041
- var checkConstraintDataSchema = import_zod4.z.object({
1042
- name: import_zod4.z.string(),
1043
- check: import_zod4.z.string()
981
+ var checkConstraintDataSchema = import_zod3.z.object({
982
+ name: import_zod3.z.string(),
983
+ check: import_zod3.z.string()
1044
984
  }).strict();
1045
- var tableDataSchema = import_zod4.z.object({
1046
- name: import_zod4.z.string(),
1047
- columns: import_zod4.z.array(columnDataSchema),
1048
- indexes: import_zod4.z.array(indexDataSchema),
1049
- foreignKeys: import_zod4.z.array(foreignKeyDataSchema),
1050
- checkConstraints: import_zod4.z.array(checkConstraintDataSchema),
1051
- roles: import_zod4.z.array(import_zod4.z.string())
985
+ var tableDataSchema = import_zod3.z.object({
986
+ name: import_zod3.z.string(),
987
+ columns: import_zod3.z.array(columnDataSchema),
988
+ indexes: import_zod3.z.array(indexDataSchema),
989
+ foreignKeys: import_zod3.z.array(foreignKeyDataSchema),
990
+ checkConstraints: import_zod3.z.array(checkConstraintDataSchema),
991
+ roles: import_zod3.z.array(import_zod3.z.string())
1052
992
  }).strict();
1053
- var endpointDataSchema = import_zod4.z.object({
1054
- name: import_zod4.z.string(),
1055
- description: import_zod4.z.string(),
1056
- baseUrl: import_zod4.z.string(),
1057
- routes: import_zod4.z.array(import_zod4.z.union([standardRouteSchema, customRouteSchema]))
993
+ var endpointDataSchema = import_zod3.z.object({
994
+ name: import_zod3.z.string(),
995
+ description: import_zod3.z.string(),
996
+ baseUrl: import_zod3.z.string(),
997
+ routes: import_zod3.z.array(import_zod3.z.union([standardRouteSchema, customRouteSchema]))
1058
998
  }).strict();
1059
- var resturaZodSchema = import_zod4.z.object({
1060
- database: import_zod4.z.array(tableDataSchema),
1061
- endpoints: import_zod4.z.array(endpointDataSchema),
1062
- globalParams: import_zod4.z.array(import_zod4.z.string()),
1063
- roles: import_zod4.z.array(import_zod4.z.string()),
1064
- customTypes: import_zod4.z.string()
999
+ var resturaZodSchema = import_zod3.z.object({
1000
+ database: import_zod3.z.array(tableDataSchema),
1001
+ endpoints: import_zod3.z.array(endpointDataSchema),
1002
+ globalParams: import_zod3.z.array(import_zod3.z.string()),
1003
+ roles: import_zod3.z.array(import_zod3.z.string()),
1004
+ customTypes: import_zod3.z.string()
1065
1005
  }).strict();
1066
1006
  async function isSchemaValid(schemaToCheck) {
1067
1007
  try {
1068
1008
  resturaZodSchema.parse(schemaToCheck);
1069
1009
  return true;
1070
- } catch (error) {
1071
- logger.error(error);
1010
+ } catch (error) {
1011
+ logger.error(error);
1012
+ return false;
1013
+ }
1014
+ }
1015
+
1016
+ // src/restura/validateRequestParams.ts
1017
+ var import_core_utils2 = require("@redskytech/core-utils");
1018
+ var import_jsonschema = __toESM(require("jsonschema"));
1019
+ var import_zod4 = require("zod");
1020
+
1021
+ // src/restura/utils/addQuotesToStrings.ts
1022
+ function addQuotesToStrings(variable) {
1023
+ if (typeof variable === "string") {
1024
+ return `'${variable}'`;
1025
+ } else if (Array.isArray(variable)) {
1026
+ const arrayWithQuotes = variable.map(addQuotesToStrings);
1027
+ return arrayWithQuotes;
1028
+ } else {
1029
+ return variable;
1030
+ }
1031
+ }
1032
+
1033
+ // src/restura/validateRequestParams.ts
1034
+ function validateRequestParams(req, routeData, validationSchema) {
1035
+ const requestData = getRequestData(req);
1036
+ req.data = requestData;
1037
+ if (routeData.request === void 0) {
1038
+ if (routeData.type !== "CUSTOM_ONE" && routeData.type !== "CUSTOM_ARRAY" && routeData.type !== "CUSTOM_PAGED")
1039
+ throw new RsError("BAD_REQUEST", `No request parameters provided for standard request.`);
1040
+ if (!routeData.responseType) throw new RsError("BAD_REQUEST", `No response type defined for custom request.`);
1041
+ if (!routeData.requestType) throw new RsError("BAD_REQUEST", `No request type defined for custom request.`);
1042
+ const currentInterface = validationSchema[routeData.requestType];
1043
+ const validator = new import_jsonschema.default.Validator();
1044
+ const executeValidation = validator.validate(req.data, currentInterface);
1045
+ if (!executeValidation.valid) {
1046
+ throw new RsError(
1047
+ "BAD_REQUEST",
1048
+ `Request custom setup has failed the following check: (${executeValidation.errors})`
1049
+ );
1050
+ }
1051
+ return;
1052
+ }
1053
+ Object.keys(req.data).forEach((requestParamName) => {
1054
+ const requestParam = routeData.request.find((param) => param.name === requestParamName);
1055
+ if (!requestParam) {
1056
+ throw new RsError("BAD_REQUEST", `Request param (${requestParamName}) is not allowed`);
1057
+ }
1058
+ });
1059
+ routeData.request.forEach((requestParam) => {
1060
+ const requestValue = requestData[requestParam.name];
1061
+ if (requestParam.required && requestValue === void 0)
1062
+ throw new RsError("BAD_REQUEST", `Request param (${requestParam.name}) is required but missing`);
1063
+ else if (!requestParam.required && requestValue === void 0) return;
1064
+ validateRequestSingleParam(requestValue, requestParam);
1065
+ });
1066
+ }
1067
+ function validateRequestSingleParam(requestValue, requestParam) {
1068
+ requestParam.validator.forEach((validator) => {
1069
+ switch (validator.type) {
1070
+ case "TYPE_CHECK":
1071
+ performTypeCheck(requestValue, validator, requestParam.name);
1072
+ break;
1073
+ case "MIN":
1074
+ performMinCheck(requestValue, validator, requestParam.name);
1075
+ break;
1076
+ case "MAX":
1077
+ performMaxCheck(requestValue, validator, requestParam.name);
1078
+ break;
1079
+ case "ONE_OF":
1080
+ performOneOfCheck(requestValue, validator, requestParam.name);
1081
+ break;
1082
+ }
1083
+ });
1084
+ }
1085
+ function isValidType(type, requestValue) {
1086
+ try {
1087
+ expectValidType(type, requestValue);
1088
+ return true;
1089
+ } catch (e) {
1072
1090
  return false;
1073
1091
  }
1074
1092
  }
1093
+ function expectValidType(type, requestValue) {
1094
+ if (type === "number") {
1095
+ return import_zod4.z.number().parse(requestValue);
1096
+ }
1097
+ if (type === "string") {
1098
+ return import_zod4.z.string().parse(requestValue);
1099
+ }
1100
+ if (type === "boolean") {
1101
+ return import_zod4.z.boolean().parse(requestValue);
1102
+ }
1103
+ if (type === "string[]") {
1104
+ return import_zod4.z.array(import_zod4.z.string()).parse(requestValue);
1105
+ }
1106
+ if (type === "number[]") {
1107
+ return import_zod4.z.array(import_zod4.z.number()).parse(requestValue);
1108
+ }
1109
+ if (type === "any[]") {
1110
+ return import_zod4.z.array(import_zod4.z.any()).parse(requestValue);
1111
+ }
1112
+ if (type === "object") {
1113
+ return import_zod4.z.object({}).strict().parse(requestValue);
1114
+ }
1115
+ }
1116
+ function performTypeCheck(requestValue, validator, requestParamName) {
1117
+ if (!isValidType(validator.value, requestValue)) {
1118
+ throw new RsError(
1119
+ "BAD_REQUEST",
1120
+ `Request param (${requestParamName}) with value (${addQuotesToStrings(requestValue)}) is not of type (${validator.value})`
1121
+ );
1122
+ }
1123
+ try {
1124
+ validatorDataSchemeValue.parse(validator.value);
1125
+ } catch (e) {
1126
+ throw new RsError("SCHEMA_ERROR", `Schema validator value (${validator.value}) is not a valid type`);
1127
+ }
1128
+ }
1129
+ function expectOnlyNumbers(requestValue, validator, requestParamName) {
1130
+ if (!isValueNumber(requestValue))
1131
+ throw new RsError(
1132
+ "BAD_REQUEST",
1133
+ `Request param (${requestParamName}) with value (${requestValue}) is not of type number`
1134
+ );
1135
+ if (!isValueNumber(validator.value))
1136
+ throw new RsError("SCHEMA_ERROR", `Schema validator value (${validator.value} is not of type number`);
1137
+ }
1138
+ function performMinCheck(requestValue, validator, requestParamName) {
1139
+ expectOnlyNumbers(requestValue, validator, requestParamName);
1140
+ if (requestValue < validator.value)
1141
+ throw new RsError(
1142
+ "BAD_REQUEST",
1143
+ `Request param (${requestParamName}) with value (${requestValue}) is less than (${validator.value})`
1144
+ );
1145
+ }
1146
+ function performMaxCheck(requestValue, validator, requestParamName) {
1147
+ expectOnlyNumbers(requestValue, validator, requestParamName);
1148
+ if (requestValue > validator.value)
1149
+ throw new RsError(
1150
+ "BAD_REQUEST",
1151
+ `Request param (${requestParamName}) with value (${requestValue}) is more than (${validator.value})`
1152
+ );
1153
+ }
1154
+ function performOneOfCheck(requestValue, validator, requestParamName) {
1155
+ if (!import_core_utils2.ObjectUtils.isArrayWithData(validator.value))
1156
+ throw new RsError("SCHEMA_ERROR", `Schema validator value (${validator.value}) is not of type array`);
1157
+ if (typeof requestValue === "object")
1158
+ throw new RsError("BAD_REQUEST", `Request param (${requestParamName}) is not of type string or number`);
1159
+ if (!validator.value.includes(requestValue))
1160
+ throw new RsError(
1161
+ "BAD_REQUEST",
1162
+ `Request param (${requestParamName}) with value (${requestValue}) is not one of (${validator.value.join(", ")})`
1163
+ );
1164
+ }
1165
+ function isValueNumber(value) {
1166
+ return !isNaN(Number(value));
1167
+ }
1168
+ function getRequestData(req) {
1169
+ let body = "";
1170
+ if (req.method === "GET" || req.method === "DELETE") {
1171
+ body = "query";
1172
+ } else {
1173
+ body = "body";
1174
+ }
1175
+ const bodyData = req[body];
1176
+ if (bodyData && body === "query") {
1177
+ for (const attr in bodyData) {
1178
+ if (bodyData[attr] instanceof Array) {
1179
+ const attrList = [];
1180
+ for (const value of bodyData[attr]) {
1181
+ if (isNaN(Number(value))) continue;
1182
+ attrList.push(Number(value));
1183
+ }
1184
+ if (import_core_utils2.ObjectUtils.isArrayWithData(attrList)) {
1185
+ bodyData[attr] = attrList;
1186
+ }
1187
+ } else {
1188
+ bodyData[attr] = import_core_utils2.ObjectUtils.safeParse(bodyData[attr]);
1189
+ if (isNaN(Number(bodyData[attr]))) continue;
1190
+ bodyData[attr] = Number(bodyData[attr]);
1191
+ }
1192
+ }
1193
+ }
1194
+ return bodyData;
1195
+ }
1075
1196
 
1076
1197
  // src/restura/middleware/schemaValidation.ts
1077
1198
  async function schemaValidation(req, res, next) {
@@ -1120,55 +1241,52 @@ function convertTable(table) {
1120
1241
  return modelString;
1121
1242
  }
1122
1243
 
1123
- // src/restura/middleware/authenticateUser.ts
1124
- function authenticateUser(applicationAuthenticateHandler) {
1125
- return (req, res, next) => {
1126
- applicationAuthenticateHandler(
1127
- req,
1128
- (userDetails) => {
1129
- req.requesterDetails = __spreadValues(__spreadValues({}, req.requesterDetails), userDetails);
1130
- next();
1131
- },
1132
- (errorMessage) => {
1133
- res.sendError("UNAUTHORIZED", errorMessage);
1134
- }
1135
- );
1136
- };
1137
- }
1244
+ // src/restura/sql/PsqlEngine.ts
1245
+ var import_core_utils5 = require("@redskytech/core-utils");
1138
1246
 
1139
- // src/restura/customTypeValidationGenerator.ts
1140
- var import_fs = __toESM(require("fs"));
1141
- var TJS = __toESM(require("typescript-json-schema"));
1142
- var import_path = __toESM(require("path"));
1143
- var import_tmp = __toESM(require("tmp"));
1144
- var process2 = __toESM(require("process"));
1145
- function customTypeValidationGenerator(currentSchema) {
1146
- const schemaObject = {};
1147
- const customInterfaceNames = currentSchema.customTypes.match(new RegExp("(?<=interface\\s)(\\w+)|(?<=type\\s)(\\w+)", "g"));
1148
- if (!customInterfaceNames) return {};
1149
- const temporaryFile = import_tmp.default.fileSync({ mode: 420, prefix: "prefix-", postfix: ".ts" });
1150
- import_fs.default.writeFileSync(temporaryFile.name, currentSchema.customTypes);
1151
- const compilerOptions = {
1152
- strictNullChecks: true,
1153
- skipLibCheck: true
1154
- };
1155
- const program = TJS.getProgramFromFiles(
1156
- [
1157
- (0, import_path.resolve)(temporaryFile.name),
1158
- // find a way to remove
1159
- import_path.default.join(process2.cwd(), "src/@types/models.d.ts"),
1160
- import_path.default.join(process2.cwd(), "src/@types/api.d.ts")
1161
- ],
1162
- compilerOptions
1163
- );
1164
- customInterfaceNames.forEach((item) => {
1165
- const ddlSchema = TJS.generateSchema(program, item, {
1166
- required: true
1167
- });
1168
- schemaObject[item] = ddlSchema || {};
1247
+ // src/restura/sql/PsqlUtils.ts
1248
+ var import_pg_format = __toESM(require("pg-format"));
1249
+ function escapeColumnName(columnName) {
1250
+ if (columnName === void 0) return "";
1251
+ return `"${columnName.replace(/"/g, "")}"`.replace(".", '"."');
1252
+ }
1253
+ function questionMarksToOrderedParams(query) {
1254
+ let count = 1;
1255
+ return query.replace(/'\?'|\?/g, () => `$${count++}`);
1256
+ }
1257
+ function insertObjectQuery(table, obj) {
1258
+ const keys = Object.keys(obj);
1259
+ const params = Object.values(obj);
1260
+ const columns = keys.map((column) => escapeColumnName(column)).join(", ");
1261
+ const values = params.map((value) => SQL`${value}`).join(", ");
1262
+ const query = `INSERT INTO "${table}" (${columns})
1263
+ VALUES (${values})
1264
+ RETURNING *`;
1265
+ return query;
1266
+ }
1267
+ function updateObjectQuery(table, obj, whereStatement) {
1268
+ const setArray = [];
1269
+ for (const i in obj) {
1270
+ setArray.push(`${escapeColumnName(i)} = ` + SQL`${obj[i]}`);
1271
+ }
1272
+ return `UPDATE ${escapeColumnName(table)}
1273
+ SET ${setArray.join(", ")} ${whereStatement}
1274
+ RETURNING *`;
1275
+ }
1276
+ function isValueNumber2(value) {
1277
+ return !isNaN(Number(value));
1278
+ }
1279
+ function SQL(strings, ...values) {
1280
+ let query = strings[0];
1281
+ values.forEach((value, index) => {
1282
+ if (isValueNumber2(value)) {
1283
+ query += value;
1284
+ } else {
1285
+ query += import_pg_format.default.literal(value);
1286
+ }
1287
+ query += strings[index + 1];
1169
1288
  });
1170
- temporaryFile.removeCallback();
1171
- return schemaObject;
1289
+ return query;
1172
1290
  }
1173
1291
 
1174
1292
  // src/restura/sql/SqlEngine.ts
@@ -1260,7 +1378,10 @@ var SqlEngine = class {
1260
1378
  param = param.replace("#", "");
1261
1379
  const globalParamValue = req.requesterDetails[param];
1262
1380
  if (!globalParamValue)
1263
- throw new RsError("SCHEMA_ERROR", `Invalid global keyword clause in route ${routeData.name}`);
1381
+ throw new RsError(
1382
+ "SCHEMA_ERROR",
1383
+ `Invalid global keyword clause in route (${routeData.path}) when looking for (#${param})`
1384
+ );
1264
1385
  sqlParams.push(globalParamValue);
1265
1386
  });
1266
1387
  return value.replace(new RegExp(/#[a-zA-Z][a-zA-Z0-9_]+/g), "?");
@@ -1269,7 +1390,7 @@ var SqlEngine = class {
1269
1390
  }
1270
1391
  };
1271
1392
 
1272
- // src/restura/sql/filterSqlParser.ts
1393
+ // src/restura/sql/filterPsqlParser.ts
1273
1394
  var import_pegjs = __toESM(require("pegjs"));
1274
1395
  var filterSqlGrammar = `
1275
1396
  start = expressionList
@@ -1281,7 +1402,7 @@ expressionList =
1281
1402
 
1282
1403
  expression =
1283
1404
  negate:negate?"(" "column:" column:column ","? value:value? ","? type:type? ")"
1284
- {return \`\${negate? "!" : ""}(\${type? type(column, value) : \`\${column} = \${mysql.escape(value)}\`})\`;}
1405
+ {return \`\${negate? "!" : ""}(\${type? type(column, value) : \`\${column} = \${format.literal(value)}\`})\`;}
1285
1406
  /
1286
1407
  negate:negate?"("expression:expressionList")" { return \`\${negate? "!" : ""}(\${expression})\`; }
1287
1408
 
@@ -1289,80 +1410,33 @@ negate = "!"
1289
1410
 
1290
1411
  operator = "and"i / "or"i
1291
1412
 
1292
- column = left:text "." right:text { return \`\${mysql.escapeId(left)}.\${mysql.escapeId(right)}\`; }
1293
- /
1294
- text:text { return mysql.escapeId(text); }
1295
-
1413
+
1414
+ column = left:text "." right:text { return \`\${format.ident(left)}.\${format.ident(right)}\`; }
1415
+ /
1416
+ text:text { return format.ident(text); }
1417
+
1296
1418
 
1297
- text = text:[ a-z0-9-_:.@]i+ { return text.join("");}
1419
+ text = text:[a-z0-9-_:@]i+ { return text.join("");}
1298
1420
 
1299
1421
  type = "type:" type:typeString { return type; }
1300
- typeString = text:"startsWith" { return function(column, value) { return \`\${column} LIKE '\${mysql.escape(value).slice(1,-1)}%'\`; } } /
1301
- text:"endsWith" { return function(column, value) { return \`\${column} LIKE '%\${mysql.escape(value).slice(1,-1)}'\`; } } /
1302
- text:"contains" { return function(column, value) { return \`\${column} LIKE '%\${mysql.escape(value).slice(1,-1)}%'\`; } } /
1303
- text:"exact" { return function(column, value) { return \`\${column} = '\${mysql.escape(value).slice(1,-1)}'\`; } } /
1304
- text:"greaterThanEqual" { return function(column, value) { return \`\${column} >= '\${mysql.escape(value).slice(1,-1)}'\`; } } /
1305
- text:"greaterThan" { return function(column, value) { return \`\${column} > '\${mysql.escape(value).slice(1,-1)}'\`; } } /
1306
- text:"lessThanEqual" { return function(column, value) { return \`\${column} <= '\${mysql.escape(value).slice(1,-1)}'\`; } } /
1307
- text:"lessThan" { return function(column, value) { return \`\${column} < '\${mysql.escape(value).slice(1,-1)}'\`; } } /
1422
+ typeString = text:"startsWith" { return function(column, value) { return \`\${column} ILIKE '\${format.literal(value).slice(1,-1)}%'\`; } } /
1423
+ text:"endsWith" { return function(column, value) { return \`\${column} ILIKE '%\${format.literal(value).slice(1,-1)}'\`; } } /
1424
+ text:"contains" { return function(column, value) { return \`\${column} ILIKE '%\${format.literal(value).slice(1,-1)}%'\`; } } /
1425
+ text:"exact" { return function(column, value) { return \`\${column} = '\${format.literal(value).slice(1,-1)}'\`; } } /
1426
+ text:"greaterThanEqual" { return function(column, value) { return \`\${column} >= '\${format.literal(value).slice(1,-1)}'\`; } } /
1427
+ text:"greaterThan" { return function(column, value) { return \`\${column} > '\${format.literal(value).slice(1,-1)}'\`; } } /
1428
+ text:"lessThanEqual" { return function(column, value) { return \`\${column} <= '\${format.literal(value).slice(1,-1)}'\`; } } /
1429
+ text:"lessThan" { return function(column, value) { return \`\${column} < '\${format.literal(value).slice(1,-1)}'\`; } } /
1308
1430
  text:"isNull" { return function(column, value) { return \`isNull(\${column})\`; } }
1309
1431
 
1310
1432
  value = "value:" value:text { return value; }
1311
1433
 
1312
1434
  `;
1313
- var filterSqlParser = import_pegjs.default.generate(filterSqlGrammar, {
1314
- format: "commonjs"
1315
- // dependencies: { mysql: 'mysql' } // todo: figure out a better way to escape values depending on the database type
1435
+ var filterPsqlParser = import_pegjs.default.generate(filterSqlGrammar, {
1436
+ format: "commonjs",
1437
+ dependencies: { format: "pg-format" }
1316
1438
  });
1317
- var filterSqlParser_default = filterSqlParser;
1318
-
1319
- // src/restura/sql/PsqlEngine.ts
1320
- var import_core_utils5 = require("@redskytech/core-utils");
1321
-
1322
- // src/restura/sql/PsqlUtils.ts
1323
- var import_pg_format = __toESM(require("pg-format"));
1324
- function escapeColumnName(columnName) {
1325
- if (columnName === void 0) return "";
1326
- return `"${columnName.replace(/"/g, "")}"`.replace(".", '"."');
1327
- }
1328
- function questionMarksToOrderedParams(query) {
1329
- let count = 1;
1330
- return query.replace(/'\?'/g, () => `$${count++}`);
1331
- }
1332
- function insertObjectQuery(table, obj) {
1333
- const keys = Object.keys(obj);
1334
- const params = Object.values(obj);
1335
- const columns = keys.map((column) => escapeColumnName(column)).join(", ");
1336
- const values = params.map((value) => SQL`${value}`).join(", ");
1337
- const query = `INSERT INTO "${table}" (${columns})
1338
- VALUES (${values})
1339
- RETURNING *`;
1340
- return query;
1341
- }
1342
- function updateObjectQuery(table, obj, whereStatement) {
1343
- const setArray = [];
1344
- for (const i in obj) {
1345
- setArray.push(`${escapeColumnName(i)} = ` + SQL`${obj[i]}`);
1346
- }
1347
- return `UPDATE ${escapeColumnName(table)}
1348
- SET ${setArray.join(", ")} ${whereStatement}
1349
- RETURNING *`;
1350
- }
1351
- function isValueNumber2(value) {
1352
- return !isNaN(Number(value));
1353
- }
1354
- function SQL(strings, ...values) {
1355
- let query = strings[0];
1356
- values.forEach((value, index) => {
1357
- if (isValueNumber2(value)) {
1358
- query += value;
1359
- } else {
1360
- query += import_pg_format.default.literal(value);
1361
- }
1362
- query += strings[index + 1];
1363
- });
1364
- return query;
1365
- }
1439
+ var filterPsqlParser_default = filterPsqlParser;
1366
1440
 
1367
1441
  // src/restura/sql/PsqlEngine.ts
1368
1442
  var PsqlEngine = class extends SqlEngine {
@@ -1379,8 +1453,45 @@ var PsqlEngine = class extends SqlEngine {
1379
1453
  return "";
1380
1454
  }
1381
1455
  createNestedSelect(req, schema, item, routeData, userRole, sqlParams) {
1382
- console.log(req, schema, item, routeData, userRole, sqlParams);
1383
- return "";
1456
+ if (!item.subquery) return "";
1457
+ if (!import_core_utils5.ObjectUtils.isArrayWithData(
1458
+ item.subquery.properties.filter((nestedItem) => {
1459
+ return this.doesRoleHavePermissionToColumn(req.requesterDetails.role, schema, nestedItem, [
1460
+ ...routeData.joins,
1461
+ ...item.subquery.joins
1462
+ ]);
1463
+ })
1464
+ )) {
1465
+ return "'[]'";
1466
+ }
1467
+ return `COALESCE((
1468
+ SELECT JSON_AGG(JSON_BUILD_OBJECT(
1469
+ ${item.subquery.properties.map((nestedItem) => {
1470
+ if (!this.doesRoleHavePermissionToColumn(req.requesterDetails.role, schema, nestedItem, [
1471
+ ...routeData.joins,
1472
+ ...item.subquery.joins
1473
+ ])) {
1474
+ return;
1475
+ }
1476
+ if (nestedItem.subquery) {
1477
+ return `"${nestedItem.name}", ${this.createNestedSelect(
1478
+ // recursion
1479
+ req,
1480
+ schema,
1481
+ nestedItem,
1482
+ routeData,
1483
+ userRole,
1484
+ sqlParams
1485
+ )}`;
1486
+ }
1487
+ return `'${nestedItem.name}', ${escapeColumnName(nestedItem.selector)}`;
1488
+ }).filter(Boolean).join(",")}
1489
+ ))
1490
+ FROM
1491
+ "${item.subquery.table}"
1492
+ ${this.generateJoinStatements(req, item.subquery.joins, item.subquery.table, routeData, schema, userRole, sqlParams)}
1493
+ ${this.generateWhereClause(req, item.subquery.where, routeData, sqlParams)}
1494
+ ), '[]')`;
1384
1495
  }
1385
1496
  async executeCreateRequest(req, routeData, schema) {
1386
1497
  const sqlParams = [];
@@ -1389,7 +1500,7 @@ var PsqlEngine = class extends SqlEngine {
1389
1500
  parameterObj[assignment.name] = this.replaceParamKeywords(assignment.value, routeData, req, sqlParams);
1390
1501
  });
1391
1502
  const query = insertObjectQuery(routeData.table, __spreadValues(__spreadValues({}, req.data), parameterObj));
1392
- const createdItem = await this.psqlConnectionPool.queryOne(query, sqlParams);
1503
+ const createdItem = await this.psqlConnectionPool.queryOne(query, sqlParams, req.requesterDetails);
1393
1504
  const insertId = createdItem == null ? void 0 : createdItem.id;
1394
1505
  const whereData = [
1395
1506
  {
@@ -1439,12 +1550,14 @@ var PsqlEngine = class extends SqlEngine {
1439
1550
  if (routeData.type === "ONE") {
1440
1551
  return this.psqlConnectionPool.queryOne(
1441
1552
  `${selectStatement}${sqlStatement}${groupByOrderByStatement};`,
1442
- sqlParams
1553
+ sqlParams,
1554
+ req.requesterDetails
1443
1555
  );
1444
1556
  } else if (routeData.type === "ARRAY") {
1445
1557
  return this.psqlConnectionPool.runQuery(
1446
1558
  `${selectStatement}${sqlStatement}${groupByOrderByStatement};`,
1447
- sqlParams
1559
+ sqlParams,
1560
+ req.requesterDetails
1448
1561
  );
1449
1562
  } else if (routeData.type === "PAGED") {
1450
1563
  const data = req.data;
@@ -1456,7 +1569,8 @@ ${sqlStatement};`,
1456
1569
  data.perPage || DEFAULT_PAGED_PER_PAGE_NUMBER,
1457
1570
  (data.page - 1) * data.perPage || DEFAULT_PAGED_PAGE_NUMBER,
1458
1571
  ...sqlParams
1459
- ]
1572
+ ],
1573
+ req.requesterDetails
1460
1574
  );
1461
1575
  let total = 0;
1462
1576
  if (import_core_utils5.ObjectUtils.isArrayWithData(pageResults)) {
@@ -1487,7 +1601,7 @@ ${sqlStatement};`,
1487
1601
  }
1488
1602
  const whereClause = this.generateWhereClause(req, routeData.where, routeData, sqlParams);
1489
1603
  const query = updateObjectQuery(routeData.table, bodyNoId, whereClause);
1490
- await this.psqlConnectionPool.queryOne(query, [...sqlParams]);
1604
+ await this.psqlConnectionPool.queryOne(query, [...sqlParams], req.requesterDetails);
1491
1605
  return this.executeGetRequest(req, routeData, schema);
1492
1606
  }
1493
1607
  async executeDeleteRequest(req, routeData, schema) {
@@ -1505,16 +1619,26 @@ ${sqlStatement};`,
1505
1619
  FROM "${routeData.table}" ${joinStatement}`;
1506
1620
  deleteStatement += this.generateWhereClause(req, routeData.where, routeData, sqlParams);
1507
1621
  deleteStatement += ";";
1508
- await this.psqlConnectionPool.runQuery(deleteStatement, sqlParams);
1622
+ await this.psqlConnectionPool.runQuery(deleteStatement, sqlParams, req.requesterDetails);
1509
1623
  return true;
1510
1624
  }
1511
1625
  generateJoinStatements(req, joins, baseTable, routeData, schema, userRole, sqlParams) {
1512
- console.log(req, joins, baseTable, routeData, schema, userRole, sqlParams);
1513
- return "";
1514
- }
1515
- getTableSchema(schema, tableName) {
1516
- console.log(schema, tableName);
1517
- return {};
1626
+ let joinStatements = "";
1627
+ joins.forEach((item) => {
1628
+ if (!this.doesRoleHavePermissionToTable(userRole, schema, item.table))
1629
+ throw new RsError("UNAUTHORIZED", "You do not have permission to access this table");
1630
+ if (item.custom) {
1631
+ const customReplaced = this.replaceParamKeywords(item.custom, routeData, req, sqlParams);
1632
+ joinStatements += ` ${item.type} JOIN ${escapeColumnName(item.table)} ON ${customReplaced}
1633
+ `;
1634
+ } else {
1635
+ joinStatements += ` ${item.type} JOIN ${escapeColumnName(item.table)}${item.alias ? `AS "${item.alias}"` : ""} ON "${baseTable}"."${item.localColumnName}" = ${escapeColumnName(item.alias ? item.alias : item.table)}.${escapeColumnName(
1636
+ item.foreignColumnName
1637
+ )}
1638
+ `;
1639
+ }
1640
+ });
1641
+ return joinStatements;
1518
1642
  }
1519
1643
  generateGroupBy(routeData) {
1520
1644
  let groupBy = "";
@@ -1573,22 +1697,24 @@ ${sqlStatement};`,
1573
1697
  const data = req.data;
1574
1698
  if (routeData.type === "PAGED" && !!(data == null ? void 0 : data.filter)) {
1575
1699
  let statement = data.filter.replace(/\$[a-zA-Z][a-zA-Z0-9_]+/g, (value) => {
1700
+ var _a;
1576
1701
  const requestParam = routeData.request.find((item) => {
1577
1702
  return item.name === value.replace("$", "");
1578
1703
  });
1579
1704
  if (!requestParam)
1580
1705
  throw new RsError("SCHEMA_ERROR", `Invalid route keyword in route ${routeData.name}`);
1581
- return data[requestParam.name];
1706
+ return ((_a = data[requestParam.name]) == null ? void 0 : _a.toString()) || "";
1582
1707
  });
1583
1708
  statement = statement.replace(/#[a-zA-Z][a-zA-Z0-9_]+/g, (value) => {
1709
+ var _a;
1584
1710
  const requestParam = routeData.request.find((item) => {
1585
1711
  return item.name === value.replace("#", "");
1586
1712
  });
1587
1713
  if (!requestParam)
1588
1714
  throw new RsError("SCHEMA_ERROR", `Invalid route keyword in route ${routeData.name}`);
1589
- return data[requestParam.name];
1715
+ return ((_a = data[requestParam.name]) == null ? void 0 : _a.toString()) || "";
1590
1716
  });
1591
- statement = filterSqlParser_default.parse(statement);
1717
+ statement = filterPsqlParser_default.parse(statement);
1592
1718
  if (whereClause.startsWith("WHERE")) {
1593
1719
  whereClause += ` AND (${statement})
1594
1720
  `;
@@ -1601,24 +1727,33 @@ ${sqlStatement};`,
1601
1727
  }
1602
1728
  };
1603
1729
 
1604
- // src/restura/restura.ts
1605
- var import_pg2 = require("pg");
1606
-
1607
1730
  // src/restura/sql/PsqlPool.ts
1608
- var import_pg = require("pg");
1731
+ var import_pg = __toESM(require("pg"));
1732
+ var import_pg_format2 = __toESM(require("pg-format"));
1733
+ var { Pool } = import_pg.default;
1609
1734
  var PsqlPool = class {
1610
1735
  constructor(poolConfig) {
1611
1736
  this.poolConfig = poolConfig;
1612
- this.pool = new import_pg.Pool(poolConfig);
1737
+ this.pool = new Pool(poolConfig);
1738
+ this.queryOne("SELECT NOW();", [], { isSystemUser: true, role: "", host: "localhost", ipAddress: "" }).then(() => {
1739
+ logger.info("Connected to PostgreSQL database");
1740
+ }).catch((error) => {
1741
+ logger.error("Error connecting to database", error);
1742
+ process.exit(1);
1743
+ });
1613
1744
  }
1614
1745
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
1615
- async queryOne(query, options) {
1746
+ async queryOne(query, options, requesterDetails) {
1616
1747
  const formattedQuery = questionMarksToOrderedParams(query);
1748
+ this.logSqlStatement(formattedQuery, options, requesterDetails);
1617
1749
  try {
1618
1750
  const response = await this.pool.query(formattedQuery, options);
1751
+ if (response.rows.length === 0) throw new RsError("NOT_FOUND", "No results found");
1752
+ else if (response.rows.length > 1) throw new RsError("DUPLICATE", "More than one result found");
1619
1753
  return response.rows[0];
1620
1754
  } catch (error) {
1621
1755
  console.error(error, query, options);
1756
+ if (RsError.isRsError(error)) throw error;
1622
1757
  if ((error == null ? void 0 : error.routine) === "_bt_check_unique") {
1623
1758
  throw new RsError("DUPLICATE", error.message);
1624
1759
  }
@@ -1626,8 +1761,9 @@ var PsqlPool = class {
1626
1761
  }
1627
1762
  }
1628
1763
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
1629
- async runQuery(query, options) {
1764
+ async runQuery(query, options, requesterDetails) {
1630
1765
  const formattedQuery = questionMarksToOrderedParams(query);
1766
+ this.logSqlStatement(formattedQuery, options, requesterDetails);
1631
1767
  const queryUpdated = query.replace(/[\t\n]/g, " ");
1632
1768
  console.log(queryUpdated, options);
1633
1769
  try {
@@ -1641,9 +1777,30 @@ var PsqlPool = class {
1641
1777
  throw new RsError("DATABASE_ERROR", `${error.message}`);
1642
1778
  }
1643
1779
  }
1780
+ logSqlStatement(query, options, requesterDetails, prefix = "") {
1781
+ if (logger.level !== "silly") return;
1782
+ let sqlStatement = "";
1783
+ if (options.length === 0) {
1784
+ sqlStatement = query;
1785
+ } else {
1786
+ let stringIndex = 0;
1787
+ sqlStatement = query.replace(/\$\d+/g, () => {
1788
+ const value = options[stringIndex++];
1789
+ if (typeof value === "number") return value.toString();
1790
+ return import_pg_format2.default.literal(value);
1791
+ });
1792
+ }
1793
+ let initiator = "Anonymous";
1794
+ if ("userId" in requesterDetails && requesterDetails.userId)
1795
+ initiator = `User Id (${requesterDetails.userId.toString()})`;
1796
+ if ("isSystemUser" in requesterDetails && requesterDetails.isSystemUser) initiator = "SYSTEM";
1797
+ logger.silly(`${prefix}query by ${initiator}, Query ->
1798
+ ${sqlStatement}`);
1799
+ }
1644
1800
  };
1645
1801
 
1646
1802
  // src/restura/restura.ts
1803
+ var { types } = import_pg2.default;
1647
1804
  var ResturaEngine = class {
1648
1805
  constructor() {
1649
1806
  this.publicEndpoints = {
@@ -1661,10 +1818,11 @@ var ResturaEngine = class {
1661
1818
  * @returns A promise that resolves when the initialization is complete.
1662
1819
  */
1663
1820
  async init(app, authenticationHandler, psqlConnectionPool) {
1821
+ this.resturaConfig = import_internal2.config.validate("restura", resturaConfigSchema);
1664
1822
  this.psqlConnectionPool = psqlConnectionPool;
1665
1823
  this.psqlEngine = new PsqlEngine(this.psqlConnectionPool);
1666
1824
  setupPgReturnTypes();
1667
- this.resturaConfig = import_internal2.config.validate("restura", resturaConfigSchema);
1825
+ await customApiFactory_default.loadApiFiles(this.resturaConfig.customApiFolderPath);
1668
1826
  this.authenticationHandler = authenticationHandler;
1669
1827
  app.use((0, import_compression.default)());
1670
1828
  app.use(import_body_parser.default.json({ limit: "32mb" }));
@@ -1727,7 +1885,7 @@ var ResturaEngine = class {
1727
1885
  * @returns A promise that resolves when the API has been successfully generated and written to the output file.
1728
1886
  */
1729
1887
  async generateApiFromSchema(outputFile, providedSchema) {
1730
- import_fs2.default.writeFileSync(
1888
+ import_fs3.default.writeFileSync(
1731
1889
  outputFile,
1732
1890
  await apiGenerator(providedSchema, await this.generateHashForSchema(providedSchema))
1733
1891
  );
@@ -1740,7 +1898,7 @@ var ResturaEngine = class {
1740
1898
  * @returns A promise that resolves when the model has been successfully written to the output file.
1741
1899
  */
1742
1900
  async generateModelFromSchema(outputFile, providedSchema) {
1743
- import_fs2.default.writeFileSync(
1901
+ import_fs3.default.writeFileSync(
1744
1902
  outputFile,
1745
1903
  await modelGenerator(providedSchema, await this.generateHashForSchema(providedSchema))
1746
1904
  );
@@ -1752,11 +1910,11 @@ var ResturaEngine = class {
1752
1910
  * @throws {Error} If the schema file is missing or the schema is not valid.
1753
1911
  */
1754
1912
  async getLatestFileSystemSchema() {
1755
- if (!import_fs2.default.existsSync(this.resturaConfig.schemaFilePath)) {
1913
+ if (!import_fs3.default.existsSync(this.resturaConfig.schemaFilePath)) {
1756
1914
  logger.error(`Missing restura schema file, expected path: ${this.resturaConfig.schemaFilePath}`);
1757
1915
  throw new Error("Missing restura schema file");
1758
1916
  }
1759
- const schemaFileData = import_fs2.default.readFileSync(this.resturaConfig.schemaFilePath, { encoding: "utf8" });
1917
+ const schemaFileData = import_fs3.default.readFileSync(this.resturaConfig.schemaFilePath, { encoding: "utf8" });
1760
1918
  const schema = import_core_utils6.ObjectUtils.safeParse(schemaFileData);
1761
1919
  const isValid = await isSchemaValid(schema);
1762
1920
  if (!isValid) {
@@ -1777,9 +1935,9 @@ var ResturaEngine = class {
1777
1935
  async getHashes(providedSchema) {
1778
1936
  var _a, _b, _c, _d;
1779
1937
  const schemaHash = await this.generateHashForSchema(providedSchema);
1780
- const apiFile = import_fs2.default.readFileSync(import_path2.default.join(this.resturaConfig.generatedTypesPath, "api.d.ts"));
1938
+ const apiFile = import_fs3.default.readFileSync(import_path3.default.join(this.resturaConfig.generatedTypesPath, "api.d.ts"));
1781
1939
  const apiCreatedSchemaHash = (_b = (_a = apiFile.toString().match(/\((.*)\)/)) == null ? void 0 : _a[1]) != null ? _b : "";
1782
- const modelFile = import_fs2.default.readFileSync(import_path2.default.join(this.resturaConfig.generatedTypesPath, "models.d.ts"));
1940
+ const modelFile = import_fs3.default.readFileSync(import_path3.default.join(this.resturaConfig.generatedTypesPath, "models.d.ts"));
1783
1941
  const modelCreatedSchemaHash = (_d = (_c = modelFile.toString().match(/\((.*)\)/)) == null ? void 0 : _c[1]) != null ? _d : "";
1784
1942
  return {
1785
1943
  schemaHash,
@@ -1815,27 +1973,27 @@ var ResturaEngine = class {
1815
1973
  logger.info(`Restura loaded (${routeCount}) endpoint${routeCount > 1 ? "s" : ""}`);
1816
1974
  }
1817
1975
  async validateGeneratedTypesFolder() {
1818
- if (!import_fs2.default.existsSync(this.resturaConfig.generatedTypesPath)) {
1819
- import_fs2.default.mkdirSync(this.resturaConfig.generatedTypesPath, { recursive: true });
1976
+ if (!import_fs3.default.existsSync(this.resturaConfig.generatedTypesPath)) {
1977
+ import_fs3.default.mkdirSync(this.resturaConfig.generatedTypesPath, { recursive: true });
1820
1978
  }
1821
- const hasApiFile = import_fs2.default.existsSync(import_path2.default.join(this.resturaConfig.generatedTypesPath, "api.d.ts"));
1822
- const hasModelsFile = import_fs2.default.existsSync(import_path2.default.join(this.resturaConfig.generatedTypesPath, "models.d.ts"));
1979
+ const hasApiFile = import_fs3.default.existsSync(import_path3.default.join(this.resturaConfig.generatedTypesPath, "api.d.ts"));
1980
+ const hasModelsFile = import_fs3.default.existsSync(import_path3.default.join(this.resturaConfig.generatedTypesPath, "models.d.ts"));
1823
1981
  if (!hasApiFile) {
1824
- await this.generateApiFromSchema(import_path2.default.join(this.resturaConfig.generatedTypesPath, "api.d.ts"), this.schema);
1982
+ await this.generateApiFromSchema(import_path3.default.join(this.resturaConfig.generatedTypesPath, "api.d.ts"), this.schema);
1825
1983
  }
1826
1984
  if (!hasModelsFile) {
1827
1985
  await this.generateModelFromSchema(
1828
- import_path2.default.join(this.resturaConfig.generatedTypesPath, "models.d.ts"),
1986
+ import_path3.default.join(this.resturaConfig.generatedTypesPath, "models.d.ts"),
1829
1987
  this.schema
1830
1988
  );
1831
1989
  }
1832
1990
  const hashes = await this.getHashes(this.schema);
1833
1991
  if (hashes.schemaHash !== hashes.apiCreatedSchemaHash) {
1834
- await this.generateApiFromSchema(import_path2.default.join(this.resturaConfig.generatedTypesPath, "api.d.ts"), this.schema);
1992
+ await this.generateApiFromSchema(import_path3.default.join(this.resturaConfig.generatedTypesPath, "api.d.ts"), this.schema);
1835
1993
  }
1836
1994
  if (hashes.schemaHash !== hashes.modelCreatedSchemaHash) {
1837
1995
  await this.generateModelFromSchema(
1838
- import_path2.default.join(this.resturaConfig.generatedTypesPath, "models.d.ts"),
1996
+ import_path3.default.join(this.resturaConfig.generatedTypesPath, "models.d.ts"),
1839
1997
  this.schema
1840
1998
  );
1841
1999
  }
@@ -1865,9 +2023,9 @@ var ResturaEngine = class {
1865
2023
  }
1866
2024
  }
1867
2025
  async updateTypes() {
1868
- await this.generateApiFromSchema(import_path2.default.join(this.resturaConfig.generatedTypesPath, "api.d.ts"), this.schema);
2026
+ await this.generateApiFromSchema(import_path3.default.join(this.resturaConfig.generatedTypesPath, "api.d.ts"), this.schema);
1869
2027
  await this.generateModelFromSchema(
1870
- import_path2.default.join(this.resturaConfig.generatedTypesPath, "models.d.ts"),
2028
+ import_path3.default.join(this.resturaConfig.generatedTypesPath, "models.d.ts"),
1871
2029
  this.schema
1872
2030
  );
1873
2031
  }
@@ -1890,11 +2048,16 @@ var ResturaEngine = class {
1890
2048
  const routeData = this.getRouteData(req.method, req.baseUrl, req.path);
1891
2049
  this.validateAuthorization(req, routeData);
1892
2050
  validateRequestParams(req, routeData, this.customTypeValidation);
2051
+ if (this.isCustomRoute(routeData)) {
2052
+ await this.runCustomRouteLogic(req, res, routeData);
2053
+ return;
2054
+ }
1893
2055
  const data = await this.psqlEngine.runQueryForRoute(
1894
2056
  req,
1895
2057
  routeData,
1896
2058
  this.schema
1897
2059
  );
2060
+ this.responseValidator.validateResponseParams(data, req.baseUrl, routeData);
1898
2061
  if (routeData.type === "PAGED") res.sendNoWrap(data);
1899
2062
  else res.sendData(data);
1900
2063
  } catch (e) {
@@ -1904,34 +2067,25 @@ var ResturaEngine = class {
1904
2067
  isCustomRoute(route) {
1905
2068
  return route.type === "CUSTOM_ONE" || route.type === "CUSTOM_ARRAY" || route.type === "CUSTOM_PAGED";
1906
2069
  }
1907
- // @boundMethod
1908
- // private async runCustomRouteLogic<T>(req: RsRequest<T>, res: RsResponse<T>, routeData: RouteData) {
1909
- // const version = req.baseUrl.split('/')[2];
1910
- // let domain = routeData.path.split('/')[1];
1911
- // domain = domain.split('-').reduce((acc, value, index) => {
1912
- // if (index === 0) acc = value;
1913
- // else acc += StringUtils.capitalizeFirst(value);
1914
- // return acc;
1915
- // }, '');
1916
- // const customApiName = `${StringUtils.capitalizeFirst(domain)}Api${StringUtils.capitalizeFirst(version)}`;
1917
- // const customApi = apiFactory.getCustomApi(customApiName);
1918
- // if (!customApi) throw new RsError('NOT_FOUND', `API domain ${domain}-${version} not found`);
1919
- // const functionName = `${routeData.method.toLowerCase()}${routeData.path
1920
- // .replace(new RegExp('-', 'g'), '/')
1921
- // .split('/')
1922
- // .reduce((acc, cur) => {
1923
- // if (cur === '') return acc;
1924
- // return acc + StringUtils.capitalizeFirst(cur);
1925
- // }, '')}`;
1926
- // // @ts-expect-error - Here we are dynamically calling the function from a custom class, not sure how to typescript this
1927
- // const customFunction = customApi[functionName] as (
1928
- // req: RsRequest<T>,
1929
- // res: RsResponse<T>,
1930
- // routeData: RouteData
1931
- // ) => Promise<void>;
1932
- // if (!customFunction) throw new RsError('NOT_FOUND', `API path ${routeData.path} not implemented`);
1933
- // await customFunction(req, res, routeData);
1934
- // }
2070
+ async runCustomRouteLogic(req, res, routeData) {
2071
+ const version = req.baseUrl.split("/")[2];
2072
+ let domain = routeData.path.split("/")[1];
2073
+ domain = domain.split("-").reduce((acc, value, index) => {
2074
+ if (index === 0) acc = value;
2075
+ else acc += import_core_utils6.StringUtils.capitalizeFirst(value);
2076
+ return acc;
2077
+ }, "");
2078
+ const customApiName = `${import_core_utils6.StringUtils.capitalizeFirst(domain)}Api${import_core_utils6.StringUtils.capitalizeFirst(version)}`;
2079
+ const customApi = customApiFactory_default.getCustomApi(customApiName);
2080
+ if (!customApi) throw new RsError("NOT_FOUND", `API domain ${domain}-${version} not found`);
2081
+ const functionName = `${routeData.method.toLowerCase()}${routeData.path.replace(new RegExp("-", "g"), "/").split("/").reduce((acc, cur) => {
2082
+ if (cur === "") return acc;
2083
+ return acc + import_core_utils6.StringUtils.capitalizeFirst(cur);
2084
+ }, "")}`;
2085
+ const customFunction = customApi[functionName];
2086
+ if (!customFunction) throw new RsError("NOT_FOUND", `API path ${routeData.path} not implemented`);
2087
+ await customFunction(req, res, routeData);
2088
+ }
1935
2089
  async generateHashForSchema(providedSchema) {
1936
2090
  const schemaPrettyStr = await prettier3.format(JSON.stringify(providedSchema), __spreadValues({
1937
2091
  parser: "json"
@@ -1956,7 +2110,7 @@ var ResturaEngine = class {
1956
2110
  printWidth: 120,
1957
2111
  singleQuote: true
1958
2112
  }));
1959
- import_fs2.default.writeFileSync(this.resturaConfig.schemaFilePath, schemaPrettyStr);
2113
+ import_fs3.default.writeFileSync(this.resturaConfig.schemaFilePath, schemaPrettyStr);
1960
2114
  }
1961
2115
  resetPublicEndpoints() {
1962
2116
  this.publicEndpoints = {
@@ -1973,13 +2127,13 @@ var ResturaEngine = class {
1973
2127
  if (!routeData.roles.includes(role))
1974
2128
  throw new RsError("UNAUTHORIZED", "Not authorized to access this endpoint");
1975
2129
  }
1976
- getRouteData(method, baseUrl, path3) {
2130
+ getRouteData(method, baseUrl, path4) {
1977
2131
  const endpoint = this.schema.endpoints.find((item) => {
1978
2132
  return item.baseUrl === baseUrl;
1979
2133
  });
1980
2134
  if (!endpoint) throw new RsError("NOT_FOUND", "Route not found");
1981
2135
  const route = endpoint.routes.find((item) => {
1982
- return item.method === method && item.path === path3;
2136
+ return item.method === method && item.path === path4;
1983
2137
  });
1984
2138
  if (!route) throw new RsError("NOT_FOUND", "Route not found");
1985
2139
  return route;
@@ -2006,13 +2160,16 @@ __decorateClass([
2006
2160
  __decorateClass([
2007
2161
  boundMethod
2008
2162
  ], ResturaEngine.prototype, "isCustomRoute", 1);
2163
+ __decorateClass([
2164
+ boundMethod
2165
+ ], ResturaEngine.prototype, "runCustomRouteLogic", 1);
2009
2166
  var setupPgReturnTypes = () => {
2010
2167
  const TIMESTAMPTZ_OID = 1184;
2011
- import_pg2.types.setTypeParser(TIMESTAMPTZ_OID, (val) => {
2168
+ types.setTypeParser(TIMESTAMPTZ_OID, (val) => {
2012
2169
  return val === null ? null : new Date(val).toISOString();
2013
2170
  });
2014
2171
  const BIGINT_OID = 20;
2015
- import_pg2.types.setTypeParser(BIGINT_OID, (val) => {
2172
+ types.setTypeParser(BIGINT_OID, (val) => {
2016
2173
  return val === null ? null : Number(val);
2017
2174
  });
2018
2175
  };
@@ -2020,8 +2177,16 @@ setupPgReturnTypes();
2020
2177
  var restura = new ResturaEngine();
2021
2178
  // Annotate the CommonJS export names for ESM import in node:
2022
2179
  0 && (module.exports = {
2180
+ HtmlStatusCodes,
2023
2181
  PsqlPool,
2182
+ RsError,
2183
+ SQL,
2184
+ escapeColumnName,
2185
+ insertObjectQuery,
2186
+ isValueNumber,
2024
2187
  logger,
2025
- restura
2188
+ questionMarksToOrderedParams,
2189
+ restura,
2190
+ updateObjectQuery
2026
2191
  });
2027
2192
  //# sourceMappingURL=index.js.map